Update of /cvsroot/gc-linux/linux/drivers/exi In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4397/drivers/exi Modified Files: Makefile exi-driver.c exi-hw.c Added Files: exi-hw.h Removed Files: exi-bus.c exi_priv.h gcn-exi-lite-tmbinc-exi_c.c gcn-exi-lite.c gcn-exi-lite.h Log Message: Added yet another EXI Layer Framework. --- NEW FILE: exi-hw.h --- /* * drivers/exi/exi-hw.h * * Nintendo GameCube EXpansion Interface support. Hardware routines. * Copyright (C) 2004-2005 The GameCube Linux Team * Copyright (C) 2004,2005 Todd Jeffreys <to...@vo...> * Copyright (C) 2005 Albert Herranz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * */ #ifndef __EXI_HW_H #define __EXI_HW_H #include <linux/interrupt.h> #include <asm/atomic.h> #include <linux/exi.h> #define exi_printk(level, format, arg...) \ printk(level "exi: " format , ## arg) #define EXI_MAX_CHANNELS 3 /* channels on the EXI bus */ #define EXI_DEVICES_PER_CHANNEL 3 /* number of devices per EXI channel */ #define EXI_MAX_EVENTS 3 /* types of events on the EXI bus */ #define EXI_MAX_FREQ 7 #define EXI_FREQ_SCAN 3 #define EXI_READ 0 #define EXI_WRITE 1 #define EXI_IRQ 4 #define EXI_DMA_ALIGN 0x1f /* 32 bytes */ #define EXI_BASE 0xcc006800 #define EXI_SIZE 0x40 #define EXI_CHANNEL_SPACING 0x14 #define EXI_IO_BASE(c) ((void __iomem *)(EXI_BASE + ((c)*EXI_CHANNEL_SPACING))) #define EXI_CSR 0x00 #define EXI_CSR_EXIINTMASK (1<<0) #define EXI_CSR_EXIINT (1<<1) #define EXI_CSR_TCINTMASK (1<<2) #define EXI_CSR_TCINT (1<<3) #define EXI_CSR_CLKMASK (0x7<<4) #define EXI_CSR_CLK_1MHZ (0x0<<4) #define EXI_CSR_CLK_2MHZ (0x1<<4) #define EXI_CSR_CLK_4MHZ (0x2<<4) #define EXI_CSR_CLK_8MHZ (0x3<<4) #define EXI_CSR_CLK_16MHZ (0x4<<4) #define EXI_CSR_CLK_32MHZ (0x5<<4) #define EXI_CSR_CSMASK (0x7<<7) #define EXI_CSR_CS_0 (0x1<<7) /* Chip Select 001 */ #define EXI_CSR_CS_1 (0x2<<7) /* Chip Select 010 */ #define EXI_CSR_CS_2 (0x4<<7) /* Chip Select 100 */ #define EXI_CSR_EXTINMASK (1<<10) #define EXI_CSR_EXTIN (1<<11) #define EXI_CSR_EXT (1<<12) #define EXI_MAR 0x04 #define EXI_LENGTH 0x08 #define EXI_CR 0x0c #define EXI_CR_TSTART (1<<0) #define EXI_CR_DMA (1<<1) #define EXI_CR_READ (0<<2) #define EXI_CR_WRITE (1<<2) #define EXI_CR_READ_WRITE (2<<2) #define EXI_CR_TLEN(len) (((len)-1)<<4) #define EXI_DATA 0x10 /* * For registering event handlers with the exi layer. */ struct exi_event_handler { exi_event_handler_t handler; void *data; unsigned int channel_mask; /* channels used by the handler */ }; /* * This structure represents an exi channel. */ struct exi_channel { spinlock_t lock; int channel; unsigned long flags; #define EXI_SELECTED (1<<0) #define EXI_DMABUSY (1<<1) spinlock_t io_lock; void __iomem *io_base; struct exi_device *device_selected; struct exi_command *dma_cmd; struct exi_command post_cmd; wait_queue_head_t wait_queue; unsigned long csr; struct tasklet_struct tasklet; struct exi_event_handler events[EXI_MAX_EVENTS]; }; extern int exi_hw_init(void); extern void exi_hw_exit(void); #define exi_is_selected(x) ((x)->flags & EXI_SELECTED) /* * Internal. * Declare simple transfer functions for single bytes, words and dwords, * and build a general transfer function based on that. */ #define __declare__exi_transfer_raw(_type,_val,_data,_on_write,_on_read) \ static inline void __exi_transfer_raw_##_type(struct exi_channel *exi_channel,\ _type *_data, int mode) \ { \ u32 __iomem *csr_reg = exi_channel->io_base + EXI_CSR; \ u32 __iomem *data_reg = exi_channel->io_base + EXI_DATA; \ u32 __iomem *cr_reg = exi_channel->io_base + EXI_CR; \ u32 _val = ~0; \ unsigned long flags; \ \ /* \ * On reads we write too some known value to EXIxDATA because \ * information currently stored there is leaked to the \ * MOSI line, confusing some hardware. \ */ \ if (mode & EXI_OP_WRITE) \ _on_write; \ writel(_val, data_reg); \ \ /* start transfer */ \ _val = EXI_CR_TSTART | EXI_CR_TLEN(sizeof(_type)) | (mode&7); \ writel(_val, cr_reg); \ \ /* wait for transfer completion */ \ while(readl(cr_reg) & EXI_CR_TSTART) \ cpu_relax(); \ \ /* XXX check if we need that on immediate mode */ \ /* assert transfer complete interrupt */ \ spin_lock_irqsave(&exi_channel->io_lock, flags); \ writel(readl(csr_reg) | EXI_CSR_TCINT, csr_reg); \ spin_unlock_irqrestore(&exi_channel->io_lock, flags); \ \ if (!(mode & EXI_OP_WRITE)) { \ _val = readl(data_reg); \ _on_read; \ } \ } #define __declare__exi_transfer_raw_simple(_type) \ __declare__exi_transfer_raw( \ _type, _v, _d, \ _v = *(_d) << (32 - (8*sizeof(_type))), \ *(_d) = (_type)(_v >> (32 - (8*sizeof(_type)))) \ ) __declare__exi_transfer_raw_simple(u8) __declare__exi_transfer_raw_simple(u16) __declare__exi_transfer_raw_simple(u32) #endif /* __EXI_HW_H */ Index: Makefile =================================================================== RCS file: /cvsroot/gc-linux/linux/drivers/exi/Makefile,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- Makefile 8 Jan 2005 22:46:47 -0000 1.3 +++ Makefile 13 Mar 2005 21:49:16 -0000 1.4 @@ -2,4 +2,4 @@ # Makefile for the EXI bus core. # -obj-$(CONFIG_EXI) += exi-bus.o exi-hw.o +obj-$(CONFIG_EXI) += exi-driver.o exi-hw.o Index: exi-driver.c =================================================================== RCS file: /cvsroot/gc-linux/linux/drivers/exi/exi-driver.c,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- exi-driver.c 19 Oct 2004 22:49:40 -0000 1.6 +++ exi-driver.c 13 Mar 2005 21:49:16 -0000 1.7 @@ -2,8 +2,10 @@ * drivers/exi/exi-driver.c * * Nintendo GameCube Expansion Interface support. Driver model routines. + * Copyright (C) 2004-2005 The GameCube Linux Team * Copyright (C) 2004 Arthur Othieno <a.o...@bl...> - * Copyright (C) 2004 The GameCube Linux Team + * Copyright (C) 2004,2005 Todd Jeffreys <to...@vo...> + * Copyright (C) 2005 Albert Herranz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -12,113 +14,395 @@ * */ -#define DEBUG - +#include <linux/module.h> #include <linux/init.h> -#include <linux/errno.h> -#include <linux/kernel.h> #include <linux/device.h> #include <linux/exi.h> +#include <linux/delay.h> -static int exi_device_probe(struct device *dev) +#define DRV_MODULE_NAME "exi-driver" +#define DRV_DESCRIPTION "Nintendo GameCube EXpansion Interface driver" +#define DRV_AUTHOR "Arthur Othieno <a.o...@bl...>, " \ + "Todd Jeffreys <to...@vo...>, " \ + "Albert Herranz" + +static char exi_driver_version[] = "3.0"; + + +extern struct device exi_bus_devices[EXI_MAX_CHANNELS]; +extern struct exi_device exi_devices[EXI_MAX_CHANNELS][EXI_DEVICES_PER_CHANNEL]; + + +struct exi_map_id_to_name { + unsigned int id; + char *name; +}; + +static struct exi_map_id_to_name exi_map_id_to_name[] = { + { .id = EXI_ID_NONE, .name = "(external card)" }, + { .id = 0xffff1698, .name = "Mask ROM/RTC/SRAM/UART" }, + { .id = 0x00000004, .name = "Memory Card 59" }, + { .id = 0x00000008, .name = "Memory Card 123" }, + { .id = 0x00000010, .name = "Memory Card 251" }, + { .id = 0x00000020, .name = "Memory Card 507" }, + { .id = 0x00000040, .name = "Memory Card 1019" }, + { .id = 0x00000080, .name = "Memory Card 2043" }, + { .id = 0x01010000, .name = "USB Adapter" }, + { .id = 0x01020000, .name = "NPDP GDEV" }, + { .id = 0x05070000, .name = "IS Viewer" }, + { .id = 0x04120000, .name = "AD16" }, + { .id = 0x03010000, .name = "Marlin?" }, + { .id = 0x02020000, .name = "Modem" }, + { .id = 0x04020200, .name = "BroadBand Adapter" }, + { .id = 0 } +}; + +/* + * Internal. Return the friendly name of an exi identifier. + */ +static const char *exi_name_id(unsigned int id) { - struct exi_dev *exi_dev = to_exi_dev(dev); - struct exi_driver *drv = to_exi_driver(dev->driver); - int err = -ENODEV; + struct exi_map_id_to_name *map = exi_map_id_to_name; - if (drv->probe) - err = drv->probe(exi_dev); + while(map->id) { + if (map->id == id) + return map->name; + map++; + } + return "Unknown"; +} - return err; + +/* + * Internal. Check if an exi device matches a given exi device id. + */ +static int exi_device_match_one(const struct exi_device_id *eid, + const struct exi_device *exi_device) +{ + /* + * We allow drivers to claim devices that do not provide + * EXI identifiers by matching directly on channel/device. + * These drivers must use EXI_ID_NONE on their eids. + */ + if (eid->id == exi_device->eid.id || eid->id == EXI_ID_NONE) { + /* match against channel and device */ + if (exi_device->eid.channel == eid->channel && + exi_device->eid.device == eid->device) { + return 1; + } + } + return 0; } -static int exi_device_remove(struct device *dev) +/* + * Internal. Check if an exi device matches a given set of exi device ids. + * Return the exi device identifier or %NULL if there is no match. + */ +static const struct exi_device_id * +exi_device_match(const struct exi_device_id *eids, + const struct exi_device *exi_device) { - struct exi_dev *exi_dev = to_exi_dev(dev); - struct exi_driver *drv = to_exi_driver(dev->driver); + while (eids && eids->id) { + if (exi_device_match_one(eids, exi_device)) + return eids; + eids++; + } + return NULL; +} - if (drv->remove) - drv->remove(exi_dev); +/* + * Internal. Used to check if an exi device is supported by an exi driver. + */ +static int exi_bus_match(struct device *dev, struct device_driver *drv) +{ + struct exi_device *exi_device = to_exi_device(dev); + struct exi_driver *exi_driver = to_exi_driver(drv); + const struct exi_device_id *eids = exi_driver->eid_table; + if (eids && exi_device_match(eids, exi_device)) + return 1; return 0; } + +/* + * Internal. Initialize an exi_device structure. + */ +static void exi_device_init(struct exi_device *exi_device, + unsigned int channel, unsigned int device) +{ + memset(exi_device, 0, sizeof(*exi_device)); + + exi_device->eid.id = EXI_ID_INVALID; + exi_device->eid.channel = channel; + exi_device->eid.device = device; + exi_device->frequency = -1; + + exi_device->exi_channel = to_exi_channel(channel); + + device_initialize(&exi_device->dev); + exi_device->dev.parent = &exi_bus_devices[channel]; + exi_device->dev.bus = &exi_bus_type; + exi_device->dev.platform_data = to_exi_channel(channel); + + sprintf(exi_device->dev.bus_id, "%01x:%01x", channel, device); +} + /** - * exi_driver_register - register an EXI device driver. - * @drv: driver structure to register. + * exi_device_get - Increments the reference count of the exi device + * @exi_device: device being referenced * - * Registers an EXI device driver with the bus - * and consequently with the driver model core. + * Each live reference to an exi device should be refcounted. + * A pointer to the device with the incremented reference counter + * is returned. */ -int exi_driver_register(struct exi_driver *drv) +struct exi_device *exi_device_get(struct exi_device *exi_device) { - drv->driver.name = drv->name; - drv->driver.bus = &exi_bus_type; - drv->driver.probe = exi_device_probe; - drv->driver.remove = exi_device_remove; + if (exi_device) + get_device(&exi_device->dev); + return exi_device; +} - return driver_register(&drv->driver); +/** + * exi_device_put - Releases a use of the exi device + * @exi_device: device that's been disconnected + * + * Must be called when a user of a device is finished with it. + */ +void exi_device_put(struct exi_device *exi_device) +{ + if (exi_device) + put_device(&exi_device->dev); +} + +/** + * exi_get_exi_device - Returns a reference to an exi device + * @exi_channel: exi channel where the device is located + * @device: device number within the channel + */ +struct exi_device *exi_get_exi_device(struct exi_channel *exi_channel, + int device) +{ + return &exi_devices[to_channel(exi_channel)][device]; +} + +/* + * Internal. Call device driver probe function on match. + */ +static int exi_device_probe(struct device *dev) +{ + struct exi_device *exi_device = to_exi_device(dev); + struct exi_driver *exi_driver = to_exi_driver(dev->driver); + const struct exi_device_id *eid; + int retval = -ENODEV; + + if (!exi_driver->eid_table) + goto out; + + eid = exi_device_match(exi_driver->eid_table, exi_device); + if (eid) { + exi_device->frequency = exi_driver->frequency; + if (exi_driver->probe) + retval = exi_driver->probe(exi_device); + } + if (retval >= 0) { + retval = 0; + } + +out: + return retval; +} + +/* + * Internal. Call device driver remove function. + */ +static int exi_device_remove(struct device *dev) +{ + struct exi_device *exi_device = to_exi_device(dev); + struct exi_driver *exi_driver = to_exi_driver(dev->driver); + + if (exi_driver->remove) + exi_driver->remove(exi_device); + + return 0; } -EXPORT_SYMBOL(exi_driver_register); /** - * exi_driver_unregister - unregister an EXI device driver. - * @drv: driver structure to unregister. + * exi_driver_register - register an EXI device driver. + * @driver: driver structure to register. * - * Unregisters an EXI device driver with the bus - * and consequently with the driver model core. + * Registers an EXI device driver with the bus + * and consequently with the driver model core. */ -void exi_driver_unregister(struct exi_driver *drv) +int exi_driver_register(struct exi_driver *driver) { - driver_unregister(&drv->driver); + driver->driver.name = driver->name; + driver->driver.bus = &exi_bus_type; + driver->driver.probe = exi_device_probe; + driver->driver.remove = exi_device_remove; + + return driver_register(&driver->driver); } -EXPORT_SYMBOL(exi_driver_unregister); +/** + * exi_driver_unregister - unregister an EXI device driver. + * @driver: driver structure to unregister. + * + * Unregisters an EXI device driver with the bus + * and consequently with the driver model core. + */ +void exi_driver_unregister(struct exi_driver *driver) +{ + driver_unregister(&driver->driver); +} -static int exi_bus_match(struct device *dev, struct device_driver *drv) +/* + * Internal. Re-scan a given exi channel, looking for added, changed and + * removed exi devices. + * XXX Currently, only _new_ devices are taken into account. + */ +static void exi_channel_rescan(struct exi_channel *exi_channel) { - struct exi_dev *exi_dev = to_exi_dev(dev); - struct exi_driver *exi_drv = to_exi_driver(drv); - const struct exi_device_id *ids = exi_drv->id_table; + struct exi_device *exi_device; + unsigned int channel, device, id; - if (!ids) - return 0; + spin_lock(&exi_channel->lock); - while (ids->dev_id) { - if (ids->dev_id == exi_dev->id) - return 1; - ids++; + /* add the exi devices underneath the parents */ + for (device = 0; device < EXI_DEVICES_PER_CHANNEL; ++device) { + channel = to_channel(exi_channel); + exi_device = &exi_devices[channel][device]; + + /* now ID the device */ + id = exi_get_id(exi_channel, device, EXI_FREQ_SCAN); + + /* + * We only process currently _new_ devices here. + */ + if (id != EXI_ID_INVALID) { + exi_printk(KERN_INFO, "[%s] id=0x%08x %s\n", + exi_device->dev.bus_id, + id, exi_name_id(id)); + + if (exi_device->eid.id == EXI_ID_INVALID) { + /* a new device has been found */ + exi_device->eid.id = id; + device_register(&exi_device->dev); + } else { + /* device changed */ + /* remove, add */ + } + } else { + if (exi_device->eid.id != EXI_ID_INVALID) { + /* device removed */ + /* remove */ + } + } } - return 0; + spin_unlock(&exi_channel->lock); } -struct bus_type exi_bus_type = { - .name = "exi", - .match = exi_bus_match, -}; +/* + * Internal. Scans all the exi channels looking for exi devices. + */ +static void exi_bus_rescan(void) +{ + struct exi_channel *exi_channel; + unsigned int channel; -EXPORT_SYMBOL(exi_bus_type); + for (channel = 0; channel < EXI_MAX_CHANNELS; ++channel) { + exi_channel = to_exi_channel(channel); + exi_channel_rescan(exi_channel); + } +} -struct device exi_bus_dev = { - .bus_id = "exi0", + +static struct exi_device exi_devices[EXI_MAX_CHANNELS][EXI_DEVICES_PER_CHANNEL]; + +static struct bus_type exi_bus_type = { + .name = "exi", + .match = exi_bus_match, }; -EXPORT_SYMBOL(exi_bus_dev); +static struct device exi_bus_devices[EXI_MAX_CHANNELS] = { + [0] = {.bus_id = "exi0"}, + [1] = {.bus_id = "exi1"}, + [2] = {.bus_id = "exi2"}, +}; -static int __init exi_driver_init(void) +extern void exi_channel_init(struct exi_channel *exi_channel, + unsigned int channel); + +static int __init exi_layer_init(void) { - int err; + struct exi_channel *exi_channel; + struct exi_device *exi_device; + unsigned int channel, device; + int retval; - if ((err = device_register(&exi_bus_dev))) - goto out; - if ((err = bus_register(&exi_bus_type))) - goto out; -out: - return err; + exi_printk(KERN_INFO, "%s - version %s\n", DRV_DESCRIPTION, + exi_driver_version); + + extern unsigned long exi_running; + if (!test_and_set_bit(1, &exi_running)) { + retval = exi_hw_init(); + if (retval) + goto err_irq_init; + } + + /* initialize channels and devices */ + for (channel = 0; channel < EXI_MAX_CHANNELS; ++channel) { + exi_channel = to_exi_channel(channel); + exi_channel_init(exi_channel, channel); + for (device = 0; device < EXI_DEVICES_PER_CHANNEL; ++device) { + exi_device = &exi_devices[channel][device]; + exi_device_init(exi_device, channel, device); + } + } + + /* register root devices */ + for (channel = 0; channel < EXI_MAX_CHANNELS; ++channel) { + retval = device_register(&exi_bus_devices[channel]); + if (retval) + goto err_device_register; + } + + /* register the bus */ + retval = bus_register(&exi_bus_type); + if (retval) + goto err_bus_register; + + /* now enumerate through the bus and add all detected devices */ + exi_bus_rescan(); + + return 0; + +err_bus_register: +err_device_register: + while(--channel > 0) { + device_unregister(&exi_bus_devices[channel]); + } + exi_hw_exit(); +err_irq_init: + return retval; } -postcore_initcall(exi_driver_init); +EXPORT_SYMBOL(exi_driver_register); +EXPORT_SYMBOL(exi_driver_unregister); +EXPORT_SYMBOL(exi_bus_type); + +EXPORT_SYMBOL(exi_get_exi_device); +EXPORT_SYMBOL(exi_device_get); +EXPORT_SYMBOL(exi_device_put); + + +postcore_initcall(exi_layer_init); + +MODULE_AUTHOR(DRV_AUTHOR); +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_LICENSE("GPL"); + Index: exi-hw.c =================================================================== RCS file: /cvsroot/gc-linux/linux/drivers/exi/exi-hw.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- exi-hw.c 13 Jan 2005 01:55:32 -0000 1.2 +++ exi-hw.c 13 Mar 2005 21:49:16 -0000 1.3 @@ -1,9 +1,10 @@ /* * drivers/exi/exi-hw.c * - * Nintendo GameCube EXI driver + * Nintendo GameCube EXpansion Interface support. Hardware routines. * Copyright (C) 2004-2005 The GameCube Linux Team * Copyright (C) 2004,2005 Todd Jeffreys <to...@vo...> + * Copyright (C) 2005 Albert Herranz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License [...1382 lines suppressed...] + exi_quiesce_all_channels(0); + free_irq(EXI_IRQ, NULL); +} + + +EXPORT_SYMBOL(to_exi_channel); + +EXPORT_SYMBOL(exi_select_raw); +EXPORT_SYMBOL(exi_deselect_raw); +EXPORT_SYMBOL(exi_transfer_raw); +EXPORT_SYMBOL(exi_dma_transfer_raw); + +EXPORT_SYMBOL(exi_select); +EXPORT_SYMBOL(exi_deselect); +EXPORT_SYMBOL(exi_transfer); + +EXPORT_SYMBOL(exi_get_id); +EXPORT_SYMBOL(exi_event_register); +EXPORT_SYMBOL(exi_event_unregister); + --- exi-bus.c DELETED --- --- exi_priv.h DELETED --- --- gcn-exi-lite-tmbinc-exi_c.c DELETED --- --- gcn-exi-lite.c DELETED --- --- gcn-exi-lite.h DELETED --- |