1c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/* 2c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * PCMCIA driver for SL811HS (as found in REX-CFU1U) 3c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * Filename: sl811_cs.c 4c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * Author: Yukio Yamamoto 5c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * 6c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * Port to sl811-hcd and 2.6.x by 7c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * Botond Botyanszki <boti@rocketmail.com> 8c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * Simon Pickering 9c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * 10c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell * Last update: 2005-05-12 11c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell */ 12c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 13c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <linux/kernel.h> 14c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <linux/module.h> 15c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <linux/init.h> 16c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <linux/ptrace.h> 17c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <linux/slab.h> 18c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <linux/string.h> 19c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <linux/timer.h> 20c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <linux/ioport.h> 21d052d1beff706920e82c5d55006b08e256b5df09Russell King#include <linux/platform_device.h> 22c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 23c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <pcmcia/cistpl.h> 24c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <pcmcia/cisreg.h> 25c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#include <pcmcia/ds.h> 26c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 27325a4af60dc945bf2da9cbcdbabb276e312b297cDavid Brownell#include <linux/usb/sl811.h> 28c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 29c6de2b64eb575a3f9326969ec5fcdc6032b38e42David BrownellMODULE_AUTHOR("Botond Botyanszki"); 30c6de2b64eb575a3f9326969ec5fcdc6032b38e42David BrownellMODULE_DESCRIPTION("REX-CFU1U PCMCIA driver for 2.6"); 31c6de2b64eb575a3f9326969ec5fcdc6032b38e42David BrownellMODULE_LICENSE("GPL"); 32c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 33c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 34c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/*====================================================================*/ 35c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/* MACROS */ 36c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/*====================================================================*/ 37c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 38c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell#define INFO(args...) printk(KERN_INFO "sl811_cs: " args) 39c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 40c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/*====================================================================*/ 41c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/* VARIABLES */ 42c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/*====================================================================*/ 43c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 44c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownelltypedef struct local_info_t { 45fd238232cd0ff4840ae6946bb338502154096d88Dominik Brodowski struct pcmcia_device *p_dev; 46c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} local_info_t; 47c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 48fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowskistatic void sl811_cs_release(struct pcmcia_device * link); 49cc3b4866bee996c922e875b8c8efe9f0d8803aaeDominik Brodowski 50c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/*====================================================================*/ 51c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 52c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellstatic void release_platform_dev(struct device * dev) 53c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell{ 549b44de2015ff4a2ed1d56efedfcc72b917d356a6Dominik Brodowski dev_dbg(dev, "sl811_cs platform_dev release\n"); 55c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell dev->parent = NULL; 56c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} 57c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 58c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellstatic struct sl811_platform_data platform_data = { 59c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .potpg = 100, 60c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .power = 50, /* == 100mA */ 61c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell // .reset = ... FIXME: invoke CF reset on the card 62c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell}; 63c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 64c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellstatic struct resource resources[] = { 65c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell [0] = { 66c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .flags = IORESOURCE_IRQ, 67c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell }, 68c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell [1] = { 69c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell // .name = "address", 70c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .flags = IORESOURCE_IO, 71c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell }, 72c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell [2] = { 73c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell // .name = "data", 74c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .flags = IORESOURCE_IO, 75c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell }, 76c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell}; 77c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 78a0c53f1dca10acc93462339cbd0bf24b10d60a13David Brownellextern struct platform_driver sl811h_driver; 79c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 80c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellstatic struct platform_device platform_dev = { 81c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .id = -1, 82c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .dev = { 83c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .platform_data = &platform_data, 84c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .release = release_platform_dev, 85c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell }, 86c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .resource = resources, 87c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .num_resources = ARRAY_SIZE(resources), 88c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell}; 89c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 90d39bd56452b509f8d6054883b8a0129950ba50ccDominik Brodowskistatic int sl811_hc_init(struct device *parent, resource_size_t base_addr, 91d39bd56452b509f8d6054883b8a0129950ba50ccDominik Brodowski int irq) 92c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell{ 93c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell if (platform_dev.dev.parent) 94c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell return -EBUSY; 95c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell platform_dev.dev.parent = parent; 96c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 97c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell /* finish seting up the platform device */ 98c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell resources[0].start = irq; 99c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 100c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell resources[1].start = base_addr; 101c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell resources[1].end = base_addr; 102c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 103c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell resources[2].start = base_addr + 1; 104c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell resources[2].end = base_addr + 1; 105c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 106c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell /* The driver core will probe for us. We know sl811-hcd has been 107c9a50cc9318772e62d56f2a9172bdfda72bdacbeDavid Brownell * initialized already because of the link order dependency created 108c9a50cc9318772e62d56f2a9172bdfda72bdacbeDavid Brownell * by referencing "sl811h_driver". 109c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell */ 110a0c53f1dca10acc93462339cbd0bf24b10d60a13David Brownell platform_dev.name = sl811h_driver.driver.name; 111c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell return platform_device_register(&platform_dev); 112c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} 113c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 114c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/*====================================================================*/ 115c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 116fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowskistatic void sl811_cs_detach(struct pcmcia_device *link) 117c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell{ 1189b44de2015ff4a2ed1d56efedfcc72b917d356a6Dominik Brodowski dev_dbg(&link->dev, "sl811_cs_detach\n"); 119c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 120e2d4096365e06b9a3799afbadc28b4519c0b3526Dominik Brodowski sl811_cs_release(link); 121c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 122c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell /* This points to the parent local_info_t struct */ 123c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell kfree(link->priv); 124c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} 125c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 126fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowskistatic void sl811_cs_release(struct pcmcia_device * link) 127c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell{ 1289b44de2015ff4a2ed1d56efedfcc72b917d356a6Dominik Brodowski dev_dbg(&link->dev, "sl811_cs_release\n"); 129c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 130fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowski pcmcia_disable_device(link); 131c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell platform_device_unregister(&platform_dev); 132c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} 133c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 13400990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowskistatic int sl811_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) 13584e2d34004dcd0c90d1af43a143511b334f11a4dDominik Brodowski{ 13600990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowski if (p_dev->config_index == 0) 13700990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowski return -EINVAL; 13800990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowski 13900990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowski return pcmcia_request_io(p_dev); 14084e2d34004dcd0c90d1af43a143511b334f11a4dDominik Brodowski} 14184e2d34004dcd0c90d1af43a143511b334f11a4dDominik Brodowski 14284e2d34004dcd0c90d1af43a143511b334f11a4dDominik Brodowski 14315b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowskistatic int sl811_cs_config(struct pcmcia_device *link) 144c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell{ 145dd2e5a156525f11754d9b1e0583f6bb49c253d62Dominik Brodowski struct device *parent = &link->dev; 1469b44de2015ff4a2ed1d56efedfcc72b917d356a6Dominik Brodowski int ret; 147c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 1489b44de2015ff4a2ed1d56efedfcc72b917d356a6Dominik Brodowski dev_dbg(&link->dev, "sl811_cs_config\n"); 149c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 150440eed43e2a95bb842488755683716814da10f2bDominik Brodowski link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | 15100990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowski CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO; 152440eed43e2a95bb842488755683716814da10f2bDominik Brodowski 153ad913c11928f51abb6174f165db8d8d205b22e21Dominik Brodowski if (pcmcia_loop_config(link, sl811_cs_config_check, NULL)) 154ad913c11928f51abb6174f165db8d8d205b22e21Dominik Brodowski goto failed; 155c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 156c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell /* require an IRQ and two registers */ 1579a017a910346afd88ec2e065989903bf211a7d37Dominik Brodowski if (resource_size(link->resource[0]) < 2) 158ad913c11928f51abb6174f165db8d8d205b22e21Dominik Brodowski goto failed; 159eb14120f743d29744d9475bffec56ff4ad43a749Dominik Brodowski 160eb14120f743d29744d9475bffec56ff4ad43a749Dominik Brodowski if (!link->irq) 161ad913c11928f51abb6174f165db8d8d205b22e21Dominik Brodowski goto failed; 162c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 1631ac71e5a35eebee60cdcf15b3980bd94498f037bDominik Brodowski ret = pcmcia_enable_device(link); 1649b44de2015ff4a2ed1d56efedfcc72b917d356a6Dominik Brodowski if (ret) 1659b44de2015ff4a2ed1d56efedfcc72b917d356a6Dominik Brodowski goto failed; 166c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 1679a017a910346afd88ec2e065989903bf211a7d37Dominik Brodowski if (sl811_hc_init(parent, link->resource[0]->start, link->irq) 168c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell < 0) { 169ad913c11928f51abb6174f165db8d8d205b22e21Dominik Brodowskifailed: 170ad913c11928f51abb6174f165db8d8d205b22e21Dominik Brodowski printk(KERN_WARNING "sl811_cs_config failed\n"); 171c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell sl811_cs_release(link); 17215b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowski return -ENODEV; 173c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell } 17415b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowski return 0; 175c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} 176c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 17715b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowskistatic int sl811_cs_probe(struct pcmcia_device *link) 178c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell{ 179c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell local_info_t *local; 180c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 181dd00cc486ab1c17049a535413d1751ef3482141cYoann Padioleau local = kzalloc(sizeof(local_info_t), GFP_KERNEL); 182c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell if (!local) 183f8cfa618dccbdc6dab5297f75779566a388a98fdDominik Brodowski return -ENOMEM; 184fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowski local->p_dev = link; 185c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell link->priv = local; 186c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 18715b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowski return sl811_cs_config(link); 188c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} 189c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 19025f8f54f6e178acfd503a95441b0ea05c525f751Joe Perchesstatic const struct pcmcia_device_id sl811_ids[] = { 19122f3a8f5fc94be4dd31c4c5ec1d1dc2b9c83a8acDavid Brownell PCMCIA_DEVICE_MANF_CARD(0xc015, 0x0001), /* RATOC USB HOST CF+ Card */ 19222f3a8f5fc94be4dd31c4c5ec1d1dc2b9c83a8acDavid Brownell PCMCIA_DEVICE_NULL, 19322f3a8f5fc94be4dd31c4c5ec1d1dc2b9c83a8acDavid Brownell}; 19422f3a8f5fc94be4dd31c4c5ec1d1dc2b9c83a8acDavid BrownellMODULE_DEVICE_TABLE(pcmcia, sl811_ids); 19522f3a8f5fc94be4dd31c4c5ec1d1dc2b9c83a8acDavid Brownell 196c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellstatic struct pcmcia_driver sl811_cs_driver = { 197c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell .owner = THIS_MODULE, 1982e9b981a7c63ee8278df6823f8389d69dad1a499Dominik Brodowski .name = "sl811_cs", 19915b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowski .probe = sl811_cs_probe, 200cc3b4866bee996c922e875b8c8efe9f0d8803aaeDominik Brodowski .remove = sl811_cs_detach, 20122f3a8f5fc94be4dd31c4c5ec1d1dc2b9c83a8acDavid Brownell .id_table = sl811_ids, 202c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell}; 203c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 204c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell/*====================================================================*/ 205c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 206c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellstatic int __init init_sl811_cs(void) 207c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell{ 208c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell return pcmcia_register_driver(&sl811_cs_driver); 209c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} 210c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellmodule_init(init_sl811_cs); 211c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell 212c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellstatic void __exit exit_sl811_cs(void) 213c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell{ 214c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell pcmcia_unregister_driver(&sl811_cs_driver); 215c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownell} 216c6de2b64eb575a3f9326969ec5fcdc6032b38e42David Brownellmodule_exit(exit_sl811_cs); 217