blob: be97789fa5fdb267eb5931d5b2dfb39f135314af [file] [log] [blame]
Adrian McMenamin17be2d22007-09-21 15:55:55 +09001/*
2 * Core maple bus functionality
3 *
Adrian McMenaminbd496662008-02-24 14:30:23 +00004 * Copyright (C) 2007, 2008 Adrian McMenamin
Adrian McMenamin17be2d22007-09-21 15:55:55 +09005 *
6 * Based on 2.4 code by:
7 *
8 * Copyright (C) 2000-2001 YAEGASHI Takeshi
9 * Copyright (C) 2001 M. R. Brown
10 * Copyright (C) 2001 Paul Mundt
11 *
12 * and others.
13 *
14 * This file is subject to the terms and conditions of the GNU General Public
15 * License. See the file "COPYING" in the main directory of this archive
16 * for more details.
17 */
18#include <linux/init.h>
19#include <linux/kernel.h>
20#include <linux/device.h>
Adrian McMenamin17be2d22007-09-21 15:55:55 +090021#include <linux/interrupt.h>
22#include <linux/list.h>
23#include <linux/io.h>
24#include <linux/slab.h>
25#include <linux/maple.h>
26#include <linux/dma-mapping.h>
Adrian McMenamin1795cf42008-07-29 22:10:56 +090027#include <linux/delay.h>
Adrian McMenamin17be2d22007-09-21 15:55:55 +090028#include <asm/cacheflush.h>
29#include <asm/dma.h>
30#include <asm/io.h>
Adrian McMenamin1795cf42008-07-29 22:10:56 +090031#include <mach/dma.h>
32#include <mach/sysasic.h>
Adrian McMenamin17be2d22007-09-21 15:55:55 +090033
34MODULE_AUTHOR("Yaegshi Takeshi, Paul Mundt, M.R. Brown, Adrian McMenamin");
35MODULE_DESCRIPTION("Maple bus driver for Dreamcast");
36MODULE_LICENSE("GPL v2");
37MODULE_SUPPORTED_DEVICE("{{SEGA, Dreamcast/Maple}}");
38
39static void maple_dma_handler(struct work_struct *work);
40static void maple_vblank_handler(struct work_struct *work);
41
42static DECLARE_WORK(maple_dma_process, maple_dma_handler);
43static DECLARE_WORK(maple_vblank_process, maple_vblank_handler);
44
45static LIST_HEAD(maple_waitq);
46static LIST_HEAD(maple_sentq);
47
Adrian McMenamin1795cf42008-07-29 22:10:56 +090048/* mutex to protect queue of waiting packets */
49static DEFINE_MUTEX(maple_wlist_lock);
Adrian McMenamin17be2d22007-09-21 15:55:55 +090050
51static struct maple_driver maple_dummy_driver;
52static struct device maple_bus;
53static int subdevice_map[MAPLE_PORTS];
54static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr;
55static unsigned long maple_pnp_time;
Adrian McMenamin1795cf42008-07-29 22:10:56 +090056static int started, scanning, fullscan;
Adrian McMenamin17be2d22007-09-21 15:55:55 +090057static struct kmem_cache *maple_queue_cache;
58
59struct maple_device_specify {
Adrian McMenaminb9482372008-02-06 22:46:21 +000060 int port;
61 int unit;
Adrian McMenamin17be2d22007-09-21 15:55:55 +090062};
63
Adrian McMenaminbd496662008-02-24 14:30:23 +000064static bool checked[4];
65static struct maple_device *baseunits[4];
66
Adrian McMenamin17be2d22007-09-21 15:55:55 +090067/**
68 * maple_driver_register - register a device driver
69 * automatically makes the driver bus a maple bus
70 * @drv: the driver to be registered
71 */
72int maple_driver_register(struct device_driver *drv)
73{
Adrian McMenaminb9482372008-02-06 22:46:21 +000074 if (!drv)
75 return -EINVAL;
76 drv->bus = &maple_bus_type;
77 return driver_register(drv);
Adrian McMenamin17be2d22007-09-21 15:55:55 +090078}
79EXPORT_SYMBOL_GPL(maple_driver_register);
80
81/* set hardware registers to enable next round of dma */
82static void maplebus_dma_reset(void)
83{
Adrian McMenaminb9482372008-02-06 22:46:21 +000084 ctrl_outl(MAPLE_MAGIC, MAPLE_RESET);
85 /* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */
86 ctrl_outl(1, MAPLE_TRIGTYPE);
87 ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(50000), MAPLE_SPEED);
88 ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR);
89 ctrl_outl(1, MAPLE_ENABLE);
Adrian McMenamin17be2d22007-09-21 15:55:55 +090090}
91
92/**
93 * maple_getcond_callback - setup handling MAPLE_COMMAND_GETCOND
94 * @dev: device responding
95 * @callback: handler callback
96 * @interval: interval in jiffies between callbacks
97 * @function: the function code for the device
98 */
99void maple_getcond_callback(struct maple_device *dev,
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000100 void (*callback) (struct mapleq *mq),
101 unsigned long interval, unsigned long function)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900102{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000103 dev->callback = callback;
104 dev->interval = interval;
105 dev->function = cpu_to_be32(function);
106 dev->when = jiffies;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900107}
108EXPORT_SYMBOL_GPL(maple_getcond_callback);
109
110static int maple_dma_done(void)
111{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000112 return (ctrl_inl(MAPLE_STATE) & 1) == 0;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900113}
114
115static void maple_release_device(struct device *dev)
116{
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000117 struct maple_device *mdev;
118 struct mapleq *mq;
119 if (!dev)
120 return;
121 mdev = to_maple_dev(dev);
122 mq = mdev->mq;
123 if (mq) {
124 if (mq->recvbufdcsp)
125 kmem_cache_free(maple_queue_cache, mq->recvbufdcsp);
126 kfree(mq);
127 mq = NULL;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000128 }
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000129 kfree(mdev);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900130}
131
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900132/*
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900133 * maple_add_packet - add a single instruction to the queue
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900134 * @mdev - maple device
135 * @function - function on device being queried
136 * @command - maple command to add
137 * @length - length of command string (in 32 bit words)
138 * @data - remainder of command string
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900139 */
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900140int maple_add_packet(struct maple_device *mdev, u32 function, u32 command,
141 size_t length, void *data)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900142{
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900143 int locking, ret = 0;
144 void *sendbuf = NULL;
145
146 mutex_lock(&maple_wlist_lock);
147 /* bounce if device already locked */
148 locking = mutex_is_locked(&mdev->mq->mutex);
149 if (locking) {
150 ret = -EBUSY;
151 goto out;
152 }
153
154 mutex_lock(&mdev->mq->mutex);
155
156 if (length) {
157 sendbuf = kmalloc(length * 4, GFP_KERNEL);
158 if (!sendbuf) {
159 mutex_unlock(&mdev->mq->mutex);
160 ret = -ENOMEM;
161 goto out;
162 }
163 ((__be32 *)sendbuf)[0] = cpu_to_be32(function);
164 }
165
166 mdev->mq->command = command;
167 mdev->mq->length = length;
168 if (length > 1)
169 memcpy(sendbuf + 4, data, (length - 1) * 4);
170 mdev->mq->sendbuf = sendbuf;
171
172 list_add(&mdev->mq->list, &maple_waitq);
173out:
174 mutex_unlock(&maple_wlist_lock);
175 return ret;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900176}
177EXPORT_SYMBOL_GPL(maple_add_packet);
178
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900179/*
180 * maple_add_packet_sleeps - add a single instruction to the queue
181 * - waits for lock to be free
182 * @mdev - maple device
183 * @function - function on device being queried
184 * @command - maple command to add
185 * @length - length of command string (in 32 bit words)
186 * @data - remainder of command string
187 */
188int maple_add_packet_sleeps(struct maple_device *mdev, u32 function,
189 u32 command, size_t length, void *data)
190{
191 int locking, ret = 0;
192 void *sendbuf = NULL;
193
194 locking = mutex_lock_interruptible(&mdev->mq->mutex);
195 if (locking) {
196 ret = -EIO;
197 goto out;
198 }
199
200 if (length) {
201 sendbuf = kmalloc(length * 4, GFP_KERNEL);
202 if (!sendbuf) {
203 mutex_unlock(&mdev->mq->mutex);
204 ret = -ENOMEM;
205 goto out;
206 }
207 ((__be32 *)sendbuf)[0] = cpu_to_be32(function);
208 }
209
210 mdev->mq->command = command;
211 mdev->mq->length = length;
212 if (length > 1)
213 memcpy(sendbuf + 4, data, (length - 1) * 4);
214 mdev->mq->sendbuf = sendbuf;
215
216 mutex_lock(&maple_wlist_lock);
217 list_add(&mdev->mq->list, &maple_waitq);
218 mutex_unlock(&maple_wlist_lock);
219out:
220 return ret;
221}
222EXPORT_SYMBOL_GPL(maple_add_packet_sleeps);
223
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000224static struct mapleq *maple_allocq(struct maple_device *mdev)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900225{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000226 struct mapleq *mq;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900227
Adrian McMenaminb9482372008-02-06 22:46:21 +0000228 mq = kmalloc(sizeof(*mq), GFP_KERNEL);
229 if (!mq)
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900230 goto failed_nomem;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900231
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000232 mq->dev = mdev;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000233 mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
234 mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp);
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900235 if (!mq->recvbuf)
236 goto failed_p2;
237 /*
238 * most devices do not need the mutex - but
239 * anything that injects block reads or writes
240 * will rely on it
241 */
242 mutex_init(&mq->mutex);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900243
Adrian McMenaminb9482372008-02-06 22:46:21 +0000244 return mq;
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900245
246failed_p2:
247 kfree(mq);
248failed_nomem:
249 return NULL;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900250}
251
252static struct maple_device *maple_alloc_dev(int port, int unit)
253{
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000254 struct maple_device *mdev;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900255
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000256 mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
257 if (!mdev)
Adrian McMenaminb9482372008-02-06 22:46:21 +0000258 return NULL;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900259
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000260 mdev->port = port;
261 mdev->unit = unit;
262 mdev->mq = maple_allocq(mdev);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900263
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000264 if (!mdev->mq) {
265 kfree(mdev);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000266 return NULL;
267 }
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000268 mdev->dev.bus = &maple_bus_type;
269 mdev->dev.parent = &maple_bus;
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000270 return mdev;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900271}
272
273static void maple_free_dev(struct maple_device *mdev)
274{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000275 if (!mdev)
276 return;
277 if (mdev->mq) {
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000278 if (mdev->mq->recvbufdcsp)
279 kmem_cache_free(maple_queue_cache,
280 mdev->mq->recvbufdcsp);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000281 kfree(mdev->mq);
282 }
283 kfree(mdev);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900284}
285
286/* process the command queue into a maple command block
287 * terminating command has bit 32 of first long set to 0
288 */
289static void maple_build_block(struct mapleq *mq)
290{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000291 int port, unit, from, to, len;
292 unsigned long *lsendbuf = mq->sendbuf;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900293
Adrian McMenaminb9482372008-02-06 22:46:21 +0000294 port = mq->dev->port & 3;
295 unit = mq->dev->unit;
296 len = mq->length;
297 from = port << 6;
298 to = (port << 6) | (unit > 0 ? (1 << (unit - 1)) & 0x1f : 0x20);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900299
Adrian McMenaminb9482372008-02-06 22:46:21 +0000300 *maple_lastptr &= 0x7fffffff;
301 maple_lastptr = maple_sendptr;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900302
Adrian McMenaminb9482372008-02-06 22:46:21 +0000303 *maple_sendptr++ = (port << 16) | len | 0x80000000;
304 *maple_sendptr++ = PHYSADDR(mq->recvbuf);
305 *maple_sendptr++ =
306 mq->command | (to << 8) | (from << 16) | (len << 24);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000307 while (len-- > 0)
308 *maple_sendptr++ = *lsendbuf++;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900309}
310
311/* build up command queue */
312static void maple_send(void)
313{
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900314 int i, maple_packets = 0;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000315 struct mapleq *mq, *nmq;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900316
Adrian McMenaminb9482372008-02-06 22:46:21 +0000317 if (!list_empty(&maple_sentq))
318 return;
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900319 mutex_lock(&maple_wlist_lock);
320 if (list_empty(&maple_waitq) || !maple_dma_done()) {
321 mutex_unlock(&maple_wlist_lock);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000322 return;
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900323 }
324 mutex_unlock(&maple_wlist_lock);
325 maple_lastptr = maple_sendbuf;
326 maple_sendptr = maple_sendbuf;
327 mutex_lock(&maple_wlist_lock);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000328 list_for_each_entry_safe(mq, nmq, &maple_waitq, list) {
329 maple_build_block(mq);
330 list_move(&mq->list, &maple_sentq);
331 if (maple_packets++ > MAPLE_MAXPACKETS)
332 break;
333 }
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900334 mutex_unlock(&maple_wlist_lock);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000335 if (maple_packets > 0) {
336 for (i = 0; i < (1 << MAPLE_DMA_PAGES); i++)
337 dma_cache_sync(0, maple_sendbuf + i * PAGE_SIZE,
338 PAGE_SIZE, DMA_BIDIRECTIONAL);
339 }
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900340}
341
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900342/* check if there is a driver registered likely to match this device */
343static int check_matching_maple_driver(struct device_driver *driver,
Adrian McMenaminb9482372008-02-06 22:46:21 +0000344 void *devptr)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900345{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000346 struct maple_driver *maple_drv;
347 struct maple_device *mdev;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900348
Adrian McMenaminb9482372008-02-06 22:46:21 +0000349 mdev = devptr;
350 maple_drv = to_maple_driver(driver);
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900351 if (mdev->devinfo.function & cpu_to_be32(maple_drv->function))
352 return 1;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000353 return 0;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900354}
355
356static void maple_detach_driver(struct maple_device *mdev)
357{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000358 if (!mdev)
359 return;
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000360 device_unregister(&mdev->dev);
361 mdev = NULL;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900362}
363
364/* process initial MAPLE_COMMAND_DEVINFO for each device or port */
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000365static void maple_attach_driver(struct maple_device *mdev)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900366{
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000367 char *p, *recvbuf;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000368 unsigned long function;
369 int matched, retval;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900370
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000371 recvbuf = mdev->mq->recvbuf;
372 /* copy the data as individual elements in
373 * case of memory optimisation */
374 memcpy(&mdev->devinfo.function, recvbuf + 4, 4);
375 memcpy(&mdev->devinfo.function_data[0], recvbuf + 8, 12);
376 memcpy(&mdev->devinfo.area_code, recvbuf + 20, 1);
377 memcpy(&mdev->devinfo.connector_direction, recvbuf + 21, 1);
378 memcpy(&mdev->devinfo.product_name[0], recvbuf + 22, 30);
379 memcpy(&mdev->devinfo.product_licence[0], recvbuf + 52, 60);
380 memcpy(&mdev->devinfo.standby_power, recvbuf + 112, 2);
381 memcpy(&mdev->devinfo.max_power, recvbuf + 114, 2);
382 memcpy(mdev->product_name, mdev->devinfo.product_name, 30);
383 mdev->product_name[30] = '\0';
384 memcpy(mdev->product_licence, mdev->devinfo.product_licence, 60);
385 mdev->product_licence[60] = '\0';
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900386
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000387 for (p = mdev->product_name + 29; mdev->product_name <= p; p--)
388 if (*p == ' ')
389 *p = '\0';
390 else
391 break;
392 for (p = mdev->product_licence + 59; mdev->product_licence <= p; p--)
Adrian McMenaminb9482372008-02-06 22:46:21 +0000393 if (*p == ' ')
394 *p = '\0';
395 else
396 break;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900397
Adrian McMenaminbd496662008-02-24 14:30:23 +0000398 printk(KERN_INFO "Maple device detected: %s\n",
399 mdev->product_name);
400 printk(KERN_INFO "Maple device: %s\n", mdev->product_licence);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900401
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000402 function = be32_to_cpu(mdev->devinfo.function);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900403
Adrian McMenaminb9482372008-02-06 22:46:21 +0000404 if (function > 0x200) {
405 /* Do this silently - as not a real device */
406 function = 0;
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000407 mdev->driver = &maple_dummy_driver;
408 sprintf(mdev->dev.bus_id, "%d:0.port", mdev->port);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000409 } else {
Adrian McMenaminbd496662008-02-24 14:30:23 +0000410 printk(KERN_INFO
411 "Maple bus at (%d, %d): Function 0x%lX\n",
412 mdev->port, mdev->unit, function);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900413
Adrian McMenaminb9482372008-02-06 22:46:21 +0000414 matched =
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900415 bus_for_each_drv(&maple_bus_type, NULL, mdev,
416 check_matching_maple_driver);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900417
Adrian McMenaminb9482372008-02-06 22:46:21 +0000418 if (matched == 0) {
419 /* Driver does not exist yet */
Adrian McMenaminbd496662008-02-24 14:30:23 +0000420 printk(KERN_INFO
421 "No maple driver found.\n");
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000422 mdev->driver = &maple_dummy_driver;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000423 }
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000424 sprintf(mdev->dev.bus_id, "%d:0%d.%lX", mdev->port,
425 mdev->unit, function);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000426 }
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000427 mdev->function = function;
428 mdev->dev.release = &maple_release_device;
429 retval = device_register(&mdev->dev);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000430 if (retval) {
431 printk(KERN_INFO
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000432 "Maple bus: Attempt to register device"
433 " (%x, %x) failed.\n",
434 mdev->port, mdev->unit);
435 maple_free_dev(mdev);
436 mdev = NULL;
437 return;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000438 }
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900439}
440
441/*
442 * if device has been registered for the given
443 * port and unit then return 1 - allows identification
444 * of which devices need to be attached or detached
445 */
446static int detach_maple_device(struct device *device, void *portptr)
447{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000448 struct maple_device_specify *ds;
449 struct maple_device *mdev;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900450
Adrian McMenaminb9482372008-02-06 22:46:21 +0000451 ds = portptr;
452 mdev = to_maple_dev(device);
453 if (mdev->port == ds->port && mdev->unit == ds->unit)
454 return 1;
455 return 0;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900456}
457
458static int setup_maple_commands(struct device *device, void *ignored)
459{
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900460 int add;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000461 struct maple_device *maple_dev = to_maple_dev(device);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900462
Adrian McMenaminb9482372008-02-06 22:46:21 +0000463 if ((maple_dev->interval > 0)
464 && time_after(jiffies, maple_dev->when)) {
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900465 /* bounce if we cannot lock */
466 add = maple_add_packet(maple_dev,
467 be32_to_cpu(maple_dev->devinfo.function),
468 MAPLE_COMMAND_GETCOND, 1, NULL);
469 if (!add)
470 maple_dev->when = jiffies + maple_dev->interval;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000471 } else {
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900472 if (time_after(jiffies, maple_pnp_time))
473 /* This will also bounce */
474 maple_add_packet(maple_dev, 0,
475 MAPLE_COMMAND_DEVINFO, 0, NULL);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000476 }
Adrian McMenaminb9482372008-02-06 22:46:21 +0000477 return 0;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900478}
479
480/* VBLANK bottom half - implemented via workqueue */
481static void maple_vblank_handler(struct work_struct *work)
482{
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900483 if (!list_empty(&maple_sentq) || !maple_dma_done())
Adrian McMenaminb9482372008-02-06 22:46:21 +0000484 return;
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900485
Adrian McMenaminb9482372008-02-06 22:46:21 +0000486 ctrl_outl(0, MAPLE_ENABLE);
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900487
Adrian McMenaminb9482372008-02-06 22:46:21 +0000488 bus_for_each_dev(&maple_bus_type, NULL, NULL,
489 setup_maple_commands);
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900490
Adrian McMenaminb9482372008-02-06 22:46:21 +0000491 if (time_after(jiffies, maple_pnp_time))
492 maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL;
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900493
494 mutex_lock(&maple_wlist_lock);
495 if (!list_empty(&maple_waitq) && list_empty(&maple_sentq)) {
496 mutex_unlock(&maple_wlist_lock);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000497 maple_send();
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900498 } else {
499 mutex_unlock(&maple_wlist_lock);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000500 }
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900501
Adrian McMenaminb9482372008-02-06 22:46:21 +0000502 maplebus_dma_reset();
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900503}
504
505/* handle devices added via hotplugs - placing them on queue for DEVINFO*/
506static void maple_map_subunits(struct maple_device *mdev, int submask)
507{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000508 int retval, k, devcheck;
509 struct maple_device *mdev_add;
510 struct maple_device_specify ds;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900511
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900512 ds.port = mdev->port;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000513 for (k = 0; k < 5; k++) {
Adrian McMenaminb9482372008-02-06 22:46:21 +0000514 ds.unit = k + 1;
515 retval =
516 bus_for_each_dev(&maple_bus_type, NULL, &ds,
517 detach_maple_device);
518 if (retval) {
519 submask = submask >> 1;
520 continue;
521 }
522 devcheck = submask & 0x01;
523 if (devcheck) {
524 mdev_add = maple_alloc_dev(mdev->port, k + 1);
525 if (!mdev_add)
526 return;
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900527 maple_add_packet(mdev_add, 0, MAPLE_COMMAND_DEVINFO,
528 0, NULL);
529 /* mark that we are checking sub devices */
Adrian McMenaminb9482372008-02-06 22:46:21 +0000530 scanning = 1;
531 }
532 submask = submask >> 1;
533 }
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900534}
535
536/* mark a device as removed */
537static void maple_clean_submap(struct maple_device *mdev)
538{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000539 int killbit;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900540
Adrian McMenaminb9482372008-02-06 22:46:21 +0000541 killbit = (mdev->unit > 0 ? (1 << (mdev->unit - 1)) & 0x1f : 0x20);
542 killbit = ~killbit;
543 killbit &= 0xFF;
544 subdevice_map[mdev->port] = subdevice_map[mdev->port] & killbit;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900545}
546
547/* handle empty port or hotplug removal */
548static void maple_response_none(struct maple_device *mdev,
Adrian McMenaminb9482372008-02-06 22:46:21 +0000549 struct mapleq *mq)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900550{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000551 if (mdev->unit != 0) {
552 list_del(&mq->list);
553 maple_clean_submap(mdev);
554 printk(KERN_INFO
555 "Maple bus device detaching at (%d, %d)\n",
556 mdev->port, mdev->unit);
557 maple_detach_driver(mdev);
558 return;
559 }
Adrian McMenaminbd496662008-02-24 14:30:23 +0000560 if (!started || !fullscan) {
561 if (checked[mdev->port] == false) {
562 checked[mdev->port] = true;
563 printk(KERN_INFO "No maple devices attached"
564 " to port %d\n", mdev->port);
565 }
Adrian McMenaminb9482372008-02-06 22:46:21 +0000566 return;
567 }
568 maple_clean_submap(mdev);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900569}
570
571/* preprocess hotplugs or scans */
572static void maple_response_devinfo(struct maple_device *mdev,
Adrian McMenaminb9482372008-02-06 22:46:21 +0000573 char *recvbuf)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900574{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000575 char submask;
Adrian McMenaminbd496662008-02-24 14:30:23 +0000576 if (!started || (scanning == 2) || !fullscan) {
577 if ((mdev->unit == 0) && (checked[mdev->port] == false)) {
578 checked[mdev->port] = true;
579 maple_attach_driver(mdev);
580 } else {
581 if (mdev->unit != 0)
582 maple_attach_driver(mdev);
583 }
Adrian McMenaminb9482372008-02-06 22:46:21 +0000584 return;
585 }
586 if (mdev->unit == 0) {
587 submask = recvbuf[2] & 0x1F;
588 if (submask ^ subdevice_map[mdev->port]) {
589 maple_map_subunits(mdev, submask);
590 subdevice_map[mdev->port] = submask;
591 }
592 }
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900593}
594
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900595static void maple_port_rescan(void)
596{
597 int i;
598 struct maple_device *mdev;
599
600 fullscan = 1;
601 for (i = 0; i < MAPLE_PORTS; i++) {
602 if (checked[i] == false) {
603 fullscan = 0;
604 mdev = baseunits[i];
605 /*
606 * test lock in case scan has failed
607 * but device is still locked
608 */
609 if (mutex_is_locked(&mdev->mq->mutex))
610 mutex_unlock(&mdev->mq->mutex);
611 maple_add_packet(mdev, 0, MAPLE_COMMAND_DEVINFO,
612 0, NULL);
613 }
614 }
615}
616
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900617/* maple dma end bottom half - implemented via workqueue */
618static void maple_dma_handler(struct work_struct *work)
619{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000620 struct mapleq *mq, *nmq;
621 struct maple_device *dev;
622 char *recvbuf;
623 enum maple_code code;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900624
Adrian McMenaminb9482372008-02-06 22:46:21 +0000625 if (!maple_dma_done())
626 return;
627 ctrl_outl(0, MAPLE_ENABLE);
628 if (!list_empty(&maple_sentq)) {
629 list_for_each_entry_safe(mq, nmq, &maple_sentq, list) {
630 recvbuf = mq->recvbuf;
631 code = recvbuf[0];
632 dev = mq->dev;
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900633 kfree(mq->sendbuf);
634 mutex_unlock(&mq->mutex);
635 list_del_init(&mq->list);
636
Adrian McMenaminb9482372008-02-06 22:46:21 +0000637 switch (code) {
638 case MAPLE_RESPONSE_NONE:
639 maple_response_none(dev, mq);
640 break;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900641
Adrian McMenaminb9482372008-02-06 22:46:21 +0000642 case MAPLE_RESPONSE_DEVINFO:
643 maple_response_devinfo(dev, recvbuf);
644 break;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900645
Adrian McMenaminb9482372008-02-06 22:46:21 +0000646 case MAPLE_RESPONSE_DATATRF:
647 if (dev->callback)
648 dev->callback(mq);
649 break;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900650
Adrian McMenaminb9482372008-02-06 22:46:21 +0000651 case MAPLE_RESPONSE_FILEERR:
652 case MAPLE_RESPONSE_AGAIN:
653 case MAPLE_RESPONSE_BADCMD:
654 case MAPLE_RESPONSE_BADFUNC:
655 printk(KERN_DEBUG
656 "Maple non-fatal error 0x%X\n",
657 code);
658 break;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900659
Adrian McMenaminb9482372008-02-06 22:46:21 +0000660 case MAPLE_RESPONSE_ALLINFO:
661 printk(KERN_DEBUG
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000662 "Maple - extended device information"
663 " not supported\n");
Adrian McMenaminb9482372008-02-06 22:46:21 +0000664 break;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900665
Adrian McMenaminb9482372008-02-06 22:46:21 +0000666 case MAPLE_RESPONSE_OK:
667 break;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900668
Adrian McMenaminb9482372008-02-06 22:46:21 +0000669 default:
670 break;
671 }
672 }
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900673 /* if scanning is 1 then we have subdevices to check */
Adrian McMenaminb9482372008-02-06 22:46:21 +0000674 if (scanning == 1) {
675 maple_send();
676 scanning = 2;
677 } else
678 scanning = 0;
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900679 /*check if we have actually tested all ports yet */
680 if (!fullscan)
681 maple_port_rescan();
682 /* mark that we have been through the first scan */
Adrian McMenaminb9482372008-02-06 22:46:21 +0000683 if (started == 0)
684 started = 1;
685 }
686 maplebus_dma_reset();
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900687}
688
689static irqreturn_t maplebus_dma_interrupt(int irq, void *dev_id)
690{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000691 /* Load everything into the bottom half */
692 schedule_work(&maple_dma_process);
693 return IRQ_HANDLED;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900694}
695
696static irqreturn_t maplebus_vblank_interrupt(int irq, void *dev_id)
697{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000698 schedule_work(&maple_vblank_process);
699 return IRQ_HANDLED;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900700}
701
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900702static int maple_set_dma_interrupt_handler(void)
703{
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000704 return request_irq(HW_EVENT_MAPLE_DMA, maplebus_dma_interrupt,
705 IRQF_SHARED, "maple bus DMA", &maple_dummy_driver);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900706}
707
708static int maple_set_vblank_interrupt_handler(void)
709{
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000710 return request_irq(HW_EVENT_VSYNC, maplebus_vblank_interrupt,
711 IRQF_SHARED, "maple bus VBLANK", &maple_dummy_driver);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900712}
713
714static int maple_get_dma_buffer(void)
715{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000716 maple_sendbuf =
717 (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO,
718 MAPLE_DMA_PAGES);
719 if (!maple_sendbuf)
720 return -ENOMEM;
721 return 0;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900722}
723
724static int match_maple_bus_driver(struct device *devptr,
Adrian McMenaminb9482372008-02-06 22:46:21 +0000725 struct device_driver *drvptr)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900726{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000727 struct maple_driver *maple_drv;
728 struct maple_device *maple_dev;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900729
Adrian McMenaminb9482372008-02-06 22:46:21 +0000730 maple_drv = container_of(drvptr, struct maple_driver, drv);
731 maple_dev = container_of(devptr, struct maple_device, dev);
732 /* Trap empty port case */
733 if (maple_dev->devinfo.function == 0xFFFFFFFF)
734 return 0;
735 else if (maple_dev->devinfo.function &
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900736 cpu_to_be32(maple_drv->function))
Adrian McMenaminb9482372008-02-06 22:46:21 +0000737 return 1;
738 return 0;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900739}
740
Adrian McMenaminb9482372008-02-06 22:46:21 +0000741static int maple_bus_uevent(struct device *dev,
742 struct kobj_uevent_env *env)
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900743{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000744 return 0;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900745}
746
747static void maple_bus_release(struct device *dev)
748{
749}
750
751static struct maple_driver maple_dummy_driver = {
Adrian McMenaminb9482372008-02-06 22:46:21 +0000752 .drv = {
753 .name = "maple_dummy_driver",
754 .bus = &maple_bus_type,
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000755 },
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900756};
757
758struct bus_type maple_bus_type = {
Adrian McMenaminb9482372008-02-06 22:46:21 +0000759 .name = "maple",
760 .match = match_maple_bus_driver,
761 .uevent = maple_bus_uevent,
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900762};
763EXPORT_SYMBOL_GPL(maple_bus_type);
764
765static struct device maple_bus = {
Adrian McMenaminb9482372008-02-06 22:46:21 +0000766 .bus_id = "maple",
767 .release = maple_bus_release,
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900768};
769
770static int __init maple_bus_init(void)
771{
Adrian McMenaminb9482372008-02-06 22:46:21 +0000772 int retval, i;
773 struct maple_device *mdev[MAPLE_PORTS];
774 ctrl_outl(0, MAPLE_STATE);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900775
Adrian McMenaminb9482372008-02-06 22:46:21 +0000776 retval = device_register(&maple_bus);
777 if (retval)
778 goto cleanup;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900779
Adrian McMenaminb9482372008-02-06 22:46:21 +0000780 retval = bus_register(&maple_bus_type);
781 if (retval)
782 goto cleanup_device;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900783
Adrian McMenaminb9482372008-02-06 22:46:21 +0000784 retval = driver_register(&maple_dummy_driver.drv);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000785 if (retval)
786 goto cleanup_bus;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900787
Adrian McMenaminb9482372008-02-06 22:46:21 +0000788 /* allocate memory for maple bus dma */
789 retval = maple_get_dma_buffer();
790 if (retval) {
791 printk(KERN_INFO
792 "Maple bus: Failed to allocate Maple DMA buffers\n");
793 goto cleanup_basic;
794 }
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900795
Adrian McMenaminb9482372008-02-06 22:46:21 +0000796 /* set up DMA interrupt handler */
797 retval = maple_set_dma_interrupt_handler();
798 if (retval) {
799 printk(KERN_INFO
800 "Maple bus: Failed to grab maple DMA IRQ\n");
801 goto cleanup_dma;
802 }
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900803
Adrian McMenaminb9482372008-02-06 22:46:21 +0000804 /* set up VBLANK interrupt handler */
805 retval = maple_set_vblank_interrupt_handler();
806 if (retval) {
807 printk(KERN_INFO "Maple bus: Failed to grab VBLANK IRQ\n");
808 goto cleanup_irq;
809 }
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900810
Adrian McMenaminb9482372008-02-06 22:46:21 +0000811 maple_queue_cache =
812 kmem_cache_create("maple_queue_cache", 0x400, 0,
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000813 SLAB_POISON|SLAB_HWCACHE_ALIGN, NULL);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900814
Adrian McMenaminb9482372008-02-06 22:46:21 +0000815 if (!maple_queue_cache)
816 goto cleanup_bothirqs;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900817
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900818 INIT_LIST_HEAD(&maple_waitq);
819 INIT_LIST_HEAD(&maple_sentq);
820
Adrian McMenaminb9482372008-02-06 22:46:21 +0000821 /* setup maple ports */
822 for (i = 0; i < MAPLE_PORTS; i++) {
Adrian McMenaminbd496662008-02-24 14:30:23 +0000823 checked[i] = false;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000824 mdev[i] = maple_alloc_dev(i, 0);
Adrian McMenaminbd496662008-02-24 14:30:23 +0000825 baseunits[i] = mdev[i];
Adrian McMenaminb9482372008-02-06 22:46:21 +0000826 if (!mdev[i]) {
827 while (i-- > 0)
828 maple_free_dev(mdev[i]);
829 goto cleanup_cache;
830 }
Adrian McMenamin1795cf42008-07-29 22:10:56 +0900831 maple_add_packet(mdev[i], 0, MAPLE_COMMAND_DEVINFO, 0, NULL);
Adrian McMenaminb9482372008-02-06 22:46:21 +0000832 subdevice_map[i] = 0;
833 }
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900834
Adrian McMenaminb9482372008-02-06 22:46:21 +0000835 /* setup maplebus hardware */
836 maplebus_dma_reset();
Adrian McMenaminb9482372008-02-06 22:46:21 +0000837 /* initial detection */
838 maple_send();
Adrian McMenaminb9482372008-02-06 22:46:21 +0000839 maple_pnp_time = jiffies;
Adrian McMenaminb9482372008-02-06 22:46:21 +0000840 printk(KERN_INFO "Maple bus core now registered.\n");
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900841
Adrian McMenaminb9482372008-02-06 22:46:21 +0000842 return 0;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900843
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000844cleanup_cache:
Adrian McMenaminb9482372008-02-06 22:46:21 +0000845 kmem_cache_destroy(maple_queue_cache);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900846
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000847cleanup_bothirqs:
Adrian McMenaminb9482372008-02-06 22:46:21 +0000848 free_irq(HW_EVENT_VSYNC, 0);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900849
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000850cleanup_irq:
Adrian McMenaminb9482372008-02-06 22:46:21 +0000851 free_irq(HW_EVENT_MAPLE_DMA, 0);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900852
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000853cleanup_dma:
Adrian McMenaminb9482372008-02-06 22:46:21 +0000854 free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900855
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000856cleanup_basic:
Adrian McMenaminb9482372008-02-06 22:46:21 +0000857 driver_unregister(&maple_dummy_driver.drv);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900858
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000859cleanup_bus:
Adrian McMenaminb9482372008-02-06 22:46:21 +0000860 bus_unregister(&maple_bus_type);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900861
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000862cleanup_device:
Adrian McMenaminb9482372008-02-06 22:46:21 +0000863 device_unregister(&maple_bus);
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900864
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000865cleanup:
Adrian McMenaminb9482372008-02-06 22:46:21 +0000866 printk(KERN_INFO "Maple bus registration failed\n");
867 return retval;
Adrian McMenamin17be2d22007-09-21 15:55:55 +0900868}
Adrian McMenaminb3c69e22008-02-06 23:51:21 +0000869/* Push init to later to ensure hardware gets detected */
870fs_initcall(maple_bus_init);