diff options
Diffstat (limited to 'packages/cfu1/files/sl811_cs.c')
-rw-r--r-- | packages/cfu1/files/sl811_cs.c | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/packages/cfu1/files/sl811_cs.c b/packages/cfu1/files/sl811_cs.c index e69de29bb2..8b6d4ae2f6 100644 --- a/packages/cfu1/files/sl811_cs.c +++ b/packages/cfu1/files/sl811_cs.c @@ -0,0 +1,529 @@ +/* + A SL811 CLIENT DRIVER for linux 2.4.x + Filename: sl811_cs.c + Version: 0.0.2 + Author: Yukio Yamamoto + + Port to sl811-hcd and 2.6.x by + Botond Botyanszki <boti()rocketmail.com> + Simon Pickering + + Last update: 2005-05-05 +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <linux/version.h> +#include <asm/io.h> +#include <asm/system.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ds.h> + +#include <linux/usb_sl811.h> + +MODULE_AUTHOR("Yukio Yamamoto"); +MODULE_DESCRIPTION("REX-CFU1 PCMCIA driver"); +MODULE_LICENSE("GPL"); + + +/*====================================================================*/ +/* MACROS */ +/*====================================================================*/ + +/* FIXME */ +//#ifdef DEBUG +#define DBG(n, args...) printk(KERN_DEBUG "sl811_cs: " args) +//#else +//#define DBG(n, args...) do{}while(0) +//#endif + +#define INFO(args...) printk(KERN_INFO "sl811_cs: " args) + +/*static char *version = "sl811_cs.c 0.04 2005/04/27 00:00:00 (OpenZaurus Team)";*/ + +#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") + +#define CS_CHECK(fn, ret) \ + do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +/*====================================================================*/ +/* VARIABLES */ +/*====================================================================*/ + +static dev_info_t dev_info = "sl811_cs"; +static dev_link_t *dev_list = NULL; + +static ioaddr_t base_addr = 0x00000000; +static int irq = -1; + +static int irq_list[4] = { -1 }; +MODULE_PARM(irq_list, "1-4i"); +INT_MODULE_PARM(free_ports, 0); +INT_MODULE_PARM(irq_mask, 0xdeb8); + +/*====================================================================*/ +/* PROTO TYPES */ +/*====================================================================*/ + +static dev_link_t* sl811_cs_attach(void); +static void sl811_cs_detach(dev_link_t *); +static void sl811_cs_config(dev_link_t *link); +static void sl811_cs_release(dev_link_t *arg); +static int sl811_cs_event(event_t event, int priority, + event_callback_args_t *args); + +/*====================================================================*/ +/* PROTO TYPES */ +/*====================================================================*/ + +typedef struct local_info_t { + dev_link_t link; + dev_node_t node; +} local_info_t; + +static struct pcmcia_driver sl811_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "sl811_cs", + }, + .attach = sl811_cs_attach, + .detach = sl811_cs_detach, +}; +extern struct device_driver sl811h_driver; + +static struct sl811_platform_data platform_data; +static struct res { + struct resource irq_res; + struct resource addr_res; + struct resource data_res; +} resources; +static struct platform_device platform_dev = { + .name = "sl811_cs", + .id = 0, + .num_resources = 0, + .dev.dma_mask = 0, + .dev.platform_data = &platform_data, + .dev.bus_id = "sl811-hcd", + .dev.driver = &sl811h_driver +}; + +/*====================================================================*/ +/* EXTERNAL FUNCTIONS */ +/*====================================================================*/ +int sl811h_probe(void *dev); +int sl811h_remove(struct device *dev); + +/*====================================================================*/ + + +/*====================================================================*/ +static void release_platform_dev(struct device * dev) { + DBG(0, "sl811_cs platform_dev release\n"); +} + +/*====================================================================*/ +static void sl811_cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + pcmcia_report_error(handle, &err); +} + +/*====================================================================*/ +static dev_link_t *sl811_cs_attach(void) +{ + local_info_t *local; + dev_link_t *link; + client_reg_t client_reg; + int ret, i; + + local = kmalloc(sizeof(local_info_t), GFP_KERNEL); + if (!local) return NULL; + memset(local, 0, sizeof(local_info_t)); + link = &local->link; link->priv = local; + + /* Initialize */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = NULL; + + link->conf.Attributes = 0; + link->conf.Vcc = 33; + link->conf.IntType = INT_MEMORY_AND_IO; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &sl811_cs_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != CS_SUCCESS) { + sl811_cs_error(link->handle, RegisterClient, ret); + sl811_cs_detach(link); + return NULL; + } + + return link; +} /* sl811_cs_attach */ + +/*====================================================================*/ +static void sl811_cs_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + DBG(0, "sl811_cs_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + if (link->state & DEV_CONFIG) { +#ifdef PCMCIA_DEBUG + printk(KERN_DEBUG "sl811_cs: detach postponed, '%s' " + "still locked\n", link->dev->dev_name); +#endif + link->state |= DEV_STALE_LINK; + return; + } +#endif + + /* Break the link with Card Services */ + if (link->handle) + pcmcia_deregister_client(link->handle); + + /* Unlink device structure, and free it */ + *linkp = link->next; + /* This points to the parent local_info_t struct */ + kfree(link->priv); + +} /* sl811_cs_detach */ + + +/*====================================================================*/ +static int sl811_hc_init(void) +{ + /* set up the device structure */ + resources.irq_res.flags = IORESOURCE_IRQ; + resources.irq_res.start = irq; + + resources.addr_res.flags = IORESOURCE_MEM; + resources.addr_res.start = base_addr; + resources.addr_res.end = base_addr + 1; + + resources.data_res.flags = IORESOURCE_MEM; + resources.data_res.start = base_addr + 1; + resources.data_res.end = base_addr + 4; + + platform_dev.dev.release = release_platform_dev; + platform_device_register(&platform_dev); + + /* FIXME: resources already claimed by pcmcia, so we cannot register + the device with the irq and io ports */ + platform_dev.num_resources = 3; + platform_dev.resource = (struct resource *) &resources; + + /* try to initialize the host controller */ + if (sl811h_probe(&platform_dev.dev) != 0) { + DBG(0, "sl811h_probe() didn't return 0\n"); + return 0; + } + return 1; +} + + +/*====================================================================*/ +static void sl811_cs_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + local_info_t *dev = link->priv; + tuple_t tuple; + cisparse_t parse; + int last_fn, last_ret; + u_char buf[64]; + config_info_t conf; + cistpl_cftable_entry_t dflt = { 0 }; + + DBG(0, "sl811_cs_config(0x%p)\n", link); + + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Look up the current Vcc */ + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); + link->conf.Vcc = conf.Vcc; + + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + while (1) { + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + if (pcmcia_get_tuple_data(handle, &tuple) != 0 || + pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + goto next_entry; + + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) { + dflt = *cfg; + } + + if (cfg->index == 0) goto next_entry; + + link->conf.ConfigIndex = cfg->index; + + /* Does this card need audio output? */ + if (cfg->flags & CISTPL_CFTABLE_AUDIO) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + + /* Use power settings for Vcc and Vpp if present */ + /* Note that the CIS values need to be rescaled */ + if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) { + if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000) { + goto next_entry; + } + } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) { + if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000) { + goto next_entry; + } + } + + if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; + else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; + + /* Do we need to allocate an interrupt? */ + if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) + link->conf.Attributes |= CONF_ENABLE_IRQ; + + /* IO window settings */ + link->io.NumPorts1 = link->io.NumPorts2 = 0; + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; + link->io.BasePort1 = io->win[0].base; + link->io.NumPorts1 = io->win[0].len; + + if (pcmcia_request_io(link->handle, &link->io) != 0) + goto next_entry; + } + break; + + next_entry: + if (link->io.NumPorts1) + pcmcia_release_io(link->handle, &link->io); + last_ret = pcmcia_get_next_tuple(handle, &tuple); + } + + if (link->conf.Attributes & CONF_ENABLE_IRQ) + CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + + if (free_ports) { + if (link->io.BasePort1) + release_region(link->io.BasePort1, link->io.NumPorts1); + } + + /* IRQ is requested by the sl811-hcd driver */ + pcmcia_release_irq(link->handle, &link->irq); + + sprintf(dev->node.dev_name, "cf_usb0"); + dev->node.major = dev->node.minor = 0; + link->dev = &dev->node; + + printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", + dev->node.dev_name, link->conf.ConfigIndex, + link->conf.Vcc/10, link->conf.Vcc%10); + if (link->conf.Vpp1) + printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); + if (link->conf.Attributes & CONF_ENABLE_IRQ) { + printk(", irq %d", link->irq.AssignedIRQ); + irq = link->irq.AssignedIRQ; + } + + if (link->io.NumPorts1) { + printk(", io 0x%04x-0x%04x", link->io.BasePort1, + link->io.BasePort1+link->io.NumPorts1-1); + base_addr = link->io.BasePort1; + } + + printk("\n"); + + link->state &= ~DEV_CONFIG_PENDING; + + if (sl811_hc_init() == 0) goto cs_failed; + + return; + + cs_failed: + printk("DEBUG: sl811_cs_config failed\n"); + sl811_cs_error(link->handle, last_fn, last_ret); + sl811_cs_release(link); + link->state &= ~DEV_CONFIG_PENDING; + +} /* sl811_cs_config */ + +/*====================================================================*/ +static void sl811_cs_release(dev_link_t * link) +{ + + DBG(0, "sl811_cs_release(0x%p)\n", link); + + if (link->open) { + DBG(1, "sl811_cs: release postponed, '%s' still open\n", + link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + /* Unlink the device chain */ + link->dev = NULL; + + pcmcia_release_configuration(link->handle); + if (link->io.NumPorts1) + pcmcia_release_io(link->handle, &link->io); + if (link->irq.AssignedIRQ) + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + + if (link->state & DEV_STALE_LINK) + sl811_cs_detach(link); + + sl811h_remove(&platform_dev.dev); + + /* FIXME: resources already claimed by pcmcia, so we cannot register + the device with the irq and io ports */ + platform_dev.num_resources = 0; + platform_dev.resource = NULL; + + platform_device_unregister(&platform_dev); + +} /* sl811_cs_release */ + +/*====================================================================*/ +static int sl811_cs_event(event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + DBG(1, "sl811_cs_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + sl811_cs_release(link); + } + break; + + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + sl811_cs_config(link); + break; + + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + pcmcia_release_configuration(link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) + pcmcia_request_configuration(link->handle, &link->conf); + + /* Reset routine will be added in the next version */ + DBG("FIXME: card reset\n"); + /* hc_found_hci(irq, base_addr, base_addr+1); */ + + break; + } + return 0; +} /* sl811_cs_event */ + +/*====================================================================*/ +static int __init init_sl811_cs(void) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + servinfo_t serv; + DBG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "sl811_cs: Card Services release " + "does not match!\n"); + return -EINVAL; + } + register_pccard_driver(&dev_info, &sl811_cs_attach, &sl811_cs_detach); + return 0; +#else + return pcmcia_register_driver(&sl811_driver); +#endif + +} + +/*====================================================================*/ +static void __exit exit_sl811_cs(void) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + DBG(0, "sl811_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) { + del_timer(&dev_list->release); + if (dev_list->state & DEV_CONFIG) + sl811_cs_release(dev_list); + sl811_cs_detach(dev_list); + } +#else + pcmcia_unregister_driver(&sl811_driver); +#endif + +} + +/*====================================================================*/ + +module_init(init_sl811_cs); +module_exit(exit_sl811_cs); + |