diff options
author | Adrian McMenamin <adrian@newgolddream.dyndns.info> | 2008-02-06 18:51:21 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2008-02-14 00:22:07 -0500 |
commit | b3c69e248176f7a123d519d63e7c0d68783d52c3 (patch) | |
tree | 5cd739142e1a293f4d38642202591609d6c0a3d3 /drivers/sh | |
parent | b9482378916abb9a1e0a2334187cdc67f2deda2c (diff) |
maple: more robust device detection.
Replacement second-in-series patch:
This patch fixes up memory leaks and, by delaying initialisation, makes
device detection more robust.
It also makes clearer the difference between struct maple_device and
struct device, as well as cleaning up the interrupt request code
(without changing its function in any way).
Also now removes redundant registration checking.
Signed-off-by: Adrian McMenamin <adrian@mcmen.demon.co.uk>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/sh')
-rw-r--r-- | drivers/sh/maple/maple.c | 202 |
1 files changed, 105 insertions, 97 deletions
diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index 9c48ccc44c29..616e2266e913 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <asm/mach/dma.h> | 31 | #include <asm/mach/dma.h> |
32 | #include <asm/mach/sysasic.h> | 32 | #include <asm/mach/sysasic.h> |
33 | #include <asm/mach/maple.h> | 33 | #include <asm/mach/maple.h> |
34 | #include <linux/delay.h> | ||
34 | 35 | ||
35 | MODULE_AUTHOR("Yaegshi Takeshi, Paul Mundt, M.R. Brown, Adrian McMenamin"); | 36 | MODULE_AUTHOR("Yaegshi Takeshi, Paul Mundt, M.R. Brown, Adrian McMenamin"); |
36 | MODULE_DESCRIPTION("Maple bus driver for Dreamcast"); | 37 | MODULE_DESCRIPTION("Maple bus driver for Dreamcast"); |
@@ -53,7 +54,7 @@ static struct device maple_bus; | |||
53 | static int subdevice_map[MAPLE_PORTS]; | 54 | static int subdevice_map[MAPLE_PORTS]; |
54 | static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr; | 55 | static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr; |
55 | static unsigned long maple_pnp_time; | 56 | static unsigned long maple_pnp_time; |
56 | static int started, scanning, liststatus; | 57 | static int started, scanning, liststatus, realscan; |
57 | static struct kmem_cache *maple_queue_cache; | 58 | static struct kmem_cache *maple_queue_cache; |
58 | 59 | ||
59 | struct maple_device_specify { | 60 | struct maple_device_specify { |
@@ -73,7 +74,6 @@ int maple_driver_register(struct device_driver *drv) | |||
73 | drv->bus = &maple_bus_type; | 74 | drv->bus = &maple_bus_type; |
74 | return driver_register(drv); | 75 | return driver_register(drv); |
75 | } | 76 | } |
76 | |||
77 | EXPORT_SYMBOL_GPL(maple_driver_register); | 77 | EXPORT_SYMBOL_GPL(maple_driver_register); |
78 | 78 | ||
79 | /* set hardware registers to enable next round of dma */ | 79 | /* set hardware registers to enable next round of dma */ |
@@ -95,15 +95,14 @@ static void maplebus_dma_reset(void) | |||
95 | * @function: the function code for the device | 95 | * @function: the function code for the device |
96 | */ | 96 | */ |
97 | void maple_getcond_callback(struct maple_device *dev, | 97 | void maple_getcond_callback(struct maple_device *dev, |
98 | void (*callback) (struct mapleq * mq), | 98 | void (*callback) (struct mapleq *mq), |
99 | unsigned long interval, unsigned long function) | 99 | unsigned long interval, unsigned long function) |
100 | { | 100 | { |
101 | dev->callback = callback; | 101 | dev->callback = callback; |
102 | dev->interval = interval; | 102 | dev->interval = interval; |
103 | dev->function = cpu_to_be32(function); | 103 | dev->function = cpu_to_be32(function); |
104 | dev->when = jiffies; | 104 | dev->when = jiffies; |
105 | } | 105 | } |
106 | |||
107 | EXPORT_SYMBOL_GPL(maple_getcond_callback); | 106 | EXPORT_SYMBOL_GPL(maple_getcond_callback); |
108 | 107 | ||
109 | static int maple_dma_done(void) | 108 | static int maple_dma_done(void) |
@@ -113,10 +112,19 @@ static int maple_dma_done(void) | |||
113 | 112 | ||
114 | static void maple_release_device(struct device *dev) | 113 | static void maple_release_device(struct device *dev) |
115 | { | 114 | { |
116 | if (dev->type) { | 115 | struct maple_device *mdev; |
117 | kfree(dev->type->name); | 116 | struct mapleq *mq; |
118 | kfree(dev->type); | 117 | if (!dev) |
118 | return; | ||
119 | mdev = to_maple_dev(dev); | ||
120 | mq = mdev->mq; | ||
121 | if (mq) { | ||
122 | if (mq->recvbufdcsp) | ||
123 | kmem_cache_free(maple_queue_cache, mq->recvbufdcsp); | ||
124 | kfree(mq); | ||
125 | mq = NULL; | ||
119 | } | 126 | } |
127 | kfree(mdev); | ||
120 | } | 128 | } |
121 | 129 | ||
122 | /** | 130 | /** |
@@ -129,10 +137,9 @@ void maple_add_packet(struct mapleq *mq) | |||
129 | list_add(&mq->list, &maple_waitq); | 137 | list_add(&mq->list, &maple_waitq); |
130 | mutex_unlock(&maple_list_lock); | 138 | mutex_unlock(&maple_list_lock); |
131 | } | 139 | } |
132 | |||
133 | EXPORT_SYMBOL_GPL(maple_add_packet); | 140 | EXPORT_SYMBOL_GPL(maple_add_packet); |
134 | 141 | ||
135 | static struct mapleq *maple_allocq(struct maple_device *dev) | 142 | static struct mapleq *maple_allocq(struct maple_device *mdev) |
136 | { | 143 | { |
137 | struct mapleq *mq; | 144 | struct mapleq *mq; |
138 | 145 | ||
@@ -140,7 +147,7 @@ static struct mapleq *maple_allocq(struct maple_device *dev) | |||
140 | if (!mq) | 147 | if (!mq) |
141 | return NULL; | 148 | return NULL; |
142 | 149 | ||
143 | mq->dev = dev; | 150 | mq->dev = mdev; |
144 | mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL); | 151 | mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL); |
145 | mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp); | 152 | mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp); |
146 | if (!mq->recvbuf) { | 153 | if (!mq->recvbuf) { |
@@ -153,22 +160,24 @@ static struct mapleq *maple_allocq(struct maple_device *dev) | |||
153 | 160 | ||
154 | static struct maple_device *maple_alloc_dev(int port, int unit) | 161 | static struct maple_device *maple_alloc_dev(int port, int unit) |
155 | { | 162 | { |
156 | struct maple_device *dev; | 163 | struct maple_device *mdev; |
157 | 164 | ||
158 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | 165 | mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); |
159 | if (!dev) | 166 | if (!mdev) |
160 | return NULL; | 167 | return NULL; |
161 | 168 | ||
162 | dev->port = port; | 169 | mdev->port = port; |
163 | dev->unit = unit; | 170 | mdev->unit = unit; |
164 | dev->mq = maple_allocq(dev); | 171 | mdev->mq = maple_allocq(mdev); |
165 | 172 | ||
166 | if (!dev->mq) { | 173 | if (!mdev->mq) { |
167 | kfree(dev); | 174 | kfree(mdev); |
168 | return NULL; | 175 | return NULL; |
169 | } | 176 | } |
170 | 177 | mdev->dev.bus = &maple_bus_type; | |
171 | return dev; | 178 | mdev->dev.parent = &maple_bus; |
179 | mdev->function = 0; | ||
180 | return mdev; | ||
172 | } | 181 | } |
173 | 182 | ||
174 | static void maple_free_dev(struct maple_device *mdev) | 183 | static void maple_free_dev(struct maple_device *mdev) |
@@ -176,7 +185,9 @@ static void maple_free_dev(struct maple_device *mdev) | |||
176 | if (!mdev) | 185 | if (!mdev) |
177 | return; | 186 | return; |
178 | if (mdev->mq) { | 187 | if (mdev->mq) { |
179 | kmem_cache_free(maple_queue_cache, mdev->mq->recvbufdcsp); | 188 | if (mdev->mq->recvbufdcsp) |
189 | kmem_cache_free(maple_queue_cache, | ||
190 | mdev->mq->recvbufdcsp); | ||
180 | kfree(mdev->mq); | 191 | kfree(mdev->mq); |
181 | } | 192 | } |
182 | kfree(mdev); | 193 | kfree(mdev); |
@@ -260,80 +271,89 @@ static void maple_detach_driver(struct maple_device *mdev) | |||
260 | mdev->driver->disconnect(mdev); | 271 | mdev->driver->disconnect(mdev); |
261 | } | 272 | } |
262 | mdev->driver = NULL; | 273 | mdev->driver = NULL; |
263 | if (mdev->registered) { | 274 | device_unregister(&mdev->dev); |
264 | maple_release_device(&mdev->dev); | 275 | mdev = NULL; |
265 | device_unregister(&mdev->dev); | ||
266 | } | ||
267 | mdev->registered = 0; | ||
268 | maple_free_dev(mdev); | ||
269 | } | 276 | } |
270 | 277 | ||
271 | /* process initial MAPLE_COMMAND_DEVINFO for each device or port */ | 278 | /* process initial MAPLE_COMMAND_DEVINFO for each device or port */ |
272 | static void maple_attach_driver(struct maple_device *dev) | 279 | static void maple_attach_driver(struct maple_device *mdev) |
273 | { | 280 | { |
274 | char *p; | 281 | char *p, *recvbuf; |
275 | |||
276 | char *recvbuf; | ||
277 | unsigned long function; | 282 | unsigned long function; |
278 | int matched, retval; | 283 | int matched, retval; |
279 | 284 | ||
280 | recvbuf = dev->mq->recvbuf; | 285 | recvbuf = mdev->mq->recvbuf; |
281 | memcpy(&dev->devinfo, recvbuf + 4, sizeof(dev->devinfo)); | 286 | /* copy the data as individual elements in |
282 | memcpy(dev->product_name, dev->devinfo.product_name, 30); | 287 | * case of memory optimisation */ |
283 | memcpy(dev->product_licence, dev->devinfo.product_licence, 60); | 288 | memcpy(&mdev->devinfo.function, recvbuf + 4, 4); |
284 | dev->product_name[30] = '\0'; | 289 | memcpy(&mdev->devinfo.function_data[0], recvbuf + 8, 12); |
285 | dev->product_licence[60] = '\0'; | 290 | memcpy(&mdev->devinfo.area_code, recvbuf + 20, 1); |
286 | 291 | memcpy(&mdev->devinfo.connector_direction, recvbuf + 21, 1); | |
287 | for (p = dev->product_name + 29; dev->product_name <= p; p--) | 292 | memcpy(&mdev->devinfo.product_name[0], recvbuf + 22, 30); |
293 | memcpy(&mdev->devinfo.product_licence[0], recvbuf + 52, 60); | ||
294 | memcpy(&mdev->devinfo.standby_power, recvbuf + 112, 2); | ||
295 | memcpy(&mdev->devinfo.max_power, recvbuf + 114, 2); | ||
296 | memcpy(mdev->product_name, mdev->devinfo.product_name, 30); | ||
297 | mdev->product_name[30] = '\0'; | ||
298 | memcpy(mdev->product_licence, mdev->devinfo.product_licence, 60); | ||
299 | mdev->product_licence[60] = '\0'; | ||
300 | |||
301 | for (p = mdev->product_name + 29; mdev->product_name <= p; p--) | ||
288 | if (*p == ' ') | 302 | if (*p == ' ') |
289 | *p = '\0'; | 303 | *p = '\0'; |
290 | else | 304 | else |
291 | break; | 305 | break; |
292 | 306 | for (p = mdev->product_licence + 59; mdev->product_licence <= p; p--) | |
293 | for (p = dev->product_licence + 59; dev->product_licence <= p; p--) | ||
294 | if (*p == ' ') | 307 | if (*p == ' ') |
295 | *p = '\0'; | 308 | *p = '\0'; |
296 | else | 309 | else |
297 | break; | 310 | break; |
298 | 311 | ||
299 | function = be32_to_cpu(dev->devinfo.function); | 312 | if (realscan) { |
313 | printk(KERN_INFO "Maple device detected: %s\n", | ||
314 | mdev->product_name); | ||
315 | printk(KERN_INFO "Maple device: %s\n", mdev->product_licence); | ||
316 | } | ||
317 | |||
318 | function = be32_to_cpu(mdev->devinfo.function); | ||
300 | 319 | ||
301 | if (function > 0x200) { | 320 | if (function > 0x200) { |
302 | /* Do this silently - as not a real device */ | 321 | /* Do this silently - as not a real device */ |
303 | function = 0; | 322 | function = 0; |
304 | dev->driver = &maple_dummy_driver; | 323 | mdev->driver = &maple_dummy_driver; |
305 | sprintf(dev->dev.bus_id, "%d:0.port", dev->port); | 324 | sprintf(mdev->dev.bus_id, "%d:0.port", mdev->port); |
306 | } else { | 325 | } else { |
307 | printk(KERN_INFO | 326 | if (realscan) |
308 | "Maple bus at (%d, %d): Connected function 0x%lX\n", | 327 | printk(KERN_INFO |
309 | dev->port, dev->unit, function); | 328 | "Maple bus at (%d, %d): Function 0x%lX\n", |
329 | mdev->port, mdev->unit, function); | ||
310 | 330 | ||
311 | matched = | 331 | matched = |
312 | bus_for_each_drv(&maple_bus_type, NULL, dev, | 332 | bus_for_each_drv(&maple_bus_type, NULL, mdev, |
313 | attach_matching_maple_driver); | 333 | attach_matching_maple_driver); |
314 | 334 | ||
315 | if (matched == 0) { | 335 | if (matched == 0) { |
316 | /* Driver does not exist yet */ | 336 | /* Driver does not exist yet */ |
317 | printk(KERN_INFO | 337 | if (realscan) |
318 | "No maple driver found for this device\n"); | 338 | printk(KERN_INFO |
319 | dev->driver = &maple_dummy_driver; | 339 | "No maple driver found.\n"); |
340 | mdev->driver = &maple_dummy_driver; | ||
320 | } | 341 | } |
321 | 342 | sprintf(mdev->dev.bus_id, "%d:0%d.%lX", mdev->port, | |
322 | sprintf(dev->dev.bus_id, "%d:0%d.%lX", dev->port, | 343 | mdev->unit, function); |
323 | dev->unit, function); | ||
324 | } | 344 | } |
325 | dev->function = function; | 345 | mdev->function = function; |
326 | dev->dev.bus = &maple_bus_type; | 346 | mdev->dev.release = &maple_release_device; |
327 | dev->dev.parent = &maple_bus; | 347 | retval = device_register(&mdev->dev); |
328 | dev->dev.release = &maple_release_device; | ||
329 | retval = device_register(&dev->dev); | ||
330 | if (retval) { | 348 | if (retval) { |
331 | printk(KERN_INFO | 349 | printk(KERN_INFO |
332 | "Maple bus: Attempt to register device (%x, %x) failed.\n", | 350 | "Maple bus: Attempt to register device" |
333 | dev->port, dev->unit); | 351 | " (%x, %x) failed.\n", |
334 | maple_free_dev(dev); | 352 | mdev->port, mdev->unit); |
353 | maple_free_dev(mdev); | ||
354 | mdev = NULL; | ||
355 | return; | ||
335 | } | 356 | } |
336 | dev->registered = 1; | ||
337 | } | 357 | } |
338 | 358 | ||
339 | /* | 359 | /* |
@@ -519,7 +539,8 @@ static void maple_dma_handler(struct work_struct *work) | |||
519 | 539 | ||
520 | case MAPLE_RESPONSE_ALLINFO: | 540 | case MAPLE_RESPONSE_ALLINFO: |
521 | printk(KERN_DEBUG | 541 | printk(KERN_DEBUG |
522 | "Maple - extended device information not supported\n"); | 542 | "Maple - extended device information" |
543 | " not supported\n"); | ||
523 | break; | 544 | break; |
524 | 545 | ||
525 | case MAPLE_RESPONSE_OK: | 546 | case MAPLE_RESPONSE_OK: |
@@ -555,26 +576,16 @@ static irqreturn_t maplebus_vblank_interrupt(int irq, void *dev_id) | |||
555 | return IRQ_HANDLED; | 576 | return IRQ_HANDLED; |
556 | } | 577 | } |
557 | 578 | ||
558 | static struct irqaction maple_dma_irq = { | ||
559 | .name = "maple bus DMA handler", | ||
560 | .handler = maplebus_dma_interrupt, | ||
561 | .flags = IRQF_SHARED, | ||
562 | }; | ||
563 | |||
564 | static struct irqaction maple_vblank_irq = { | ||
565 | .name = "maple bus VBLANK handler", | ||
566 | .handler = maplebus_vblank_interrupt, | ||
567 | .flags = IRQF_SHARED, | ||
568 | }; | ||
569 | |||
570 | static int maple_set_dma_interrupt_handler(void) | 579 | static int maple_set_dma_interrupt_handler(void) |
571 | { | 580 | { |
572 | return setup_irq(HW_EVENT_MAPLE_DMA, &maple_dma_irq); | 581 | return request_irq(HW_EVENT_MAPLE_DMA, maplebus_dma_interrupt, |
582 | IRQF_SHARED, "maple bus DMA", &maple_dummy_driver); | ||
573 | } | 583 | } |
574 | 584 | ||
575 | static int maple_set_vblank_interrupt_handler(void) | 585 | static int maple_set_vblank_interrupt_handler(void) |
576 | { | 586 | { |
577 | return setup_irq(HW_EVENT_VSYNC, &maple_vblank_irq); | 587 | return request_irq(HW_EVENT_VSYNC, maplebus_vblank_interrupt, |
588 | IRQF_SHARED, "maple bus VBLANK", &maple_dummy_driver); | ||
578 | } | 589 | } |
579 | 590 | ||
580 | static int maple_get_dma_buffer(void) | 591 | static int maple_get_dma_buffer(void) |
@@ -618,7 +629,7 @@ static struct maple_driver maple_dummy_driver = { | |||
618 | .drv = { | 629 | .drv = { |
619 | .name = "maple_dummy_driver", | 630 | .name = "maple_dummy_driver", |
620 | .bus = &maple_bus_type, | 631 | .bus = &maple_bus_type, |
621 | }, | 632 | }, |
622 | }; | 633 | }; |
623 | 634 | ||
624 | struct bus_type maple_bus_type = { | 635 | struct bus_type maple_bus_type = { |
@@ -626,7 +637,6 @@ struct bus_type maple_bus_type = { | |||
626 | .match = match_maple_bus_driver, | 637 | .match = match_maple_bus_driver, |
627 | .uevent = maple_bus_uevent, | 638 | .uevent = maple_bus_uevent, |
628 | }; | 639 | }; |
629 | |||
630 | EXPORT_SYMBOL_GPL(maple_bus_type); | 640 | EXPORT_SYMBOL_GPL(maple_bus_type); |
631 | 641 | ||
632 | static struct device maple_bus = { | 642 | static struct device maple_bus = { |
@@ -678,7 +688,7 @@ static int __init maple_bus_init(void) | |||
678 | 688 | ||
679 | maple_queue_cache = | 689 | maple_queue_cache = |
680 | kmem_cache_create("maple_queue_cache", 0x400, 0, | 690 | kmem_cache_create("maple_queue_cache", 0x400, 0, |
681 | SLAB_HWCACHE_ALIGN, NULL); | 691 | SLAB_POISON|SLAB_HWCACHE_ALIGN, NULL); |
682 | 692 | ||
683 | if (!maple_queue_cache) | 693 | if (!maple_queue_cache) |
684 | goto cleanup_bothirqs; | 694 | goto cleanup_bothirqs; |
@@ -691,50 +701,48 @@ static int __init maple_bus_init(void) | |||
691 | maple_free_dev(mdev[i]); | 701 | maple_free_dev(mdev[i]); |
692 | goto cleanup_cache; | 702 | goto cleanup_cache; |
693 | } | 703 | } |
694 | mdev[i]->registered = 0; | ||
695 | mdev[i]->mq->command = MAPLE_COMMAND_DEVINFO; | 704 | mdev[i]->mq->command = MAPLE_COMMAND_DEVINFO; |
696 | mdev[i]->mq->length = 0; | 705 | mdev[i]->mq->length = 0; |
697 | maple_attach_driver(mdev[i]); | ||
698 | maple_add_packet(mdev[i]->mq); | 706 | maple_add_packet(mdev[i]->mq); |
707 | /* delay aids hardware detection */ | ||
708 | udelay(20); | ||
699 | subdevice_map[i] = 0; | 709 | subdevice_map[i] = 0; |
700 | } | 710 | } |
701 | 711 | ||
712 | realscan = 1; | ||
702 | /* setup maplebus hardware */ | 713 | /* setup maplebus hardware */ |
703 | maplebus_dma_reset(); | 714 | maplebus_dma_reset(); |
704 | |||
705 | /* initial detection */ | 715 | /* initial detection */ |
706 | maple_send(); | 716 | maple_send(); |
707 | |||
708 | maple_pnp_time = jiffies; | 717 | maple_pnp_time = jiffies; |
709 | |||
710 | printk(KERN_INFO "Maple bus core now registered.\n"); | 718 | printk(KERN_INFO "Maple bus core now registered.\n"); |
711 | 719 | ||
712 | return 0; | 720 | return 0; |
713 | 721 | ||
714 | cleanup_cache: | 722 | cleanup_cache: |
715 | kmem_cache_destroy(maple_queue_cache); | 723 | kmem_cache_destroy(maple_queue_cache); |
716 | 724 | ||
717 | cleanup_bothirqs: | 725 | cleanup_bothirqs: |
718 | free_irq(HW_EVENT_VSYNC, 0); | 726 | free_irq(HW_EVENT_VSYNC, 0); |
719 | 727 | ||
720 | cleanup_irq: | 728 | cleanup_irq: |
721 | free_irq(HW_EVENT_MAPLE_DMA, 0); | 729 | free_irq(HW_EVENT_MAPLE_DMA, 0); |
722 | 730 | ||
723 | cleanup_dma: | 731 | cleanup_dma: |
724 | free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES); | 732 | free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES); |
725 | 733 | ||
726 | cleanup_basic: | 734 | cleanup_basic: |
727 | driver_unregister(&maple_dummy_driver.drv); | 735 | driver_unregister(&maple_dummy_driver.drv); |
728 | 736 | ||
729 | cleanup_bus: | 737 | cleanup_bus: |
730 | bus_unregister(&maple_bus_type); | 738 | bus_unregister(&maple_bus_type); |
731 | 739 | ||
732 | cleanup_device: | 740 | cleanup_device: |
733 | device_unregister(&maple_bus); | 741 | device_unregister(&maple_bus); |
734 | 742 | ||
735 | cleanup: | 743 | cleanup: |
736 | printk(KERN_INFO "Maple bus registration failed\n"); | 744 | printk(KERN_INFO "Maple bus registration failed\n"); |
737 | return retval; | 745 | return retval; |
738 | } | 746 | } |
739 | 747 | /* Push init to later to ensure hardware gets detected */ | |
740 | subsys_initcall(maple_bus_init); | 748 | fs_initcall(maple_bus_init); |