xref: /linux/drivers/spi/spi-microchip-core-spi.c (revision 8c8081cc599fcafa25371d50959c17e154f9fd08)
1 // SPDX-License-Identifier: (GPL-2.0)
2 //
3 // Microchip CoreSPI controller driver
4 //
5 // Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries
6 //
7 // Author: Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>
8 
9 #include <linux/clk.h>
10 #include <linux/delay.h>
11 #include <linux/err.h>
12 #include <linux/init.h>
13 #include <linux/interrupt.h>
14 #include <linux/io.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/platform_device.h>
18 #include <linux/spi/spi.h>
19 
20 #define MCHP_CORESPI_MAX_CS				(8)
21 #define MCHP_CORESPI_DEFAULT_FIFO_DEPTH			(4)
22 #define MCHP_CORESPI_DEFAULT_MOTOROLA_MODE		(3)
23 
24 #define MCHP_CORESPI_CONTROL_ENABLE			BIT(0)
25 #define MCHP_CORESPI_CONTROL_MASTER			BIT(1)
26 #define MCHP_CORESPI_CONTROL_TX_DATA_INT		BIT(3)
27 #define MCHP_CORESPI_CONTROL_RX_OVER_INT		BIT(4)
28 #define MCHP_CORESPI_CONTROL_TX_UNDER_INT		BIT(5)
29 #define MCHP_CORESPI_CONTROL_FRAMEURUN			BIT(6)
30 #define MCHP_CORESPI_CONTROL_OENOFF			BIT(7)
31 
32 #define MCHP_CORESPI_STATUS_ACTIVE			BIT(7)
33 #define MCHP_CORESPI_STATUS_SSEL			BIT(6)
34 #define MCHP_CORESPI_STATUS_TXFIFO_UNDERFLOW		BIT(5)
35 #define MCHP_CORESPI_STATUS_RXFIFO_FULL			BIT(4)
36 #define MCHP_CORESPI_STATUS_TXFIFO_FULL			BIT(3)
37 #define MCHP_CORESPI_STATUS_RXFIFO_EMPTY		BIT(2)
38 #define MCHP_CORESPI_STATUS_DONE			BIT(1)
39 #define MCHP_CORESPI_STATUS_FIRSTFRAME			BIT(0)
40 
41 #define MCHP_CORESPI_INT_TXDONE				BIT(0)
42 #define MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW		BIT(2)
43 #define MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN		BIT(3)
44 #define MCHP_CORESPI_INT_CMDINT				BIT(4)
45 #define MCHP_CORESPI_INT_SSEND				BIT(5)
46 #define MCHP_CORESPI_INT_DATA_RX			BIT(6)
47 #define MCHP_CORESPI_INT_TXRFM				BIT(7)
48 
49 #define MCHP_CORESPI_CONTROL2_INTEN_TXRFMT		BIT(7)
50 #define MCHP_CORESPI_CONTROL2_INTEN_DATA_RX		BIT(6)
51 #define MCHP_CORESPI_CONTROL2_INTEN_SSEND		BIT(5)
52 #define MCHP_CORESPI_CONTROL2_INTEN_CMD			BIT(4)
53 
54 #define INT_ENABLE_MASK (MCHP_CORESPI_CONTROL_TX_DATA_INT | MCHP_CORESPI_CONTROL_RX_OVER_INT | \
55 			 MCHP_CORESPI_CONTROL_TX_UNDER_INT)
56 
57 #define MCHP_CORESPI_REG_CONTROL			(0x00)
58 #define MCHP_CORESPI_REG_INTCLEAR			(0x04)
59 #define MCHP_CORESPI_REG_RXDATA				(0x08)
60 #define MCHP_CORESPI_REG_TXDATA				(0x0c)
61 #define MCHP_CORESPI_REG_INTMASK			(0X10)
62 #define MCHP_CORESPI_REG_INTRAW				(0X14)
63 #define MCHP_CORESPI_REG_CONTROL2			(0x18)
64 #define MCHP_CORESPI_REG_COMMAND			(0x1c)
65 #define MCHP_CORESPI_REG_STAT				(0x20)
66 #define MCHP_CORESPI_REG_SSEL				(0x24)
67 #define MCHP_CORESPI_REG_TXDATA_LAST			(0X28)
68 #define MCHP_CORESPI_REG_CLK_DIV			(0x2c)
69 
70 struct mchp_corespi {
71 	void __iomem *regs;
72 	struct clk *clk;
73 	const u8 *tx_buf;
74 	u8 *rx_buf;
75 	u32 clk_gen;
76 	int irq;
77 	unsigned int tx_len;
78 	unsigned int rx_len;
79 	u32 fifo_depth;
80 };
81 
mchp_corespi_disable(struct mchp_corespi * spi)82 static inline void mchp_corespi_disable(struct mchp_corespi *spi)
83 {
84 	u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
85 
86 	control &= ~MCHP_CORESPI_CONTROL_ENABLE;
87 
88 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
89 }
90 
mchp_corespi_read_fifo(struct mchp_corespi * spi,u32 fifo_max)91 static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, u32 fifo_max)
92 {
93 	for (int i = 0; i < fifo_max; i++) {
94 		u32 data;
95 
96 		while (readb(spi->regs + MCHP_CORESPI_REG_STAT) &
97 		       MCHP_CORESPI_STATUS_RXFIFO_EMPTY)
98 			;
99 
100 		/* On TX-only transfers always perform a dummy read */
101 		data = readb(spi->regs + MCHP_CORESPI_REG_RXDATA);
102 		if (spi->rx_buf)
103 			*spi->rx_buf++ = data;
104 
105 		spi->rx_len--;
106 	}
107 }
108 
mchp_corespi_enable_ints(struct mchp_corespi * spi)109 static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
110 {
111 	u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
112 
113 	control |= INT_ENABLE_MASK;
114 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
115 }
116 
mchp_corespi_disable_ints(struct mchp_corespi * spi)117 static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
118 {
119 	u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
120 
121 	control &= ~INT_ENABLE_MASK;
122 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
123 }
124 
mchp_corespi_write_fifo(struct mchp_corespi * spi,u32 fifo_max)125 static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, u32 fifo_max)
126 {
127 	for (int i = 0; i < fifo_max; i++) {
128 		if (readb(spi->regs + MCHP_CORESPI_REG_STAT) &
129 		    MCHP_CORESPI_STATUS_TXFIFO_FULL)
130 			break;
131 
132 		/* On RX-only transfers always perform a dummy write */
133 		if (spi->tx_buf)
134 			writeb(*spi->tx_buf++, spi->regs + MCHP_CORESPI_REG_TXDATA);
135 		else
136 			writeb(0xaa, spi->regs + MCHP_CORESPI_REG_TXDATA);
137 
138 		spi->tx_len--;
139 	}
140 }
141 
mchp_corespi_set_cs(struct spi_device * spi,bool disable)142 static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
143 {
144 	struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
145 	u32 reg;
146 
147 	reg = readb(corespi->regs + MCHP_CORESPI_REG_SSEL);
148 	reg &= ~BIT(spi_get_chipselect(spi, 0));
149 	reg |= !disable << spi_get_chipselect(spi, 0);
150 
151 	writeb(reg, corespi->regs + MCHP_CORESPI_REG_SSEL);
152 }
153 
mchp_corespi_setup(struct spi_device * spi)154 static int mchp_corespi_setup(struct spi_device *spi)
155 {
156 	if (spi_get_csgpiod(spi, 0))
157 		return 0;
158 
159 	if (spi->mode & (SPI_CS_HIGH)) {
160 		dev_err(&spi->dev, "unable to support active-high CS in Motorola mode\n");
161 		return -EOPNOTSUPP;
162 	}
163 
164 	if (spi->mode & SPI_MODE_X_MASK & ~spi->controller->mode_bits) {
165 		dev_err(&spi->dev, "incompatible CPOL/CPHA, must match controller's Motorola mode\n");
166 		return -EINVAL;
167 	}
168 
169 	return 0;
170 }
171 
mchp_corespi_init(struct spi_controller * host,struct mchp_corespi * spi)172 static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi)
173 {
174 	u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
175 
176 	/* Master mode changes require core to be disabled.*/
177 	control = (control & ~MCHP_CORESPI_CONTROL_ENABLE) | MCHP_CORESPI_CONTROL_MASTER;
178 
179 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
180 
181 	mchp_corespi_enable_ints(spi);
182 
183 	control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
184 	control |= MCHP_CORESPI_CONTROL_ENABLE;
185 
186 	writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
187 }
188 
mchp_corespi_interrupt(int irq,void * dev_id)189 static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
190 {
191 	struct spi_controller *host = dev_id;
192 	struct mchp_corespi *spi = spi_controller_get_devdata(host);
193 	u8 intfield = readb(spi->regs + MCHP_CORESPI_REG_INTMASK) & 0xff;
194 	bool finalise = false;
195 
196 	/* Interrupt line may be shared and not for us at all */
197 	if (intfield == 0)
198 		return IRQ_NONE;
199 
200 	if (intfield & MCHP_CORESPI_INT_TXDONE)
201 		writeb(MCHP_CORESPI_INT_TXDONE, spi->regs + MCHP_CORESPI_REG_INTCLEAR);
202 
203 	if (intfield & MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW) {
204 		writeb(MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW,
205 		       spi->regs + MCHP_CORESPI_REG_INTCLEAR);
206 		finalise = true;
207 		dev_err(&host->dev,
208 			"RX OVERFLOW: rxlen: %u, txlen: %u\n",
209 			spi->rx_len, spi->tx_len);
210 	}
211 
212 	if (intfield & MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN) {
213 		writeb(MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN,
214 		       spi->regs + MCHP_CORESPI_REG_INTCLEAR);
215 		finalise = true;
216 		dev_err(&host->dev,
217 			"TX UNDERFLOW: rxlen: %u, txlen: %u\n",
218 			spi->rx_len, spi->tx_len);
219 	}
220 
221 	if (finalise)
222 		spi_finalize_current_transfer(host);
223 
224 	return IRQ_HANDLED;
225 }
226 
mchp_corespi_set_clk_div(struct mchp_corespi * spi,unsigned long target_hz)227 static int mchp_corespi_set_clk_div(struct mchp_corespi *spi,
228 				    unsigned long target_hz)
229 {
230 	unsigned long pclk_hz, spi_hz;
231 	u32 clk_div;
232 
233 	/* Get peripheral clock rate */
234 	pclk_hz = clk_get_rate(spi->clk);
235 	if (!pclk_hz)
236 		return -EINVAL;
237 
238 	/*
239 	 * Calculate clock rate generated by SPI master
240 	 * Formula: SPICLK = PCLK / (2 * (CLK_DIV + 1))
241 	 */
242 	clk_div = DIV_ROUND_UP(pclk_hz, 2 * target_hz) - 1;
243 
244 	if (clk_div > 0xFF)
245 		return -EINVAL;
246 
247 	spi_hz = pclk_hz / (2 * (clk_div + 1));
248 
249 	if (spi_hz > target_hz)
250 		return -EINVAL;
251 
252 	writeb(clk_div, spi->regs + MCHP_CORESPI_REG_CLK_DIV);
253 
254 	return 0;
255 }
256 
mchp_corespi_transfer_one(struct spi_controller * host,struct spi_device * spi_dev,struct spi_transfer * xfer)257 static int mchp_corespi_transfer_one(struct spi_controller *host,
258 				     struct spi_device *spi_dev,
259 				     struct spi_transfer *xfer)
260 {
261 	struct mchp_corespi *spi = spi_controller_get_devdata(host);
262 	int ret;
263 
264 	ret = mchp_corespi_set_clk_div(spi, (unsigned long)xfer->speed_hz);
265 	if (ret) {
266 		dev_err(&host->dev, "failed to set clock divider for target %u Hz\n",
267 			xfer->speed_hz);
268 		return ret;
269 	}
270 
271 	spi->tx_buf = xfer->tx_buf;
272 	spi->rx_buf = xfer->rx_buf;
273 	spi->tx_len = xfer->len;
274 	spi->rx_len = xfer->len;
275 
276 	while (spi->tx_len) {
277 		unsigned int fifo_max = min(spi->tx_len, spi->fifo_depth);
278 
279 		mchp_corespi_write_fifo(spi, fifo_max);
280 		mchp_corespi_read_fifo(spi, fifo_max);
281 	}
282 
283 	spi_finalize_current_transfer(host);
284 	return 1;
285 }
286 
mchp_corespi_probe(struct platform_device * pdev)287 static int mchp_corespi_probe(struct platform_device *pdev)
288 {
289 	const char *protocol = "motorola";
290 	struct device *dev = &pdev->dev;
291 	struct spi_controller *host;
292 	struct mchp_corespi *spi;
293 	struct resource *res;
294 	u32 num_cs, mode, frame_size;
295 	bool assert_ssel;
296 	int ret = 0;
297 
298 	host = devm_spi_alloc_host(dev, sizeof(*spi));
299 	if (!host)
300 		return -ENOMEM;
301 
302 	platform_set_drvdata(pdev, host);
303 
304 	if (of_property_read_u32(dev->of_node, "num-cs", &num_cs))
305 		num_cs = MCHP_CORESPI_MAX_CS;
306 
307 	/*
308 	 * Protocol: CFG_MODE
309 	 * CoreSPI can be configured for Motorola, TI or NSC.
310 	 * The current driver supports only Motorola mode.
311 	 */
312 	ret = of_property_read_string(dev->of_node, "microchip,protocol-configuration",
313 				      &protocol);
314 	if (ret && ret != -EINVAL)
315 		return dev_err_probe(dev, ret, "Error reading protocol-configuration\n");
316 	if (strcmp(protocol, "motorola") != 0)
317 		return dev_err_probe(dev, -EINVAL,
318 				     "CoreSPI: protocol '%s' not supported by this driver\n",
319 				      protocol);
320 
321 	/*
322 	 * Motorola mode (0-3): CFG_MOT_MODE
323 	 * Mode is fixed in the IP configurator.
324 	 */
325 	ret = of_property_read_u32(dev->of_node, "microchip,motorola-mode", &mode);
326 	if (ret)
327 		mode = MCHP_CORESPI_DEFAULT_MOTOROLA_MODE;
328 	else if (mode > 3)
329 		return dev_err_probe(dev, -EINVAL,
330 				     "invalid 'microchip,motorola-mode' value %u\n", mode);
331 
332 	/*
333 	 * Frame size: CFG_FRAME_SIZE
334 	 * The hardware allows frame sizes <= APB data width.
335 	 * However, this driver currently only supports 8-bit frames.
336 	 */
337 	ret = of_property_read_u32(dev->of_node, "microchip,frame-size", &frame_size);
338 	if (!ret && frame_size != 8)
339 		return dev_err_probe(dev, -EINVAL,
340 				     "CoreSPI: frame size %u not supported by this driver\n",
341 				     frame_size);
342 
343 	/*
344 	 * SSEL: CFG_MOT_SSEL
345 	 * CoreSPI deasserts SSEL when the TX FIFO empties.
346 	 * To prevent CS deassertion when TX FIFO drains, the ssel-active property
347 	 * keeps CS asserted for the full SPI transfer.
348 	 */
349 	assert_ssel = of_property_read_bool(dev->of_node, "microchip,ssel-active");
350 	if (!assert_ssel)
351 		return dev_err_probe(dev, -EINVAL,
352 				     "hardware must enable 'microchip,ssel-active' to keep CS asserted for the SPI transfer\n");
353 
354 	spi = spi_controller_get_devdata(host);
355 
356 	host->num_chipselect = num_cs;
357 	host->mode_bits = mode;
358 	host->setup = mchp_corespi_setup;
359 	host->use_gpio_descriptors = true;
360 	host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
361 	host->transfer_one = mchp_corespi_transfer_one;
362 	host->set_cs = mchp_corespi_set_cs;
363 	host->dev.of_node = dev->of_node;
364 
365 	ret = of_property_read_u32(dev->of_node, "fifo-depth", &spi->fifo_depth);
366 	if (ret)
367 		spi->fifo_depth = MCHP_CORESPI_DEFAULT_FIFO_DEPTH;
368 
369 	spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
370 	if (IS_ERR(spi->regs))
371 		return PTR_ERR(spi->regs);
372 
373 	spi->irq = platform_get_irq(pdev, 0);
374 	if (spi->irq < 0)
375 		return spi->irq;
376 
377 	ret = devm_request_irq(dev, spi->irq, mchp_corespi_interrupt, IRQF_SHARED,
378 			       dev_name(dev), host);
379 	if (ret)
380 		return dev_err_probe(dev, ret, "could not request irq\n");
381 
382 	spi->clk = devm_clk_get_enabled(dev, NULL);
383 	if (IS_ERR(spi->clk))
384 		return dev_err_probe(dev, PTR_ERR(spi->clk), "could not get clk\n");
385 
386 	mchp_corespi_init(host, spi);
387 
388 	ret = devm_spi_register_controller(dev, host);
389 	if (ret) {
390 		mchp_corespi_disable_ints(spi);
391 		mchp_corespi_disable(spi);
392 		return dev_err_probe(dev, ret, "unable to register host for CoreSPI controller\n");
393 	}
394 
395 	return 0;
396 }
397 
mchp_corespi_remove(struct platform_device * pdev)398 static void mchp_corespi_remove(struct platform_device *pdev)
399 {
400 	struct spi_controller *host = platform_get_drvdata(pdev);
401 	struct mchp_corespi *spi = spi_controller_get_devdata(host);
402 
403 	mchp_corespi_disable_ints(spi);
404 	mchp_corespi_disable(spi);
405 }
406 
407 /*
408  * Platform driver data structure
409  */
410 
411 #if defined(CONFIG_OF)
412 static const struct of_device_id mchp_corespi_dt_ids[] = {
413 	{ .compatible = "microchip,corespi-rtl-v5" },
414 	{ /* sentinel */ }
415 };
416 MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
417 #endif
418 
419 static struct platform_driver mchp_corespi_driver = {
420 	.probe = mchp_corespi_probe,
421 	.driver = {
422 		.name = "microchip-corespi",
423 		.of_match_table = of_match_ptr(mchp_corespi_dt_ids),
424 	},
425 	.remove = mchp_corespi_remove,
426 };
427 module_platform_driver(mchp_corespi_driver);
428 MODULE_DESCRIPTION("Microchip CoreSPI controller driver");
429 MODULE_AUTHOR("Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>");
430 MODULE_LICENSE("GPL");
431