]> pilppa.org Git - familiar-h63xx-build.git/blob - org.handhelds.familiar/packages/cfu1/files/sl811_cs.c
OE tree imported from monotone branch org.openembedded.oz354fam083 at revision 8b12e3...
[familiar-h63xx-build.git] / org.handhelds.familiar / packages / cfu1 / files / sl811_cs.c
1 /*
2   A SL811 CLIENT DRIVER for linux 2.4.x
3   Filename: sl811_cs.c
4   Version:  0.0.2
5   Author:   Yukio Yamamoto
6
7   Port to sl811-hcd and 2.6.x by
8     Botond Botyanszki <boti()rocketmail.com>
9     Simon Pickering
10
11   Last update: 2005-05-05
12 */
13
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>
24 #include <asm/io.h>
25 #include <asm/system.h>
26
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>
33
34 #include <linux/usb_sl811.h>
35
36 MODULE_AUTHOR("Botond Botyanszki");
37 MODULE_DESCRIPTION("REX-CFU1 PCMCIA driver for 2.6");
38 MODULE_LICENSE("GPL");
39
40
41 /*====================================================================*/
42 /* MACROS                                                             */
43 /*====================================================================*/
44
45 #if defined(DEBUG) || defined(CONFIG_USB_DEBUG)
46 #define DBG(n, args...) printk(KERN_DEBUG "sl811_cs: " args)
47 #else
48 #define DBG(n, args...) do{}while(0)
49 #endif
50
51 #define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
52
53 /*static char *version = "sl811_cs.c 0.04 2005/04/27 00:00:00 (OpenZaurus Team)";*/
54
55 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
56
57 #define CS_CHECK(fn, ret) \
58         do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
59
60 /*====================================================================*/
61 /* VARIABLES                                                          */
62 /*====================================================================*/
63
64 static dev_info_t dev_info  = "sl811_cs";
65 static dev_link_t *dev_list = NULL;
66
67 static ioaddr_t base_addr   = 0x00000000;
68 static int irq              = -1;
69
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);
74
75 /*====================================================================*/
76 /* PROTO TYPES                                                        */
77 /*====================================================================*/
78
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);
85
86 /*====================================================================*/
87 /* PROTO TYPES                                                        */
88 /*====================================================================*/
89
90 typedef struct local_info_t {
91   dev_link_t            link;
92   dev_node_t            node;
93 } local_info_t;
94
95 static struct pcmcia_driver sl811_driver = {
96         .owner          = THIS_MODULE,
97         .drv            = {
98                 .name   = "sl811_cs",
99         },
100         .attach         = sl811_cs_attach,
101         .detach         = sl811_cs_detach,
102 };
103 extern struct device_driver sl811h_driver;
104
105 static struct sl811_platform_data platform_data;
106 static struct res {
107         struct resource irq_res;
108         struct resource addr_res;
109         struct resource data_res;
110 } resources;
111 static struct platform_device platform_dev = {
112         .name              = "sl811_cs",
113         .id                = 0,
114         .num_resources     = 0,
115         .dev.dma_mask      = 0,
116         .dev.platform_data = &platform_data,
117         .dev.bus_id        = "sl811-hcd",
118         .dev.driver        = &sl811h_driver
119 };
120
121 /*====================================================================*/
122 /* EXTERNAL FUNCTIONS                                                 */
123 /*====================================================================*/
124 int sl811h_probe(void *dev);
125
126 /*====================================================================*/
127
128
129 /*====================================================================*/
130 static void release_platform_dev(struct device * dev) {
131         DBG(0, "sl811_cs platform_dev release\n");
132 }
133
134 /*====================================================================*/
135 static dev_link_t *sl811_cs_attach(void)
136 {
137   local_info_t *local;
138   dev_link_t *link;
139   client_reg_t client_reg;
140   int ret, i;
141   
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;
146
147   /* Initialize */
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;
152   else
153     for (i = 0; i < 4; i++)
154       link->irq.IRQInfo2 |= 1 << irq_list[i];
155   link->irq.Handler = NULL;
156     
157   link->conf.Attributes = 0;
158   link->conf.Vcc = 33;
159   link->conf.IntType = INT_MEMORY_AND_IO;
160   
161   /* Register with Card Services */
162   link->next = dev_list;
163   dev_list = link;
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);
177           return NULL;
178   }
179   
180   return link;
181 } /* sl811_cs_attach */
182
183 /*====================================================================*/
184 static void sl811_cs_detach(dev_link_t *link)
185 {
186   dev_link_t **linkp;
187
188   DBG(0, "sl811_cs_detach(0x%p)\n", link);
189     
190   /* Locate device structure */
191   for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
192     if (*linkp == link) break;
193   if (*linkp == NULL)
194     return;
195   
196 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) 
197   if (link->state & DEV_CONFIG) {
198 #ifdef PCMCIA_DEBUG
199     printk(KERN_DEBUG "sl811_cs: detach postponed, '%s' "
200            "still locked\n", link->dev->dev_name);
201 #endif
202     link->state |= DEV_STALE_LINK;
203     return;
204   }
205 #endif
206
207   /* Break the link with Card Services */
208   if (link->handle)
209     pcmcia_deregister_client(link->handle);
210   
211   /* Unlink device structure, and free it */
212   *linkp = link->next;
213   /* This points to the parent local_info_t struct */
214   kfree(link->priv);
215   
216 } /* sl811_cs_detach */
217
218
219 /*====================================================================*/
220 static int sl811_hc_init(void)
221 {
222         /* set up the device structure */
223         resources.irq_res.flags = IORESOURCE_IRQ;
224         resources.irq_res.start = irq;
225
226         resources.addr_res.flags = IORESOURCE_MEM;
227         resources.addr_res.start = base_addr;
228         resources.addr_res.end = base_addr + 1;
229
230         resources.data_res.flags = IORESOURCE_MEM;
231         resources.data_res.start = base_addr + 1;
232         resources.data_res.end   = base_addr + 4;
233
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*/
238
239         platform_dev.num_resources = 3;
240         platform_dev.resource      = (struct resource *) &resources;
241
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");
245                 return 0;
246         }
247         return 1;
248 }
249
250
251 /*====================================================================*/
252 static void sl811_cs_config(dev_link_t *link)
253 {
254   client_handle_t handle = link->handle;
255   local_info_t *dev = link->priv;
256   tuple_t tuple;
257   cisparse_t parse;
258   int last_fn, last_ret;
259   u_char buf[64];
260   config_info_t conf;
261   cistpl_cftable_entry_t dflt = { 0 };
262
263   DBG(0, "sl811_cs_config(0x%p)\n", link);
264   
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];
275     
276   /* Configure card */
277   link->state |= DEV_CONFIG;
278
279   /* Look up the current Vcc */
280   CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
281   link->conf.Vcc = conf.Vcc;
282   
283   tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
284   CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
285   while (1) {
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)
289                 goto next_entry;
290
291     if (cfg->flags & CISTPL_CFTABLE_DEFAULT) {
292       dflt = *cfg;
293     }
294
295     if (cfg->index == 0) goto next_entry;    
296
297     link->conf.ConfigIndex = cfg->index;
298     
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;
303     }
304     
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) {
309         goto next_entry;
310       }
311     } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
312       if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000) {
313         goto next_entry;
314       }
315     }
316     
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;
323     
324     /* Do we need to allocate an interrupt? */
325     if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
326       link->conf.Attributes |= CONF_ENABLE_IRQ;
327     
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;
332
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;
337
338       if (pcmcia_request_io(link->handle, &link->io) != 0)
339               goto next_entry;
340     }
341     break;
342     
343   next_entry:
344     if (link->io.NumPorts1)
345       pcmcia_release_io(link->handle, &link->io);
346           last_ret = pcmcia_get_next_tuple(handle, &tuple);
347   }
348   
349   if (link->conf.Attributes & CONF_ENABLE_IRQ)
350     CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
351   
352   CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
353   
354   if (free_ports) {
355     if (link->io.BasePort1)
356       release_region(link->io.BasePort1, link->io.NumPorts1);
357   }
358
359   sprintf(dev->node.dev_name, "cf_usb0");
360   dev->node.major = dev->node.minor = 0;
361   link->dev = &dev->node;
362   
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);
366   if (link->conf.Vpp1)
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;
371   }
372
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;
377   }
378
379   printk("\n");
380   
381   link->state &= ~DEV_CONFIG_PENDING;
382
383   /* Release resources claimed by PCMCIA for the sl811h driver */
384   release_region(link->io.BasePort1, link->io.NumPorts1);
385   
386   if (sl811_hc_init() == 0) goto cs_failed;
387
388   return;
389   
390  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;
395
396 } /* sl811_cs_config */
397
398 /*====================================================================*/
399 static void sl811_cs_release(dev_link_t * link)
400 {
401
402   DBG(0, "sl811_cs_release(0x%p)\n", link);
403   
404   if (link->open) {
405     DBG(1, "sl811_cs: release postponed, '%s' still open\n",
406           link->dev->dev_name);
407     link->state |= DEV_STALE_CONFIG;
408     return;
409   }
410
411   /* request IO, because PCMCIA thinks it has claimed it */
412   request_region(link->io.BasePort1, link->io.NumPorts1, "sl811_cs");
413
414   /* Unlink the device chain */
415   link->dev = NULL;
416   
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;
423   
424   if (link->state & DEV_STALE_LINK)
425     sl811_cs_detach(link);
426
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;
431
432   platform_device_unregister(&platform_dev);
433
434 } /* sl811_cs_release */
435
436 /*====================================================================*/
437 static int sl811_cs_event(event_t event, int priority, event_callback_args_t *args)
438 {
439   dev_link_t *link = args->client_data;
440     
441   DBG(1, "sl811_cs_event(0x%06x)\n", event);
442     
443   switch (event) {
444   case CS_EVENT_CARD_REMOVAL:
445     link->state &= ~DEV_PRESENT;
446     if (link->state & DEV_CONFIG) {
447       sl811_cs_release(link);
448     }
449     break;
450
451   case CS_EVENT_CARD_INSERTION:
452     link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
453     sl811_cs_config(link);
454     break;
455
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);
462     break;
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);
469
470     INFO("FIXME: card reset\n");
471
472     break;
473   }
474   return 0;
475 } /* sl811_cs_event */
476
477 /*====================================================================*/
478 static int __init init_sl811_cs(void)
479 {
480 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
481   servinfo_t serv;
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");
487     return -EINVAL;
488   }
489   register_pccard_driver(&dev_info, &sl811_cs_attach, &sl811_cs_detach);
490   return 0;
491 #else
492   return pcmcia_register_driver(&sl811_driver);
493 #endif
494
495 }
496
497 /*====================================================================*/
498 static void __exit exit_sl811_cs(void)
499 {
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);
508   }
509 #else
510   pcmcia_unregister_driver(&sl811_driver);
511 #endif
512
513 }
514
515 /*====================================================================*/
516
517 module_init(init_sl811_cs);
518 module_exit(exit_sl811_cs);