[PATCH] USB: add sl811_cs support

This adds support for a CF-card USB Host adapter, the Ratoc REX-CFU1U, by
wrapping a PCMCIA driver around the existing "sl811-hcd" platform driver.

This CF card is especially useful for PDAs, which currently tend to have
no other solution for USB host capability.

From: Botond Botyanszki <boti@rocketmail.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

David Brownell and committed by
Greg KH
c6de2b64 1e9a47b6

+454
+11
drivers/usb/host/Kconfig
··· 124 To compile this driver as a module, choose M here: the 125 module will be called sl811-hcd. 126
··· 124 To compile this driver as a module, choose M here: the 125 module will be called sl811-hcd. 126 127 + config USB_SL811_CS 128 + tristate "CF/PCMCIA support for SL811HS HCD" 129 + depends on USB_SL811_HCD && PCMCIA 130 + default N 131 + help 132 + Wraps a PCMCIA driver around the SL811HS HCD, supporting the RATOC 133 + REX-CFU1U CF card (often used with PDAs). If unsure, say N. 134 + 135 + To compile this driver as a module, choose M here: the 136 + module will be called "sl811_cs". 137 +
+1
drivers/usb/host/Makefile
··· 7 obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o 8 obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o 9 obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o 10 obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
··· 7 obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o 8 obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o 9 obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o 10 + obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o 11 obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
+442
drivers/usb/host/sl811_cs.c
···
··· 1 + /* 2 + * PCMCIA driver for SL811HS (as found in REX-CFU1U) 3 + * Filename: sl811_cs.c 4 + * Author: Yukio Yamamoto 5 + * 6 + * Port to sl811-hcd and 2.6.x by 7 + * Botond Botyanszki <boti@rocketmail.com> 8 + * Simon Pickering 9 + * 10 + * Last update: 2005-05-12 11 + */ 12 + 13 + #include <linux/kernel.h> 14 + #include <linux/module.h> 15 + #include <linux/init.h> 16 + #include <linux/sched.h> 17 + #include <linux/ptrace.h> 18 + #include <linux/slab.h> 19 + #include <linux/string.h> 20 + #include <linux/timer.h> 21 + #include <linux/ioport.h> 22 + 23 + #include <pcmcia/version.h> 24 + #include <pcmcia/cs_types.h> 25 + #include <pcmcia/cs.h> 26 + #include <pcmcia/cistpl.h> 27 + #include <pcmcia/cisreg.h> 28 + #include <pcmcia/ds.h> 29 + 30 + #include <linux/usb_sl811.h> 31 + 32 + MODULE_AUTHOR("Botond Botyanszki"); 33 + MODULE_DESCRIPTION("REX-CFU1U PCMCIA driver for 2.6"); 34 + MODULE_LICENSE("GPL"); 35 + 36 + 37 + /*====================================================================*/ 38 + /* MACROS */ 39 + /*====================================================================*/ 40 + 41 + #if defined(DEBUG) || defined(CONFIG_USB_DEBUG) || defined(PCMCIA_DEBUG) 42 + 43 + static int pc_debug = 0; 44 + module_param(pc_debug, int, 0644); 45 + 46 + #define DBG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG "sl811_cs: " args) 47 + 48 + #else 49 + #define DBG(n, args...) do{}while(0) 50 + #endif /* no debugging */ 51 + 52 + #define INFO(args...) printk(KERN_INFO "sl811_cs: " args) 53 + 54 + #define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444) 55 + 56 + #define CS_CHECK(fn, ret) \ 57 + do { \ 58 + last_fn = (fn); \ 59 + if ((last_ret = (ret)) != 0) \ 60 + goto cs_failed; \ 61 + } while (0) 62 + 63 + /*====================================================================*/ 64 + /* VARIABLES */ 65 + /*====================================================================*/ 66 + 67 + static const char driver_name[DEV_NAME_LEN] = "sl811_cs"; 68 + 69 + static dev_link_t *dev_list = NULL; 70 + 71 + static int irq_list[4] = { -1 }; 72 + static int irq_list_count; 73 + 74 + module_param_array(irq_list, int, &irq_list_count, 0444); 75 + 76 + INT_MODULE_PARM(irq_mask, 0xdeb8); 77 + 78 + typedef struct local_info_t { 79 + dev_link_t link; 80 + dev_node_t node; 81 + } local_info_t; 82 + 83 + /*====================================================================*/ 84 + 85 + static void release_platform_dev(struct device * dev) 86 + { 87 + DBG(0, "sl811_cs platform_dev release\n"); 88 + dev->parent = NULL; 89 + } 90 + 91 + static struct sl811_platform_data platform_data = { 92 + .potpg = 100, 93 + .power = 50, /* == 100mA */ 94 + // .reset = ... FIXME: invoke CF reset on the card 95 + }; 96 + 97 + static struct resource resources[] = { 98 + [0] = { 99 + .flags = IORESOURCE_IRQ, 100 + }, 101 + [1] = { 102 + // .name = "address", 103 + .flags = IORESOURCE_IO, 104 + }, 105 + [2] = { 106 + // .name = "data", 107 + .flags = IORESOURCE_IO, 108 + }, 109 + }; 110 + 111 + extern struct device_driver sl811h_driver; 112 + 113 + static struct platform_device platform_dev = { 114 + .id = -1, 115 + .dev = { 116 + .platform_data = &platform_data, 117 + .release = release_platform_dev, 118 + }, 119 + .resource = resources, 120 + .num_resources = ARRAY_SIZE(resources), 121 + }; 122 + 123 + static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq) 124 + { 125 + if (platform_dev.dev.parent) 126 + return -EBUSY; 127 + platform_dev.dev.parent = parent; 128 + 129 + /* finish seting up the platform device */ 130 + resources[0].start = irq; 131 + 132 + resources[1].start = base_addr; 133 + resources[1].end = base_addr; 134 + 135 + resources[2].start = base_addr + 1; 136 + resources[2].end = base_addr + 1; 137 + 138 + /* The driver core will probe for us. We know sl811-hcd has been 139 + * initialized already because of the link order dependency. 140 + */ 141 + platform_dev.name = sl811h_driver.name; 142 + return platform_device_register(&platform_dev); 143 + } 144 + 145 + /*====================================================================*/ 146 + 147 + static void sl811_cs_detach(dev_link_t *link) 148 + { 149 + dev_link_t **linkp; 150 + 151 + DBG(0, "sl811_cs_detach(0x%p)\n", link); 152 + 153 + /* Locate device structure */ 154 + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) { 155 + if (*linkp == link) 156 + break; 157 + } 158 + if (*linkp == NULL) 159 + return; 160 + 161 + /* Break the link with Card Services */ 162 + if (link->handle) 163 + pcmcia_deregister_client(link->handle); 164 + 165 + /* Unlink device structure, and free it */ 166 + *linkp = link->next; 167 + /* This points to the parent local_info_t struct */ 168 + kfree(link->priv); 169 + } 170 + 171 + static void sl811_cs_release(dev_link_t * link) 172 + { 173 + 174 + DBG(0, "sl811_cs_release(0x%p)\n", link); 175 + 176 + if (link->open) { 177 + DBG(1, "sl811_cs: release postponed, '%s' still open\n", 178 + link->dev->dev_name); 179 + link->state |= DEV_STALE_CONFIG; 180 + return; 181 + } 182 + 183 + /* Unlink the device chain */ 184 + link->dev = NULL; 185 + 186 + platform_device_unregister(&platform_dev); 187 + pcmcia_release_configuration(link->handle); 188 + if (link->io.NumPorts1) 189 + pcmcia_release_io(link->handle, &link->io); 190 + if (link->irq.AssignedIRQ) 191 + pcmcia_release_irq(link->handle, &link->irq); 192 + link->state &= ~DEV_CONFIG; 193 + 194 + if (link->state & DEV_STALE_LINK) 195 + sl811_cs_detach(link); 196 + } 197 + 198 + static void sl811_cs_config(dev_link_t *link) 199 + { 200 + client_handle_t handle = link->handle; 201 + struct device *parent = &handle_to_dev(handle); 202 + local_info_t *dev = link->priv; 203 + tuple_t tuple; 204 + cisparse_t parse; 205 + int last_fn, last_ret; 206 + u_char buf[64]; 207 + config_info_t conf; 208 + cistpl_cftable_entry_t dflt = { 0 }; 209 + 210 + DBG(0, "sl811_cs_config(0x%p)\n", link); 211 + 212 + tuple.DesiredTuple = CISTPL_CONFIG; 213 + tuple.Attributes = 0; 214 + tuple.TupleData = buf; 215 + tuple.TupleDataMax = sizeof(buf); 216 + tuple.TupleOffset = 0; 217 + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); 218 + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); 219 + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); 220 + link->conf.ConfigBase = parse.config.base; 221 + link->conf.Present = parse.config.rmask[0]; 222 + 223 + /* Configure card */ 224 + link->state |= DEV_CONFIG; 225 + 226 + /* Look up the current Vcc */ 227 + CS_CHECK(GetConfigurationInfo, 228 + pcmcia_get_configuration_info(handle, &conf)); 229 + link->conf.Vcc = conf.Vcc; 230 + 231 + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; 232 + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); 233 + while (1) { 234 + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); 235 + 236 + if (pcmcia_get_tuple_data(handle, &tuple) != 0 237 + || pcmcia_parse_tuple(handle, &tuple, &parse) 238 + != 0) 239 + goto next_entry; 240 + 241 + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) { 242 + dflt = *cfg; 243 + } 244 + 245 + if (cfg->index == 0) 246 + goto next_entry; 247 + 248 + link->conf.ConfigIndex = cfg->index; 249 + 250 + /* Use power settings for Vcc and Vpp if present */ 251 + /* Note that the CIS values need to be rescaled */ 252 + if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) { 253 + if (cfg->vcc.param[CISTPL_POWER_VNOM]/10000 254 + != conf.Vcc) 255 + goto next_entry; 256 + } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) { 257 + if (dflt.vcc.param[CISTPL_POWER_VNOM]/10000 258 + != conf.Vcc) 259 + goto next_entry; 260 + } 261 + 262 + if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM)) 263 + link->conf.Vpp1 = link->conf.Vpp2 = 264 + cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; 265 + else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM)) 266 + link->conf.Vpp1 = link->conf.Vpp2 = 267 + dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; 268 + 269 + /* we need an interrupt */ 270 + if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) 271 + link->conf.Attributes |= CONF_ENABLE_IRQ; 272 + 273 + /* IO window settings */ 274 + link->io.NumPorts1 = link->io.NumPorts2 = 0; 275 + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { 276 + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; 277 + 278 + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; 279 + link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; 280 + link->io.BasePort1 = io->win[0].base; 281 + link->io.NumPorts1 = io->win[0].len; 282 + 283 + if (pcmcia_request_io(link->handle, &link->io) != 0) 284 + goto next_entry; 285 + } 286 + break; 287 + 288 + next_entry: 289 + if (link->io.NumPorts1) 290 + pcmcia_release_io(link->handle, &link->io); 291 + last_ret = pcmcia_get_next_tuple(handle, &tuple); 292 + } 293 + 294 + /* require an IRQ and two registers */ 295 + if (!link->io.NumPorts1 || link->io.NumPorts1 < 2) 296 + goto cs_failed; 297 + if (link->conf.Attributes & CONF_ENABLE_IRQ) 298 + CS_CHECK(RequestIRQ, 299 + pcmcia_request_irq(link->handle, &link->irq)); 300 + else 301 + goto cs_failed; 302 + 303 + CS_CHECK(RequestConfiguration, 304 + pcmcia_request_configuration(link->handle, &link->conf)); 305 + 306 + sprintf(dev->node.dev_name, driver_name); 307 + dev->node.major = dev->node.minor = 0; 308 + link->dev = &dev->node; 309 + 310 + printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", 311 + dev->node.dev_name, link->conf.ConfigIndex, 312 + link->conf.Vcc/10, link->conf.Vcc%10); 313 + if (link->conf.Vpp1) 314 + printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); 315 + printk(", irq %d", link->irq.AssignedIRQ); 316 + printk(", io 0x%04x-0x%04x", link->io.BasePort1, 317 + link->io.BasePort1+link->io.NumPorts1-1); 318 + printk("\n"); 319 + 320 + link->state &= ~DEV_CONFIG_PENDING; 321 + 322 + if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ) 323 + < 0) { 324 + cs_failed: 325 + printk("sl811_cs_config failed\n"); 326 + cs_error(link->handle, last_fn, last_ret); 327 + sl811_cs_release(link); 328 + link->state &= ~DEV_CONFIG_PENDING; 329 + } 330 + } 331 + 332 + static int 333 + sl811_cs_event(event_t event, int priority, event_callback_args_t *args) 334 + { 335 + dev_link_t *link = args->client_data; 336 + 337 + DBG(1, "sl811_cs_event(0x%06x)\n", event); 338 + 339 + switch (event) { 340 + case CS_EVENT_CARD_REMOVAL: 341 + link->state &= ~DEV_PRESENT; 342 + if (link->state & DEV_CONFIG) 343 + sl811_cs_release(link); 344 + break; 345 + 346 + case CS_EVENT_CARD_INSERTION: 347 + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; 348 + sl811_cs_config(link); 349 + break; 350 + 351 + case CS_EVENT_PM_SUSPEND: 352 + link->state |= DEV_SUSPEND; 353 + /* Fall through... */ 354 + case CS_EVENT_RESET_PHYSICAL: 355 + if (link->state & DEV_CONFIG) 356 + pcmcia_release_configuration(link->handle); 357 + break; 358 + 359 + case CS_EVENT_PM_RESUME: 360 + link->state &= ~DEV_SUSPEND; 361 + /* Fall through... */ 362 + case CS_EVENT_CARD_RESET: 363 + if (link->state & DEV_CONFIG) 364 + pcmcia_request_configuration(link->handle, &link->conf); 365 + DBG(0, "reset sl811-hcd here?\n"); 366 + break; 367 + } 368 + return 0; 369 + } 370 + 371 + static dev_link_t *sl811_cs_attach(void) 372 + { 373 + local_info_t *local; 374 + dev_link_t *link; 375 + client_reg_t client_reg; 376 + int ret, i; 377 + 378 + local = kmalloc(sizeof(local_info_t), GFP_KERNEL); 379 + if (!local) 380 + return NULL; 381 + memset(local, 0, sizeof(local_info_t)); 382 + link = &local->link; 383 + link->priv = local; 384 + 385 + /* Initialize */ 386 + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; 387 + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; 388 + if (irq_list[0] == -1) 389 + link->irq.IRQInfo2 = irq_mask; 390 + else 391 + for (i = 0; i < irq_list_count; i++) 392 + link->irq.IRQInfo2 |= 1 << irq_list[i]; 393 + link->irq.Handler = NULL; 394 + 395 + link->conf.Attributes = 0; 396 + link->conf.Vcc = 33; 397 + link->conf.IntType = INT_MEMORY_AND_IO; 398 + 399 + /* Register with Card Services */ 400 + link->next = dev_list; 401 + dev_list = link; 402 + client_reg.dev_info = (dev_info_t *) &driver_name; 403 + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; 404 + client_reg.EventMask = 405 + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | 406 + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | 407 + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; 408 + client_reg.event_handler = &sl811_cs_event; 409 + client_reg.Version = 0x0210; 410 + client_reg.event_callback_args.client_data = link; 411 + ret = pcmcia_register_client(&link->handle, &client_reg); 412 + if (ret != CS_SUCCESS) { 413 + cs_error(link->handle, RegisterClient, ret); 414 + sl811_cs_detach(link); 415 + return NULL; 416 + } 417 + 418 + return link; 419 + } 420 + 421 + static struct pcmcia_driver sl811_cs_driver = { 422 + .owner = THIS_MODULE, 423 + .drv = { 424 + .name = (char *)driver_name, 425 + }, 426 + .attach = sl811_cs_attach, 427 + .detach = sl811_cs_detach, 428 + }; 429 + 430 + /*====================================================================*/ 431 + 432 + static int __init init_sl811_cs(void) 433 + { 434 + return pcmcia_register_driver(&sl811_cs_driver); 435 + } 436 + module_init(init_sl811_cs); 437 + 438 + static void __exit exit_sl811_cs(void) 439 + { 440 + pcmcia_unregister_driver(&sl811_cs_driver); 441 + } 442 + module_exit(exit_sl811_cs);