projects/linux·blob master · refs

431 lines · 12.0 KiB

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 <[email protected]>
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
70struct 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
82static 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
91static 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
109static 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
117static 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
125static 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
142static 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
154static 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->controller->mode_bits) & SPI_MODE_X_MASK) {
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
172static 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
189static 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
227static 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
257static 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
287static 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
364 ret = of_property_read_u32(dev->of_node, "fifo-depth", &spi->fifo_depth);
365 if (ret)
366 spi->fifo_depth = MCHP_CORESPI_DEFAULT_FIFO_DEPTH;
367
368 spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
369 if (IS_ERR(spi->regs))
370 return PTR_ERR(spi->regs);
371
372 spi->irq = platform_get_irq(pdev, 0);
373 if (spi->irq < 0)
374 return spi->irq;
375
376 ret = devm_request_irq(dev, spi->irq, mchp_corespi_interrupt, IRQF_SHARED,
377 dev_name(dev), host);
378 if (ret)
379 return dev_err_probe(dev, ret, "could not request irq\n");
380
381 spi->clk = devm_clk_get_enabled(dev, NULL);
382 if (IS_ERR(spi->clk))
383 return dev_err_probe(dev, PTR_ERR(spi->clk), "could not get clk\n");
384
385 mchp_corespi_init(host, spi);
386
387 ret = spi_register_controller(host);
388 if (ret) {
389 mchp_corespi_disable_ints(spi);
390 mchp_corespi_disable(spi);
391 return dev_err_probe(dev, ret, "unable to register host for CoreSPI controller\n");
392 }
393
394 return 0;
395}
396
397static void mchp_corespi_remove(struct platform_device *pdev)
398{
399 struct spi_controller *host = platform_get_drvdata(pdev);
400 struct mchp_corespi *spi = spi_controller_get_devdata(host);
401
402 spi_unregister_controller(host);
403
404 mchp_corespi_disable_ints(spi);
405 mchp_corespi_disable(spi);
406}
407
408/*
409 * Platform driver data structure
410 */
411
412#if defined(CONFIG_OF)
413static const struct of_device_id mchp_corespi_dt_ids[] = {
414 { .compatible = "microchip,corespi-rtl-v5" },
415 { /* sentinel */ }
416};
417MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
418#endif
419
420static struct platform_driver mchp_corespi_driver = {
421 .probe = mchp_corespi_probe,
422 .driver = {
423 .name = "microchip-corespi",
424 .of_match_table = of_match_ptr(mchp_corespi_dt_ids),
425 },
426 .remove = mchp_corespi_remove,
427};
428module_platform_driver(mchp_corespi_driver);
429MODULE_DESCRIPTION("Microchip CoreSPI controller driver");
430MODULE_AUTHOR("Prajna Rajendra Kumar <[email protected]>");
431MODULE_LICENSE("GPL");