2 A SL811 CLIENT DRIVER for linux 2.4.x
7 Port to sl811-hcd and 2.6.x by
8 Botond Botyanszki <boti()rocketmail.com>
11 Last update: 2005-05-05
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/sched.h>
18 #include <linux/ptrace.h>
19 #include <linux/slab.h>
20 #include <linux/string.h>
21 #include <linux/timer.h>
22 #include <linux/ioport.h>
23 #include <linux/version.h>
25 #include <asm/system.h>
27 #include <pcmcia/version.h>
28 #include <pcmcia/cs_types.h>
29 #include <pcmcia/cs.h>
30 #include <pcmcia/cistpl.h>
31 #include <pcmcia/cisreg.h>
32 #include <pcmcia/ds.h>
34 #include <linux/usb_sl811.h>
36 MODULE_AUTHOR("Botond Botyanszki");
37 MODULE_DESCRIPTION("REX-CFU1 PCMCIA driver for 2.6");
38 MODULE_LICENSE("GPL");
41 /*====================================================================*/
43 /*====================================================================*/
45 #if defined(DEBUG) || defined(CONFIG_USB_DEBUG)
46 #define DBG(n, args...) printk(KERN_DEBUG "sl811_cs: " args)
48 #define DBG(n, args...) do{}while(0)
51 #define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
53 /*static char *version = "sl811_cs.c 0.04 2005/04/27 00:00:00 (OpenZaurus Team)";*/
55 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
57 #define CS_CHECK(fn, ret) \
58 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
60 /*====================================================================*/
62 /*====================================================================*/
64 static dev_info_t dev_info = "sl811_cs";
65 static dev_link_t *dev_list = NULL;
67 static ioaddr_t base_addr = 0x00000000;
70 static int irq_list[4] = { -1 };
71 MODULE_PARM(irq_list, "1-4i");
72 INT_MODULE_PARM(free_ports, 0);
73 INT_MODULE_PARM(irq_mask, 0xdeb8);
75 /*====================================================================*/
77 /*====================================================================*/
79 static dev_link_t* sl811_cs_attach(void);
80 static void sl811_cs_detach(dev_link_t *);
81 static void sl811_cs_config(dev_link_t *link);
82 static void sl811_cs_release(dev_link_t *arg);
83 static int sl811_cs_event(event_t event, int priority,
84 event_callback_args_t *args);
86 /*====================================================================*/
88 /*====================================================================*/
90 typedef struct local_info_t {
95 static struct pcmcia_driver sl811_driver = {
100 .attach = sl811_cs_attach,
101 .detach = sl811_cs_detach,
103 extern struct device_driver sl811h_driver;
105 static struct sl811_platform_data platform_data;
107 struct resource irq_res;
108 struct resource addr_res;
109 struct resource data_res;
111 static struct platform_device platform_dev = {
116 .dev.platform_data = &platform_data,
117 .dev.bus_id = "sl811-hcd",
118 .dev.driver = &sl811h_driver
121 /*====================================================================*/
122 /* EXTERNAL FUNCTIONS */
123 /*====================================================================*/
124 int sl811h_probe(void *dev);
126 /*====================================================================*/
129 /*====================================================================*/
130 static void release_platform_dev(struct device * dev) {
131 DBG(0, "sl811_cs platform_dev release\n");
134 /*====================================================================*/
135 static dev_link_t *sl811_cs_attach(void)
139 client_reg_t client_reg;
142 local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
143 if (!local) return NULL;
144 memset(local, 0, sizeof(local_info_t));
145 link = &local->link; link->priv = local;
148 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
149 link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
150 if (irq_list[0] == -1)
151 link->irq.IRQInfo2 = irq_mask;
153 for (i = 0; i < 4; i++)
154 link->irq.IRQInfo2 |= 1 << irq_list[i];
155 link->irq.Handler = NULL;
157 link->conf.Attributes = 0;
159 link->conf.IntType = INT_MEMORY_AND_IO;
161 /* Register with Card Services */
162 link->next = dev_list;
164 client_reg.dev_info = &dev_info;
165 client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
166 client_reg.EventMask =
167 CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
168 CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
169 CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
170 client_reg.event_handler = &sl811_cs_event;
171 client_reg.Version = 0x0210;
172 client_reg.event_callback_args.client_data = link;
173 ret = pcmcia_register_client(&link->handle, &client_reg);
174 if (ret != CS_SUCCESS) {
175 cs_error(link->handle, RegisterClient, ret);
176 sl811_cs_detach(link);
181 } /* sl811_cs_attach */
183 /*====================================================================*/
184 static void sl811_cs_detach(dev_link_t *link)
188 DBG(0, "sl811_cs_detach(0x%p)\n", link);
190 /* Locate device structure */
191 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
192 if (*linkp == link) break;
196 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
197 if (link->state & DEV_CONFIG) {
199 printk(KERN_DEBUG "sl811_cs: detach postponed, '%s' "
200 "still locked\n", link->dev->dev_name);
202 link->state |= DEV_STALE_LINK;
207 /* Break the link with Card Services */
209 pcmcia_deregister_client(link->handle);
211 /* Unlink device structure, and free it */
213 /* This points to the parent local_info_t struct */
216 } /* sl811_cs_detach */
219 /*====================================================================*/
220 static int sl811_hc_init(void)
222 /* set up the device structure */
223 resources.irq_res.flags = IORESOURCE_IRQ;
224 resources.irq_res.start = irq;
226 resources.addr_res.flags = IORESOURCE_MEM;
227 resources.addr_res.start = base_addr;
228 resources.addr_res.end = base_addr + 1;
230 resources.data_res.flags = IORESOURCE_MEM;
231 resources.data_res.start = base_addr + 1;
232 resources.data_res.end = base_addr + 4;
234 platform_dev.dev.release = release_platform_dev;
235 platform_device_register(&platform_dev);
236 /* FIXME: we register the platform device with 0 resources
237 otherwise the unregister call won't work*/
239 platform_dev.num_resources = 3;
240 platform_dev.resource = (struct resource *) &resources;
242 /* try to initialize the host controller */
243 if (sl811h_probe(&platform_dev.dev) != 0) {
244 DBG(0, "sl811h_probe() didn't return 0\n");
251 /*====================================================================*/
252 static void sl811_cs_config(dev_link_t *link)
254 client_handle_t handle = link->handle;
255 local_info_t *dev = link->priv;
258 int last_fn, last_ret;
261 cistpl_cftable_entry_t dflt = { 0 };
263 DBG(0, "sl811_cs_config(0x%p)\n", link);
265 tuple.DesiredTuple = CISTPL_CONFIG;
266 tuple.Attributes = 0;
267 tuple.TupleData = buf;
268 tuple.TupleDataMax = sizeof(buf);
269 tuple.TupleOffset = 0;
270 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
271 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
272 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
273 link->conf.ConfigBase = parse.config.base;
274 link->conf.Present = parse.config.rmask[0];
277 link->state |= DEV_CONFIG;
279 /* Look up the current Vcc */
280 CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
281 link->conf.Vcc = conf.Vcc;
283 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
284 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
286 cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
287 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
288 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
291 if (cfg->flags & CISTPL_CFTABLE_DEFAULT) {
295 if (cfg->index == 0) goto next_entry;
297 link->conf.ConfigIndex = cfg->index;
299 /* Does this card need audio output? */
300 if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
301 link->conf.Attributes |= CONF_ENABLE_SPKR;
302 link->conf.Status = CCSR_AUDIO_ENA;
305 /* Use power settings for Vcc and Vpp if present */
306 /* Note that the CIS values need to be rescaled */
307 if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
308 if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000) {
311 } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
312 if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000) {
317 if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
318 link->conf.Vpp1 = link->conf.Vpp2 =
319 cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
320 else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
321 link->conf.Vpp1 = link->conf.Vpp2 =
322 dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
324 /* Do we need to allocate an interrupt? */
325 if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
326 link->conf.Attributes |= CONF_ENABLE_IRQ;
328 /* IO window settings */
329 link->io.NumPorts1 = link->io.NumPorts2 = 0;
330 if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
331 cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
333 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
334 link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
335 link->io.BasePort1 = io->win[0].base;
336 link->io.NumPorts1 = io->win[0].len;
338 if (pcmcia_request_io(link->handle, &link->io) != 0)
344 if (link->io.NumPorts1)
345 pcmcia_release_io(link->handle, &link->io);
346 last_ret = pcmcia_get_next_tuple(handle, &tuple);
349 if (link->conf.Attributes & CONF_ENABLE_IRQ)
350 CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
352 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
355 if (link->io.BasePort1)
356 release_region(link->io.BasePort1, link->io.NumPorts1);
359 sprintf(dev->node.dev_name, "cf_usb0");
360 dev->node.major = dev->node.minor = 0;
361 link->dev = &dev->node;
363 printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
364 dev->node.dev_name, link->conf.ConfigIndex,
365 link->conf.Vcc/10, link->conf.Vcc%10);
367 printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
368 if (link->conf.Attributes & CONF_ENABLE_IRQ) {
369 printk(", irq %d", link->irq.AssignedIRQ);
370 irq = link->irq.AssignedIRQ;
373 if (link->io.NumPorts1) {
374 printk(", io 0x%04x-0x%04x", link->io.BasePort1,
375 link->io.BasePort1+link->io.NumPorts1-1);
376 base_addr = link->io.BasePort1;
381 link->state &= ~DEV_CONFIG_PENDING;
383 /* Release resources claimed by PCMCIA for the sl811h driver */
384 release_region(link->io.BasePort1, link->io.NumPorts1);
386 if (sl811_hc_init() == 0) goto cs_failed;
391 printk("sl811_cs_config failed\n");
392 cs_error(link->handle, last_fn, last_ret);
393 sl811_cs_release(link);
394 link->state &= ~DEV_CONFIG_PENDING;
396 } /* sl811_cs_config */
398 /*====================================================================*/
399 static void sl811_cs_release(dev_link_t * link)
402 DBG(0, "sl811_cs_release(0x%p)\n", link);
405 DBG(1, "sl811_cs: release postponed, '%s' still open\n",
406 link->dev->dev_name);
407 link->state |= DEV_STALE_CONFIG;
411 /* request IO, because PCMCIA thinks it has claimed it */
412 request_region(link->io.BasePort1, link->io.NumPorts1, "sl811_cs");
414 /* Unlink the device chain */
417 pcmcia_release_configuration(link->handle);
418 if (link->io.NumPorts1)
419 pcmcia_release_io(link->handle, &link->io);
420 if (link->irq.AssignedIRQ)
421 pcmcia_release_irq(link->handle, &link->irq);
422 link->state &= ~DEV_CONFIG;
424 if (link->state & DEV_STALE_LINK)
425 sl811_cs_detach(link);
427 /* FIXME: if the unregister call frees up the resources, it oopses
428 so we pretend to have 0 resources */
429 platform_dev.num_resources = 0;
430 platform_dev.resource = NULL;
432 platform_device_unregister(&platform_dev);
434 } /* sl811_cs_release */
436 /*====================================================================*/
437 static int sl811_cs_event(event_t event, int priority, event_callback_args_t *args)
439 dev_link_t *link = args->client_data;
441 DBG(1, "sl811_cs_event(0x%06x)\n", event);
444 case CS_EVENT_CARD_REMOVAL:
445 link->state &= ~DEV_PRESENT;
446 if (link->state & DEV_CONFIG) {
447 sl811_cs_release(link);
451 case CS_EVENT_CARD_INSERTION:
452 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
453 sl811_cs_config(link);
456 case CS_EVENT_PM_SUSPEND:
457 link->state |= DEV_SUSPEND;
458 /* Fall through... */
459 case CS_EVENT_RESET_PHYSICAL:
460 if (link->state & DEV_CONFIG)
461 pcmcia_release_configuration(link->handle);
463 case CS_EVENT_PM_RESUME:
464 link->state &= ~DEV_SUSPEND;
465 /* Fall through... */
466 case CS_EVENT_CARD_RESET:
467 if (link->state & DEV_CONFIG)
468 pcmcia_request_configuration(link->handle, &link->conf);
470 INFO("FIXME: card reset\n");
475 } /* sl811_cs_event */
477 /*====================================================================*/
478 static int __init init_sl811_cs(void)
480 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
482 DBG(0, "%s\n", version);
483 CardServices(GetCardServicesInfo, &serv);
484 if (serv.Revision != CS_RELEASE_CODE) {
485 printk(KERN_NOTICE "sl811_cs: Card Services release "
486 "does not match!\n");
489 register_pccard_driver(&dev_info, &sl811_cs_attach, &sl811_cs_detach);
492 return pcmcia_register_driver(&sl811_driver);
497 /*====================================================================*/
498 static void __exit exit_sl811_cs(void)
500 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
501 DBG(0, "sl811_cs: unloading\n");
502 unregister_pccard_driver(&dev_info);
503 while (dev_list != NULL) {
504 del_timer(&dev_list->release);
505 if (dev_list->state & DEV_CONFIG)
506 sl811_cs_release(dev_list);
507 sl811_cs_detach(dev_list);
510 pcmcia_unregister_driver(&sl811_driver);
515 /*====================================================================*/
517 module_init(init_sl811_cs);
518 module_exit(exit_sl811_cs);