File Index
Files and directories
Sign in
FILE

spi-microchip-core-spi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/spi/spi-microchip-core-spi.c C
  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
 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	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
398static 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)
412static const struct of_device_id mchp_corespi_dt_ids[] = {
413	{ .compatible = "microchip,corespi-rtl-v5" },
414	{ /* sentinel */ }
415};
416MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
417#endif
418
419static 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};
427module_platform_driver(mchp_corespi_driver);
428MODULE_DESCRIPTION("Microchip CoreSPI controller driver");
429MODULE_AUTHOR("Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>");
430MODULE_LICENSE("GPL");
~/ Read-only file preview.