diff options
author | Atsushi Nemoto <anemo@mba.ocn.ne.jp> | 2007-07-17 07:04:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-17 13:23:05 -0400 |
commit | f2cac67dd36626128e06e79fc7ca95d544dcdc67 (patch) | |
tree | 689ff4133fa87e2bcbe532158b2fea7684e73a8d | |
parent | ccdc7bf925731ef37f0af95262d675b74544932f (diff) |
spi_txx9 controller driver
This is a driver for SPI controller built into TXx9 MIPS SoCs.
This driver is derived from arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c.
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/spi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi_txx9.c | 474 |
3 files changed, 481 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5ac498bbf2b6..b91571122daa 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig | |||
@@ -174,6 +174,12 @@ config SPI_S3C24XX_GPIO | |||
174 | the inbuilt hardware cannot provide the transfer mode, or | 174 | the inbuilt hardware cannot provide the transfer mode, or |
175 | where the board is using non hardware connected pins. | 175 | where the board is using non hardware connected pins. |
176 | 176 | ||
177 | config SPI_TXX9 | ||
178 | tristate "Toshiba TXx9 SPI controller" | ||
179 | depends on SPI_MASTER && GENERIC_GPIO && CPU_TX49XX | ||
180 | help | ||
181 | SPI driver for Toshiba TXx9 MIPS SoCs | ||
182 | |||
177 | config SPI_XILINX | 183 | config SPI_XILINX |
178 | tristate "Xilinx SPI controller" | 184 | tristate "Xilinx SPI controller" |
179 | depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL | 185 | depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL |
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index be69667e0871..41fbac45c323 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile | |||
@@ -25,6 +25,7 @@ obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o | |||
25 | obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o | 25 | obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o |
26 | obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o | 26 | obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o |
27 | obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o | 27 | obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o |
28 | obj-$(CONFIG_SPI_TXX9) += spi_txx9.o | ||
28 | obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o | 29 | obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o |
29 | # ... add above this line ... | 30 | # ... add above this line ... |
30 | 31 | ||
diff --git a/drivers/spi/spi_txx9.c b/drivers/spi/spi_txx9.c new file mode 100644 index 000000000000..08e981c40646 --- /dev/null +++ b/drivers/spi/spi_txx9.c | |||
@@ -0,0 +1,474 @@ | |||
1 | /* | ||
2 | * spi_txx9.c - TXx9 SPI controller driver. | ||
3 | * | ||
4 | * Based on linux/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c | ||
5 | * Copyright (C) 2000-2001 Toshiba Corporation | ||
6 | * | ||
7 | * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the | ||
8 | * terms of the GNU General Public License version 2. This program is | ||
9 | * licensed "as is" without any warranty of any kind, whether express | ||
10 | * or implied. | ||
11 | * | ||
12 | * Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com) | ||
13 | * | ||
14 | * Convert to generic SPI framework - Atsushi Nemoto (anemo@mba.ocn.ne.jp) | ||
15 | */ | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | #include <linux/spi/spi.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <asm/gpio.h> | ||
28 | |||
29 | |||
30 | #define SPI_FIFO_SIZE 4 | ||
31 | |||
32 | #define TXx9_SPMCR 0x00 | ||
33 | #define TXx9_SPCR0 0x04 | ||
34 | #define TXx9_SPCR1 0x08 | ||
35 | #define TXx9_SPFS 0x0c | ||
36 | #define TXx9_SPSR 0x14 | ||
37 | #define TXx9_SPDR 0x18 | ||
38 | |||
39 | /* SPMCR : SPI Master Control */ | ||
40 | #define TXx9_SPMCR_OPMODE 0xc0 | ||
41 | #define TXx9_SPMCR_CONFIG 0x40 | ||
42 | #define TXx9_SPMCR_ACTIVE 0x80 | ||
43 | #define TXx9_SPMCR_SPSTP 0x02 | ||
44 | #define TXx9_SPMCR_BCLR 0x01 | ||
45 | |||
46 | /* SPCR0 : SPI Control 0 */ | ||
47 | #define TXx9_SPCR0_TXIFL_MASK 0xc000 | ||
48 | #define TXx9_SPCR0_RXIFL_MASK 0x3000 | ||
49 | #define TXx9_SPCR0_SIDIE 0x0800 | ||
50 | #define TXx9_SPCR0_SOEIE 0x0400 | ||
51 | #define TXx9_SPCR0_RBSIE 0x0200 | ||
52 | #define TXx9_SPCR0_TBSIE 0x0100 | ||
53 | #define TXx9_SPCR0_IFSPSE 0x0010 | ||
54 | #define TXx9_SPCR0_SBOS 0x0004 | ||
55 | #define TXx9_SPCR0_SPHA 0x0002 | ||
56 | #define TXx9_SPCR0_SPOL 0x0001 | ||
57 | |||
58 | /* SPSR : SPI Status */ | ||
59 | #define TXx9_SPSR_TBSI 0x8000 | ||
60 | #define TXx9_SPSR_RBSI 0x4000 | ||
61 | #define TXx9_SPSR_TBS_MASK 0x3800 | ||
62 | #define TXx9_SPSR_RBS_MASK 0x0700 | ||
63 | #define TXx9_SPSR_SPOE 0x0080 | ||
64 | #define TXx9_SPSR_IFSD 0x0008 | ||
65 | #define TXx9_SPSR_SIDLE 0x0004 | ||
66 | #define TXx9_SPSR_STRDY 0x0002 | ||
67 | #define TXx9_SPSR_SRRDY 0x0001 | ||
68 | |||
69 | |||
70 | struct txx9spi { | ||
71 | struct workqueue_struct *workqueue; | ||
72 | struct work_struct work; | ||
73 | spinlock_t lock; /* protect 'queue' */ | ||
74 | struct list_head queue; | ||
75 | wait_queue_head_t waitq; | ||
76 | void __iomem *membase; | ||
77 | int irq; | ||
78 | int baseclk; | ||
79 | struct clk *clk; | ||
80 | u32 max_speed_hz, min_speed_hz; | ||
81 | int last_chipselect; | ||
82 | int last_chipselect_val; | ||
83 | }; | ||
84 | |||
85 | static u32 txx9spi_rd(struct txx9spi *c, int reg) | ||
86 | { | ||
87 | return __raw_readl(c->membase + reg); | ||
88 | } | ||
89 | static void txx9spi_wr(struct txx9spi *c, u32 val, int reg) | ||
90 | { | ||
91 | __raw_writel(val, c->membase + reg); | ||
92 | } | ||
93 | |||
94 | static void txx9spi_cs_func(struct spi_device *spi, struct txx9spi *c, | ||
95 | int on, unsigned int cs_delay) | ||
96 | { | ||
97 | int val = (spi->mode & SPI_CS_HIGH) ? on : !on; | ||
98 | if (on) { | ||
99 | /* deselect the chip with cs_change hint in last transfer */ | ||
100 | if (c->last_chipselect >= 0) | ||
101 | gpio_set_value(c->last_chipselect, | ||
102 | !c->last_chipselect_val); | ||
103 | c->last_chipselect = spi->chip_select; | ||
104 | c->last_chipselect_val = val; | ||
105 | } else { | ||
106 | c->last_chipselect = -1; | ||
107 | ndelay(cs_delay); /* CS Hold Time */ | ||
108 | } | ||
109 | gpio_set_value(spi->chip_select, val); | ||
110 | ndelay(cs_delay); /* CS Setup Time / CS Recovery Time */ | ||
111 | } | ||
112 | |||
113 | /* the spi->mode bits understood by this driver: */ | ||
114 | #define MODEBITS (SPI_CS_HIGH|SPI_CPOL|SPI_CPHA) | ||
115 | |||
116 | static int txx9spi_setup(struct spi_device *spi) | ||
117 | { | ||
118 | struct txx9spi *c = spi_master_get_devdata(spi->master); | ||
119 | u8 bits_per_word; | ||
120 | |||
121 | if (spi->mode & ~MODEBITS) | ||
122 | return -EINVAL; | ||
123 | |||
124 | if (!spi->max_speed_hz | ||
125 | || spi->max_speed_hz > c->max_speed_hz | ||
126 | || spi->max_speed_hz < c->min_speed_hz) | ||
127 | return -EINVAL; | ||
128 | |||
129 | bits_per_word = spi->bits_per_word ? : 8; | ||
130 | if (bits_per_word != 8 && bits_per_word != 16) | ||
131 | return -EINVAL; | ||
132 | |||
133 | if (gpio_direction_output(spi->chip_select, | ||
134 | !(spi->mode & SPI_CS_HIGH))) { | ||
135 | dev_err(&spi->dev, "Cannot setup GPIO for chipselect.\n"); | ||
136 | return -EINVAL; | ||
137 | } | ||
138 | |||
139 | /* deselect chip */ | ||
140 | spin_lock(&c->lock); | ||
141 | txx9spi_cs_func(spi, c, 0, (NSEC_PER_SEC / 2) / spi->max_speed_hz); | ||
142 | spin_unlock(&c->lock); | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static irqreturn_t txx9spi_interrupt(int irq, void *dev_id) | ||
148 | { | ||
149 | struct txx9spi *c = dev_id; | ||
150 | |||
151 | /* disable rx intr */ | ||
152 | txx9spi_wr(c, txx9spi_rd(c, TXx9_SPCR0) & ~TXx9_SPCR0_RBSIE, | ||
153 | TXx9_SPCR0); | ||
154 | wake_up(&c->waitq); | ||
155 | return IRQ_HANDLED; | ||
156 | } | ||
157 | |||
158 | static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m) | ||
159 | { | ||
160 | struct spi_device *spi = m->spi; | ||
161 | struct spi_transfer *t; | ||
162 | unsigned int cs_delay; | ||
163 | unsigned int cs_change = 1; | ||
164 | int status = 0; | ||
165 | u32 mcr; | ||
166 | u32 prev_speed_hz = 0; | ||
167 | u8 prev_bits_per_word = 0; | ||
168 | |||
169 | /* CS setup/hold/recovery time in nsec */ | ||
170 | cs_delay = 100 + (NSEC_PER_SEC / 2) / spi->max_speed_hz; | ||
171 | |||
172 | mcr = txx9spi_rd(c, TXx9_SPMCR); | ||
173 | if (unlikely((mcr & TXx9_SPMCR_OPMODE) == TXx9_SPMCR_ACTIVE)) { | ||
174 | dev_err(&spi->dev, "Bad mode.\n"); | ||
175 | status = -EIO; | ||
176 | goto exit; | ||
177 | } | ||
178 | mcr &= ~(TXx9_SPMCR_OPMODE | TXx9_SPMCR_SPSTP | TXx9_SPMCR_BCLR); | ||
179 | |||
180 | /* enter config mode */ | ||
181 | txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR, TXx9_SPMCR); | ||
182 | txx9spi_wr(c, TXx9_SPCR0_SBOS | ||
183 | | ((spi->mode & SPI_CPOL) ? TXx9_SPCR0_SPOL : 0) | ||
184 | | ((spi->mode & SPI_CPHA) ? TXx9_SPCR0_SPHA : 0) | ||
185 | | 0x08, | ||
186 | TXx9_SPCR0); | ||
187 | |||
188 | list_for_each_entry (t, &m->transfers, transfer_list) { | ||
189 | const void *txbuf = t->tx_buf; | ||
190 | void *rxbuf = t->rx_buf; | ||
191 | u32 data; | ||
192 | unsigned int len = t->len; | ||
193 | unsigned int wsize; | ||
194 | u32 speed_hz = t->speed_hz ? : spi->max_speed_hz; | ||
195 | u8 bits_per_word = t->bits_per_word ? : spi->bits_per_word; | ||
196 | |||
197 | bits_per_word = bits_per_word ? : 8; | ||
198 | wsize = bits_per_word >> 3; /* in bytes */ | ||
199 | |||
200 | if (prev_speed_hz != speed_hz | ||
201 | || prev_bits_per_word != bits_per_word) { | ||
202 | u32 n = (c->baseclk + speed_hz - 1) / speed_hz; | ||
203 | if (n < 1) | ||
204 | n = 1; | ||
205 | else if (n > 0xff) | ||
206 | n = 0xff; | ||
207 | /* enter config mode */ | ||
208 | txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR, | ||
209 | TXx9_SPMCR); | ||
210 | txx9spi_wr(c, (n << 8) | bits_per_word, TXx9_SPCR1); | ||
211 | /* enter active mode */ | ||
212 | txx9spi_wr(c, mcr | TXx9_SPMCR_ACTIVE, TXx9_SPMCR); | ||
213 | |||
214 | prev_speed_hz = speed_hz; | ||
215 | prev_bits_per_word = bits_per_word; | ||
216 | } | ||
217 | |||
218 | if (cs_change) | ||
219 | txx9spi_cs_func(spi, c, 1, cs_delay); | ||
220 | cs_change = t->cs_change; | ||
221 | while (len) { | ||
222 | unsigned int count = SPI_FIFO_SIZE; | ||
223 | int i; | ||
224 | u32 cr0; | ||
225 | |||
226 | if (len < count * wsize) | ||
227 | count = len / wsize; | ||
228 | /* now tx must be idle... */ | ||
229 | while (!(txx9spi_rd(c, TXx9_SPSR) & TXx9_SPSR_SIDLE)) | ||
230 | cpu_relax(); | ||
231 | cr0 = txx9spi_rd(c, TXx9_SPCR0); | ||
232 | cr0 &= ~TXx9_SPCR0_RXIFL_MASK; | ||
233 | cr0 |= (count - 1) << 12; | ||
234 | /* enable rx intr */ | ||
235 | cr0 |= TXx9_SPCR0_RBSIE; | ||
236 | txx9spi_wr(c, cr0, TXx9_SPCR0); | ||
237 | /* send */ | ||
238 | for (i = 0; i < count; i++) { | ||
239 | if (txbuf) { | ||
240 | data = (wsize == 1) | ||
241 | ? *(const u8 *)txbuf | ||
242 | : *(const u16 *)txbuf; | ||
243 | txx9spi_wr(c, data, TXx9_SPDR); | ||
244 | txbuf += wsize; | ||
245 | } else | ||
246 | txx9spi_wr(c, 0, TXx9_SPDR); | ||
247 | } | ||
248 | /* wait all rx data */ | ||
249 | wait_event(c->waitq, | ||
250 | txx9spi_rd(c, TXx9_SPSR) & TXx9_SPSR_RBSI); | ||
251 | /* receive */ | ||
252 | for (i = 0; i < count; i++) { | ||
253 | data = txx9spi_rd(c, TXx9_SPDR); | ||
254 | if (rxbuf) { | ||
255 | if (wsize == 1) | ||
256 | *(u8 *)rxbuf = data; | ||
257 | else | ||
258 | *(u16 *)rxbuf = data; | ||
259 | rxbuf += wsize; | ||
260 | } | ||
261 | } | ||
262 | len -= count * wsize; | ||
263 | } | ||
264 | m->actual_length += t->len; | ||
265 | if (t->delay_usecs) | ||
266 | udelay(t->delay_usecs); | ||
267 | |||
268 | if (!cs_change) | ||
269 | continue; | ||
270 | if (t->transfer_list.next == &m->transfers) | ||
271 | break; | ||
272 | /* sometimes a short mid-message deselect of the chip | ||
273 | * may be needed to terminate a mode or command | ||
274 | */ | ||
275 | txx9spi_cs_func(spi, c, 0, cs_delay); | ||
276 | } | ||
277 | |||
278 | exit: | ||
279 | m->status = status; | ||
280 | m->complete(m->context); | ||
281 | |||
282 | /* normally deactivate chipselect ... unless no error and | ||
283 | * cs_change has hinted that the next message will probably | ||
284 | * be for this chip too. | ||
285 | */ | ||
286 | if (!(status == 0 && cs_change)) | ||
287 | txx9spi_cs_func(spi, c, 0, cs_delay); | ||
288 | |||
289 | /* enter config mode */ | ||
290 | txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR, TXx9_SPMCR); | ||
291 | } | ||
292 | |||
293 | static void txx9spi_work(struct work_struct *work) | ||
294 | { | ||
295 | struct txx9spi *c = container_of(work, struct txx9spi, work); | ||
296 | unsigned long flags; | ||
297 | |||
298 | spin_lock_irqsave(&c->lock, flags); | ||
299 | while (!list_empty(&c->queue)) { | ||
300 | struct spi_message *m; | ||
301 | |||
302 | m = container_of(c->queue.next, struct spi_message, queue); | ||
303 | list_del_init(&m->queue); | ||
304 | spin_unlock_irqrestore(&c->lock, flags); | ||
305 | |||
306 | txx9spi_work_one(c, m); | ||
307 | |||
308 | spin_lock_irqsave(&c->lock, flags); | ||
309 | } | ||
310 | spin_unlock_irqrestore(&c->lock, flags); | ||
311 | } | ||
312 | |||
313 | static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) | ||
314 | { | ||
315 | struct spi_master *master = spi->master; | ||
316 | struct txx9spi *c = spi_master_get_devdata(master); | ||
317 | struct spi_transfer *t; | ||
318 | unsigned long flags; | ||
319 | |||
320 | m->actual_length = 0; | ||
321 | |||
322 | /* check each transfer's parameters */ | ||
323 | list_for_each_entry (t, &m->transfers, transfer_list) { | ||
324 | u32 speed_hz = t->speed_hz ? : spi->max_speed_hz; | ||
325 | u8 bits_per_word = t->bits_per_word ? : spi->bits_per_word; | ||
326 | |||
327 | bits_per_word = bits_per_word ? : 8; | ||
328 | if (!t->tx_buf && !t->rx_buf && t->len) | ||
329 | return -EINVAL; | ||
330 | if (bits_per_word != 8 && bits_per_word != 16) | ||
331 | return -EINVAL; | ||
332 | if (t->len & ((bits_per_word >> 3) - 1)) | ||
333 | return -EINVAL; | ||
334 | if (speed_hz < c->min_speed_hz || speed_hz > c->max_speed_hz) | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | |||
338 | spin_lock_irqsave(&c->lock, flags); | ||
339 | list_add_tail(&m->queue, &c->queue); | ||
340 | queue_work(c->workqueue, &c->work); | ||
341 | spin_unlock_irqrestore(&c->lock, flags); | ||
342 | |||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static int __init txx9spi_probe(struct platform_device *dev) | ||
347 | { | ||
348 | struct spi_master *master; | ||
349 | struct txx9spi *c; | ||
350 | struct resource *res; | ||
351 | int ret = -ENODEV; | ||
352 | u32 mcr; | ||
353 | |||
354 | master = spi_alloc_master(&dev->dev, sizeof(*c)); | ||
355 | if (!master) | ||
356 | return ret; | ||
357 | c = spi_master_get_devdata(master); | ||
358 | c->irq = -1; | ||
359 | platform_set_drvdata(dev, master); | ||
360 | |||
361 | INIT_WORK(&c->work, txx9spi_work); | ||
362 | spin_lock_init(&c->lock); | ||
363 | INIT_LIST_HEAD(&c->queue); | ||
364 | init_waitqueue_head(&c->waitq); | ||
365 | |||
366 | c->clk = clk_get(&dev->dev, "spi-baseclk"); | ||
367 | if (IS_ERR(c->clk)) { | ||
368 | ret = PTR_ERR(c->clk); | ||
369 | c->clk = NULL; | ||
370 | goto exit; | ||
371 | } | ||
372 | ret = clk_enable(c->clk); | ||
373 | if (ret) { | ||
374 | clk_put(c->clk); | ||
375 | c->clk = NULL; | ||
376 | goto exit; | ||
377 | } | ||
378 | c->baseclk = clk_get_rate(c->clk); | ||
379 | c->min_speed_hz = (c->baseclk + 0xff - 1) / 0xff; | ||
380 | c->max_speed_hz = c->baseclk; | ||
381 | |||
382 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
383 | if (!res) | ||
384 | goto exit; | ||
385 | c->membase = ioremap(res->start, res->end - res->start + 1); | ||
386 | if (!c->membase) | ||
387 | goto exit; | ||
388 | |||
389 | /* enter config mode */ | ||
390 | mcr = txx9spi_rd(c, TXx9_SPMCR); | ||
391 | mcr &= ~(TXx9_SPMCR_OPMODE | TXx9_SPMCR_SPSTP | TXx9_SPMCR_BCLR); | ||
392 | txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR, TXx9_SPMCR); | ||
393 | |||
394 | c->irq = platform_get_irq(dev, 0); | ||
395 | if (c->irq < 0) | ||
396 | goto exit; | ||
397 | ret = request_irq(c->irq, txx9spi_interrupt, 0, dev->name, c); | ||
398 | if (ret) { | ||
399 | c->irq = -1; | ||
400 | goto exit; | ||
401 | } | ||
402 | |||
403 | c->workqueue = create_singlethread_workqueue(master->cdev.dev->bus_id); | ||
404 | if (!c->workqueue) | ||
405 | goto exit; | ||
406 | c->last_chipselect = -1; | ||
407 | |||
408 | dev_info(&dev->dev, "at %#llx, irq %d, %dMHz\n", | ||
409 | (unsigned long long)res->start, c->irq, | ||
410 | (c->baseclk + 500000) / 1000000); | ||
411 | |||
412 | master->bus_num = dev->id; | ||
413 | master->setup = txx9spi_setup; | ||
414 | master->transfer = txx9spi_transfer; | ||
415 | master->num_chipselect = (u16)UINT_MAX; /* any GPIO numbers */ | ||
416 | |||
417 | ret = spi_register_master(master); | ||
418 | if (ret) | ||
419 | goto exit; | ||
420 | return 0; | ||
421 | exit: | ||
422 | if (c->workqueue) | ||
423 | destroy_workqueue(c->workqueue); | ||
424 | if (c->irq >= 0) | ||
425 | free_irq(c->irq, c); | ||
426 | if (c->membase) | ||
427 | iounmap(c->membase); | ||
428 | if (c->clk) { | ||
429 | clk_disable(c->clk); | ||
430 | clk_put(c->clk); | ||
431 | } | ||
432 | platform_set_drvdata(dev, NULL); | ||
433 | spi_master_put(master); | ||
434 | return ret; | ||
435 | } | ||
436 | |||
437 | static int __exit txx9spi_remove(struct platform_device *dev) | ||
438 | { | ||
439 | struct spi_master *master = spi_master_get(platform_get_drvdata(dev)); | ||
440 | struct txx9spi *c = spi_master_get_devdata(master); | ||
441 | |||
442 | spi_unregister_master(master); | ||
443 | platform_set_drvdata(dev, NULL); | ||
444 | destroy_workqueue(c->workqueue); | ||
445 | free_irq(c->irq, c); | ||
446 | iounmap(c->membase); | ||
447 | clk_disable(c->clk); | ||
448 | clk_put(c->clk); | ||
449 | spi_master_put(master); | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static struct platform_driver txx9spi_driver = { | ||
454 | .remove = __exit_p(txx9spi_remove), | ||
455 | .driver = { | ||
456 | .name = "txx9spi", | ||
457 | .owner = THIS_MODULE, | ||
458 | }, | ||
459 | }; | ||
460 | |||
461 | static int __init txx9spi_init(void) | ||
462 | { | ||
463 | return platform_driver_probe(&txx9spi_driver, txx9spi_probe); | ||
464 | } | ||
465 | subsys_initcall(txx9spi_init); | ||
466 | |||
467 | static void __exit txx9spi_exit(void) | ||
468 | { | ||
469 | platform_driver_unregister(&txx9spi_driver); | ||
470 | } | ||
471 | module_exit(txx9spi_exit); | ||
472 | |||
473 | MODULE_DESCRIPTION("TXx9 SPI Driver"); | ||
474 | MODULE_LICENSE("GPL"); | ||