aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/spi/spi_oc_tiny.txt12
-rw-r--r--drivers/spi/Kconfig7
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi_oc_tiny.c425
-rw-r--r--include/linux/spi/spi_oc_tiny.h20
5 files changed, 465 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/spi/spi_oc_tiny.txt b/Documentation/devicetree/bindings/spi/spi_oc_tiny.txt
new file mode 100644
index 000000000000..d95c0b367a04
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi_oc_tiny.txt
@@ -0,0 +1,12 @@
1OpenCores tiny SPI
2
3Required properties:
4- compatible : should be "opencores,tiny-spi-rtlsvn2".
5- gpios : should specify GPIOs used for chipselect.
6Optional properties:
7- clock-frequency : input clock frequency to the core.
8- baud-width: width, in bits, of the programmable divider used to scale
9 the input clock to SCLK.
10
11The clock-frequency and baud-width properties are needed only if the divider
12is programmable. They are not needed if the divider is fixed.
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index bb233a9cbad2..8faa57a58dd1 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -231,6 +231,13 @@ config SPI_FSL_ESPI
231 From MPC8536, 85xx platform uses the controller, and all P10xx, 231 From MPC8536, 85xx platform uses the controller, and all P10xx,
232 P20xx, P30xx,P40xx, P50xx uses this controller. 232 P20xx, P30xx,P40xx, P50xx uses this controller.
233 233
234config SPI_OC_TINY
235 tristate "OpenCores tiny SPI"
236 depends on GENERIC_GPIO
237 select SPI_BITBANG
238 help
239 This is the driver for OpenCores tiny SPI master controller.
240
234config SPI_OMAP_UWIRE 241config SPI_OMAP_UWIRE
235 tristate "OMAP1 MicroWire" 242 tristate "OMAP1 MicroWire"
236 depends on ARCH_OMAP1 243 depends on ARCH_OMAP1
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 86d1b5f9bbd9..c7e4c23c6f36 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_IMX) += spi_imx.o
27obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o 27obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
28obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o 28obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
29obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o 29obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o
30obj-$(CONFIG_SPI_OC_TINY) += spi_oc_tiny.o
30obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o 31obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
31obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o 32obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
32obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o 33obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o
diff --git a/drivers/spi/spi_oc_tiny.c b/drivers/spi/spi_oc_tiny.c
new file mode 100644
index 000000000000..f1bde66cea19
--- /dev/null
+++ b/drivers/spi/spi_oc_tiny.c
@@ -0,0 +1,425 @@
1/*
2 * OpenCores tiny SPI master driver
3 *
4 * http://opencores.org/project,tiny_spi
5 *
6 * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
7 *
8 * Based on spi_s3c24xx.c, which is:
9 * Copyright (c) 2006 Ben Dooks
10 * Copyright (c) 2006 Simtec Electronics
11 * Ben Dooks <ben@simtec.co.uk>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 */
17
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/errno.h>
21#include <linux/platform_device.h>
22#include <linux/spi/spi.h>
23#include <linux/spi/spi_bitbang.h>
24#include <linux/spi/spi_oc_tiny.h>
25#include <linux/io.h>
26#include <linux/gpio.h>
27#include <linux/of.h>
28
29#define DRV_NAME "spi_oc_tiny"
30
31#define TINY_SPI_RXDATA 0
32#define TINY_SPI_TXDATA 4
33#define TINY_SPI_STATUS 8
34#define TINY_SPI_CONTROL 12
35#define TINY_SPI_BAUD 16
36
37#define TINY_SPI_STATUS_TXE 0x1
38#define TINY_SPI_STATUS_TXR 0x2
39
40struct tiny_spi {
41 /* bitbang has to be first */
42 struct spi_bitbang bitbang;
43 struct completion done;
44
45 void __iomem *base;
46 int irq;
47 unsigned int freq;
48 unsigned int baudwidth;
49 unsigned int baud;
50 unsigned int speed_hz;
51 unsigned int mode;
52 unsigned int len;
53 unsigned int txc, rxc;
54 const u8 *txp;
55 u8 *rxp;
56 unsigned int gpio_cs_count;
57 int *gpio_cs;
58};
59
60static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev)
61{
62 return spi_master_get_devdata(sdev->master);
63}
64
65static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz)
66{
67 struct tiny_spi *hw = tiny_spi_to_hw(spi);
68
69 return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1;
70}
71
72static void tiny_spi_chipselect(struct spi_device *spi, int is_active)
73{
74 struct tiny_spi *hw = tiny_spi_to_hw(spi);
75
76 if (hw->gpio_cs_count) {
77 gpio_set_value(hw->gpio_cs[spi->chip_select],
78 (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
79 }
80}
81
82static int tiny_spi_setup_transfer(struct spi_device *spi,
83 struct spi_transfer *t)
84{
85 struct tiny_spi *hw = tiny_spi_to_hw(spi);
86 unsigned int baud = hw->baud;
87
88 if (t) {
89 if (t->speed_hz && t->speed_hz != hw->speed_hz)
90 baud = tiny_spi_baud(spi, t->speed_hz);
91 }
92 writel(baud, hw->base + TINY_SPI_BAUD);
93 writel(hw->mode, hw->base + TINY_SPI_CONTROL);
94 return 0;
95}
96
97static int tiny_spi_setup(struct spi_device *spi)
98{
99 struct tiny_spi *hw = tiny_spi_to_hw(spi);
100
101 if (spi->max_speed_hz != hw->speed_hz) {
102 hw->speed_hz = spi->max_speed_hz;
103 hw->baud = tiny_spi_baud(spi, hw->speed_hz);
104 }
105 hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA);
106 return 0;
107}
108
109static inline void tiny_spi_wait_txr(struct tiny_spi *hw)
110{
111 while (!(readb(hw->base + TINY_SPI_STATUS) &
112 TINY_SPI_STATUS_TXR))
113 cpu_relax();
114}
115
116static inline void tiny_spi_wait_txe(struct tiny_spi *hw)
117{
118 while (!(readb(hw->base + TINY_SPI_STATUS) &
119 TINY_SPI_STATUS_TXE))
120 cpu_relax();
121}
122
123static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
124{
125 struct tiny_spi *hw = tiny_spi_to_hw(spi);
126 const u8 *txp = t->tx_buf;
127 u8 *rxp = t->rx_buf;
128 unsigned int i;
129
130 if (hw->irq >= 0) {
131 /* use intrrupt driven data transfer */
132 hw->len = t->len;
133 hw->txp = t->tx_buf;
134 hw->rxp = t->rx_buf;
135 hw->txc = 0;
136 hw->rxc = 0;
137
138 /* send the first byte */
139 if (t->len > 1) {
140 writeb(hw->txp ? *hw->txp++ : 0,
141 hw->base + TINY_SPI_TXDATA);
142 hw->txc++;
143 writeb(hw->txp ? *hw->txp++ : 0,
144 hw->base + TINY_SPI_TXDATA);
145 hw->txc++;
146 writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS);
147 } else {
148 writeb(hw->txp ? *hw->txp++ : 0,
149 hw->base + TINY_SPI_TXDATA);
150 hw->txc++;
151 writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS);
152 }
153
154 wait_for_completion(&hw->done);
155 } else if (txp && rxp) {
156 /* we need to tighten the transfer loop */
157 writeb(*txp++, hw->base + TINY_SPI_TXDATA);
158 if (t->len > 1) {
159 writeb(*txp++, hw->base + TINY_SPI_TXDATA);
160 for (i = 2; i < t->len; i++) {
161 u8 rx, tx = *txp++;
162 tiny_spi_wait_txr(hw);
163 rx = readb(hw->base + TINY_SPI_TXDATA);
164 writeb(tx, hw->base + TINY_SPI_TXDATA);
165 *rxp++ = rx;
166 }
167 tiny_spi_wait_txr(hw);
168 *rxp++ = readb(hw->base + TINY_SPI_TXDATA);
169 }
170 tiny_spi_wait_txe(hw);
171 *rxp++ = readb(hw->base + TINY_SPI_RXDATA);
172 } else if (rxp) {
173 writeb(0, hw->base + TINY_SPI_TXDATA);
174 if (t->len > 1) {
175 writeb(0,
176 hw->base + TINY_SPI_TXDATA);
177 for (i = 2; i < t->len; i++) {
178 u8 rx;
179 tiny_spi_wait_txr(hw);
180 rx = readb(hw->base + TINY_SPI_TXDATA);
181 writeb(0, hw->base + TINY_SPI_TXDATA);
182 *rxp++ = rx;
183 }
184 tiny_spi_wait_txr(hw);
185 *rxp++ = readb(hw->base + TINY_SPI_TXDATA);
186 }
187 tiny_spi_wait_txe(hw);
188 *rxp++ = readb(hw->base + TINY_SPI_RXDATA);
189 } else if (txp) {
190 writeb(*txp++, hw->base + TINY_SPI_TXDATA);
191 if (t->len > 1) {
192 writeb(*txp++, hw->base + TINY_SPI_TXDATA);
193 for (i = 2; i < t->len; i++) {
194 u8 tx = *txp++;
195 tiny_spi_wait_txr(hw);
196 writeb(tx, hw->base + TINY_SPI_TXDATA);
197 }
198 }
199 tiny_spi_wait_txe(hw);
200 } else {
201 writeb(0, hw->base + TINY_SPI_TXDATA);
202 if (t->len > 1) {
203 writeb(0, hw->base + TINY_SPI_TXDATA);
204 for (i = 2; i < t->len; i++) {
205 tiny_spi_wait_txr(hw);
206 writeb(0, hw->base + TINY_SPI_TXDATA);
207 }
208 }
209 tiny_spi_wait_txe(hw);
210 }
211 return t->len;
212}
213
214static irqreturn_t tiny_spi_irq(int irq, void *dev)
215{
216 struct tiny_spi *hw = dev;
217
218 writeb(0, hw->base + TINY_SPI_STATUS);
219 if (hw->rxc + 1 == hw->len) {
220 if (hw->rxp)
221 *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
222 hw->rxc++;
223 complete(&hw->done);
224 } else {
225 if (hw->rxp)
226 *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
227 hw->rxc++;
228 if (hw->txc < hw->len) {
229 writeb(hw->txp ? *hw->txp++ : 0,
230 hw->base + TINY_SPI_TXDATA);
231 hw->txc++;
232 writeb(TINY_SPI_STATUS_TXR,
233 hw->base + TINY_SPI_STATUS);
234 } else {
235 writeb(TINY_SPI_STATUS_TXE,
236 hw->base + TINY_SPI_STATUS);
237 }
238 }
239 return IRQ_HANDLED;
240}
241
242#ifdef CONFIG_OF
243#include <linux/of_gpio.h>
244
245static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
246{
247 struct tiny_spi *hw = platform_get_drvdata(pdev);
248 struct device_node *np = pdev->dev.of_node;
249 unsigned int i;
250 const __be32 *val;
251 int len;
252
253 if (!np)
254 return 0;
255 hw->gpio_cs_count = of_gpio_count(np);
256 if (hw->gpio_cs_count) {
257 hw->gpio_cs = devm_kzalloc(&pdev->dev,
258 hw->gpio_cs_count * sizeof(unsigned int),
259 GFP_KERNEL);
260 if (!hw->gpio_cs)
261 return -ENOMEM;
262 }
263 for (i = 0; i < hw->gpio_cs_count; i++) {
264 hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL);
265 if (hw->gpio_cs[i] < 0)
266 return -ENODEV;
267 }
268 hw->bitbang.master->dev.of_node = pdev->dev.of_node;
269 val = of_get_property(pdev->dev.of_node,
270 "clock-frequency", &len);
271 if (val && len >= sizeof(__be32))
272 hw->freq = be32_to_cpup(val);
273 val = of_get_property(pdev->dev.of_node, "baud-width", &len);
274 if (val && len >= sizeof(__be32))
275 hw->baudwidth = be32_to_cpup(val);
276 return 0;
277}
278#else /* !CONFIG_OF */
279static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
280{
281 return 0;
282}
283#endif /* CONFIG_OF */
284
285static int __devinit tiny_spi_probe(struct platform_device *pdev)
286{
287 struct tiny_spi_platform_data *platp = pdev->dev.platform_data;
288 struct tiny_spi *hw;
289 struct spi_master *master;
290 struct resource *res;
291 unsigned int i;
292 int err = -ENODEV;
293
294 master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
295 if (!master)
296 return err;
297
298 /* setup the master state. */
299 master->bus_num = pdev->id;
300 master->num_chipselect = 255;
301 master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
302 master->setup = tiny_spi_setup;
303
304 hw = spi_master_get_devdata(master);
305 platform_set_drvdata(pdev, hw);
306
307 /* setup the state for the bitbang driver */
308 hw->bitbang.master = spi_master_get(master);
309 if (!hw->bitbang.master)
310 return err;
311 hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
312 hw->bitbang.chipselect = tiny_spi_chipselect;
313 hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
314
315 /* find and map our resources */
316 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
317 if (!res)
318 goto exit_busy;
319 if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
320 pdev->name))
321 goto exit_busy;
322 hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
323 resource_size(res));
324 if (!hw->base)
325 goto exit_busy;
326 /* irq is optional */
327 hw->irq = platform_get_irq(pdev, 0);
328 if (hw->irq >= 0) {
329 init_completion(&hw->done);
330 err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
331 pdev->name, hw);
332 if (err)
333 goto exit;
334 }
335 /* find platform data */
336 if (platp) {
337 hw->gpio_cs_count = platp->gpio_cs_count;
338 hw->gpio_cs = platp->gpio_cs;
339 if (platp->gpio_cs_count && !platp->gpio_cs)
340 goto exit_busy;
341 hw->freq = platp->freq;
342 hw->baudwidth = platp->baudwidth;
343 } else {
344 err = tiny_spi_of_probe(pdev);
345 if (err)
346 goto exit;
347 }
348 for (i = 0; i < hw->gpio_cs_count; i++) {
349 err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev));
350 if (err)
351 goto exit_gpio;
352 gpio_direction_output(hw->gpio_cs[i], 1);
353 }
354 hw->bitbang.master->num_chipselect = max(1U, hw->gpio_cs_count);
355
356 /* register our spi controller */
357 err = spi_bitbang_start(&hw->bitbang);
358 if (err)
359 goto exit;
360 dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
361
362 return 0;
363
364exit_gpio:
365 while (i-- > 0)
366 gpio_free(hw->gpio_cs[i]);
367exit_busy:
368 err = -EBUSY;
369exit:
370 platform_set_drvdata(pdev, NULL);
371 spi_master_put(master);
372 return err;
373}
374
375static int __devexit tiny_spi_remove(struct platform_device *pdev)
376{
377 struct tiny_spi *hw = platform_get_drvdata(pdev);
378 struct spi_master *master = hw->bitbang.master;
379 unsigned int i;
380
381 spi_bitbang_stop(&hw->bitbang);
382 for (i = 0; i < hw->gpio_cs_count; i++)
383 gpio_free(hw->gpio_cs[i]);
384 platform_set_drvdata(pdev, NULL);
385 spi_master_put(master);
386 return 0;
387}
388
389#ifdef CONFIG_OF
390static const struct of_device_id tiny_spi_match[] = {
391 { .compatible = "opencores,tiny-spi-rtlsvn2", },
392 {},
393};
394MODULE_DEVICE_TABLE(of, tiny_spi_match);
395#else /* CONFIG_OF */
396#define tiny_spi_match NULL
397#endif /* CONFIG_OF */
398
399static struct platform_driver tiny_spi_driver = {
400 .probe = tiny_spi_probe,
401 .remove = __devexit_p(tiny_spi_remove),
402 .driver = {
403 .name = DRV_NAME,
404 .owner = THIS_MODULE,
405 .pm = NULL,
406 .of_match_table = tiny_spi_match,
407 },
408};
409
410static int __init tiny_spi_init(void)
411{
412 return platform_driver_register(&tiny_spi_driver);
413}
414module_init(tiny_spi_init);
415
416static void __exit tiny_spi_exit(void)
417{
418 platform_driver_unregister(&tiny_spi_driver);
419}
420module_exit(tiny_spi_exit);
421
422MODULE_DESCRIPTION("OpenCores tiny SPI driver");
423MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
424MODULE_LICENSE("GPL");
425MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/include/linux/spi/spi_oc_tiny.h b/include/linux/spi/spi_oc_tiny.h
new file mode 100644
index 000000000000..1ac529cf4f06
--- /dev/null
+++ b/include/linux/spi/spi_oc_tiny.h
@@ -0,0 +1,20 @@
1#ifndef _LINUX_SPI_SPI_OC_TINY_H
2#define _LINUX_SPI_SPI_OC_TINY_H
3
4/**
5 * struct tiny_spi_platform_data - platform data of the OpenCores tiny SPI
6 * @freq: input clock freq to the core.
7 * @baudwidth: baud rate divider width of the core.
8 * @gpio_cs_count: number of gpio pins used for chipselect.
9 * @gpio_cs: array of gpio pins used for chipselect.
10 *
11 * freq and baudwidth are used only if the divider is programmable.
12 */
13struct tiny_spi_platform_data {
14 unsigned int freq;
15 unsigned int baudwidth;
16 unsigned int gpio_cs_count;
17 int *gpio_cs;
18};
19
20#endif /* _LINUX_SPI_SPI_OC_TINY_H */