Hello, I've ported the old sl811_cs driver to use the new sl811-hcd code in 2.6. There are quite a few people with the RATOC REX-CFU1 usb compact flash card on PDAs who would benefit from this. I have tested the driver on arm (zaurus) and works well. I had to make a few changes to the sl811-hcd code in order to work with the pcmcia driver.
As noted in the thread[1] on lkml, I had to make the same tweaks in order to get the hcd code working. There is still one issue I found though: when "urb->hcpriv = hep" is uncommented in sl811h_urb_enqueue(), transfers don't work. Without this line, everything seems to be working well, except when I connect then disconnect an external hub, I get the following error from sl811h_endpoint_disable() and khubd goes into DW: sl811: ep c1d36da0 not empty? I tested some other usb-storage devices and this error didn't occur on disconnect. Patch against 2.6.12-rc4-mm1 is attached. [1] http://lkml.org/lkml/2005/2/14/110
diff -aurN linux.orig/drivers/usb/host/Kconfig linux.new/drivers/usb/host/Kconfig --- linux.orig/drivers/usb/host/Kconfig 2005-05-14 14:52:48.000000000 +0200 +++ linux.new/drivers/usb/host/Kconfig 2005-05-14 15:18:49.000000000 +0200 @@ -137,3 +137,11 @@ To compile this driver as a module, choose M here: the module will be called sl811-hcd. +config USB_SL811_CS + tristate "PCMCIA support for SL811HS HCD" + depends on USB_SL811_HCD && PCMCIA + default N + help + Enables support for PCMCIA cards with the SL811HS USB controller + such as the RATOC REX-CFU1 CF card. + If unsure, say N. diff -aurN linux.orig/drivers/usb/host/Makefile linux.new/drivers/usb/host/Makefile --- linux.orig/drivers/usb/host/Makefile 2005-05-14 14:52:48.000000000 +0200 +++ linux.new/drivers/usb/host/Makefile 2005-05-14 14:57:59.000000000 +0200 @@ -8,4 +8,5 @@ obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o +obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o diff -aurN linux.orig/drivers/usb/host/sl811_cs.c linux.new/drivers/usb/host/sl811_cs.c --- linux.orig/drivers/usb/host/sl811_cs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.new/drivers/usb/host/sl811_cs.c 2005-05-13 17:59:56.000000000 +0200 @@ -0,0 +1,465 @@ +/* + PCMCIA driver for SL811 + Filename: sl811_cs.c + Author: Yukio Yamamoto + + Port to sl811-hcd and 2.6.x by + Botond Botyanszki <boti()rocketmail.com> + Simon Pickering + + Last update: 2005-05-12 +*/ + +#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 <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("Botond Botyanszki"); +MODULE_DESCRIPTION("REX-CFU1 PCMCIA driver for 2.6"); +MODULE_LICENSE("GPL"); + + +/*====================================================================*/ +/* MACROS */ +/*====================================================================*/ + +#if defined(DEBUG) || defined(CONFIG_USB_DEBUG) || defined(PCMCIA_DEBUG) +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +module_param(pc_debug, int, 0); +static char *version = "sl811_cs.c 0.4 2005/05/12 19:37:14 (Botond Botyanszki)"; +#define DBG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +#else +#define DBG(n, args...) printk(KERN_DEBUG "sl811_cs: " args) +#endif +#else +#define DBG(n, args...) do{}while(0) +#endif + +#define INFO(args...) printk(KERN_INFO "sl811_cs: " args) + +#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444) + +#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 }; +static int irq_list_count; +module_param_array(irq_list, int, &irq_list_count, 0444); +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_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "sl811_cs", + }, + .attach = sl811_cs_attach, + .detach = sl811_cs_detach, +}; + +/*====================================================================*/ +int sl811h_probe(void *dev); +extern struct device_driver sl811h_driver; + +/*====================================================================*/ + +/*====================================================================*/ +static void release_platform_dev(struct device * dev) { + DBG(0, "sl811_cs platform_dev release\n"); +} + +static struct sl811_platform_data platform_data; +static struct resource resources[] = { + [0] = { + .flags = IORESOURCE_IRQ, + }, + [1] = { + .flags = IORESOURCE_MEM, + }, + [2] = { + .flags = IORESOURCE_MEM, + }, +}; +static struct platform_device platform_dev = { + .name = "sl811_cs", + .id = 0, + .num_resources = 3, + .dev.dma_mask = 0, + .dev.platform_data = &platform_data, + .dev.bus_id = "sl811-hcd", + .dev.driver = &sl811h_driver, + .dev.release = release_platform_dev, + .resource = resources + +}; + + +/*====================================================================*/ +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 < irq_list_count; 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) { + 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; + + /* 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[0].start = irq; + resources[1].start = base_addr; + resources[1].end = base_addr; + resources[2].start = base_addr + 1; + resources[2].end = base_addr + 256; + + platform_device_register(&platform_dev); + + /* try to initialize the host controller */ + if (sl811h_probe(&platform_dev.dev) != 0) { + DBG(0, "sl811h_probe() failed!\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); + } + + 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("sl811_cs_config failed\n"); + 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); + + 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); + DBG("reset sl811-hcd here?\n"); + break; + } + return 0; +} /* sl811_cs_event */ + +/*====================================================================*/ +static int __init init_sl811_cs(void) +{ + return pcmcia_register_driver(&sl811_cs_driver); +} + +/*====================================================================*/ +static void __exit exit_sl811_cs(void) +{ + pcmcia_unregister_driver(&sl811_cs_driver); +} + +/*====================================================================*/ + +module_init(init_sl811_cs); +module_exit(exit_sl811_cs); diff -aurN linux.orig/drivers/usb/host/sl811-hcd.c linux.new/drivers/usb/host/sl811-hcd.c --- linux.orig/drivers/usb/host/sl811-hcd.c 2005-05-14 14:52:49.000000000 +0200 +++ linux.new/drivers/usb/host/sl811-hcd.c 2005-05-14 14:59:45.000000000 +0200 @@ -83,8 +83,8 @@ */ #define DISABLE_ISO -// #define QUIRK2 -#define QUIRK3 +#define QUIRK2 +//#define QUIRK3 static const char hcd_name[] = "sl811-hcd"; @@ -127,6 +127,11 @@ sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT); sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + /* reset */ + sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0); + mdelay(20); + sl811_write(sl811, SL11H_CTLREG1, 0); + // if !is_on, put into lowpower mode now } @@ -827,8 +832,11 @@ #endif /* avoid all allocations within spinlocks */ - if (!hep->hcpriv) + if (!hep->hcpriv) { ep = kcalloc(1, sizeof *ep, mem_flags); + /* set hep, otherwise sl811h_ep *start(struct sl811 *sl811, u8 bank) crashes */ + ep->hep = hep; + } spin_lock_irqsave(&sl811->lock, flags); @@ -948,7 +956,8 @@ retval = 0; goto fail; } - urb->hcpriv = hep; + /* transfers fail with the following uncommented */ + /*urb->hcpriv = hep;*/ spin_unlock(&urb->lock); start_transfer(sl811); @@ -1555,6 +1564,7 @@ struct sl811 *sl811 = hcd_to_sl811(hcd); /* chip has been reset, VBUS power is off */ + hcd->state = HC_STATE_RUNNING; if (sl811->board) { @@ -1562,6 +1572,9 @@ hcd->power_budget = sl811->board->power * 2; } + /* enable power and interupts */ + port_power(sl811, 1); + return 0; } @@ -1610,20 +1623,22 @@ struct usb_hcd *hcd = dev_get_drvdata(dev); struct sl811 *sl811 = hcd_to_sl811(hcd); struct platform_device *pdev; +#ifndef CONFIG_USB_SL811_CS struct resource *res; - +#endif pdev = container_of(dev, struct platform_device, dev); remove_debug_file(sl811); usb_remove_hcd(hcd); +#ifndef CONFIG_USB_SL811_CS iounmap(sl811->data_reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); release_mem_region(res->start, 1); - iounmap(sl811->addr_reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, 1); +#endif usb_put_hcd(hcd); return 0; @@ -1631,8 +1646,7 @@ #define resource_len(r) (((r)->end - (r)->start) + 1) -static int __init -sl811h_probe(struct device *dev) +int sl811h_probe(struct device *dev) { struct usb_hcd *hcd; struct sl811 *sl811; @@ -1665,10 +1679,15 @@ return -EINVAL; } +#ifdef CONFIG_USB_SL811_CS + addr_reg = (void *) addr->start; + data_reg = (void *) data->start; +#else if (!request_mem_region(addr->start, 1, hcd_name)) { retval = -EBUSY; goto err1; } + addr_reg = ioremap(addr->start, resource_len(addr)); if (addr_reg == NULL) { retval = -ENOMEM; @@ -1679,11 +1698,13 @@ retval = -EBUSY; goto err3; } - data_reg = ioremap(data->start, resource_len(addr)); + + data_reg = ioremap(data->start, resource_len(data)); if (data_reg == NULL) { retval = -ENOMEM; goto err4; } +#endif /* allocate and initialize hcd */ hcd = usb_create_hcd(&sl811h_hc_driver, dev, dev->bus_id); @@ -1724,7 +1745,7 @@ } /* sl811s would need a different handler for this irq */ -#ifdef CONFIG_ARM +#if !defined(CONFIG_USB_SL811_CS) && defined(CONFIG_ARM) /* Cypress docs say the IRQ is IRQT_HIGH ... */ set_irq_type(irq, IRQT_RISING); #endif @@ -1738,6 +1759,7 @@ err6: usb_put_hcd(hcd); err5: +#ifndef CONFIG_USB_SL811_CS iounmap(data_reg); err4: release_mem_region(data->start, 1); @@ -1746,9 +1768,11 @@ err2: release_mem_region(addr->start, 1); err1: +#endif DBG("init error, %d\n", retval); return retval; } +EXPORT_SYMBOL (sl811h_probe); #ifdef CONFIG_PM @@ -1807,7 +1831,7 @@ #endif -static struct device_driver sl811h_driver = { +struct device_driver sl811h_driver = { .name = (char *) hcd_name, .bus = &platform_bus_type, @@ -1817,6 +1841,7 @@ .suspend = sl811h_suspend, .resume = sl811h_resume, }; +EXPORT_SYMBOL(sl811h_driver); /*-------------------------------------------------------------------------*/