1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <console/console.h>
8 #include <soc/qcom_qup_se.h>
9 #include <soc/qup_se_handlers_common.h>
10 #include <soc/qupv3_config_common.h>
11 #include <soc/qupv3_spi_common.h>
14 /* SE_SPI_LOOPBACK register fields */
15 #define LOOPBACK_ENABLE 0x1
17 /* SE_SPI_WORD_LEN register fields */
18 #define WORD_LEN_MSK QC_GENMASK(9, 0)
19 #define MIN_WORD_LEN 4
21 /* SPI_TX/SPI_RX_TRANS_LEN fields */
22 #define TRANS_LEN_MSK QC_GENMASK(23, 0)
24 /* M_CMD OP codes for SPI */
27 #define SPI_FULL_DUPLEX 3
29 #define SPI_CS_ASSERT 8
30 #define SPI_CS_DEASSERT 9
31 #define SPI_SCK_ONLY 10
33 /* M_CMD params for SPI */
34 /* If fragmentation bit is set then CS will not toggle after each transfer */
35 #define M_CMD_FRAGMENTATION BIT(2)
37 #define BITS_PER_BYTE 8
38 #define BITS_PER_WORD 8
39 #define TX_WATERMARK 1
41 #define IRQ_TRIGGER (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN | \
42 M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN | \
43 M_CMD_CANCEL_EN | M_CMD_ABORT_EN)
45 static void setup_fifo_params(const struct spi_slave
*slave
)
47 unsigned int se_bus
= slave
->bus
;
48 struct qup_regs
*regs
= qup
[se_bus
].regs
;
51 /* Disable loopback mode */
52 write32(®s
->proto_loopback_cfg
, 0);
54 write32(®s
->spi_demux_sel
, slave
->cs
);
55 word_len
= ((BITS_PER_WORD
- MIN_WORD_LEN
) & WORD_LEN_MSK
);
56 write32(®s
->spi_word_len
, word_len
);
58 /* FIFO PACKING CONFIGURATION */
59 write32(®s
->geni_tx_packing_cfg0
, PACK_VECTOR0
60 | (PACK_VECTOR1
<< 10));
61 write32(®s
->geni_tx_packing_cfg1
, PACK_VECTOR2
62 | (PACK_VECTOR3
<< 10));
63 write32(®s
->geni_rx_packing_cfg0
, PACK_VECTOR0
64 | (PACK_VECTOR1
<< 10));
65 write32(®s
->geni_rx_packing_cfg1
, PACK_VECTOR2
66 | (PACK_VECTOR3
<< 10));
67 write32(®s
->geni_byte_granularity
, (log2(BITS_PER_WORD
) - 3));
70 static void qup_setup_m_cmd(unsigned int se_bus
, u32 cmd
, u32 params
)
72 struct qup_regs
*regs
= qup
[se_bus
].regs
;
73 u32 m_cmd
= (cmd
<< M_OPCODE_SHFT
);
75 m_cmd
|= (params
& M_PARAMS_MSK
);
76 write32(®s
->geni_m_cmd0
, m_cmd
);
79 int qup_spi_xfer(const struct spi_slave
*slave
, const void *dout
,
80 size_t bytes_out
, void *din
, size_t bytes_in
)
83 u32 m_param
= M_CMD_FRAGMENTATION
;
85 unsigned int se_bus
= slave
->bus
;
86 struct qup_regs
*regs
= qup
[se_bus
].regs
;
87 struct stopwatch timeout
;
89 if ((bytes_in
== 0) && (bytes_out
== 0))
92 setup_fifo_params(slave
);
98 } else if (!bytes_in
) {
103 size
= MIN(bytes_in
, bytes_out
);
104 m_cmd
= SPI_FULL_DUPLEX
;
107 /* Check for maximum permissible transfer length */
108 assert(!(size
& ~TRANS_LEN_MSK
));
111 write32(®s
->spi_tx_trans_len
, size
);
112 write32(®s
->geni_tx_watermark_reg
, TX_WATERMARK
);
115 write32(®s
->spi_rx_trans_len
, size
);
117 qup_setup_m_cmd(se_bus
, m_cmd
, m_param
);
119 stopwatch_init_msecs_expire(&timeout
, 1000);
120 if (qup_handle_transfer(se_bus
, dout
, din
, size
, &timeout
))
123 qup_spi_xfer(slave
, dout
+ size
, MAX((int)bytes_out
- size
, 0),
124 din
+ size
, MAX((int)bytes_in
- size
, 0));
129 static int spi_qup_set_cs(const struct spi_slave
*slave
, bool enable
)
133 unsigned int se_bus
= slave
->bus
;
136 m_cmd
= (enable
) ? SPI_CS_ASSERT
: SPI_CS_DEASSERT
;
137 qup_setup_m_cmd(se_bus
, m_cmd
, 0);
139 stopwatch_init_usecs_expire(&sw
, 100);
141 m_irq
= qup_wait_for_m_irq(se_bus
);
142 if (m_irq
& M_CMD_DONE_EN
) {
143 write32(&qup
[se_bus
].regs
->geni_m_irq_clear
, m_irq
);
146 write32(&qup
[se_bus
].regs
->geni_m_irq_clear
, m_irq
);
147 } while (!stopwatch_expired(&sw
));
149 if (!(m_irq
& M_CMD_DONE_EN
)) {
150 printk(BIOS_INFO
, "%s:Failed to %s chip\n", __func__
,
151 (enable
) ? "Assert" : "Deassert");
152 qup_m_cancel_and_abort(se_bus
);
158 void qup_spi_init(unsigned int bus
, unsigned int speed_hz
)
160 u32 m_clk_cfg
= 0, div
= DEFAULT_SE_CLK
/ speed_hz
;
161 struct qup_regs
*regs
= qup
[bus
].regs
;
163 /* Make sure div can hit target frequency within +/- 1KHz range */
164 assert(((DEFAULT_SE_CLK
- speed_hz
* div
) <= div
* KHz
) && (div
> 0));
165 qupv3_se_fw_load_and_init(bus
, SE_PROTOCOL_SPI
, MIXED
);
166 clock_enable_qup(bus
);
167 m_clk_cfg
|= ((div
<< CLK_DIV_SHFT
) | SER_CLK_EN
);
168 write32(®s
->geni_ser_m_clk_cfg
, m_clk_cfg
);
169 /* Mode:0, cpha=0, cpol=0 */
170 write32(®s
->spi_cpha
, 0);
171 write32(®s
->spi_cpol
, 0);
173 /* Serial engine IO initialization */
174 write32(®s
->geni_cgc_ctrl
, DEFAULT_CGC_EN
);
175 write32(®s
->dma_general_cfg
,
176 (AHB_SEC_SLV_CLK_CGC_ON
| DMA_AHB_SLV_CFG_ON
177 | DMA_TX_CLK_CGC_ON
| DMA_RX_CLK_CGC_ON
));
178 write32(®s
->geni_output_ctrl
,
179 DEFAULT_IO_OUTPUT_CTRL_MSK
);
180 write32(®s
->geni_force_default_reg
, FORCE_DEFAULT
);
182 /* Serial engine IO set mode */
183 write32(®s
->se_irq_en
, (GENI_M_IRQ_EN
|
184 GENI_S_IRQ_EN
| DMA_TX_IRQ_EN
| DMA_RX_IRQ_EN
));
185 write32(®s
->se_gsi_event_en
, 0);
187 /* Set RX and RFR watermark */
188 write32(®s
->geni_rx_watermark_reg
, 0);
189 write32(®s
->geni_rx_rfr_watermark_reg
, FIFO_DEPTH
- 2);
191 /* GPIO Configuration */
192 gpio_configure(qup
[bus
].pin
[0], qup
[bus
].func
[0], GPIO_NO_PULL
,
193 GPIO_6MA
, GPIO_INPUT
); /* MISO */
194 gpio_configure(qup
[bus
].pin
[1], qup
[bus
].func
[1], GPIO_NO_PULL
,
195 GPIO_6MA
, GPIO_OUTPUT
); /* MOSI */
196 gpio_configure(qup
[bus
].pin
[2], qup
[bus
].func
[2], GPIO_NO_PULL
,
197 GPIO_6MA
, GPIO_OUTPUT
); /* CLK */
198 gpio_configure(qup
[bus
].pin
[3], qup
[bus
].func
[3], GPIO_NO_PULL
,
199 GPIO_6MA
, GPIO_OUTPUT
); /* CS */
201 /* Select and setup FIFO mode */
202 write32(®s
->geni_m_irq_clear
, 0xFFFFFFFF);
203 write32(®s
->geni_s_irq_clear
, 0xFFFFFFFF);
204 write32(®s
->dma_tx_irq_clr
, 0xFFFFFFFF);
205 write32(®s
->dma_rx_irq_clr
, 0xFFFFFFFF);
206 write32(®s
->geni_m_irq_enable
, (M_COMMON_GENI_M_IRQ_EN
|
207 M_CMD_DONE_EN
| M_TX_FIFO_WATERMARK_EN
|
208 M_RX_FIFO_WATERMARK_EN
| M_RX_FIFO_LAST_EN
));
209 write32(®s
->geni_s_irq_enable
, (S_COMMON_GENI_S_IRQ_EN
211 clrbits32(®s
->geni_dma_mode_en
, GENI_DMA_MODE_EN
);
214 int qup_spi_claim_bus(const struct spi_slave
*slave
)
216 return spi_qup_set_cs(slave
, 1);
219 void qup_spi_release_bus(const struct spi_slave
*slave
)
221 spi_qup_set_cs(slave
, 0);