diff --git a/src/soc/qualcomm/ipq806x/include/spi.h b/src/soc/qualcomm/ipq806x/include/spi.h new file mode 100644 index 0000000000..2020043c6c --- /dev/null +++ b/src/soc/qualcomm/ipq806x/include/spi.h @@ -0,0 +1,340 @@ +/* + * Register definitions for the IPQ GSBI Controller + * Copyright (c) 2012 The Linux Foundation. All rights reserved. + */ + +#ifndef _IPQ_SPI_H_ +#define _IPQ_SPI_H_ + +#define QUP5_BASE 0x1a280000 +#define QUP6_BASE 0x16580000 +#define QUP7_BASE 0x16680000 + +#define GSBI5_BASE 0x1a200000 +#define GSBI6_BASE 0x16500000 +#define GSBI7_BASE 0x16600000 + +#define GSBI5_QUP5_REG_BASE (QUP5_BASE + 0x00000000) +#define GSBI6_QUP6_REG_BASE (QUP6_BASE + 0x00000000) +#define GSBI7_QUP7_REG_BASE (QUP7_BASE + 0x00000000) + +#define GSBI5_REG_BASE (GSBI5_BASE + 0x00000000) +#define GSBI6_REG_BASE (GSBI6_BASE + 0x00000000) +#define GSBI7_REG_BASE (GSBI7_BASE + 0x00000000) + +#define BOOT_SPI_PORT5_BASE QUP5_BASE +#define BOOT_SPI_PORT6_BASE QUP6_BASE +#define BOOT_SPI_PORT7_BASE QUP7_BASE + +#define GSBI5_SPI_CONFIG_REG (GSBI5_QUP5_REG_BASE + 0x00000300) +#define GSBI6_SPI_CONFIG_REG (GSBI6_QUP6_REG_BASE + 0x00000300) +#define GSBI7_SPI_CONFIG_REG (GSBI7_QUP7_REG_BASE + 0x00000300) + +#define GSBI5_SPI_IO_CONTROL_REG (GSBI5_QUP5_REG_BASE + 0x00000304) +#define GSBI6_SPI_IO_CONTROL_REG (GSBI6_QUP6_REG_BASE + 0x00000304) +#define GSBI7_SPI_IO_CONTROL_REG (GSBI7_QUP7_REG_BASE + 0x00000304) + +#define GSBI5_SPI_ERROR_FLAGS_REG (GSBI5_QUP5_REG_BASE + 0x00000308) +#define GSBI6_SPI_ERROR_FLAGS_REG (GSBI6_QUP6_REG_BASE + 0x00000308) +#define GSBI7_SPI_ERROR_FLAGS_REG (GSBI7_QUP7_REG_BASE + 0x00000308) + +#define GSBI5_SPI_ERROR_FLAGS_EN_REG (GSBI5_QUP5_REG_BASE + 0x0000030c) +#define GSBI6_SPI_ERROR_FLAGS_EN_REG (GSBI6_QUP6_REG_BASE + 0x0000030c) +#define GSBI7_SPI_ERROR_FLAGS_EN_REG (GSBI7_QUP7_REG_BASE + 0x0000030c) + +#define GSBI5_GSBI_CTRL_REG_REG (GSBI5_REG_BASE + 0x00000000) +#define GSBI6_GSBI_CTRL_REG_REG (GSBI6_REG_BASE + 0x00000000) +#define GSBI7_GSBI_CTRL_REG_REG (GSBI7_REG_BASE + 0x00000000) + +#define GSBI5_QUP_CONFIG_REG (GSBI5_QUP5_REG_BASE + 0x00000000) +#define GSBI6_QUP_CONFIG_REG (GSBI6_QUP6_REG_BASE + 0x00000000) +#define GSBI7_QUP_CONFIG_REG (GSBI7_QUP7_REG_BASE + 0x00000000) + +#define GSBI5_QUP_ERROR_FLAGS_REG (GSBI5_QUP5_REG_BASE + 0x0000001c) +#define GSBI6_QUP_ERROR_FLAGS_REG (GSBI6_QUP6_REG_BASE + 0x0000001c) +#define GSBI7_QUP_ERROR_FLAGS_REG (GSBI7_QUP7_REG_BASE + 0x0000001c) + +#define GSBI5_QUP_ERROR_FLAGS_EN_REG (GSBI5_QUP5_REG_BASE + 0x00000020) +#define GSBI6_QUP_ERROR_FLAGS_EN_REG (GSBI6_QUP6_REG_BASE + 0x00000020) +#define GSBI7_QUP_ERROR_FLAGS_EN_REG (GSBI7_QUP7_REG_BASE + 0x00000020) + +#define GSBI5_QUP_OPERATIONAL_REG (GSBI5_QUP5_REG_BASE + 0x00000018) +#define GSBI6_QUP_OPERATIONAL_REG (GSBI6_QUP6_REG_BASE + 0x00000018) +#define GSBI7_QUP_OPERATIONAL_REG (GSBI7_QUP7_REG_BASE + 0x00000018) + +#define GSBI5_QUP_IO_MODES_REG (GSBI5_QUP5_REG_BASE + 0x00000008) +#define GSBI6_QUP_IO_MODES_REG (GSBI6_QUP6_REG_BASE + 0x00000008) +#define GSBI7_QUP_IO_MODES_REG (GSBI7_QUP7_REG_BASE + 0x00000008) + +#define GSBI5_QUP_STATE_REG (GSBI5_QUP5_REG_BASE + 0x00000004) +#define GSBI6_QUP_STATE_REG (GSBI6_QUP6_REG_BASE + 0x00000004) +#define GSBI7_QUP_STATE_REG (GSBI7_QUP7_REG_BASE + 0x00000004) + +#define GSBI5_QUP_INPUT_FIFOc_REG(c) \ + (GSBI5_QUP5_REG_BASE + 0x00000218 + 4 * (c)) +#define GSBI6_QUP_INPUT_FIFOc_REG(c) \ + (GSBI6_QUP6_REG_BASE + 0x00000218 + 4 * (c)) +#define GSBI7_QUP_INPUT_FIFOc_REG(c) \ + (GSBI7_QUP7_REG_BASE + 0x00000218 + 4 * (c)) + +#define GSBI5_QUP_OUTPUT_FIFOc_REG(c) \ + (GSBI5_QUP5_REG_BASE + 0x00000110 + 4 * (c)) +#define GSBI6_QUP_OUTPUT_FIFOc_REG(c) \ + (GSBI6_QUP6_REG_BASE + 0x00000110 + 4 * (c)) +#define GSBI7_QUP_OUTPUT_FIFOc_REG(c) \ + (GSBI7_QUP7_REG_BASE + 0x00000110 + 4 * (c)) + +#define GSBI5_QUP_MX_INPUT_COUNT_REG (GSBI5_QUP5_REG_BASE + 0x00000200) +#define GSBI6_QUP_MX_INPUT_COUNT_REG (GSBI6_QUP6_REG_BASE + 0x00000200) +#define GSBI7_QUP_MX_INPUT_COUNT_REG (GSBI7_QUP7_REG_BASE + 0x00000200) + +#define GSBI5_QUP_MX_OUTPUT_COUNT_REG (GSBI5_QUP5_REG_BASE + 0x00000100) +#define GSBI6_QUP_MX_OUTPUT_COUNT_REG (GSBI6_QUP6_REG_BASE + 0x00000100) +#define GSBI7_QUP_MX_OUTPUT_COUNT_REG (GSBI7_QUP7_REG_BASE + 0x00000100) + +#define GSBI5_QUP_SW_RESET_REG (GSBI5_QUP5_REG_BASE + 0x0000000c) +#define GSBI6_QUP_SW_RESET_REG (GSBI6_QUP6_REG_BASE + 0x0000000c) +#define GSBI7_QUP_SW_RESET_REG (GSBI7_QUP7_REG_BASE + 0x0000000c) + +#define CLK_CTL_REG_BASE 0x00900000 +#define GSBIn_RESET_REG(n) \ + (CLK_CTL_REG_BASE + 0x000029dc + 32 * ((n)-1)) + +#define SFAB_AHB_S3_FCLK_CTL_REG \ + (CLK_CTL_REG_BASE + 0x0000216c) +#define CFPB_CLK_NS_REG \ + (CLK_CTL_REG_BASE + 0x0000264c) +#define SFAB_CFPB_S_HCLK_CTL_REG \ + (CLK_CTL_REG_BASE + 0x000026c0) +#define CFPB_SPLITTER_HCLK_CTL_REG \ + (CLK_CTL_REG_BASE + 0x000026e0) +#define CFPB0_HCLK_CTL_REG \ + (CLK_CTL_REG_BASE + 0x00002650) +#define CFPB2_HCLK_CTL_REG \ + (CLK_CTL_REG_BASE + 0x00002658) +#define GSBIn_HCLK_CTL_REG(n) \ + (CLK_CTL_REG_BASE + 0x000029c0 + 32 * ((n)-1)) +#define GSBIn_QUP_APPS_NS_REG(n) \ + (CLK_CTL_REG_BASE + 0x000029cc + 32 * ((n)-1)) +#define GSBIn_QUP_APPS_MD_REG(n) \ + (CLK_CTL_REG_BASE + 0x000029c8 + 32 * ((n)-1)) +#define CLK_HALT_CFPB_STATEB_REG \ + (CLK_CTL_REG_BASE + 0x00002fd0) + +#define GSBI5_HCLK 23 +#define GSBI6_HCLK 19 +#define GSBI7_HCLK 15 +#define GSBI5_QUP_APPS_CLK 20 +#define GSBI6_QUP_APPS_CLK 16 +#define GSBI7_QUP_APPS_CLK 12 +#define GSBI_CLK_BRANCH_ENA_MSK (1 << 4) +#define GSBI_CLK_BRANCH_ENA (1 << 4) +#define GSBI_CLK_BRANCH_DIS (0 << 4) +#define QUP_CLK_BRANCH_ENA_MSK (1 << 9) +#define QUP_CLK_BRANCH_ENA (1 << 9) +#define QUP_CLK_BRANCH_DIS (0 << 9) +#define CLK_ROOT_ENA_MSK (1 << 11) +#define CLK_ROOT_ENA (1 << 11) +#define CLK_ROOT_DIS (0 << 11) + +#define QUP_STATE_VALID_BIT 2 +#define QUP_STATE_VALID 1 +#define QUP_STATE_MASK 0x3 +#define QUP_CONFIG_MINI_CORE_MSK (0x0F << 8) +#define QUP_CONFIG_MINI_CORE_SPI (1 << 8) +#define SPI_QUP_CONF_INPUT_MSK (1 << 7) +#define SPI_QUP_CONF_INPUT_ENA (0 << 7) +#define SPI_QUP_CONF_OUTPUT_MSK (1 << 6) +#define SPI_QUP_CONF_OUTPUT_ENA (0 << 6) +#define QUP_STATE_RUN_STATE 0x1 +#define QUP_STATE_RESET_STATE 0x0 +#define SPI_BIT_WORD_MSK 0x1F +#define SPI_8_BIT_WORD 0x07 +#define PROTOCOL_CODE_MSK (0x07 << 4) +#define PROTOCOL_CODE_SPI (0x03 << 4) +#define LOOP_BACK_MSK (1 << 8) +#define NO_LOOP_BACK (0 << 8) +#define SLAVE_OPERATION_MSK (1 << 5) +#define SLAVE_OPERATION (0 << 5) +#define CLK_ALWAYS_ON (0 << 9) +#define MX_CS_MODE (0 << 8) +#define NO_TRI_STATE (1 << 0) +#define OUTPUT_BIT_SHIFT_MSK (1 << 16) +#define OUTPUT_BIT_SHIFT_EN (1 << 16) +#define INPUT_BLOCK_MODE_MSK (0x03 << 12) +#define INPUT_BLOCK_MODE (0x01 << 12) +#define OUTPUT_BLOCK_MODE_MSK (0x03 << 10) +#define OUTPUT_BLOCK_MODE (0x01 << 10) +#define GSBI1_RESET (1 << 0) +#define GSBI1_RESET_MSK 0x1 + +#define GSBI_M_VAL_SHFT 16 +#define GSBIn_M_VAL_MSK (0xFF << GSBI_M_VAL_SHFT) +#define GSBI_N_VAL_SHFT 16 +#define GSBIn_N_VAL_MSK (0xFF << GSBI_N_VAL_SHFT) +#define GSBI_D_VAL_SHFT 0 +#define GSBIn_D_VAL_MSK (0xFF << GSBI_D_VAL_SHFT) +#define MNCNTR_RST_MSK (1 << 7) +#define MNCNTR_RST_ENA (1 << 7) +#define MNCNTR_RST_DIS (0 << 7) +#define MNCNTR_MSK (1 << 8) +#define MNCNTR_EN (1 << 8) +#define MNCNTR_DIS (0 << 8) +#define MNCNTR_MODE_MSK (0x3 << 5) +#define MNCNTR_MODE_BYPASS (0 << 5) +#define MNCNTR_MODE_DUAL_EDGE (0x2 << 5) +#define GSBI_PRE_DIV_SEL_SHFT 3 +#define GSBIn_PRE_DIV_SEL_MSK (0x3 << GSBI_PRE_DIV_SEL_SHFT) +#define GSBIn_PLL_SRC_MSK (0x03 << 0) +#define GSBIn_PLL_SRC_PXO (0 << 0) +#define GSBIn_PLL_SRC_PLL8 (0x3 << 0) + +#define SPI_INPUT_FIRST_MODE (1 << 9) +#define SPI_IO_CONTROL_CLOCK_IDLE_HIGH (1 << 10) +#define QUP_DATA_AVAILABLE_FOR_READ (1 << 5) +#define QUP_OUTPUT_FIFO_NOT_EMPTY (1 << 4) +#define OUTPUT_SERVICE_FLAG (1 << 8) +#define INPUT_SERVICE_FLAG (1 << 9) +#define QUP_OUTPUT_FIFO_FULL (1 << 6) +#define SPI_INPUT_BLOCK_SIZE 4 +#define SPI_OUTPUT_BLOCK_SIZE 4 +#define GSBI5_SPI_CLK 21 +#define GSBI5_SPI_MISO 19 +#define GSBI5_SPI_MOSI 18 +#define GSBI5_SPI_CS_0 20 +#define GSBI5_SPI_CS_1 61 +#define GSBI5_SPI_CS_2 62 +#define GSBI5_SPI_CS_3 2 +#define GSBI6_SPI_CLK 30 +#define GSBI6_SPI_CS_0 29 +#define GSBI6_SPI_MISO 28 +#define GSBI6_SPI_MOSI 27 +#define GSBI7_SPI_CLK 9 +#define GSBI7_SPI_CS_0 8 +#define GSBI7_SPI_MISO 7 +#define GSBI7_SPI_MOSI 6 + +#define GPIO_FUNC_ENABLE 1 +#define GPIO_FUNC_DISABLE 0 +#define FUNC_SEL_1 1 +#define FUNC_SEL_3 3 +#define FUNC_SEL_GPIO 0 +#define GPIO_DRV_STR_10MA 0x4 +#define GPIO_DRV_STR_11MA 0x7 +#define GPIO_OUT 1 +#define MSM_GSBI_MAX_FREQ 51200000 + +#define SPI_RESET_STATE 0 +#define SPI_RUN_STATE 1 +#define SPI_CORE_RESET 0 +#define SPI_CORE_RUNNING 1 +#define GSBI_SPI_MODE_0 0 +#define GSBI_SPI_MODE_1 1 +#define GSBI_SPI_MODE_2 2 +#define GSBI_SPI_MODE_3 3 +#define GSBI5_SPI 0 +#define GSBI6_SPI 1 +#define GSBI7_SPI 2 + +struct gsbi_spi { + unsigned int spi_config; + unsigned int io_control; + unsigned int error_flags; + unsigned int error_flags_en; + unsigned int gsbi_ctrl; + unsigned int qup_config; + unsigned int qup_error_flags; + unsigned int qup_error_flags_en; + unsigned int qup_operational; + unsigned int qup_io_modes; + unsigned int qup_state; + unsigned int qup_input_fifo; + unsigned int qup_output_fifo; + unsigned int qup_mx_input_count; + unsigned int qup_mx_output_count; + unsigned int qup_sw_reset; + unsigned int qup_ns_reg; + unsigned int qup_md_reg; +}; + +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) + } +}; + +struct ipq_spi_slave { + struct spi_slave slave; + const struct gsbi_spi *regs; + unsigned int core_state; + unsigned int mode; + unsigned int initialized; + unsigned long freq; +}; + +static inline struct ipq_spi_slave *to_ipq_spi(struct spi_slave *slave) +{ + return container_of(slave, struct ipq_spi_slave, slave); +} + +#endif /* _IPQ_SPI_H_ */ 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 +#include +#include +#include +#include +#include "ipq_spi.h" +#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 + +#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) +{ + +}