Patrick Georgi | 7051707 | 2020-05-10 18:47:05 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: BSD-3-Clause */ |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 2 | |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 3 | #include <device/mmio.h> |
Furquan Shaikh | c28984d | 2016-11-20 21:04:00 -0800 | [diff] [blame] | 4 | #include <console/console.h> |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 5 | #include <delay.h> |
| 6 | #include <gpio.h> |
| 7 | #include <soc/iomap.h> |
| 8 | #include <soc/spi.h> |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 9 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 10 | static const struct blsp_spi spi_reg[] = { |
| 11 | /* BLSP0 registers for SPI interface */ |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 12 | { |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 13 | BLSP0_SPI_CONFIG_REG, |
| 14 | BLSP0_SPI_IO_CONTROL_REG, |
| 15 | BLSP0_SPI_ERROR_FLAGS_REG, |
| 16 | BLSP0_SPI_ERROR_FLAGS_EN_REG, |
| 17 | BLSP0_QUP_CONFIG_REG, |
| 18 | BLSP0_QUP_ERROR_FLAGS_REG, |
| 19 | BLSP0_QUP_ERROR_FLAGS_EN_REG, |
| 20 | BLSP0_QUP_OPERATIONAL_REG, |
| 21 | BLSP0_QUP_IO_MODES_REG, |
| 22 | BLSP0_QUP_STATE_REG, |
| 23 | BLSP0_QUP_INPUT_FIFOc_REG(0), |
| 24 | BLSP0_QUP_OUTPUT_FIFOc_REG(0), |
| 25 | BLSP0_QUP_MX_INPUT_COUNT_REG, |
| 26 | BLSP0_QUP_MX_OUTPUT_COUNT_REG, |
| 27 | BLSP0_QUP_SW_RESET_REG, |
| 28 | 0, |
| 29 | 0, |
| 30 | BLSP0_QUP_OPERATIONAL_MASK, |
| 31 | BLSP0_SPI_DEASSERT_WAIT_REG, |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 32 | }, |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 33 | /* BLSP1 registers for SPI interface */ |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 34 | { |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 35 | BLSP1_SPI_CONFIG_REG, |
| 36 | BLSP1_SPI_IO_CONTROL_REG, |
| 37 | BLSP1_SPI_ERROR_FLAGS_REG, |
| 38 | BLSP1_SPI_ERROR_FLAGS_EN_REG, |
| 39 | BLSP1_QUP_CONFIG_REG, |
| 40 | BLSP1_QUP_ERROR_FLAGS_REG, |
| 41 | BLSP1_QUP_ERROR_FLAGS_EN_REG, |
| 42 | BLSP1_QUP_OPERATIONAL_REG, |
| 43 | BLSP1_QUP_IO_MODES_REG, |
| 44 | BLSP1_QUP_STATE_REG, |
| 45 | BLSP1_QUP_INPUT_FIFOc_REG(0), |
| 46 | BLSP1_QUP_OUTPUT_FIFOc_REG(0), |
| 47 | BLSP1_QUP_MX_INPUT_COUNT_REG, |
| 48 | BLSP1_QUP_MX_OUTPUT_COUNT_REG, |
| 49 | BLSP1_QUP_SW_RESET_REG, |
| 50 | 0, |
| 51 | 0, |
| 52 | BLSP1_QUP_OPERATIONAL_MASK, |
| 53 | BLSP1_SPI_DEASSERT_WAIT_REG, |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 54 | }, |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 55 | }; |
| 56 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 57 | static int check_bit_state(void *reg_addr, int mask, |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 58 | int val, int us_delay) |
| 59 | { |
| 60 | unsigned int count = TIMEOUT_CNT; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 61 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 62 | while ((read32(reg_addr) & mask) != val) { |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 63 | count--; |
| 64 | if (count == 0) |
| 65 | return -ETIMEDOUT; |
| 66 | udelay(us_delay); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | return SUCCESS; |
| 70 | } |
| 71 | |
| 72 | /* |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 73 | * Check whether QUPn State is valid |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 74 | */ |
| 75 | static int check_qup_state_valid(struct ipq_spi_slave *ds) |
| 76 | { |
| 77 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 78 | return check_bit_state(ds->regs->qup_state, QUP_STATE_VALID_MASK, |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 79 | QUP_STATE_VALID, 1); |
| 80 | |
| 81 | } |
| 82 | |
| 83 | /* |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 84 | * Configure QUPn Core state |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 85 | */ |
| 86 | static int config_spi_state(struct ipq_spi_slave *ds, unsigned int state) |
| 87 | { |
| 88 | uint32_t val; |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 89 | int ret = SUCCESS; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 90 | |
| 91 | ret = check_qup_state_valid(ds); |
| 92 | if (ret != SUCCESS) |
| 93 | return ret; |
| 94 | |
| 95 | switch (state) { |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 96 | case QUP_STATE_RUN: |
| 97 | /* Set the state to RUN */ |
| 98 | val = ((read32(ds->regs->qup_state) & ~QUP_STATE_MASK) |
| 99 | | QUP_STATE_RUN); |
| 100 | write32(ds->regs->qup_state, val); |
| 101 | ret = check_qup_state_valid(ds); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 102 | break; |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 103 | case QUP_STATE_RESET: |
| 104 | /* Set the state to RESET */ |
| 105 | val = ((read32(ds->regs->qup_state) & ~QUP_STATE_MASK) |
| 106 | | QUP_STATE_RESET); |
| 107 | write32(ds->regs->qup_state, val); |
| 108 | ret = check_qup_state_valid(ds); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 109 | break; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 110 | default: |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 111 | printk(BIOS_ERR, "unsupported QUP SPI state : %d\n", state); |
| 112 | ret = -EINVAL; |
| 113 | break; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 114 | } |
| 115 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 116 | return ret; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 117 | } |
| 118 | |
| 119 | /* |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 120 | * Set QUPn SPI Mode |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 121 | */ |
| 122 | static void spi_set_mode(struct ipq_spi_slave *ds, unsigned int mode) |
| 123 | { |
| 124 | unsigned int clk_idle_state; |
| 125 | unsigned int input_first_mode; |
| 126 | uint32_t val; |
| 127 | |
| 128 | switch (mode) { |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 129 | case SPI_MODE0: |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 130 | clk_idle_state = 0; |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 131 | input_first_mode = SPI_CONFIG_INPUT_FIRST; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 132 | break; |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 133 | case SPI_MODE1: |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 134 | clk_idle_state = 0; |
| 135 | input_first_mode = 0; |
| 136 | break; |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 137 | case SPI_MODE2: |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 138 | clk_idle_state = 1; |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 139 | input_first_mode = SPI_CONFIG_INPUT_FIRST; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 140 | break; |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 141 | case SPI_MODE3: |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 142 | clk_idle_state = 1; |
| 143 | input_first_mode = 0; |
| 144 | break; |
| 145 | default: |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 146 | printk(BIOS_ERR, "unsupported spi mode : %d\n", mode); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 147 | return; |
| 148 | } |
| 149 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 150 | val = read32(ds->regs->spi_config); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 151 | val |= input_first_mode; |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 152 | write32(ds->regs->spi_config, val); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 153 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 154 | val = read32(ds->regs->io_control); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 155 | if (clk_idle_state) |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 156 | val |= SPI_IO_CTRL_CLOCK_IDLE_HIGH; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 157 | else |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 158 | val &= ~SPI_IO_CTRL_CLOCK_IDLE_HIGH; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 159 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 160 | write32(ds->regs->io_control, val); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | /* |
| 164 | * Reset entire QUP and all mini cores |
| 165 | */ |
| 166 | static void spi_reset(struct ipq_spi_slave *ds) |
| 167 | { |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 168 | write32(ds->regs->qup_sw_reset, 0x1); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 169 | udelay(5); |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 170 | check_qup_state_valid(ds); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 171 | } |
| 172 | |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 173 | static struct ipq_spi_slave spi_slave_pool[2]; |
| 174 | |
Furquan Shaikh | 36b81af | 2016-12-01 01:02:44 -0800 | [diff] [blame] | 175 | static struct ipq_spi_slave *to_ipq_spi(const struct spi_slave *slave) |
| 176 | { |
| 177 | struct ipq_spi_slave *ds; |
| 178 | size_t i; |
| 179 | |
| 180 | for (i = 0; i < ARRAY_SIZE(spi_slave_pool); i++) { |
| 181 | ds = spi_slave_pool + i; |
| 182 | |
| 183 | if (!ds->allocated) |
| 184 | continue; |
| 185 | |
| 186 | if ((ds->slave.bus == slave->bus) && |
| 187 | (ds->slave.cs == slave->cs)) |
| 188 | return ds; |
| 189 | } |
| 190 | |
| 191 | return NULL; |
| 192 | } |
| 193 | |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 194 | /* |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 195 | * BLSP QUPn SPI Hardware Initialisation |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 196 | */ |
| 197 | static int spi_hw_init(struct ipq_spi_slave *ds) |
| 198 | { |
| 199 | int ret; |
| 200 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 201 | ds->initialized = 0; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 202 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 203 | /* QUPn module configuration */ |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 204 | spi_reset(ds); |
| 205 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 206 | /* Set the QUPn state */ |
| 207 | ret = config_spi_state(ds, QUP_STATE_RESET); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 208 | if (ret) |
| 209 | return ret; |
| 210 | |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 211 | /* |
| 212 | * Configure Mini core to SPI core with Input Output enabled, |
| 213 | * SPI master, N = 8 bits |
| 214 | */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 215 | clrsetbits32(ds->regs->qup_config, QUP_CONFIG_MINI_CORE_MSK | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 216 | QUP_CONF_INPUT_MSK | |
| 217 | QUP_CONF_OUTPUT_MSK | |
| 218 | QUP_CONF_N_MASK, |
| 219 | QUP_CONFIG_MINI_CORE_SPI | |
| 220 | QUP_CONF_INPUT_ENA | |
| 221 | QUP_CONF_OUTPUT_ENA | |
| 222 | QUP_CONF_N_SPI_8_BIT_WORD); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 223 | |
| 224 | /* |
| 225 | * Configure Input first SPI protocol, |
| 226 | * SPI master mode and no loopback |
| 227 | */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 228 | clrsetbits32(ds->regs->spi_config, SPI_CONFIG_LOOP_BACK_MSK | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 229 | SPI_CONFIG_NO_SLAVE_OPER_MSK, |
| 230 | SPI_CONFIG_NO_LOOP_BACK | |
| 231 | SPI_CONFIG_NO_SLAVE_OPER); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 232 | |
| 233 | /* |
| 234 | * Configure SPI IO Control Register |
| 235 | * CLK_ALWAYS_ON = 0 |
| 236 | * MX_CS_MODE = 0 |
| 237 | * NO_TRI_STATE = 1 |
| 238 | */ |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 239 | write32(ds->regs->io_control, SPI_IO_CTRL_CLK_ALWAYS_ON | |
| 240 | SPI_IO_CTRL_NO_TRI_STATE); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 241 | |
| 242 | /* |
| 243 | * Configure SPI IO Modes. |
| 244 | * OUTPUT_BIT_SHIFT_EN = 1 |
| 245 | * INPUT_MODE = Block Mode |
| 246 | * OUTPUT MODE = Block Mode |
| 247 | */ |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 248 | clrsetbits32(ds->regs->qup_io_modes, |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 249 | QUP_IO_MODES_OUTPUT_BIT_SHIFT_MSK | |
| 250 | QUP_IO_MODES_INPUT_MODE_MSK | |
| 251 | QUP_IO_MODES_OUTPUT_MODE_MSK, |
| 252 | QUP_IO_MODES_OUTPUT_BIT_SHIFT_EN | |
| 253 | QUP_IO_MODES_INPUT_BLOCK_MODE | |
| 254 | QUP_IO_MODES_OUTPUT_BLOCK_MODE); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 255 | |
| 256 | spi_set_mode(ds, ds->mode); |
| 257 | |
| 258 | /* Disable Error mask */ |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 259 | write32(ds->regs->error_flags_en, 0); |
| 260 | write32(ds->regs->qup_error_flags_en, 0); |
| 261 | |
| 262 | write32(ds->regs->qup_deassert_wait, 0); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 263 | |
| 264 | ds->initialized = 1; |
| 265 | |
| 266 | return SUCCESS; |
| 267 | } |
| 268 | |
Furquan Shaikh | 94f8699 | 2016-12-01 07:12:32 -0800 | [diff] [blame] | 269 | static int spi_ctrlr_claim_bus(const struct spi_slave *slave) |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 270 | { |
| 271 | struct ipq_spi_slave *ds = to_ipq_spi(slave); |
| 272 | unsigned int ret; |
| 273 | |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 274 | ret = spi_hw_init(ds); |
| 275 | if (ret) |
| 276 | return -EIO; |
| 277 | |
| 278 | return SUCCESS; |
| 279 | } |
| 280 | |
Furquan Shaikh | 94f8699 | 2016-12-01 07:12:32 -0800 | [diff] [blame] | 281 | static void spi_ctrlr_release_bus(const struct spi_slave *slave) |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 282 | { |
| 283 | struct ipq_spi_slave *ds = to_ipq_spi(slave); |
| 284 | |
| 285 | /* Reset the SPI hardware */ |
| 286 | spi_reset(ds); |
| 287 | ds->initialized = 0; |
| 288 | } |
| 289 | |
Furquan Shaikh | 0dba025 | 2016-11-30 04:34:22 -0800 | [diff] [blame] | 290 | static void write_force_cs(const struct spi_slave *slave, int assert) |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 291 | { |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 292 | struct ipq_spi_slave *ds = to_ipq_spi(slave); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 293 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 294 | if (assert) |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 295 | clrsetbits32(ds->regs->io_control, |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 296 | SPI_IO_CTRL_FORCE_CS_MSK, SPI_IO_CTRL_FORCE_CS_EN); |
| 297 | else |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 298 | clrsetbits32(ds->regs->io_control, |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 299 | SPI_IO_CTRL_FORCE_CS_MSK, SPI_IO_CTRL_FORCE_CS_DIS); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 300 | |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 301 | } |
| 302 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 303 | /* |
| 304 | * Function to write data to OUTPUT FIFO |
| 305 | */ |
| 306 | static void spi_write_byte(struct ipq_spi_slave *ds, unsigned char data) |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 307 | { |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 308 | /* Wait for space in the FIFO */ |
| 309 | while ((read32(ds->regs->qup_operational) & OUTPUT_FIFO_FULL)) |
| 310 | udelay(1); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 311 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 312 | /* Write the byte of data */ |
| 313 | write32(ds->regs->qup_output_fifo, data); |
| 314 | } |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 315 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 316 | /* |
| 317 | * Function to read data from Input FIFO |
| 318 | */ |
| 319 | static unsigned char spi_read_byte(struct ipq_spi_slave *ds) |
| 320 | { |
| 321 | /* Wait for Data in FIFO */ |
| 322 | while (!(read32(ds->regs->qup_operational) & INPUT_FIFO_NOT_EMPTY)) |
| 323 | udelay(1); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 324 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 325 | /* Read a byte of data */ |
| 326 | return read32(ds->regs->qup_input_fifo) & 0xff; |
| 327 | } |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 328 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 329 | /* |
Martin Roth | 26f97f9 | 2021-10-01 14:53:22 -0600 | [diff] [blame] | 330 | * Function to check whether Input or Output FIFO |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 331 | * has data to be serviced |
| 332 | */ |
| 333 | static int check_fifo_status(void *reg_addr) |
| 334 | { |
| 335 | unsigned int count = TIMEOUT_CNT; |
| 336 | unsigned int status_flag; |
| 337 | unsigned int val; |
| 338 | |
| 339 | do { |
| 340 | val = read32(reg_addr); |
| 341 | count--; |
| 342 | if (count == 0) |
| 343 | return -ETIMEDOUT; |
| 344 | status_flag = ((val & OUTPUT_SERVICE_FLAG) | |
| 345 | (val & INPUT_SERVICE_FLAG)); |
| 346 | } while (!status_flag); |
| 347 | |
| 348 | return SUCCESS; |
| 349 | } |
| 350 | |
| 351 | /* |
| 352 | * Function to configure Input and Output enable/disable |
| 353 | */ |
| 354 | static void enable_io_config(struct ipq_spi_slave *ds, |
| 355 | uint32_t write_cnt, uint32_t read_cnt) |
| 356 | { |
| 357 | |
| 358 | if (write_cnt) { |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 359 | clrsetbits32(ds->regs->qup_config, |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 360 | QUP_CONF_OUTPUT_MSK, QUP_CONF_OUTPUT_ENA); |
| 361 | } else { |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 362 | clrsetbits32(ds->regs->qup_config, |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 363 | QUP_CONF_OUTPUT_MSK, QUP_CONF_NO_OUTPUT); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 364 | } |
| 365 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 366 | if (read_cnt) { |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 367 | clrsetbits32(ds->regs->qup_config, |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 368 | QUP_CONF_INPUT_MSK, QUP_CONF_INPUT_ENA); |
| 369 | } else { |
Julius Werner | 55009af | 2019-12-02 22:03:27 -0800 | [diff] [blame] | 370 | clrsetbits32(ds->regs->qup_config, |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 371 | QUP_CONF_INPUT_MSK, QUP_CONF_NO_INPUT); |
| 372 | } |
| 373 | |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 374 | } |
| 375 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 376 | /* |
| 377 | * Function to read bytes number of data from the Input FIFO |
| 378 | */ |
| 379 | static int __blsp_spi_read(struct ipq_spi_slave *ds, u8 *data_buffer, |
| 380 | unsigned int bytes) |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 381 | { |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 382 | uint32_t val; |
| 383 | unsigned int i; |
| 384 | unsigned int fifo_count; |
| 385 | int ret = SUCCESS; |
| 386 | int state_config; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 387 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 388 | /* Configure no of bytes to read */ |
| 389 | state_config = config_spi_state(ds, QUP_STATE_RESET); |
| 390 | if (state_config) |
| 391 | return state_config; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 392 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 393 | /* Configure input and output enable */ |
| 394 | enable_io_config(ds, 0, bytes); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 395 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 396 | write32(ds->regs->qup_mx_input_count, bytes); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 397 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 398 | state_config = config_spi_state(ds, QUP_STATE_RUN); |
| 399 | if (state_config) |
| 400 | return state_config; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 401 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 402 | while (bytes) { |
| 403 | ret = check_fifo_status(ds->regs->qup_operational); |
| 404 | if (ret != SUCCESS) |
| 405 | goto out; |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 406 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 407 | val = read32(ds->regs->qup_operational); |
| 408 | if (val & INPUT_SERVICE_FLAG) { |
| 409 | /* |
| 410 | * acknowledge to hw that software will |
| 411 | * read input data |
| 412 | */ |
| 413 | val &= INPUT_SERVICE_FLAG; |
| 414 | write32(ds->regs->qup_operational, val); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 415 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 416 | fifo_count = ((bytes > SPI_INPUT_BLOCK_SIZE) ? |
| 417 | SPI_INPUT_BLOCK_SIZE : bytes); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 418 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 419 | for (i = 0; i < fifo_count; i++) { |
| 420 | *data_buffer = spi_read_byte(ds); |
| 421 | data_buffer++; |
| 422 | bytes--; |
| 423 | } |
| 424 | } |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 425 | } |
| 426 | |
| 427 | out: |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 428 | /* |
| 429 | * Put the SPI Core back in the Reset State |
| 430 | * to end the transfer |
| 431 | */ |
| 432 | (void)config_spi_state(ds, QUP_STATE_RESET); |
| 433 | return ret; |
| 434 | } |
| 435 | |
| 436 | static int blsp_spi_read(struct ipq_spi_slave *ds, u8 *data_buffer, |
| 437 | unsigned int bytes) |
| 438 | { |
| 439 | int length, ret; |
| 440 | |
| 441 | while (bytes) { |
| 442 | length = (bytes < MAX_COUNT_SIZE) ? bytes : MAX_COUNT_SIZE; |
| 443 | |
| 444 | ret = __blsp_spi_read(ds, data_buffer, length); |
| 445 | if (ret != SUCCESS) |
| 446 | return ret; |
| 447 | |
| 448 | data_buffer += length; |
| 449 | bytes -= length; |
| 450 | } |
| 451 | |
| 452 | return 0; |
| 453 | } |
| 454 | |
| 455 | /* |
| 456 | * Function to write data to the Output FIFO |
| 457 | */ |
| 458 | static int __blsp_spi_write(struct ipq_spi_slave *ds, const u8 *cmd_buffer, |
| 459 | unsigned int bytes) |
| 460 | { |
| 461 | uint32_t val; |
| 462 | unsigned int i; |
| 463 | unsigned int write_len = bytes; |
| 464 | unsigned int read_len = bytes; |
| 465 | unsigned int fifo_count; |
| 466 | int ret = SUCCESS; |
| 467 | int state_config; |
| 468 | |
| 469 | state_config = config_spi_state(ds, QUP_STATE_RESET); |
| 470 | if (state_config) |
| 471 | return state_config; |
| 472 | |
| 473 | /* No of bytes to be written in Output FIFO */ |
| 474 | write32(ds->regs->qup_mx_output_count, bytes); |
| 475 | write32(ds->regs->qup_mx_input_count, bytes); |
| 476 | state_config = config_spi_state(ds, QUP_STATE_RUN); |
| 477 | if (state_config) |
| 478 | return state_config; |
| 479 | |
| 480 | /* Configure input and output enable */ |
| 481 | enable_io_config(ds, write_len, read_len); |
| 482 | |
| 483 | /* |
| 484 | * read_len considered to ensure that we read the dummy data for the |
| 485 | * write we performed. This is needed to ensure with WR-RD transaction |
| 486 | * to get the actual data on the subsequent read cycle that happens |
| 487 | */ |
| 488 | while (write_len || read_len) { |
| 489 | |
| 490 | ret = check_fifo_status(ds->regs->qup_operational); |
| 491 | if (ret != SUCCESS) |
| 492 | goto out; |
| 493 | |
| 494 | val = read32(ds->regs->qup_operational); |
| 495 | if (val & OUTPUT_SERVICE_FLAG) { |
| 496 | /* |
| 497 | * acknowledge to hw that software will write |
| 498 | * expected output data |
| 499 | */ |
| 500 | val &= OUTPUT_SERVICE_FLAG; |
| 501 | write32(ds->regs->qup_operational, val); |
| 502 | |
| 503 | if (write_len > SPI_OUTPUT_BLOCK_SIZE) |
| 504 | fifo_count = SPI_OUTPUT_BLOCK_SIZE; |
| 505 | else |
| 506 | fifo_count = write_len; |
| 507 | |
| 508 | for (i = 0; i < fifo_count; i++) { |
| 509 | /* Write actual data to output FIFO */ |
| 510 | spi_write_byte(ds, *cmd_buffer); |
| 511 | cmd_buffer++; |
| 512 | write_len--; |
| 513 | } |
| 514 | } |
| 515 | if (val & INPUT_SERVICE_FLAG) { |
| 516 | /* |
| 517 | * acknowledge to hw that software |
| 518 | * will read input data |
| 519 | */ |
| 520 | val &= INPUT_SERVICE_FLAG; |
| 521 | write32(ds->regs->qup_operational, val); |
| 522 | |
| 523 | if (read_len > SPI_INPUT_BLOCK_SIZE) |
| 524 | fifo_count = SPI_INPUT_BLOCK_SIZE; |
| 525 | else |
| 526 | fifo_count = read_len; |
| 527 | |
| 528 | for (i = 0; i < fifo_count; i++) { |
| 529 | /* Read dummy data for the data written */ |
| 530 | (void)spi_read_byte(ds); |
| 531 | |
| 532 | /* Decrement the read count after reading the |
| 533 | * dummy data from the device. This is to make |
| 534 | * sure we read dummy data before we write the |
| 535 | * data to fifo |
| 536 | */ |
| 537 | read_len--; |
| 538 | } |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | out: |
| 543 | /* |
| 544 | * Put the SPI Core back in the Reset State |
| 545 | * to end the transfer |
| 546 | */ |
| 547 | (void)config_spi_state(ds, QUP_STATE_RESET); |
| 548 | |
| 549 | return ret; |
| 550 | } |
| 551 | |
| 552 | static int blsp_spi_write(struct ipq_spi_slave *ds, u8 *cmd_buffer, |
| 553 | unsigned int bytes) |
| 554 | { |
| 555 | int length, ret; |
| 556 | |
| 557 | while (bytes) { |
| 558 | length = (bytes < MAX_COUNT_SIZE) ? bytes : MAX_COUNT_SIZE; |
| 559 | |
| 560 | ret = __blsp_spi_write(ds, cmd_buffer, length); |
| 561 | if (ret != SUCCESS) |
| 562 | return ret; |
| 563 | |
| 564 | cmd_buffer += length; |
| 565 | bytes -= length; |
| 566 | } |
| 567 | |
| 568 | return 0; |
| 569 | } |
| 570 | |
| 571 | /* |
| 572 | * This function is invoked with either tx_buf or rx_buf. |
| 573 | * Calling this function with both null does a chip select change. |
| 574 | */ |
Furquan Shaikh | 94f8699 | 2016-12-01 07:12:32 -0800 | [diff] [blame] | 575 | static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout, |
| 576 | size_t out_bytes, void *din, size_t in_bytes) |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 577 | { |
| 578 | struct ipq_spi_slave *ds = to_ipq_spi(slave); |
| 579 | u8 *txp = (u8 *)dout; |
| 580 | u8 *rxp = (u8 *)din; |
| 581 | int ret; |
| 582 | |
Aaron Durbin | 9a1bb36 | 2018-04-19 16:58:13 -0600 | [diff] [blame] | 583 | /* Driver implementation does not support full duplex. */ |
| 584 | if (dout && din) |
| 585 | return -1; |
| 586 | |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 587 | ret = config_spi_state(ds, QUP_STATE_RESET); |
| 588 | if (ret != SUCCESS) |
| 589 | return ret; |
| 590 | |
| 591 | write_force_cs(slave, 1); |
| 592 | |
| 593 | if (dout != NULL) { |
Elyes Haouas | 9776738 | 2022-11-18 15:08:13 +0100 | [diff] [blame] | 594 | ret = blsp_spi_write(ds, txp, (unsigned int)out_bytes); |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 595 | if (ret != SUCCESS) |
| 596 | goto out; |
| 597 | } |
| 598 | |
| 599 | if (din != NULL) { |
| 600 | ret = blsp_spi_read(ds, rxp, in_bytes); |
| 601 | if (ret != SUCCESS) |
| 602 | goto out; |
| 603 | } |
| 604 | |
| 605 | out: |
| 606 | write_force_cs(slave, 0); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 607 | |
| 608 | /* |
| 609 | * Put the SPI Core back in the Reset State |
| 610 | * to end the transfer |
| 611 | */ |
Varadarajan Narayanan | 2596764 | 2016-03-08 15:02:56 +0530 | [diff] [blame] | 612 | (void)config_spi_state(ds, QUP_STATE_RESET); |
Varadarajan Narayanan | a6935c2 | 2016-03-02 16:57:10 +0530 | [diff] [blame] | 613 | |
| 614 | return ret; |
| 615 | } |
Furquan Shaikh | 94f8699 | 2016-12-01 07:12:32 -0800 | [diff] [blame] | 616 | |
Furquan Shaikh | e424a59 | 2017-05-18 14:40:58 -0700 | [diff] [blame] | 617 | static int spi_ctrlr_setup(const struct spi_slave *slave) |
Furquan Shaikh | 94f8699 | 2016-12-01 07:12:32 -0800 | [diff] [blame] | 618 | { |
| 619 | struct ipq_spi_slave *ds = NULL; |
| 620 | int i; |
Julius Werner | 7c712bb | 2019-05-01 16:51:20 -0700 | [diff] [blame] | 621 | int bus = slave->bus; |
| 622 | int cs = slave->cs; |
Furquan Shaikh | 94f8699 | 2016-12-01 07:12:32 -0800 | [diff] [blame] | 623 | |
| 624 | if ((bus < BLSP0_SPI) || (bus > BLSP1_SPI) |
| 625 | || ((bus == BLSP0_SPI) && (cs > 2)) |
| 626 | || ((bus == BLSP1_SPI) && (cs > 0))) { |
| 627 | printk(BIOS_ERR, |
Martin Roth | 26f97f9 | 2021-10-01 14:53:22 -0600 | [diff] [blame] | 628 | "SPI error: unsupported bus %d (Supported buses 0, 1 and 2) " |
Elyes HAOUAS | 88607a4 | 2018-10-05 10:36:45 +0200 | [diff] [blame] | 629 | "or chipselect\n", bus); |
Furquan Shaikh | 94f8699 | 2016-12-01 07:12:32 -0800 | [diff] [blame] | 630 | return -1; |
| 631 | } |
| 632 | |
| 633 | for (i = 0; i < ARRAY_SIZE(spi_slave_pool); i++) { |
| 634 | if (spi_slave_pool[i].allocated) |
| 635 | continue; |
| 636 | ds = spi_slave_pool + i; |
| 637 | |
Furquan Shaikh | e424a59 | 2017-05-18 14:40:58 -0700 | [diff] [blame] | 638 | ds->slave.bus = bus; |
| 639 | ds->slave.cs = cs; |
Furquan Shaikh | 94f8699 | 2016-12-01 07:12:32 -0800 | [diff] [blame] | 640 | ds->regs = &spi_reg[bus]; |
| 641 | |
| 642 | /* |
| 643 | * TODO(vbendeb): |
| 644 | * hardcoded frequency and mode - we might need to find a way |
| 645 | * to configure this |
| 646 | */ |
| 647 | ds->freq = 10000000; |
| 648 | ds->mode = SPI_MODE3; |
| 649 | ds->allocated = 1; |
| 650 | |
| 651 | return 0; |
| 652 | } |
| 653 | |
| 654 | printk(BIOS_ERR, "SPI error: all %d pools busy\n", i); |
| 655 | return -1; |
| 656 | } |
Furquan Shaikh | e424a59 | 2017-05-18 14:40:58 -0700 | [diff] [blame] | 657 | |
| 658 | static const struct spi_ctrlr spi_ctrlr = { |
| 659 | .setup = spi_ctrlr_setup, |
| 660 | .claim_bus = spi_ctrlr_claim_bus, |
| 661 | .release_bus = spi_ctrlr_release_bus, |
| 662 | .xfer = spi_ctrlr_xfer, |
Furquan Shaikh | e424a59 | 2017-05-18 14:40:58 -0700 | [diff] [blame] | 663 | .max_xfer_size = MAX_PACKET_COUNT, |
| 664 | }; |
| 665 | |
| 666 | const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = { |
| 667 | { |
| 668 | .ctrlr = &spi_ctrlr, |
| 669 | .bus_start = BLSP0_SPI, |
| 670 | .bus_end = BLSP1_SPI, |
| 671 | }, |
| 672 | }; |
| 673 | |
| 674 | const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map); |