diff options
Diffstat (limited to 'arch/arm/mach-pxa/ssp.c')
-rw-r--r-- | arch/arm/mach-pxa/ssp.c | 384 |
1 files changed, 300 insertions, 84 deletions
diff --git a/arch/arm/mach-pxa/ssp.c b/arch/arm/mach-pxa/ssp.c index b2eb38543d1c..00af7f2fed66 100644 --- a/arch/arm/mach-pxa/ssp.c +++ b/arch/arm/mach-pxa/ssp.c | |||
@@ -32,45 +32,27 @@ | |||
32 | #include <linux/ioport.h> | 32 | #include <linux/ioport.h> |
33 | #include <linux/init.h> | 33 | #include <linux/init.h> |
34 | #include <linux/mutex.h> | 34 | #include <linux/mutex.h> |
35 | #include <linux/clk.h> | ||
36 | #include <linux/err.h> | ||
37 | #include <linux/platform_device.h> | ||
38 | |||
35 | #include <asm/io.h> | 39 | #include <asm/io.h> |
36 | #include <asm/irq.h> | 40 | #include <asm/irq.h> |
37 | #include <asm/hardware.h> | 41 | #include <asm/hardware.h> |
38 | #include <asm/arch/ssp.h> | 42 | #include <asm/arch/ssp.h> |
39 | #include <asm/arch/pxa-regs.h> | 43 | #include <asm/arch/pxa-regs.h> |
40 | 44 | #include <asm/arch/regs-ssp.h> | |
41 | #define PXA_SSP_PORTS 3 | ||
42 | 45 | ||
43 | #define TIMEOUT 100000 | 46 | #define TIMEOUT 100000 |
44 | 47 | ||
45 | struct ssp_info_ { | ||
46 | int irq; | ||
47 | u32 clock; | ||
48 | }; | ||
49 | |||
50 | /* | ||
51 | * SSP port clock and IRQ settings | ||
52 | */ | ||
53 | static const struct ssp_info_ ssp_info[PXA_SSP_PORTS] = { | ||
54 | #if defined (CONFIG_PXA27x) | ||
55 | {IRQ_SSP, CKEN_SSP1}, | ||
56 | {IRQ_SSP2, CKEN_SSP2}, | ||
57 | {IRQ_SSP3, CKEN_SSP3}, | ||
58 | #else | ||
59 | {IRQ_SSP, CKEN_SSP}, | ||
60 | {IRQ_NSSP, CKEN_NSSP}, | ||
61 | {IRQ_ASSP, CKEN_ASSP}, | ||
62 | #endif | ||
63 | }; | ||
64 | |||
65 | static DEFINE_MUTEX(mutex); | ||
66 | static int use_count[PXA_SSP_PORTS] = {0, 0, 0}; | ||
67 | |||
68 | static irqreturn_t ssp_interrupt(int irq, void *dev_id) | 48 | static irqreturn_t ssp_interrupt(int irq, void *dev_id) |
69 | { | 49 | { |
70 | struct ssp_dev *dev = dev_id; | 50 | struct ssp_dev *dev = dev_id; |
71 | unsigned int status = SSSR_P(dev->port); | 51 | struct ssp_device *ssp = dev->ssp; |
52 | unsigned int status; | ||
72 | 53 | ||
73 | SSSR_P(dev->port) = status; /* clear status bits */ | 54 | status = __raw_readl(ssp->mmio_base + SSSR); |
55 | __raw_writel(status, ssp->mmio_base + SSSR); | ||
74 | 56 | ||
75 | if (status & SSSR_ROR) | 57 | if (status & SSSR_ROR) |
76 | printk(KERN_WARNING "SSP(%d): receiver overrun\n", dev->port); | 58 | printk(KERN_WARNING "SSP(%d): receiver overrun\n", dev->port); |
@@ -99,15 +81,16 @@ static irqreturn_t ssp_interrupt(int irq, void *dev_id) | |||
99 | */ | 81 | */ |
100 | int ssp_write_word(struct ssp_dev *dev, u32 data) | 82 | int ssp_write_word(struct ssp_dev *dev, u32 data) |
101 | { | 83 | { |
84 | struct ssp_device *ssp = dev->ssp; | ||
102 | int timeout = TIMEOUT; | 85 | int timeout = TIMEOUT; |
103 | 86 | ||
104 | while (!(SSSR_P(dev->port) & SSSR_TNF)) { | 87 | while (!(__raw_readl(ssp->mmio_base + SSSR) & SSSR_TNF)) { |
105 | if (!--timeout) | 88 | if (!--timeout) |
106 | return -ETIMEDOUT; | 89 | return -ETIMEDOUT; |
107 | cpu_relax(); | 90 | cpu_relax(); |
108 | } | 91 | } |
109 | 92 | ||
110 | SSDR_P(dev->port) = data; | 93 | __raw_writel(data, ssp->mmio_base + SSDR); |
111 | 94 | ||
112 | return 0; | 95 | return 0; |
113 | } | 96 | } |
@@ -129,15 +112,16 @@ int ssp_write_word(struct ssp_dev *dev, u32 data) | |||
129 | */ | 112 | */ |
130 | int ssp_read_word(struct ssp_dev *dev, u32 *data) | 113 | int ssp_read_word(struct ssp_dev *dev, u32 *data) |
131 | { | 114 | { |
115 | struct ssp_device *ssp = dev->ssp; | ||
132 | int timeout = TIMEOUT; | 116 | int timeout = TIMEOUT; |
133 | 117 | ||
134 | while (!(SSSR_P(dev->port) & SSSR_RNE)) { | 118 | while (!(__raw_readl(ssp->mmio_base + SSSR) & SSSR_RNE)) { |
135 | if (!--timeout) | 119 | if (!--timeout) |
136 | return -ETIMEDOUT; | 120 | return -ETIMEDOUT; |
137 | cpu_relax(); | 121 | cpu_relax(); |
138 | } | 122 | } |
139 | 123 | ||
140 | *data = SSDR_P(dev->port); | 124 | *data = __raw_readl(ssp->mmio_base + SSDR); |
141 | return 0; | 125 | return 0; |
142 | } | 126 | } |
143 | 127 | ||
@@ -151,17 +135,28 @@ int ssp_read_word(struct ssp_dev *dev, u32 *data) | |||
151 | */ | 135 | */ |
152 | int ssp_flush(struct ssp_dev *dev) | 136 | int ssp_flush(struct ssp_dev *dev) |
153 | { | 137 | { |
138 | struct ssp_device *ssp = dev->ssp; | ||
154 | int timeout = TIMEOUT * 2; | 139 | int timeout = TIMEOUT * 2; |
155 | 140 | ||
141 | /* ensure TX FIFO is empty instead of not full */ | ||
142 | if (cpu_is_pxa3xx()) { | ||
143 | while (__raw_readl(ssp->mmio_base + SSSR) & 0xf00) { | ||
144 | if (!--timeout) | ||
145 | return -ETIMEDOUT; | ||
146 | cpu_relax(); | ||
147 | } | ||
148 | timeout = TIMEOUT * 2; | ||
149 | } | ||
150 | |||
156 | do { | 151 | do { |
157 | while (SSSR_P(dev->port) & SSSR_RNE) { | 152 | while (__raw_readl(ssp->mmio_base + SSSR) & SSSR_RNE) { |
158 | if (!--timeout) | 153 | if (!--timeout) |
159 | return -ETIMEDOUT; | 154 | return -ETIMEDOUT; |
160 | (void) SSDR_P(dev->port); | 155 | (void)__raw_readl(ssp->mmio_base + SSDR); |
161 | } | 156 | } |
162 | if (!--timeout) | 157 | if (!--timeout) |
163 | return -ETIMEDOUT; | 158 | return -ETIMEDOUT; |
164 | } while (SSSR_P(dev->port) & SSSR_BSY); | 159 | } while (__raw_readl(ssp->mmio_base + SSSR) & SSSR_BSY); |
165 | 160 | ||
166 | return 0; | 161 | return 0; |
167 | } | 162 | } |
@@ -173,7 +168,12 @@ int ssp_flush(struct ssp_dev *dev) | |||
173 | */ | 168 | */ |
174 | void ssp_enable(struct ssp_dev *dev) | 169 | void ssp_enable(struct ssp_dev *dev) |
175 | { | 170 | { |
176 | SSCR0_P(dev->port) |= SSCR0_SSE; | 171 | struct ssp_device *ssp = dev->ssp; |
172 | uint32_t sscr0; | ||
173 | |||
174 | sscr0 = __raw_readl(ssp->mmio_base + SSCR0); | ||
175 | sscr0 |= SSCR0_SSE; | ||
176 | __raw_writel(sscr0, ssp->mmio_base + SSCR0); | ||
177 | } | 177 | } |
178 | 178 | ||
179 | /** | 179 | /** |
@@ -183,7 +183,12 @@ void ssp_enable(struct ssp_dev *dev) | |||
183 | */ | 183 | */ |
184 | void ssp_disable(struct ssp_dev *dev) | 184 | void ssp_disable(struct ssp_dev *dev) |
185 | { | 185 | { |
186 | SSCR0_P(dev->port) &= ~SSCR0_SSE; | 186 | struct ssp_device *ssp = dev->ssp; |
187 | uint32_t sscr0; | ||
188 | |||
189 | sscr0 = __raw_readl(ssp->mmio_base + SSCR0); | ||
190 | sscr0 &= ~SSCR0_SSE; | ||
191 | __raw_writel(sscr0, ssp->mmio_base + SSCR0); | ||
187 | } | 192 | } |
188 | 193 | ||
189 | /** | 194 | /** |
@@ -192,14 +197,16 @@ void ssp_disable(struct ssp_dev *dev) | |||
192 | * | 197 | * |
193 | * Save the configured SSP state for suspend. | 198 | * Save the configured SSP state for suspend. |
194 | */ | 199 | */ |
195 | void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp) | 200 | void ssp_save_state(struct ssp_dev *dev, struct ssp_state *state) |
196 | { | 201 | { |
197 | ssp->cr0 = SSCR0_P(dev->port); | 202 | struct ssp_device *ssp = dev->ssp; |
198 | ssp->cr1 = SSCR1_P(dev->port); | 203 | |
199 | ssp->to = SSTO_P(dev->port); | 204 | state->cr0 = __raw_readl(ssp->mmio_base + SSCR0); |
200 | ssp->psp = SSPSP_P(dev->port); | 205 | state->cr1 = __raw_readl(ssp->mmio_base + SSCR1); |
206 | state->to = __raw_readl(ssp->mmio_base + SSTO); | ||
207 | state->psp = __raw_readl(ssp->mmio_base + SSPSP); | ||
201 | 208 | ||
202 | SSCR0_P(dev->port) &= ~SSCR0_SSE; | 209 | ssp_disable(dev); |
203 | } | 210 | } |
204 | 211 | ||
205 | /** | 212 | /** |
@@ -208,16 +215,18 @@ void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp) | |||
208 | * | 215 | * |
209 | * Restore the SSP configuration saved previously by ssp_save_state. | 216 | * Restore the SSP configuration saved previously by ssp_save_state. |
210 | */ | 217 | */ |
211 | void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp) | 218 | void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *state) |
212 | { | 219 | { |
213 | SSSR_P(dev->port) = SSSR_ROR | SSSR_TUR | SSSR_BCE; | 220 | struct ssp_device *ssp = dev->ssp; |
221 | uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE; | ||
214 | 222 | ||
215 | SSCR0_P(dev->port) = ssp->cr0 & ~SSCR0_SSE; | 223 | __raw_writel(sssr, ssp->mmio_base + SSSR); |
216 | SSCR1_P(dev->port) = ssp->cr1; | ||
217 | SSTO_P(dev->port) = ssp->to; | ||
218 | SSPSP_P(dev->port) = ssp->psp; | ||
219 | 224 | ||
220 | SSCR0_P(dev->port) = ssp->cr0; | 225 | __raw_writel(state->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0); |
226 | __raw_writel(state->cr1, ssp->mmio_base + SSCR1); | ||
227 | __raw_writel(state->to, ssp->mmio_base + SSTO); | ||
228 | __raw_writel(state->psp, ssp->mmio_base + SSPSP); | ||
229 | __raw_writel(state->cr0, ssp->mmio_base + SSCR0); | ||
221 | } | 230 | } |
222 | 231 | ||
223 | /** | 232 | /** |
@@ -231,15 +240,17 @@ void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp) | |||
231 | */ | 240 | */ |
232 | int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed) | 241 | int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed) |
233 | { | 242 | { |
243 | struct ssp_device *ssp = dev->ssp; | ||
244 | |||
234 | dev->mode = mode; | 245 | dev->mode = mode; |
235 | dev->flags = flags; | 246 | dev->flags = flags; |
236 | dev->psp_flags = psp_flags; | 247 | dev->psp_flags = psp_flags; |
237 | dev->speed = speed; | 248 | dev->speed = speed; |
238 | 249 | ||
239 | /* set up port type, speed, port settings */ | 250 | /* set up port type, speed, port settings */ |
240 | SSCR0_P(dev->port) = (dev->speed | dev->mode); | 251 | __raw_writel((dev->speed | dev->mode), ssp->mmio_base + SSCR0); |
241 | SSCR1_P(dev->port) = dev->flags; | 252 | __raw_writel(dev->flags, ssp->mmio_base + SSCR1); |
242 | SSPSP_P(dev->port) = dev->psp_flags; | 253 | __raw_writel(dev->psp_flags, ssp->mmio_base + SSPSP); |
243 | 254 | ||
244 | return 0; | 255 | return 0; |
245 | } | 256 | } |
@@ -256,44 +267,32 @@ int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 spee | |||
256 | */ | 267 | */ |
257 | int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags) | 268 | int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags) |
258 | { | 269 | { |
270 | struct ssp_device *ssp; | ||
259 | int ret; | 271 | int ret; |
260 | 272 | ||
261 | if (port > PXA_SSP_PORTS || port == 0) | 273 | ssp = ssp_request(port, "SSP"); |
274 | if (ssp == NULL) | ||
262 | return -ENODEV; | 275 | return -ENODEV; |
263 | 276 | ||
264 | mutex_lock(&mutex); | 277 | dev->ssp = ssp; |
265 | if (use_count[port - 1]) { | ||
266 | mutex_unlock(&mutex); | ||
267 | return -EBUSY; | ||
268 | } | ||
269 | use_count[port - 1]++; | ||
270 | |||
271 | if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) { | ||
272 | use_count[port - 1]--; | ||
273 | mutex_unlock(&mutex); | ||
274 | return -EBUSY; | ||
275 | } | ||
276 | dev->port = port; | 278 | dev->port = port; |
277 | 279 | ||
278 | /* do we need to get irq */ | 280 | /* do we need to get irq */ |
279 | if (!(init_flags & SSP_NO_IRQ)) { | 281 | if (!(init_flags & SSP_NO_IRQ)) { |
280 | ret = request_irq(ssp_info[port-1].irq, ssp_interrupt, | 282 | ret = request_irq(ssp->irq, ssp_interrupt, |
281 | 0, "SSP", dev); | 283 | 0, "SSP", dev); |
282 | if (ret) | 284 | if (ret) |
283 | goto out_region; | 285 | goto out_region; |
284 | dev->irq = ssp_info[port-1].irq; | 286 | dev->irq = ssp->irq; |
285 | } else | 287 | } else |
286 | dev->irq = 0; | 288 | dev->irq = 0; |
287 | 289 | ||
288 | /* turn on SSP port clock */ | 290 | /* turn on SSP port clock */ |
289 | pxa_set_cken(ssp_info[port-1].clock, 1); | 291 | clk_enable(ssp->clk); |
290 | mutex_unlock(&mutex); | ||
291 | return 0; | 292 | return 0; |
292 | 293 | ||
293 | out_region: | 294 | out_region: |
294 | release_mem_region(__PREG(SSCR0_P(port)), 0x2c); | 295 | ssp_free(ssp); |
295 | use_count[port - 1]--; | ||
296 | mutex_unlock(&mutex); | ||
297 | return ret; | 296 | return ret; |
298 | } | 297 | } |
299 | 298 | ||
@@ -304,23 +303,240 @@ out_region: | |||
304 | */ | 303 | */ |
305 | void ssp_exit(struct ssp_dev *dev) | 304 | void ssp_exit(struct ssp_dev *dev) |
306 | { | 305 | { |
307 | mutex_lock(&mutex); | 306 | struct ssp_device *ssp = dev->ssp; |
308 | SSCR0_P(dev->port) &= ~SSCR0_SSE; | 307 | |
308 | ssp_disable(dev); | ||
309 | free_irq(dev->irq, dev); | ||
310 | clk_disable(ssp->clk); | ||
311 | ssp_free(ssp); | ||
312 | } | ||
313 | |||
314 | static DEFINE_MUTEX(ssp_lock); | ||
315 | static LIST_HEAD(ssp_list); | ||
316 | |||
317 | struct ssp_device *ssp_request(int port, const char *label) | ||
318 | { | ||
319 | struct ssp_device *ssp = NULL; | ||
320 | |||
321 | mutex_lock(&ssp_lock); | ||
322 | |||
323 | list_for_each_entry(ssp, &ssp_list, node) { | ||
324 | if (ssp->port_id == port && ssp->use_count == 0) { | ||
325 | ssp->use_count++; | ||
326 | ssp->label = label; | ||
327 | break; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | mutex_unlock(&ssp_lock); | ||
332 | |||
333 | if (ssp->port_id != port) | ||
334 | return NULL; | ||
335 | |||
336 | return ssp; | ||
337 | } | ||
338 | EXPORT_SYMBOL(ssp_request); | ||
339 | |||
340 | void ssp_free(struct ssp_device *ssp) | ||
341 | { | ||
342 | mutex_lock(&ssp_lock); | ||
343 | if (ssp->use_count) { | ||
344 | ssp->use_count--; | ||
345 | ssp->label = NULL; | ||
346 | } else | ||
347 | dev_err(&ssp->pdev->dev, "device already free\n"); | ||
348 | mutex_unlock(&ssp_lock); | ||
349 | } | ||
350 | EXPORT_SYMBOL(ssp_free); | ||
351 | |||
352 | static int __devinit ssp_probe(struct platform_device *pdev, int type) | ||
353 | { | ||
354 | struct resource *res; | ||
355 | struct ssp_device *ssp; | ||
356 | int ret = 0; | ||
357 | |||
358 | ssp = kzalloc(sizeof(struct ssp_device), GFP_KERNEL); | ||
359 | if (ssp == NULL) { | ||
360 | dev_err(&pdev->dev, "failed to allocate memory"); | ||
361 | return -ENOMEM; | ||
362 | } | ||
363 | |||
364 | ssp->clk = clk_get(&pdev->dev, "SSPCLK"); | ||
365 | if (IS_ERR(ssp->clk)) { | ||
366 | ret = PTR_ERR(ssp->clk); | ||
367 | goto err_free; | ||
368 | } | ||
369 | |||
370 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
371 | if (res == NULL) { | ||
372 | dev_err(&pdev->dev, "no memory resource defined\n"); | ||
373 | ret = -ENODEV; | ||
374 | goto err_free_clk; | ||
375 | } | ||
376 | |||
377 | res = request_mem_region(res->start, res->end - res->start + 1, | ||
378 | pdev->name); | ||
379 | if (res == NULL) { | ||
380 | dev_err(&pdev->dev, "failed to request memory resource\n"); | ||
381 | ret = -EBUSY; | ||
382 | goto err_free_clk; | ||
383 | } | ||
384 | |||
385 | ssp->phys_base = res->start; | ||
386 | |||
387 | ssp->mmio_base = ioremap(res->start, res->end - res->start + 1); | ||
388 | if (ssp->mmio_base == NULL) { | ||
389 | dev_err(&pdev->dev, "failed to ioremap() registers\n"); | ||
390 | ret = -ENODEV; | ||
391 | goto err_free_mem; | ||
392 | } | ||
393 | |||
394 | ssp->irq = platform_get_irq(pdev, 0); | ||
395 | if (ssp->irq < 0) { | ||
396 | dev_err(&pdev->dev, "no IRQ resource defined\n"); | ||
397 | ret = -ENODEV; | ||
398 | goto err_free_io; | ||
399 | } | ||
400 | |||
401 | res = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
402 | if (res == NULL) { | ||
403 | dev_err(&pdev->dev, "no SSP RX DRCMR defined\n"); | ||
404 | ret = -ENODEV; | ||
405 | goto err_free_io; | ||
406 | } | ||
407 | ssp->drcmr_rx = res->start; | ||
408 | |||
409 | res = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
410 | if (res == NULL) { | ||
411 | dev_err(&pdev->dev, "no SSP TX DRCMR defined\n"); | ||
412 | ret = -ENODEV; | ||
413 | goto err_free_io; | ||
414 | } | ||
415 | ssp->drcmr_tx = res->start; | ||
416 | |||
417 | /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id | ||
418 | * starts from 0, do a translation here | ||
419 | */ | ||
420 | ssp->port_id = pdev->id + 1; | ||
421 | ssp->use_count = 0; | ||
422 | ssp->type = type; | ||
423 | |||
424 | mutex_lock(&ssp_lock); | ||
425 | list_add(&ssp->node, &ssp_list); | ||
426 | mutex_unlock(&ssp_lock); | ||
427 | |||
428 | platform_set_drvdata(pdev, ssp); | ||
429 | return 0; | ||
430 | |||
431 | err_free_io: | ||
432 | iounmap(ssp->mmio_base); | ||
433 | err_free_mem: | ||
434 | release_mem_region(res->start, res->end - res->start + 1); | ||
435 | err_free_clk: | ||
436 | clk_put(ssp->clk); | ||
437 | err_free: | ||
438 | kfree(ssp); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | static int __devexit ssp_remove(struct platform_device *pdev) | ||
443 | { | ||
444 | struct resource *res; | ||
445 | struct ssp_device *ssp; | ||
446 | |||
447 | ssp = platform_get_drvdata(pdev); | ||
448 | if (ssp == NULL) | ||
449 | return -ENODEV; | ||
450 | |||
451 | iounmap(ssp->mmio_base); | ||
452 | |||
453 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
454 | release_mem_region(res->start, res->end - res->start + 1); | ||
455 | |||
456 | clk_put(ssp->clk); | ||
309 | 457 | ||
310 | if (dev->port > PXA_SSP_PORTS || dev->port == 0) { | 458 | mutex_lock(&ssp_lock); |
311 | printk(KERN_WARNING "SSP: tried to close invalid port\n"); | 459 | list_del(&ssp->node); |
312 | mutex_unlock(&mutex); | 460 | mutex_unlock(&ssp_lock); |
313 | return; | 461 | |
462 | kfree(ssp); | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | static int __devinit pxa25x_ssp_probe(struct platform_device *pdev) | ||
467 | { | ||
468 | return ssp_probe(pdev, PXA25x_SSP); | ||
469 | } | ||
470 | |||
471 | static int __devinit pxa25x_nssp_probe(struct platform_device *pdev) | ||
472 | { | ||
473 | return ssp_probe(pdev, PXA25x_NSSP); | ||
474 | } | ||
475 | |||
476 | static int __devinit pxa27x_ssp_probe(struct platform_device *pdev) | ||
477 | { | ||
478 | return ssp_probe(pdev, PXA27x_SSP); | ||
479 | } | ||
480 | |||
481 | static struct platform_driver pxa25x_ssp_driver = { | ||
482 | .driver = { | ||
483 | .name = "pxa25x-ssp", | ||
484 | }, | ||
485 | .probe = pxa25x_ssp_probe, | ||
486 | .remove = __devexit_p(ssp_remove), | ||
487 | }; | ||
488 | |||
489 | static struct platform_driver pxa25x_nssp_driver = { | ||
490 | .driver = { | ||
491 | .name = "pxa25x-nssp", | ||
492 | }, | ||
493 | .probe = pxa25x_nssp_probe, | ||
494 | .remove = __devexit_p(ssp_remove), | ||
495 | }; | ||
496 | |||
497 | static struct platform_driver pxa27x_ssp_driver = { | ||
498 | .driver = { | ||
499 | .name = "pxa27x-ssp", | ||
500 | }, | ||
501 | .probe = pxa27x_ssp_probe, | ||
502 | .remove = __devexit_p(ssp_remove), | ||
503 | }; | ||
504 | |||
505 | static int __init pxa_ssp_init(void) | ||
506 | { | ||
507 | int ret = 0; | ||
508 | |||
509 | ret = platform_driver_register(&pxa25x_ssp_driver); | ||
510 | if (ret) { | ||
511 | printk(KERN_ERR "failed to register pxa25x_ssp_driver"); | ||
512 | return ret; | ||
513 | } | ||
514 | |||
515 | ret = platform_driver_register(&pxa25x_nssp_driver); | ||
516 | if (ret) { | ||
517 | printk(KERN_ERR "failed to register pxa25x_nssp_driver"); | ||
518 | return ret; | ||
519 | } | ||
520 | |||
521 | ret = platform_driver_register(&pxa27x_ssp_driver); | ||
522 | if (ret) { | ||
523 | printk(KERN_ERR "failed to register pxa27x_ssp_driver"); | ||
524 | return ret; | ||
314 | } | 525 | } |
315 | 526 | ||
316 | pxa_set_cken(ssp_info[dev->port-1].clock, 0); | 527 | return ret; |
317 | if (dev->irq) | 528 | } |
318 | free_irq(dev->irq, dev); | 529 | |
319 | release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c); | 530 | static void __exit pxa_ssp_exit(void) |
320 | use_count[dev->port - 1]--; | 531 | { |
321 | mutex_unlock(&mutex); | 532 | platform_driver_unregister(&pxa25x_ssp_driver); |
533 | platform_driver_unregister(&pxa25x_nssp_driver); | ||
534 | platform_driver_unregister(&pxa27x_ssp_driver); | ||
322 | } | 535 | } |
323 | 536 | ||
537 | arch_initcall(pxa_ssp_init); | ||
538 | module_exit(pxa_ssp_exit); | ||
539 | |||
324 | EXPORT_SYMBOL(ssp_write_word); | 540 | EXPORT_SYMBOL(ssp_write_word); |
325 | EXPORT_SYMBOL(ssp_read_word); | 541 | EXPORT_SYMBOL(ssp_read_word); |
326 | EXPORT_SYMBOL(ssp_flush); | 542 | EXPORT_SYMBOL(ssp_flush); |