xref: /linux/drivers/spi/spi-microchip-core-spi.c (revision 8c8081cc599fcafa25371d50959c17e154f9fd08)
1059f5458SPrajna Rajendra Kumar // SPDX-License-Identifier: (GPL-2.0)
2059f5458SPrajna Rajendra Kumar //
3059f5458SPrajna Rajendra Kumar // Microchip CoreSPI controller driver
4059f5458SPrajna Rajendra Kumar //
5059f5458SPrajna Rajendra Kumar // Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries
6059f5458SPrajna Rajendra Kumar //
7059f5458SPrajna Rajendra Kumar // Author: Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>
8059f5458SPrajna Rajendra Kumar 
9059f5458SPrajna Rajendra Kumar #include <linux/clk.h>
10059f5458SPrajna Rajendra Kumar #include <linux/delay.h>
11059f5458SPrajna Rajendra Kumar #include <linux/err.h>
12059f5458SPrajna Rajendra Kumar #include <linux/init.h>
13059f5458SPrajna Rajendra Kumar #include <linux/interrupt.h>
14059f5458SPrajna Rajendra Kumar #include <linux/io.h>
15059f5458SPrajna Rajendra Kumar #include <linux/module.h>
16059f5458SPrajna Rajendra Kumar #include <linux/of.h>
17059f5458SPrajna Rajendra Kumar #include <linux/platform_device.h>
18059f5458SPrajna Rajendra Kumar #include <linux/spi/spi.h>
19059f5458SPrajna Rajendra Kumar 
20059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_MAX_CS				(8)
21059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_DEFAULT_FIFO_DEPTH			(4)
22059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_DEFAULT_MOTOROLA_MODE		(3)
23059f5458SPrajna Rajendra Kumar 
24059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL_ENABLE			BIT(0)
25059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL_MASTER			BIT(1)
26059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL_TX_DATA_INT		BIT(3)
27059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL_RX_OVER_INT		BIT(4)
28059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL_TX_UNDER_INT		BIT(5)
29059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL_FRAMEURUN			BIT(6)
30059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL_OENOFF			BIT(7)
31059f5458SPrajna Rajendra Kumar 
32059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_STATUS_ACTIVE			BIT(7)
33059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_STATUS_SSEL			BIT(6)
34059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_STATUS_TXFIFO_UNDERFLOW		BIT(5)
35059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_STATUS_RXFIFO_FULL			BIT(4)
36059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_STATUS_TXFIFO_FULL			BIT(3)
37059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_STATUS_RXFIFO_EMPTY		BIT(2)
38059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_STATUS_DONE			BIT(1)
39059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_STATUS_FIRSTFRAME			BIT(0)
40059f5458SPrajna Rajendra Kumar 
41059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_INT_TXDONE				BIT(0)
42059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW		BIT(2)
43059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN		BIT(3)
44059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_INT_CMDINT				BIT(4)
45059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_INT_SSEND				BIT(5)
46059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_INT_DATA_RX			BIT(6)
47059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_INT_TXRFM				BIT(7)
48059f5458SPrajna Rajendra Kumar 
49059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL2_INTEN_TXRFMT		BIT(7)
50059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL2_INTEN_DATA_RX		BIT(6)
51059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL2_INTEN_SSEND		BIT(5)
52059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_CONTROL2_INTEN_CMD			BIT(4)
53059f5458SPrajna Rajendra Kumar 
54059f5458SPrajna Rajendra Kumar #define INT_ENABLE_MASK (MCHP_CORESPI_CONTROL_TX_DATA_INT | MCHP_CORESPI_CONTROL_RX_OVER_INT | \
55059f5458SPrajna Rajendra Kumar 			 MCHP_CORESPI_CONTROL_TX_UNDER_INT)
56059f5458SPrajna Rajendra Kumar 
57059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_CONTROL			(0x00)
58059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_INTCLEAR			(0x04)
59059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_RXDATA				(0x08)
60059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_TXDATA				(0x0c)
61059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_INTMASK			(0X10)
62059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_INTRAW				(0X14)
63059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_CONTROL2			(0x18)
64059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_COMMAND			(0x1c)
65059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_STAT				(0x20)
66059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_SSEL				(0x24)
67059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_TXDATA_LAST			(0X28)
68059f5458SPrajna Rajendra Kumar #define MCHP_CORESPI_REG_CLK_DIV			(0x2c)
69059f5458SPrajna Rajendra Kumar 
70059f5458SPrajna Rajendra Kumar struct mchp_corespi {
71059f5458SPrajna Rajendra Kumar 	void __iomem *regs;
72059f5458SPrajna Rajendra Kumar 	struct clk *clk;
73059f5458SPrajna Rajendra Kumar 	const u8 *tx_buf;
74059f5458SPrajna Rajendra Kumar 	u8 *rx_buf;
75059f5458SPrajna Rajendra Kumar 	u32 clk_gen;
76059f5458SPrajna Rajendra Kumar 	int irq;
77e29aca70SAndy Shevchenko 	unsigned int tx_len;
78e29aca70SAndy Shevchenko 	unsigned int rx_len;
79059f5458SPrajna Rajendra Kumar 	u32 fifo_depth;
80059f5458SPrajna Rajendra Kumar };
81059f5458SPrajna Rajendra Kumar 
mchp_corespi_disable(struct mchp_corespi * spi)82059f5458SPrajna Rajendra Kumar static inline void mchp_corespi_disable(struct mchp_corespi *spi)
83059f5458SPrajna Rajendra Kumar {
84059f5458SPrajna Rajendra Kumar 	u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
85059f5458SPrajna Rajendra Kumar 
86059f5458SPrajna Rajendra Kumar 	control &= ~MCHP_CORESPI_CONTROL_ENABLE;
87059f5458SPrajna Rajendra Kumar 
88059f5458SPrajna Rajendra Kumar 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
89059f5458SPrajna Rajendra Kumar }
90059f5458SPrajna Rajendra Kumar 
mchp_corespi_read_fifo(struct mchp_corespi * spi,u32 fifo_max)91059f5458SPrajna Rajendra Kumar static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, u32 fifo_max)
92059f5458SPrajna Rajendra Kumar {
93059f5458SPrajna Rajendra Kumar 	for (int i = 0; i < fifo_max; i++) {
94059f5458SPrajna Rajendra Kumar 		u32 data;
95059f5458SPrajna Rajendra Kumar 
96059f5458SPrajna Rajendra Kumar 		while (readb(spi->regs + MCHP_CORESPI_REG_STAT) &
97059f5458SPrajna Rajendra Kumar 		       MCHP_CORESPI_STATUS_RXFIFO_EMPTY)
98059f5458SPrajna Rajendra Kumar 			;
99059f5458SPrajna Rajendra Kumar 
100cb5c2eb4SAndy Shevchenko 		/* On TX-only transfers always perform a dummy read */
101059f5458SPrajna Rajendra Kumar 		data = readb(spi->regs + MCHP_CORESPI_REG_RXDATA);
102cb5c2eb4SAndy Shevchenko 		if (spi->rx_buf)
103cb5c2eb4SAndy Shevchenko 			*spi->rx_buf++ = data;
104059f5458SPrajna Rajendra Kumar 
105059f5458SPrajna Rajendra Kumar 		spi->rx_len--;
106059f5458SPrajna Rajendra Kumar 	}
107059f5458SPrajna Rajendra Kumar }
108059f5458SPrajna Rajendra Kumar 
mchp_corespi_enable_ints(struct mchp_corespi * spi)109059f5458SPrajna Rajendra Kumar static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
110059f5458SPrajna Rajendra Kumar {
111059f5458SPrajna Rajendra Kumar 	u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
112059f5458SPrajna Rajendra Kumar 
113059f5458SPrajna Rajendra Kumar 	control |= INT_ENABLE_MASK;
114059f5458SPrajna Rajendra Kumar 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
115059f5458SPrajna Rajendra Kumar }
116059f5458SPrajna Rajendra Kumar 
mchp_corespi_disable_ints(struct mchp_corespi * spi)117059f5458SPrajna Rajendra Kumar static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
118059f5458SPrajna Rajendra Kumar {
119059f5458SPrajna Rajendra Kumar 	u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
120059f5458SPrajna Rajendra Kumar 
121059f5458SPrajna Rajendra Kumar 	control &= ~INT_ENABLE_MASK;
122059f5458SPrajna Rajendra Kumar 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
123059f5458SPrajna Rajendra Kumar }
124059f5458SPrajna Rajendra Kumar 
mchp_corespi_write_fifo(struct mchp_corespi * spi,u32 fifo_max)125059f5458SPrajna Rajendra Kumar static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, u32 fifo_max)
126059f5458SPrajna Rajendra Kumar {
127cb5c2eb4SAndy Shevchenko 	for (int i = 0; i < fifo_max; i++) {
128cb5c2eb4SAndy Shevchenko 		if (readb(spi->regs + MCHP_CORESPI_REG_STAT) &
129cb5c2eb4SAndy Shevchenko 		    MCHP_CORESPI_STATUS_TXFIFO_FULL)
130cb5c2eb4SAndy Shevchenko 			break;
131059f5458SPrajna Rajendra Kumar 
132cb5c2eb4SAndy Shevchenko 		/* On RX-only transfers always perform a dummy write */
133059f5458SPrajna Rajendra Kumar 		if (spi->tx_buf)
134cb5c2eb4SAndy Shevchenko 			writeb(*spi->tx_buf++, spi->regs + MCHP_CORESPI_REG_TXDATA);
135cb5c2eb4SAndy Shevchenko 		else
136cb5c2eb4SAndy Shevchenko 			writeb(0xaa, spi->regs + MCHP_CORESPI_REG_TXDATA);
137059f5458SPrajna Rajendra Kumar 
138cb5c2eb4SAndy Shevchenko 		spi->tx_len--;
139059f5458SPrajna Rajendra Kumar 	}
140059f5458SPrajna Rajendra Kumar }
141059f5458SPrajna Rajendra Kumar 
mchp_corespi_set_cs(struct spi_device * spi,bool disable)142059f5458SPrajna Rajendra Kumar static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
143059f5458SPrajna Rajendra Kumar {
144059f5458SPrajna Rajendra Kumar 	struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
145059f5458SPrajna Rajendra Kumar 	u32 reg;
146059f5458SPrajna Rajendra Kumar 
147059f5458SPrajna Rajendra Kumar 	reg = readb(corespi->regs + MCHP_CORESPI_REG_SSEL);
148059f5458SPrajna Rajendra Kumar 	reg &= ~BIT(spi_get_chipselect(spi, 0));
149059f5458SPrajna Rajendra Kumar 	reg |= !disable << spi_get_chipselect(spi, 0);
150059f5458SPrajna Rajendra Kumar 
151059f5458SPrajna Rajendra Kumar 	writeb(reg, corespi->regs + MCHP_CORESPI_REG_SSEL);
152059f5458SPrajna Rajendra Kumar }
153059f5458SPrajna Rajendra Kumar 
mchp_corespi_setup(struct spi_device * spi)154059f5458SPrajna Rajendra Kumar static int mchp_corespi_setup(struct spi_device *spi)
155059f5458SPrajna Rajendra Kumar {
156059f5458SPrajna Rajendra Kumar 	if (spi_get_csgpiod(spi, 0))
157059f5458SPrajna Rajendra Kumar 		return 0;
158059f5458SPrajna Rajendra Kumar 
159059f5458SPrajna Rajendra Kumar 	if (spi->mode & (SPI_CS_HIGH)) {
160059f5458SPrajna Rajendra Kumar 		dev_err(&spi->dev, "unable to support active-high CS in Motorola mode\n");
161059f5458SPrajna Rajendra Kumar 		return -EOPNOTSUPP;
162059f5458SPrajna Rajendra Kumar 	}
163059f5458SPrajna Rajendra Kumar 
1644db5a070SAndy Shevchenko 	if (spi->mode & SPI_MODE_X_MASK & ~spi->controller->mode_bits) {
165059f5458SPrajna Rajendra Kumar 		dev_err(&spi->dev, "incompatible CPOL/CPHA, must match controller's Motorola mode\n");
166059f5458SPrajna Rajendra Kumar 		return -EINVAL;
167059f5458SPrajna Rajendra Kumar 	}
168059f5458SPrajna Rajendra Kumar 
169059f5458SPrajna Rajendra Kumar 	return 0;
170059f5458SPrajna Rajendra Kumar }
171059f5458SPrajna Rajendra Kumar 
mchp_corespi_init(struct spi_controller * host,struct mchp_corespi * spi)172059f5458SPrajna Rajendra Kumar static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi)
173059f5458SPrajna Rajendra Kumar {
174059f5458SPrajna Rajendra Kumar 	u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
175059f5458SPrajna Rajendra Kumar 
176059f5458SPrajna Rajendra Kumar 	/* Master mode changes require core to be disabled.*/
177059f5458SPrajna Rajendra Kumar 	control = (control & ~MCHP_CORESPI_CONTROL_ENABLE) | MCHP_CORESPI_CONTROL_MASTER;
178059f5458SPrajna Rajendra Kumar 
179059f5458SPrajna Rajendra Kumar 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
180059f5458SPrajna Rajendra Kumar 
181059f5458SPrajna Rajendra Kumar 	mchp_corespi_enable_ints(spi);
182059f5458SPrajna Rajendra Kumar 
183059f5458SPrajna Rajendra Kumar 	control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
184059f5458SPrajna Rajendra Kumar 	control |= MCHP_CORESPI_CONTROL_ENABLE;
185059f5458SPrajna Rajendra Kumar 
186059f5458SPrajna Rajendra Kumar 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
187059f5458SPrajna Rajendra Kumar }
188059f5458SPrajna Rajendra Kumar 
mchp_corespi_interrupt(int irq,void * dev_id)189059f5458SPrajna Rajendra Kumar static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
190059f5458SPrajna Rajendra Kumar {
191059f5458SPrajna Rajendra Kumar 	struct spi_controller *host = dev_id;
192059f5458SPrajna Rajendra Kumar 	struct mchp_corespi *spi = spi_controller_get_devdata(host);
193059f5458SPrajna Rajendra Kumar 	u8 intfield = readb(spi->regs + MCHP_CORESPI_REG_INTMASK) & 0xff;
194059f5458SPrajna Rajendra Kumar 	bool finalise = false;
195059f5458SPrajna Rajendra Kumar 
196059f5458SPrajna Rajendra Kumar 	/* Interrupt line may be shared and not for us at all */
197059f5458SPrajna Rajendra Kumar 	if (intfield == 0)
198059f5458SPrajna Rajendra Kumar 		return IRQ_NONE;
199059f5458SPrajna Rajendra Kumar 
200059f5458SPrajna Rajendra Kumar 	if (intfield & MCHP_CORESPI_INT_TXDONE)
201059f5458SPrajna Rajendra Kumar 		writeb(MCHP_CORESPI_INT_TXDONE, spi->regs + MCHP_CORESPI_REG_INTCLEAR);
202059f5458SPrajna Rajendra Kumar 
203059f5458SPrajna Rajendra Kumar 	if (intfield & MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW) {
204059f5458SPrajna Rajendra Kumar 		writeb(MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW,
205059f5458SPrajna Rajendra Kumar 		       spi->regs + MCHP_CORESPI_REG_INTCLEAR);
206059f5458SPrajna Rajendra Kumar 		finalise = true;
207059f5458SPrajna Rajendra Kumar 		dev_err(&host->dev,
208e29aca70SAndy Shevchenko 			"RX OVERFLOW: rxlen: %u, txlen: %u\n",
209059f5458SPrajna Rajendra Kumar 			spi->rx_len, spi->tx_len);
210059f5458SPrajna Rajendra Kumar 	}
211059f5458SPrajna Rajendra Kumar 
212059f5458SPrajna Rajendra Kumar 	if (intfield & MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN) {
213059f5458SPrajna Rajendra Kumar 		writeb(MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN,
214059f5458SPrajna Rajendra Kumar 		       spi->regs + MCHP_CORESPI_REG_INTCLEAR);
215059f5458SPrajna Rajendra Kumar 		finalise = true;
216059f5458SPrajna Rajendra Kumar 		dev_err(&host->dev,
217e29aca70SAndy Shevchenko 			"TX UNDERFLOW: rxlen: %u, txlen: %u\n",
218059f5458SPrajna Rajendra Kumar 			spi->rx_len, spi->tx_len);
219059f5458SPrajna Rajendra Kumar 	}
220059f5458SPrajna Rajendra Kumar 
221059f5458SPrajna Rajendra Kumar 	if (finalise)
222059f5458SPrajna Rajendra Kumar 		spi_finalize_current_transfer(host);
223059f5458SPrajna Rajendra Kumar 
224059f5458SPrajna Rajendra Kumar 	return IRQ_HANDLED;
225059f5458SPrajna Rajendra Kumar }
226059f5458SPrajna Rajendra Kumar 
mchp_corespi_set_clk_div(struct mchp_corespi * spi,unsigned long target_hz)227059f5458SPrajna Rajendra Kumar static int mchp_corespi_set_clk_div(struct mchp_corespi *spi,
228059f5458SPrajna Rajendra Kumar 				    unsigned long target_hz)
229059f5458SPrajna Rajendra Kumar {
230059f5458SPrajna Rajendra Kumar 	unsigned long pclk_hz, spi_hz;
231059f5458SPrajna Rajendra Kumar 	u32 clk_div;
232059f5458SPrajna Rajendra Kumar 
233059f5458SPrajna Rajendra Kumar 	/* Get peripheral clock rate */
234059f5458SPrajna Rajendra Kumar 	pclk_hz = clk_get_rate(spi->clk);
235059f5458SPrajna Rajendra Kumar 	if (!pclk_hz)
236059f5458SPrajna Rajendra Kumar 		return -EINVAL;
237059f5458SPrajna Rajendra Kumar 
238059f5458SPrajna Rajendra Kumar 	/*
239059f5458SPrajna Rajendra Kumar 	 * Calculate clock rate generated by SPI master
240059f5458SPrajna Rajendra Kumar 	 * Formula: SPICLK = PCLK / (2 * (CLK_DIV + 1))
241059f5458SPrajna Rajendra Kumar 	 */
242059f5458SPrajna Rajendra Kumar 	clk_div = DIV_ROUND_UP(pclk_hz, 2 * target_hz) - 1;
243059f5458SPrajna Rajendra Kumar 
244059f5458SPrajna Rajendra Kumar 	if (clk_div > 0xFF)
245059f5458SPrajna Rajendra Kumar 		return -EINVAL;
246059f5458SPrajna Rajendra Kumar 
247059f5458SPrajna Rajendra Kumar 	spi_hz = pclk_hz / (2 * (clk_div + 1));
248059f5458SPrajna Rajendra Kumar 
249059f5458SPrajna Rajendra Kumar 	if (spi_hz > target_hz)
250059f5458SPrajna Rajendra Kumar 		return -EINVAL;
251059f5458SPrajna Rajendra Kumar 
252059f5458SPrajna Rajendra Kumar 	writeb(clk_div, spi->regs + MCHP_CORESPI_REG_CLK_DIV);
253059f5458SPrajna Rajendra Kumar 
254059f5458SPrajna Rajendra Kumar 	return 0;
255059f5458SPrajna Rajendra Kumar }
256059f5458SPrajna Rajendra Kumar 
mchp_corespi_transfer_one(struct spi_controller * host,struct spi_device * spi_dev,struct spi_transfer * xfer)257059f5458SPrajna Rajendra Kumar static int mchp_corespi_transfer_one(struct spi_controller *host,
258059f5458SPrajna Rajendra Kumar 				     struct spi_device *spi_dev,
259059f5458SPrajna Rajendra Kumar 				     struct spi_transfer *xfer)
260059f5458SPrajna Rajendra Kumar {
261059f5458SPrajna Rajendra Kumar 	struct mchp_corespi *spi = spi_controller_get_devdata(host);
262059f5458SPrajna Rajendra Kumar 	int ret;
263059f5458SPrajna Rajendra Kumar 
264059f5458SPrajna Rajendra Kumar 	ret = mchp_corespi_set_clk_div(spi, (unsigned long)xfer->speed_hz);
265059f5458SPrajna Rajendra Kumar 	if (ret) {
266059f5458SPrajna Rajendra Kumar 		dev_err(&host->dev, "failed to set clock divider for target %u Hz\n",
267059f5458SPrajna Rajendra Kumar 			xfer->speed_hz);
268059f5458SPrajna Rajendra Kumar 		return ret;
269059f5458SPrajna Rajendra Kumar 	}
270059f5458SPrajna Rajendra Kumar 
271059f5458SPrajna Rajendra Kumar 	spi->tx_buf = xfer->tx_buf;
272059f5458SPrajna Rajendra Kumar 	spi->rx_buf = xfer->rx_buf;
273059f5458SPrajna Rajendra Kumar 	spi->tx_len = xfer->len;
274059f5458SPrajna Rajendra Kumar 	spi->rx_len = xfer->len;
275059f5458SPrajna Rajendra Kumar 
276059f5458SPrajna Rajendra Kumar 	while (spi->tx_len) {
277e29aca70SAndy Shevchenko 		unsigned int fifo_max = min(spi->tx_len, spi->fifo_depth);
278059f5458SPrajna Rajendra Kumar 
279059f5458SPrajna Rajendra Kumar 		mchp_corespi_write_fifo(spi, fifo_max);
280059f5458SPrajna Rajendra Kumar 		mchp_corespi_read_fifo(spi, fifo_max);
281059f5458SPrajna Rajendra Kumar 	}
282059f5458SPrajna Rajendra Kumar 
283059f5458SPrajna Rajendra Kumar 	spi_finalize_current_transfer(host);
284059f5458SPrajna Rajendra Kumar 	return 1;
285059f5458SPrajna Rajendra Kumar }
286059f5458SPrajna Rajendra Kumar 
mchp_corespi_probe(struct platform_device * pdev)287059f5458SPrajna Rajendra Kumar static int mchp_corespi_probe(struct platform_device *pdev)
288059f5458SPrajna Rajendra Kumar {
289cb99656bSDan Carpenter 	const char *protocol = "motorola";
29006b010d3SAndy Shevchenko 	struct device *dev = &pdev->dev;
291059f5458SPrajna Rajendra Kumar 	struct spi_controller *host;
292059f5458SPrajna Rajendra Kumar 	struct mchp_corespi *spi;
293059f5458SPrajna Rajendra Kumar 	struct resource *res;
294059f5458SPrajna Rajendra Kumar 	u32 num_cs, mode, frame_size;
295059f5458SPrajna Rajendra Kumar 	bool assert_ssel;
296059f5458SPrajna Rajendra Kumar 	int ret = 0;
297059f5458SPrajna Rajendra Kumar 
29806b010d3SAndy Shevchenko 	host = devm_spi_alloc_host(dev, sizeof(*spi));
299059f5458SPrajna Rajendra Kumar 	if (!host)
300274b3458SAndy Shevchenko 		return -ENOMEM;
301059f5458SPrajna Rajendra Kumar 
302059f5458SPrajna Rajendra Kumar 	platform_set_drvdata(pdev, host);
303059f5458SPrajna Rajendra Kumar 
30406b010d3SAndy Shevchenko 	if (of_property_read_u32(dev->of_node, "num-cs", &num_cs))
305059f5458SPrajna Rajendra Kumar 		num_cs = MCHP_CORESPI_MAX_CS;
306059f5458SPrajna Rajendra Kumar 
307059f5458SPrajna Rajendra Kumar 	/*
308059f5458SPrajna Rajendra Kumar 	 * Protocol: CFG_MODE
309059f5458SPrajna Rajendra Kumar 	 * CoreSPI can be configured for Motorola, TI or NSC.
310059f5458SPrajna Rajendra Kumar 	 * The current driver supports only Motorola mode.
311059f5458SPrajna Rajendra Kumar 	 */
31206b010d3SAndy Shevchenko 	ret = of_property_read_string(dev->of_node, "microchip,protocol-configuration",
313059f5458SPrajna Rajendra Kumar 				      &protocol);
314cb99656bSDan Carpenter 	if (ret && ret != -EINVAL)
31506b010d3SAndy Shevchenko 		return dev_err_probe(dev, ret, "Error reading protocol-configuration\n");
316059f5458SPrajna Rajendra Kumar 	if (strcmp(protocol, "motorola") != 0)
31706b010d3SAndy Shevchenko 		return dev_err_probe(dev, -EINVAL,
318059f5458SPrajna Rajendra Kumar 				     "CoreSPI: protocol '%s' not supported by this driver\n",
319059f5458SPrajna Rajendra Kumar 				      protocol);
320059f5458SPrajna Rajendra Kumar 
321059f5458SPrajna Rajendra Kumar 	/*
322059f5458SPrajna Rajendra Kumar 	 * Motorola mode (0-3): CFG_MOT_MODE
323059f5458SPrajna Rajendra Kumar 	 * Mode is fixed in the IP configurator.
324059f5458SPrajna Rajendra Kumar 	 */
32506b010d3SAndy Shevchenko 	ret = of_property_read_u32(dev->of_node, "microchip,motorola-mode", &mode);
326059f5458SPrajna Rajendra Kumar 	if (ret)
327059f5458SPrajna Rajendra Kumar 		mode = MCHP_CORESPI_DEFAULT_MOTOROLA_MODE;
328059f5458SPrajna Rajendra Kumar 	else if (mode > 3)
32906b010d3SAndy Shevchenko 		return dev_err_probe(dev, -EINVAL,
330059f5458SPrajna Rajendra Kumar 				     "invalid 'microchip,motorola-mode' value %u\n", mode);
331059f5458SPrajna Rajendra Kumar 
332059f5458SPrajna Rajendra Kumar 	/*
333059f5458SPrajna Rajendra Kumar 	 * Frame size: CFG_FRAME_SIZE
334059f5458SPrajna Rajendra Kumar 	 * The hardware allows frame sizes <= APB data width.
335059f5458SPrajna Rajendra Kumar 	 * However, this driver currently only supports 8-bit frames.
336059f5458SPrajna Rajendra Kumar 	 */
33706b010d3SAndy Shevchenko 	ret = of_property_read_u32(dev->of_node, "microchip,frame-size", &frame_size);
338059f5458SPrajna Rajendra Kumar 	if (!ret && frame_size != 8)
33906b010d3SAndy Shevchenko 		return dev_err_probe(dev, -EINVAL,
340059f5458SPrajna Rajendra Kumar 				     "CoreSPI: frame size %u not supported by this driver\n",
341059f5458SPrajna Rajendra Kumar 				     frame_size);
342059f5458SPrajna Rajendra Kumar 
343059f5458SPrajna Rajendra Kumar 	/*
344059f5458SPrajna Rajendra Kumar 	 * SSEL: CFG_MOT_SSEL
345059f5458SPrajna Rajendra Kumar 	 * CoreSPI deasserts SSEL when the TX FIFO empties.
346059f5458SPrajna Rajendra Kumar 	 * To prevent CS deassertion when TX FIFO drains, the ssel-active property
347059f5458SPrajna Rajendra Kumar 	 * keeps CS asserted for the full SPI transfer.
348059f5458SPrajna Rajendra Kumar 	 */
34906b010d3SAndy Shevchenko 	assert_ssel = of_property_read_bool(dev->of_node, "microchip,ssel-active");
350059f5458SPrajna Rajendra Kumar 	if (!assert_ssel)
35106b010d3SAndy Shevchenko 		return dev_err_probe(dev, -EINVAL,
352059f5458SPrajna Rajendra Kumar 				     "hardware must enable 'microchip,ssel-active' to keep CS asserted for the SPI transfer\n");
353059f5458SPrajna Rajendra Kumar 
354059f5458SPrajna Rajendra Kumar 	spi = spi_controller_get_devdata(host);
355059f5458SPrajna Rajendra Kumar 
356059f5458SPrajna Rajendra Kumar 	host->num_chipselect = num_cs;
357059f5458SPrajna Rajendra Kumar 	host->mode_bits = mode;
358059f5458SPrajna Rajendra Kumar 	host->setup = mchp_corespi_setup;
359059f5458SPrajna Rajendra Kumar 	host->use_gpio_descriptors = true;
360059f5458SPrajna Rajendra Kumar 	host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
361059f5458SPrajna Rajendra Kumar 	host->transfer_one = mchp_corespi_transfer_one;
362059f5458SPrajna Rajendra Kumar 	host->set_cs = mchp_corespi_set_cs;
36306b010d3SAndy Shevchenko 	host->dev.of_node = dev->of_node;
364059f5458SPrajna Rajendra Kumar 
36506b010d3SAndy Shevchenko 	ret = of_property_read_u32(dev->of_node, "fifo-depth", &spi->fifo_depth);
366059f5458SPrajna Rajendra Kumar 	if (ret)
367059f5458SPrajna Rajendra Kumar 		spi->fifo_depth = MCHP_CORESPI_DEFAULT_FIFO_DEPTH;
368059f5458SPrajna Rajendra Kumar 
369059f5458SPrajna Rajendra Kumar 	spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
370059f5458SPrajna Rajendra Kumar 	if (IS_ERR(spi->regs))
371059f5458SPrajna Rajendra Kumar 		return PTR_ERR(spi->regs);
372059f5458SPrajna Rajendra Kumar 
373059f5458SPrajna Rajendra Kumar 	spi->irq = platform_get_irq(pdev, 0);
374059f5458SPrajna Rajendra Kumar 	if (spi->irq < 0)
375059f5458SPrajna Rajendra Kumar 		return spi->irq;
376059f5458SPrajna Rajendra Kumar 
37706b010d3SAndy Shevchenko 	ret = devm_request_irq(dev, spi->irq, mchp_corespi_interrupt, IRQF_SHARED,
37806b010d3SAndy Shevchenko 			       dev_name(dev), host);
379059f5458SPrajna Rajendra Kumar 	if (ret)
38006b010d3SAndy Shevchenko 		return dev_err_probe(dev, ret, "could not request irq\n");
381059f5458SPrajna Rajendra Kumar 
38206b010d3SAndy Shevchenko 	spi->clk = devm_clk_get_enabled(dev, NULL);
383059f5458SPrajna Rajendra Kumar 	if (IS_ERR(spi->clk))
38406b010d3SAndy Shevchenko 		return dev_err_probe(dev, PTR_ERR(spi->clk), "could not get clk\n");
385059f5458SPrajna Rajendra Kumar 
386059f5458SPrajna Rajendra Kumar 	mchp_corespi_init(host, spi);
387059f5458SPrajna Rajendra Kumar 
38806b010d3SAndy Shevchenko 	ret = devm_spi_register_controller(dev, host);
389059f5458SPrajna Rajendra Kumar 	if (ret) {
390*8cef9b45SChristophe JAILLET 		mchp_corespi_disable_ints(spi);
391059f5458SPrajna Rajendra Kumar 		mchp_corespi_disable(spi);
39206b010d3SAndy Shevchenko 		return dev_err_probe(dev, ret, "unable to register host for CoreSPI controller\n");
393059f5458SPrajna Rajendra Kumar 	}
394059f5458SPrajna Rajendra Kumar 
395059f5458SPrajna Rajendra Kumar 	return 0;
396059f5458SPrajna Rajendra Kumar }
397059f5458SPrajna Rajendra Kumar 
mchp_corespi_remove(struct platform_device * pdev)398059f5458SPrajna Rajendra Kumar static void mchp_corespi_remove(struct platform_device *pdev)
399059f5458SPrajna Rajendra Kumar {
400059f5458SPrajna Rajendra Kumar 	struct spi_controller *host = platform_get_drvdata(pdev);
401059f5458SPrajna Rajendra Kumar 	struct mchp_corespi *spi = spi_controller_get_devdata(host);
402059f5458SPrajna Rajendra Kumar 
403059f5458SPrajna Rajendra Kumar 	mchp_corespi_disable_ints(spi);
404059f5458SPrajna Rajendra Kumar 	mchp_corespi_disable(spi);
405059f5458SPrajna Rajendra Kumar }
406059f5458SPrajna Rajendra Kumar 
407059f5458SPrajna Rajendra Kumar /*
408059f5458SPrajna Rajendra Kumar  * Platform driver data structure
409059f5458SPrajna Rajendra Kumar  */
410059f5458SPrajna Rajendra Kumar 
411059f5458SPrajna Rajendra Kumar #if defined(CONFIG_OF)
412059f5458SPrajna Rajendra Kumar static const struct of_device_id mchp_corespi_dt_ids[] = {
413059f5458SPrajna Rajendra Kumar 	{ .compatible = "microchip,corespi-rtl-v5" },
414059f5458SPrajna Rajendra Kumar 	{ /* sentinel */ }
415059f5458SPrajna Rajendra Kumar };
416059f5458SPrajna Rajendra Kumar MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
417059f5458SPrajna Rajendra Kumar #endif
418059f5458SPrajna Rajendra Kumar 
419059f5458SPrajna Rajendra Kumar static struct platform_driver mchp_corespi_driver = {
420059f5458SPrajna Rajendra Kumar 	.probe = mchp_corespi_probe,
421059f5458SPrajna Rajendra Kumar 	.driver = {
422059f5458SPrajna Rajendra Kumar 		.name = "microchip-corespi",
423059f5458SPrajna Rajendra Kumar 		.of_match_table = of_match_ptr(mchp_corespi_dt_ids),
424059f5458SPrajna Rajendra Kumar 	},
425059f5458SPrajna Rajendra Kumar 	.remove = mchp_corespi_remove,
426059f5458SPrajna Rajendra Kumar };
427059f5458SPrajna Rajendra Kumar module_platform_driver(mchp_corespi_driver);
428059f5458SPrajna Rajendra Kumar MODULE_DESCRIPTION("Microchip CoreSPI controller driver");
429059f5458SPrajna Rajendra Kumar MODULE_AUTHOR("Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>");
430059f5458SPrajna Rajendra Kumar MODULE_LICENSE("GPL");
431