xref: /openbmc/linux/drivers/spi/spi-oc-tiny.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ca632f55SGrant Likely /*
3ca632f55SGrant Likely  * OpenCores tiny SPI master driver
4ca632f55SGrant Likely  *
53ea4eac3SAlexander A. Klimov  * https://opencores.org/project,tiny_spi
6ca632f55SGrant Likely  *
7ca632f55SGrant Likely  * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
8ca632f55SGrant Likely  *
9ca632f55SGrant Likely  * Based on spi_s3c24xx.c, which is:
10ca632f55SGrant Likely  * Copyright (c) 2006 Ben Dooks
11ca632f55SGrant Likely  * Copyright (c) 2006 Simtec Electronics
12ca632f55SGrant Likely  *	Ben Dooks <ben@simtec.co.uk>
13ca632f55SGrant Likely  */
14ca632f55SGrant Likely 
15ca632f55SGrant Likely #include <linux/interrupt.h>
16ca632f55SGrant Likely #include <linux/errno.h>
17d7614de4SPaul Gortmaker #include <linux/module.h>
18ca632f55SGrant Likely #include <linux/platform_device.h>
19ca632f55SGrant Likely #include <linux/spi/spi.h>
20ca632f55SGrant Likely #include <linux/spi/spi_bitbang.h>
21ca632f55SGrant Likely #include <linux/spi/spi_oc_tiny.h>
22ca632f55SGrant Likely #include <linux/io.h>
23ca632f55SGrant Likely #include <linux/of.h>
24ca632f55SGrant Likely 
25ca632f55SGrant Likely #define DRV_NAME "spi_oc_tiny"
26ca632f55SGrant Likely 
27ca632f55SGrant Likely #define TINY_SPI_RXDATA 0
28ca632f55SGrant Likely #define TINY_SPI_TXDATA 4
29ca632f55SGrant Likely #define TINY_SPI_STATUS 8
30ca632f55SGrant Likely #define TINY_SPI_CONTROL 12
31ca632f55SGrant Likely #define TINY_SPI_BAUD 16
32ca632f55SGrant Likely 
33ca632f55SGrant Likely #define TINY_SPI_STATUS_TXE 0x1
34ca632f55SGrant Likely #define TINY_SPI_STATUS_TXR 0x2
35ca632f55SGrant Likely 
36ca632f55SGrant Likely struct tiny_spi {
37ca632f55SGrant Likely 	/* bitbang has to be first */
38ca632f55SGrant Likely 	struct spi_bitbang bitbang;
39ca632f55SGrant Likely 	struct completion done;
40ca632f55SGrant Likely 
41ca632f55SGrant Likely 	void __iomem *base;
42ca632f55SGrant Likely 	int irq;
43ca632f55SGrant Likely 	unsigned int freq;
44ca632f55SGrant Likely 	unsigned int baudwidth;
45ca632f55SGrant Likely 	unsigned int baud;
46ca632f55SGrant Likely 	unsigned int speed_hz;
47ca632f55SGrant Likely 	unsigned int mode;
48ca632f55SGrant Likely 	unsigned int len;
49ca632f55SGrant Likely 	unsigned int txc, rxc;
50ca632f55SGrant Likely 	const u8 *txp;
51ca632f55SGrant Likely 	u8 *rxp;
52ca632f55SGrant Likely };
53ca632f55SGrant Likely 
tiny_spi_to_hw(struct spi_device * sdev)54ca632f55SGrant Likely static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev)
55ca632f55SGrant Likely {
56ca632f55SGrant Likely 	return spi_master_get_devdata(sdev->master);
57ca632f55SGrant Likely }
58ca632f55SGrant Likely 
tiny_spi_baud(struct spi_device * spi,unsigned int hz)59ca632f55SGrant Likely static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz)
60ca632f55SGrant Likely {
61ca632f55SGrant Likely 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
62ca632f55SGrant Likely 
63ca632f55SGrant Likely 	return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1;
64ca632f55SGrant Likely }
65ca632f55SGrant Likely 
tiny_spi_setup_transfer(struct spi_device * spi,struct spi_transfer * t)66ca632f55SGrant Likely static int tiny_spi_setup_transfer(struct spi_device *spi,
67ca632f55SGrant Likely 				   struct spi_transfer *t)
68ca632f55SGrant Likely {
69ca632f55SGrant Likely 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
70ca632f55SGrant Likely 	unsigned int baud = hw->baud;
71ca632f55SGrant Likely 
72ca632f55SGrant Likely 	if (t) {
73ca632f55SGrant Likely 		if (t->speed_hz && t->speed_hz != hw->speed_hz)
74ca632f55SGrant Likely 			baud = tiny_spi_baud(spi, t->speed_hz);
75ca632f55SGrant Likely 	}
76ca632f55SGrant Likely 	writel(baud, hw->base + TINY_SPI_BAUD);
77ca632f55SGrant Likely 	writel(hw->mode, hw->base + TINY_SPI_CONTROL);
78ca632f55SGrant Likely 	return 0;
79ca632f55SGrant Likely }
80ca632f55SGrant Likely 
tiny_spi_setup(struct spi_device * spi)81ca632f55SGrant Likely static int tiny_spi_setup(struct spi_device *spi)
82ca632f55SGrant Likely {
83ca632f55SGrant Likely 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
84ca632f55SGrant Likely 
85ca632f55SGrant Likely 	if (spi->max_speed_hz != hw->speed_hz) {
86ca632f55SGrant Likely 		hw->speed_hz = spi->max_speed_hz;
87ca632f55SGrant Likely 		hw->baud = tiny_spi_baud(spi, hw->speed_hz);
88ca632f55SGrant Likely 	}
89a2f2db6bSAndy Shevchenko 	hw->mode = spi->mode & SPI_MODE_X_MASK;
90ca632f55SGrant Likely 	return 0;
91ca632f55SGrant Likely }
92ca632f55SGrant Likely 
tiny_spi_wait_txr(struct tiny_spi * hw)93ca632f55SGrant Likely static inline void tiny_spi_wait_txr(struct tiny_spi *hw)
94ca632f55SGrant Likely {
95ca632f55SGrant Likely 	while (!(readb(hw->base + TINY_SPI_STATUS) &
96ca632f55SGrant Likely 		 TINY_SPI_STATUS_TXR))
97ca632f55SGrant Likely 		cpu_relax();
98ca632f55SGrant Likely }
99ca632f55SGrant Likely 
tiny_spi_wait_txe(struct tiny_spi * hw)100ca632f55SGrant Likely static inline void tiny_spi_wait_txe(struct tiny_spi *hw)
101ca632f55SGrant Likely {
102ca632f55SGrant Likely 	while (!(readb(hw->base + TINY_SPI_STATUS) &
103ca632f55SGrant Likely 		 TINY_SPI_STATUS_TXE))
104ca632f55SGrant Likely 		cpu_relax();
105ca632f55SGrant Likely }
106ca632f55SGrant Likely 
tiny_spi_txrx_bufs(struct spi_device * spi,struct spi_transfer * t)107ca632f55SGrant Likely static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
108ca632f55SGrant Likely {
109ca632f55SGrant Likely 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
110ca632f55SGrant Likely 	const u8 *txp = t->tx_buf;
111ca632f55SGrant Likely 	u8 *rxp = t->rx_buf;
112ca632f55SGrant Likely 	unsigned int i;
113ca632f55SGrant Likely 
114ca632f55SGrant Likely 	if (hw->irq >= 0) {
115886db6acSMasanari Iida 		/* use interrupt driven data transfer */
116ca632f55SGrant Likely 		hw->len = t->len;
117ca632f55SGrant Likely 		hw->txp = t->tx_buf;
118ca632f55SGrant Likely 		hw->rxp = t->rx_buf;
119ca632f55SGrant Likely 		hw->txc = 0;
120ca632f55SGrant Likely 		hw->rxc = 0;
121ca632f55SGrant Likely 
122ca632f55SGrant Likely 		/* send the first byte */
123ca632f55SGrant Likely 		if (t->len > 1) {
124ca632f55SGrant Likely 			writeb(hw->txp ? *hw->txp++ : 0,
125ca632f55SGrant Likely 			       hw->base + TINY_SPI_TXDATA);
126ca632f55SGrant Likely 			hw->txc++;
127ca632f55SGrant Likely 			writeb(hw->txp ? *hw->txp++ : 0,
128ca632f55SGrant Likely 			       hw->base + TINY_SPI_TXDATA);
129ca632f55SGrant Likely 			hw->txc++;
130ca632f55SGrant Likely 			writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS);
131ca632f55SGrant Likely 		} else {
132ca632f55SGrant Likely 			writeb(hw->txp ? *hw->txp++ : 0,
133ca632f55SGrant Likely 			       hw->base + TINY_SPI_TXDATA);
134ca632f55SGrant Likely 			hw->txc++;
135ca632f55SGrant Likely 			writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS);
136ca632f55SGrant Likely 		}
137ca632f55SGrant Likely 
138ca632f55SGrant Likely 		wait_for_completion(&hw->done);
139ca632f55SGrant Likely 	} else {
140e826a7ffSAxel Lin 		/* we need to tighten the transfer loop */
141e826a7ffSAxel Lin 		writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA);
142e826a7ffSAxel Lin 		for (i = 1; i < t->len; i++) {
143e826a7ffSAxel Lin 			writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA);
144e826a7ffSAxel Lin 
145e826a7ffSAxel Lin 			if (rxp || (i != t->len - 1))
146ca632f55SGrant Likely 				tiny_spi_wait_txr(hw);
147e826a7ffSAxel Lin 			if (rxp)
148e826a7ffSAxel Lin 				*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
149ca632f55SGrant Likely 		}
150ca632f55SGrant Likely 		tiny_spi_wait_txe(hw);
151e826a7ffSAxel Lin 		if (rxp)
152e826a7ffSAxel Lin 			*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
153ca632f55SGrant Likely 	}
154e826a7ffSAxel Lin 
155ca632f55SGrant Likely 	return t->len;
156ca632f55SGrant Likely }
157ca632f55SGrant Likely 
tiny_spi_irq(int irq,void * dev)158ca632f55SGrant Likely static irqreturn_t tiny_spi_irq(int irq, void *dev)
159ca632f55SGrant Likely {
160ca632f55SGrant Likely 	struct tiny_spi *hw = dev;
161ca632f55SGrant Likely 
162ca632f55SGrant Likely 	writeb(0, hw->base + TINY_SPI_STATUS);
163ca632f55SGrant Likely 	if (hw->rxc + 1 == hw->len) {
164ca632f55SGrant Likely 		if (hw->rxp)
165ca632f55SGrant Likely 			*hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
166ca632f55SGrant Likely 		hw->rxc++;
167ca632f55SGrant Likely 		complete(&hw->done);
168ca632f55SGrant Likely 	} else {
169ca632f55SGrant Likely 		if (hw->rxp)
170ca632f55SGrant Likely 			*hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
171ca632f55SGrant Likely 		hw->rxc++;
172ca632f55SGrant Likely 		if (hw->txc < hw->len) {
173ca632f55SGrant Likely 			writeb(hw->txp ? *hw->txp++ : 0,
174ca632f55SGrant Likely 			       hw->base + TINY_SPI_TXDATA);
175ca632f55SGrant Likely 			hw->txc++;
176ca632f55SGrant Likely 			writeb(TINY_SPI_STATUS_TXR,
177ca632f55SGrant Likely 			       hw->base + TINY_SPI_STATUS);
178ca632f55SGrant Likely 		} else {
179ca632f55SGrant Likely 			writeb(TINY_SPI_STATUS_TXE,
180ca632f55SGrant Likely 			       hw->base + TINY_SPI_STATUS);
181ca632f55SGrant Likely 		}
182ca632f55SGrant Likely 	}
183ca632f55SGrant Likely 	return IRQ_HANDLED;
184ca632f55SGrant Likely }
185ca632f55SGrant Likely 
186ca632f55SGrant Likely #ifdef CONFIG_OF
187ca632f55SGrant Likely #include <linux/of_gpio.h>
188ca632f55SGrant Likely 
tiny_spi_of_probe(struct platform_device * pdev)189fd4a319bSGrant Likely static int tiny_spi_of_probe(struct platform_device *pdev)
190ca632f55SGrant Likely {
191ca632f55SGrant Likely 	struct tiny_spi *hw = platform_get_drvdata(pdev);
192ca632f55SGrant Likely 	struct device_node *np = pdev->dev.of_node;
19345a3e771STobias Klauser 	u32 val;
194ca632f55SGrant Likely 
195ca632f55SGrant Likely 	if (!np)
196ca632f55SGrant Likely 		return 0;
197ca632f55SGrant Likely 	hw->bitbang.master->dev.of_node = pdev->dev.of_node;
19845a3e771STobias Klauser 	if (!of_property_read_u32(np, "clock-frequency", &val))
19945a3e771STobias Klauser 		hw->freq = val;
20045a3e771STobias Klauser 	if (!of_property_read_u32(np, "baud-width", &val))
20145a3e771STobias Klauser 		hw->baudwidth = val;
202ca632f55SGrant Likely 	return 0;
203ca632f55SGrant Likely }
204ca632f55SGrant Likely #else /* !CONFIG_OF */
tiny_spi_of_probe(struct platform_device * pdev)205fd4a319bSGrant Likely static int tiny_spi_of_probe(struct platform_device *pdev)
206ca632f55SGrant Likely {
207ca632f55SGrant Likely 	return 0;
208ca632f55SGrant Likely }
209ca632f55SGrant Likely #endif /* CONFIG_OF */
210ca632f55SGrant Likely 
tiny_spi_probe(struct platform_device * pdev)211fd4a319bSGrant Likely static int tiny_spi_probe(struct platform_device *pdev)
212ca632f55SGrant Likely {
2138074cf06SJingoo Han 	struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
214ca632f55SGrant Likely 	struct tiny_spi *hw;
215ca632f55SGrant Likely 	struct spi_master *master;
216ca632f55SGrant Likely 	int err = -ENODEV;
217ca632f55SGrant Likely 
218ca632f55SGrant Likely 	master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
219ca632f55SGrant Likely 	if (!master)
220ca632f55SGrant Likely 		return err;
221ca632f55SGrant Likely 
222ca632f55SGrant Likely 	/* setup the master state. */
223ca632f55SGrant Likely 	master->bus_num = pdev->id;
224ca632f55SGrant Likely 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
225ca632f55SGrant Likely 	master->setup = tiny_spi_setup;
226f03ee204SLinus Walleij 	master->use_gpio_descriptors = true;
227ca632f55SGrant Likely 
228ca632f55SGrant Likely 	hw = spi_master_get_devdata(master);
229ca632f55SGrant Likely 	platform_set_drvdata(pdev, hw);
230ca632f55SGrant Likely 
231ca632f55SGrant Likely 	/* setup the state for the bitbang driver */
23294c69f76SAxel Lin 	hw->bitbang.master = master;
233ca632f55SGrant Likely 	hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
234ca632f55SGrant Likely 	hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
235ca632f55SGrant Likely 
236ca632f55SGrant Likely 	/* find and map our resources */
237f601a654SYueHaibing 	hw->base = devm_platform_ioremap_resource(pdev, 0);
238b3136f8fSJulia Lawall 	if (IS_ERR(hw->base)) {
239b3136f8fSJulia Lawall 		err = PTR_ERR(hw->base);
240b3136f8fSJulia Lawall 		goto exit;
241b3136f8fSJulia Lawall 	}
242ca632f55SGrant Likely 	/* irq is optional */
243ca632f55SGrant Likely 	hw->irq = platform_get_irq(pdev, 0);
244ca632f55SGrant Likely 	if (hw->irq >= 0) {
245ca632f55SGrant Likely 		init_completion(&hw->done);
246ca632f55SGrant Likely 		err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
247ca632f55SGrant Likely 				       pdev->name, hw);
248ca632f55SGrant Likely 		if (err)
249ca632f55SGrant Likely 			goto exit;
250ca632f55SGrant Likely 	}
251ca632f55SGrant Likely 	/* find platform data */
252ca632f55SGrant Likely 	if (platp) {
253ca632f55SGrant Likely 		hw->freq = platp->freq;
254ca632f55SGrant Likely 		hw->baudwidth = platp->baudwidth;
255ca632f55SGrant Likely 	} else {
256ca632f55SGrant Likely 		err = tiny_spi_of_probe(pdev);
257ca632f55SGrant Likely 		if (err)
258ca632f55SGrant Likely 			goto exit;
259ca632f55SGrant Likely 	}
260ca632f55SGrant Likely 
261ca632f55SGrant Likely 	/* register our spi controller */
262ca632f55SGrant Likely 	err = spi_bitbang_start(&hw->bitbang);
263ca632f55SGrant Likely 	if (err)
264ca632f55SGrant Likely 		goto exit;
265ca632f55SGrant Likely 	dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
266ca632f55SGrant Likely 
267ca632f55SGrant Likely 	return 0;
268ca632f55SGrant Likely 
269ca632f55SGrant Likely exit:
270ca632f55SGrant Likely 	spi_master_put(master);
271ca632f55SGrant Likely 	return err;
272ca632f55SGrant Likely }
273ca632f55SGrant Likely 
tiny_spi_remove(struct platform_device * pdev)274*bdffa602SUwe Kleine-König static void tiny_spi_remove(struct platform_device *pdev)
275ca632f55SGrant Likely {
276ca632f55SGrant Likely 	struct tiny_spi *hw = platform_get_drvdata(pdev);
277ca632f55SGrant Likely 	struct spi_master *master = hw->bitbang.master;
278ca632f55SGrant Likely 
279ca632f55SGrant Likely 	spi_bitbang_stop(&hw->bitbang);
280ca632f55SGrant Likely 	spi_master_put(master);
281ca632f55SGrant Likely }
282ca632f55SGrant Likely 
283ca632f55SGrant Likely #ifdef CONFIG_OF
284ca632f55SGrant Likely static const struct of_device_id tiny_spi_match[] = {
285ca632f55SGrant Likely 	{ .compatible = "opencores,tiny-spi-rtlsvn2", },
286ca632f55SGrant Likely 	{},
287ca632f55SGrant Likely };
288ca632f55SGrant Likely MODULE_DEVICE_TABLE(of, tiny_spi_match);
289ca632f55SGrant Likely #endif /* CONFIG_OF */
290ca632f55SGrant Likely 
291ca632f55SGrant Likely static struct platform_driver tiny_spi_driver = {
292ca632f55SGrant Likely 	.probe = tiny_spi_probe,
293*bdffa602SUwe Kleine-König 	.remove_new = tiny_spi_remove,
294ca632f55SGrant Likely 	.driver = {
295ca632f55SGrant Likely 		.name = DRV_NAME,
296ca632f55SGrant Likely 		.pm = NULL,
2979547acceSSachin Kamat 		.of_match_table = of_match_ptr(tiny_spi_match),
298ca632f55SGrant Likely 	},
299ca632f55SGrant Likely };
300940ab889SGrant Likely module_platform_driver(tiny_spi_driver);
301ca632f55SGrant Likely 
302ca632f55SGrant Likely MODULE_DESCRIPTION("OpenCores tiny SPI driver");
303ca632f55SGrant Likely MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
304ca632f55SGrant Likely MODULE_LICENSE("GPL");
305ca632f55SGrant Likely MODULE_ALIAS("platform:" DRV_NAME);
306