ramips: Power down phy on disabled switch ports
[openwrt/svn-archive/archive.git] / target / linux / cns21xx / patches-3.3 / 105-cns21xx-spi-driver.patch
1 --- a/include/linux/spi/spi.h
2 +++ b/include/linux/spi/spi.h
3 @@ -456,6 +456,8 @@ struct spi_transfer {
4 u16 delay_usecs;
5 u32 speed_hz;
6
7 + unsigned last_in_message_list;
8 +
9 struct list_head transfer_list;
10 };
11
12 --- a/drivers/spi/Kconfig
13 +++ b/drivers/spi/Kconfig
14 @@ -163,6 +163,14 @@ config SPI_GPIO_OLD
15
16 If unsure, say N.
17
18 +config SPI_CNS21XX
19 + tristate "Cavium Netowrks CNS21xx SPI master"
20 + depends on ARCH_CNS21XX && EXPERIMENTAL
21 + select SPI_BITBANG
22 + help
23 + This driver supports the buil-in SPI controller of the Cavium Networks
24 + CNS21xx SoCs.
25 +
26 config SPI_IMX
27 tristate "Freescale i.MX SPI controllers"
28 depends on ARCH_MXC
29 --- a/drivers/spi/Makefile
30 +++ b/drivers/spi/Makefile
31 @@ -18,6 +18,7 @@ obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.
32 obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
33 obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
34 obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
35 +obj-$(CONFIG_SPI_CNS21XX) += spi-cns21xx.o
36 obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
37 obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
38 obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o
39 --- a/drivers/spi/spi-bitbang.c
40 +++ b/drivers/spi/spi-bitbang.c
41 @@ -330,6 +330,13 @@ static void bitbang_work(struct work_str
42 */
43 if (!m->is_dma_mapped)
44 t->rx_dma = t->tx_dma = 0;
45 +
46 + if (t->transfer_list.next == &m->transfers) {
47 + t->last_in_message_list = 1;
48 + } else {
49 + t->last_in_message_list = 0;
50 + }
51 +
52 status = bitbang->txrx_bufs(spi, t);
53 }
54 if (status > 0)
55 --- /dev/null
56 +++ b/drivers/spi/spi-cns21xx.c
57 @@ -0,0 +1,521 @@
58 +/*
59 + * Copyright (c) 2008 Cavium Networks
60 + * Copyright (c) 2010-2012 Gabor Juhos <juhosg@openwrt.org>
61 + *
62 + * This file is free software; you can redistribute it and/or modify
63 + * it under the terms of the GNU General Public License, Version 2, as
64 + * published by the Free Software Foundation.
65 + */
66 +
67 +#include <linux/init.h>
68 +#include <linux/module.h>
69 +#include <linux/spinlock.h>
70 +#include <linux/workqueue.h>
71 +#include <linux/interrupt.h>
72 +#include <linux/delay.h>
73 +#include <linux/errno.h>
74 +#include <linux/platform_device.h>
75 +#include <linux/io.h>
76 +#include <linux/spi/spi.h>
77 +#include <linux/spi/spi_bitbang.h>
78 +
79 +#include <mach/hardware.h>
80 +#include <mach/cns21xx.h>
81 +
82 +#define DRIVER_NAME "cns21xx-spi"
83 +
84 +#ifdef CONFIG_CNS21XX_SPI_DEBUG
85 +#define DBG(fmt, args...) pr_info("[CNS21XX_SPI_DEBUG]" fmt, ## args)
86 +#else
87 +#define DBG(fmt, args...) do {} while (0)
88 +#endif /* CNS21XX_SPI_DEBUG */
89 +
90 +#define SPI_REG_CFG 0x40
91 +#define SPI_REG_STAT 0x44
92 +#define SPI_REG_BIT_RATE 0x48
93 +#define SPI_REG_TX_CTRL 0x4c
94 +#define SPI_REG_TX_DATA 0x50
95 +#define SPI_REG_RX_CTRL 0x54
96 +#define SPI_REG_RX_DATA 0x58
97 +#define SPI_REG_FIFO_TX_CFG 0x5c
98 +#define SPI_REG_FIFO_TX_CTRL 0x60
99 +#define SPI_REG_FIFO_RX_CFG 0x64
100 +#define SPI_REG_INTR_STAT 0x68
101 +#define SPI_REG_INTR_ENA 0x6c
102 +
103 +#define CFG_SPI_EN BIT(31)
104 +#define CFG_SPI_CLKPOL BIT(14)
105 +#define CFG_SPI_CLKPHA BIT(13)
106 +#define CFG_SPI_MASTER_EN BIT(11)
107 +#define CFG_SPI_CHAR_LEN_M 0x3
108 +#define CFG_SPI_CHAR_LEN_8BITS 0
109 +#define CFG_SPI_CHAR_LEN_16BITS 1
110 +#define CFG_SPI_CHAR_LEN_24BITS 2
111 +#define CFG_SPI_CHAR_LEN_32BITS 3
112 +
113 +#define STAT_SPI_BUSY_STA BIT(1)
114 +
115 +#define BIT_RATE_DIV_1 0
116 +#define BIT_RATE_DIV_2 1
117 +#define BIT_RATE_DIV_4 2
118 +#define BIT_RATE_DIV_8 3
119 +#define BIT_RATE_DIV_16 4
120 +#define BIT_RATE_DIV_32 5
121 +#define BIT_RATE_DIV_64 6
122 +#define BIT_RATE_DIV_128 7
123 +
124 +#define TX_CTRL_SPI_TXDAT_EOF BIT(2)
125 +#define TX_CTRL_SPI_TXCH_NUM_M 0x3
126 +#define TX_CTRL_CLEAR_MASK (TX_CTRL_SPI_TXDAT_EOF | \
127 + TX_CTRL_SPI_TXCH_NUM_M)
128 +
129 +#define RX_CTRL_SPI_RXDAT_EOF BIT(2)
130 +#define RX_CTRL_SPI_RXCH_NUM_M 0x3
131 +
132 +#define INTR_STAT_SPI_TXBF_UNRN_FG BIT(7)
133 +#define INTR_STAT_SPI_RXBF_OVRN_FG BIT(6)
134 +#define INTR_STAT_SPI_TXFF_UNRN_FG BIT(5)
135 +#define INTR_STAT_SPI_RXFF_OVRN_FG BIT(4)
136 +#define INTR_STAT_SPI_TXBUF_FG BIT(3)
137 +#define INTR_STAT_SPI_RXBUF_FG BIT(2)
138 +#define INTR_STAT_SPI_TXFF_FG BIT(1)
139 +#define INTR_STAT_SPI_RXFF_FG BIT(0)
140 +
141 +#define INTR_STAT_CLEAR_MASK (INTR_STAT_SPI_TXBF_UNRN_FG | \
142 + INTR_STAT_SPI_RXBF_OVRN_FG | \
143 + INTR_STAT_SPI_TXFF_UNRN_FG | \
144 + INTR_STAT_SPI_RXFF_OVRN_FG)
145 +
146 +#define FIFO_TX_CFG_SPI_TXFF_THRED_M 0x3
147 +#define FIFO_TX_CFG_SPI_TXFF_THRED_S 4
148 +#define FIFO_TX_CFG_SPI_TXFF_THRED_2 0
149 +#define FIFO_TX_CFG_SPI_TXFF_THRED_4 1
150 +#define FIFO_TX_CFG_SPI_TXFF_THRED_6 0
151 +#define FIFO_TX_CFG_SPI_TXFF_STATUS_M 0xf
152 +
153 +#define FIFO_RX_CFG_SPI_RXFF_THRED_M 0x3
154 +#define FIFO_RX_CFG_SPI_RXFF_THRED_S 4
155 +#define FIFO_RX_CFG_SPI_RXFF_THRED_2 0
156 +#define FIFO_RX_CFG_SPI_RXFF_THRED_4 1
157 +#define FIFO_RX_CFG_SPI_RXFF_THRED_6 0
158 +#define FIFO_RX_CFG_SPI_RXFF_STATUS_M 0xf
159 +
160 +#define CNS21XX_SPI_NUM_BIT_RATES 8
161 +
162 +struct cns21xx_spi {
163 + struct spi_bitbang bitbang;
164 +
165 + struct spi_master *master;
166 + struct device *dev;
167 + void __iomem *base;
168 + struct resource *region;
169 +
170 + unsigned freq_max;
171 + unsigned freq_min;
172 +
173 +};
174 +
175 +static inline struct cns21xx_spi *to_hw(struct spi_device *spi)
176 +{
177 + return spi_master_get_devdata(spi->master);
178 +}
179 +
180 +static inline u32 cns21xx_spi_rr(struct cns21xx_spi *hw, unsigned int reg)
181 +{
182 + return __raw_readl(hw->base + reg);
183 +}
184 +
185 +static inline void cns21xx_spi_wr(struct cns21xx_spi *hw, u32 val,
186 + unsigned int reg)
187 +{
188 + __raw_writel(val, hw->base + reg);
189 +}
190 +
191 +#define CNS21XX_SPI_RETRY_COUNT 100
192 +static inline int cns21xx_spi_wait(struct cns21xx_spi *hw, unsigned int reg,
193 + u32 mask, u32 val)
194 +{
195 + int retry_cnt = 0;
196 +
197 + do {
198 + if ((cns21xx_spi_rr(hw, reg) & mask) == val)
199 + break;
200 +
201 + if (++retry_cnt > CNS21XX_SPI_RETRY_COUNT) {
202 + dev_err(hw->dev, "timeout waiting on register %02x\n",
203 + reg);
204 + return -EIO;
205 + }
206 + } while (1);
207 +
208 + return 0;
209 +}
210 +
211 +static int cns21xx_spi_txrx_word(struct cns21xx_spi *hw, u8 tx_channel,
212 + u8 tx_eof_flag, u32 tx_data, u32 *rx_data)
213 +{
214 + unsigned int tx_ctrl;
215 + u8 rx_channel;
216 + u8 rx_eof_flag;
217 + int err = 0;
218 +
219 + err = cns21xx_spi_wait(hw, SPI_REG_STAT, STAT_SPI_BUSY_STA, 0);
220 + if (err)
221 + return err;
222 +
223 + err = cns21xx_spi_wait(hw, SPI_REG_INTR_STAT, INTR_STAT_SPI_TXBUF_FG,
224 + INTR_STAT_SPI_TXBUF_FG);
225 + if (err)
226 + return err;
227 +
228 + tx_ctrl = cns21xx_spi_rr(hw, SPI_REG_TX_CTRL);
229 + tx_ctrl &= ~(TX_CTRL_CLEAR_MASK);
230 + tx_ctrl |= (tx_channel & TX_CTRL_SPI_TXCH_NUM_M);
231 + tx_ctrl |= (tx_eof_flag) ? TX_CTRL_SPI_TXDAT_EOF : 0;
232 + cns21xx_spi_wr(hw, tx_ctrl, SPI_REG_TX_CTRL);
233 +
234 + cns21xx_spi_wr(hw, tx_data, SPI_REG_TX_DATA);
235 +
236 + err = cns21xx_spi_wait(hw, SPI_REG_INTR_STAT, INTR_STAT_SPI_RXBUF_FG,
237 + INTR_STAT_SPI_RXBUF_FG);
238 + if (err)
239 + return err;
240 +
241 + rx_channel = cns21xx_spi_rr(hw, SPI_REG_RX_CTRL) &
242 + RX_CTRL_SPI_RXCH_NUM_M;
243 +
244 + rx_eof_flag = (cns21xx_spi_rr(hw, SPI_REG_RX_CTRL) &
245 + RX_CTRL_SPI_RXDAT_EOF) ? 1 : 0;
246 +
247 + *rx_data = cns21xx_spi_rr(hw, SPI_REG_RX_DATA);
248 +
249 + if ((tx_channel != rx_channel) || (tx_eof_flag != rx_eof_flag))
250 + return -EPROTO;
251 +
252 + return 0;
253 +}
254 +
255 +static void cns21xx_spi_chipselect(struct spi_device *spi, int value)
256 +{
257 + struct cns21xx_spi *hw = to_hw(spi);
258 + unsigned int spi_config;
259 + unsigned int tx_ctrl;
260 +
261 + switch (value) {
262 + case BITBANG_CS_INACTIVE:
263 + break;
264 +
265 + case BITBANG_CS_ACTIVE:
266 + spi_config = cns21xx_spi_rr(hw, SPI_REG_CFG);
267 +
268 + if (spi->mode & SPI_CPHA)
269 + spi_config |= CFG_SPI_CLKPHA;
270 + else
271 + spi_config &= ~CFG_SPI_CLKPHA;
272 +
273 + if (spi->mode & SPI_CPOL)
274 + spi_config |= CFG_SPI_CLKPOL;
275 + else
276 + spi_config &= ~CFG_SPI_CLKPOL;
277 +
278 + cns21xx_spi_wr(hw, spi_config, SPI_REG_CFG);
279 +
280 + tx_ctrl = cns21xx_spi_rr(hw, SPI_REG_TX_CTRL);
281 + tx_ctrl &= ~(TX_CTRL_CLEAR_MASK);
282 + tx_ctrl |= (spi->chip_select & TX_CTRL_SPI_TXCH_NUM_M);
283 + cns21xx_spi_wr(hw, tx_ctrl, SPI_REG_TX_CTRL);
284 +
285 + break;
286 + }
287 +}
288 +
289 +static int cns21xx_spi_setup(struct spi_device *spi)
290 +{
291 + struct cns21xx_spi *hw = to_hw(spi);
292 +
293 + if (spi->bits_per_word != 8) {
294 + dev_err(&spi->dev, "%s: invalid bits_per_word=%u\n",
295 + __func__, spi->bits_per_word);
296 + return -EINVAL;
297 + }
298 +
299 + if (spi->max_speed_hz == 0)
300 + spi->max_speed_hz = hw->freq_max;
301 +
302 + if (spi->max_speed_hz > hw->freq_max ||
303 + spi->max_speed_hz < hw->freq_min) {
304 + dev_err(&spi->dev, "%s: max_speed_hz=%u out of range\n",
305 + __func__, spi->max_speed_hz);
306 + return -EINVAL;
307 + }
308 +
309 + return 0;
310 +}
311 +
312 +static int cns21xx_spi_setup_transfer(struct spi_device *spi,
313 + struct spi_transfer *t)
314 +{
315 + struct cns21xx_spi *hw = to_hw(spi);
316 + u8 bits_per_word;
317 + u32 hz;
318 + int i;
319 +
320 + bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
321 + hz = t ? t->speed_hz : spi->max_speed_hz;
322 +
323 + if (!bits_per_word)
324 + bits_per_word = spi->bits_per_word;
325 +
326 + if (!hz)
327 + hz = spi->max_speed_hz;
328 +
329 + if (bits_per_word != 8) {
330 + dev_err(&spi->dev, "%s: invalid bits_per_word=%u\n",
331 + __func__, bits_per_word);
332 + return -EINVAL;
333 + }
334 +
335 + if (hz > spi->max_speed_hz || hz > hw->freq_max || hz < hw->freq_min) {
336 + dev_err(&spi->dev, "%s: max_speed_hz=%u out of range\n",
337 + __func__, hz);
338 + return -EINVAL;
339 + }
340 +
341 + for (i = 0; i < CNS21XX_SPI_NUM_BIT_RATES; i++)
342 + if (spi->max_speed_hz > (cns21xx_get_apb_freq() >> i))
343 + break;
344 +
345 + DBG("max_speed:%uHz, curr_speed:%luHz, rate_index=%d\n",
346 + spi->max_speed_hz, cns21xx_get_apb_freq() / (1 << i), i);
347 +
348 + cns21xx_spi_wr(hw, i, SPI_REG_BIT_RATE);
349 +
350 + return 0;
351 +}
352 +
353 +static int cns21xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
354 +{
355 + struct cns21xx_spi *hw = to_hw(spi);
356 + const unsigned char *tx_buf;
357 + unsigned char *rx_buf;
358 + u32 rx_data;
359 + int tx_eof;
360 + int err = 0;
361 + int i;
362 +
363 + tx_buf = t->tx_buf;
364 + rx_buf = t->rx_buf;
365 + tx_eof = t->last_in_message_list;
366 +
367 + DBG("txrx: tx %p, rx %p, len %d\n", tx_buf, rx_buf, t->len);
368 +
369 + if (tx_buf) {
370 + for (i = 0; i < t->len; i++)
371 + DBG("tx_buf[%02d]: 0x%02x\n", i, tx_buf[i]);
372 +
373 + for (i = 0; i < (t->len - 1); i++) {
374 + err = cns21xx_spi_txrx_word(hw, spi->chip_select, 0,
375 + tx_buf[i], &rx_data);
376 + if (err)
377 + goto done;
378 +
379 + if (rx_buf) {
380 + rx_buf[i] = rx_data;
381 + DBG("rx_buf[%02d]:0x%02x\n", i, rx_buf[i]);
382 + }
383 + }
384 +
385 + err = cns21xx_spi_txrx_word(hw, spi->chip_select, tx_eof,
386 + tx_buf[i], &rx_data);
387 + if (err)
388 + goto done;
389 +
390 + if ((tx_eof) && rx_buf) {
391 + rx_buf[i] = rx_data;
392 + DBG("rx_buf[%02d]:0x%02x\n", i, rx_buf[i]);
393 + }
394 + } else if (rx_buf) {
395 + for (i = 0; i < (t->len - 1); i++) {
396 + err = cns21xx_spi_txrx_word(hw, spi->chip_select, 0,
397 + 0xff, &rx_data);
398 + if (err)
399 + goto done;
400 +
401 + rx_buf[i] = rx_data;
402 + DBG("rx_buf[%02d]:0x%02x\n", i, rx_buf[i]);
403 + }
404 +
405 + err = cns21xx_spi_txrx_word(hw, spi->chip_select, tx_eof,
406 + 0xff, &rx_data);
407 + if (err)
408 + goto done;
409 +
410 + rx_buf[i] = rx_data;
411 + DBG("rx_buf[%02d]:0x%02x\n", i, rx_buf[i]);
412 + }
413 +
414 + done:
415 + return (err) ? err : t->len;
416 +}
417 +
418 +static void __init cns21xx_spi_hw_init(struct cns21xx_spi *hw)
419 +{
420 + u32 t;
421 + u32 pclk;
422 +
423 + /* Setup configuration register */
424 + cns21xx_spi_wr(hw, CFG_SPI_MASTER_EN, SPI_REG_CFG);
425 +
426 + /* Set default clock to PCLK/2 */
427 + cns21xx_spi_wr(hw, BIT_RATE_DIV_2, SPI_REG_BIT_RATE);
428 +
429 + /* Configure SPI's Tx channel */
430 + cns21xx_spi_wr(hw, 0, SPI_REG_TX_CTRL);
431 +
432 + /* Configure Tx FIFO Threshold */
433 + t = cns21xx_spi_rr(hw, SPI_REG_FIFO_TX_CFG);
434 + t &= ~(FIFO_TX_CFG_SPI_TXFF_THRED_M << FIFO_TX_CFG_SPI_TXFF_THRED_S);
435 + t |= (FIFO_TX_CFG_SPI_TXFF_THRED_2 << FIFO_TX_CFG_SPI_TXFF_THRED_S);
436 + cns21xx_spi_wr(hw, t, SPI_REG_FIFO_TX_CFG);
437 +
438 + /* Configure Rx FIFO Threshold */
439 + t = cns21xx_spi_rr(hw, SPI_REG_FIFO_RX_CFG);
440 + t &= ~(FIFO_RX_CFG_SPI_RXFF_THRED_M << FIFO_RX_CFG_SPI_RXFF_THRED_S);
441 + t |= (FIFO_RX_CFG_SPI_RXFF_THRED_2 << FIFO_RX_CFG_SPI_RXFF_THRED_S);
442 + cns21xx_spi_wr(hw, t, SPI_REG_FIFO_RX_CFG);
443 +
444 + /* Disable interrupts, and clear interrupt status */
445 + cns21xx_spi_wr(hw, 0, SPI_REG_INTR_ENA);
446 + cns21xx_spi_wr(hw, INTR_STAT_CLEAR_MASK, SPI_REG_INTR_STAT);
447 +
448 + (void) cns21xx_spi_rr(hw, SPI_REG_RX_DATA);
449 +
450 + /* Enable SPI */
451 + t = cns21xx_spi_rr(hw, SPI_REG_CFG);
452 + t |= CFG_SPI_EN;
453 + cns21xx_spi_wr(hw, t, SPI_REG_CFG);
454 +
455 + pclk = cns21xx_get_apb_freq();
456 + hw->freq_max = pclk;
457 + hw->freq_min = pclk / (1 << BIT_RATE_DIV_128);
458 +}
459 +
460 +static int __init cns21xx_spi_probe(struct platform_device *pdev)
461 +{
462 + struct cns21xx_spi *hw;
463 + struct spi_master *master;
464 + struct resource *res;
465 + int err = 0;
466 +
467 + master = spi_alloc_master(&pdev->dev, sizeof(struct cns21xx_spi));
468 + if (!master) {
469 + dev_err(&pdev->dev, "No memory for spi_master\n");
470 + return -ENOMEM;
471 + }
472 +
473 + hw = spi_master_get_devdata(master);
474 +
475 + platform_set_drvdata(pdev, hw);
476 + hw->master = spi_master_get(master);
477 + hw->dev = &pdev->dev;
478 +
479 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
480 + if (!res) {
481 + dev_dbg(&pdev->dev, "no MEM resource found\n");
482 + err = -ENOENT;
483 + goto err_put_master;
484 + }
485 +
486 + hw->region = request_mem_region(res->start, resource_size(res),
487 + dev_name(&pdev->dev));
488 + if (!hw->region) {
489 + dev_err(&pdev->dev, "unable to reserve iomem region\n");
490 + err = -ENXIO;
491 + goto err_put_master;
492 + }
493 +
494 + hw->base = ioremap(res->start, resource_size(res));
495 + if (!hw->base) {
496 + dev_err(&pdev->dev, "ioremap failed\n");
497 + err = -ENOENT;
498 + goto err_release_region;
499 + }
500 +
501 + cns21xx_spi_hw_init(hw);
502 +
503 + master->bus_num = pdev->id;
504 + if (master->bus_num == -1)
505 + master->bus_num = 0;
506 +
507 + master->num_chipselect = 4;
508 + master->setup = cns21xx_spi_setup;
509 +
510 + hw->bitbang.master = hw->master;
511 + hw->bitbang.chipselect = cns21xx_spi_chipselect;
512 + hw->bitbang.txrx_bufs = cns21xx_spi_txrx;
513 + hw->bitbang.setup_transfer = cns21xx_spi_setup_transfer;
514 +
515 + err = spi_bitbang_start(&hw->bitbang);
516 + if (err) {
517 + dev_err(hw->dev, "unable to register SPI master\n");
518 + goto err_unmap;
519 + }
520 +
521 + dev_info(hw->dev, "iomem at %08x\n", res->start);
522 +
523 + return 0;
524 +
525 + err_unmap:
526 + iounmap(hw->base);
527 +
528 + err_release_region:
529 + release_resource(hw->region);
530 + kfree(hw->region);
531 +
532 + err_put_master:
533 + spi_master_put(hw->bitbang.master);
534 + platform_set_drvdata(pdev, NULL);
535 +
536 + return err;
537 +}
538 +
539 +static int __devexit cns21xx_spi_remove(struct platform_device *pdev)
540 +{
541 + struct cns21xx_spi *hw = platform_get_drvdata(pdev);
542 +
543 + spi_bitbang_stop(&hw->bitbang);
544 + iounmap(hw->base);
545 + release_resource(hw->region);
546 + kfree(hw->region);
547 + spi_master_put(hw->bitbang.master);
548 + platform_set_drvdata(pdev, NULL);
549 +
550 + return 0;
551 +}
552 +
553 +static struct platform_driver cns21xx_spi_driver = {
554 + .remove = __devexit_p(cns21xx_spi_remove),
555 + .driver = {
556 + .name = DRIVER_NAME,
557 + .owner = THIS_MODULE,
558 + },
559 +};
560 +
561 +static int __init cns21xx_spi_init(void)
562 +{
563 + return platform_driver_probe(&cns21xx_spi_driver, cns21xx_spi_probe);
564 +}
565 +
566 +static void __exit cns21xx_spi_exit(void)
567 +{
568 + platform_driver_unregister(&cns21xx_spi_driver);
569 +}
570 +
571 +module_init(cns21xx_spi_init);
572 +module_exit(cns21xx_spi_exit);
573 +
574 +MODULE_DESCRIPTION("Cavium Networks CNS21xx SPI Controller driver");
575 +MODULE_AUTHOR("STAR Semi Corp.");
576 +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
577 +MODULE_LICENSE("GPL v2");
578 +MODULE_ALIAS("platform:" DRIVER_NAME);