Commit 478e4e9d7a618379676b17e64583ff3622f2fec5
Exists in
master
and in
39 other branches
Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6
* 'next-spi' of git://git.secretlab.ca/git/linux-2.6: (23 commits) spi: fix probe/remove section markings Add OMAP spi100k driver spi-imx: don't access struct device directly but use dev_get_platdata spi-imx: Add mx25 support spi-imx: use positive logic to distinguish cpu variants spi-imx: correct check for platform_get_irq failing ARM: NUC900: Add spi driver support for nuc900 spi: SuperH MSIOF SPI Master driver V2 spi: fix spidev compilation failure when VERBOSE is defined spi/au1550_spi: fix setupxfer not to override cfg with zeros spi/mpc8xxx: don't use __exit_p to wrap plat_mpc8xxx_spi_remove spi/i.MX: fix broken error handling for gpio_request spi/i.mx: drain MXC SPI transfer buffer when probing device MAINTAINERS: add SPI co-maintainer. spi/xilinx_spi: fix incorrect casting spi/mpc52xx-spi: minor cleanups xilinx_spi: add a platform driver using the xilinx_spi common module. xilinx_spi: add support for the DS570 IP. xilinx_spi: Switch to iomem functions and support little endian. xilinx_spi: Split into of driver and generic part. ...
Showing 19 changed files Side-by-side Diff
- MAINTAINERS
- arch/arm/mach-w90x900/include/mach/nuc900_spi.h
- drivers/spi/Kconfig
- drivers/spi/Makefile
- drivers/spi/au1550_spi.c
- drivers/spi/mpc52xx_spi.c
- drivers/spi/omap_spi_100k.c
- drivers/spi/spi_imx.c
- drivers/spi/spi_mpc8xxx.c
- drivers/spi/spi_nuc900.c
- drivers/spi/spi_sh_msiof.c
- drivers/spi/spidev.c
- drivers/spi/xilinx_spi.c
- drivers/spi/xilinx_spi.h
- drivers/spi/xilinx_spi_of.c
- drivers/spi/xilinx_spi_pltfm.c
- include/linux/spi/mpc52xx_spi.h
- include/linux/spi/sh_msiof.h
- include/linux/spi/xilinx_spi.h
MAINTAINERS
arch/arm/mach-w90x900/include/mach/nuc900_spi.h
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 | + | |
17 | +extern void mfp_set_groupg(struct device *dev); | |
18 | + | |
19 | +struct 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 | + | |
31 | +struct nuc900_spi_chip { | |
32 | + unsigned char bits_per_word; | |
33 | +}; | |
34 | + | |
35 | +#endif /* __ASM_ARCH_SPI_H */ |
drivers/spi/Kconfig
... | ... | @@ -169,6 +169,12 @@ |
169 | 169 | SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI |
170 | 170 | (McSPI) modules. |
171 | 171 | |
172 | +config SPI_OMAP_100K | |
173 | + tristate "OMAP SPI 100K" | |
174 | + depends on SPI_MASTER && (ARCH_OMAP850 || ARCH_OMAP730) | |
175 | + help | |
176 | + OMAP SPI 100K master controller for omap7xx boards. | |
177 | + | |
172 | 178 | config SPI_ORION |
173 | 179 | tristate "Orion SPI master (EXPERIMENTAL)" |
174 | 180 | depends on PLAT_ORION && EXPERIMENTAL |
... | ... | @@ -220,6 +226,13 @@ |
220 | 226 | the inbuilt hardware cannot provide the transfer mode, or |
221 | 227 | where the board is using non hardware connected pins. |
222 | 228 | |
229 | +config SPI_SH_MSIOF | |
230 | + tristate "SuperH MSIOF SPI controller" | |
231 | + depends on SUPERH && HAVE_CLK | |
232 | + select SPI_BITBANG | |
233 | + help | |
234 | + SPI driver for SuperH MSIOF blocks. | |
235 | + | |
223 | 236 | config SPI_SH_SCI |
224 | 237 | tristate "SuperH SCI SPI controller" |
225 | 238 | depends on SUPERH |
226 | 239 | |
227 | 240 | |
... | ... | @@ -240,14 +253,37 @@ |
240 | 253 | SPI driver for Toshiba TXx9 MIPS SoCs |
241 | 254 | |
242 | 255 | config SPI_XILINX |
243 | - tristate "Xilinx SPI controller" | |
244 | - depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL | |
256 | + tristate "Xilinx SPI controller common module" | |
257 | + depends on HAS_IOMEM && EXPERIMENTAL | |
245 | 258 | select SPI_BITBANG |
259 | + select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE) | |
246 | 260 | help |
247 | 261 | This exposes the SPI controller IP from the Xilinx EDK. |
248 | 262 | |
249 | 263 | See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" |
250 | 264 | Product Specification document (DS464) for hardware details. |
265 | + | |
266 | + Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)" | |
267 | + | |
268 | +config SPI_XILINX_OF | |
269 | + tristate "Xilinx SPI controller OF device" | |
270 | + depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE) | |
271 | + help | |
272 | + This is the OF driver for the SPI controller IP from the Xilinx EDK. | |
273 | + | |
274 | +config SPI_XILINX_PLTFM | |
275 | + tristate "Xilinx SPI controller platform device" | |
276 | + depends on SPI_XILINX | |
277 | + help | |
278 | + This is the platform driver for the SPI controller IP | |
279 | + from the Xilinx EDK. | |
280 | + | |
281 | +config SPI_NUC900 | |
282 | + tristate "Nuvoton NUC900 series SPI" | |
283 | + depends on ARCH_W90X900 && EXPERIMENTAL | |
284 | + select SPI_BITBANG | |
285 | + help | |
286 | + SPI driver for Nuvoton NUC900 series ARM SoCs | |
251 | 287 | |
252 | 288 | # |
253 | 289 | # Add new SPI master controllers in alphabetical order above this line |
drivers/spi/Makefile
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o |
23 | 23 | obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o |
24 | 24 | obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o |
25 | +obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o | |
25 | 26 | obj-$(CONFIG_SPI_ORION) += orion_spi.o |
26 | 27 | obj-$(CONFIG_SPI_PL022) += amba-pl022.o |
27 | 28 | obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o |
28 | 29 | |
29 | 30 | |
... | ... | @@ -32,8 +33,12 @@ |
32 | 33 | obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o |
33 | 34 | obj-$(CONFIG_SPI_TXX9) += spi_txx9.o |
34 | 35 | obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o |
36 | +obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o | |
37 | +obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.o | |
35 | 38 | obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o |
39 | +obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o | |
36 | 40 | obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o |
41 | +obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o | |
37 | 42 | # ... add above this line ... |
38 | 43 | |
39 | 44 | # SPI protocol drivers (device/link on bus) |
drivers/spi/au1550_spi.c
... | ... | @@ -237,8 +237,14 @@ |
237 | 237 | unsigned bpw, hz; |
238 | 238 | u32 cfg, stat; |
239 | 239 | |
240 | - bpw = t ? t->bits_per_word : spi->bits_per_word; | |
241 | - hz = t ? t->speed_hz : spi->max_speed_hz; | |
240 | + bpw = spi->bits_per_word; | |
241 | + hz = spi->max_speed_hz; | |
242 | + if (t) { | |
243 | + if (t->bits_per_word) | |
244 | + bpw = t->bits_per_word; | |
245 | + if (t->speed_hz) | |
246 | + hz = t->speed_hz; | |
247 | + } | |
242 | 248 | |
243 | 249 | if (bpw < 4 || bpw > 24) { |
244 | 250 | dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n", |
drivers/spi/mpc52xx_spi.c
... | ... | @@ -18,9 +18,9 @@ |
18 | 18 | #include <linux/interrupt.h> |
19 | 19 | #include <linux/delay.h> |
20 | 20 | #include <linux/spi/spi.h> |
21 | -#include <linux/spi/mpc52xx_spi.h> | |
22 | 21 | #include <linux/of_spi.h> |
23 | 22 | #include <linux/io.h> |
23 | +#include <linux/of_gpio.h> | |
24 | 24 | #include <asm/time.h> |
25 | 25 | #include <asm/mpc52xx.h> |
26 | 26 | |
... | ... | @@ -53,7 +53,7 @@ |
53 | 53 | /* FSM state return values */ |
54 | 54 | #define FSM_STOP 0 /* Nothing more for the state machine to */ |
55 | 55 | /* do. If something interesting happens */ |
56 | - /* then and IRQ will be received */ | |
56 | + /* then an IRQ will be received */ | |
57 | 57 | #define FSM_POLL 1 /* need to poll for completion, an IRQ is */ |
58 | 58 | /* not expected */ |
59 | 59 | #define FSM_CONTINUE 2 /* Keep iterating the state machine */ |
60 | 60 | |
61 | 61 | |
... | ... | @@ -61,13 +61,12 @@ |
61 | 61 | /* Driver internal data */ |
62 | 62 | struct mpc52xx_spi { |
63 | 63 | struct spi_master *master; |
64 | - u32 sysclk; | |
65 | 64 | void __iomem *regs; |
66 | 65 | int irq0; /* MODF irq */ |
67 | 66 | int irq1; /* SPIF irq */ |
68 | - int ipb_freq; | |
67 | + unsigned int ipb_freq; | |
69 | 68 | |
70 | - /* Statistics */ | |
69 | + /* Statistics; not used now, but will be reintroduced for debugfs */ | |
71 | 70 | int msg_count; |
72 | 71 | int wcol_count; |
73 | 72 | int wcol_ticks; |
... | ... | @@ -79,7 +78,6 @@ |
79 | 78 | spinlock_t lock; |
80 | 79 | struct work_struct work; |
81 | 80 | |
82 | - | |
83 | 81 | /* Details of current transfer (length, and buffer pointers) */ |
84 | 82 | struct spi_message *message; /* current message */ |
85 | 83 | struct spi_transfer *transfer; /* current transfer */ |
... | ... | @@ -89,6 +87,8 @@ |
89 | 87 | u8 *rx_buf; |
90 | 88 | const u8 *tx_buf; |
91 | 89 | int cs_change; |
90 | + int gpio_cs_count; | |
91 | + unsigned int *gpio_cs; | |
92 | 92 | }; |
93 | 93 | |
94 | 94 | /* |
... | ... | @@ -96,7 +96,13 @@ |
96 | 96 | */ |
97 | 97 | static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value) |
98 | 98 | { |
99 | - out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08); | |
99 | + int cs; | |
100 | + | |
101 | + if (ms->gpio_cs_count > 0) { | |
102 | + cs = ms->message->spi->chip_select; | |
103 | + gpio_set_value(ms->gpio_cs[cs], value ? 0 : 1); | |
104 | + } else | |
105 | + out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08); | |
100 | 106 | } |
101 | 107 | |
102 | 108 | /* |
... | ... | @@ -221,7 +227,7 @@ |
221 | 227 | ms->wcol_tx_timestamp = get_tbl(); |
222 | 228 | data = 0; |
223 | 229 | if (ms->tx_buf) |
224 | - data = *(ms->tx_buf-1); | |
230 | + data = *(ms->tx_buf - 1); | |
225 | 231 | out_8(ms->regs + SPI_DATA, data); /* try again */ |
226 | 232 | return FSM_CONTINUE; |
227 | 233 | } else if (status & SPI_STATUS_MODF) { |
... | ... | @@ -390,7 +396,9 @@ |
390 | 396 | struct spi_master *master; |
391 | 397 | struct mpc52xx_spi *ms; |
392 | 398 | void __iomem *regs; |
393 | - int rc; | |
399 | + u8 ctrl1; | |
400 | + int rc, i = 0; | |
401 | + int gpio_cs; | |
394 | 402 | |
395 | 403 | /* MMIO registers */ |
396 | 404 | dev_dbg(&op->dev, "probing mpc5200 SPI device\n"); |
... | ... | @@ -399,7 +407,8 @@ |
399 | 407 | return -ENODEV; |
400 | 408 | |
401 | 409 | /* initialize the device */ |
402 | - out_8(regs+SPI_CTRL1, SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR); | |
410 | + ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR; | |
411 | + out_8(regs + SPI_CTRL1, ctrl1); | |
403 | 412 | out_8(regs + SPI_CTRL2, 0x0); |
404 | 413 | out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */ |
405 | 414 | out_8(regs + SPI_PORTDATA, 0x8); /* Deassert /SS signal */ |
... | ... | @@ -409,6 +418,8 @@ |
409 | 418 | * on the SPI bus. This fault will also occur if the SPI signals |
410 | 419 | * are not connected to any pins (port_config setting) */ |
411 | 420 | in_8(regs + SPI_STATUS); |
421 | + out_8(regs + SPI_CTRL1, ctrl1); | |
422 | + | |
412 | 423 | in_8(regs + SPI_DATA); |
413 | 424 | if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) { |
414 | 425 | dev_err(&op->dev, "mode fault; is port_config correct?\n"); |
415 | 426 | |
416 | 427 | |
... | ... | @@ -422,10 +433,12 @@ |
422 | 433 | rc = -ENOMEM; |
423 | 434 | goto err_alloc; |
424 | 435 | } |
436 | + | |
425 | 437 | master->bus_num = -1; |
426 | - master->num_chipselect = 1; | |
427 | 438 | master->setup = mpc52xx_spi_setup; |
428 | 439 | master->transfer = mpc52xx_spi_transfer; |
440 | + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; | |
441 | + | |
429 | 442 | dev_set_drvdata(&op->dev, master); |
430 | 443 | |
431 | 444 | ms = spi_master_get_devdata(master); |
432 | 445 | |
433 | 446 | |
... | ... | @@ -435,16 +448,51 @@ |
435 | 448 | ms->irq1 = irq_of_parse_and_map(op->node, 1); |
436 | 449 | ms->state = mpc52xx_spi_fsmstate_idle; |
437 | 450 | ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node); |
451 | + ms->gpio_cs_count = of_gpio_count(op->node); | |
452 | + if (ms->gpio_cs_count > 0) { | |
453 | + master->num_chipselect = ms->gpio_cs_count; | |
454 | + ms->gpio_cs = kmalloc(ms->gpio_cs_count * sizeof(unsigned int), | |
455 | + GFP_KERNEL); | |
456 | + if (!ms->gpio_cs) { | |
457 | + rc = -ENOMEM; | |
458 | + goto err_alloc; | |
459 | + } | |
460 | + | |
461 | + for (i = 0; i < ms->gpio_cs_count; i++) { | |
462 | + gpio_cs = of_get_gpio(op->node, i); | |
463 | + if (gpio_cs < 0) { | |
464 | + dev_err(&op->dev, | |
465 | + "could not parse the gpio field " | |
466 | + "in oftree\n"); | |
467 | + rc = -ENODEV; | |
468 | + goto err_gpio; | |
469 | + } | |
470 | + | |
471 | + rc = gpio_request(gpio_cs, dev_name(&op->dev)); | |
472 | + if (rc) { | |
473 | + dev_err(&op->dev, | |
474 | + "can't request spi cs gpio #%d " | |
475 | + "on gpio line %d\n", i, gpio_cs); | |
476 | + goto err_gpio; | |
477 | + } | |
478 | + | |
479 | + gpio_direction_output(gpio_cs, 1); | |
480 | + ms->gpio_cs[i] = gpio_cs; | |
481 | + } | |
482 | + } else { | |
483 | + master->num_chipselect = 1; | |
484 | + } | |
485 | + | |
438 | 486 | spin_lock_init(&ms->lock); |
439 | 487 | INIT_LIST_HEAD(&ms->queue); |
440 | 488 | INIT_WORK(&ms->work, mpc52xx_spi_wq); |
441 | 489 | |
442 | 490 | /* Decide if interrupts can be used */ |
443 | 491 | if (ms->irq0 && ms->irq1) { |
444 | - rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM, | |
492 | + rc = request_irq(ms->irq0, mpc52xx_spi_irq, 0, | |
445 | 493 | "mpc5200-spi-modf", ms); |
446 | - rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM, | |
447 | - "mpc5200-spi-spiF", ms); | |
494 | + rc |= request_irq(ms->irq1, mpc52xx_spi_irq, 0, | |
495 | + "mpc5200-spi-spif", ms); | |
448 | 496 | if (rc) { |
449 | 497 | free_irq(ms->irq0, ms); |
450 | 498 | free_irq(ms->irq1, ms); |
... | ... | @@ -471,6 +519,11 @@ |
471 | 519 | err_register: |
472 | 520 | dev_err(&ms->master->dev, "initialization failed\n"); |
473 | 521 | spi_master_put(master); |
522 | + err_gpio: | |
523 | + while (i-- > 0) | |
524 | + gpio_free(ms->gpio_cs[i]); | |
525 | + | |
526 | + kfree(ms->gpio_cs); | |
474 | 527 | err_alloc: |
475 | 528 | err_init: |
476 | 529 | iounmap(regs); |
477 | 530 | |
... | ... | @@ -481,10 +534,15 @@ |
481 | 534 | { |
482 | 535 | struct spi_master *master = dev_get_drvdata(&op->dev); |
483 | 536 | struct mpc52xx_spi *ms = spi_master_get_devdata(master); |
537 | + int i; | |
484 | 538 | |
485 | 539 | free_irq(ms->irq0, ms); |
486 | 540 | free_irq(ms->irq1, ms); |
487 | 541 | |
542 | + for (i = 0; i < ms->gpio_cs_count; i++) | |
543 | + gpio_free(ms->gpio_cs[i]); | |
544 | + | |
545 | + kfree(ms->gpio_cs); | |
488 | 546 | spi_unregister_master(master); |
489 | 547 | spi_master_put(master); |
490 | 548 | iounmap(ms->regs); |
drivers/spi/omap_spi_100k.c
1 | +/* | |
2 | + * OMAP7xx SPI 100k controller driver | |
3 | + * Author: Fabrice Crohas <fcrohas@gmail.com> | |
4 | + * from original omap1_mcspi driver | |
5 | + * | |
6 | + * Copyright (C) 2005, 2006 Nokia Corporation | |
7 | + * Author: Samuel Ortiz <samuel.ortiz@nokia.com> and | |
8 | + * Juha Yrj๏ฟฝl๏ฟฝ <juha.yrjola@nokia.com> | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify | |
11 | + * it under the terms of the GNU General Public License as published by | |
12 | + * the Free Software Foundation; either version 2 of the License, or | |
13 | + * (at your option) any later version. | |
14 | + * | |
15 | + * This program is distributed in the hope that it will be useful, | |
16 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | + * GNU General Public License for more details. | |
19 | + * | |
20 | + * You should have received a copy of the GNU General Public License | |
21 | + * along with this program; if not, write to the Free Software | |
22 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 | + * | |
24 | + */ | |
25 | +#include <linux/kernel.h> | |
26 | +#include <linux/init.h> | |
27 | +#include <linux/interrupt.h> | |
28 | +#include <linux/module.h> | |
29 | +#include <linux/device.h> | |
30 | +#include <linux/delay.h> | |
31 | +#include <linux/platform_device.h> | |
32 | +#include <linux/err.h> | |
33 | +#include <linux/clk.h> | |
34 | +#include <linux/io.h> | |
35 | +#include <linux/gpio.h> | |
36 | + | |
37 | +#include <linux/spi/spi.h> | |
38 | + | |
39 | +#include <plat/clock.h> | |
40 | + | |
41 | +#define OMAP1_SPI100K_MAX_FREQ 48000000 | |
42 | + | |
43 | +#define ICR_SPITAS (OMAP7XX_ICR_BASE + 0x12) | |
44 | + | |
45 | +#define SPI_SETUP1 0x00 | |
46 | +#define SPI_SETUP2 0x02 | |
47 | +#define SPI_CTRL 0x04 | |
48 | +#define SPI_STATUS 0x06 | |
49 | +#define SPI_TX_LSB 0x08 | |
50 | +#define SPI_TX_MSB 0x0a | |
51 | +#define SPI_RX_LSB 0x0c | |
52 | +#define SPI_RX_MSB 0x0e | |
53 | + | |
54 | +#define SPI_SETUP1_INT_READ_ENABLE (1UL << 5) | |
55 | +#define SPI_SETUP1_INT_WRITE_ENABLE (1UL << 4) | |
56 | +#define SPI_SETUP1_CLOCK_DIVISOR(x) ((x) << 1) | |
57 | +#define SPI_SETUP1_CLOCK_ENABLE (1UL << 0) | |
58 | + | |
59 | +#define SPI_SETUP2_ACTIVE_EDGE_FALLING (0UL << 0) | |
60 | +#define SPI_SETUP2_ACTIVE_EDGE_RISING (1UL << 0) | |
61 | +#define SPI_SETUP2_NEGATIVE_LEVEL (0UL << 5) | |
62 | +#define SPI_SETUP2_POSITIVE_LEVEL (1UL << 5) | |
63 | +#define SPI_SETUP2_LEVEL_TRIGGER (0UL << 10) | |
64 | +#define SPI_SETUP2_EDGE_TRIGGER (1UL << 10) | |
65 | + | |
66 | +#define SPI_CTRL_SEN(x) ((x) << 7) | |
67 | +#define SPI_CTRL_WORD_SIZE(x) (((x) - 1) << 2) | |
68 | +#define SPI_CTRL_WR (1UL << 1) | |
69 | +#define SPI_CTRL_RD (1UL << 0) | |
70 | + | |
71 | +#define SPI_STATUS_WE (1UL << 1) | |
72 | +#define SPI_STATUS_RD (1UL << 0) | |
73 | + | |
74 | +#define WRITE 0 | |
75 | +#define READ 1 | |
76 | + | |
77 | + | |
78 | +/* use PIO for small transfers, avoiding DMA setup/teardown overhead and | |
79 | + * cache operations; better heuristics consider wordsize and bitrate. | |
80 | + */ | |
81 | +#define DMA_MIN_BYTES 8 | |
82 | + | |
83 | +#define SPI_RUNNING 0 | |
84 | +#define SPI_SHUTDOWN 1 | |
85 | + | |
86 | +struct omap1_spi100k { | |
87 | + struct work_struct work; | |
88 | + | |
89 | + /* lock protects queue and registers */ | |
90 | + spinlock_t lock; | |
91 | + struct list_head msg_queue; | |
92 | + struct spi_master *master; | |
93 | + struct clk *ick; | |
94 | + struct clk *fck; | |
95 | + | |
96 | + /* Virtual base address of the controller */ | |
97 | + void __iomem *base; | |
98 | + | |
99 | + /* State of the SPI */ | |
100 | + unsigned int state; | |
101 | +}; | |
102 | + | |
103 | +struct omap1_spi100k_cs { | |
104 | + void __iomem *base; | |
105 | + int word_len; | |
106 | +}; | |
107 | + | |
108 | +static struct workqueue_struct *omap1_spi100k_wq; | |
109 | + | |
110 | +#define MOD_REG_BIT(val, mask, set) do { \ | |
111 | + if (set) \ | |
112 | + val |= mask; \ | |
113 | + else \ | |
114 | + val &= ~mask; \ | |
115 | +} while (0) | |
116 | + | |
117 | +static void spi100k_enable_clock(struct spi_master *master) | |
118 | +{ | |
119 | + unsigned int val; | |
120 | + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
121 | + | |
122 | + /* enable SPI */ | |
123 | + val = readw(spi100k->base + SPI_SETUP1); | |
124 | + val |= SPI_SETUP1_CLOCK_ENABLE; | |
125 | + writew(val, spi100k->base + SPI_SETUP1); | |
126 | +} | |
127 | + | |
128 | +static void spi100k_disable_clock(struct spi_master *master) | |
129 | +{ | |
130 | + unsigned int val; | |
131 | + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
132 | + | |
133 | + /* disable SPI */ | |
134 | + val = readw(spi100k->base + SPI_SETUP1); | |
135 | + val &= ~SPI_SETUP1_CLOCK_ENABLE; | |
136 | + writew(val, spi100k->base + SPI_SETUP1); | |
137 | +} | |
138 | + | |
139 | +static void spi100k_write_data(struct spi_master *master, int len, int data) | |
140 | +{ | |
141 | + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
142 | + | |
143 | + /* write 16-bit word */ | |
144 | + spi100k_enable_clock(master); | |
145 | + writew( data , spi100k->base + SPI_TX_MSB); | |
146 | + | |
147 | + writew(SPI_CTRL_SEN(0) | | |
148 | + SPI_CTRL_WORD_SIZE(len) | | |
149 | + SPI_CTRL_WR, | |
150 | + spi100k->base + SPI_CTRL); | |
151 | + | |
152 | + /* Wait for bit ack send change */ | |
153 | + while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE); | |
154 | + udelay(1000); | |
155 | + | |
156 | + spi100k_disable_clock(master); | |
157 | +} | |
158 | + | |
159 | +static int spi100k_read_data(struct spi_master *master, int len) | |
160 | +{ | |
161 | + int dataH,dataL; | |
162 | + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
163 | + | |
164 | + spi100k_enable_clock(master); | |
165 | + writew(SPI_CTRL_SEN(0) | | |
166 | + SPI_CTRL_WORD_SIZE(len) | | |
167 | + SPI_CTRL_RD, | |
168 | + spi100k->base + SPI_CTRL); | |
169 | + | |
170 | + while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD); | |
171 | + udelay(1000); | |
172 | + | |
173 | + dataL = readw(spi100k->base + SPI_RX_LSB); | |
174 | + dataH = readw(spi100k->base + SPI_RX_MSB); | |
175 | + spi100k_disable_clock(master); | |
176 | + | |
177 | + return dataL; | |
178 | +} | |
179 | + | |
180 | +static void spi100k_open(struct spi_master *master) | |
181 | +{ | |
182 | + /* get control of SPI */ | |
183 | + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
184 | + | |
185 | + writew(SPI_SETUP1_INT_READ_ENABLE | | |
186 | + SPI_SETUP1_INT_WRITE_ENABLE | | |
187 | + SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base + SPI_SETUP1); | |
188 | + | |
189 | + /* configure clock and interrupts */ | |
190 | + writew(SPI_SETUP2_ACTIVE_EDGE_FALLING | | |
191 | + SPI_SETUP2_NEGATIVE_LEVEL | | |
192 | + SPI_SETUP2_LEVEL_TRIGGER, spi100k->base + SPI_SETUP2); | |
193 | +} | |
194 | + | |
195 | +static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable) | |
196 | +{ | |
197 | + if (enable) | |
198 | + writew(0x05fc, spi100k->base + SPI_CTRL); | |
199 | + else | |
200 | + writew(0x05fd, spi100k->base + SPI_CTRL); | |
201 | +} | |
202 | + | |
203 | +static unsigned | |
204 | +omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |
205 | +{ | |
206 | + struct omap1_spi100k *spi100k; | |
207 | + struct omap1_spi100k_cs *cs = spi->controller_state; | |
208 | + unsigned int count, c; | |
209 | + int word_len; | |
210 | + | |
211 | + spi100k = spi_master_get_devdata(spi->master); | |
212 | + count = xfer->len; | |
213 | + c = count; | |
214 | + word_len = cs->word_len; | |
215 | + | |
216 | + /* RX_ONLY mode needs dummy data in TX reg */ | |
217 | + if (xfer->tx_buf == NULL) | |
218 | + spi100k_write_data(spi->master,word_len, 0); | |
219 | + | |
220 | + if (word_len <= 8) { | |
221 | + u8 *rx; | |
222 | + const u8 *tx; | |
223 | + | |
224 | + rx = xfer->rx_buf; | |
225 | + tx = xfer->tx_buf; | |
226 | + do { | |
227 | + c-=1; | |
228 | + if (xfer->tx_buf != NULL) | |
229 | + spi100k_write_data(spi->master,word_len, *tx); | |
230 | + if (xfer->rx_buf != NULL) | |
231 | + *rx = spi100k_read_data(spi->master,word_len); | |
232 | + } while(c); | |
233 | + } else if (word_len <= 16) { | |
234 | + u16 *rx; | |
235 | + const u16 *tx; | |
236 | + | |
237 | + rx = xfer->rx_buf; | |
238 | + tx = xfer->tx_buf; | |
239 | + do { | |
240 | + c-=2; | |
241 | + if (xfer->tx_buf != NULL) | |
242 | + spi100k_write_data(spi->master,word_len, *tx++); | |
243 | + if (xfer->rx_buf != NULL) | |
244 | + *rx++ = spi100k_read_data(spi->master,word_len); | |
245 | + } while(c); | |
246 | + } else if (word_len <= 32) { | |
247 | + u32 *rx; | |
248 | + const u32 *tx; | |
249 | + | |
250 | + rx = xfer->rx_buf; | |
251 | + tx = xfer->tx_buf; | |
252 | + do { | |
253 | + c-=4; | |
254 | + if (xfer->tx_buf != NULL) | |
255 | + spi100k_write_data(spi->master,word_len, *tx); | |
256 | + if (xfer->rx_buf != NULL) | |
257 | + *rx = spi100k_read_data(spi->master,word_len); | |
258 | + } while(c); | |
259 | + } | |
260 | + return count - c; | |
261 | +} | |
262 | + | |
263 | +/* called only when no transfer is active to this device */ | |
264 | +static int omap1_spi100k_setup_transfer(struct spi_device *spi, | |
265 | + struct spi_transfer *t) | |
266 | +{ | |
267 | + struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master); | |
268 | + struct omap1_spi100k_cs *cs = spi->controller_state; | |
269 | + u8 word_len = spi->bits_per_word; | |
270 | + | |
271 | + if (t != NULL && t->bits_per_word) | |
272 | + word_len = t->bits_per_word; | |
273 | + if (!word_len) | |
274 | + word_len = 8; | |
275 | + | |
276 | + if (spi->bits_per_word > 32) | |
277 | + return -EINVAL; | |
278 | + cs->word_len = word_len; | |
279 | + | |
280 | + /* SPI init before transfer */ | |
281 | + writew(0x3e , spi100k->base + SPI_SETUP1); | |
282 | + writew(0x00 , spi100k->base + SPI_STATUS); | |
283 | + writew(0x3e , spi100k->base + SPI_CTRL); | |
284 | + | |
285 | + return 0; | |
286 | +} | |
287 | + | |
288 | +/* the spi->mode bits understood by this driver: */ | |
289 | +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) | |
290 | + | |
291 | +static int omap1_spi100k_setup(struct spi_device *spi) | |
292 | +{ | |
293 | + int ret; | |
294 | + struct omap1_spi100k *spi100k; | |
295 | + struct omap1_spi100k_cs *cs = spi->controller_state; | |
296 | + | |
297 | + if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { | |
298 | + dev_dbg(&spi->dev, "setup: unsupported %d bit words\n", | |
299 | + spi->bits_per_word); | |
300 | + return -EINVAL; | |
301 | + } | |
302 | + | |
303 | + spi100k = spi_master_get_devdata(spi->master); | |
304 | + | |
305 | + if (!cs) { | |
306 | + cs = kzalloc(sizeof *cs, GFP_KERNEL); | |
307 | + if (!cs) | |
308 | + return -ENOMEM; | |
309 | + cs->base = spi100k->base + spi->chip_select * 0x14; | |
310 | + spi->controller_state = cs; | |
311 | + } | |
312 | + | |
313 | + spi100k_open(spi->master); | |
314 | + | |
315 | + clk_enable(spi100k->ick); | |
316 | + clk_enable(spi100k->fck); | |
317 | + | |
318 | + ret = omap1_spi100k_setup_transfer(spi, NULL); | |
319 | + | |
320 | + clk_disable(spi100k->ick); | |
321 | + clk_disable(spi100k->fck); | |
322 | + | |
323 | + return ret; | |
324 | +} | |
325 | + | |
326 | +static void omap1_spi100k_work(struct work_struct *work) | |
327 | +{ | |
328 | + struct omap1_spi100k *spi100k; | |
329 | + int status = 0; | |
330 | + | |
331 | + spi100k = container_of(work, struct omap1_spi100k, work); | |
332 | + spin_lock_irq(&spi100k->lock); | |
333 | + | |
334 | + clk_enable(spi100k->ick); | |
335 | + clk_enable(spi100k->fck); | |
336 | + | |
337 | + /* We only enable one channel at a time -- the one whose message is | |
338 | + * at the head of the queue -- although this controller would gladly | |
339 | + * arbitrate among multiple channels. This corresponds to "single | |
340 | + * channel" master mode. As a side effect, we need to manage the | |
341 | + * chipselect with the FORCE bit ... CS != channel enable. | |
342 | + */ | |
343 | + while (!list_empty(&spi100k->msg_queue)) { | |
344 | + struct spi_message *m; | |
345 | + struct spi_device *spi; | |
346 | + struct spi_transfer *t = NULL; | |
347 | + int cs_active = 0; | |
348 | + struct omap1_spi100k_cs *cs; | |
349 | + int par_override = 0; | |
350 | + | |
351 | + m = container_of(spi100k->msg_queue.next, struct spi_message, | |
352 | + queue); | |
353 | + | |
354 | + list_del_init(&m->queue); | |
355 | + spin_unlock_irq(&spi100k->lock); | |
356 | + | |
357 | + spi = m->spi; | |
358 | + cs = spi->controller_state; | |
359 | + | |
360 | + list_for_each_entry(t, &m->transfers, transfer_list) { | |
361 | + if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { | |
362 | + status = -EINVAL; | |
363 | + break; | |
364 | + } | |
365 | + if (par_override || t->speed_hz || t->bits_per_word) { | |
366 | + par_override = 1; | |
367 | + status = omap1_spi100k_setup_transfer(spi, t); | |
368 | + if (status < 0) | |
369 | + break; | |
370 | + if (!t->speed_hz && !t->bits_per_word) | |
371 | + par_override = 0; | |
372 | + } | |
373 | + | |
374 | + if (!cs_active) { | |
375 | + omap1_spi100k_force_cs(spi100k, 1); | |
376 | + cs_active = 1; | |
377 | + } | |
378 | + | |
379 | + if (t->len) { | |
380 | + unsigned count; | |
381 | + | |
382 | + /* RX_ONLY mode needs dummy data in TX reg */ | |
383 | + if (t->tx_buf == NULL) | |
384 | + spi100k_write_data(spi->master, 8, 0); | |
385 | + | |
386 | + count = omap1_spi100k_txrx_pio(spi, t); | |
387 | + m->actual_length += count; | |
388 | + | |
389 | + if (count != t->len) { | |
390 | + status = -EIO; | |
391 | + break; | |
392 | + } | |
393 | + } | |
394 | + | |
395 | + if (t->delay_usecs) | |
396 | + udelay(t->delay_usecs); | |
397 | + | |
398 | + /* ignore the "leave it on after last xfer" hint */ | |
399 | + | |
400 | + if (t->cs_change) { | |
401 | + omap1_spi100k_force_cs(spi100k, 0); | |
402 | + cs_active = 0; | |
403 | + } | |
404 | + } | |
405 | + | |
406 | + /* Restore defaults if they were overriden */ | |
407 | + if (par_override) { | |
408 | + par_override = 0; | |
409 | + status = omap1_spi100k_setup_transfer(spi, NULL); | |
410 | + } | |
411 | + | |
412 | + if (cs_active) | |
413 | + omap1_spi100k_force_cs(spi100k, 0); | |
414 | + | |
415 | + m->status = status; | |
416 | + m->complete(m->context); | |
417 | + | |
418 | + spin_lock_irq(&spi100k->lock); | |
419 | + } | |
420 | + | |
421 | + clk_disable(spi100k->ick); | |
422 | + clk_disable(spi100k->fck); | |
423 | + spin_unlock_irq(&spi100k->lock); | |
424 | + | |
425 | + if (status < 0) | |
426 | + printk(KERN_WARNING "spi transfer failed with %d\n", status); | |
427 | +} | |
428 | + | |
429 | +static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) | |
430 | +{ | |
431 | + struct omap1_spi100k *spi100k; | |
432 | + unsigned long flags; | |
433 | + struct spi_transfer *t; | |
434 | + | |
435 | + m->actual_length = 0; | |
436 | + m->status = -EINPROGRESS; | |
437 | + | |
438 | + spi100k = spi_master_get_devdata(spi->master); | |
439 | + | |
440 | + /* Don't accept new work if we're shutting down */ | |
441 | + if (spi100k->state == SPI_SHUTDOWN) | |
442 | + return -ESHUTDOWN; | |
443 | + | |
444 | + /* reject invalid messages and transfers */ | |
445 | + if (list_empty(&m->transfers) || !m->complete) | |
446 | + return -EINVAL; | |
447 | + | |
448 | + list_for_each_entry(t, &m->transfers, transfer_list) { | |
449 | + const void *tx_buf = t->tx_buf; | |
450 | + void *rx_buf = t->rx_buf; | |
451 | + unsigned len = t->len; | |
452 | + | |
453 | + if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ | |
454 | + || (len && !(rx_buf || tx_buf)) | |
455 | + || (t->bits_per_word && | |
456 | + ( t->bits_per_word < 4 | |
457 | + || t->bits_per_word > 32))) { | |
458 | + dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", | |
459 | + t->speed_hz, | |
460 | + len, | |
461 | + tx_buf ? "tx" : "", | |
462 | + rx_buf ? "rx" : "", | |
463 | + t->bits_per_word); | |
464 | + return -EINVAL; | |
465 | + } | |
466 | + | |
467 | + if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) { | |
468 | + dev_dbg(&spi->dev, "%d Hz max exceeds %d\n", | |
469 | + t->speed_hz, | |
470 | + OMAP1_SPI100K_MAX_FREQ/(1<<16)); | |
471 | + return -EINVAL; | |
472 | + } | |
473 | + | |
474 | + } | |
475 | + | |
476 | + spin_lock_irqsave(&spi100k->lock, flags); | |
477 | + list_add_tail(&m->queue, &spi100k->msg_queue); | |
478 | + queue_work(omap1_spi100k_wq, &spi100k->work); | |
479 | + spin_unlock_irqrestore(&spi100k->lock, flags); | |
480 | + | |
481 | + return 0; | |
482 | +} | |
483 | + | |
484 | +static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k) | |
485 | +{ | |
486 | + return 0; | |
487 | +} | |
488 | + | |
489 | +static int __devinit omap1_spi100k_probe(struct platform_device *pdev) | |
490 | +{ | |
491 | + struct spi_master *master; | |
492 | + struct omap1_spi100k *spi100k; | |
493 | + int status = 0; | |
494 | + | |
495 | + if (!pdev->id) | |
496 | + return -EINVAL; | |
497 | + | |
498 | + master = spi_alloc_master(&pdev->dev, sizeof *spi100k); | |
499 | + if (master == NULL) { | |
500 | + dev_dbg(&pdev->dev, "master allocation failed\n"); | |
501 | + return -ENOMEM; | |
502 | + } | |
503 | + | |
504 | + if (pdev->id != -1) | |
505 | + master->bus_num = pdev->id; | |
506 | + | |
507 | + master->setup = omap1_spi100k_setup; | |
508 | + master->transfer = omap1_spi100k_transfer; | |
509 | + master->cleanup = NULL; | |
510 | + master->num_chipselect = 2; | |
511 | + master->mode_bits = MODEBITS; | |
512 | + | |
513 | + dev_set_drvdata(&pdev->dev, master); | |
514 | + | |
515 | + spi100k = spi_master_get_devdata(master); | |
516 | + spi100k->master = master; | |
517 | + | |
518 | + /* | |
519 | + * The memory region base address is taken as the platform_data. | |
520 | + * You should allocate this with ioremap() before initializing | |
521 | + * the SPI. | |
522 | + */ | |
523 | + spi100k->base = (void __iomem *) pdev->dev.platform_data; | |
524 | + | |
525 | + INIT_WORK(&spi100k->work, omap1_spi100k_work); | |
526 | + | |
527 | + spin_lock_init(&spi100k->lock); | |
528 | + INIT_LIST_HEAD(&spi100k->msg_queue); | |
529 | + spi100k->ick = clk_get(&pdev->dev, "ick"); | |
530 | + if (IS_ERR(spi100k->ick)) { | |
531 | + dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); | |
532 | + status = PTR_ERR(spi100k->ick); | |
533 | + goto err1; | |
534 | + } | |
535 | + | |
536 | + spi100k->fck = clk_get(&pdev->dev, "fck"); | |
537 | + if (IS_ERR(spi100k->fck)) { | |
538 | + dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); | |
539 | + status = PTR_ERR(spi100k->fck); | |
540 | + goto err2; | |
541 | + } | |
542 | + | |
543 | + if (omap1_spi100k_reset(spi100k) < 0) | |
544 | + goto err3; | |
545 | + | |
546 | + status = spi_register_master(master); | |
547 | + if (status < 0) | |
548 | + goto err3; | |
549 | + | |
550 | + spi100k->state = SPI_RUNNING; | |
551 | + | |
552 | + return status; | |
553 | + | |
554 | +err3: | |
555 | + clk_put(spi100k->fck); | |
556 | +err2: | |
557 | + clk_put(spi100k->ick); | |
558 | +err1: | |
559 | + spi_master_put(master); | |
560 | + return status; | |
561 | +} | |
562 | + | |
563 | +static int __exit omap1_spi100k_remove(struct platform_device *pdev) | |
564 | +{ | |
565 | + struct spi_master *master; | |
566 | + struct omap1_spi100k *spi100k; | |
567 | + struct resource *r; | |
568 | + unsigned limit = 500; | |
569 | + unsigned long flags; | |
570 | + int status = 0; | |
571 | + | |
572 | + master = dev_get_drvdata(&pdev->dev); | |
573 | + spi100k = spi_master_get_devdata(master); | |
574 | + | |
575 | + spin_lock_irqsave(&spi100k->lock, flags); | |
576 | + | |
577 | + spi100k->state = SPI_SHUTDOWN; | |
578 | + while (!list_empty(&spi100k->msg_queue) && limit--) { | |
579 | + spin_unlock_irqrestore(&spi100k->lock, flags); | |
580 | + msleep(10); | |
581 | + spin_lock_irqsave(&spi100k->lock, flags); | |
582 | + } | |
583 | + | |
584 | + if (!list_empty(&spi100k->msg_queue)) | |
585 | + status = -EBUSY; | |
586 | + | |
587 | + spin_unlock_irqrestore(&spi100k->lock, flags); | |
588 | + | |
589 | + if (status != 0) | |
590 | + return status; | |
591 | + | |
592 | + clk_put(spi100k->fck); | |
593 | + clk_put(spi100k->ick); | |
594 | + | |
595 | + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
596 | + | |
597 | + spi_unregister_master(master); | |
598 | + | |
599 | + return 0; | |
600 | +} | |
601 | + | |
602 | +static struct platform_driver omap1_spi100k_driver = { | |
603 | + .driver = { | |
604 | + .name = "omap1_spi100k", | |
605 | + .owner = THIS_MODULE, | |
606 | + }, | |
607 | + .remove = __exit_p(omap1_spi100k_remove), | |
608 | +}; | |
609 | + | |
610 | + | |
611 | +static int __init omap1_spi100k_init(void) | |
612 | +{ | |
613 | + omap1_spi100k_wq = create_singlethread_workqueue( | |
614 | + omap1_spi100k_driver.driver.name); | |
615 | + | |
616 | + if (omap1_spi100k_wq == NULL) | |
617 | + return -1; | |
618 | + | |
619 | + return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe); | |
620 | +} | |
621 | + | |
622 | +static void __exit omap1_spi100k_exit(void) | |
623 | +{ | |
624 | + platform_driver_unregister(&omap1_spi100k_driver); | |
625 | + | |
626 | + destroy_workqueue(omap1_spi100k_wq); | |
627 | +} | |
628 | + | |
629 | +module_init(omap1_spi100k_init); | |
630 | +module_exit(omap1_spi100k_exit); | |
631 | + | |
632 | +MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); | |
633 | +MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>"); | |
634 | +MODULE_LICENSE("GPL"); |
drivers/spi/spi_imx.c
... | ... | @@ -44,6 +44,9 @@ |
44 | 44 | #define MXC_CSPIINT 0x0c |
45 | 45 | #define MXC_RESET 0x1c |
46 | 46 | |
47 | +#define MX3_CSPISTAT 0x14 | |
48 | +#define MX3_CSPISTAT_RR (1 << 3) | |
49 | + | |
47 | 50 | /* generic defines to abstract from the different register layouts */ |
48 | 51 | #define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */ |
49 | 52 | #define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */ |
... | ... | @@ -205,7 +208,7 @@ |
205 | 208 | |
206 | 209 | if (cpu_is_mx31()) |
207 | 210 | reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; |
208 | - else if (cpu_is_mx35()) { | |
211 | + else if (cpu_is_mx25() || cpu_is_mx35()) { | |
209 | 212 | reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; |
210 | 213 | reg |= MX31_CSPICTRL_SSCTL; |
211 | 214 | } |
... | ... | @@ -219,7 +222,7 @@ |
219 | 222 | if (config->cs < 0) { |
220 | 223 | if (cpu_is_mx31()) |
221 | 224 | reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT; |
222 | - else if (cpu_is_mx35()) | |
225 | + else if (cpu_is_mx25() || cpu_is_mx35()) | |
223 | 226 | reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT; |
224 | 227 | } |
225 | 228 | |
... | ... | @@ -481,7 +484,7 @@ |
481 | 484 | { |
482 | 485 | } |
483 | 486 | |
484 | -static int __init spi_imx_probe(struct platform_device *pdev) | |
487 | +static int __devinit spi_imx_probe(struct platform_device *pdev) | |
485 | 488 | { |
486 | 489 | struct spi_imx_master *mxc_platform_info; |
487 | 490 | struct spi_master *master; |
... | ... | @@ -489,7 +492,7 @@ |
489 | 492 | struct resource *res; |
490 | 493 | int i, ret; |
491 | 494 | |
492 | - mxc_platform_info = (struct spi_imx_master *)pdev->dev.platform_data; | |
495 | + mxc_platform_info = dev_get_platdata(&pdev->dev); | |
493 | 496 | if (!mxc_platform_info) { |
494 | 497 | dev_err(&pdev->dev, "can't get the platform data\n"); |
495 | 498 | return -EINVAL; |
496 | 499 | |
... | ... | @@ -513,11 +516,12 @@ |
513 | 516 | continue; |
514 | 517 | ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME); |
515 | 518 | if (ret) { |
516 | - i--; | |
517 | - while (i > 0) | |
519 | + while (i > 0) { | |
520 | + i--; | |
518 | 521 | if (spi_imx->chipselect[i] >= 0) |
519 | - gpio_free(spi_imx->chipselect[i--]); | |
520 | - dev_err(&pdev->dev, "can't get cs gpios"); | |
522 | + gpio_free(spi_imx->chipselect[i]); | |
523 | + } | |
524 | + dev_err(&pdev->dev, "can't get cs gpios\n"); | |
521 | 525 | goto out_master_put; |
522 | 526 | } |
523 | 527 | } |
... | ... | @@ -551,7 +555,7 @@ |
551 | 555 | } |
552 | 556 | |
553 | 557 | spi_imx->irq = platform_get_irq(pdev, 0); |
554 | - if (!spi_imx->irq) { | |
558 | + if (spi_imx->irq <= 0) { | |
555 | 559 | ret = -EINVAL; |
556 | 560 | goto out_iounmap; |
557 | 561 | } |
... | ... | @@ -562,7 +566,7 @@ |
562 | 566 | goto out_iounmap; |
563 | 567 | } |
564 | 568 | |
565 | - if (cpu_is_mx31() || cpu_is_mx35()) { | |
569 | + if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) { | |
566 | 570 | spi_imx->intctrl = mx31_intctrl; |
567 | 571 | spi_imx->config = mx31_config; |
568 | 572 | spi_imx->trigger = mx31_trigger; |
569 | 573 | |
... | ... | @@ -590,9 +594,14 @@ |
590 | 594 | clk_enable(spi_imx->clk); |
591 | 595 | spi_imx->spi_clk = clk_get_rate(spi_imx->clk); |
592 | 596 | |
593 | - if (!cpu_is_mx31() || !cpu_is_mx35()) | |
597 | + if (cpu_is_mx1() || cpu_is_mx21() || cpu_is_mx27()) | |
594 | 598 | writel(1, spi_imx->base + MXC_RESET); |
595 | 599 | |
600 | + /* drain receive buffer */ | |
601 | + if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) | |
602 | + while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR) | |
603 | + readl(spi_imx->base + MXC_CSPIRXDATA); | |
604 | + | |
596 | 605 | spi_imx->intctrl(spi_imx, 0); |
597 | 606 | |
598 | 607 | ret = spi_bitbang_start(&spi_imx->bitbang); |
... | ... | @@ -625,7 +634,7 @@ |
625 | 634 | return ret; |
626 | 635 | } |
627 | 636 | |
628 | -static int __exit spi_imx_remove(struct platform_device *pdev) | |
637 | +static int __devexit spi_imx_remove(struct platform_device *pdev) | |
629 | 638 | { |
630 | 639 | struct spi_master *master = platform_get_drvdata(pdev); |
631 | 640 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
... | ... | @@ -659,7 +668,7 @@ |
659 | 668 | .owner = THIS_MODULE, |
660 | 669 | }, |
661 | 670 | .probe = spi_imx_probe, |
662 | - .remove = __exit_p(spi_imx_remove), | |
671 | + .remove = __devexit_p(spi_imx_remove), | |
663 | 672 | }; |
664 | 673 | |
665 | 674 | static int __init spi_imx_init(void) |
drivers/spi/spi_mpc8xxx.c
... | ... | @@ -1356,7 +1356,7 @@ |
1356 | 1356 | MODULE_ALIAS("platform:mpc8xxx_spi"); |
1357 | 1357 | static struct platform_driver mpc8xxx_spi_driver = { |
1358 | 1358 | .probe = plat_mpc8xxx_spi_probe, |
1359 | - .remove = __exit_p(plat_mpc8xxx_spi_remove), | |
1359 | + .remove = __devexit_p(plat_mpc8xxx_spi_remove), | |
1360 | 1360 | .driver = { |
1361 | 1361 | .name = "mpc8xxx_spi", |
1362 | 1362 | .owner = THIS_MODULE, |
drivers/spi/spi_nuc900.c
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 | + | |
49 | +struct 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 | + | |
68 | +static inline struct nuc900_spi *to_hw(struct spi_device *sdev) | |
69 | +{ | |
70 | + return spi_master_get_devdata(sdev->master); | |
71 | +} | |
72 | + | |
73 | +static 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 | + | |
109 | +static 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 | + | |
122 | +static 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 | + | |
143 | +static 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 | + | |
160 | +static 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 | + | |
176 | +static int nuc900_spi_setupxfer(struct spi_device *spi, | |
177 | + struct spi_transfer *t) | |
178 | +{ | |
179 | + return 0; | |
180 | +} | |
181 | + | |
182 | +static int nuc900_spi_setup(struct spi_device *spi) | |
183 | +{ | |
184 | + return 0; | |
185 | +} | |
186 | + | |
187 | +static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count) | |
188 | +{ | |
189 | + return hw->tx ? hw->tx[count] : 0; | |
190 | +} | |
191 | + | |
192 | +static 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 | + | |
210 | +static 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 | + | |
240 | +static 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 | + | |
258 | +static 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 | + | |
276 | +static 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 | + | |
294 | +static 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 | + | |
312 | +static 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 | + | |
328 | +static void nuc900_set_divider(struct nuc900_spi *hw) | |
329 | +{ | |
330 | + __raw_writel(hw->pdata->divider, hw->regs + USI_DIV); | |
331 | +} | |
332 | + | |
333 | +static 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 | + | |
348 | +static 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 | + | |
440 | +err_register: | |
441 | + clk_disable(hw->clk); | |
442 | + clk_put(hw->clk); | |
443 | +err_clk: | |
444 | + free_irq(hw->irq, hw); | |
445 | +err_irq: | |
446 | + iounmap(hw->regs); | |
447 | +err_iomap: | |
448 | + release_mem_region(hw->res->start, resource_size(hw->res)); | |
449 | + kfree(hw->ioarea); | |
450 | +err_pdata: | |
451 | + spi_master_put(hw->master);; | |
452 | + | |
453 | +err_nomem: | |
454 | + return err; | |
455 | +} | |
456 | + | |
457 | +static 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 | + | |
479 | +static 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 | + | |
488 | +static int __init nuc900_spi_init(void) | |
489 | +{ | |
490 | + return platform_driver_register(&nuc900_spi_driver); | |
491 | +} | |
492 | + | |
493 | +static void __exit nuc900_spi_exit(void) | |
494 | +{ | |
495 | + platform_driver_unregister(&nuc900_spi_driver); | |
496 | +} | |
497 | + | |
498 | +module_init(nuc900_spi_init); | |
499 | +module_exit(nuc900_spi_exit); | |
500 | + | |
501 | +MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); | |
502 | +MODULE_DESCRIPTION("nuc900 spi driver!"); | |
503 | +MODULE_LICENSE("GPL"); | |
504 | +MODULE_ALIAS("platform:nuc900-spi"); |
drivers/spi/spi_sh_msiof.c
1 | +/* | |
2 | + * SuperH MSIOF SPI Master Interface | |
3 | + * | |
4 | + * Copyright (c) 2009 Magnus Damm | |
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/kernel.h> | |
13 | +#include <linux/init.h> | |
14 | +#include <linux/delay.h> | |
15 | +#include <linux/interrupt.h> | |
16 | +#include <linux/platform_device.h> | |
17 | +#include <linux/completion.h> | |
18 | +#include <linux/pm_runtime.h> | |
19 | +#include <linux/gpio.h> | |
20 | +#include <linux/bitmap.h> | |
21 | +#include <linux/clk.h> | |
22 | +#include <linux/io.h> | |
23 | + | |
24 | +#include <linux/spi/spi.h> | |
25 | +#include <linux/spi/spi_bitbang.h> | |
26 | +#include <linux/spi/sh_msiof.h> | |
27 | + | |
28 | +#include <asm/spi.h> | |
29 | +#include <asm/unaligned.h> | |
30 | + | |
31 | +struct sh_msiof_spi_priv { | |
32 | + struct spi_bitbang bitbang; /* must be first for spi_bitbang.c */ | |
33 | + void __iomem *mapbase; | |
34 | + struct clk *clk; | |
35 | + struct platform_device *pdev; | |
36 | + struct sh_msiof_spi_info *info; | |
37 | + struct completion done; | |
38 | + unsigned long flags; | |
39 | + int tx_fifo_size; | |
40 | + int rx_fifo_size; | |
41 | +}; | |
42 | + | |
43 | +#define TMDR1 0x00 | |
44 | +#define TMDR2 0x04 | |
45 | +#define TMDR3 0x08 | |
46 | +#define RMDR1 0x10 | |
47 | +#define RMDR2 0x14 | |
48 | +#define RMDR3 0x18 | |
49 | +#define TSCR 0x20 | |
50 | +#define RSCR 0x22 | |
51 | +#define CTR 0x28 | |
52 | +#define FCTR 0x30 | |
53 | +#define STR 0x40 | |
54 | +#define IER 0x44 | |
55 | +#define TDR1 0x48 | |
56 | +#define TDR2 0x4c | |
57 | +#define TFDR 0x50 | |
58 | +#define RDR1 0x58 | |
59 | +#define RDR2 0x5c | |
60 | +#define RFDR 0x60 | |
61 | + | |
62 | +#define CTR_TSCKE (1 << 15) | |
63 | +#define CTR_TFSE (1 << 14) | |
64 | +#define CTR_TXE (1 << 9) | |
65 | +#define CTR_RXE (1 << 8) | |
66 | + | |
67 | +#define STR_TEOF (1 << 23) | |
68 | +#define STR_REOF (1 << 7) | |
69 | + | |
70 | +static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs) | |
71 | +{ | |
72 | + switch (reg_offs) { | |
73 | + case TSCR: | |
74 | + case RSCR: | |
75 | + return ioread16(p->mapbase + reg_offs); | |
76 | + default: | |
77 | + return ioread32(p->mapbase + reg_offs); | |
78 | + } | |
79 | +} | |
80 | + | |
81 | +static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs, | |
82 | + unsigned long value) | |
83 | +{ | |
84 | + switch (reg_offs) { | |
85 | + case TSCR: | |
86 | + case RSCR: | |
87 | + iowrite16(value, p->mapbase + reg_offs); | |
88 | + break; | |
89 | + default: | |
90 | + iowrite32(value, p->mapbase + reg_offs); | |
91 | + break; | |
92 | + } | |
93 | +} | |
94 | + | |
95 | +static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p, | |
96 | + unsigned long clr, unsigned long set) | |
97 | +{ | |
98 | + unsigned long mask = clr | set; | |
99 | + unsigned long data; | |
100 | + int k; | |
101 | + | |
102 | + data = sh_msiof_read(p, CTR); | |
103 | + data &= ~clr; | |
104 | + data |= set; | |
105 | + sh_msiof_write(p, CTR, data); | |
106 | + | |
107 | + for (k = 100; k > 0; k--) { | |
108 | + if ((sh_msiof_read(p, CTR) & mask) == set) | |
109 | + break; | |
110 | + | |
111 | + udelay(10); | |
112 | + } | |
113 | + | |
114 | + return k > 0 ? 0 : -ETIMEDOUT; | |
115 | +} | |
116 | + | |
117 | +static irqreturn_t sh_msiof_spi_irq(int irq, void *data) | |
118 | +{ | |
119 | + struct sh_msiof_spi_priv *p = data; | |
120 | + | |
121 | + /* just disable the interrupt and wake up */ | |
122 | + sh_msiof_write(p, IER, 0); | |
123 | + complete(&p->done); | |
124 | + | |
125 | + return IRQ_HANDLED; | |
126 | +} | |
127 | + | |
128 | +static struct { | |
129 | + unsigned short div; | |
130 | + unsigned short scr; | |
131 | +} const sh_msiof_spi_clk_table[] = { | |
132 | + { 1, 0x0007 }, | |
133 | + { 2, 0x0000 }, | |
134 | + { 4, 0x0001 }, | |
135 | + { 8, 0x0002 }, | |
136 | + { 16, 0x0003 }, | |
137 | + { 32, 0x0004 }, | |
138 | + { 64, 0x1f00 }, | |
139 | + { 128, 0x1f01 }, | |
140 | + { 256, 0x1f02 }, | |
141 | + { 512, 0x1f03 }, | |
142 | + { 1024, 0x1f04 }, | |
143 | +}; | |
144 | + | |
145 | +static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, | |
146 | + unsigned long parent_rate, | |
147 | + unsigned long spi_hz) | |
148 | +{ | |
149 | + unsigned long div = 1024; | |
150 | + size_t k; | |
151 | + | |
152 | + if (!WARN_ON(!spi_hz || !parent_rate)) | |
153 | + div = parent_rate / spi_hz; | |
154 | + | |
155 | + /* TODO: make more fine grained */ | |
156 | + | |
157 | + for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_clk_table); k++) { | |
158 | + if (sh_msiof_spi_clk_table[k].div >= div) | |
159 | + break; | |
160 | + } | |
161 | + | |
162 | + k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1); | |
163 | + | |
164 | + sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr); | |
165 | + sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr); | |
166 | +} | |
167 | + | |
168 | +static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, | |
169 | + int cpol, int cpha, | |
170 | + int tx_hi_z, int lsb_first) | |
171 | +{ | |
172 | + unsigned long tmp; | |
173 | + int edge; | |
174 | + | |
175 | + /* | |
176 | + * CPOL CPHA TSCKIZ RSCKIZ TEDG REDG(!) | |
177 | + * 0 0 10 10 1 0 | |
178 | + * 0 1 10 10 0 1 | |
179 | + * 1 0 11 11 0 1 | |
180 | + * 1 1 11 11 1 0 | |
181 | + * | |
182 | + * (!) Note: REDG is inverted recommended data sheet setting | |
183 | + */ | |
184 | + | |
185 | + sh_msiof_write(p, FCTR, 0); | |
186 | + sh_msiof_write(p, TMDR1, 0xe2000005 | (lsb_first << 24)); | |
187 | + sh_msiof_write(p, RMDR1, 0x22000005 | (lsb_first << 24)); | |
188 | + | |
189 | + tmp = 0xa0000000; | |
190 | + tmp |= cpol << 30; /* TSCKIZ */ | |
191 | + tmp |= cpol << 28; /* RSCKIZ */ | |
192 | + | |
193 | + edge = cpol ? cpha : !cpha; | |
194 | + | |
195 | + tmp |= edge << 27; /* TEDG */ | |
196 | + tmp |= !edge << 26; /* REDG */ | |
197 | + tmp |= (tx_hi_z ? 2 : 0) << 22; /* TXDIZ */ | |
198 | + sh_msiof_write(p, CTR, tmp); | |
199 | +} | |
200 | + | |
201 | +static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, | |
202 | + const void *tx_buf, void *rx_buf, | |
203 | + int bits, int words) | |
204 | +{ | |
205 | + unsigned long dr2; | |
206 | + | |
207 | + dr2 = ((bits - 1) << 24) | ((words - 1) << 16); | |
208 | + | |
209 | + if (tx_buf) | |
210 | + sh_msiof_write(p, TMDR2, dr2); | |
211 | + else | |
212 | + sh_msiof_write(p, TMDR2, dr2 | 1); | |
213 | + | |
214 | + if (rx_buf) | |
215 | + sh_msiof_write(p, RMDR2, dr2); | |
216 | + | |
217 | + sh_msiof_write(p, IER, STR_TEOF | STR_REOF); | |
218 | +} | |
219 | + | |
220 | +static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p) | |
221 | +{ | |
222 | + sh_msiof_write(p, STR, sh_msiof_read(p, STR)); | |
223 | +} | |
224 | + | |
225 | +static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p, | |
226 | + const void *tx_buf, int words, int fs) | |
227 | +{ | |
228 | + const unsigned char *buf_8 = tx_buf; | |
229 | + int k; | |
230 | + | |
231 | + for (k = 0; k < words; k++) | |
232 | + sh_msiof_write(p, TFDR, buf_8[k] << fs); | |
233 | +} | |
234 | + | |
235 | +static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p, | |
236 | + const void *tx_buf, int words, int fs) | |
237 | +{ | |
238 | + const unsigned short *buf_16 = tx_buf; | |
239 | + int k; | |
240 | + | |
241 | + for (k = 0; k < words; k++) | |
242 | + sh_msiof_write(p, TFDR, buf_16[k] << fs); | |
243 | +} | |
244 | + | |
245 | +static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p, | |
246 | + const void *tx_buf, int words, int fs) | |
247 | +{ | |
248 | + const unsigned short *buf_16 = tx_buf; | |
249 | + int k; | |
250 | + | |
251 | + for (k = 0; k < words; k++) | |
252 | + sh_msiof_write(p, TFDR, get_unaligned(&buf_16[k]) << fs); | |
253 | +} | |
254 | + | |
255 | +static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p, | |
256 | + const void *tx_buf, int words, int fs) | |
257 | +{ | |
258 | + const unsigned int *buf_32 = tx_buf; | |
259 | + int k; | |
260 | + | |
261 | + for (k = 0; k < words; k++) | |
262 | + sh_msiof_write(p, TFDR, buf_32[k] << fs); | |
263 | +} | |
264 | + | |
265 | +static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p, | |
266 | + const void *tx_buf, int words, int fs) | |
267 | +{ | |
268 | + const unsigned int *buf_32 = tx_buf; | |
269 | + int k; | |
270 | + | |
271 | + for (k = 0; k < words; k++) | |
272 | + sh_msiof_write(p, TFDR, get_unaligned(&buf_32[k]) << fs); | |
273 | +} | |
274 | + | |
275 | +static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p, | |
276 | + void *rx_buf, int words, int fs) | |
277 | +{ | |
278 | + unsigned char *buf_8 = rx_buf; | |
279 | + int k; | |
280 | + | |
281 | + for (k = 0; k < words; k++) | |
282 | + buf_8[k] = sh_msiof_read(p, RFDR) >> fs; | |
283 | +} | |
284 | + | |
285 | +static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p, | |
286 | + void *rx_buf, int words, int fs) | |
287 | +{ | |
288 | + unsigned short *buf_16 = rx_buf; | |
289 | + int k; | |
290 | + | |
291 | + for (k = 0; k < words; k++) | |
292 | + buf_16[k] = sh_msiof_read(p, RFDR) >> fs; | |
293 | +} | |
294 | + | |
295 | +static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p, | |
296 | + void *rx_buf, int words, int fs) | |
297 | +{ | |
298 | + unsigned short *buf_16 = rx_buf; | |
299 | + int k; | |
300 | + | |
301 | + for (k = 0; k < words; k++) | |
302 | + put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_16[k]); | |
303 | +} | |
304 | + | |
305 | +static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p, | |
306 | + void *rx_buf, int words, int fs) | |
307 | +{ | |
308 | + unsigned int *buf_32 = rx_buf; | |
309 | + int k; | |
310 | + | |
311 | + for (k = 0; k < words; k++) | |
312 | + buf_32[k] = sh_msiof_read(p, RFDR) >> fs; | |
313 | +} | |
314 | + | |
315 | +static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p, | |
316 | + void *rx_buf, int words, int fs) | |
317 | +{ | |
318 | + unsigned int *buf_32 = rx_buf; | |
319 | + int k; | |
320 | + | |
321 | + for (k = 0; k < words; k++) | |
322 | + put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]); | |
323 | +} | |
324 | + | |
325 | +static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t) | |
326 | +{ | |
327 | + int bits; | |
328 | + | |
329 | + bits = t ? t->bits_per_word : 0; | |
330 | + bits = bits ? bits : spi->bits_per_word; | |
331 | + return bits; | |
332 | +} | |
333 | + | |
334 | +static unsigned long sh_msiof_spi_hz(struct spi_device *spi, | |
335 | + struct spi_transfer *t) | |
336 | +{ | |
337 | + unsigned long hz; | |
338 | + | |
339 | + hz = t ? t->speed_hz : 0; | |
340 | + hz = hz ? hz : spi->max_speed_hz; | |
341 | + return hz; | |
342 | +} | |
343 | + | |
344 | +static int sh_msiof_spi_setup_transfer(struct spi_device *spi, | |
345 | + struct spi_transfer *t) | |
346 | +{ | |
347 | + int bits; | |
348 | + | |
349 | + /* noting to check hz values against since parent clock is disabled */ | |
350 | + | |
351 | + bits = sh_msiof_spi_bits(spi, t); | |
352 | + if (bits < 8) | |
353 | + return -EINVAL; | |
354 | + if (bits > 32) | |
355 | + return -EINVAL; | |
356 | + | |
357 | + return spi_bitbang_setup_transfer(spi, t); | |
358 | +} | |
359 | + | |
360 | +static void sh_msiof_spi_chipselect(struct spi_device *spi, int is_on) | |
361 | +{ | |
362 | + struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master); | |
363 | + int value; | |
364 | + | |
365 | + /* chip select is active low unless SPI_CS_HIGH is set */ | |
366 | + if (spi->mode & SPI_CS_HIGH) | |
367 | + value = (is_on == BITBANG_CS_ACTIVE) ? 1 : 0; | |
368 | + else | |
369 | + value = (is_on == BITBANG_CS_ACTIVE) ? 0 : 1; | |
370 | + | |
371 | + if (is_on == BITBANG_CS_ACTIVE) { | |
372 | + if (!test_and_set_bit(0, &p->flags)) { | |
373 | + pm_runtime_get_sync(&p->pdev->dev); | |
374 | + clk_enable(p->clk); | |
375 | + } | |
376 | + | |
377 | + /* Configure pins before asserting CS */ | |
378 | + sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL), | |
379 | + !!(spi->mode & SPI_CPHA), | |
380 | + !!(spi->mode & SPI_3WIRE), | |
381 | + !!(spi->mode & SPI_LSB_FIRST)); | |
382 | + } | |
383 | + | |
384 | + /* use spi->controller data for CS (same strategy as spi_gpio) */ | |
385 | + gpio_set_value((unsigned)spi->controller_data, value); | |
386 | + | |
387 | + if (is_on == BITBANG_CS_INACTIVE) { | |
388 | + if (test_and_clear_bit(0, &p->flags)) { | |
389 | + clk_disable(p->clk); | |
390 | + pm_runtime_put(&p->pdev->dev); | |
391 | + } | |
392 | + } | |
393 | +} | |
394 | + | |
395 | +static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, | |
396 | + void (*tx_fifo)(struct sh_msiof_spi_priv *, | |
397 | + const void *, int, int), | |
398 | + void (*rx_fifo)(struct sh_msiof_spi_priv *, | |
399 | + void *, int, int), | |
400 | + const void *tx_buf, void *rx_buf, | |
401 | + int words, int bits) | |
402 | +{ | |
403 | + int fifo_shift; | |
404 | + int ret; | |
405 | + | |
406 | + /* limit maximum word transfer to rx/tx fifo size */ | |
407 | + if (tx_buf) | |
408 | + words = min_t(int, words, p->tx_fifo_size); | |
409 | + if (rx_buf) | |
410 | + words = min_t(int, words, p->rx_fifo_size); | |
411 | + | |
412 | + /* the fifo contents need shifting */ | |
413 | + fifo_shift = 32 - bits; | |
414 | + | |
415 | + /* setup msiof transfer mode registers */ | |
416 | + sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words); | |
417 | + | |
418 | + /* write tx fifo */ | |
419 | + if (tx_buf) | |
420 | + tx_fifo(p, tx_buf, words, fifo_shift); | |
421 | + | |
422 | + /* setup clock and rx/tx signals */ | |
423 | + ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE); | |
424 | + if (rx_buf) | |
425 | + ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_RXE); | |
426 | + ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TXE); | |
427 | + | |
428 | + /* start by setting frame bit */ | |
429 | + INIT_COMPLETION(p->done); | |
430 | + ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE); | |
431 | + if (ret) { | |
432 | + dev_err(&p->pdev->dev, "failed to start hardware\n"); | |
433 | + goto err; | |
434 | + } | |
435 | + | |
436 | + /* wait for tx fifo to be emptied / rx fifo to be filled */ | |
437 | + wait_for_completion(&p->done); | |
438 | + | |
439 | + /* read rx fifo */ | |
440 | + if (rx_buf) | |
441 | + rx_fifo(p, rx_buf, words, fifo_shift); | |
442 | + | |
443 | + /* clear status bits */ | |
444 | + sh_msiof_reset_str(p); | |
445 | + | |
446 | + /* shut down frame, tx/tx and clock signals */ | |
447 | + ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0); | |
448 | + ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0); | |
449 | + if (rx_buf) | |
450 | + ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_RXE, 0); | |
451 | + ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0); | |
452 | + if (ret) { | |
453 | + dev_err(&p->pdev->dev, "failed to shut down hardware\n"); | |
454 | + goto err; | |
455 | + } | |
456 | + | |
457 | + return words; | |
458 | + | |
459 | + err: | |
460 | + sh_msiof_write(p, IER, 0); | |
461 | + return ret; | |
462 | +} | |
463 | + | |
464 | +static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) | |
465 | +{ | |
466 | + struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master); | |
467 | + void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int); | |
468 | + void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int); | |
469 | + int bits; | |
470 | + int bytes_per_word; | |
471 | + int bytes_done; | |
472 | + int words; | |
473 | + int n; | |
474 | + | |
475 | + bits = sh_msiof_spi_bits(spi, t); | |
476 | + | |
477 | + /* setup bytes per word and fifo read/write functions */ | |
478 | + if (bits <= 8) { | |
479 | + bytes_per_word = 1; | |
480 | + tx_fifo = sh_msiof_spi_write_fifo_8; | |
481 | + rx_fifo = sh_msiof_spi_read_fifo_8; | |
482 | + } else if (bits <= 16) { | |
483 | + bytes_per_word = 2; | |
484 | + if ((unsigned long)t->tx_buf & 0x01) | |
485 | + tx_fifo = sh_msiof_spi_write_fifo_16u; | |
486 | + else | |
487 | + tx_fifo = sh_msiof_spi_write_fifo_16; | |
488 | + | |
489 | + if ((unsigned long)t->rx_buf & 0x01) | |
490 | + rx_fifo = sh_msiof_spi_read_fifo_16u; | |
491 | + else | |
492 | + rx_fifo = sh_msiof_spi_read_fifo_16; | |
493 | + } else { | |
494 | + bytes_per_word = 4; | |
495 | + if ((unsigned long)t->tx_buf & 0x03) | |
496 | + tx_fifo = sh_msiof_spi_write_fifo_32u; | |
497 | + else | |
498 | + tx_fifo = sh_msiof_spi_write_fifo_32; | |
499 | + | |
500 | + if ((unsigned long)t->rx_buf & 0x03) | |
501 | + rx_fifo = sh_msiof_spi_read_fifo_32u; | |
502 | + else | |
503 | + rx_fifo = sh_msiof_spi_read_fifo_32; | |
504 | + } | |
505 | + | |
506 | + /* setup clocks (clock already enabled in chipselect()) */ | |
507 | + sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), | |
508 | + sh_msiof_spi_hz(spi, t)); | |
509 | + | |
510 | + /* transfer in fifo sized chunks */ | |
511 | + words = t->len / bytes_per_word; | |
512 | + bytes_done = 0; | |
513 | + | |
514 | + while (bytes_done < t->len) { | |
515 | + n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo, | |
516 | + t->tx_buf + bytes_done, | |
517 | + t->rx_buf + bytes_done, | |
518 | + words, bits); | |
519 | + if (n < 0) | |
520 | + break; | |
521 | + | |
522 | + bytes_done += n * bytes_per_word; | |
523 | + words -= n; | |
524 | + } | |
525 | + | |
526 | + return bytes_done; | |
527 | +} | |
528 | + | |
529 | +static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs, | |
530 | + u32 word, u8 bits) | |
531 | +{ | |
532 | + BUG(); /* unused but needed by bitbang code */ | |
533 | + return 0; | |
534 | +} | |
535 | + | |
536 | +static int sh_msiof_spi_probe(struct platform_device *pdev) | |
537 | +{ | |
538 | + struct resource *r; | |
539 | + struct spi_master *master; | |
540 | + struct sh_msiof_spi_priv *p; | |
541 | + char clk_name[16]; | |
542 | + int i; | |
543 | + int ret; | |
544 | + | |
545 | + master = spi_alloc_master(&pdev->dev, sizeof(struct sh_msiof_spi_priv)); | |
546 | + if (master == NULL) { | |
547 | + dev_err(&pdev->dev, "failed to allocate spi master\n"); | |
548 | + ret = -ENOMEM; | |
549 | + goto err0; | |
550 | + } | |
551 | + | |
552 | + p = spi_master_get_devdata(master); | |
553 | + | |
554 | + platform_set_drvdata(pdev, p); | |
555 | + p->info = pdev->dev.platform_data; | |
556 | + init_completion(&p->done); | |
557 | + | |
558 | + snprintf(clk_name, sizeof(clk_name), "msiof%d", pdev->id); | |
559 | + p->clk = clk_get(&pdev->dev, clk_name); | |
560 | + if (IS_ERR(p->clk)) { | |
561 | + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); | |
562 | + ret = PTR_ERR(p->clk); | |
563 | + goto err1; | |
564 | + } | |
565 | + | |
566 | + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
567 | + i = platform_get_irq(pdev, 0); | |
568 | + if (!r || i < 0) { | |
569 | + dev_err(&pdev->dev, "cannot get platform resources\n"); | |
570 | + ret = -ENOENT; | |
571 | + goto err2; | |
572 | + } | |
573 | + p->mapbase = ioremap_nocache(r->start, resource_size(r)); | |
574 | + if (!p->mapbase) { | |
575 | + dev_err(&pdev->dev, "unable to ioremap\n"); | |
576 | + ret = -ENXIO; | |
577 | + goto err2; | |
578 | + } | |
579 | + | |
580 | + ret = request_irq(i, sh_msiof_spi_irq, IRQF_DISABLED, | |
581 | + dev_name(&pdev->dev), p); | |
582 | + if (ret) { | |
583 | + dev_err(&pdev->dev, "unable to request irq\n"); | |
584 | + goto err3; | |
585 | + } | |
586 | + | |
587 | + p->pdev = pdev; | |
588 | + pm_runtime_enable(&pdev->dev); | |
589 | + | |
590 | + /* The standard version of MSIOF use 64 word FIFOs */ | |
591 | + p->tx_fifo_size = 64; | |
592 | + p->rx_fifo_size = 64; | |
593 | + | |
594 | + /* Platform data may override FIFO sizes */ | |
595 | + if (p->info->tx_fifo_override) | |
596 | + p->tx_fifo_size = p->info->tx_fifo_override; | |
597 | + if (p->info->rx_fifo_override) | |
598 | + p->rx_fifo_size = p->info->rx_fifo_override; | |
599 | + | |
600 | + /* init master and bitbang code */ | |
601 | + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; | |
602 | + master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE; | |
603 | + master->flags = 0; | |
604 | + master->bus_num = pdev->id; | |
605 | + master->num_chipselect = p->info->num_chipselect; | |
606 | + master->setup = spi_bitbang_setup; | |
607 | + master->cleanup = spi_bitbang_cleanup; | |
608 | + | |
609 | + p->bitbang.master = master; | |
610 | + p->bitbang.chipselect = sh_msiof_spi_chipselect; | |
611 | + p->bitbang.setup_transfer = sh_msiof_spi_setup_transfer; | |
612 | + p->bitbang.txrx_bufs = sh_msiof_spi_txrx; | |
613 | + p->bitbang.txrx_word[SPI_MODE_0] = sh_msiof_spi_txrx_word; | |
614 | + p->bitbang.txrx_word[SPI_MODE_1] = sh_msiof_spi_txrx_word; | |
615 | + p->bitbang.txrx_word[SPI_MODE_2] = sh_msiof_spi_txrx_word; | |
616 | + p->bitbang.txrx_word[SPI_MODE_3] = sh_msiof_spi_txrx_word; | |
617 | + | |
618 | + ret = spi_bitbang_start(&p->bitbang); | |
619 | + if (ret == 0) | |
620 | + return 0; | |
621 | + | |
622 | + pm_runtime_disable(&pdev->dev); | |
623 | + err3: | |
624 | + iounmap(p->mapbase); | |
625 | + err2: | |
626 | + clk_put(p->clk); | |
627 | + err1: | |
628 | + spi_master_put(master); | |
629 | + err0: | |
630 | + return ret; | |
631 | +} | |
632 | + | |
633 | +static int sh_msiof_spi_remove(struct platform_device *pdev) | |
634 | +{ | |
635 | + struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev); | |
636 | + int ret; | |
637 | + | |
638 | + ret = spi_bitbang_stop(&p->bitbang); | |
639 | + if (!ret) { | |
640 | + pm_runtime_disable(&pdev->dev); | |
641 | + free_irq(platform_get_irq(pdev, 0), sh_msiof_spi_irq); | |
642 | + iounmap(p->mapbase); | |
643 | + clk_put(p->clk); | |
644 | + spi_master_put(p->bitbang.master); | |
645 | + } | |
646 | + return ret; | |
647 | +} | |
648 | + | |
649 | +static int sh_msiof_spi_runtime_nop(struct device *dev) | |
650 | +{ | |
651 | + /* Runtime PM callback shared between ->runtime_suspend() | |
652 | + * and ->runtime_resume(). Simply returns success. | |
653 | + * | |
654 | + * This driver re-initializes all registers after | |
655 | + * pm_runtime_get_sync() anyway so there is no need | |
656 | + * to save and restore registers here. | |
657 | + */ | |
658 | + return 0; | |
659 | +} | |
660 | + | |
661 | +static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = { | |
662 | + .runtime_suspend = sh_msiof_spi_runtime_nop, | |
663 | + .runtime_resume = sh_msiof_spi_runtime_nop, | |
664 | +}; | |
665 | + | |
666 | +static struct platform_driver sh_msiof_spi_drv = { | |
667 | + .probe = sh_msiof_spi_probe, | |
668 | + .remove = sh_msiof_spi_remove, | |
669 | + .driver = { | |
670 | + .name = "spi_sh_msiof", | |
671 | + .owner = THIS_MODULE, | |
672 | + .pm = &sh_msiof_spi_dev_pm_ops, | |
673 | + }, | |
674 | +}; | |
675 | + | |
676 | +static int __init sh_msiof_spi_init(void) | |
677 | +{ | |
678 | + return platform_driver_register(&sh_msiof_spi_drv); | |
679 | +} | |
680 | +module_init(sh_msiof_spi_init); | |
681 | + | |
682 | +static void __exit sh_msiof_spi_exit(void) | |
683 | +{ | |
684 | + platform_driver_unregister(&sh_msiof_spi_drv); | |
685 | +} | |
686 | +module_exit(sh_msiof_spi_exit); | |
687 | + | |
688 | +MODULE_DESCRIPTION("SuperH MSIOF SPI Master Interface Driver"); | |
689 | +MODULE_AUTHOR("Magnus Damm"); | |
690 | +MODULE_LICENSE("GPL v2"); | |
691 | +MODULE_ALIAS("platform:spi_sh_msiof"); |
drivers/spi/spidev.c
... | ... | @@ -266,15 +266,15 @@ |
266 | 266 | k_tmp->delay_usecs = u_tmp->delay_usecs; |
267 | 267 | k_tmp->speed_hz = u_tmp->speed_hz; |
268 | 268 | #ifdef VERBOSE |
269 | - dev_dbg(&spi->dev, | |
269 | + dev_dbg(&spidev->spi->dev, | |
270 | 270 | " xfer len %zd %s%s%s%dbits %u usec %uHz\n", |
271 | 271 | u_tmp->len, |
272 | 272 | u_tmp->rx_buf ? "rx " : "", |
273 | 273 | u_tmp->tx_buf ? "tx " : "", |
274 | 274 | u_tmp->cs_change ? "cs " : "", |
275 | - u_tmp->bits_per_word ? : spi->bits_per_word, | |
275 | + u_tmp->bits_per_word ? : spidev->spi->bits_per_word, | |
276 | 276 | u_tmp->delay_usecs, |
277 | - u_tmp->speed_hz ? : spi->max_speed_hz); | |
277 | + u_tmp->speed_hz ? : spidev->spi->max_speed_hz); | |
278 | 278 | #endif |
279 | 279 | spi_message_add_tail(k_tmp, &msg); |
280 | 280 | } |
drivers/spi/xilinx_spi.c
... | ... | @@ -14,22 +14,20 @@ |
14 | 14 | #include <linux/module.h> |
15 | 15 | #include <linux/init.h> |
16 | 16 | #include <linux/interrupt.h> |
17 | -#include <linux/platform_device.h> | |
18 | 17 | |
19 | -#include <linux/of_platform.h> | |
20 | -#include <linux/of_device.h> | |
21 | -#include <linux/of_spi.h> | |
22 | - | |
23 | 18 | #include <linux/spi/spi.h> |
24 | 19 | #include <linux/spi/spi_bitbang.h> |
25 | 20 | #include <linux/io.h> |
26 | 21 | |
22 | +#include "xilinx_spi.h" | |
23 | +#include <linux/spi/xilinx_spi.h> | |
24 | + | |
27 | 25 | #define XILINX_SPI_NAME "xilinx_spi" |
28 | 26 | |
29 | 27 | /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) |
30 | 28 | * Product Specification", DS464 |
31 | 29 | */ |
32 | -#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */ | |
30 | +#define XSPI_CR_OFFSET 0x60 /* Control Register */ | |
33 | 31 | |
34 | 32 | #define XSPI_CR_ENABLE 0x02 |
35 | 33 | #define XSPI_CR_MASTER_MODE 0x04 |
36 | 34 | |
... | ... | @@ -40,8 +38,9 @@ |
40 | 38 | #define XSPI_CR_RXFIFO_RESET 0x40 |
41 | 39 | #define XSPI_CR_MANUAL_SSELECT 0x80 |
42 | 40 | #define XSPI_CR_TRANS_INHIBIT 0x100 |
41 | +#define XSPI_CR_LSB_FIRST 0x200 | |
43 | 42 | |
44 | -#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */ | |
43 | +#define XSPI_SR_OFFSET 0x64 /* Status Register */ | |
45 | 44 | |
46 | 45 | #define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */ |
47 | 46 | #define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */ |
... | ... | @@ -49,8 +48,8 @@ |
49 | 48 | #define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */ |
50 | 49 | #define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */ |
51 | 50 | |
52 | -#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */ | |
53 | -#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */ | |
51 | +#define XSPI_TXD_OFFSET 0x68 /* Data Transmit Register */ | |
52 | +#define XSPI_RXD_OFFSET 0x6c /* Data Receive Register */ | |
54 | 53 | |
55 | 54 | #define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */ |
56 | 55 | |
... | ... | @@ -70,6 +69,7 @@ |
70 | 69 | #define XSPI_INTR_TX_UNDERRUN 0x08 /* TxFIFO was underrun */ |
71 | 70 | #define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */ |
72 | 71 | #define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */ |
72 | +#define XSPI_INTR_TX_HALF_EMPTY 0x40 /* TxFIFO is half empty */ | |
73 | 73 | |
74 | 74 | #define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */ |
75 | 75 | #define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */ |
76 | 76 | |
77 | 77 | |
78 | 78 | |
79 | 79 | |
80 | 80 | |
81 | 81 | |
82 | 82 | |
83 | 83 | |
84 | 84 | |
... | ... | @@ -78,35 +78,85 @@ |
78 | 78 | /* bitbang has to be first */ |
79 | 79 | struct spi_bitbang bitbang; |
80 | 80 | struct completion done; |
81 | - | |
81 | + struct resource mem; /* phys mem */ | |
82 | 82 | void __iomem *regs; /* virt. address of the control registers */ |
83 | 83 | |
84 | 84 | u32 irq; |
85 | 85 | |
86 | - u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */ | |
87 | - | |
88 | 86 | u8 *rx_ptr; /* pointer in the Tx buffer */ |
89 | 87 | const u8 *tx_ptr; /* pointer in the Rx buffer */ |
90 | 88 | int remaining_bytes; /* the number of bytes left to transfer */ |
89 | + u8 bits_per_word; | |
90 | + unsigned int (*read_fn) (void __iomem *); | |
91 | + void (*write_fn) (u32, void __iomem *); | |
92 | + void (*tx_fn) (struct xilinx_spi *); | |
93 | + void (*rx_fn) (struct xilinx_spi *); | |
91 | 94 | }; |
92 | 95 | |
93 | -static void xspi_init_hw(void __iomem *regs_base) | |
96 | +static void xspi_tx8(struct xilinx_spi *xspi) | |
94 | 97 | { |
98 | + xspi->write_fn(*xspi->tx_ptr, xspi->regs + XSPI_TXD_OFFSET); | |
99 | + xspi->tx_ptr++; | |
100 | +} | |
101 | + | |
102 | +static void xspi_tx16(struct xilinx_spi *xspi) | |
103 | +{ | |
104 | + xspi->write_fn(*(u16 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET); | |
105 | + xspi->tx_ptr += 2; | |
106 | +} | |
107 | + | |
108 | +static void xspi_tx32(struct xilinx_spi *xspi) | |
109 | +{ | |
110 | + xspi->write_fn(*(u32 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET); | |
111 | + xspi->tx_ptr += 4; | |
112 | +} | |
113 | + | |
114 | +static void xspi_rx8(struct xilinx_spi *xspi) | |
115 | +{ | |
116 | + u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); | |
117 | + if (xspi->rx_ptr) { | |
118 | + *xspi->rx_ptr = data & 0xff; | |
119 | + xspi->rx_ptr++; | |
120 | + } | |
121 | +} | |
122 | + | |
123 | +static void xspi_rx16(struct xilinx_spi *xspi) | |
124 | +{ | |
125 | + u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); | |
126 | + if (xspi->rx_ptr) { | |
127 | + *(u16 *)(xspi->rx_ptr) = data & 0xffff; | |
128 | + xspi->rx_ptr += 2; | |
129 | + } | |
130 | +} | |
131 | + | |
132 | +static void xspi_rx32(struct xilinx_spi *xspi) | |
133 | +{ | |
134 | + u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); | |
135 | + if (xspi->rx_ptr) { | |
136 | + *(u32 *)(xspi->rx_ptr) = data; | |
137 | + xspi->rx_ptr += 4; | |
138 | + } | |
139 | +} | |
140 | + | |
141 | +static void xspi_init_hw(struct xilinx_spi *xspi) | |
142 | +{ | |
143 | + void __iomem *regs_base = xspi->regs; | |
144 | + | |
95 | 145 | /* Reset the SPI device */ |
96 | - out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET, | |
97 | - XIPIF_V123B_RESET_MASK); | |
146 | + xspi->write_fn(XIPIF_V123B_RESET_MASK, | |
147 | + regs_base + XIPIF_V123B_RESETR_OFFSET); | |
98 | 148 | /* Disable all the interrupts just in case */ |
99 | - out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0); | |
149 | + xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET); | |
100 | 150 | /* Enable the global IPIF interrupt */ |
101 | - out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET, | |
102 | - XIPIF_V123B_GINTR_ENABLE); | |
151 | + xspi->write_fn(XIPIF_V123B_GINTR_ENABLE, | |
152 | + regs_base + XIPIF_V123B_DGIER_OFFSET); | |
103 | 153 | /* Deselect the slave on the SPI bus */ |
104 | - out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff); | |
154 | + xspi->write_fn(0xffff, regs_base + XSPI_SSR_OFFSET); | |
105 | 155 | /* Disable the transmitter, enable Manual Slave Select Assertion, |
106 | 156 | * put SPI controller into master mode, and enable it */ |
107 | - out_be16(regs_base + XSPI_CR_OFFSET, | |
108 | - XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | |
109 | - | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE); | |
157 | + xspi->write_fn(XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | | |
158 | + XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | | |
159 | + XSPI_CR_RXFIFO_RESET, regs_base + XSPI_CR_OFFSET); | |
110 | 160 | } |
111 | 161 | |
112 | 162 | static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) |
113 | 163 | |
114 | 164 | |
... | ... | @@ -115,16 +165,16 @@ |
115 | 165 | |
116 | 166 | if (is_on == BITBANG_CS_INACTIVE) { |
117 | 167 | /* Deselect the slave on the SPI bus */ |
118 | - out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff); | |
168 | + xspi->write_fn(0xffff, xspi->regs + XSPI_SSR_OFFSET); | |
119 | 169 | } else if (is_on == BITBANG_CS_ACTIVE) { |
120 | 170 | /* Set the SPI clock phase and polarity */ |
121 | - u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET) | |
171 | + u16 cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) | |
122 | 172 | & ~XSPI_CR_MODE_MASK; |
123 | 173 | if (spi->mode & SPI_CPHA) |
124 | 174 | cr |= XSPI_CR_CPHA; |
125 | 175 | if (spi->mode & SPI_CPOL) |
126 | 176 | cr |= XSPI_CR_CPOL; |
127 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | |
177 | + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); | |
128 | 178 | |
129 | 179 | /* We do not check spi->max_speed_hz here as the SPI clock |
130 | 180 | * frequency is not software programmable (the IP block design |
131 | 181 | |
132 | 182 | |
133 | 183 | |
... | ... | @@ -132,25 +182,27 @@ |
132 | 182 | */ |
133 | 183 | |
134 | 184 | /* Activate the chip select */ |
135 | - out_be32(xspi->regs + XSPI_SSR_OFFSET, | |
136 | - ~(0x0001 << spi->chip_select)); | |
185 | + xspi->write_fn(~(0x0001 << spi->chip_select), | |
186 | + xspi->regs + XSPI_SSR_OFFSET); | |
137 | 187 | } |
138 | 188 | } |
139 | 189 | |
140 | 190 | /* spi_bitbang requires custom setup_transfer() to be defined if there is a |
141 | 191 | * custom txrx_bufs(). We have nothing to setup here as the SPI IP block |
142 | - * supports just 8 bits per word, and SPI clock can't be changed in software. | |
143 | - * Check for 8 bits per word. Chip select delay calculations could be | |
192 | + * supports 8 or 16 bits per word which cannot be changed in software. | |
193 | + * SPI clock can't be changed in software either. | |
194 | + * Check for correct bits per word. Chip select delay calculations could be | |
144 | 195 | * added here as soon as bitbang_work() can be made aware of the delay value. |
145 | 196 | */ |
146 | 197 | static int xilinx_spi_setup_transfer(struct spi_device *spi, |
147 | 198 | struct spi_transfer *t) |
148 | 199 | { |
200 | + struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); | |
149 | 201 | u8 bits_per_word; |
150 | 202 | |
151 | 203 | bits_per_word = (t && t->bits_per_word) |
152 | 204 | ? t->bits_per_word : spi->bits_per_word; |
153 | - if (bits_per_word != 8) { | |
205 | + if (bits_per_word != xspi->bits_per_word) { | |
154 | 206 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", |
155 | 207 | __func__, bits_per_word); |
156 | 208 | return -EINVAL; |
... | ... | @@ -161,17 +213,16 @@ |
161 | 213 | |
162 | 214 | static int xilinx_spi_setup(struct spi_device *spi) |
163 | 215 | { |
164 | - struct spi_bitbang *bitbang; | |
165 | - struct xilinx_spi *xspi; | |
166 | - int retval; | |
167 | - | |
168 | - xspi = spi_master_get_devdata(spi->master); | |
169 | - bitbang = &xspi->bitbang; | |
170 | - | |
171 | - retval = xilinx_spi_setup_transfer(spi, NULL); | |
172 | - if (retval < 0) | |
173 | - return retval; | |
174 | - | |
216 | + /* always return 0, we can not check the number of bits. | |
217 | + * There are cases when SPI setup is called before any driver is | |
218 | + * there, in that case the SPI core defaults to 8 bits, which we | |
219 | + * do not support in some cases. But if we return an error, the | |
220 | + * SPI device would not be registered and no driver can get hold of it | |
221 | + * When the driver is there, it will call SPI setup again with the | |
222 | + * correct number of bits per transfer. | |
223 | + * If a driver setups with the wrong bit number, it will fail when | |
224 | + * it tries to do a transfer | |
225 | + */ | |
175 | 226 | return 0; |
176 | 227 | } |
177 | 228 | |
178 | 229 | |
... | ... | @@ -180,15 +231,14 @@ |
180 | 231 | u8 sr; |
181 | 232 | |
182 | 233 | /* Fill the Tx FIFO with as many bytes as possible */ |
183 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | |
234 | + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); | |
184 | 235 | while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) { |
185 | - if (xspi->tx_ptr) { | |
186 | - out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++); | |
187 | - } else { | |
188 | - out_8(xspi->regs + XSPI_TXD_OFFSET, 0); | |
189 | - } | |
190 | - xspi->remaining_bytes--; | |
191 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | |
236 | + if (xspi->tx_ptr) | |
237 | + xspi->tx_fn(xspi); | |
238 | + else | |
239 | + xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); | |
240 | + xspi->remaining_bytes -= xspi->bits_per_word / 8; | |
241 | + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); | |
192 | 242 | } |
193 | 243 | } |
194 | 244 | |
195 | 245 | |
196 | 246 | |
... | ... | @@ -210,18 +260,19 @@ |
210 | 260 | /* Enable the transmit empty interrupt, which we use to determine |
211 | 261 | * progress on the transmission. |
212 | 262 | */ |
213 | - ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET); | |
214 | - out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, | |
215 | - ipif_ier | XSPI_INTR_TX_EMPTY); | |
263 | + ipif_ier = xspi->read_fn(xspi->regs + XIPIF_V123B_IIER_OFFSET); | |
264 | + xspi->write_fn(ipif_ier | XSPI_INTR_TX_EMPTY, | |
265 | + xspi->regs + XIPIF_V123B_IIER_OFFSET); | |
216 | 266 | |
217 | 267 | /* Start the transfer by not inhibiting the transmitter any longer */ |
218 | - cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT; | |
219 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | |
268 | + cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) & | |
269 | + ~XSPI_CR_TRANS_INHIBIT; | |
270 | + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); | |
220 | 271 | |
221 | 272 | wait_for_completion(&xspi->done); |
222 | 273 | |
223 | 274 | /* Disable the transmit empty interrupt */ |
224 | - out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier); | |
275 | + xspi->write_fn(ipif_ier, xspi->regs + XIPIF_V123B_IIER_OFFSET); | |
225 | 276 | |
226 | 277 | return t->len - xspi->remaining_bytes; |
227 | 278 | } |
... | ... | @@ -238,8 +289,8 @@ |
238 | 289 | u32 ipif_isr; |
239 | 290 | |
240 | 291 | /* Get the IPIF interrupts, and clear them immediately */ |
241 | - ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET); | |
242 | - out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr); | |
292 | + ipif_isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET); | |
293 | + xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET); | |
243 | 294 | |
244 | 295 | if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */ |
245 | 296 | u16 cr; |
246 | 297 | |
247 | 298 | |
... | ... | @@ -250,20 +301,15 @@ |
250 | 301 | * transmitter while the Isr refills the transmit register/FIFO, |
251 | 302 | * or make sure it is stopped if we're done. |
252 | 303 | */ |
253 | - cr = in_be16(xspi->regs + XSPI_CR_OFFSET); | |
254 | - out_be16(xspi->regs + XSPI_CR_OFFSET, | |
255 | - cr | XSPI_CR_TRANS_INHIBIT); | |
304 | + cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); | |
305 | + xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, | |
306 | + xspi->regs + XSPI_CR_OFFSET); | |
256 | 307 | |
257 | 308 | /* Read out all the data from the Rx FIFO */ |
258 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | |
309 | + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); | |
259 | 310 | while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) { |
260 | - u8 data; | |
261 | - | |
262 | - data = in_8(xspi->regs + XSPI_RXD_OFFSET); | |
263 | - if (xspi->rx_ptr) { | |
264 | - *xspi->rx_ptr++ = data; | |
265 | - } | |
266 | - sr = in_8(xspi->regs + XSPI_SR_OFFSET); | |
311 | + xspi->rx_fn(xspi); | |
312 | + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); | |
267 | 313 | } |
268 | 314 | |
269 | 315 | /* See if there is more data to send */ |
... | ... | @@ -272,7 +318,7 @@ |
272 | 318 | /* Start the transfer by not inhibiting the |
273 | 319 | * transmitter any longer |
274 | 320 | */ |
275 | - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); | |
321 | + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); | |
276 | 322 | } else { |
277 | 323 | /* No more data to send. |
278 | 324 | * Indicate the transfer is completed. |
279 | 325 | |
280 | 326 | |
281 | 327 | |
282 | 328 | |
... | ... | @@ -284,41 +330,23 @@ |
284 | 330 | return IRQ_HANDLED; |
285 | 331 | } |
286 | 332 | |
287 | -static int __init xilinx_spi_of_probe(struct of_device *ofdev, | |
288 | - const struct of_device_id *match) | |
333 | +struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, | |
334 | + u32 irq, s16 bus_num) | |
289 | 335 | { |
290 | 336 | struct spi_master *master; |
291 | 337 | struct xilinx_spi *xspi; |
292 | - struct resource r_irq_struct; | |
293 | - struct resource r_mem_struct; | |
338 | + struct xspi_platform_data *pdata = dev->platform_data; | |
339 | + int ret; | |
294 | 340 | |
295 | - struct resource *r_irq = &r_irq_struct; | |
296 | - struct resource *r_mem = &r_mem_struct; | |
297 | - int rc = 0; | |
298 | - const u32 *prop; | |
299 | - int len; | |
300 | - | |
301 | - /* Get resources(memory, IRQ) associated with the device */ | |
302 | - master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi)); | |
303 | - | |
304 | - if (master == NULL) { | |
305 | - return -ENOMEM; | |
341 | + if (!pdata) { | |
342 | + dev_err(dev, "No platform data attached\n"); | |
343 | + return NULL; | |
306 | 344 | } |
307 | 345 | |
308 | - dev_set_drvdata(&ofdev->dev, master); | |
346 | + master = spi_alloc_master(dev, sizeof(struct xilinx_spi)); | |
347 | + if (!master) | |
348 | + return NULL; | |
309 | 349 | |
310 | - rc = of_address_to_resource(ofdev->node, 0, r_mem); | |
311 | - if (rc) { | |
312 | - dev_warn(&ofdev->dev, "invalid address\n"); | |
313 | - goto put_master; | |
314 | - } | |
315 | - | |
316 | - rc = of_irq_to_resource(ofdev->node, 0, r_irq); | |
317 | - if (rc == NO_IRQ) { | |
318 | - dev_warn(&ofdev->dev, "no IRQ found\n"); | |
319 | - goto put_master; | |
320 | - } | |
321 | - | |
322 | 350 | /* the spi->mode bits understood by this driver: */ |
323 | 351 | master->mode_bits = SPI_CPOL | SPI_CPHA; |
324 | 352 | |
325 | 353 | |
326 | 354 | |
327 | 355 | |
328 | 356 | |
329 | 357 | |
330 | 358 | |
331 | 359 | |
332 | 360 | |
333 | 361 | |
334 | 362 | |
335 | 363 | |
336 | 364 | |
337 | 365 | |
338 | 366 | |
339 | 367 | |
340 | 368 | |
341 | 369 | |
342 | 370 | |
343 | 371 | |
344 | 372 | |
345 | 373 | |
346 | 374 | |
347 | 375 | |
348 | 376 | |
... | ... | @@ -330,128 +358,87 @@ |
330 | 358 | xspi->bitbang.master->setup = xilinx_spi_setup; |
331 | 359 | init_completion(&xspi->done); |
332 | 360 | |
333 | - xspi->irq = r_irq->start; | |
334 | - | |
335 | - if (!request_mem_region(r_mem->start, | |
336 | - r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) { | |
337 | - rc = -ENXIO; | |
338 | - dev_warn(&ofdev->dev, "memory request failure\n"); | |
361 | + if (!request_mem_region(mem->start, resource_size(mem), | |
362 | + XILINX_SPI_NAME)) | |
339 | 363 | goto put_master; |
340 | - } | |
341 | 364 | |
342 | - xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1); | |
365 | + xspi->regs = ioremap(mem->start, resource_size(mem)); | |
343 | 366 | if (xspi->regs == NULL) { |
344 | - rc = -ENOMEM; | |
345 | - dev_warn(&ofdev->dev, "ioremap failure\n"); | |
346 | - goto release_mem; | |
367 | + dev_warn(dev, "ioremap failure\n"); | |
368 | + goto map_failed; | |
347 | 369 | } |
348 | - xspi->irq = r_irq->start; | |
349 | 370 | |
350 | - /* dynamic bus assignment */ | |
351 | - master->bus_num = -1; | |
371 | + master->bus_num = bus_num; | |
372 | + master->num_chipselect = pdata->num_chipselect; | |
352 | 373 | |
353 | - /* number of slave select bits is required */ | |
354 | - prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); | |
355 | - if (!prop || len < sizeof(*prop)) { | |
356 | - dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); | |
357 | - goto unmap_io; | |
374 | + xspi->mem = *mem; | |
375 | + xspi->irq = irq; | |
376 | + if (pdata->little_endian) { | |
377 | + xspi->read_fn = ioread32; | |
378 | + xspi->write_fn = iowrite32; | |
379 | + } else { | |
380 | + xspi->read_fn = ioread32be; | |
381 | + xspi->write_fn = iowrite32be; | |
358 | 382 | } |
359 | - master->num_chipselect = *prop; | |
383 | + xspi->bits_per_word = pdata->bits_per_word; | |
384 | + if (xspi->bits_per_word == 8) { | |
385 | + xspi->tx_fn = xspi_tx8; | |
386 | + xspi->rx_fn = xspi_rx8; | |
387 | + } else if (xspi->bits_per_word == 16) { | |
388 | + xspi->tx_fn = xspi_tx16; | |
389 | + xspi->rx_fn = xspi_rx16; | |
390 | + } else if (xspi->bits_per_word == 32) { | |
391 | + xspi->tx_fn = xspi_tx32; | |
392 | + xspi->rx_fn = xspi_rx32; | |
393 | + } else | |
394 | + goto unmap_io; | |
360 | 395 | |
396 | + | |
361 | 397 | /* SPI controller initializations */ |
362 | - xspi_init_hw(xspi->regs); | |
398 | + xspi_init_hw(xspi); | |
363 | 399 | |
364 | 400 | /* Register for SPI Interrupt */ |
365 | - rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); | |
366 | - if (rc != 0) { | |
367 | - dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq); | |
401 | + ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); | |
402 | + if (ret) | |
368 | 403 | goto unmap_io; |
369 | - } | |
370 | 404 | |
371 | - rc = spi_bitbang_start(&xspi->bitbang); | |
372 | - if (rc != 0) { | |
373 | - dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n"); | |
405 | + ret = spi_bitbang_start(&xspi->bitbang); | |
406 | + if (ret) { | |
407 | + dev_err(dev, "spi_bitbang_start FAILED\n"); | |
374 | 408 | goto free_irq; |
375 | 409 | } |
376 | 410 | |
377 | - dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", | |
378 | - (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq); | |
411 | + dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n", | |
412 | + (unsigned long long)mem->start, xspi->regs, xspi->irq); | |
413 | + return master; | |
379 | 414 | |
380 | - /* Add any subnodes on the SPI bus */ | |
381 | - of_register_spi_devices(master, ofdev->node); | |
382 | - | |
383 | - return rc; | |
384 | - | |
385 | 415 | free_irq: |
386 | 416 | free_irq(xspi->irq, xspi); |
387 | 417 | unmap_io: |
388 | 418 | iounmap(xspi->regs); |
389 | -release_mem: | |
390 | - release_mem_region(r_mem->start, resource_size(r_mem)); | |
419 | +map_failed: | |
420 | + release_mem_region(mem->start, resource_size(mem)); | |
391 | 421 | put_master: |
392 | 422 | spi_master_put(master); |
393 | - return rc; | |
423 | + return NULL; | |
394 | 424 | } |
425 | +EXPORT_SYMBOL(xilinx_spi_init); | |
395 | 426 | |
396 | -static int __devexit xilinx_spi_remove(struct of_device *ofdev) | |
427 | +void xilinx_spi_deinit(struct spi_master *master) | |
397 | 428 | { |
398 | 429 | struct xilinx_spi *xspi; |
399 | - struct spi_master *master; | |
400 | - struct resource r_mem; | |
401 | 430 | |
402 | - master = platform_get_drvdata(ofdev); | |
403 | 431 | xspi = spi_master_get_devdata(master); |
404 | 432 | |
405 | 433 | spi_bitbang_stop(&xspi->bitbang); |
406 | 434 | free_irq(xspi->irq, xspi); |
407 | 435 | iounmap(xspi->regs); |
408 | - if (!of_address_to_resource(ofdev->node, 0, &r_mem)) | |
409 | - release_mem_region(r_mem.start, resource_size(&r_mem)); | |
410 | - dev_set_drvdata(&ofdev->dev, 0); | |
411 | - spi_master_put(xspi->bitbang.master); | |
412 | 436 | |
413 | - return 0; | |
437 | + release_mem_region(xspi->mem.start, resource_size(&xspi->mem)); | |
438 | + spi_master_put(xspi->bitbang.master); | |
414 | 439 | } |
440 | +EXPORT_SYMBOL(xilinx_spi_deinit); | |
415 | 441 | |
416 | -/* work with hotplug and coldplug */ | |
417 | -MODULE_ALIAS("platform:" XILINX_SPI_NAME); | |
418 | - | |
419 | -static int __exit xilinx_spi_of_remove(struct of_device *op) | |
420 | -{ | |
421 | - return xilinx_spi_remove(op); | |
422 | -} | |
423 | - | |
424 | -static struct of_device_id xilinx_spi_of_match[] = { | |
425 | - { .compatible = "xlnx,xps-spi-2.00.a", }, | |
426 | - { .compatible = "xlnx,xps-spi-2.00.b", }, | |
427 | - {} | |
428 | -}; | |
429 | - | |
430 | -MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); | |
431 | - | |
432 | -static struct of_platform_driver xilinx_spi_of_driver = { | |
433 | - .owner = THIS_MODULE, | |
434 | - .name = "xilinx-xps-spi", | |
435 | - .match_table = xilinx_spi_of_match, | |
436 | - .probe = xilinx_spi_of_probe, | |
437 | - .remove = __exit_p(xilinx_spi_of_remove), | |
438 | - .driver = { | |
439 | - .name = "xilinx-xps-spi", | |
440 | - .owner = THIS_MODULE, | |
441 | - }, | |
442 | -}; | |
443 | - | |
444 | -static int __init xilinx_spi_init(void) | |
445 | -{ | |
446 | - return of_register_platform_driver(&xilinx_spi_of_driver); | |
447 | -} | |
448 | -module_init(xilinx_spi_init); | |
449 | - | |
450 | -static void __exit xilinx_spi_exit(void) | |
451 | -{ | |
452 | - of_unregister_platform_driver(&xilinx_spi_of_driver); | |
453 | -} | |
454 | -module_exit(xilinx_spi_exit); | |
455 | 442 | MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); |
456 | 443 | MODULE_DESCRIPTION("Xilinx SPI driver"); |
457 | 444 | MODULE_LICENSE("GPL"); |
drivers/spi/xilinx_spi.h
1 | +/* | |
2 | + * Xilinx SPI device driver API and platform data header file | |
3 | + * | |
4 | + * Copyright (c) 2009 Intel Corporation | |
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 | + * This program is distributed in the hope that it will be useful, | |
11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | + * GNU General Public License for more details. | |
14 | + * | |
15 | + * You should have received a copy of the GNU General Public License | |
16 | + * along with this program; if not, write to the Free Software | |
17 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | + */ | |
19 | + | |
20 | +#ifndef _XILINX_SPI_H_ | |
21 | +#define _XILINX_SPI_H_ | |
22 | + | |
23 | +#include <linux/spi/spi.h> | |
24 | +#include <linux/spi/spi_bitbang.h> | |
25 | + | |
26 | +#define XILINX_SPI_NAME "xilinx_spi" | |
27 | + | |
28 | +struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, | |
29 | + u32 irq, s16 bus_num); | |
30 | + | |
31 | +void xilinx_spi_deinit(struct spi_master *master); | |
32 | +#endif |
drivers/spi/xilinx_spi_of.c
1 | +/* | |
2 | + * Xilinx SPI OF device driver | |
3 | + * | |
4 | + * Copyright (c) 2009 Intel Corporation | |
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 | + * This program is distributed in the hope that it will be useful, | |
11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | + * GNU General Public License for more details. | |
14 | + * | |
15 | + * You should have received a copy of the GNU General Public License | |
16 | + * along with this program; if not, write to the Free Software | |
17 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | + */ | |
19 | + | |
20 | +/* Supports: | |
21 | + * Xilinx SPI devices as OF devices | |
22 | + * | |
23 | + * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. | |
24 | + */ | |
25 | + | |
26 | +#include <linux/module.h> | |
27 | +#include <linux/init.h> | |
28 | +#include <linux/interrupt.h> | |
29 | +#include <linux/io.h> | |
30 | + | |
31 | +#include <linux/of_platform.h> | |
32 | +#include <linux/of_device.h> | |
33 | +#include <linux/of_spi.h> | |
34 | + | |
35 | +#include <linux/spi/xilinx_spi.h> | |
36 | +#include "xilinx_spi.h" | |
37 | + | |
38 | + | |
39 | +static int __devinit xilinx_spi_of_probe(struct of_device *ofdev, | |
40 | + const struct of_device_id *match) | |
41 | +{ | |
42 | + struct spi_master *master; | |
43 | + struct xspi_platform_data *pdata; | |
44 | + struct resource r_mem; | |
45 | + struct resource r_irq; | |
46 | + int rc = 0; | |
47 | + const u32 *prop; | |
48 | + int len; | |
49 | + | |
50 | + rc = of_address_to_resource(ofdev->node, 0, &r_mem); | |
51 | + if (rc) { | |
52 | + dev_warn(&ofdev->dev, "invalid address\n"); | |
53 | + return rc; | |
54 | + } | |
55 | + | |
56 | + rc = of_irq_to_resource(ofdev->node, 0, &r_irq); | |
57 | + if (rc == NO_IRQ) { | |
58 | + dev_warn(&ofdev->dev, "no IRQ found\n"); | |
59 | + return -ENODEV; | |
60 | + } | |
61 | + | |
62 | + ofdev->dev.platform_data = | |
63 | + kzalloc(sizeof(struct xspi_platform_data), GFP_KERNEL); | |
64 | + pdata = ofdev->dev.platform_data; | |
65 | + if (!pdata) | |
66 | + return -ENOMEM; | |
67 | + | |
68 | + /* number of slave select bits is required */ | |
69 | + prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); | |
70 | + if (!prop || len < sizeof(*prop)) { | |
71 | + dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); | |
72 | + return -EINVAL; | |
73 | + } | |
74 | + pdata->num_chipselect = *prop; | |
75 | + pdata->bits_per_word = 8; | |
76 | + master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1); | |
77 | + if (!master) | |
78 | + return -ENODEV; | |
79 | + | |
80 | + dev_set_drvdata(&ofdev->dev, master); | |
81 | + | |
82 | + /* Add any subnodes on the SPI bus */ | |
83 | + of_register_spi_devices(master, ofdev->node); | |
84 | + | |
85 | + return 0; | |
86 | +} | |
87 | + | |
88 | +static int __devexit xilinx_spi_remove(struct of_device *ofdev) | |
89 | +{ | |
90 | + xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev)); | |
91 | + dev_set_drvdata(&ofdev->dev, 0); | |
92 | + kfree(ofdev->dev.platform_data); | |
93 | + ofdev->dev.platform_data = NULL; | |
94 | + return 0; | |
95 | +} | |
96 | + | |
97 | +static int __exit xilinx_spi_of_remove(struct of_device *op) | |
98 | +{ | |
99 | + return xilinx_spi_remove(op); | |
100 | +} | |
101 | + | |
102 | +static struct of_device_id xilinx_spi_of_match[] = { | |
103 | + { .compatible = "xlnx,xps-spi-2.00.a", }, | |
104 | + { .compatible = "xlnx,xps-spi-2.00.b", }, | |
105 | + {} | |
106 | +}; | |
107 | + | |
108 | +MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); | |
109 | + | |
110 | +static struct of_platform_driver xilinx_spi_of_driver = { | |
111 | + .match_table = xilinx_spi_of_match, | |
112 | + .probe = xilinx_spi_of_probe, | |
113 | + .remove = __exit_p(xilinx_spi_of_remove), | |
114 | + .driver = { | |
115 | + .name = "xilinx-xps-spi", | |
116 | + .owner = THIS_MODULE, | |
117 | + }, | |
118 | +}; | |
119 | + | |
120 | +static int __init xilinx_spi_of_init(void) | |
121 | +{ | |
122 | + return of_register_platform_driver(&xilinx_spi_of_driver); | |
123 | +} | |
124 | +module_init(xilinx_spi_of_init); | |
125 | + | |
126 | +static void __exit xilinx_spi_of_exit(void) | |
127 | +{ | |
128 | + of_unregister_platform_driver(&xilinx_spi_of_driver); | |
129 | +} | |
130 | +module_exit(xilinx_spi_of_exit); | |
131 | + | |
132 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | |
133 | +MODULE_DESCRIPTION("Xilinx SPI platform driver"); | |
134 | +MODULE_LICENSE("GPL v2"); |
drivers/spi/xilinx_spi_pltfm.c
1 | +/* | |
2 | + * Support for Xilinx SPI platform devices | |
3 | + * Copyright (c) 2009 Intel Corporation | |
4 | + * | |
5 | + * This program is free software; you can redistribute it and/or modify | |
6 | + * it under the terms of the GNU General Public License version 2 as | |
7 | + * published by the Free Software Foundation. | |
8 | + * | |
9 | + * This program is distributed in the hope that it will be useful, | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | + * GNU General Public License for more details. | |
13 | + * | |
14 | + * You should have received a copy of the GNU General Public License | |
15 | + * along with this program; if not, write to the Free Software | |
16 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
17 | + */ | |
18 | + | |
19 | +/* Supports: | |
20 | + * Xilinx SPI devices as platform devices | |
21 | + * | |
22 | + * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. | |
23 | + */ | |
24 | + | |
25 | +#include <linux/module.h> | |
26 | +#include <linux/init.h> | |
27 | +#include <linux/interrupt.h> | |
28 | +#include <linux/io.h> | |
29 | +#include <linux/platform_device.h> | |
30 | + | |
31 | +#include <linux/spi/spi.h> | |
32 | +#include <linux/spi/spi_bitbang.h> | |
33 | +#include <linux/spi/xilinx_spi.h> | |
34 | + | |
35 | +#include "xilinx_spi.h" | |
36 | + | |
37 | +static int __devinit xilinx_spi_probe(struct platform_device *dev) | |
38 | +{ | |
39 | + struct xspi_platform_data *pdata; | |
40 | + struct resource *r; | |
41 | + int irq; | |
42 | + struct spi_master *master; | |
43 | + u8 i; | |
44 | + | |
45 | + pdata = dev->dev.platform_data; | |
46 | + if (!pdata) | |
47 | + return -ENODEV; | |
48 | + | |
49 | + r = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
50 | + if (!r) | |
51 | + return -ENODEV; | |
52 | + | |
53 | + irq = platform_get_irq(dev, 0); | |
54 | + if (irq < 0) | |
55 | + return -ENXIO; | |
56 | + | |
57 | + master = xilinx_spi_init(&dev->dev, r, irq, dev->id); | |
58 | + if (!master) | |
59 | + return -ENODEV; | |
60 | + | |
61 | + for (i = 0; i < pdata->num_devices; i++) | |
62 | + spi_new_device(master, pdata->devices + i); | |
63 | + | |
64 | + platform_set_drvdata(dev, master); | |
65 | + return 0; | |
66 | +} | |
67 | + | |
68 | +static int __devexit xilinx_spi_remove(struct platform_device *dev) | |
69 | +{ | |
70 | + xilinx_spi_deinit(platform_get_drvdata(dev)); | |
71 | + platform_set_drvdata(dev, 0); | |
72 | + | |
73 | + return 0; | |
74 | +} | |
75 | + | |
76 | +/* work with hotplug and coldplug */ | |
77 | +MODULE_ALIAS("platform:" XILINX_SPI_NAME); | |
78 | + | |
79 | +static struct platform_driver xilinx_spi_driver = { | |
80 | + .probe = xilinx_spi_probe, | |
81 | + .remove = __devexit_p(xilinx_spi_remove), | |
82 | + .driver = { | |
83 | + .name = XILINX_SPI_NAME, | |
84 | + .owner = THIS_MODULE, | |
85 | + }, | |
86 | +}; | |
87 | + | |
88 | +static int __init xilinx_spi_pltfm_init(void) | |
89 | +{ | |
90 | + return platform_driver_register(&xilinx_spi_driver); | |
91 | +} | |
92 | +module_init(xilinx_spi_pltfm_init); | |
93 | + | |
94 | +static void __exit xilinx_spi_pltfm_exit(void) | |
95 | +{ | |
96 | + platform_driver_unregister(&xilinx_spi_driver); | |
97 | +} | |
98 | +module_exit(xilinx_spi_pltfm_exit); | |
99 | + | |
100 | +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); | |
101 | +MODULE_DESCRIPTION("Xilinx SPI platform driver"); | |
102 | +MODULE_LICENSE("GPL v2"); |
include/linux/spi/mpc52xx_spi.h
include/linux/spi/sh_msiof.h
include/linux/spi/xilinx_spi.h
1 | +#ifndef __LINUX_SPI_XILINX_SPI_H | |
2 | +#define __LINUX_SPI_XILINX_SPI_H | |
3 | + | |
4 | +/** | |
5 | + * struct xspi_platform_data - Platform data of the Xilinx SPI driver | |
6 | + * @num_chipselect: Number of chip select by the IP. | |
7 | + * @little_endian: If registers should be accessed little endian or not. | |
8 | + * @bits_per_word: Number of bits per word. | |
9 | + * @devices: Devices to add when the driver is probed. | |
10 | + * @num_devices: Number of devices in the devices array. | |
11 | + */ | |
12 | +struct xspi_platform_data { | |
13 | + u16 num_chipselect; | |
14 | + bool little_endian; | |
15 | + u8 bits_per_word; | |
16 | + struct spi_board_info *devices; | |
17 | + u8 num_devices; | |
18 | +}; | |
19 | + | |
20 | +#endif /* __LINUX_SPI_XILINX_SPI_H */ |