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