aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWan ZongShun <mcuos.com@gmail.com>2009-12-01 09:29:20 -0500
committerGrant Likely <grant.likely@secretlab.ca>2009-12-13 02:58:00 -0500
commit30eaed053c9bced7a23624e4bab5602e5b85124f (patch)
tree4d3b923a7281b4d12d251399b775eb0584bdafe9
parent8051effcbced8478119167b93b0e9554cb82d28e (diff)
ARM: NUC900: Add spi driver support for nuc900
Signed-off-by: Wan ZongShun <mcuos.com@gmail.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
-rw-r--r--arch/arm/mach-w90x900/include/mach/nuc900_spi.h35
-rw-r--r--drivers/spi/Kconfig7
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi_nuc900.c504
4 files changed, 547 insertions, 0 deletions
diff --git a/arch/arm/mach-w90x900/include/mach/nuc900_spi.h b/arch/arm/mach-w90x900/include/mach/nuc900_spi.h
new file mode 100644
index 000000000000..bd94819e314f
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/nuc900_spi.h
@@ -0,0 +1,35 @@
1/*
2 * arch/arm/mach-w90x900/include/mach/nuc900_spi.h
3 *
4 * Copyright (c) 2009 Nuvoton technology corporation.
5 *
6 * Wan ZongShun <mcuos.com@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation;version 2 of the License.
11 *
12 */
13
14#ifndef __ASM_ARCH_SPI_H
15#define __ASM_ARCH_SPI_H
16
17extern void mfp_set_groupg(struct device *dev);
18
19struct nuc900_spi_info {
20 unsigned int num_cs;
21 unsigned int lsb;
22 unsigned int txneg;
23 unsigned int rxneg;
24 unsigned int divider;
25 unsigned int sleep;
26 unsigned int txnum;
27 unsigned int txbitlen;
28 int bus_num;
29};
30
31struct nuc900_spi_chip {
32 unsigned char bits_per_word;
33};
34
35#endif /* __ASM_ARCH_SPI_H */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 06e1c0c9d35e..b9908e9a9954 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -275,6 +275,13 @@ config SPI_XILINX_PLTFM
275 This is the platform driver for the SPI controller IP 275 This is the platform driver for the SPI controller IP
276 from the Xilinx EDK. 276 from the Xilinx EDK.
277 277
278config SPI_NUC900
279 tristate "Nuvoton NUC900 series SPI"
280 depends on ARCH_W90X900 && EXPERIMENTAL
281 select SPI_BITBANG
282 help
283 SPI driver for Nuvoton NUC900 series ARM SoCs
284
278# 285#
279# Add new SPI master controllers in alphabetical order above this line 286# Add new SPI master controllers in alphabetical order above this line
280# 287#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9c51e2526892..be6e8a50468e 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.o
37obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o 37obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
38obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o 38obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
39obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o 39obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
40obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o
40# ... add above this line ... 41# ... add above this line ...
41 42
42# SPI protocol drivers (device/link on bus) 43# SPI protocol drivers (device/link on bus)
diff --git a/drivers/spi/spi_nuc900.c b/drivers/spi/spi_nuc900.c
new file mode 100644
index 000000000000..b319f9bf9b9b
--- /dev/null
+++ b/drivers/spi/spi_nuc900.c
@@ -0,0 +1,504 @@
1/* linux/drivers/spi/spi_nuc900.c
2 *
3 * Copyright (c) 2009 Nuvoton technology.
4 * Wan ZongShun <mcuos.com@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10*/
11
12#include <linux/init.h>
13#include <linux/spinlock.h>
14#include <linux/workqueue.h>
15#include <linux/interrupt.h>
16#include <linux/delay.h>
17#include <linux/errno.h>
18#include <linux/err.h>
19#include <linux/clk.h>
20#include <linux/device.h>
21#include <linux/platform_device.h>
22#include <linux/gpio.h>
23#include <linux/io.h>
24
25#include <linux/spi/spi.h>
26#include <linux/spi/spi_bitbang.h>
27
28#include <mach/nuc900_spi.h>
29
30/* usi registers offset */
31#define USI_CNT 0x00
32#define USI_DIV 0x04
33#define USI_SSR 0x08
34#define USI_RX0 0x10
35#define USI_TX0 0x10
36
37/* usi register bit */
38#define ENINT (0x01 << 17)
39#define ENFLG (0x01 << 16)
40#define TXNUM (0x03 << 8)
41#define TXNEG (0x01 << 2)
42#define RXNEG (0x01 << 1)
43#define LSB (0x01 << 10)
44#define SELECTLEV (0x01 << 2)
45#define SELECTPOL (0x01 << 31)
46#define SELECTSLAVE 0x01
47#define GOBUSY 0x01
48
49struct nuc900_spi {
50 struct spi_bitbang bitbang;
51 struct completion done;
52 void __iomem *regs;
53 int irq;
54 int len;
55 int count;
56 const unsigned char *tx;
57 unsigned char *rx;
58 struct clk *clk;
59 struct resource *ioarea;
60 struct spi_master *master;
61 struct spi_device *curdev;
62 struct device *dev;
63 struct nuc900_spi_info *pdata;
64 spinlock_t lock;
65 struct resource *res;
66};
67
68static inline struct nuc900_spi *to_hw(struct spi_device *sdev)
69{
70 return spi_master_get_devdata(sdev->master);
71}
72
73static void nuc900_slave_select(struct spi_device *spi, unsigned int ssr)
74{
75 struct nuc900_spi *hw = to_hw(spi);
76 unsigned int val;
77 unsigned int cs = spi->mode & SPI_CS_HIGH ? 1 : 0;
78 unsigned int cpol = spi->mode & SPI_CPOL ? 1 : 0;
79 unsigned long flags;
80
81 spin_lock_irqsave(&hw->lock, flags);
82
83 val = __raw_readl(hw->regs + USI_SSR);
84
85 if (!cs)
86 val &= ~SELECTLEV;
87 else
88 val |= SELECTLEV;
89
90 if (!ssr)
91 val &= ~SELECTSLAVE;
92 else
93 val |= SELECTSLAVE;
94
95 __raw_writel(val, hw->regs + USI_SSR);
96
97 val = __raw_readl(hw->regs + USI_CNT);
98
99 if (!cpol)
100 val &= ~SELECTPOL;
101 else
102 val |= SELECTPOL;
103
104 __raw_writel(val, hw->regs + USI_CNT);
105
106 spin_unlock_irqrestore(&hw->lock, flags);
107}
108
109static void nuc900_spi_chipsel(struct spi_device *spi, int value)
110{
111 switch (value) {
112 case BITBANG_CS_INACTIVE:
113 nuc900_slave_select(spi, 0);
114 break;
115
116 case BITBANG_CS_ACTIVE:
117 nuc900_slave_select(spi, 1);
118 break;
119 }
120}
121
122static void nuc900_spi_setup_txnum(struct nuc900_spi *hw,
123 unsigned int txnum)
124{
125 unsigned int val;
126 unsigned long flags;
127
128 spin_lock_irqsave(&hw->lock, flags);
129
130 val = __raw_readl(hw->regs + USI_CNT);
131
132 if (!txnum)
133 val &= ~TXNUM;
134 else
135 val |= txnum << 0x08;
136
137 __raw_writel(val, hw->regs + USI_CNT);
138
139 spin_unlock_irqrestore(&hw->lock, flags);
140
141}
142
143static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw,
144 unsigned int txbitlen)
145{
146 unsigned int val;
147 unsigned long flags;
148
149 spin_lock_irqsave(&hw->lock, flags);
150
151 val = __raw_readl(hw->regs + USI_CNT);
152
153 val |= (txbitlen << 0x03);
154
155 __raw_writel(val, hw->regs + USI_CNT);
156
157 spin_unlock_irqrestore(&hw->lock, flags);
158}
159
160static void nuc900_spi_gobusy(struct nuc900_spi *hw)
161{
162 unsigned int val;
163 unsigned long flags;
164
165 spin_lock_irqsave(&hw->lock, flags);
166
167 val = __raw_readl(hw->regs + USI_CNT);
168
169 val |= GOBUSY;
170
171 __raw_writel(val, hw->regs + USI_CNT);
172
173 spin_unlock_irqrestore(&hw->lock, flags);
174}
175
176static int nuc900_spi_setupxfer(struct spi_device *spi,
177 struct spi_transfer *t)
178{
179 return 0;
180}
181
182static int nuc900_spi_setup(struct spi_device *spi)
183{
184 return 0;
185}
186
187static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count)
188{
189 return hw->tx ? hw->tx[count] : 0;
190}
191
192static int nuc900_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
193{
194 struct nuc900_spi *hw = to_hw(spi);
195
196 hw->tx = t->tx_buf;
197 hw->rx = t->rx_buf;
198 hw->len = t->len;
199 hw->count = 0;
200
201 __raw_writel(hw_txbyte(hw, 0x0), hw->regs + USI_TX0);
202
203 nuc900_spi_gobusy(hw);
204
205 wait_for_completion(&hw->done);
206
207 return hw->count;
208}
209
210static irqreturn_t nuc900_spi_irq(int irq, void *dev)
211{
212 struct nuc900_spi *hw = dev;
213 unsigned int status;
214 unsigned int count = hw->count;
215
216 status = __raw_readl(hw->regs + USI_CNT);
217 __raw_writel(status, hw->regs + USI_CNT);
218
219 if (status & ENFLG) {
220 hw->count++;
221
222 if (hw->rx)
223 hw->rx[count] = __raw_readl(hw->regs + USI_RX0);
224 count++;
225
226 if (count < hw->len) {
227 __raw_writel(hw_txbyte(hw, count), hw->regs + USI_TX0);
228 nuc900_spi_gobusy(hw);
229 } else {
230 complete(&hw->done);
231 }
232
233 return IRQ_HANDLED;
234 }
235
236 complete(&hw->done);
237 return IRQ_HANDLED;
238}
239
240static void nuc900_tx_edge(struct nuc900_spi *hw, unsigned int edge)
241{
242 unsigned int val;
243 unsigned long flags;
244
245 spin_lock_irqsave(&hw->lock, flags);
246
247 val = __raw_readl(hw->regs + USI_CNT);
248
249 if (edge)
250 val |= TXNEG;
251 else
252 val &= ~TXNEG;
253 __raw_writel(val, hw->regs + USI_CNT);
254
255 spin_unlock_irqrestore(&hw->lock, flags);
256}
257
258static void nuc900_rx_edge(struct nuc900_spi *hw, unsigned int edge)
259{
260 unsigned int val;
261 unsigned long flags;
262
263 spin_lock_irqsave(&hw->lock, flags);
264
265 val = __raw_readl(hw->regs + USI_CNT);
266
267 if (edge)
268 val |= RXNEG;
269 else
270 val &= ~RXNEG;
271 __raw_writel(val, hw->regs + USI_CNT);
272
273 spin_unlock_irqrestore(&hw->lock, flags);
274}
275
276static void nuc900_send_first(struct nuc900_spi *hw, unsigned int lsb)
277{
278 unsigned int val;
279 unsigned long flags;
280
281 spin_lock_irqsave(&hw->lock, flags);
282
283 val = __raw_readl(hw->regs + USI_CNT);
284
285 if (lsb)
286 val |= LSB;
287 else
288 val &= ~LSB;
289 __raw_writel(val, hw->regs + USI_CNT);
290
291 spin_unlock_irqrestore(&hw->lock, flags);
292}
293
294static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep)
295{
296 unsigned int val;
297 unsigned long flags;
298
299 spin_lock_irqsave(&hw->lock, flags);
300
301 val = __raw_readl(hw->regs + USI_CNT);
302
303 if (sleep)
304 val |= (sleep << 12);
305 else
306 val &= ~(0x0f << 12);
307 __raw_writel(val, hw->regs + USI_CNT);
308
309 spin_unlock_irqrestore(&hw->lock, flags);
310}
311
312static void nuc900_enable_int(struct nuc900_spi *hw)
313{
314 unsigned int val;
315 unsigned long flags;
316
317 spin_lock_irqsave(&hw->lock, flags);
318
319 val = __raw_readl(hw->regs + USI_CNT);
320
321 val |= ENINT;
322
323 __raw_writel(val, hw->regs + USI_CNT);
324
325 spin_unlock_irqrestore(&hw->lock, flags);
326}
327
328static void nuc900_set_divider(struct nuc900_spi *hw)
329{
330 __raw_writel(hw->pdata->divider, hw->regs + USI_DIV);
331}
332
333static void nuc900_init_spi(struct nuc900_spi *hw)
334{
335 clk_enable(hw->clk);
336 spin_lock_init(&hw->lock);
337
338 nuc900_tx_edge(hw, hw->pdata->txneg);
339 nuc900_rx_edge(hw, hw->pdata->rxneg);
340 nuc900_send_first(hw, hw->pdata->lsb);
341 nuc900_set_sleep(hw, hw->pdata->sleep);
342 nuc900_spi_setup_txbitlen(hw, hw->pdata->txbitlen);
343 nuc900_spi_setup_txnum(hw, hw->pdata->txnum);
344 nuc900_set_divider(hw);
345 nuc900_enable_int(hw);
346}
347
348static int __devinit nuc900_spi_probe(struct platform_device *pdev)
349{
350 struct nuc900_spi *hw;
351 struct spi_master *master;
352 int err = 0;
353
354 master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi));
355 if (master == NULL) {
356 dev_err(&pdev->dev, "No memory for spi_master\n");
357 err = -ENOMEM;
358 goto err_nomem;
359 }
360
361 hw = spi_master_get_devdata(master);
362 memset(hw, 0, sizeof(struct nuc900_spi));
363
364 hw->master = spi_master_get(master);
365 hw->pdata = pdev->dev.platform_data;
366 hw->dev = &pdev->dev;
367
368 if (hw->pdata == NULL) {
369 dev_err(&pdev->dev, "No platform data supplied\n");
370 err = -ENOENT;
371 goto err_pdata;
372 }
373
374 platform_set_drvdata(pdev, hw);
375 init_completion(&hw->done);
376
377 master->mode_bits = SPI_MODE_0;
378 master->num_chipselect = hw->pdata->num_cs;
379 master->bus_num = hw->pdata->bus_num;
380 hw->bitbang.master = hw->master;
381 hw->bitbang.setup_transfer = nuc900_spi_setupxfer;
382 hw->bitbang.chipselect = nuc900_spi_chipsel;
383 hw->bitbang.txrx_bufs = nuc900_spi_txrx;
384 hw->bitbang.master->setup = nuc900_spi_setup;
385
386 hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
387 if (hw->res == NULL) {
388 dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
389 err = -ENOENT;
390 goto err_pdata;
391 }
392
393 hw->ioarea = request_mem_region(hw->res->start,
394 resource_size(hw->res), pdev->name);
395
396 if (hw->ioarea == NULL) {
397 dev_err(&pdev->dev, "Cannot reserve region\n");
398 err = -ENXIO;
399 goto err_pdata;
400 }
401
402 hw->regs = ioremap(hw->res->start, resource_size(hw->res));
403 if (hw->regs == NULL) {
404 dev_err(&pdev->dev, "Cannot map IO\n");
405 err = -ENXIO;
406 goto err_iomap;
407 }
408
409 hw->irq = platform_get_irq(pdev, 0);
410 if (hw->irq < 0) {
411 dev_err(&pdev->dev, "No IRQ specified\n");
412 err = -ENOENT;
413 goto err_irq;
414 }
415
416 err = request_irq(hw->irq, nuc900_spi_irq, 0, pdev->name, hw);
417 if (err) {
418 dev_err(&pdev->dev, "Cannot claim IRQ\n");
419 goto err_irq;
420 }
421
422 hw->clk = clk_get(&pdev->dev, "spi");
423 if (IS_ERR(hw->clk)) {
424 dev_err(&pdev->dev, "No clock for device\n");
425 err = PTR_ERR(hw->clk);
426 goto err_clk;
427 }
428
429 mfp_set_groupg(&pdev->dev);
430 nuc900_init_spi(hw);
431
432 err = spi_bitbang_start(&hw->bitbang);
433 if (err) {
434 dev_err(&pdev->dev, "Failed to register SPI master\n");
435 goto err_register;
436 }
437
438 return 0;
439
440err_register:
441 clk_disable(hw->clk);
442 clk_put(hw->clk);
443err_clk:
444 free_irq(hw->irq, hw);
445err_irq:
446 iounmap(hw->regs);
447err_iomap:
448 release_mem_region(hw->res->start, resource_size(hw->res));
449 kfree(hw->ioarea);
450err_pdata:
451 spi_master_put(hw->master);;
452
453err_nomem:
454 return err;
455}
456
457static int __devexit nuc900_spi_remove(struct platform_device *dev)
458{
459 struct nuc900_spi *hw = platform_get_drvdata(dev);
460
461 free_irq(hw->irq, hw);
462
463 platform_set_drvdata(dev, NULL);
464
465 spi_unregister_master(hw->master);
466
467 clk_disable(hw->clk);
468 clk_put(hw->clk);
469
470 iounmap(hw->regs);
471
472 release_mem_region(hw->res->start, resource_size(hw->res));
473 kfree(hw->ioarea);
474
475 spi_master_put(hw->master);
476 return 0;
477}
478
479static struct platform_driver nuc900_spi_driver = {
480 .probe = nuc900_spi_probe,
481 .remove = __devexit_p(nuc900_spi_remove),
482 .driver = {
483 .name = "nuc900-spi",
484 .owner = THIS_MODULE,
485 },
486};
487
488static int __init nuc900_spi_init(void)
489{
490 return platform_driver_register(&nuc900_spi_driver);
491}
492
493static void __exit nuc900_spi_exit(void)
494{
495 platform_driver_unregister(&nuc900_spi_driver);
496}
497
498module_init(nuc900_spi_init);
499module_exit(nuc900_spi_exit);
500
501MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
502MODULE_DESCRIPTION("nuc900 spi driver!");
503MODULE_LICENSE("GPL");
504MODULE_ALIAS("platform:nuc900-spi");