Patrick Georgi | ac95903 | 2020-05-05 22:49:26 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
T Michael Turney | 47a0832 | 2019-11-27 19:22:45 -0800 | [diff] [blame] | 2 | |
| 3 | #include <assert.h> |
Elyes HAOUAS | 27718ac | 2020-09-19 09:32:36 +0200 | [diff] [blame] | 4 | #include <console/console.h> |
T Michael Turney | 47a0832 | 2019-11-27 19:22:45 -0800 | [diff] [blame] | 5 | #include <lib.h> |
| 6 | #include <soc/clock.h> |
| 7 | #include <soc/gpio.h> |
| 8 | #include <soc/qcom_qup_se.h> |
| 9 | #include <soc/qupv3_config.h> |
| 10 | #include <soc/qupv3_spi.h> |
Elyes HAOUAS | 75f75bf | 2020-07-22 11:45:50 +0200 | [diff] [blame] | 11 | #include <types.h> |
T Michael Turney | 47a0832 | 2019-11-27 19:22:45 -0800 | [diff] [blame] | 12 | |
| 13 | /* SE_SPI_LOOPBACK register fields */ |
| 14 | #define LOOPBACK_ENABLE 0x1 |
| 15 | |
| 16 | /* SE_SPI_WORD_LEN register fields */ |
| 17 | #define WORD_LEN_MSK GENMASK(9, 0) |
| 18 | #define MIN_WORD_LEN 4 |
| 19 | |
| 20 | /* SPI_TX/SPI_RX_TRANS_LEN fields */ |
| 21 | #define TRANS_LEN_MSK GENMASK(23, 0) |
| 22 | |
| 23 | /* M_CMD OP codes for SPI */ |
| 24 | #define SPI_TX_ONLY 1 |
| 25 | #define SPI_RX_ONLY 2 |
| 26 | #define SPI_FULL_DUPLEX 3 |
| 27 | #define SPI_TX_RX 7 |
| 28 | #define SPI_CS_ASSERT 8 |
| 29 | #define SPI_CS_DEASSERT 9 |
| 30 | #define SPI_SCK_ONLY 10 |
| 31 | |
| 32 | /* M_CMD params for SPI */ |
| 33 | /* If fragmentation bit is set then CS will not toggle after each transfer */ |
| 34 | #define M_CMD_FRAGMENTATION BIT(2) |
| 35 | |
| 36 | #define BITS_PER_BYTE 8 |
| 37 | #define BITS_PER_WORD 8 |
| 38 | #define TX_WATERMARK 1 |
| 39 | |
| 40 | #define IRQ_TRIGGER (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN | \ |
| 41 | M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN | \ |
| 42 | M_CMD_CANCEL_EN | M_CMD_ABORT_EN) |
| 43 | |
| 44 | static void setup_fifo_params(const struct spi_slave *slave) |
| 45 | { |
| 46 | unsigned int se_bus = slave->bus; |
| 47 | struct qup_regs *regs = qup[se_bus].regs; |
| 48 | u32 word_len = 0; |
| 49 | |
| 50 | /* Disable loopback mode */ |
| 51 | write32(®s->proto_loopback_cfg, 0); |
| 52 | |
| 53 | write32(®s->spi_demux_sel, slave->cs); |
| 54 | word_len = ((BITS_PER_WORD - MIN_WORD_LEN) & WORD_LEN_MSK); |
| 55 | write32(®s->spi_word_len, word_len); |
| 56 | |
| 57 | /* FIFO PACKING CONFIGURATION */ |
| 58 | write32(®s->geni_tx_packing_cfg0, PACK_VECTOR0 |
| 59 | | (PACK_VECTOR1 << 10)); |
| 60 | write32(®s->geni_tx_packing_cfg1, PACK_VECTOR2 |
| 61 | | (PACK_VECTOR3 << 10)); |
| 62 | write32(®s->geni_rx_packing_cfg0, PACK_VECTOR0 |
| 63 | | (PACK_VECTOR1 << 10)); |
| 64 | write32(®s->geni_rx_packing_cfg1, PACK_VECTOR2 |
| 65 | | (PACK_VECTOR3 << 10)); |
| 66 | write32(®s->geni_byte_granularity, (log2(BITS_PER_WORD) - 3)); |
| 67 | } |
| 68 | |
| 69 | static void qup_setup_m_cmd(unsigned int se_bus, u32 cmd, u32 params) |
| 70 | { |
| 71 | struct qup_regs *regs = qup[se_bus].regs; |
| 72 | u32 m_cmd = (cmd << M_OPCODE_SHFT); |
| 73 | |
| 74 | m_cmd |= (params & M_PARAMS_MSK); |
| 75 | write32(®s->geni_m_cmd0, m_cmd); |
| 76 | } |
| 77 | |
| 78 | int qup_spi_xfer(const struct spi_slave *slave, const void *dout, |
| 79 | size_t bytes_out, void *din, size_t bytes_in) |
| 80 | { |
| 81 | u32 m_cmd = 0; |
| 82 | u32 m_param = M_CMD_FRAGMENTATION; |
| 83 | int size; |
| 84 | unsigned int se_bus = slave->bus; |
| 85 | struct qup_regs *regs = qup[se_bus].regs; |
| 86 | |
| 87 | if ((bytes_in == 0) && (bytes_out == 0)) |
| 88 | return 0; |
| 89 | |
| 90 | setup_fifo_params(slave); |
| 91 | |
| 92 | if (!bytes_out) { |
| 93 | size = bytes_in; |
| 94 | m_cmd = SPI_RX_ONLY; |
| 95 | dout = NULL; |
| 96 | } else if (!bytes_in) { |
| 97 | size = bytes_out; |
| 98 | m_cmd = SPI_TX_ONLY; |
| 99 | din = NULL; |
| 100 | } else { |
| 101 | size = MIN(bytes_in, bytes_out); |
| 102 | m_cmd = SPI_FULL_DUPLEX; |
| 103 | } |
| 104 | |
| 105 | /* Check for maximum permissible transfer length */ |
| 106 | assert(!(size & ~TRANS_LEN_MSK)); |
| 107 | |
| 108 | if (bytes_out) { |
| 109 | write32(®s->spi_tx_trans_len, size); |
| 110 | write32(®s->geni_tx_watermark_reg, TX_WATERMARK); |
| 111 | } |
| 112 | if (bytes_in) |
| 113 | write32(®s->spi_rx_trans_len, size); |
| 114 | |
| 115 | qup_setup_m_cmd(se_bus, m_cmd, m_param); |
| 116 | |
| 117 | if (qup_handle_transfer(se_bus, dout, din, size)) |
| 118 | return -1; |
| 119 | |
| 120 | qup_spi_xfer(slave, dout + size, MAX((int)bytes_out - size, 0), |
| 121 | din + size, MAX((int)bytes_in - size, 0)); |
| 122 | |
| 123 | return 0; |
| 124 | } |
| 125 | |
| 126 | static int spi_qup_set_cs(const struct spi_slave *slave, bool enable) |
| 127 | { |
| 128 | u32 m_cmd = 0; |
| 129 | u32 m_irq = 0; |
| 130 | unsigned int se_bus = slave->bus; |
| 131 | struct stopwatch sw; |
| 132 | |
| 133 | m_cmd = (enable) ? SPI_CS_ASSERT : SPI_CS_DEASSERT; |
| 134 | qup_setup_m_cmd(se_bus, m_cmd, 0); |
| 135 | |
| 136 | stopwatch_init_usecs_expire(&sw, 100); |
| 137 | do { |
| 138 | m_irq = qup_wait_for_m_irq(se_bus); |
| 139 | if (m_irq & M_CMD_DONE_EN) { |
| 140 | write32(&qup[se_bus].regs->geni_m_irq_clear, m_irq); |
| 141 | break; |
| 142 | } |
| 143 | write32(&qup[se_bus].regs->geni_m_irq_clear, m_irq); |
| 144 | } while (!stopwatch_expired(&sw)); |
| 145 | |
| 146 | if (!(m_irq & M_CMD_DONE_EN)) { |
| 147 | printk(BIOS_INFO, "%s:Failed to %s chip\n", __func__, |
| 148 | (enable) ? "Assert" : "Deassert"); |
| 149 | qup_m_cancel_and_abort(se_bus); |
| 150 | return -1; |
| 151 | } |
| 152 | return 0; |
| 153 | } |
| 154 | |
| 155 | void qup_spi_init(unsigned int bus, unsigned int speed_hz) |
| 156 | { |
| 157 | u32 m_clk_cfg = 0, div = DEFAULT_SE_CLK / speed_hz; |
| 158 | struct qup_regs *regs = qup[bus].regs; |
| 159 | |
| 160 | /* Make sure div can hit target frequency within +/- 1KHz range */ |
| 161 | assert(((DEFAULT_SE_CLK - speed_hz * div) <= div * KHz) && (div > 0)); |
| 162 | qupv3_se_fw_load_and_init(bus, SE_PROTOCOL_SPI, MIXED); |
| 163 | clock_enable_qup(bus); |
| 164 | m_clk_cfg |= ((div << CLK_DIV_SHFT) | SER_CLK_EN); |
| 165 | write32(®s->geni_ser_m_clk_cfg, m_clk_cfg); |
| 166 | /* Mode:0, cpha=0, cpol=0 */ |
| 167 | write32(®s->spi_cpha, 0); |
| 168 | write32(®s->spi_cpol, 0); |
| 169 | |
| 170 | /* Serial engine IO initialization */ |
| 171 | write32(®s->geni_cgc_ctrl, DEFAULT_CGC_EN); |
| 172 | write32(®s->dma_general_cfg, |
| 173 | (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
| 174 | | DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON)); |
| 175 | write32(®s->geni_output_ctrl, |
| 176 | DEFAULT_IO_OUTPUT_CTRL_MSK); |
| 177 | write32(®s->geni_force_default_reg, FORCE_DEFAULT); |
| 178 | |
| 179 | /* Serial engine IO set mode */ |
| 180 | write32(®s->se_irq_en, (GENI_M_IRQ_EN | |
| 181 | GENI_S_IRQ_EN | DMA_TX_IRQ_EN | DMA_RX_IRQ_EN)); |
| 182 | write32(®s->se_gsi_event_en, 0); |
| 183 | |
| 184 | /* Set RX and RFR watermark */ |
| 185 | write32(®s->geni_rx_watermark_reg, 0); |
| 186 | write32(®s->geni_rx_rfr_watermark_reg, FIFO_DEPTH - 2); |
| 187 | |
| 188 | /* GPIO Configuration */ |
| 189 | gpio_configure(qup[bus].pin[0], qup[bus].func[0], GPIO_NO_PULL, |
| 190 | GPIO_6MA, GPIO_INPUT); /* MISO */ |
| 191 | gpio_configure(qup[bus].pin[1], qup[bus].func[1], GPIO_NO_PULL, |
| 192 | GPIO_6MA, GPIO_OUTPUT); /* MOSI */ |
| 193 | gpio_configure(qup[bus].pin[2], qup[bus].func[2], GPIO_NO_PULL, |
| 194 | GPIO_6MA, GPIO_OUTPUT); /* CLK */ |
| 195 | gpio_configure(qup[bus].pin[3], qup[bus].func[3], GPIO_NO_PULL, |
| 196 | GPIO_6MA, GPIO_OUTPUT); /* CS */ |
| 197 | |
| 198 | /* Select and setup FIFO mode */ |
| 199 | write32(®s->geni_m_irq_clear, 0xFFFFFFFF); |
| 200 | write32(®s->geni_s_irq_clear, 0xFFFFFFFF); |
| 201 | write32(®s->dma_tx_irq_clr, 0xFFFFFFFF); |
| 202 | write32(®s->dma_rx_irq_clr, 0xFFFFFFFF); |
| 203 | write32(®s->geni_m_irq_enable, (M_COMMON_GENI_M_IRQ_EN | |
| 204 | M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN | |
| 205 | M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)); |
| 206 | write32(®s->geni_s_irq_enable, (S_COMMON_GENI_S_IRQ_EN |
| 207 | | S_CMD_DONE_EN)); |
| 208 | clrbits32(®s->geni_dma_mode_en, GENI_DMA_MODE_EN); |
| 209 | } |
| 210 | |
| 211 | int qup_spi_claim_bus(const struct spi_slave *slave) |
| 212 | { |
| 213 | return spi_qup_set_cs(slave, 1); |
| 214 | } |
| 215 | |
| 216 | void qup_spi_release_bus(const struct spi_slave *slave) |
| 217 | { |
| 218 | spi_qup_set_cs(slave, 0); |
| 219 | } |