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 <todd@...>
* 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.othieno@...>
- * Copyright (C) 2004 The GameCube Linux Team
+ * Copyright (C) 2004,2005 Todd Jeffreys <todd@...>
+ * 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.othieno@...>, " \
+ "Todd Jeffreys <todd@...>, " \
+ "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 <todd@...>
+ * 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 ---
|