diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2014-04-10 14:20:39 -0700 |
---|---|---|
committer | Marc Jones <marc.jones@se-eng.com> | 2014-12-30 22:09:49 +0100 |
commit | 3afa03e995c39aa60e23f3af2714a51abe54e948 (patch) | |
tree | ff07216bf0f17a379774fde50c591208fb155452 /src/soc/qualcomm/ipq806x/spi.c | |
parent | 6a0982e5b6926fe626baf3944542aeda213cd821 (diff) |
ipq8064: copy u-boot spi driver as is
This brings in the banana_cs version of the SPI driver.
BUG=chrome-os-partner:27784
TEST=none
Original-Change-Id: Ie93ec8c962c26fff1f0a235516cd8a4062cab40b
Original-Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/194225
Original-Reviewed-by: David Hendricks <dhendrix@chromium.org>
(cherry picked from commit 3cada6e4ed51a6d4f637aa31a1a836352a99d13d)
Signed-off-by: Marc Jones <marc.jones@se-eng.com>
Change-Id: I0a58a4ddaf9375c22c9b2b249a2baa2c5538ba6c
Reviewed-on: http://review.coreboot.org/7982
Tested-by: build bot (Jenkins)
Reviewed-by: David Hendricks <dhendrix@chromium.org>
Diffstat (limited to 'src/soc/qualcomm/ipq806x/spi.c')
-rw-r--r-- | src/soc/qualcomm/ipq806x/spi.c | 924 |
1 files changed, 924 insertions, 0 deletions
diff --git a/src/soc/qualcomm/ipq806x/spi.c b/src/soc/qualcomm/ipq806x/spi.c new file mode 100644 index 0000000000..45ecc0e079 --- /dev/null +++ b/src/soc/qualcomm/ipq806x/spi.c @@ -0,0 +1,924 @@ +/* + * Copyright (c) 2012 The Linux Foundation. All rights reserved. + */ + +#include <common.h> +#include <spi.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include "ipq_spi.h" +#include <asm/arch-ipq806x/gpio.h> +#include <asm/arch-ipq806x/iomap.h> + +#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 + +#define GSBI_IDX_TO_GSBI(idx) (idx + 5) + +/* + * 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] = { + { + /* 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 + } + }, + { + /* 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 + } + }, + { + /* 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, int val, int us_delay) +{ + unsigned int count = TIMEOUT_CNT; + unsigned int bit_val = ((readl(reg_addr) >> bit_num) & 0x01); + + while (bit_val != val) { + count--; + if (count == 0) + return -ETIMEDOUT; + udelay(us_delay); + bit_val = ((readl(reg_addr) >> bit_num) & 0x01); + } + + return SUCCESS; +} + +/* + * Check whether GSBIn_QUP 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, + QUP_STATE_VALID, 1); + +} + +/* + * Configure GSBIn Core state + */ +static int config_spi_state(struct ipq_spi_slave *ds, unsigned int state) +{ + uint32_t val; + int ret = SUCCESS; + + ret = check_qup_state_valid(ds); + if (ret != SUCCESS) + return ret; + + switch (state) { + case SPI_RUN_STATE: + /* Set the state to RUN */ + val = ((readl(ds->regs->qup_state) & ~QUP_STATE_MASK) + | QUP_STATE_RUN_STATE); + writel(val, ds->regs->qup_state); + ret = check_qup_state_valid(ds); + if (ret != SUCCESS) + return ret; + ds->core_state = SPI_CORE_RUNNING; + break; + case SPI_RESET_STATE: + /* Set the state to RESET */ + val = ((readl(ds->regs->qup_state) & ~QUP_STATE_MASK) + | QUP_STATE_RESET_STATE); + writel(val, ds->regs->qup_state); + ret = check_qup_state_valid(ds); + if (ret != SUCCESS) + return ret; + ds->core_state = SPI_CORE_RESET; + break; + default: + printf("err: unsupported GSBI SPI state : %d\n", state); + ret = -EINVAL; + break; + } + + return ret; +} + +/* + * Set GSBIn SPI Mode + */ +static void spi_set_mode(struct ipq_spi_slave *ds, unsigned int mode) +{ + unsigned int clk_idle_state; + unsigned int input_first_mode; + uint32_t val; + + switch (mode) { + case GSBI_SPI_MODE_0: + clk_idle_state = 0; + input_first_mode = SPI_INPUT_FIRST_MODE; + break; + case GSBI_SPI_MODE_1: + clk_idle_state = 0; + input_first_mode = 0; + break; + case GSBI_SPI_MODE_2: + clk_idle_state = 1; + input_first_mode = SPI_INPUT_FIRST_MODE; + break; + case GSBI_SPI_MODE_3: + clk_idle_state = 1; + input_first_mode = 0; + break; + default: + printf("err : unsupported spi mode : %d\n", mode); + return; + } + + val = readl(ds->regs->spi_config); + val |= input_first_mode; + writel(val, ds->regs->spi_config); + + val = readl(ds->regs->io_control); + if (clk_idle_state) + val |= SPI_IO_CONTROL_CLOCK_IDLE_HIGH; + else + val &= ~SPI_IO_CONTROL_CLOCK_IDLE_HIGH; + + writel(val, ds->regs->io_control); +} + +/* + * Check for HCLK state + */ +static int check_hclk_state(unsigned int core_num, int enable) +{ + if (clk_is_dummy()) + return 0; + + 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) +{ + if (clk_is_dummy()) + return 0; + + 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]; + uint32_t addr = GPIO_IN_OUT_ADDR(cs_gpio); + uint32_t val = readl(addr); + + val &= (~(1 << GPIO_OUT)); + if (!enable) + val |= (1 << GPIO_OUT); + writel(val, addr); +} + +/* + * 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(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(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(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) { + printf("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(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(ds->regs->qup_ns_reg, CLK_ROOT_ENA_MSK, CLK_ROOT_DIS); + + clrsetbits_le32(ds->regs->qup_ns_reg, GSBIn_PLL_SRC_MSK, + GSBIn_PLL_SRC_PLL8); + clrsetbits_le32(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(ds->regs->qup_md_reg, GSBIn_M_VAL_MSK, + (0x01 << GSBI_M_VAL_SHFT)); + clrsetbits_le32(ds->regs->qup_md_reg, GSBIn_D_VAL_MSK, + (0xF7 << GSBI_D_VAL_SHFT)); + clrsetbits_le32(ds->regs->qup_ns_reg, GSBIn_N_VAL_MSK, + (0xF8 << GSBI_N_VAL_SHFT)); + + /* Set MNCNTR_MODE = 0: Bypass mode */ + clrsetbits_le32(ds->regs->qup_ns_reg, MNCNTR_MODE_MSK, + MNCNTR_MODE_DUAL_EDGE); + + /* De-assert the M/N:D counter reset */ + clrsetbits_le32(ds->regs->qup_ns_reg, MNCNTR_RST_MSK, MNCNTR_RST_DIS); + clrsetbits_le32(ds->regs->qup_ns_reg, MNCNTR_MSK, MNCNTR_EN); + + /* + * Enable the GSBIn (core_num) QUP core clock root. + * Keep MND counter disabled + */ + clrsetbits_le32(ds->regs->qup_ns_reg, CLK_ROOT_ENA_MSK, CLK_ROOT_ENA); + + /* Enable GSBIn (core_num) QUP core clock branch */ + clrsetbits_le32(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) { + printf("QUP Clock Enable For GSBI%d" + " failed!\n", ds->slave.bus); + return ret; + } + + /* Enable GSBIn (core_num) core clock branch */ + clrsetbits_le32(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) { + printf("HCLK Enable For GSBI%d failed!\n", ds->slave.bus); + return ret; + } + + /* Release GSBIn (core_num) core from reset */ + clrsetbits_le32(GSBIn_RESET_REG(GSBI_IDX_TO_GSBI(ds->slave.bus)), + GSBI1_RESET_MSK, 0); + udelay(50); + + return SUCCESS; +} + +/* + * Reset entire QUP and all mini cores + */ +static void spi_reset(struct ipq_spi_slave *ds) +{ + writel(0x1, ds->regs->qup_sw_reset); + udelay(5); +} + +void spi_init() +{ + /* do nothing */ + +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct ipq_spi_slave *ds; + + ds = malloc(sizeof(struct ipq_spi_slave)); + if (!ds) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + + /* + * 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))) { + printf("SPI error: unsupported bus %d " + "(Supported busses 0,1 and 2) or chipselect\n", bus); + goto err; + } + ds->slave.bus = bus; + ds->slave.cs = cs; + + ds->regs = &spi_reg[bus]; + + /* TODO For different clock frequency */ + if (max_hz > MSM_GSBI_MAX_FREQ) { + printf("SPI error: unsupported frequency %d Hz " + "Max frequency is %d Hz\n", max_hz, MSM_GSBI_MAX_FREQ); + goto err; + } + ds->freq = max_hz; + + if (mode > GSBI_SPI_MODE_3) { + printf("SPI error: unsupported SPI mode %d\n", mode); + goto err; + } + ds->mode = mode; + + return &ds->slave; + +err: + free(ds); + return NULL; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct ipq_spi_slave *ds = to_ipq_spi(slave); + + if (ds != NULL) + free(ds); +} + +/* + * GSBIn SPI Hardware Initialisation + */ +static int spi_hw_init(struct ipq_spi_slave *ds) +{ + int ret; + + ds->initialized = 0; + + /* GSBI module configuration */ + spi_reset(ds); + + /* Set the GSBIn QUP state */ + ret = config_spi_state(ds, SPI_RESET_STATE); + if (ret) + return ret; + + /* Configure GSBI_CTRL register to set protocol_mode to SPI:011 */ + clrsetbits_le32(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(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_INPUT_ENA | + SPI_QUP_CONF_OUTPUT_ENA | + SPI_8_BIT_WORD)); + + /* + * Configure Input first SPI protocol, + * SPI master mode and no loopback + */ + clrsetbits_le32(ds->regs->spi_config, (LOOP_BACK_MSK | + SLAVE_OPERATION_MSK), + (NO_LOOP_BACK | + SLAVE_OPERATION)); + + /* + * Configure SPI IO Control Register + * CLK_ALWAYS_ON = 0 + * MX_CS_MODE = 0 + * NO_TRI_STATE = 1 + */ + writel((CLK_ALWAYS_ON | MX_CS_MODE | NO_TRI_STATE), + ds->regs->io_control); + + /* + * Configure SPI IO Modes. + * OUTPUT_BIT_SHIFT_EN = 1 + * INPUT_MODE = Block Mode + * OUTPUT MODE = Block Mode + */ + clrsetbits_le32(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)); + + spi_set_mode(ds, ds->mode); + + /* Disable Error mask */ + writel(0, ds->regs->error_flags_en); + writel(0, ds->regs->qup_error_flags_en); + + ds->initialized = 1; + + return SUCCESS; +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct ipq_spi_slave *ds = to_ipq_spi(slave); + unsigned int ret; + + /* 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; + + return SUCCESS; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct ipq_spi_slave *ds = to_ipq_spi(slave); + + /* Reset the SPI hardware */ + spi_reset(ds); + ds->initialized = 0; +} + +/* Drain input fifo + * If input fifo is not empty drain the input FIFO. When the + * input fifo is drained make sure that the output fifo is also + * empty and break when the input fifo is completely drained. + */ +static void flush_fifos(struct ipq_spi_slave *ds) +{ + unsigned int fifo_data; + + while (1) { + if (readl(ds->regs->qup_operational) & + QUP_DATA_AVAILABLE_FOR_READ) { + fifo_data = readl(ds->regs->qup_input_fifo); + } else { + if (!(readl(ds->regs->qup_operational) & + QUP_OUTPUT_FIFO_NOT_EMPTY)) { + if (!(readl(ds->regs->qup_operational) & + QUP_DATA_AVAILABLE_FOR_READ)) + break; + } + } + } + + (void)fifo_data; +} + +/* + * 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 ((readl(ds->regs->qup_operational) & QUP_OUTPUT_FIFO_FULL)) + udelay(1); + + /* Write the byte of data */ + writel(data, ds->regs->qup_output_fifo); +} + +/* + * Function to read data from Input FIFO + */ +static unsigned char spi_read_byte(struct ipq_spi_slave *ds) +{ + /* Wait for Data in FIFO */ + while (!(readl(ds->regs->qup_operational) & + QUP_DATA_AVAILABLE_FOR_READ)) { + udelay(1); + } + + /* Read a byte of data */ + return readl(ds->regs->qup_input_fifo) & 0xff; +} + +/* + * Function to check wheather Input or Output FIFO + * has data to be serviced + */ +static int check_fifo_status(uint32_t reg_addr) +{ + unsigned int count = TIMEOUT_CNT; + unsigned int status_flag; + unsigned int val; + + do { + val = readl(reg_addr); + count--; + if (count == 0) + return -ETIMEDOUT; + status_flag = ((val & OUTPUT_SERVICE_FLAG) | (val & INPUT_SERVICE_FLAG)); + } while (!status_flag); + + return SUCCESS; +} + +/* + * Function to read bytes number of data from the Input FIFO + */ +static int gsbi_spi_read(struct ipq_spi_slave *ds, u8 *data_buffer, + unsigned int bytes, unsigned long flags) +{ + uint32_t val; + unsigned int i; + unsigned int read_bytes = bytes; + unsigned int fifo_count; + int ret = SUCCESS; + int state_config; + + if (flags & SPI_XFER_BEGIN) { + /* Assert chip select */ + CS_change(ds->slave.bus, ds->slave.cs, CS_ASSERT); + } + + /* Configure no of bytes to read */ + state_config = config_spi_state(ds, SPI_RESET_STATE); + if (state_config) + return state_config; + + writel(bytes, ds->regs->qup_mx_output_count); + writel(bytes, ds->regs->qup_mx_input_count); + + state_config = config_spi_state(ds, SPI_RUN_STATE); + if (state_config) + return state_config; + + while (read_bytes) { + + ret = check_fifo_status(ds->regs->qup_operational); + if (ret != SUCCESS) + goto out; + + val = readl(ds->regs->qup_operational); + if (val & INPUT_SERVICE_FLAG) { + /* + * acknowledge to hw that software will + * read input data + */ + val &= INPUT_SERVICE_FLAG; + writel(val, ds->regs->qup_operational); + + fifo_count = ((read_bytes > SPI_INPUT_BLOCK_SIZE) ? + SPI_INPUT_BLOCK_SIZE : read_bytes); + + for (i = 0; i < fifo_count; i++) { + *data_buffer = spi_read_byte(ds); + data_buffer++; + read_bytes--; + } + } + + if (val & OUTPUT_SERVICE_FLAG) { + /* + * acknowledge to hw that software will + * write output data + */ + val &= OUTPUT_SERVICE_FLAG; + writel(val, ds->regs->qup_operational); + + fifo_count = ((read_bytes > SPI_OUTPUT_BLOCK_SIZE) ? + SPI_OUTPUT_BLOCK_SIZE : read_bytes); + + for (i = 0; i < fifo_count; i++) { + /* + * Write dummy data byte for the device + * to shift in actual data. Most of the SPI devices + * accepts dummy data value as 0. In case of any + * other value change DUMMY_DATA_VAL. + */ + spi_write_byte(ds, DUMMY_DATA_VAL); + } + } + } + + if (flags & SPI_XFER_END) { + flush_fifos(ds); + goto out; + } + + return ret; + +out: + /* Deassert CS */ + CS_change(ds->slave.bus, ds->slave.cs, CS_DEASSERT); + + /* + * Put the SPI Core back in the Reset State + * to end the transfer + */ + (void)config_spi_state(ds, SPI_RESET_STATE); + + return ret; + +} + +/* + * Function to write data to the Output FIFO + */ +static int gsbi_spi_write(struct ipq_spi_slave *ds, const u8 *cmd_buffer, + unsigned int bytes, unsigned long flags) +{ + 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; + + if (flags & SPI_XFER_BEGIN) { + /* Select the chip select */ + CS_change(ds->slave.bus, ds->slave.cs, CS_ASSERT); + } + + state_config = config_spi_state(ds, SPI_RESET_STATE); + if (state_config) + return state_config; + + /* No of bytes to be written in Output FIFO */ + writel(bytes, ds->regs->qup_mx_output_count); + writel(bytes, ds->regs->qup_mx_input_count); + state_config = config_spi_state(ds, SPI_RUN_STATE); + if (state_config) + return state_config; + + /* + * 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 + */ + while (write_len || read_len) { + + ret = check_fifo_status(ds->regs->qup_operational); + if (ret != SUCCESS) + goto out; + + val = readl(ds->regs->qup_operational); + if (val & OUTPUT_SERVICE_FLAG) { + /* + * acknowledge to hw that software will write + * expected output data + */ + val &= OUTPUT_SERVICE_FLAG; + writel(val, ds->regs->qup_operational); + + 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; + writel(val, ds->regs->qup_operational); + + 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 write 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--; + } + } + } + + if (flags & SPI_XFER_END) { + flush_fifos(ds); + goto out; + } + + return ret; + +out: + /* Deassert CS */ + CS_change(ds->slave.bus, ds->slave.cs, CS_DEASSERT); + + /* + * Put the SPI Core back in the Reset State + * to end the transfer + */ + (void)config_spi_state(ds, SPI_RESET_STATE); + + return ret; +} + +/* + * 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, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct ipq_spi_slave *ds = to_ipq_spi(slave); + unsigned int len; + const u8 *txp = dout; + u8 *rxp = din; + int ret; + + if (bitlen & 0x07) { + printf("err : Invalid bit length"); + return -EINVAL; + } + + len = bitlen >> 3; + + if (dout != NULL) { + ret = gsbi_spi_write(ds, txp, len, flags); + if (ret != SUCCESS) + return ret; + } + + if (din != NULL) + return gsbi_spi_read(ds, rxp, len, flags); + + if ((din == NULL) && (dout == NULL)) + /* To handle only when chip select change is needed */ + ret = gsbi_spi_write(ds, NULL, 0, flags); + + return ret; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + +} |