diff options
-rw-r--r-- | drivers/spi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx.c | 486 |
3 files changed, 493 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3508648c32a2..3308c12785e6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig | |||
@@ -94,6 +94,12 @@ config SPI_AU1550 | |||
94 | If you say yes to this option, support will be included for the | 94 | If you say yes to this option, support will be included for the |
95 | PSC SPI controller found on Au1550, Au1200 and Au1300 series. | 95 | PSC SPI controller found on Au1550, Au1200 and Au1300 series. |
96 | 96 | ||
97 | config SPI_BCM63XX | ||
98 | tristate "Broadcom BCM63xx SPI controller" | ||
99 | depends on BCM63XX | ||
100 | help | ||
101 | Enable support for the SPI controller on the Broadcom BCM63xx SoCs. | ||
102 | |||
97 | config SPI_BITBANG | 103 | config SPI_BITBANG |
98 | tristate "Utilities for Bitbanging SPI masters" | 104 | tristate "Utilities for Bitbanging SPI masters" |
99 | help | 105 | help |
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 2e556484345a..a1d48e0ba3dc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile | |||
@@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o | |||
14 | obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o | 14 | obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o |
15 | obj-$(CONFIG_SPI_ATH79) += spi-ath79.o | 15 | obj-$(CONFIG_SPI_ATH79) += spi-ath79.o |
16 | obj-$(CONFIG_SPI_AU1550) += spi-au1550.o | 16 | obj-$(CONFIG_SPI_AU1550) += spi-au1550.o |
17 | obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o | ||
17 | obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o | 18 | obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o |
18 | obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o | 19 | obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o |
19 | obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o | 20 | obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o |
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c new file mode 100644 index 000000000000..f01b2648452e --- /dev/null +++ b/drivers/spi/spi-bcm63xx.c | |||
@@ -0,0 +1,486 @@ | |||
1 | /* | ||
2 | * Broadcom BCM63xx SPI controller support | ||
3 | * | ||
4 | * Copyright (C) 2009-2011 Florian Fainelli <florian@openwrt.org> | ||
5 | * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version 2 | ||
10 | * of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the | ||
19 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
20 | */ | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/clk.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/spi/spi.h> | ||
31 | #include <linux/completion.h> | ||
32 | #include <linux/err.h> | ||
33 | |||
34 | #include <bcm63xx_dev_spi.h> | ||
35 | |||
36 | #define PFX KBUILD_MODNAME | ||
37 | #define DRV_VER "0.1.2" | ||
38 | |||
39 | struct bcm63xx_spi { | ||
40 | spinlock_t lock; | ||
41 | int stopping; | ||
42 | struct completion done; | ||
43 | |||
44 | void __iomem *regs; | ||
45 | int irq; | ||
46 | |||
47 | /* Platform data */ | ||
48 | u32 speed_hz; | ||
49 | unsigned fifo_size; | ||
50 | |||
51 | /* Data buffers */ | ||
52 | const unsigned char *tx_ptr; | ||
53 | unsigned char *rx_ptr; | ||
54 | |||
55 | /* data iomem */ | ||
56 | u8 __iomem *tx_io; | ||
57 | const u8 __iomem *rx_io; | ||
58 | |||
59 | int remaining_bytes; | ||
60 | |||
61 | struct clk *clk; | ||
62 | struct platform_device *pdev; | ||
63 | }; | ||
64 | |||
65 | static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs, | ||
66 | unsigned int offset) | ||
67 | { | ||
68 | return bcm_readb(bs->regs + bcm63xx_spireg(offset)); | ||
69 | } | ||
70 | |||
71 | static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs, | ||
72 | unsigned int offset) | ||
73 | { | ||
74 | return bcm_readw(bs->regs + bcm63xx_spireg(offset)); | ||
75 | } | ||
76 | |||
77 | static inline void bcm_spi_writeb(struct bcm63xx_spi *bs, | ||
78 | u8 value, unsigned int offset) | ||
79 | { | ||
80 | bcm_writeb(value, bs->regs + bcm63xx_spireg(offset)); | ||
81 | } | ||
82 | |||
83 | static inline void bcm_spi_writew(struct bcm63xx_spi *bs, | ||
84 | u16 value, unsigned int offset) | ||
85 | { | ||
86 | bcm_writew(value, bs->regs + bcm63xx_spireg(offset)); | ||
87 | } | ||
88 | |||
89 | static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { | ||
90 | { 20000000, SPI_CLK_20MHZ }, | ||
91 | { 12500000, SPI_CLK_12_50MHZ }, | ||
92 | { 6250000, SPI_CLK_6_250MHZ }, | ||
93 | { 3125000, SPI_CLK_3_125MHZ }, | ||
94 | { 1563000, SPI_CLK_1_563MHZ }, | ||
95 | { 781000, SPI_CLK_0_781MHZ }, | ||
96 | { 391000, SPI_CLK_0_391MHZ } | ||
97 | }; | ||
98 | |||
99 | static int bcm63xx_spi_setup_transfer(struct spi_device *spi, | ||
100 | struct spi_transfer *t) | ||
101 | { | ||
102 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | ||
103 | u8 bits_per_word; | ||
104 | u8 clk_cfg, reg; | ||
105 | u32 hz; | ||
106 | int i; | ||
107 | |||
108 | bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; | ||
109 | hz = (t) ? t->speed_hz : spi->max_speed_hz; | ||
110 | if (bits_per_word != 8) { | ||
111 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", | ||
112 | __func__, bits_per_word); | ||
113 | return -EINVAL; | ||
114 | } | ||
115 | |||
116 | if (spi->chip_select > spi->master->num_chipselect) { | ||
117 | dev_err(&spi->dev, "%s, unsupported slave %d\n", | ||
118 | __func__, spi->chip_select); | ||
119 | return -EINVAL; | ||
120 | } | ||
121 | |||
122 | /* Find the closest clock configuration */ | ||
123 | for (i = 0; i < SPI_CLK_MASK; i++) { | ||
124 | if (hz <= bcm63xx_spi_freq_table[i][0]) { | ||
125 | clk_cfg = bcm63xx_spi_freq_table[i][1]; | ||
126 | break; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | /* No matching configuration found, default to lowest */ | ||
131 | if (i == SPI_CLK_MASK) | ||
132 | clk_cfg = SPI_CLK_0_391MHZ; | ||
133 | |||
134 | /* clear existing clock configuration bits of the register */ | ||
135 | reg = bcm_spi_readb(bs, SPI_CLK_CFG); | ||
136 | reg &= ~SPI_CLK_MASK; | ||
137 | reg |= clk_cfg; | ||
138 | |||
139 | bcm_spi_writeb(bs, reg, SPI_CLK_CFG); | ||
140 | dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", | ||
141 | clk_cfg, hz); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | /* the spi->mode bits understood by this driver: */ | ||
147 | #define MODEBITS (SPI_CPOL | SPI_CPHA) | ||
148 | |||
149 | static int bcm63xx_spi_setup(struct spi_device *spi) | ||
150 | { | ||
151 | struct bcm63xx_spi *bs; | ||
152 | int ret; | ||
153 | |||
154 | bs = spi_master_get_devdata(spi->master); | ||
155 | |||
156 | if (bs->stopping) | ||
157 | return -ESHUTDOWN; | ||
158 | |||
159 | if (!spi->bits_per_word) | ||
160 | spi->bits_per_word = 8; | ||
161 | |||
162 | if (spi->mode & ~MODEBITS) { | ||
163 | dev_err(&spi->dev, "%s, unsupported mode bits %x\n", | ||
164 | __func__, spi->mode & ~MODEBITS); | ||
165 | return -EINVAL; | ||
166 | } | ||
167 | |||
168 | ret = bcm63xx_spi_setup_transfer(spi, NULL); | ||
169 | if (ret < 0) { | ||
170 | dev_err(&spi->dev, "setup: unsupported mode bits %x\n", | ||
171 | spi->mode & ~MODEBITS); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n", | ||
176 | __func__, spi->mode & MODEBITS, spi->bits_per_word, 0); | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | /* Fill the TX FIFO with as many bytes as possible */ | ||
182 | static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) | ||
183 | { | ||
184 | u8 size; | ||
185 | |||
186 | /* Fill the Tx FIFO with as many bytes as possible */ | ||
187 | size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes : | ||
188 | bs->fifo_size; | ||
189 | memcpy_toio(bs->tx_io, bs->tx_ptr, size); | ||
190 | bs->remaining_bytes -= size; | ||
191 | } | ||
192 | |||
193 | static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) | ||
194 | { | ||
195 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | ||
196 | u16 msg_ctl; | ||
197 | u16 cmd; | ||
198 | |||
199 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", | ||
200 | t->tx_buf, t->rx_buf, t->len); | ||
201 | |||
202 | /* Transmitter is inhibited */ | ||
203 | bs->tx_ptr = t->tx_buf; | ||
204 | bs->rx_ptr = t->rx_buf; | ||
205 | init_completion(&bs->done); | ||
206 | |||
207 | if (t->tx_buf) { | ||
208 | bs->remaining_bytes = t->len; | ||
209 | bcm63xx_spi_fill_tx_fifo(bs); | ||
210 | } | ||
211 | |||
212 | /* Enable the command done interrupt which | ||
213 | * we use to determine completion of a command */ | ||
214 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); | ||
215 | |||
216 | /* Fill in the Message control register */ | ||
217 | msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); | ||
218 | |||
219 | if (t->rx_buf && t->tx_buf) | ||
220 | msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT); | ||
221 | else if (t->rx_buf) | ||
222 | msg_ctl |= (SPI_HD_R << SPI_MSG_TYPE_SHIFT); | ||
223 | else if (t->tx_buf) | ||
224 | msg_ctl |= (SPI_HD_W << SPI_MSG_TYPE_SHIFT); | ||
225 | |||
226 | bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL); | ||
227 | |||
228 | /* Issue the transfer */ | ||
229 | cmd = SPI_CMD_START_IMMEDIATE; | ||
230 | cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); | ||
231 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); | ||
232 | bcm_spi_writew(bs, cmd, SPI_CMD); | ||
233 | wait_for_completion(&bs->done); | ||
234 | |||
235 | /* Disable the CMD_DONE interrupt */ | ||
236 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | ||
237 | |||
238 | return t->len - bs->remaining_bytes; | ||
239 | } | ||
240 | |||
241 | static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m) | ||
242 | { | ||
243 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | ||
244 | struct spi_transfer *t; | ||
245 | int ret = 0; | ||
246 | |||
247 | if (unlikely(list_empty(&m->transfers))) | ||
248 | return -EINVAL; | ||
249 | |||
250 | if (bs->stopping) | ||
251 | return -ESHUTDOWN; | ||
252 | |||
253 | list_for_each_entry(t, &m->transfers, transfer_list) { | ||
254 | ret += bcm63xx_txrx_bufs(spi, t); | ||
255 | } | ||
256 | |||
257 | m->complete(m->context); | ||
258 | |||
259 | return ret; | ||
260 | } | ||
261 | |||
262 | /* This driver supports single master mode only. Hence | ||
263 | * CMD_DONE is the only interrupt we care about | ||
264 | */ | ||
265 | static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) | ||
266 | { | ||
267 | struct spi_master *master = (struct spi_master *)dev_id; | ||
268 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | ||
269 | u8 intr; | ||
270 | u16 cmd; | ||
271 | |||
272 | /* Read interupts and clear them immediately */ | ||
273 | intr = bcm_spi_readb(bs, SPI_INT_STATUS); | ||
274 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); | ||
275 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | ||
276 | |||
277 | /* A tansfer completed */ | ||
278 | if (intr & SPI_INTR_CMD_DONE) { | ||
279 | u8 rx_tail; | ||
280 | |||
281 | rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); | ||
282 | |||
283 | /* Read out all the data */ | ||
284 | if (rx_tail) | ||
285 | memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); | ||
286 | |||
287 | /* See if there is more data to send */ | ||
288 | if (bs->remaining_bytes > 0) { | ||
289 | bcm63xx_spi_fill_tx_fifo(bs); | ||
290 | |||
291 | /* Start the transfer */ | ||
292 | bcm_spi_writew(bs, SPI_HD_W << SPI_MSG_TYPE_SHIFT, | ||
293 | SPI_MSG_CTL); | ||
294 | cmd = bcm_spi_readw(bs, SPI_CMD); | ||
295 | cmd |= SPI_CMD_START_IMMEDIATE; | ||
296 | cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); | ||
297 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); | ||
298 | bcm_spi_writew(bs, cmd, SPI_CMD); | ||
299 | } else { | ||
300 | complete(&bs->done); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | return IRQ_HANDLED; | ||
305 | } | ||
306 | |||
307 | |||
308 | static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) | ||
309 | { | ||
310 | struct resource *r; | ||
311 | struct device *dev = &pdev->dev; | ||
312 | struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data; | ||
313 | int irq; | ||
314 | struct spi_master *master; | ||
315 | struct clk *clk; | ||
316 | struct bcm63xx_spi *bs; | ||
317 | int ret; | ||
318 | |||
319 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
320 | if (!r) { | ||
321 | dev_err(dev, "no iomem\n"); | ||
322 | ret = -ENXIO; | ||
323 | goto out; | ||
324 | } | ||
325 | |||
326 | irq = platform_get_irq(pdev, 0); | ||
327 | if (irq < 0) { | ||
328 | dev_err(dev, "no irq\n"); | ||
329 | ret = -ENXIO; | ||
330 | goto out; | ||
331 | } | ||
332 | |||
333 | clk = clk_get(dev, "spi"); | ||
334 | if (IS_ERR(clk)) { | ||
335 | dev_err(dev, "no clock for device\n"); | ||
336 | ret = PTR_ERR(clk); | ||
337 | goto out; | ||
338 | } | ||
339 | |||
340 | master = spi_alloc_master(dev, sizeof(*bs)); | ||
341 | if (!master) { | ||
342 | dev_err(dev, "out of memory\n"); | ||
343 | ret = -ENOMEM; | ||
344 | goto out_clk; | ||
345 | } | ||
346 | |||
347 | bs = spi_master_get_devdata(master); | ||
348 | init_completion(&bs->done); | ||
349 | |||
350 | platform_set_drvdata(pdev, master); | ||
351 | bs->pdev = pdev; | ||
352 | |||
353 | if (!devm_request_mem_region(&pdev->dev, r->start, | ||
354 | resource_size(r), PFX)) { | ||
355 | dev_err(dev, "iomem request failed\n"); | ||
356 | ret = -ENXIO; | ||
357 | goto out_err; | ||
358 | } | ||
359 | |||
360 | bs->regs = devm_ioremap_nocache(&pdev->dev, r->start, | ||
361 | resource_size(r)); | ||
362 | if (!bs->regs) { | ||
363 | dev_err(dev, "unable to ioremap regs\n"); | ||
364 | ret = -ENOMEM; | ||
365 | goto out_err; | ||
366 | } | ||
367 | |||
368 | bs->irq = irq; | ||
369 | bs->clk = clk; | ||
370 | bs->fifo_size = pdata->fifo_size; | ||
371 | |||
372 | ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0, | ||
373 | pdev->name, master); | ||
374 | if (ret) { | ||
375 | dev_err(dev, "unable to request irq\n"); | ||
376 | goto out_err; | ||
377 | } | ||
378 | |||
379 | master->bus_num = pdata->bus_num; | ||
380 | master->num_chipselect = pdata->num_chipselect; | ||
381 | master->setup = bcm63xx_spi_setup; | ||
382 | master->transfer = bcm63xx_transfer; | ||
383 | bs->speed_hz = pdata->speed_hz; | ||
384 | bs->stopping = 0; | ||
385 | bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); | ||
386 | bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); | ||
387 | spin_lock_init(&bs->lock); | ||
388 | |||
389 | /* Initialize hardware */ | ||
390 | clk_enable(bs->clk); | ||
391 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); | ||
392 | |||
393 | /* register and we are done */ | ||
394 | ret = spi_register_master(master); | ||
395 | if (ret) { | ||
396 | dev_err(dev, "spi register failed\n"); | ||
397 | goto out_clk_disable; | ||
398 | } | ||
399 | |||
400 | dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n", | ||
401 | r->start, irq, bs->fifo_size, DRV_VER); | ||
402 | |||
403 | return 0; | ||
404 | |||
405 | out_clk_disable: | ||
406 | clk_disable(clk); | ||
407 | out_err: | ||
408 | platform_set_drvdata(pdev, NULL); | ||
409 | spi_master_put(master); | ||
410 | out_clk: | ||
411 | clk_put(clk); | ||
412 | out: | ||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | static int __devexit bcm63xx_spi_remove(struct platform_device *pdev) | ||
417 | { | ||
418 | struct spi_master *master = platform_get_drvdata(pdev); | ||
419 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | ||
420 | |||
421 | /* reset spi block */ | ||
422 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | ||
423 | spin_lock(&bs->lock); | ||
424 | bs->stopping = 1; | ||
425 | |||
426 | /* HW shutdown */ | ||
427 | clk_disable(bs->clk); | ||
428 | clk_put(bs->clk); | ||
429 | |||
430 | spin_unlock(&bs->lock); | ||
431 | platform_set_drvdata(pdev, 0); | ||
432 | spi_unregister_master(master); | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | #ifdef CONFIG_PM | ||
438 | static int bcm63xx_spi_suspend(struct device *dev) | ||
439 | { | ||
440 | struct spi_master *master = | ||
441 | platform_get_drvdata(to_platform_device(dev)); | ||
442 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | ||
443 | |||
444 | clk_disable(bs->clk); | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int bcm63xx_spi_resume(struct device *dev) | ||
450 | { | ||
451 | struct spi_master *master = | ||
452 | platform_get_drvdata(to_platform_device(dev)); | ||
453 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | ||
454 | |||
455 | clk_enable(bs->clk); | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | static const struct dev_pm_ops bcm63xx_spi_pm_ops = { | ||
461 | .suspend = bcm63xx_spi_suspend, | ||
462 | .resume = bcm63xx_spi_resume, | ||
463 | }; | ||
464 | |||
465 | #define BCM63XX_SPI_PM_OPS (&bcm63xx_spi_pm_ops) | ||
466 | #else | ||
467 | #define BCM63XX_SPI_PM_OPS NULL | ||
468 | #endif | ||
469 | |||
470 | static struct platform_driver bcm63xx_spi_driver = { | ||
471 | .driver = { | ||
472 | .name = "bcm63xx-spi", | ||
473 | .owner = THIS_MODULE, | ||
474 | .pm = BCM63XX_SPI_PM_OPS, | ||
475 | }, | ||
476 | .probe = bcm63xx_spi_probe, | ||
477 | .remove = __devexit_p(bcm63xx_spi_remove), | ||
478 | }; | ||
479 | |||
480 | module_platform_driver(bcm63xx_spi_driver); | ||
481 | |||
482 | MODULE_ALIAS("platform:bcm63xx_spi"); | ||
483 | MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); | ||
484 | MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>"); | ||
485 | MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); | ||
486 | MODULE_LICENSE("GPL"); | ||