xref: /linux/drivers/spi/spi-axiado.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*e75a6b00SVladimir Moravcevic // SPDX-License-Identifier: GPL-2.0-or-later
2*e75a6b00SVladimir Moravcevic //
3*e75a6b00SVladimir Moravcevic // Axiado SPI controller driver (Host mode only)
4*e75a6b00SVladimir Moravcevic //
5*e75a6b00SVladimir Moravcevic // Copyright (C) 2022-2025 Axiado Corporation (or its affiliates).
6*e75a6b00SVladimir Moravcevic //
7*e75a6b00SVladimir Moravcevic 
8*e75a6b00SVladimir Moravcevic #include <linux/clk.h>
9*e75a6b00SVladimir Moravcevic #include <linux/delay.h>
10*e75a6b00SVladimir Moravcevic #include <linux/gpio/consumer.h>
11*e75a6b00SVladimir Moravcevic #include <linux/interrupt.h>
12*e75a6b00SVladimir Moravcevic #include <linux/io.h>
13*e75a6b00SVladimir Moravcevic #include <linux/module.h>
14*e75a6b00SVladimir Moravcevic #include <linux/of_irq.h>
15*e75a6b00SVladimir Moravcevic #include <linux/of_address.h>
16*e75a6b00SVladimir Moravcevic #include <linux/platform_device.h>
17*e75a6b00SVladimir Moravcevic #include <linux/pm_runtime.h>
18*e75a6b00SVladimir Moravcevic #include <linux/spi/spi.h>
19*e75a6b00SVladimir Moravcevic #include <linux/spi/spi-mem.h>
20*e75a6b00SVladimir Moravcevic #include <linux/sizes.h>
21*e75a6b00SVladimir Moravcevic 
22*e75a6b00SVladimir Moravcevic #include "spi-axiado.h"
23*e75a6b00SVladimir Moravcevic 
24*e75a6b00SVladimir Moravcevic /**
25*e75a6b00SVladimir Moravcevic  * ax_spi_read - Register Read - 32 bit per word
26*e75a6b00SVladimir Moravcevic  * @xspi:	Pointer to the ax_spi structure
27*e75a6b00SVladimir Moravcevic  * @offset:	Register offset address
28*e75a6b00SVladimir Moravcevic  *
29*e75a6b00SVladimir Moravcevic  * @return:	Returns the value of that register
30*e75a6b00SVladimir Moravcevic  */
31*e75a6b00SVladimir Moravcevic static inline u32 ax_spi_read(struct ax_spi *xspi, u32 offset)
32*e75a6b00SVladimir Moravcevic {
33*e75a6b00SVladimir Moravcevic 	return readl_relaxed(xspi->regs + offset);
34*e75a6b00SVladimir Moravcevic }
35*e75a6b00SVladimir Moravcevic 
36*e75a6b00SVladimir Moravcevic /**
37*e75a6b00SVladimir Moravcevic  * ax_spi_write - Register write - 32 bit per word
38*e75a6b00SVladimir Moravcevic  * @xspi:	Pointer to the ax_spi structure
39*e75a6b00SVladimir Moravcevic  * @offset:	Register offset address
40*e75a6b00SVladimir Moravcevic  * @val:	Value to write into that register
41*e75a6b00SVladimir Moravcevic  */
42*e75a6b00SVladimir Moravcevic static inline void ax_spi_write(struct ax_spi *xspi, u32 offset, u32 val)
43*e75a6b00SVladimir Moravcevic {
44*e75a6b00SVladimir Moravcevic 	writel_relaxed(val, xspi->regs + offset);
45*e75a6b00SVladimir Moravcevic }
46*e75a6b00SVladimir Moravcevic 
47*e75a6b00SVladimir Moravcevic /**
48*e75a6b00SVladimir Moravcevic  * ax_spi_write_b - Register Read - 8 bit per word
49*e75a6b00SVladimir Moravcevic  * @xspi:	Pointer to the ax_spi structure
50*e75a6b00SVladimir Moravcevic  * @offset:	Register offset address
51*e75a6b00SVladimir Moravcevic  * @val:	Value to write into that register
52*e75a6b00SVladimir Moravcevic  */
53*e75a6b00SVladimir Moravcevic static inline void ax_spi_write_b(struct ax_spi *xspi, u32 offset, u8 val)
54*e75a6b00SVladimir Moravcevic {
55*e75a6b00SVladimir Moravcevic 	writeb_relaxed(val, xspi->regs + offset);
56*e75a6b00SVladimir Moravcevic }
57*e75a6b00SVladimir Moravcevic 
58*e75a6b00SVladimir Moravcevic /**
59*e75a6b00SVladimir Moravcevic  * ax_spi_init_hw - Initialize the hardware and configure the SPI controller
60*e75a6b00SVladimir Moravcevic  * @xspi:	Pointer to the ax_spi structure
61*e75a6b00SVladimir Moravcevic  *
62*e75a6b00SVladimir Moravcevic  * * On reset the SPI controller is configured to be in host mode.
63*e75a6b00SVladimir Moravcevic  * In host mode baud rate divisor is set to 4, threshold value for TX FIFO
64*e75a6b00SVladimir Moravcevic  * not full interrupt is set to 1 and size of the word to be transferred as 8 bit.
65*e75a6b00SVladimir Moravcevic  *
66*e75a6b00SVladimir Moravcevic  * This function initializes the SPI controller to disable and clear all the
67*e75a6b00SVladimir Moravcevic  * interrupts, enable manual target select and manual start, deselect all the
68*e75a6b00SVladimir Moravcevic  * chip select lines, and enable the SPI controller.
69*e75a6b00SVladimir Moravcevic  */
70*e75a6b00SVladimir Moravcevic static void ax_spi_init_hw(struct ax_spi *xspi)
71*e75a6b00SVladimir Moravcevic {
72*e75a6b00SVladimir Moravcevic 	u32 reg_value;
73*e75a6b00SVladimir Moravcevic 
74*e75a6b00SVladimir Moravcevic 	/* Clear CR1 */
75*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR1, AX_SPI_CR1_CLR);
76*e75a6b00SVladimir Moravcevic 
77*e75a6b00SVladimir Moravcevic 	/* CR1 - CPO CHP MSS SCE SCR */
78*e75a6b00SVladimir Moravcevic 	reg_value = ax_spi_read(xspi, AX_SPI_CR1);
79*e75a6b00SVladimir Moravcevic 	reg_value |= AX_SPI_CR1_SCR | AX_SPI_CR1_SCE;
80*e75a6b00SVladimir Moravcevic 
81*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR1, reg_value);
82*e75a6b00SVladimir Moravcevic 
83*e75a6b00SVladimir Moravcevic 	/* CR2 - MTE SRD SWD SSO */
84*e75a6b00SVladimir Moravcevic 	reg_value = ax_spi_read(xspi, AX_SPI_CR2);
85*e75a6b00SVladimir Moravcevic 	reg_value |= AX_SPI_CR2_SWD | AX_SPI_CR2_SRD;
86*e75a6b00SVladimir Moravcevic 
87*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR2, reg_value);
88*e75a6b00SVladimir Moravcevic 
89*e75a6b00SVladimir Moravcevic 	/* CR3 - Reserverd bits S3W SDL */
90*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR3, AX_SPI_CR3_SDL);
91*e75a6b00SVladimir Moravcevic 
92*e75a6b00SVladimir Moravcevic 	/* SCDR - Reserved bits SCS SCD */
93*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_SCDR, (AX_SPI_SCDR_SCS | AX_SPI_SCD_DEFAULT));
94*e75a6b00SVladimir Moravcevic 
95*e75a6b00SVladimir Moravcevic 	/* IMR */
96*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_IMR, AX_SPI_IMR_CLR);
97*e75a6b00SVladimir Moravcevic 
98*e75a6b00SVladimir Moravcevic 	/* ISR - Clear all the interrupt */
99*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_ISR, AX_SPI_ISR_CLR);
100*e75a6b00SVladimir Moravcevic }
101*e75a6b00SVladimir Moravcevic 
102*e75a6b00SVladimir Moravcevic /**
103*e75a6b00SVladimir Moravcevic  * ax_spi_chipselect - Select or deselect the chip select line
104*e75a6b00SVladimir Moravcevic  * @spi:	Pointer to the spi_device structure
105*e75a6b00SVladimir Moravcevic  * @is_high:	Select(0) or deselect (1) the chip select line
106*e75a6b00SVladimir Moravcevic  */
107*e75a6b00SVladimir Moravcevic static void ax_spi_chipselect(struct spi_device *spi, bool is_high)
108*e75a6b00SVladimir Moravcevic {
109*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(spi->controller);
110*e75a6b00SVladimir Moravcevic 	u32 ctrl_reg;
111*e75a6b00SVladimir Moravcevic 
112*e75a6b00SVladimir Moravcevic 	ctrl_reg = ax_spi_read(xspi, AX_SPI_CR2);
113*e75a6b00SVladimir Moravcevic 	/* Reset the chip select */
114*e75a6b00SVladimir Moravcevic 	ctrl_reg &= ~AX_SPI_DEFAULT_TS_MASK;
115*e75a6b00SVladimir Moravcevic 	ctrl_reg |= spi_get_chipselect(spi, 0);
116*e75a6b00SVladimir Moravcevic 
117*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR2, ctrl_reg);
118*e75a6b00SVladimir Moravcevic }
119*e75a6b00SVladimir Moravcevic 
120*e75a6b00SVladimir Moravcevic /**
121*e75a6b00SVladimir Moravcevic  * ax_spi_config_clock_mode - Sets clock polarity and phase
122*e75a6b00SVladimir Moravcevic  * @spi:	Pointer to the spi_device structure
123*e75a6b00SVladimir Moravcevic  *
124*e75a6b00SVladimir Moravcevic  * Sets the requested clock polarity and phase.
125*e75a6b00SVladimir Moravcevic  */
126*e75a6b00SVladimir Moravcevic static void ax_spi_config_clock_mode(struct spi_device *spi)
127*e75a6b00SVladimir Moravcevic {
128*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(spi->controller);
129*e75a6b00SVladimir Moravcevic 	u32 ctrl_reg, new_ctrl_reg;
130*e75a6b00SVladimir Moravcevic 
131*e75a6b00SVladimir Moravcevic 	new_ctrl_reg = ax_spi_read(xspi, AX_SPI_CR1);
132*e75a6b00SVladimir Moravcevic 	ctrl_reg = new_ctrl_reg;
133*e75a6b00SVladimir Moravcevic 
134*e75a6b00SVladimir Moravcevic 	/* Set the SPI clock phase and clock polarity */
135*e75a6b00SVladimir Moravcevic 	new_ctrl_reg &= ~(AX_SPI_CR1_CPHA | AX_SPI_CR1_CPOL);
136*e75a6b00SVladimir Moravcevic 	if (spi->mode & SPI_CPHA)
137*e75a6b00SVladimir Moravcevic 		new_ctrl_reg |= AX_SPI_CR1_CPHA;
138*e75a6b00SVladimir Moravcevic 	if (spi->mode & SPI_CPOL)
139*e75a6b00SVladimir Moravcevic 		new_ctrl_reg |= AX_SPI_CR1_CPOL;
140*e75a6b00SVladimir Moravcevic 
141*e75a6b00SVladimir Moravcevic 	if (new_ctrl_reg != ctrl_reg)
142*e75a6b00SVladimir Moravcevic 		ax_spi_write(xspi, AX_SPI_CR1, new_ctrl_reg);
143*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR1, 0x03);
144*e75a6b00SVladimir Moravcevic }
145*e75a6b00SVladimir Moravcevic 
146*e75a6b00SVladimir Moravcevic /**
147*e75a6b00SVladimir Moravcevic  * ax_spi_config_clock_freq - Sets clock frequency
148*e75a6b00SVladimir Moravcevic  * @spi:	Pointer to the spi_device structure
149*e75a6b00SVladimir Moravcevic  * @transfer:	Pointer to the spi_transfer structure which provides
150*e75a6b00SVladimir Moravcevic  *		information about next transfer setup parameters
151*e75a6b00SVladimir Moravcevic  *
152*e75a6b00SVladimir Moravcevic  * Sets the requested clock frequency.
153*e75a6b00SVladimir Moravcevic  * Note: If the requested frequency is not an exact match with what can be
154*e75a6b00SVladimir Moravcevic  * obtained using the prescalar value the driver sets the clock frequency which
155*e75a6b00SVladimir Moravcevic  * is lower than the requested frequency (maximum lower) for the transfer. If
156*e75a6b00SVladimir Moravcevic  * the requested frequency is higher or lower than that is supported by the SPI
157*e75a6b00SVladimir Moravcevic  * controller the driver will set the highest or lowest frequency supported by
158*e75a6b00SVladimir Moravcevic  * controller.
159*e75a6b00SVladimir Moravcevic  */
160*e75a6b00SVladimir Moravcevic static void ax_spi_config_clock_freq(struct spi_device *spi,
161*e75a6b00SVladimir Moravcevic 				     struct spi_transfer *transfer)
162*e75a6b00SVladimir Moravcevic {
163*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(spi->controller);
164*e75a6b00SVladimir Moravcevic 
165*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_SCDR, (AX_SPI_SCDR_SCS | AX_SPI_SCD_DEFAULT));
166*e75a6b00SVladimir Moravcevic }
167*e75a6b00SVladimir Moravcevic 
168*e75a6b00SVladimir Moravcevic /**
169*e75a6b00SVladimir Moravcevic  * ax_spi_setup_transfer - Configure SPI controller for specified transfer
170*e75a6b00SVladimir Moravcevic  * @spi:	Pointer to the spi_device structure
171*e75a6b00SVladimir Moravcevic  * @transfer:	Pointer to the spi_transfer structure which provides
172*e75a6b00SVladimir Moravcevic  *		information about next transfer setup parameters
173*e75a6b00SVladimir Moravcevic  *
174*e75a6b00SVladimir Moravcevic  * Sets the operational mode of SPI controller for the next SPI transfer and
175*e75a6b00SVladimir Moravcevic  * sets the requested clock frequency.
176*e75a6b00SVladimir Moravcevic  *
177*e75a6b00SVladimir Moravcevic  */
178*e75a6b00SVladimir Moravcevic static void ax_spi_setup_transfer(struct spi_device *spi,
179*e75a6b00SVladimir Moravcevic 				 struct spi_transfer *transfer)
180*e75a6b00SVladimir Moravcevic {
181*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(spi->controller);
182*e75a6b00SVladimir Moravcevic 
183*e75a6b00SVladimir Moravcevic 	ax_spi_config_clock_freq(spi, transfer);
184*e75a6b00SVladimir Moravcevic 
185*e75a6b00SVladimir Moravcevic 	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u clock speed\n",
186*e75a6b00SVladimir Moravcevic 		__func__, spi->mode, spi->bits_per_word,
187*e75a6b00SVladimir Moravcevic 		xspi->speed_hz);
188*e75a6b00SVladimir Moravcevic }
189*e75a6b00SVladimir Moravcevic 
190*e75a6b00SVladimir Moravcevic /**
191*e75a6b00SVladimir Moravcevic  * ax_spi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible
192*e75a6b00SVladimir Moravcevic  * @xspi:	Pointer to the ax_spi structure
193*e75a6b00SVladimir Moravcevic  */
194*e75a6b00SVladimir Moravcevic static void ax_spi_fill_tx_fifo(struct ax_spi *xspi)
195*e75a6b00SVladimir Moravcevic {
196*e75a6b00SVladimir Moravcevic 	unsigned long trans_cnt = 0;
197*e75a6b00SVladimir Moravcevic 
198*e75a6b00SVladimir Moravcevic 	while ((trans_cnt < xspi->tx_fifo_depth) &&
199*e75a6b00SVladimir Moravcevic 	       (xspi->tx_bytes > 0)) {
200*e75a6b00SVladimir Moravcevic 		/* When xspi in busy condition, bytes may send failed,
201*e75a6b00SVladimir Moravcevic 		 * then spi control did't work thoroughly, add one byte delay
202*e75a6b00SVladimir Moravcevic 		 */
203*e75a6b00SVladimir Moravcevic 		if (ax_spi_read(xspi, AX_SPI_IVR) & AX_SPI_IVR_TFOV)
204*e75a6b00SVladimir Moravcevic 			usleep_range(10, 10);
205*e75a6b00SVladimir Moravcevic 		if (xspi->tx_buf)
206*e75a6b00SVladimir Moravcevic 			ax_spi_write_b(xspi, AX_SPI_TXFIFO, *xspi->tx_buf++);
207*e75a6b00SVladimir Moravcevic 		else
208*e75a6b00SVladimir Moravcevic 			ax_spi_write_b(xspi, AX_SPI_TXFIFO, 0);
209*e75a6b00SVladimir Moravcevic 
210*e75a6b00SVladimir Moravcevic 		xspi->tx_bytes--;
211*e75a6b00SVladimir Moravcevic 		trans_cnt++;
212*e75a6b00SVladimir Moravcevic 	}
213*e75a6b00SVladimir Moravcevic }
214*e75a6b00SVladimir Moravcevic 
215*e75a6b00SVladimir Moravcevic /**
216*e75a6b00SVladimir Moravcevic  * ax_spi_get_rx_byte - Gets a byte from the RX FIFO buffer
217*e75a6b00SVladimir Moravcevic  * @xspi: Controller private data (struct ax_spi *)
218*e75a6b00SVladimir Moravcevic  *
219*e75a6b00SVladimir Moravcevic  * This function handles the logic of extracting bytes from the 32-bit RX FIFO.
220*e75a6b00SVladimir Moravcevic  * It reads a new 32-bit word from AX_SPI_RXFIFO only when the current buffered
221*e75a6b00SVladimir Moravcevic  * word has been fully processed (all 4 bytes extracted). It then extracts
222*e75a6b00SVladimir Moravcevic  * bytes one by one, assuming the controller is little-endian.
223*e75a6b00SVladimir Moravcevic  *
224*e75a6b00SVladimir Moravcevic  * Returns: The next 8-bit byte read from the RX FIFO stream.
225*e75a6b00SVladimir Moravcevic  */
226*e75a6b00SVladimir Moravcevic static u8 ax_spi_get_rx_byte_for_irq(struct ax_spi *xspi)
227*e75a6b00SVladimir Moravcevic {
228*e75a6b00SVladimir Moravcevic 	u8 byte_val;
229*e75a6b00SVladimir Moravcevic 
230*e75a6b00SVladimir Moravcevic 	/* If all bytes from the current 32-bit word have been extracted,
231*e75a6b00SVladimir Moravcevic 	 * read a new word from the hardware RX FIFO.
232*e75a6b00SVladimir Moravcevic 	 */
233*e75a6b00SVladimir Moravcevic 	if (xspi->bytes_left_in_current_rx_word_for_irq == 0) {
234*e75a6b00SVladimir Moravcevic 		xspi->current_rx_fifo_word_for_irq = ax_spi_read(xspi, AX_SPI_RXFIFO);
235*e75a6b00SVladimir Moravcevic 		xspi->bytes_left_in_current_rx_word_for_irq = 4; // A new 32-bit word has 4 bytes
236*e75a6b00SVladimir Moravcevic 	}
237*e75a6b00SVladimir Moravcevic 
238*e75a6b00SVladimir Moravcevic 	/* Extract the least significant byte from the current 32-bit word */
239*e75a6b00SVladimir Moravcevic 	byte_val = (u8)(xspi->current_rx_fifo_word_for_irq & 0xFF);
240*e75a6b00SVladimir Moravcevic 
241*e75a6b00SVladimir Moravcevic 	/* Shift the word right by 8 bits to prepare the next byte for extraction */
242*e75a6b00SVladimir Moravcevic 	xspi->current_rx_fifo_word_for_irq >>= 8;
243*e75a6b00SVladimir Moravcevic 	xspi->bytes_left_in_current_rx_word_for_irq--;
244*e75a6b00SVladimir Moravcevic 
245*e75a6b00SVladimir Moravcevic 	return byte_val;
246*e75a6b00SVladimir Moravcevic }
247*e75a6b00SVladimir Moravcevic 
248*e75a6b00SVladimir Moravcevic /**
249*e75a6b00SVladimir Moravcevic  * Helper function to process received bytes and check for transfer completion.
250*e75a6b00SVladimir Moravcevic  * This avoids code duplication and centralizes the completion logic.
251*e75a6b00SVladimir Moravcevic  * Returns true if the transfer was finalized.
252*e75a6b00SVladimir Moravcevic  */
253*e75a6b00SVladimir Moravcevic static bool ax_spi_process_rx_and_finalize(struct spi_controller *ctlr)
254*e75a6b00SVladimir Moravcevic {
255*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
256*e75a6b00SVladimir Moravcevic 
257*e75a6b00SVladimir Moravcevic 	/* Process any remaining bytes in the RX FIFO */
258*e75a6b00SVladimir Moravcevic 	u32 avail_bytes = ax_spi_read(xspi, AX_SPI_RX_FBCAR);
259*e75a6b00SVladimir Moravcevic 
260*e75a6b00SVladimir Moravcevic 	/* This loop handles bytes that are already staged from a previous word read */
261*e75a6b00SVladimir Moravcevic 	while (xspi->bytes_left_in_current_rx_word_for_irq &&
262*e75a6b00SVladimir Moravcevic 	       (xspi->rx_copy_remaining || xspi->rx_discard)) {
263*e75a6b00SVladimir Moravcevic 		u8 b = ax_spi_get_rx_byte_for_irq(xspi);
264*e75a6b00SVladimir Moravcevic 
265*e75a6b00SVladimir Moravcevic 		if (xspi->rx_discard) {
266*e75a6b00SVladimir Moravcevic 			xspi->rx_discard--;
267*e75a6b00SVladimir Moravcevic 		} else {
268*e75a6b00SVladimir Moravcevic 			*xspi->rx_buf++ = b;
269*e75a6b00SVladimir Moravcevic 			xspi->rx_copy_remaining--;
270*e75a6b00SVladimir Moravcevic 		}
271*e75a6b00SVladimir Moravcevic 	}
272*e75a6b00SVladimir Moravcevic 
273*e75a6b00SVladimir Moravcevic 	/* This loop processes new words directly from the FIFO */
274*e75a6b00SVladimir Moravcevic 	while (avail_bytes >= 4 && (xspi->rx_copy_remaining || xspi->rx_discard)) {
275*e75a6b00SVladimir Moravcevic 		/* This function should handle reading from the FIFO */
276*e75a6b00SVladimir Moravcevic 		u8 b = ax_spi_get_rx_byte_for_irq(xspi);
277*e75a6b00SVladimir Moravcevic 
278*e75a6b00SVladimir Moravcevic 		if (xspi->rx_discard) {
279*e75a6b00SVladimir Moravcevic 			xspi->rx_discard--;
280*e75a6b00SVladimir Moravcevic 		} else {
281*e75a6b00SVladimir Moravcevic 			*xspi->rx_buf++ = b;
282*e75a6b00SVladimir Moravcevic 			xspi->rx_copy_remaining--;
283*e75a6b00SVladimir Moravcevic 		}
284*e75a6b00SVladimir Moravcevic 		/* ax_spi_get_rx_byte_for_irq fetches a new word when needed
285*e75a6b00SVladimir Moravcevic 		 * and updates internal state.
286*e75a6b00SVladimir Moravcevic 		 */
287*e75a6b00SVladimir Moravcevic 		if (xspi->bytes_left_in_current_rx_word_for_irq == 3)
288*e75a6b00SVladimir Moravcevic 			avail_bytes -= 4;
289*e75a6b00SVladimir Moravcevic 	}
290*e75a6b00SVladimir Moravcevic 
291*e75a6b00SVladimir Moravcevic 	/* Completion Check: The transfer is truly complete if all expected
292*e75a6b00SVladimir Moravcevic 	 * RX bytes have been copied or discarded.
293*e75a6b00SVladimir Moravcevic 	 */
294*e75a6b00SVladimir Moravcevic 	if (xspi->rx_copy_remaining == 0 && xspi->rx_discard == 0) {
295*e75a6b00SVladimir Moravcevic 		/* Defensive drain: If for some reason there are leftover bytes
296*e75a6b00SVladimir Moravcevic 		 * in the HW FIFO after we've logically finished,
297*e75a6b00SVladimir Moravcevic 		 * read and discard them to prevent them from corrupting the next transfer.
298*e75a6b00SVladimir Moravcevic 		 * This should be a bounded operation.
299*e75a6b00SVladimir Moravcevic 		 */
300*e75a6b00SVladimir Moravcevic 		int safety_words = AX_SPI_RX_FIFO_DRAIN_LIMIT; // Limit to avoid getting stuck
301*e75a6b00SVladimir Moravcevic 
302*e75a6b00SVladimir Moravcevic 		while (ax_spi_read(xspi, AX_SPI_RX_FBCAR) > 0 && safety_words-- > 0)
303*e75a6b00SVladimir Moravcevic 			ax_spi_read(xspi, AX_SPI_RXFIFO);
304*e75a6b00SVladimir Moravcevic 
305*e75a6b00SVladimir Moravcevic 		/* Disable all interrupts for this transfer and finalize. */
306*e75a6b00SVladimir Moravcevic 		ax_spi_write(xspi, AX_SPI_IMR, 0x00);
307*e75a6b00SVladimir Moravcevic 		spi_finalize_current_transfer(ctlr);
308*e75a6b00SVladimir Moravcevic 		return true;
309*e75a6b00SVladimir Moravcevic 	}
310*e75a6b00SVladimir Moravcevic 
311*e75a6b00SVladimir Moravcevic 	return false;
312*e75a6b00SVladimir Moravcevic }
313*e75a6b00SVladimir Moravcevic 
314*e75a6b00SVladimir Moravcevic /**
315*e75a6b00SVladimir Moravcevic  * ax_spi_irq - Interrupt service routine of the SPI controller
316*e75a6b00SVladimir Moravcevic  * @irq:	IRQ number
317*e75a6b00SVladimir Moravcevic  * @dev_id:	Pointer to the xspi structure
318*e75a6b00SVladimir Moravcevic  *
319*e75a6b00SVladimir Moravcevic  * This function handles RX FIFO almost full and Host Transfer Completed interrupts only.
320*e75a6b00SVladimir Moravcevic  * On RX FIFO amlost full interrupt this function reads the received data from RX FIFO and
321*e75a6b00SVladimir Moravcevic  * fills the TX FIFO if there is any data remaining to be transferred.
322*e75a6b00SVladimir Moravcevic  * On Host Transfer Completed interrupt this function indicates that transfer is completed,
323*e75a6b00SVladimir Moravcevic  * the SPI subsystem will clear MTC bit.
324*e75a6b00SVladimir Moravcevic  *
325*e75a6b00SVladimir Moravcevic  * Return:	IRQ_HANDLED when handled; IRQ_NONE otherwise.
326*e75a6b00SVladimir Moravcevic  */
327*e75a6b00SVladimir Moravcevic static irqreturn_t ax_spi_irq(int irq, void *dev_id)
328*e75a6b00SVladimir Moravcevic {
329*e75a6b00SVladimir Moravcevic 	struct spi_controller *ctlr = dev_id;
330*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
331*e75a6b00SVladimir Moravcevic 	u32 intr_status;
332*e75a6b00SVladimir Moravcevic 
333*e75a6b00SVladimir Moravcevic 	intr_status = ax_spi_read(xspi, AX_SPI_IVR);
334*e75a6b00SVladimir Moravcevic 	if (!intr_status)
335*e75a6b00SVladimir Moravcevic 		return IRQ_NONE;
336*e75a6b00SVladimir Moravcevic 
337*e75a6b00SVladimir Moravcevic 	/* Handle "Message Transfer Complete" interrupt.
338*e75a6b00SVladimir Moravcevic 	 * This means all bytes have been shifted out of the TX FIFO.
339*e75a6b00SVladimir Moravcevic 	 * It's time to harvest the final incoming bytes from the RX FIFO.
340*e75a6b00SVladimir Moravcevic 	 */
341*e75a6b00SVladimir Moravcevic 	if (intr_status & AX_SPI_IVR_MTCV) {
342*e75a6b00SVladimir Moravcevic 		/* Clear the MTC interrupt flag immediately. */
343*e75a6b00SVladimir Moravcevic 		ax_spi_write(xspi, AX_SPI_ISR, AX_SPI_ISR_MTC);
344*e75a6b00SVladimir Moravcevic 
345*e75a6b00SVladimir Moravcevic 		/* For a TX-only transfer, rx_buf would be NULL.
346*e75a6b00SVladimir Moravcevic 		 * In the spi-core, rx_copy_remaining would be 0.
347*e75a6b00SVladimir Moravcevic 		 * So we can finalize immediately.
348*e75a6b00SVladimir Moravcevic 		 */
349*e75a6b00SVladimir Moravcevic 		if (!xspi->rx_buf) {
350*e75a6b00SVladimir Moravcevic 			ax_spi_write(xspi, AX_SPI_IMR, 0x00);
351*e75a6b00SVladimir Moravcevic 			spi_finalize_current_transfer(ctlr);
352*e75a6b00SVladimir Moravcevic 			return IRQ_HANDLED;
353*e75a6b00SVladimir Moravcevic 		}
354*e75a6b00SVladimir Moravcevic 		/* For a full-duplex transfer, process any remaining RX data.
355*e75a6b00SVladimir Moravcevic 		 * The helper function will handle finalization if everything is received.
356*e75a6b00SVladimir Moravcevic 		 */
357*e75a6b00SVladimir Moravcevic 		ax_spi_process_rx_and_finalize(ctlr);
358*e75a6b00SVladimir Moravcevic 		return IRQ_HANDLED;
359*e75a6b00SVladimir Moravcevic 	}
360*e75a6b00SVladimir Moravcevic 
361*e75a6b00SVladimir Moravcevic 	/* Handle "RX FIFO Full / Threshold Met" interrupt.
362*e75a6b00SVladimir Moravcevic 	 * This means we need to make space in the RX FIFO by reading from it.
363*e75a6b00SVladimir Moravcevic 	 */
364*e75a6b00SVladimir Moravcevic 	if (intr_status & AX_SPI_IVR_RFFV) {
365*e75a6b00SVladimir Moravcevic 		if (ax_spi_process_rx_and_finalize(ctlr)) {
366*e75a6b00SVladimir Moravcevic 			/* Transfer was finalized inside the helper, we are done. */
367*e75a6b00SVladimir Moravcevic 		} else {
368*e75a6b00SVladimir Moravcevic 			/* RX is not yet complete. If there are still TX bytes to send
369*e75a6b00SVladimir Moravcevic 			 * (for very long transfers), we can fill the TX FIFO again.
370*e75a6b00SVladimir Moravcevic 			 */
371*e75a6b00SVladimir Moravcevic 			if (xspi->tx_bytes)
372*e75a6b00SVladimir Moravcevic 				ax_spi_fill_tx_fifo(xspi);
373*e75a6b00SVladimir Moravcevic 		}
374*e75a6b00SVladimir Moravcevic 		return IRQ_HANDLED;
375*e75a6b00SVladimir Moravcevic 	}
376*e75a6b00SVladimir Moravcevic 
377*e75a6b00SVladimir Moravcevic 	return IRQ_NONE;
378*e75a6b00SVladimir Moravcevic }
379*e75a6b00SVladimir Moravcevic 
380*e75a6b00SVladimir Moravcevic static int ax_prepare_message(struct spi_controller *ctlr,
381*e75a6b00SVladimir Moravcevic 			      struct spi_message *msg)
382*e75a6b00SVladimir Moravcevic {
383*e75a6b00SVladimir Moravcevic 	ax_spi_config_clock_mode(msg->spi);
384*e75a6b00SVladimir Moravcevic 	return 0;
385*e75a6b00SVladimir Moravcevic }
386*e75a6b00SVladimir Moravcevic 
387*e75a6b00SVladimir Moravcevic /**
388*e75a6b00SVladimir Moravcevic  * ax_transfer_one - Initiates the SPI transfer
389*e75a6b00SVladimir Moravcevic  * @ctlr:	Pointer to spi_controller structure
390*e75a6b00SVladimir Moravcevic  * @spi:	Pointer to the spi_device structure
391*e75a6b00SVladimir Moravcevic  * @transfer:	Pointer to the spi_transfer structure which provides
392*e75a6b00SVladimir Moravcevic  *		information about next transfer parameters
393*e75a6b00SVladimir Moravcevic  *
394*e75a6b00SVladimir Moravcevic  * This function fills the TX FIFO, starts the SPI transfer and
395*e75a6b00SVladimir Moravcevic  * returns a positive transfer count so that core will wait for completion.
396*e75a6b00SVladimir Moravcevic  *
397*e75a6b00SVladimir Moravcevic  * Return:	Number of bytes transferred in the last transfer
398*e75a6b00SVladimir Moravcevic  */
399*e75a6b00SVladimir Moravcevic static int ax_transfer_one(struct spi_controller *ctlr,
400*e75a6b00SVladimir Moravcevic 			   struct spi_device *spi,
401*e75a6b00SVladimir Moravcevic 			   struct spi_transfer *transfer)
402*e75a6b00SVladimir Moravcevic {
403*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
404*e75a6b00SVladimir Moravcevic 	int drain_limit;
405*e75a6b00SVladimir Moravcevic 
406*e75a6b00SVladimir Moravcevic 	/* Pre-transfer cleanup:Flush the RX FIFO to discard any stale data.
407*e75a6b00SVladimir Moravcevic 	 * This is the crucial part. Before every new transfer, we must ensure
408*e75a6b00SVladimir Moravcevic 	 * the HW is in a clean state to avoid processing stale data
409*e75a6b00SVladimir Moravcevic 	 * from a previous, possibly failed or interrupted, transfer.
410*e75a6b00SVladimir Moravcevic 	 */
411*e75a6b00SVladimir Moravcevic 	drain_limit = AX_SPI_RX_FIFO_DRAIN_LIMIT; // Sane limit to prevent infinite loop on HW error
412*e75a6b00SVladimir Moravcevic 	while (ax_spi_read(xspi, AX_SPI_RX_FBCAR) > 0 && drain_limit-- > 0)
413*e75a6b00SVladimir Moravcevic 		ax_spi_read(xspi, AX_SPI_RXFIFO); // Read and discard
414*e75a6b00SVladimir Moravcevic 
415*e75a6b00SVladimir Moravcevic 	if (drain_limit <= 0)
416*e75a6b00SVladimir Moravcevic 		dev_warn(&ctlr->dev, "RX FIFO drain timeout before transfer\n");
417*e75a6b00SVladimir Moravcevic 
418*e75a6b00SVladimir Moravcevic 	/* Clear any stale interrupt flags from a previous transfer.
419*e75a6b00SVladimir Moravcevic 	 * This prevents an immediate, false interrupt trigger.
420*e75a6b00SVladimir Moravcevic 	 */
421*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_ISR, AX_SPI_ISR_CLR);
422*e75a6b00SVladimir Moravcevic 
423*e75a6b00SVladimir Moravcevic 	xspi->tx_buf = transfer->tx_buf;
424*e75a6b00SVladimir Moravcevic 	xspi->rx_buf = transfer->rx_buf;
425*e75a6b00SVladimir Moravcevic 	xspi->tx_bytes = transfer->len;
426*e75a6b00SVladimir Moravcevic 	xspi->rx_bytes = transfer->len;
427*e75a6b00SVladimir Moravcevic 
428*e75a6b00SVladimir Moravcevic 	/* Reset RX 32-bit to byte buffer for each new transfer */
429*e75a6b00SVladimir Moravcevic 	if (transfer->tx_buf && !transfer->rx_buf) {
430*e75a6b00SVladimir Moravcevic 		/* TX mode: discard all received data */
431*e75a6b00SVladimir Moravcevic 		xspi->rx_discard = transfer->len;
432*e75a6b00SVladimir Moravcevic 		xspi->rx_copy_remaining = 0;
433*e75a6b00SVladimir Moravcevic 	} else if ((!transfer->tx_buf && transfer->rx_buf) ||
434*e75a6b00SVladimir Moravcevic 		   (transfer->tx_buf && transfer->rx_buf)) {
435*e75a6b00SVladimir Moravcevic 		/* RX mode: generate clock by filling TX FIFO with dummy bytes
436*e75a6b00SVladimir Moravcevic 		 * Full-duplex mode: generate clock by filling TX FIFO
437*e75a6b00SVladimir Moravcevic 		 */
438*e75a6b00SVladimir Moravcevic 		xspi->rx_discard = 0;
439*e75a6b00SVladimir Moravcevic 		xspi->rx_copy_remaining = transfer->len;
440*e75a6b00SVladimir Moravcevic 	} else {
441*e75a6b00SVladimir Moravcevic 		/* No TX and RX */
442*e75a6b00SVladimir Moravcevic 		xspi->rx_discard = 0;
443*e75a6b00SVladimir Moravcevic 		xspi->rx_copy_remaining = transfer->len;
444*e75a6b00SVladimir Moravcevic 	}
445*e75a6b00SVladimir Moravcevic 
446*e75a6b00SVladimir Moravcevic 	ax_spi_setup_transfer(spi, transfer);
447*e75a6b00SVladimir Moravcevic 	ax_spi_fill_tx_fifo(xspi);
448*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR2, (AX_SPI_CR2_HTE | AX_SPI_CR2_SRD | AX_SPI_CR2_SWD));
449*e75a6b00SVladimir Moravcevic 
450*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_IMR, (AX_SPI_IMR_MTCM | AX_SPI_IMR_RFFM));
451*e75a6b00SVladimir Moravcevic 	return transfer->len;
452*e75a6b00SVladimir Moravcevic }
453*e75a6b00SVladimir Moravcevic 
454*e75a6b00SVladimir Moravcevic /**
455*e75a6b00SVladimir Moravcevic  * ax_prepare_transfer_hardware - Prepares hardware for transfer.
456*e75a6b00SVladimir Moravcevic  * @ctlr:	Pointer to the spi_controller structure which provides
457*e75a6b00SVladimir Moravcevic  *		information about the controller.
458*e75a6b00SVladimir Moravcevic  *
459*e75a6b00SVladimir Moravcevic  * This function enables SPI host controller.
460*e75a6b00SVladimir Moravcevic  *
461*e75a6b00SVladimir Moravcevic  * Return:	0 always
462*e75a6b00SVladimir Moravcevic  */
463*e75a6b00SVladimir Moravcevic static int ax_prepare_transfer_hardware(struct spi_controller *ctlr)
464*e75a6b00SVladimir Moravcevic {
465*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
466*e75a6b00SVladimir Moravcevic 
467*e75a6b00SVladimir Moravcevic 	u32 reg_value;
468*e75a6b00SVladimir Moravcevic 
469*e75a6b00SVladimir Moravcevic 	reg_value = ax_spi_read(xspi, AX_SPI_CR1);
470*e75a6b00SVladimir Moravcevic 	reg_value |= AX_SPI_CR1_SCE;
471*e75a6b00SVladimir Moravcevic 
472*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR1, reg_value);
473*e75a6b00SVladimir Moravcevic 
474*e75a6b00SVladimir Moravcevic 	return 0;
475*e75a6b00SVladimir Moravcevic }
476*e75a6b00SVladimir Moravcevic 
477*e75a6b00SVladimir Moravcevic /**
478*e75a6b00SVladimir Moravcevic  * ax_unprepare_transfer_hardware - Relaxes hardware after transfer
479*e75a6b00SVladimir Moravcevic  * @ctlr:	Pointer to the spi_controller structure which provides
480*e75a6b00SVladimir Moravcevic  *		information about the controller.
481*e75a6b00SVladimir Moravcevic  *
482*e75a6b00SVladimir Moravcevic  * This function disables the SPI host controller when no target selected.
483*e75a6b00SVladimir Moravcevic  *
484*e75a6b00SVladimir Moravcevic  * Return:	0 always
485*e75a6b00SVladimir Moravcevic  */
486*e75a6b00SVladimir Moravcevic static int ax_unprepare_transfer_hardware(struct spi_controller *ctlr)
487*e75a6b00SVladimir Moravcevic {
488*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
489*e75a6b00SVladimir Moravcevic 
490*e75a6b00SVladimir Moravcevic 	u32 reg_value;
491*e75a6b00SVladimir Moravcevic 
492*e75a6b00SVladimir Moravcevic 	/* Disable the SPI if target is deselected */
493*e75a6b00SVladimir Moravcevic 	reg_value = ax_spi_read(xspi, AX_SPI_CR1);
494*e75a6b00SVladimir Moravcevic 	reg_value &= ~AX_SPI_CR1_SCE;
495*e75a6b00SVladimir Moravcevic 
496*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR1, reg_value);
497*e75a6b00SVladimir Moravcevic 
498*e75a6b00SVladimir Moravcevic 	return 0;
499*e75a6b00SVladimir Moravcevic }
500*e75a6b00SVladimir Moravcevic 
501*e75a6b00SVladimir Moravcevic /**
502*e75a6b00SVladimir Moravcevic  * ax_spi_detect_fifo_depth - Detect the FIFO depth of the hardware
503*e75a6b00SVladimir Moravcevic  * @xspi:	Pointer to the ax_spi structure
504*e75a6b00SVladimir Moravcevic  *
505*e75a6b00SVladimir Moravcevic  * The depth of the TX FIFO is a synthesis configuration parameter of the SPI
506*e75a6b00SVladimir Moravcevic  * IP. The FIFO threshold register is sized so that its maximum value can be the
507*e75a6b00SVladimir Moravcevic  * FIFO size - 1. This is used to detect the size of the FIFO.
508*e75a6b00SVladimir Moravcevic  */
509*e75a6b00SVladimir Moravcevic static void ax_spi_detect_fifo_depth(struct ax_spi *xspi)
510*e75a6b00SVladimir Moravcevic {
511*e75a6b00SVladimir Moravcevic 	/* The MSBs will get truncated giving us the size of the FIFO */
512*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_TX_FAETR, ALMOST_EMPTY_TRESHOLD);
513*e75a6b00SVladimir Moravcevic 	xspi->tx_fifo_depth = FIFO_DEPTH;
514*e75a6b00SVladimir Moravcevic 
515*e75a6b00SVladimir Moravcevic 	/* Set the threshold limit */
516*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_TX_FAETR, ALMOST_EMPTY_TRESHOLD);
517*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_RX_FAFTR, ALMOST_FULL_TRESHOLD);
518*e75a6b00SVladimir Moravcevic }
519*e75a6b00SVladimir Moravcevic 
520*e75a6b00SVladimir Moravcevic /* --- Internal Helper Function for 32-bit RX FIFO Read --- */
521*e75a6b00SVladimir Moravcevic /**
522*e75a6b00SVladimir Moravcevic  * ax_spi_get_rx_byte - Gets a byte from the RX FIFO buffer
523*e75a6b00SVladimir Moravcevic  * @xspi: Controller private data (struct ax_spi *)
524*e75a6b00SVladimir Moravcevic  *
525*e75a6b00SVladimir Moravcevic  * This function handles the logic of extracting bytes from the 32-bit RX FIFO.
526*e75a6b00SVladimir Moravcevic  * It reads a new 32-bit word from AX_SPI_RXFIFO only when the current buffered
527*e75a6b00SVladimir Moravcevic  * word has been fully processed (all 4 bytes extracted). It then extracts
528*e75a6b00SVladimir Moravcevic  * bytes one by one, assuming the controller is little-endian.
529*e75a6b00SVladimir Moravcevic  *
530*e75a6b00SVladimir Moravcevic  * Returns: The next 8-bit byte read from the RX FIFO stream.
531*e75a6b00SVladimir Moravcevic  */
532*e75a6b00SVladimir Moravcevic static u8 ax_spi_get_rx_byte(struct ax_spi *xspi)
533*e75a6b00SVladimir Moravcevic {
534*e75a6b00SVladimir Moravcevic 	u8 byte_val;
535*e75a6b00SVladimir Moravcevic 
536*e75a6b00SVladimir Moravcevic 	/* If all bytes from the current 32-bit word have been extracted,
537*e75a6b00SVladimir Moravcevic 	 * read a new word from the hardware RX FIFO.
538*e75a6b00SVladimir Moravcevic 	 */
539*e75a6b00SVladimir Moravcevic 	if (xspi->bytes_left_in_current_rx_word == 0) {
540*e75a6b00SVladimir Moravcevic 		xspi->current_rx_fifo_word = ax_spi_read(xspi, AX_SPI_RXFIFO);
541*e75a6b00SVladimir Moravcevic 		xspi->bytes_left_in_current_rx_word = 4; // A new 32-bit word has 4 bytes
542*e75a6b00SVladimir Moravcevic 	}
543*e75a6b00SVladimir Moravcevic 
544*e75a6b00SVladimir Moravcevic 	/* Extract the least significant byte from the current 32-bit word */
545*e75a6b00SVladimir Moravcevic 	byte_val = (u8)(xspi->current_rx_fifo_word & 0xFF);
546*e75a6b00SVladimir Moravcevic 
547*e75a6b00SVladimir Moravcevic 	/* Shift the word right by 8 bits to prepare the next byte for extraction */
548*e75a6b00SVladimir Moravcevic 	xspi->current_rx_fifo_word >>= 8;
549*e75a6b00SVladimir Moravcevic 	xspi->bytes_left_in_current_rx_word--;
550*e75a6b00SVladimir Moravcevic 
551*e75a6b00SVladimir Moravcevic 	return byte_val;
552*e75a6b00SVladimir Moravcevic }
553*e75a6b00SVladimir Moravcevic 
554*e75a6b00SVladimir Moravcevic static int ax_spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
555*e75a6b00SVladimir Moravcevic {
556*e75a6b00SVladimir Moravcevic 	struct spi_device *spi = mem->spi;
557*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(spi->controller);
558*e75a6b00SVladimir Moravcevic 	u32 reg_val;
559*e75a6b00SVladimir Moravcevic 	int ret = 0;
560*e75a6b00SVladimir Moravcevic 	u8 cmd_buf[AX_SPI_COMMAND_BUFFER_SIZE];
561*e75a6b00SVladimir Moravcevic 	int cmd_len = 0;
562*e75a6b00SVladimir Moravcevic 	int i = 0, timeout = AX_SPI_TRX_FIFO_TIMEOUT;
563*e75a6b00SVladimir Moravcevic 	int bytes_to_discard_from_rx;
564*e75a6b00SVladimir Moravcevic 	u8 *rx_buf_ptr = (u8 *)op->data.buf.in;
565*e75a6b00SVladimir Moravcevic 	u8 *tx_buf_ptr = (u8 *)op->data.buf.out;
566*e75a6b00SVladimir Moravcevic 	u32 rx_count_reg = 0;
567*e75a6b00SVladimir Moravcevic 
568*e75a6b00SVladimir Moravcevic 	dev_dbg(&spi->dev,
569*e75a6b00SVladimir Moravcevic 		"%s: cmd:%02x mode:%d.%d.%d.%d addr:%llx len:%d\n",
570*e75a6b00SVladimir Moravcevic 		__func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
571*e75a6b00SVladimir Moravcevic 		op->dummy.buswidth, op->data.buswidth, op->addr.val,
572*e75a6b00SVladimir Moravcevic 		op->data.nbytes);
573*e75a6b00SVladimir Moravcevic 
574*e75a6b00SVladimir Moravcevic 	/* Validate operation parameters: Only 1-bit bus width supported */
575*e75a6b00SVladimir Moravcevic 	if (op->cmd.buswidth != 1 ||
576*e75a6b00SVladimir Moravcevic 	    (op->addr.nbytes && op->addr.buswidth != 0 &&
577*e75a6b00SVladimir Moravcevic 	    op->addr.buswidth != 1) ||
578*e75a6b00SVladimir Moravcevic 	    (op->dummy.nbytes && op->dummy.buswidth != 0 &&
579*e75a6b00SVladimir Moravcevic 	    op->dummy.buswidth != 1) ||
580*e75a6b00SVladimir Moravcevic 	    (op->data.nbytes && op->data.buswidth != 1)) {
581*e75a6b00SVladimir Moravcevic 		dev_err(&spi->dev, "Unsupported bus width, only 1-bit bus width supported\n");
582*e75a6b00SVladimir Moravcevic 		return -EOPNOTSUPP;
583*e75a6b00SVladimir Moravcevic 	}
584*e75a6b00SVladimir Moravcevic 
585*e75a6b00SVladimir Moravcevic 	/* Initialize controller hardware */
586*e75a6b00SVladimir Moravcevic 	ax_spi_init_hw(xspi);
587*e75a6b00SVladimir Moravcevic 
588*e75a6b00SVladimir Moravcevic 	/* Assert chip select (pull low) */
589*e75a6b00SVladimir Moravcevic 	ax_spi_chipselect(spi, false);
590*e75a6b00SVladimir Moravcevic 
591*e75a6b00SVladimir Moravcevic 	/* Build command phase: Copy opcode to cmd_buf */
592*e75a6b00SVladimir Moravcevic 	if (op->cmd.nbytes == 2) {
593*e75a6b00SVladimir Moravcevic 		cmd_buf[cmd_len++] = (op->cmd.opcode >> 8) & 0xFF;
594*e75a6b00SVladimir Moravcevic 		cmd_buf[cmd_len++] = op->cmd.opcode & 0xFF;
595*e75a6b00SVladimir Moravcevic 	} else {
596*e75a6b00SVladimir Moravcevic 		cmd_buf[cmd_len++] = op->cmd.opcode;
597*e75a6b00SVladimir Moravcevic 	}
598*e75a6b00SVladimir Moravcevic 
599*e75a6b00SVladimir Moravcevic 	/* Put address bytes to cmd_buf */
600*e75a6b00SVladimir Moravcevic 	if (op->addr.nbytes) {
601*e75a6b00SVladimir Moravcevic 		for (i = op->addr.nbytes - 1; i >= 0; i--) {
602*e75a6b00SVladimir Moravcevic 			cmd_buf[cmd_len] = (op->addr.val >> (i * 8)) & 0xFF;
603*e75a6b00SVladimir Moravcevic 			cmd_len++;
604*e75a6b00SVladimir Moravcevic 		}
605*e75a6b00SVladimir Moravcevic 	}
606*e75a6b00SVladimir Moravcevic 
607*e75a6b00SVladimir Moravcevic 	/* Configure controller for desired operation mode (write/read) */
608*e75a6b00SVladimir Moravcevic 	reg_val = ax_spi_read(xspi, AX_SPI_CR2);
609*e75a6b00SVladimir Moravcevic 	reg_val |= AX_SPI_CR2_SWD | AX_SPI_CR2_SRI | AX_SPI_CR2_SRD;
610*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR2, reg_val);
611*e75a6b00SVladimir Moravcevic 
612*e75a6b00SVladimir Moravcevic 	/* Write command and address bytes to TX_FIFO */
613*e75a6b00SVladimir Moravcevic 	for (i = 0; i < cmd_len; i++)
614*e75a6b00SVladimir Moravcevic 		ax_spi_write_b(xspi, AX_SPI_TXFIFO, cmd_buf[i]);
615*e75a6b00SVladimir Moravcevic 
616*e75a6b00SVladimir Moravcevic 	/* Add dummy bytes (for clock generation) or actual data bytes to TX_FIFO */
617*e75a6b00SVladimir Moravcevic 	if (op->data.dir == SPI_MEM_DATA_IN) {
618*e75a6b00SVladimir Moravcevic 		for (i = 0; i < op->dummy.nbytes; i++)
619*e75a6b00SVladimir Moravcevic 			ax_spi_write_b(xspi, AX_SPI_TXFIFO, 0x00);
620*e75a6b00SVladimir Moravcevic 		for (i = 0; i < op->data.nbytes; i++)
621*e75a6b00SVladimir Moravcevic 			ax_spi_write_b(xspi, AX_SPI_TXFIFO, 0x00);
622*e75a6b00SVladimir Moravcevic 	} else {
623*e75a6b00SVladimir Moravcevic 		for (i = 0; i < op->data.nbytes; i++)
624*e75a6b00SVladimir Moravcevic 			ax_spi_write_b(xspi, AX_SPI_TXFIFO, tx_buf_ptr[i]);
625*e75a6b00SVladimir Moravcevic 	}
626*e75a6b00SVladimir Moravcevic 
627*e75a6b00SVladimir Moravcevic 	/* Start the SPI transmission */
628*e75a6b00SVladimir Moravcevic 	reg_val = ax_spi_read(xspi, AX_SPI_CR2);
629*e75a6b00SVladimir Moravcevic 	reg_val |= AX_SPI_CR2_HTE;
630*e75a6b00SVladimir Moravcevic 	ax_spi_write(xspi, AX_SPI_CR2, reg_val);
631*e75a6b00SVladimir Moravcevic 
632*e75a6b00SVladimir Moravcevic 	/* Wait for TX FIFO to become empty */
633*e75a6b00SVladimir Moravcevic 	while (timeout-- > 0) {
634*e75a6b00SVladimir Moravcevic 		u32 tx_count_reg = ax_spi_read(xspi, AX_SPI_TX_FBCAR);
635*e75a6b00SVladimir Moravcevic 
636*e75a6b00SVladimir Moravcevic 		if (tx_count_reg == 0) {
637*e75a6b00SVladimir Moravcevic 			udelay(1);
638*e75a6b00SVladimir Moravcevic 			break;
639*e75a6b00SVladimir Moravcevic 		}
640*e75a6b00SVladimir Moravcevic 		udelay(1);
641*e75a6b00SVladimir Moravcevic 	}
642*e75a6b00SVladimir Moravcevic 
643*e75a6b00SVladimir Moravcevic 	/* Handle Data Reception (for read operations) */
644*e75a6b00SVladimir Moravcevic 	if (op->data.dir == SPI_MEM_DATA_IN) {
645*e75a6b00SVladimir Moravcevic 		/* Reset the internal RX byte buffer for this new operation.
646*e75a6b00SVladimir Moravcevic 		 * This ensures ax_spi_get_rx_byte starts fresh for each exec_op call.
647*e75a6b00SVladimir Moravcevic 		 */
648*e75a6b00SVladimir Moravcevic 		xspi->bytes_left_in_current_rx_word = 0;
649*e75a6b00SVladimir Moravcevic 		xspi->current_rx_fifo_word = 0;
650*e75a6b00SVladimir Moravcevic 
651*e75a6b00SVladimir Moravcevic 		timeout = AX_SPI_TRX_FIFO_TIMEOUT;
652*e75a6b00SVladimir Moravcevic 		while (timeout-- > 0) {
653*e75a6b00SVladimir Moravcevic 			rx_count_reg = ax_spi_read(xspi, AX_SPI_RX_FBCAR);
654*e75a6b00SVladimir Moravcevic 			if (rx_count_reg >= op->data.nbytes)
655*e75a6b00SVladimir Moravcevic 				break;
656*e75a6b00SVladimir Moravcevic 			udelay(1); /* Small delay to prevent aggressive busy-waiting */
657*e75a6b00SVladimir Moravcevic 		}
658*e75a6b00SVladimir Moravcevic 
659*e75a6b00SVladimir Moravcevic 		if (timeout < 0) {
660*e75a6b00SVladimir Moravcevic 			ret = -ETIMEDOUT;
661*e75a6b00SVladimir Moravcevic 			goto out_unlock;
662*e75a6b00SVladimir Moravcevic 		}
663*e75a6b00SVladimir Moravcevic 
664*e75a6b00SVladimir Moravcevic 		/* Calculate how many bytes we need to discard from the RX FIFO.
665*e75a6b00SVladimir Moravcevic 		 * Since we set SRI, we only need to discard the address bytes and
666*e75a6b00SVladimir Moravcevic 		 * dummy bytes from the RX FIFO.
667*e75a6b00SVladimir Moravcevic 		 */
668*e75a6b00SVladimir Moravcevic 		bytes_to_discard_from_rx = op->addr.nbytes + op->dummy.nbytes;
669*e75a6b00SVladimir Moravcevic 		for (i = 0; i < bytes_to_discard_from_rx; i++)
670*e75a6b00SVladimir Moravcevic 			ax_spi_get_rx_byte(xspi);
671*e75a6b00SVladimir Moravcevic 
672*e75a6b00SVladimir Moravcevic 		/* Read actual data bytes into op->data.buf.in */
673*e75a6b00SVladimir Moravcevic 		for (i = 0; i < op->data.nbytes; i++) {
674*e75a6b00SVladimir Moravcevic 			*rx_buf_ptr = ax_spi_get_rx_byte(xspi);
675*e75a6b00SVladimir Moravcevic 			rx_buf_ptr++;
676*e75a6b00SVladimir Moravcevic 		}
677*e75a6b00SVladimir Moravcevic 	} else if (op->data.dir == SPI_MEM_DATA_OUT) {
678*e75a6b00SVladimir Moravcevic 		timeout = AX_SPI_TRX_FIFO_TIMEOUT;
679*e75a6b00SVladimir Moravcevic 		while (timeout-- > 0) {
680*e75a6b00SVladimir Moravcevic 			u32 tx_fifo_level = ax_spi_read(xspi, AX_SPI_TX_FBCAR);
681*e75a6b00SVladimir Moravcevic 
682*e75a6b00SVladimir Moravcevic 			if (tx_fifo_level == 0)
683*e75a6b00SVladimir Moravcevic 				break;
684*e75a6b00SVladimir Moravcevic 			udelay(1);
685*e75a6b00SVladimir Moravcevic 		}
686*e75a6b00SVladimir Moravcevic 		if (timeout < 0) {
687*e75a6b00SVladimir Moravcevic 			ret = -ETIMEDOUT;
688*e75a6b00SVladimir Moravcevic 			goto out_unlock;
689*e75a6b00SVladimir Moravcevic 		}
690*e75a6b00SVladimir Moravcevic 	}
691*e75a6b00SVladimir Moravcevic 
692*e75a6b00SVladimir Moravcevic out_unlock:
693*e75a6b00SVladimir Moravcevic 	/* Deassert chip select (pull high) */
694*e75a6b00SVladimir Moravcevic 	ax_spi_chipselect(spi, true);
695*e75a6b00SVladimir Moravcevic 
696*e75a6b00SVladimir Moravcevic 	return ret;
697*e75a6b00SVladimir Moravcevic }
698*e75a6b00SVladimir Moravcevic 
699*e75a6b00SVladimir Moravcevic static int ax_spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
700*e75a6b00SVladimir Moravcevic {
701*e75a6b00SVladimir Moravcevic 	struct spi_device *spi = mem->spi;
702*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(spi->controller);
703*e75a6b00SVladimir Moravcevic 	size_t max_transfer_payload_bytes;
704*e75a6b00SVladimir Moravcevic 	size_t fifo_total_bytes;
705*e75a6b00SVladimir Moravcevic 	size_t protocol_overhead_bytes;
706*e75a6b00SVladimir Moravcevic 
707*e75a6b00SVladimir Moravcevic 	fifo_total_bytes = xspi->tx_fifo_depth;
708*e75a6b00SVladimir Moravcevic 	/* Calculate protocol overhead bytes according to the real operation each time. */
709*e75a6b00SVladimir Moravcevic 	protocol_overhead_bytes = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
710*e75a6b00SVladimir Moravcevic 
711*e75a6b00SVladimir Moravcevic 	/* Calculate the maximum data payload that can fit into the FIFO. */
712*e75a6b00SVladimir Moravcevic 	if (fifo_total_bytes <= protocol_overhead_bytes) {
713*e75a6b00SVladimir Moravcevic 		max_transfer_payload_bytes = 0;
714*e75a6b00SVladimir Moravcevic 		dev_warn_once(&spi->dev, "SPI FIFO (%zu bytes) is too small for protocol overhead (%zu bytes)! Max data size forced to 0.\n",
715*e75a6b00SVladimir Moravcevic 			 fifo_total_bytes, protocol_overhead_bytes);
716*e75a6b00SVladimir Moravcevic 	} else {
717*e75a6b00SVladimir Moravcevic 		max_transfer_payload_bytes = fifo_total_bytes - protocol_overhead_bytes;
718*e75a6b00SVladimir Moravcevic 	}
719*e75a6b00SVladimir Moravcevic 
720*e75a6b00SVladimir Moravcevic 	/* Limit op->data.nbytes based on the calculated max payload and SZ_64K.
721*e75a6b00SVladimir Moravcevic 	 * This is the value that spi-mem will then use to split requests.
722*e75a6b00SVladimir Moravcevic 	 */
723*e75a6b00SVladimir Moravcevic 	if (op->data.nbytes > max_transfer_payload_bytes) {
724*e75a6b00SVladimir Moravcevic 		op->data.nbytes = max_transfer_payload_bytes;
725*e75a6b00SVladimir Moravcevic 		dev_dbg(&spi->dev, "%s %d: op->data.nbytes adjusted to %u due to FIFO overhead\n",
726*e75a6b00SVladimir Moravcevic 			__func__, __LINE__, op->data.nbytes);
727*e75a6b00SVladimir Moravcevic 	}
728*e75a6b00SVladimir Moravcevic 
729*e75a6b00SVladimir Moravcevic 	/* Also apply the overall max transfer size */
730*e75a6b00SVladimir Moravcevic 	if (op->data.nbytes > SZ_64K) {
731*e75a6b00SVladimir Moravcevic 		op->data.nbytes = SZ_64K;
732*e75a6b00SVladimir Moravcevic 		dev_dbg(&spi->dev, "%s %d: op->data.nbytes adjusted to %u due to SZ_64K limit\n",
733*e75a6b00SVladimir Moravcevic 			__func__, __LINE__, op->data.nbytes);
734*e75a6b00SVladimir Moravcevic 	}
735*e75a6b00SVladimir Moravcevic 
736*e75a6b00SVladimir Moravcevic 	return 0;
737*e75a6b00SVladimir Moravcevic }
738*e75a6b00SVladimir Moravcevic 
739*e75a6b00SVladimir Moravcevic static const struct spi_controller_mem_ops ax_spi_mem_ops = {
740*e75a6b00SVladimir Moravcevic 	.exec_op = ax_spi_mem_exec_op,
741*e75a6b00SVladimir Moravcevic 	.adjust_op_size = ax_spi_mem_adjust_op_size,
742*e75a6b00SVladimir Moravcevic };
743*e75a6b00SVladimir Moravcevic 
744*e75a6b00SVladimir Moravcevic /**
745*e75a6b00SVladimir Moravcevic  * ax_spi_probe - Probe method for the SPI driver
746*e75a6b00SVladimir Moravcevic  * @pdev:	Pointer to the platform_device structure
747*e75a6b00SVladimir Moravcevic  *
748*e75a6b00SVladimir Moravcevic  * This function initializes the driver data structures and the hardware.
749*e75a6b00SVladimir Moravcevic  *
750*e75a6b00SVladimir Moravcevic  * Return:	0 on success and error value on error
751*e75a6b00SVladimir Moravcevic  */
752*e75a6b00SVladimir Moravcevic static int ax_spi_probe(struct platform_device *pdev)
753*e75a6b00SVladimir Moravcevic {
754*e75a6b00SVladimir Moravcevic 	int ret = 0, irq;
755*e75a6b00SVladimir Moravcevic 	struct spi_controller *ctlr;
756*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi;
757*e75a6b00SVladimir Moravcevic 	u32 num_cs;
758*e75a6b00SVladimir Moravcevic 
759*e75a6b00SVladimir Moravcevic 	ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xspi));
760*e75a6b00SVladimir Moravcevic 	if (!ctlr)
761*e75a6b00SVladimir Moravcevic 		return -ENOMEM;
762*e75a6b00SVladimir Moravcevic 
763*e75a6b00SVladimir Moravcevic 	xspi = spi_controller_get_devdata(ctlr);
764*e75a6b00SVladimir Moravcevic 	ctlr->dev.of_node = pdev->dev.of_node;
765*e75a6b00SVladimir Moravcevic 	platform_set_drvdata(pdev, ctlr);
766*e75a6b00SVladimir Moravcevic 
767*e75a6b00SVladimir Moravcevic 	xspi->regs = devm_platform_ioremap_resource(pdev, 0);
768*e75a6b00SVladimir Moravcevic 	if (IS_ERR(xspi->regs)) {
769*e75a6b00SVladimir Moravcevic 		ret = PTR_ERR(xspi->regs);
770*e75a6b00SVladimir Moravcevic 		goto remove_ctlr;
771*e75a6b00SVladimir Moravcevic 	}
772*e75a6b00SVladimir Moravcevic 
773*e75a6b00SVladimir Moravcevic 	xspi->pclk = devm_clk_get(&pdev->dev, "pclk");
774*e75a6b00SVladimir Moravcevic 	if (IS_ERR(xspi->pclk)) {
775*e75a6b00SVladimir Moravcevic 		dev_err(&pdev->dev, "pclk clock not found.\n");
776*e75a6b00SVladimir Moravcevic 		ret = PTR_ERR(xspi->pclk);
777*e75a6b00SVladimir Moravcevic 		goto remove_ctlr;
778*e75a6b00SVladimir Moravcevic 	}
779*e75a6b00SVladimir Moravcevic 
780*e75a6b00SVladimir Moravcevic 	xspi->ref_clk = devm_clk_get(&pdev->dev, "ref");
781*e75a6b00SVladimir Moravcevic 	if (IS_ERR(xspi->ref_clk)) {
782*e75a6b00SVladimir Moravcevic 		dev_err(&pdev->dev, "ref clock not found.\n");
783*e75a6b00SVladimir Moravcevic 		ret = PTR_ERR(xspi->ref_clk);
784*e75a6b00SVladimir Moravcevic 		goto remove_ctlr;
785*e75a6b00SVladimir Moravcevic 	}
786*e75a6b00SVladimir Moravcevic 
787*e75a6b00SVladimir Moravcevic 	ret = clk_prepare_enable(xspi->pclk);
788*e75a6b00SVladimir Moravcevic 	if (ret) {
789*e75a6b00SVladimir Moravcevic 		dev_err(&pdev->dev, "Unable to enable APB clock.\n");
790*e75a6b00SVladimir Moravcevic 		goto remove_ctlr;
791*e75a6b00SVladimir Moravcevic 	}
792*e75a6b00SVladimir Moravcevic 
793*e75a6b00SVladimir Moravcevic 	ret = clk_prepare_enable(xspi->ref_clk);
794*e75a6b00SVladimir Moravcevic 	if (ret) {
795*e75a6b00SVladimir Moravcevic 		dev_err(&pdev->dev, "Unable to enable device clock.\n");
796*e75a6b00SVladimir Moravcevic 		goto clk_dis_apb;
797*e75a6b00SVladimir Moravcevic 	}
798*e75a6b00SVladimir Moravcevic 
799*e75a6b00SVladimir Moravcevic 	pm_runtime_use_autosuspend(&pdev->dev);
800*e75a6b00SVladimir Moravcevic 	pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
801*e75a6b00SVladimir Moravcevic 	pm_runtime_get_noresume(&pdev->dev);
802*e75a6b00SVladimir Moravcevic 	pm_runtime_set_active(&pdev->dev);
803*e75a6b00SVladimir Moravcevic 	pm_runtime_enable(&pdev->dev);
804*e75a6b00SVladimir Moravcevic 
805*e75a6b00SVladimir Moravcevic 	ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs);
806*e75a6b00SVladimir Moravcevic 	if (ret < 0)
807*e75a6b00SVladimir Moravcevic 		ctlr->num_chipselect = AX_SPI_DEFAULT_NUM_CS;
808*e75a6b00SVladimir Moravcevic 	else
809*e75a6b00SVladimir Moravcevic 		ctlr->num_chipselect = num_cs;
810*e75a6b00SVladimir Moravcevic 
811*e75a6b00SVladimir Moravcevic 	ax_spi_detect_fifo_depth(xspi);
812*e75a6b00SVladimir Moravcevic 
813*e75a6b00SVladimir Moravcevic 	xspi->current_rx_fifo_word = 0;
814*e75a6b00SVladimir Moravcevic 	xspi->bytes_left_in_current_rx_word = 0;
815*e75a6b00SVladimir Moravcevic 
816*e75a6b00SVladimir Moravcevic 	/* Initialize IRQ-related variables */
817*e75a6b00SVladimir Moravcevic 	xspi->bytes_left_in_current_rx_word_for_irq = 0;
818*e75a6b00SVladimir Moravcevic 	xspi->current_rx_fifo_word_for_irq = 0;
819*e75a6b00SVladimir Moravcevic 
820*e75a6b00SVladimir Moravcevic 	/* SPI controller initializations */
821*e75a6b00SVladimir Moravcevic 	ax_spi_init_hw(xspi);
822*e75a6b00SVladimir Moravcevic 
823*e75a6b00SVladimir Moravcevic 	irq = platform_get_irq(pdev, 0);
824*e75a6b00SVladimir Moravcevic 	if (irq <= 0) {
825*e75a6b00SVladimir Moravcevic 		ret = -ENXIO;
826*e75a6b00SVladimir Moravcevic 		goto clk_dis_all;
827*e75a6b00SVladimir Moravcevic 	}
828*e75a6b00SVladimir Moravcevic 
829*e75a6b00SVladimir Moravcevic 	ret = devm_request_irq(&pdev->dev, irq, ax_spi_irq,
830*e75a6b00SVladimir Moravcevic 			       0, pdev->name, ctlr);
831*e75a6b00SVladimir Moravcevic 	if (ret != 0) {
832*e75a6b00SVladimir Moravcevic 		ret = -ENXIO;
833*e75a6b00SVladimir Moravcevic 		dev_err(&pdev->dev, "request_irq failed\n");
834*e75a6b00SVladimir Moravcevic 		goto clk_dis_all;
835*e75a6b00SVladimir Moravcevic 	}
836*e75a6b00SVladimir Moravcevic 
837*e75a6b00SVladimir Moravcevic 	ctlr->use_gpio_descriptors = true;
838*e75a6b00SVladimir Moravcevic 	ctlr->prepare_transfer_hardware = ax_prepare_transfer_hardware;
839*e75a6b00SVladimir Moravcevic 	ctlr->prepare_message = ax_prepare_message;
840*e75a6b00SVladimir Moravcevic 	ctlr->transfer_one = ax_transfer_one;
841*e75a6b00SVladimir Moravcevic 	ctlr->unprepare_transfer_hardware = ax_unprepare_transfer_hardware;
842*e75a6b00SVladimir Moravcevic 	ctlr->set_cs = ax_spi_chipselect;
843*e75a6b00SVladimir Moravcevic 	ctlr->auto_runtime_pm = true;
844*e75a6b00SVladimir Moravcevic 	ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
845*e75a6b00SVladimir Moravcevic 
846*e75a6b00SVladimir Moravcevic 	xspi->clk_rate = clk_get_rate(xspi->ref_clk);
847*e75a6b00SVladimir Moravcevic 	/* Set to default valid value */
848*e75a6b00SVladimir Moravcevic 	ctlr->max_speed_hz = xspi->clk_rate / 2;
849*e75a6b00SVladimir Moravcevic 	xspi->speed_hz = ctlr->max_speed_hz;
850*e75a6b00SVladimir Moravcevic 
851*e75a6b00SVladimir Moravcevic 	ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
852*e75a6b00SVladimir Moravcevic 
853*e75a6b00SVladimir Moravcevic 	pm_runtime_mark_last_busy(&pdev->dev);
854*e75a6b00SVladimir Moravcevic 	pm_runtime_put_autosuspend(&pdev->dev);
855*e75a6b00SVladimir Moravcevic 
856*e75a6b00SVladimir Moravcevic 	ctlr->mem_ops = &ax_spi_mem_ops;
857*e75a6b00SVladimir Moravcevic 
858*e75a6b00SVladimir Moravcevic 	ret = spi_register_controller(ctlr);
859*e75a6b00SVladimir Moravcevic 	if (ret) {
860*e75a6b00SVladimir Moravcevic 		dev_err(&pdev->dev, "spi_register_controller failed\n");
861*e75a6b00SVladimir Moravcevic 		goto clk_dis_all;
862*e75a6b00SVladimir Moravcevic 	}
863*e75a6b00SVladimir Moravcevic 
864*e75a6b00SVladimir Moravcevic 	return ret;
865*e75a6b00SVladimir Moravcevic 
866*e75a6b00SVladimir Moravcevic clk_dis_all:
867*e75a6b00SVladimir Moravcevic 	pm_runtime_set_suspended(&pdev->dev);
868*e75a6b00SVladimir Moravcevic 	pm_runtime_disable(&pdev->dev);
869*e75a6b00SVladimir Moravcevic 	clk_disable_unprepare(xspi->ref_clk);
870*e75a6b00SVladimir Moravcevic clk_dis_apb:
871*e75a6b00SVladimir Moravcevic 	clk_disable_unprepare(xspi->pclk);
872*e75a6b00SVladimir Moravcevic remove_ctlr:
873*e75a6b00SVladimir Moravcevic 	spi_controller_put(ctlr);
874*e75a6b00SVladimir Moravcevic 	return ret;
875*e75a6b00SVladimir Moravcevic }
876*e75a6b00SVladimir Moravcevic 
877*e75a6b00SVladimir Moravcevic /**
878*e75a6b00SVladimir Moravcevic  * ax_spi_remove - Remove method for the SPI driver
879*e75a6b00SVladimir Moravcevic  * @pdev:	Pointer to the platform_device structure
880*e75a6b00SVladimir Moravcevic  *
881*e75a6b00SVladimir Moravcevic  * This function is called if a device is physically removed from the system or
882*e75a6b00SVladimir Moravcevic  * if the driver module is being unloaded. It frees all resources allocated to
883*e75a6b00SVladimir Moravcevic  * the device.
884*e75a6b00SVladimir Moravcevic  */
885*e75a6b00SVladimir Moravcevic static void ax_spi_remove(struct platform_device *pdev)
886*e75a6b00SVladimir Moravcevic {
887*e75a6b00SVladimir Moravcevic 	struct spi_controller *ctlr = platform_get_drvdata(pdev);
888*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
889*e75a6b00SVladimir Moravcevic 
890*e75a6b00SVladimir Moravcevic 	spi_unregister_controller(ctlr);
891*e75a6b00SVladimir Moravcevic 
892*e75a6b00SVladimir Moravcevic 	pm_runtime_set_suspended(&pdev->dev);
893*e75a6b00SVladimir Moravcevic 	pm_runtime_disable(&pdev->dev);
894*e75a6b00SVladimir Moravcevic 
895*e75a6b00SVladimir Moravcevic 	clk_disable_unprepare(xspi->ref_clk);
896*e75a6b00SVladimir Moravcevic 	clk_disable_unprepare(xspi->pclk);
897*e75a6b00SVladimir Moravcevic }
898*e75a6b00SVladimir Moravcevic 
899*e75a6b00SVladimir Moravcevic /**
900*e75a6b00SVladimir Moravcevic  * ax_spi_suspend - Suspend method for the SPI driver
901*e75a6b00SVladimir Moravcevic  * @dev:	Address of the platform_device structure
902*e75a6b00SVladimir Moravcevic  *
903*e75a6b00SVladimir Moravcevic  * This function disables the SPI controller and
904*e75a6b00SVladimir Moravcevic  * changes the driver state to "suspend"
905*e75a6b00SVladimir Moravcevic  *
906*e75a6b00SVladimir Moravcevic  * Return:	0 on success and error value on error
907*e75a6b00SVladimir Moravcevic  */
908*e75a6b00SVladimir Moravcevic static int __maybe_unused ax_spi_suspend(struct device *dev)
909*e75a6b00SVladimir Moravcevic {
910*e75a6b00SVladimir Moravcevic 	struct spi_controller *ctlr = dev_get_drvdata(dev);
911*e75a6b00SVladimir Moravcevic 
912*e75a6b00SVladimir Moravcevic 	return spi_controller_suspend(ctlr);
913*e75a6b00SVladimir Moravcevic }
914*e75a6b00SVladimir Moravcevic 
915*e75a6b00SVladimir Moravcevic /**
916*e75a6b00SVladimir Moravcevic  * ax_spi_resume - Resume method for the SPI driver
917*e75a6b00SVladimir Moravcevic  * @dev:	Address of the platform_device structure
918*e75a6b00SVladimir Moravcevic  *
919*e75a6b00SVladimir Moravcevic  * This function changes the driver state to "ready"
920*e75a6b00SVladimir Moravcevic  *
921*e75a6b00SVladimir Moravcevic  * Return:	0 on success and error value on error
922*e75a6b00SVladimir Moravcevic  */
923*e75a6b00SVladimir Moravcevic static int __maybe_unused ax_spi_resume(struct device *dev)
924*e75a6b00SVladimir Moravcevic {
925*e75a6b00SVladimir Moravcevic 	struct spi_controller *ctlr = dev_get_drvdata(dev);
926*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
927*e75a6b00SVladimir Moravcevic 
928*e75a6b00SVladimir Moravcevic 	ax_spi_init_hw(xspi);
929*e75a6b00SVladimir Moravcevic 	return spi_controller_resume(ctlr);
930*e75a6b00SVladimir Moravcevic }
931*e75a6b00SVladimir Moravcevic 
932*e75a6b00SVladimir Moravcevic /**
933*e75a6b00SVladimir Moravcevic  * ax_spi_runtime_resume - Runtime resume method for the SPI driver
934*e75a6b00SVladimir Moravcevic  * @dev:	Address of the platform_device structure
935*e75a6b00SVladimir Moravcevic  *
936*e75a6b00SVladimir Moravcevic  * This function enables the clocks
937*e75a6b00SVladimir Moravcevic  *
938*e75a6b00SVladimir Moravcevic  * Return:	0 on success and error value on error
939*e75a6b00SVladimir Moravcevic  */
940*e75a6b00SVladimir Moravcevic static int __maybe_unused ax_spi_runtime_resume(struct device *dev)
941*e75a6b00SVladimir Moravcevic {
942*e75a6b00SVladimir Moravcevic 	struct spi_controller *ctlr = dev_get_drvdata(dev);
943*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
944*e75a6b00SVladimir Moravcevic 	int ret;
945*e75a6b00SVladimir Moravcevic 
946*e75a6b00SVladimir Moravcevic 	ret = clk_prepare_enable(xspi->pclk);
947*e75a6b00SVladimir Moravcevic 	if (ret) {
948*e75a6b00SVladimir Moravcevic 		dev_err(dev, "Cannot enable APB clock.\n");
949*e75a6b00SVladimir Moravcevic 		return ret;
950*e75a6b00SVladimir Moravcevic 	}
951*e75a6b00SVladimir Moravcevic 
952*e75a6b00SVladimir Moravcevic 	ret = clk_prepare_enable(xspi->ref_clk);
953*e75a6b00SVladimir Moravcevic 	if (ret) {
954*e75a6b00SVladimir Moravcevic 		dev_err(dev, "Cannot enable device clock.\n");
955*e75a6b00SVladimir Moravcevic 		clk_disable_unprepare(xspi->pclk);
956*e75a6b00SVladimir Moravcevic 		return ret;
957*e75a6b00SVladimir Moravcevic 	}
958*e75a6b00SVladimir Moravcevic 	return 0;
959*e75a6b00SVladimir Moravcevic }
960*e75a6b00SVladimir Moravcevic 
961*e75a6b00SVladimir Moravcevic /**
962*e75a6b00SVladimir Moravcevic  * ax_spi_runtime_suspend - Runtime suspend method for the SPI driver
963*e75a6b00SVladimir Moravcevic  * @dev:	Address of the platform_device structure
964*e75a6b00SVladimir Moravcevic  *
965*e75a6b00SVladimir Moravcevic  * This function disables the clocks
966*e75a6b00SVladimir Moravcevic  *
967*e75a6b00SVladimir Moravcevic  * Return:	Always 0
968*e75a6b00SVladimir Moravcevic  */
969*e75a6b00SVladimir Moravcevic static int __maybe_unused ax_spi_runtime_suspend(struct device *dev)
970*e75a6b00SVladimir Moravcevic {
971*e75a6b00SVladimir Moravcevic 	struct spi_controller *ctlr = dev_get_drvdata(dev);
972*e75a6b00SVladimir Moravcevic 	struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
973*e75a6b00SVladimir Moravcevic 
974*e75a6b00SVladimir Moravcevic 	clk_disable_unprepare(xspi->ref_clk);
975*e75a6b00SVladimir Moravcevic 	clk_disable_unprepare(xspi->pclk);
976*e75a6b00SVladimir Moravcevic 
977*e75a6b00SVladimir Moravcevic 	return 0;
978*e75a6b00SVladimir Moravcevic }
979*e75a6b00SVladimir Moravcevic 
980*e75a6b00SVladimir Moravcevic static const struct dev_pm_ops ax_spi_dev_pm_ops = {
981*e75a6b00SVladimir Moravcevic 	SET_RUNTIME_PM_OPS(ax_spi_runtime_suspend,
982*e75a6b00SVladimir Moravcevic 			   ax_spi_runtime_resume, NULL)
983*e75a6b00SVladimir Moravcevic 	SET_SYSTEM_SLEEP_PM_OPS(ax_spi_suspend, ax_spi_resume)
984*e75a6b00SVladimir Moravcevic };
985*e75a6b00SVladimir Moravcevic 
986*e75a6b00SVladimir Moravcevic static const struct of_device_id ax_spi_of_match[] = {
987*e75a6b00SVladimir Moravcevic 	{ .compatible = "axiado,ax3000-spi" },
988*e75a6b00SVladimir Moravcevic 	{ /* end of table */ }
989*e75a6b00SVladimir Moravcevic };
990*e75a6b00SVladimir Moravcevic MODULE_DEVICE_TABLE(of, ax_spi_of_match);
991*e75a6b00SVladimir Moravcevic 
992*e75a6b00SVladimir Moravcevic /* ax_spi_driver - This structure defines the SPI subsystem platform driver */
993*e75a6b00SVladimir Moravcevic static struct platform_driver ax_spi_driver = {
994*e75a6b00SVladimir Moravcevic 	.probe	= ax_spi_probe,
995*e75a6b00SVladimir Moravcevic 	.remove	= ax_spi_remove,
996*e75a6b00SVladimir Moravcevic 	.driver = {
997*e75a6b00SVladimir Moravcevic 		.name = AX_SPI_NAME,
998*e75a6b00SVladimir Moravcevic 		.of_match_table = ax_spi_of_match,
999*e75a6b00SVladimir Moravcevic 		.pm = &ax_spi_dev_pm_ops,
1000*e75a6b00SVladimir Moravcevic 	},
1001*e75a6b00SVladimir Moravcevic };
1002*e75a6b00SVladimir Moravcevic 
1003*e75a6b00SVladimir Moravcevic module_platform_driver(ax_spi_driver);
1004*e75a6b00SVladimir Moravcevic 
1005*e75a6b00SVladimir Moravcevic MODULE_AUTHOR("Axiado Corporation");
1006*e75a6b00SVladimir Moravcevic MODULE_DESCRIPTION("Axiado SPI Host driver");
1007*e75a6b00SVladimir Moravcevic MODULE_LICENSE("GPL");
1008