From 2596764f34a03e4f53704ca5efef71de5c4f9f4c Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Tue, 8 Mar 2016 15:02:56 +0530 Subject: soc/qualcomm/ipq40xx: Add support for BLSP QUP I2C Able to talk to the TPM device and the commands seem to succeed. BUG=chrome-os-partner:49249 chrome-os-partner:49250 TEST=All commands to the TPM succeed BRANCH=none Original-Commit-Id: c13900108f524c8422c38dee88469c8bfe24d0bd Original-Change-Id: Ie8c3c1ab1290cd8d7e6ddd1ae22f765c7be81019 Original-Signed-off-by: Varadarajan Narayanan Original-Reviewed-on: https://chromium-review.googlesource.com/333314 Original-Commit-Ready: David Hendricks Original-Tested-by: David Hendricks Original-Reviewed-by: David Hendricks squashed: soc/qualcomm/ipq40xx: Add support for BLSP QUP SPI - Enable BLSP SPI driver for ipq40xx - supports only FIFO mode BUG=chrome-os-partner:49249 TEST=None. Initial code not sure if it will even compile BRANCH=none Original-Commit-Id: 0714025975854dd048d35fe602824ead4c7d94e9 Original-Change-Id: If809b0fdf7d6c9405db6fd3747a3774c00ea9870 Original-Signed-off-by: Varadarajan Narayanan Original-Reviewed-on: https://chromium-review.googlesource.com/333303 Original-Commit-Ready: David Hendricks Original-Tested-by: David Hendricks Original-Reviewed-by: David Hendricks Change-Id: Ia518af5bfc782b08a0883ac93224d476d07e2426 Signed-off-by: Patrick Georgi Reviewed-on: https://review.coreboot.org/14677 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- src/soc/qualcomm/ipq40xx/spi.c | 924 ++++++++++++++++++----------------------- 1 file changed, 395 insertions(+), 529 deletions(-) (limited to 'src/soc/qualcomm/ipq40xx/spi.c') diff --git a/src/soc/qualcomm/ipq40xx/spi.c b/src/soc/qualcomm/ipq40xx/spi.c index be98fba665..8d39f77ae4 100644 --- a/src/soc/qualcomm/ipq40xx/spi.c +++ b/src/soc/qualcomm/ipq40xx/spi.c @@ -35,209 +35,117 @@ #include #include -#define SUCCESS 0 - -#define DUMMY_DATA_VAL 0 -#define TIMEOUT_CNT 100 -#define CS_ASSERT 1 -#define CS_DEASSERT 0 -#define NUM_PORTS 3 -#define NUM_GSBI_PINS 3 -#define TLMM_ARGS 6 -#define NUM_CS 4 -#define GSBI_PIN_IDX 0 -#define FUNC_SEL_IDX 1 -#define GPIO_DIR_IDX 2 -#define PULL_CONF_IDX 3 -#define DRV_STR_IDX 4 -#define GPIO_EN_IDX 5 - -/* Arbitrarily assigned error code values */ -#define ETIMEDOUT -10 -#define EINVAL -11 -#define EIO -12 - -#define GSBI_IDX_TO_GSBI(idx) (idx + 5) - - -/* MX_INPUT_COUNT and MX_OUTPUT_COUNT are 16-bits. Zero has a special meaning - * (count function disabled) and does not hold significance in the count. */ -#define MAX_PACKET_COUNT ((64 * KiB) - 1) - -/* - * TLMM Configuration for SPI NOR - * gsbi_pin_conf[bus_num][GPIO_NUM, FUNC_SEL, I/O, - * PULL UP/DOWN, DRV_STR, GPIO_FUNC] - * gsbi_pin_conf[0][x][y] -- GSBI5 - * gsbi_pin_conf[1][x][y] -- GSBI6 - * gsbi_pin_conf[2][x][y] -- GSBI7 -*/ -static unsigned int gsbi_pin_conf[NUM_PORTS][NUM_GSBI_PINS][TLMM_ARGS] = { +static const struct blsp_spi spi_reg[] = { + /* BLSP0 registers for SPI interface */ { - /* GSBI5 CLK */ - { - GSBI5_SPI_CLK, FUNC_SEL_1, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_11MA, GPIO_FUNC_DISABLE - }, - /* GSBI5 MISO */ - { - GSBI5_SPI_MISO, FUNC_SEL_1, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_10MA, GPIO_FUNC_DISABLE - }, - /* GSBI5 MOSI */ - { - GSBI5_SPI_MOSI, FUNC_SEL_1, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_10MA, GPIO_FUNC_DISABLE - } + BLSP0_SPI_CONFIG_REG, + BLSP0_SPI_IO_CONTROL_REG, + BLSP0_SPI_ERROR_FLAGS_REG, + BLSP0_SPI_ERROR_FLAGS_EN_REG, + BLSP0_QUP_CONFIG_REG, + BLSP0_QUP_ERROR_FLAGS_REG, + BLSP0_QUP_ERROR_FLAGS_EN_REG, + BLSP0_QUP_OPERATIONAL_REG, + BLSP0_QUP_IO_MODES_REG, + BLSP0_QUP_STATE_REG, + BLSP0_QUP_INPUT_FIFOc_REG(0), + BLSP0_QUP_OUTPUT_FIFOc_REG(0), + BLSP0_QUP_MX_INPUT_COUNT_REG, + BLSP0_QUP_MX_OUTPUT_COUNT_REG, + BLSP0_QUP_SW_RESET_REG, + 0, + 0, + BLSP0_QUP_OPERATIONAL_MASK, + BLSP0_SPI_DEASSERT_WAIT_REG, }, + /* BLSP1 registers for SPI interface */ { - /* GSBI6 CLK */ - { - GSBI6_SPI_CLK, FUNC_SEL_3, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_11MA, GPIO_FUNC_DISABLE - }, - /* GSBI6 MISO */ - { - GSBI6_SPI_MISO, FUNC_SEL_3, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_10MA, GPIO_FUNC_DISABLE - }, - /* GSBI6 MOSI */ - { - GSBI6_SPI_MOSI, FUNC_SEL_3, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_10MA, GPIO_FUNC_DISABLE - } + BLSP1_SPI_CONFIG_REG, + BLSP1_SPI_IO_CONTROL_REG, + BLSP1_SPI_ERROR_FLAGS_REG, + BLSP1_SPI_ERROR_FLAGS_EN_REG, + BLSP1_QUP_CONFIG_REG, + BLSP1_QUP_ERROR_FLAGS_REG, + BLSP1_QUP_ERROR_FLAGS_EN_REG, + BLSP1_QUP_OPERATIONAL_REG, + BLSP1_QUP_IO_MODES_REG, + BLSP1_QUP_STATE_REG, + BLSP1_QUP_INPUT_FIFOc_REG(0), + BLSP1_QUP_OUTPUT_FIFOc_REG(0), + BLSP1_QUP_MX_INPUT_COUNT_REG, + BLSP1_QUP_MX_OUTPUT_COUNT_REG, + BLSP1_QUP_SW_RESET_REG, + 0, + 0, + BLSP1_QUP_OPERATIONAL_MASK, + BLSP1_SPI_DEASSERT_WAIT_REG, }, - { - /* GSBI7 CLK */ - { - GSBI7_SPI_CLK, FUNC_SEL_1, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_11MA, GPIO_FUNC_DISABLE - }, - /* GSBI7 MISO */ - { - GSBI7_SPI_MISO, FUNC_SEL_1, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_10MA, GPIO_FUNC_DISABLE - }, - /* GSBI7 MOSI */ - { - GSBI7_SPI_MOSI, FUNC_SEL_1, GPIO_INPUT, - GPIO_PULL_DOWN, GPIO_DRV_STR_10MA, GPIO_FUNC_DISABLE - } - } }; -/* - * CS GPIO number array cs_gpio_array[port_num][cs_num] - * cs_gpio_array[0][x] -- GSBI5 - * cs_gpio_array[1][x] -- GSBI6 - * cs_gpio_array[2][x] -- GSBI7 - */ -static unsigned int cs_gpio_array[NUM_PORTS][NUM_CS] = { - { - GSBI5_SPI_CS_0, GSBI5_SPI_CS_1, GSBI5_SPI_CS_2, GSBI5_SPI_CS_3 - }, - { - GSBI6_SPI_CS_0, 0, 0, 0 - }, - { - GSBI7_SPI_CS_0, 0, 0, 0 - } -}; - -/* - * GSBI HCLK state register bit - * hclk_state[0] -- GSBI5 - * hclk_state[1] -- GSBI6 - * hclk_state[2] -- GSBI7 -*/ -static unsigned int hclk_state[NUM_PORTS] = { - GSBI5_HCLK, - GSBI6_HCLK, - GSBI7_HCLK -}; - -/* - * GSBI QUP_APPS_CLK state register bit - * qup_apps_clk_state[0] -- GSBI5 - * qup_apps_clk_state[1] -- GSBI6 - * qup_apps_clk_state[2] -- GSBI7 -*/ -static unsigned int qup_apps_clk_state[NUM_PORTS] = { - GSBI5_QUP_APPS_CLK, - GSBI6_QUP_APPS_CLK, - GSBI7_QUP_APPS_CLK -}; - - -static int check_bit_state(uint32_t reg_addr, int bit_num, +static int check_bit_state(void *reg_addr, int mask, int val, int us_delay) { unsigned int count = TIMEOUT_CNT; - unsigned int bit_val = ((readl_i(reg_addr) >> bit_num) & 0x01); - while (bit_val != val) { + while ((read32(reg_addr) & mask) != val) { count--; if (count == 0) return -ETIMEDOUT; udelay(us_delay); - bit_val = ((readl_i(reg_addr) >> bit_num) & 0x01); } return SUCCESS; } /* - * Check whether GSBIn_QUP State is valid + * Check whether QUPn State is valid */ static int check_qup_state_valid(struct ipq_spi_slave *ds) { - return check_bit_state(ds->regs->qup_state, QUP_STATE_VALID_BIT, + return check_bit_state(ds->regs->qup_state, QUP_STATE_VALID_MASK, QUP_STATE_VALID, 1); } /* - * Configure GSBIn Core state + * Configure QUPn Core state */ static int config_spi_state(struct ipq_spi_slave *ds, unsigned int state) { uint32_t val; - int ret; - uint32_t new_state; + int ret = SUCCESS; ret = check_qup_state_valid(ds); if (ret != SUCCESS) return ret; switch (state) { - case SPI_RUN_STATE: - new_state = QUP_STATE_RUN_STATE; + case QUP_STATE_RUN: + /* Set the state to RUN */ + val = ((read32(ds->regs->qup_state) & ~QUP_STATE_MASK) + | QUP_STATE_RUN); + write32(ds->regs->qup_state, val); + ret = check_qup_state_valid(ds); break; - - case SPI_RESET_STATE: - new_state = QUP_STATE_RESET_STATE; - break; - - case SPI_PAUSE_STATE: - new_state = QUP_STATE_PAUSE_STATE; + case QUP_STATE_RESET: + /* Set the state to RESET */ + val = ((read32(ds->regs->qup_state) & ~QUP_STATE_MASK) + | QUP_STATE_RESET); + write32(ds->regs->qup_state, val); + ret = check_qup_state_valid(ds); break; - default: - printk(BIOS_ERR, - "err: unsupported GSBI SPI state : %d\n", state); - return -EINVAL; + printk(BIOS_ERR, "unsupported QUP SPI state : %d\n", state); + ret = -EINVAL; + break; } - /* Set the state as requested */ - val = (readl_i(ds->regs->qup_state) & ~QUP_STATE_MASK) - | new_state; - writel_i(val, ds->regs->qup_state); - return check_qup_state_valid(ds); + return ret; } /* - * Set GSBIn SPI Mode + * Set QUPn SPI Mode */ static void spi_set_mode(struct ipq_spi_slave *ds, unsigned int mode) { @@ -246,199 +154,38 @@ static void spi_set_mode(struct ipq_spi_slave *ds, unsigned int mode) uint32_t val; switch (mode) { - case GSBI_SPI_MODE_0: + case SPI_MODE0: clk_idle_state = 0; - input_first_mode = SPI_INPUT_FIRST_MODE; + input_first_mode = SPI_CONFIG_INPUT_FIRST; break; - case GSBI_SPI_MODE_1: + case SPI_MODE1: clk_idle_state = 0; input_first_mode = 0; break; - case GSBI_SPI_MODE_2: + case SPI_MODE2: clk_idle_state = 1; - input_first_mode = SPI_INPUT_FIRST_MODE; + input_first_mode = SPI_CONFIG_INPUT_FIRST; break; - case GSBI_SPI_MODE_3: + case SPI_MODE3: clk_idle_state = 1; input_first_mode = 0; break; default: - printk(BIOS_ERR, - "err : unsupported spi mode : %d\n", mode); + printk(BIOS_ERR, "unsupported spi mode : %d\n", mode); return; } - val = readl_i(ds->regs->spi_config); + val = read32(ds->regs->spi_config); val |= input_first_mode; - writel_i(val, ds->regs->spi_config); + write32(ds->regs->spi_config, val); - val = readl_i(ds->regs->io_control); + val = read32(ds->regs->io_control); if (clk_idle_state) - val |= SPI_IO_CONTROL_CLOCK_IDLE_HIGH; + val |= SPI_IO_CTRL_CLOCK_IDLE_HIGH; else - val &= ~SPI_IO_CONTROL_CLOCK_IDLE_HIGH; - - writel_i(val, ds->regs->io_control); -} + val &= ~SPI_IO_CTRL_CLOCK_IDLE_HIGH; -/* - * Check for HCLK state - */ -static int check_hclk_state(unsigned int core_num, int enable) -{ - return check_bit_state(CLK_HALT_CFPB_STATEB_REG, - hclk_state[core_num], enable, 5); -} - -/* - * Check for QUP APPS CLK state - */ -static int check_qup_clk_state(unsigned int core_num, int enable) -{ - return check_bit_state(CLK_HALT_CFPB_STATEB_REG, - qup_apps_clk_state[core_num], enable, 5); -} - -/* - * Function to assert and De-assert chip select - */ -static void CS_change(int port_num, int cs_num, int enable) -{ - unsigned int cs_gpio = cs_gpio_array[port_num][cs_num]; - void *addr = GPIO_IN_OUT_ADDR(cs_gpio); - uint32_t val = readl_i(addr); - - val &= (~(1 << GPIO_OUTPUT)); - if (!enable) - val |= (1 << GPIO_OUTPUT); - write32(addr, val); -} - -/* - * GSBIn TLMM configuration - */ -static void gsbi_pin_config(unsigned int port_num, int cs_num) -{ - unsigned int gpio; - unsigned int i; - /* Hold the GSBIn (core_num) core in reset */ - clrsetbits_le32_i(GSBIn_RESET_REG(GSBI_IDX_TO_GSBI(port_num)), - GSBI1_RESET_MSK, GSBI1_RESET); - - /* - * Configure SPI_CLK, SPI_MISO and SPI_MOSI - */ - for (i = 0; i < NUM_GSBI_PINS; i++) { - unsigned int func_sel; - unsigned int io_config; - unsigned int pull_config; - unsigned int drv_strength; - unsigned int gpio_en; - unsigned int *ptr; - - ptr = gsbi_pin_conf[port_num][i]; - gpio = *(ptr + GSBI_PIN_IDX); - func_sel = *(ptr + FUNC_SEL_IDX); - io_config = *(ptr + GPIO_DIR_IDX); - pull_config = *(ptr + PULL_CONF_IDX); - drv_strength = *(ptr + DRV_STR_IDX); - gpio_en = *(ptr + GPIO_EN_IDX); - - gpio_tlmm_config(gpio, func_sel, io_config, - pull_config, drv_strength, gpio_en); - } - - gpio = cs_gpio_array[port_num][cs_num]; - /* configure CS */ - gpio_tlmm_config(gpio, FUNC_SEL_GPIO, GPIO_OUTPUT, GPIO_PULL_UP, - GPIO_DRV_STR_10MA, GPIO_FUNC_ENABLE); - CS_change(port_num, cs_num, CS_DEASSERT); -} - -/* - * Clock configuration for GSBIn Core - */ -static int gsbi_clock_init(struct ipq_spi_slave *ds) -{ - int ret; - - /* Hold the GSBIn (core_num) core in reset */ - clrsetbits_le32_i(GSBIn_RESET_REG(GSBI_IDX_TO_GSBI(ds->slave.bus)), - GSBI1_RESET_MSK, GSBI1_RESET); - - /* Disable GSBIn (core_num) QUP core clock branch */ - clrsetbits_le32_i(ds->regs->qup_ns_reg, QUP_CLK_BRANCH_ENA_MSK, - QUP_CLK_BRANCH_DIS); - - ret = check_qup_clk_state(ds->slave.bus, 1); - if (ret) { - printk(BIOS_ERR, - "QUP Clock Halt For GSBI%d failed!\n", ds->slave.bus); - return ret; - } - - /* Disable M/N:D counter and hold M/N:D counter in reset */ - clrsetbits_le32_i(ds->regs->qup_ns_reg, (MNCNTR_MSK | MNCNTR_RST_MSK), - (MNCNTR_RST_ENA | MNCNTR_DIS)); - - /* Disable GSBIn (core_num) QUP core clock root */ - clrsetbits_le32_i(ds->regs->qup_ns_reg, CLK_ROOT_ENA_MSK, CLK_ROOT_DIS); - - clrsetbits_le32_i(ds->regs->qup_ns_reg, GSBIn_PLL_SRC_MSK, - GSBIn_PLL_SRC_PLL8); - clrsetbits_le32_i(ds->regs->qup_ns_reg, GSBIn_PRE_DIV_SEL_MSK, - (0 << GSBI_PRE_DIV_SEL_SHFT)); - - /* Program M/N:D values for GSBIn_QUP_APPS_CLK @50MHz */ - clrsetbits_le32_i(ds->regs->qup_md_reg, GSBIn_M_VAL_MSK, - (0x01 << GSBI_M_VAL_SHFT)); - clrsetbits_le32_i(ds->regs->qup_md_reg, GSBIn_D_VAL_MSK, - (0xF7 << GSBI_D_VAL_SHFT)); - clrsetbits_le32_i(ds->regs->qup_ns_reg, GSBIn_N_VAL_MSK, - (0xF8 << GSBI_N_VAL_SHFT)); - - /* Set MNCNTR_MODE = 0: Bypass mode */ - clrsetbits_le32_i(ds->regs->qup_ns_reg, MNCNTR_MODE_MSK, - MNCNTR_MODE_DUAL_EDGE); - - /* De-assert the M/N:D counter reset */ - clrsetbits_le32_i(ds->regs->qup_ns_reg, MNCNTR_RST_MSK, MNCNTR_RST_DIS); - clrsetbits_le32_i(ds->regs->qup_ns_reg, MNCNTR_MSK, MNCNTR_EN); - - /* - * Enable the GSBIn (core_num) QUP core clock root. - * Keep MND counter disabled - */ - clrsetbits_le32_i(ds->regs->qup_ns_reg, CLK_ROOT_ENA_MSK, CLK_ROOT_ENA); - - /* Enable GSBIn (core_num) QUP core clock branch */ - clrsetbits_le32_i(ds->regs->qup_ns_reg, QUP_CLK_BRANCH_ENA_MSK, - QUP_CLK_BRANCH_ENA); - - ret = check_qup_clk_state(ds->slave.bus, 0); - if (ret) { - printk(BIOS_ERR, - "QUP Clock Enable For GSBI%d failed!\n", ds->slave.bus); - return ret; - } - - /* Enable GSBIn (core_num) core clock branch */ - clrsetbits_le32_i(GSBIn_HCLK_CTL_REG(GSBI_IDX_TO_GSBI(ds->slave.bus)), - GSBI_CLK_BRANCH_ENA_MSK, GSBI_CLK_BRANCH_ENA); - - ret = check_hclk_state(ds->slave.bus, 0); - if (ret) { - printk(BIOS_ERR, - "HCLK Enable For GSBI%d failed!\n", ds->slave.bus); - return ret; - } - - /* Release GSBIn (core_num) core from reset */ - clrsetbits_le32_i(GSBIn_RESET_REG(GSBI_IDX_TO_GSBI(ds->slave.bus)), - GSBI1_RESET_MSK, 0); - udelay(50); - - return SUCCESS; + write32(ds->regs->io_control, val); } /* @@ -446,75 +193,11 @@ static int gsbi_clock_init(struct ipq_spi_slave *ds) */ static void spi_reset(struct ipq_spi_slave *ds) { - writel_i(0x1, ds->regs->qup_sw_reset); + write32(ds->regs->qup_sw_reset, 0x1); udelay(5); + check_qup_state_valid(ds); } -static const struct gsbi_spi spi_reg[] = { - /* GSBI5 registers for SPI interface */ - { - GSBI5_SPI_CONFIG_REG, - GSBI5_SPI_IO_CONTROL_REG, - GSBI5_SPI_ERROR_FLAGS_REG, - GSBI5_SPI_ERROR_FLAGS_EN_REG, - GSBI5_GSBI_CTRL_REG_REG, - GSBI5_QUP_CONFIG_REG, - GSBI5_QUP_ERROR_FLAGS_REG, - GSBI5_QUP_ERROR_FLAGS_EN_REG, - GSBI5_QUP_OPERATIONAL_REG, - GSBI5_QUP_IO_MODES_REG, - GSBI5_QUP_STATE_REG, - GSBI5_QUP_INPUT_FIFOc_REG(0), - GSBI5_QUP_OUTPUT_FIFOc_REG(0), - GSBI5_QUP_MX_INPUT_COUNT_REG, - GSBI5_QUP_MX_OUTPUT_COUNT_REG, - GSBI5_QUP_SW_RESET_REG, - GSBIn_QUP_APPS_NS_REG(5), - GSBIn_QUP_APPS_MD_REG(5), - }, - /* GSBI6 registers for SPI interface */ - { - GSBI6_SPI_CONFIG_REG, - GSBI6_SPI_IO_CONTROL_REG, - GSBI6_SPI_ERROR_FLAGS_REG, - GSBI6_SPI_ERROR_FLAGS_EN_REG, - GSBI6_GSBI_CTRL_REG_REG, - GSBI6_QUP_CONFIG_REG, - GSBI6_QUP_ERROR_FLAGS_REG, - GSBI6_QUP_ERROR_FLAGS_EN_REG, - GSBI6_QUP_OPERATIONAL_REG, - GSBI6_QUP_IO_MODES_REG, - GSBI6_QUP_STATE_REG, - GSBI6_QUP_INPUT_FIFOc_REG(0), - GSBI6_QUP_OUTPUT_FIFOc_REG(0), - GSBI6_QUP_MX_INPUT_COUNT_REG, - GSBI6_QUP_MX_OUTPUT_COUNT_REG, - GSBI6_QUP_SW_RESET_REG, - GSBIn_QUP_APPS_NS_REG(6), - GSBIn_QUP_APPS_MD_REG(6), - }, - /* GSBI7 registers for SPI interface */ - { - GSBI7_SPI_CONFIG_REG, - GSBI7_SPI_IO_CONTROL_REG, - GSBI7_SPI_ERROR_FLAGS_REG, - GSBI7_SPI_ERROR_FLAGS_EN_REG, - GSBI7_GSBI_CTRL_REG_REG, - GSBI7_QUP_CONFIG_REG, - GSBI7_QUP_ERROR_FLAGS_REG, - GSBI7_QUP_ERROR_FLAGS_EN_REG, - GSBI7_QUP_OPERATIONAL_REG, - GSBI7_QUP_IO_MODES_REG, - GSBI7_QUP_STATE_REG, - GSBI7_QUP_INPUT_FIFOc_REG(0), - GSBI7_QUP_OUTPUT_FIFOc_REG(0), - GSBI7_QUP_MX_INPUT_COUNT_REG, - GSBI7_QUP_MX_OUTPUT_COUNT_REG, - GSBI7_QUP_SW_RESET_REG, - GSBIn_QUP_APPS_NS_REG(7), - GSBIn_QUP_APPS_MD_REG(7), - } -}; static struct ipq_spi_slave spi_slave_pool[2]; void spi_init(void) @@ -528,17 +211,13 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs) struct ipq_spi_slave *ds = NULL; int i; - /* - * IPQ GSBI (Generic Serial Bus Interface) supports SPI Flash - * on different GSBI5, GSBI6 and GSBI7 - * with different number of chip selects (CS, channels): - */ - if ((bus < GSBI5_SPI) || (bus > GSBI7_SPI) - || ((bus == GSBI5_SPI) && (cs > 3)) - || ((bus == GSBI6_SPI) && (cs > 0)) - || ((bus == GSBI7_SPI) && (cs > 0))) { - printk(BIOS_ERR, "SPI error: unsupported bus %d (Supported busses 0,1 and 2) or chipselect\n", - bus); + if ((bus < BLSP0_SPI) || (bus > BLSP1_SPI) + || ((bus == BLSP0_SPI) && (cs > 2)) + || ((bus == BLSP1_SPI) && (cs > 0))) { + printk(BIOS_ERR, + "SPI error: unsupported bus %d (Supported busses 0, 1 and 2) " + "or chipselect\n", bus); + return NULL; } for (i = 0; i < ARRAY_SIZE(spi_slave_pool); i++) { @@ -555,7 +234,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs) * to configure this */ ds->freq = 10000000; - ds->mode = GSBI_SPI_MODE_0; + ds->mode = SPI_MODE3; ds->allocated = 1; return &ds->slave; @@ -566,48 +245,43 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs) } /* - * GSBIn SPI Hardware Initialisation + * BLSP QUPn SPI Hardware Initialisation */ static int spi_hw_init(struct ipq_spi_slave *ds) { int ret; - if (ds->initialized) - return 0; + ds->initialized = 0; - /* GSBI module configuration */ + /* QUPn module configuration */ spi_reset(ds); - /* Set the GSBIn QUP state */ - ret = config_spi_state(ds, SPI_RESET_STATE); + /* Set the QUPn state */ + ret = config_spi_state(ds, QUP_STATE_RESET); if (ret) return ret; - /* Configure GSBI_CTRL register to set protocol_mode to SPI:011 */ - clrsetbits_le32_i(ds->regs->gsbi_ctrl, PROTOCOL_CODE_MSK, - PROTOCOL_CODE_SPI); - /* * Configure Mini core to SPI core with Input Output enabled, * SPI master, N = 8 bits */ - clrsetbits_le32_i(ds->regs->qup_config, (QUP_CONFIG_MINI_CORE_MSK | - SPI_QUP_CONF_INPUT_MSK | - SPI_QUP_CONF_OUTPUT_MSK | - SPI_BIT_WORD_MSK), - (QUP_CONFIG_MINI_CORE_SPI | - SPI_QUP_CONF_NO_INPUT | - SPI_QUP_CONF_NO_OUTPUT | - SPI_8_BIT_WORD)); + clrsetbits_le32(ds->regs->qup_config, QUP_CONFIG_MINI_CORE_MSK | + QUP_CONF_INPUT_MSK | + QUP_CONF_OUTPUT_MSK | + QUP_CONF_N_MASK, + QUP_CONFIG_MINI_CORE_SPI | + QUP_CONF_INPUT_ENA | + QUP_CONF_OUTPUT_ENA | + QUP_CONF_N_SPI_8_BIT_WORD); /* * Configure Input first SPI protocol, * SPI master mode and no loopback */ - clrsetbits_le32_i(ds->regs->spi_config, (LOOP_BACK_MSK | - SLAVE_OPERATION_MSK), - (NO_LOOP_BACK | - SLAVE_OPERATION)); + clrsetbits_le32(ds->regs->spi_config, SPI_CONFIG_LOOP_BACK_MSK | + SPI_CONFIG_NO_SLAVE_OPER_MSK, + SPI_CONFIG_NO_LOOP_BACK | + SPI_CONFIG_NO_SLAVE_OPER); /* * Configure SPI IO Control Register @@ -615,8 +289,8 @@ static int spi_hw_init(struct ipq_spi_slave *ds) * MX_CS_MODE = 0 * NO_TRI_STATE = 1 */ - writel_i((CLK_ALWAYS_ON | MX_CS_MODE | NO_TRI_STATE), - ds->regs->io_control); + write32(ds->regs->io_control, SPI_IO_CTRL_CLK_ALWAYS_ON | + SPI_IO_CTRL_NO_TRI_STATE); /* * Configure SPI IO Modes. @@ -624,18 +298,21 @@ static int spi_hw_init(struct ipq_spi_slave *ds) * INPUT_MODE = Block Mode * OUTPUT MODE = Block Mode */ - clrsetbits_le32_i(ds->regs->qup_io_modes, (OUTPUT_BIT_SHIFT_MSK | - INPUT_BLOCK_MODE_MSK | - OUTPUT_BLOCK_MODE_MSK), - (OUTPUT_BIT_SHIFT_EN | - INPUT_BLOCK_MODE | - OUTPUT_BLOCK_MODE)); + clrsetbits_le32(ds->regs->qup_io_modes, + QUP_IO_MODES_OUTPUT_BIT_SHIFT_MSK | + QUP_IO_MODES_INPUT_MODE_MSK | + QUP_IO_MODES_OUTPUT_MODE_MSK, + QUP_IO_MODES_OUTPUT_BIT_SHIFT_EN | + QUP_IO_MODES_INPUT_BLOCK_MODE | + QUP_IO_MODES_OUTPUT_BLOCK_MODE); spi_set_mode(ds, ds->mode); /* Disable Error mask */ - writel_i(0, ds->regs->error_flags_en); - writel_i(0, ds->regs->qup_error_flags_en); + write32(ds->regs->error_flags_en, 0); + write32(ds->regs->qup_error_flags_en, 0); + + write32(ds->regs->qup_deassert_wait, 0); ds->initialized = 1; @@ -647,17 +324,6 @@ int spi_claim_bus(struct spi_slave *slave) struct ipq_spi_slave *ds = to_ipq_spi(slave); unsigned int ret; - if (ds->initialized) - return SUCCESS; - - /* GPIO Configuration for SPI port */ - gsbi_pin_config(ds->slave.bus, ds->slave.cs); - - /* Clock configuration */ - ret = gsbi_clock_init(ds); - if (ret) - return ret; - ret = spi_hw_init(ds); if (ret) return -EIO; @@ -674,60 +340,92 @@ void spi_release_bus(struct spi_slave *slave) ds->initialized = 0; } -static int spi_xfer_tx_packet(struct ipq_spi_slave *ds, - const uint8_t *dout, unsigned out_bytes) +static void write_force_cs(struct spi_slave *slave, int assert) { - int ret; + struct ipq_spi_slave *ds = to_ipq_spi(slave); - writel_i(out_bytes, ds->regs->qup_mx_output_count); + if (assert) + clrsetbits_le32(ds->regs->io_control, + SPI_IO_CTRL_FORCE_CS_MSK, SPI_IO_CTRL_FORCE_CS_EN); + else + clrsetbits_le32(ds->regs->io_control, + SPI_IO_CTRL_FORCE_CS_MSK, SPI_IO_CTRL_FORCE_CS_DIS); - ret = config_spi_state(ds, SPI_RUN_STATE); - if (ret) - return ret; + return; +} - while (out_bytes) { - if (readl_i(ds->regs->qup_operational) & QUP_OUTPUT_FIFO_FULL) - continue; +/* + * Function to write data to OUTPUT FIFO + */ +static void spi_write_byte(struct ipq_spi_slave *ds, unsigned char data) +{ + /* Wait for space in the FIFO */ + while ((read32(ds->regs->qup_operational) & OUTPUT_FIFO_FULL)) + udelay(1); - writel_i(*dout++, ds->regs->qup_output_fifo); - out_bytes--; + /* Write the byte of data */ + write32(ds->regs->qup_output_fifo, data); +} - /* Wait for output FIFO to drain. */ - if (!out_bytes) - while (readl_i(ds->regs->qup_operational) & - QUP_OUTPUT_FIFO_NOT_EMPTY) - ; - } +/* + * Function to read data from Input FIFO + */ +static unsigned char spi_read_byte(struct ipq_spi_slave *ds) +{ + /* Wait for Data in FIFO */ + while (!(read32(ds->regs->qup_operational) & INPUT_FIFO_NOT_EMPTY)) + udelay(1); - return config_spi_state(ds, SPI_RESET_STATE); + /* Read a byte of data */ + return read32(ds->regs->qup_input_fifo) & 0xff; } -static int spi_xfer_rx_packet(struct ipq_spi_slave *ds, - uint8_t *din, unsigned in_bytes) +/* + * Function to check wheather Input or Output FIFO + * has data to be serviced + */ +static int check_fifo_status(void *reg_addr) { - int ret; + unsigned int count = TIMEOUT_CNT; + unsigned int status_flag; + unsigned int val; - writel_i(in_bytes, ds->regs->qup_mx_input_count); - writel_i(in_bytes, ds->regs->qup_mx_output_count); + do { + val = read32(reg_addr); + count--; + if (count == 0) + return -ETIMEDOUT; + status_flag = ((val & OUTPUT_SERVICE_FLAG) | + (val & INPUT_SERVICE_FLAG)); + } while (!status_flag); - ret = config_spi_state(ds, SPI_RUN_STATE); - if (ret) - return ret; + return SUCCESS; +} - /* Seed clocking */ - writel_i(0xff, ds->regs->qup_output_fifo); - while (in_bytes) { - if (!(readl_i(ds->regs->qup_operational) & - QUP_INPUT_FIFO_NOT_EMPTY)) - continue; - /* Keep it clocking */ - writel_i(0xff, ds->regs->qup_output_fifo); +/* + * Function to configure Input and Output enable/disable + */ +static void enable_io_config(struct ipq_spi_slave *ds, + uint32_t write_cnt, uint32_t read_cnt) +{ - *din++ = readl_i(ds->regs->qup_input_fifo) & 0xff; - in_bytes--; + if (write_cnt) { + clrsetbits_le32(ds->regs->qup_config, + QUP_CONF_OUTPUT_MSK, QUP_CONF_OUTPUT_ENA); + } else { + clrsetbits_le32(ds->regs->qup_config, + QUP_CONF_OUTPUT_MSK, QUP_CONF_NO_OUTPUT); } - return config_spi_state(ds, SPI_RESET_STATE); + if (read_cnt) { + clrsetbits_le32(ds->regs->qup_config, + QUP_CONF_INPUT_MSK, QUP_CONF_INPUT_ENA); + } else { + clrsetbits_le32(ds->regs->qup_config, + QUP_CONF_INPUT_MSK, QUP_CONF_NO_INPUT); + } + + return; } unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len) @@ -735,71 +433,239 @@ unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len) return min(MAX_PACKET_COUNT, buf_len); } -int spi_xfer(struct spi_slave *slave, const void *dout, - unsigned out_bytes, void *din, unsigned in_bytes) +/* + * Function to read bytes number of data from the Input FIFO + */ +static int __blsp_spi_read(struct ipq_spi_slave *ds, u8 *data_buffer, + unsigned int bytes) { - int ret; - struct ipq_spi_slave *ds = to_ipq_spi(slave); + uint32_t val; + unsigned int i; + unsigned int fifo_count; + int ret = SUCCESS; + int state_config; + + /* Configure no of bytes to read */ + state_config = config_spi_state(ds, QUP_STATE_RESET); + if (state_config) + return state_config; + + /* Configure input and output enable */ + enable_io_config(ds, 0, bytes); + + write32(ds->regs->qup_mx_input_count, bytes); + + state_config = config_spi_state(ds, QUP_STATE_RUN); + if (state_config) + return state_config; + + while (bytes) { + ret = check_fifo_status(ds->regs->qup_operational); + if (ret != SUCCESS) + goto out; + + val = read32(ds->regs->qup_operational); + if (val & INPUT_SERVICE_FLAG) { + /* + * acknowledge to hw that software will + * read input data + */ + val &= INPUT_SERVICE_FLAG; + write32(ds->regs->qup_operational, val); + + fifo_count = ((bytes > SPI_INPUT_BLOCK_SIZE) ? + SPI_INPUT_BLOCK_SIZE : bytes); + + for (i = 0; i < fifo_count; i++) { + *data_buffer = spi_read_byte(ds); + data_buffer++; + bytes--; + } + } + } - /* Assert the chip select */ - CS_change(ds->slave.bus, ds->slave.cs, CS_ASSERT); +out: + /* + * Put the SPI Core back in the Reset State + * to end the transfer + */ + (void)config_spi_state(ds, QUP_STATE_RESET); + return ret; +} - ret = config_spi_state(ds, SPI_RESET_STATE); - if (ret) - goto out; +static int blsp_spi_read(struct ipq_spi_slave *ds, u8 *data_buffer, + unsigned int bytes) +{ + int length, ret; + + while (bytes) { + length = (bytes < MAX_COUNT_SIZE) ? bytes : MAX_COUNT_SIZE; - if (!out_bytes) - goto spi_receive; + ret = __blsp_spi_read(ds, data_buffer, length); + if (ret != SUCCESS) + return ret; + + data_buffer += length; + bytes -= length; + } + + return 0; +} + +/* + * Function to write data to the Output FIFO + */ +static int __blsp_spi_write(struct ipq_spi_slave *ds, const u8 *cmd_buffer, + unsigned int bytes) +{ + uint32_t val; + unsigned int i; + unsigned int write_len = bytes; + unsigned int read_len = bytes; + unsigned int fifo_count; + int ret = SUCCESS; + int state_config; + + state_config = config_spi_state(ds, QUP_STATE_RESET); + if (state_config) + return state_config; + + /* No of bytes to be written in Output FIFO */ + write32(ds->regs->qup_mx_output_count, bytes); + write32(ds->regs->qup_mx_input_count, bytes); + state_config = config_spi_state(ds, QUP_STATE_RUN); + if (state_config) + return state_config; + + /* Configure input and output enable */ + enable_io_config(ds, write_len, read_len); /* - * Let's do the write side of the transaction first. Enable output - * FIFO. + * read_len considered to ensure that we read the dummy data for the + * write we performed. This is needed to ensure with WR-RD transaction + * to get the actual data on the subsequent read cycle that happens */ - clrsetbits_le32_i(ds->regs->qup_config, SPI_QUP_CONF_OUTPUT_MSK, - SPI_QUP_CONF_OUTPUT_ENA); + while (write_len || read_len) { + + ret = check_fifo_status(ds->regs->qup_operational); + if (ret != SUCCESS) + goto out; + + val = read32(ds->regs->qup_operational); + if (val & OUTPUT_SERVICE_FLAG) { + /* + * acknowledge to hw that software will write + * expected output data + */ + val &= OUTPUT_SERVICE_FLAG; + write32(ds->regs->qup_operational, val); + + if (write_len > SPI_OUTPUT_BLOCK_SIZE) + fifo_count = SPI_OUTPUT_BLOCK_SIZE; + else + fifo_count = write_len; + + for (i = 0; i < fifo_count; i++) { + /* Write actual data to output FIFO */ + spi_write_byte(ds, *cmd_buffer); + cmd_buffer++; + write_len--; + } + } + if (val & INPUT_SERVICE_FLAG) { + /* + * acknowledge to hw that software + * will read input data + */ + val &= INPUT_SERVICE_FLAG; + write32(ds->regs->qup_operational, val); + + if (read_len > SPI_INPUT_BLOCK_SIZE) + fifo_count = SPI_INPUT_BLOCK_SIZE; + else + fifo_count = read_len; + + for (i = 0; i < fifo_count; i++) { + /* Read dummy data for the data written */ + (void)spi_read_byte(ds); + + /* Decrement the read count after reading the + * dummy data from the device. This is to make + * sure we read dummy data before we write the + * data to fifo + */ + read_len--; + } + } + } + +out: + /* + * Put the SPI Core back in the Reset State + * to end the transfer + */ + (void)config_spi_state(ds, QUP_STATE_RESET); + + return ret; +} + +static int blsp_spi_write(struct ipq_spi_slave *ds, u8 *cmd_buffer, + unsigned int bytes) +{ + int length, ret; - while (out_bytes) { - unsigned todo = MIN(out_bytes, MAX_PACKET_COUNT); + while (bytes) { + length = (bytes < MAX_COUNT_SIZE) ? bytes : MAX_COUNT_SIZE; - ret = spi_xfer_tx_packet(ds, dout, todo); - if (ret) - break; + ret = __blsp_spi_write(ds, cmd_buffer, length); + if (ret != SUCCESS) + return ret; - out_bytes -= todo; - dout += todo; + cmd_buffer += length; + bytes -= length; } - if (ret) - goto out; + return 0; +} -spi_receive: - if (!in_bytes) /* Nothing to read. */ - goto out; +/* + * This function is invoked with either tx_buf or rx_buf. + * Calling this function with both null does a chip select change. + */ +int spi_xfer(struct spi_slave *slave, const void *dout, + unsigned out_bytes, void *din, unsigned in_bytes) +{ + struct ipq_spi_slave *ds = to_ipq_spi(slave); + u8 *txp = (u8 *)dout; + u8 *rxp = (u8 *)din; + int ret; - /* Enable input FIFO */ - clrsetbits_le32_i(ds->regs->qup_config, SPI_QUP_CONF_INPUT_MSK, - SPI_QUP_CONF_INPUT_ENA); + ret = config_spi_state(ds, QUP_STATE_RESET); + if (ret != SUCCESS) + return ret; - while (in_bytes) { - unsigned todo = MIN(in_bytes, MAX_PACKET_COUNT); + write_force_cs(slave, 1); - ret = spi_xfer_rx_packet(ds, din, todo); - if (ret) - break; + if (dout != NULL) { + ret = blsp_spi_write(ds, txp, (unsigned int) out_bytes); + if (ret != SUCCESS) + goto out; + } - in_bytes -= todo; - din += todo; + if (din != NULL) { + ret = blsp_spi_read(ds, rxp, in_bytes); + if (ret != SUCCESS) + goto out; } out: - /* Deassert CS */ - CS_change(ds->slave.bus, ds->slave.cs, CS_DEASSERT); + write_force_cs(slave, 0); /* * Put the SPI Core back in the Reset State * to end the transfer */ - (void)config_spi_state(ds, SPI_RESET_STATE); + (void)config_spi_state(ds, QUP_STATE_RESET); return ret; } -- cgit v1.2.3