1/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 Exp $ */ 2/*====================================================================== 3 4 A teles S0 PCMCIA client driver 5 6 Based on skeleton by David Hinds, dhinds@allegro.stanford.edu 7 Written by Christof Petig, christof.petig@wtal.de 8 9 Also inspired by ELSA PCMCIA driver 10 by Klaus Lichtenwalder <Lichtenwalder@ACM.org> 11 12 Extentions to new hisax_pcmcia by Karsten Keil 13 14 minor changes to be compatible with kernel 2.4.x 15 by Jan.Schubert@GMX.li 16 17======================================================================*/ 18 19#include <linux/kernel.h> 20#include <linux/module.h> 21#include <linux/init.h> 22#include <linux/ptrace.h> 23#include <linux/slab.h> 24#include <linux/string.h> 25#include <linux/timer.h> 26#include <linux/ioport.h> 27#include <asm/io.h> 28#include <asm/system.h> 29 30#include <pcmcia/cs.h> 31#include <pcmcia/cistpl.h> 32#include <pcmcia/cisreg.h> 33#include <pcmcia/ds.h> 34#include "hisax_cfg.h" 35 36MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards"); 37MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de"); 38MODULE_LICENSE("GPL"); 39 40 41/*====================================================================*/ 42 43/* Parameters that can be set with 'insmod' */ 44 45static int protocol = 2; /* EURO-ISDN Default */ 46module_param(protocol, int, 0); 47 48/*====================================================================*/ 49 50/* 51 The event() function is this driver's Card Services event handler. 52 It will be called by Card Services when an appropriate card status 53 event is received. The config() and release() entry points are 54 used to configure or release a socket, in response to card insertion 55 and ejection events. They are invoked from the teles_cs event 56 handler. 57*/ 58 59static int teles_cs_config(struct pcmcia_device *link) __devinit ; 60static void teles_cs_release(struct pcmcia_device *link); 61 62/* 63 The attach() and detach() entry points are used to create and destroy 64 "instances" of the driver, where each instance represents everything 65 needed to manage one actual PCMCIA card. 66*/ 67 68static void teles_detach(struct pcmcia_device *p_dev) __devexit ; 69 70typedef struct local_info_t { 71 struct pcmcia_device *p_dev; 72 int busy; 73 int cardnr; 74} local_info_t; 75 76/*====================================================================== 77 78 teles_attach() creates an "instance" of the driver, allocatingx 79 local data structures for one device. The device is registered 80 with Card Services. 81 82 The dev_link structure is initialized, but we don't actually 83 configure the card at this point -- we wait until we receive a 84 card insertion event. 85 86======================================================================*/ 87 88static int __devinit teles_probe(struct pcmcia_device *link) 89{ 90 local_info_t *local; 91 92 dev_dbg(&link->dev, "teles_attach()\n"); 93 94 /* Allocate space for private device-specific data */ 95 local = kzalloc(sizeof(local_info_t), GFP_KERNEL); 96 if (!local) return -ENOMEM; 97 local->cardnr = -1; 98 99 local->p_dev = link; 100 link->priv = local; 101 102 /* 103 General socket configuration defaults can go here. In this 104 client, we assume very little, and rely on the CIS for almost 105 everything. In most clients, many details (i.e., number, sizes, 106 and attributes of IO windows) are fixed by the nature of the 107 device, and can be hard-wired here. 108 */ 109 link->resource[0]->end = 96; 110 link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; 111 112 link->conf.Attributes = CONF_ENABLE_IRQ; 113 link->conf.IntType = INT_MEMORY_AND_IO; 114 115 return teles_cs_config(link); 116} /* teles_attach */ 117 118/*====================================================================== 119 120 This deletes a driver "instance". The device is de-registered 121 with Card Services. If it has been released, all local data 122 structures are freed. Otherwise, the structures will be freed 123 when the device is released. 124 125======================================================================*/ 126 127static void __devexit teles_detach(struct pcmcia_device *link) 128{ 129 local_info_t *info = link->priv; 130 131 dev_dbg(&link->dev, "teles_detach(0x%p)\n", link); 132 133 info->busy = 1; 134 teles_cs_release(link); 135 136 kfree(info); 137} /* teles_detach */ 138 139/*====================================================================== 140 141 teles_cs_config() is scheduled to run after a CARD_INSERTION event 142 is received, to configure the PCMCIA socket, and to make the 143 device available to the system. 144 145======================================================================*/ 146 147static int teles_cs_configcheck(struct pcmcia_device *p_dev, 148 cistpl_cftable_entry_t *cf, 149 cistpl_cftable_entry_t *dflt, 150 unsigned int vcc, 151 void *priv_data) 152{ 153 int j; 154 155 p_dev->io_lines = 5; 156 157 if ((cf->io.nwin > 0) && cf->io.win[0].base) { 158 printk(KERN_INFO "(teles_cs: looks like the 96 model)\n"); 159 p_dev->resource[0]->start = cf->io.win[0].base; 160 if (!pcmcia_request_io(p_dev)) 161 return 0; 162 } else { 163 printk(KERN_INFO "(teles_cs: looks like the 97 model)\n"); 164 for (j = 0x2f0; j > 0x100; j -= 0x10) { 165 p_dev->resource[0]->start = j; 166 if (!pcmcia_request_io(p_dev)) 167 return 0; 168 } 169 } 170 return -ENODEV; 171} 172 173static int __devinit teles_cs_config(struct pcmcia_device *link) 174{ 175 local_info_t *dev; 176 int i; 177 IsdnCard_t icard; 178 179 dev_dbg(&link->dev, "teles_config(0x%p)\n", link); 180 dev = link->priv; 181 182 i = pcmcia_loop_config(link, teles_cs_configcheck, NULL); 183 if (i != 0) 184 goto cs_failed; 185 186 if (!link->irq) 187 goto cs_failed; 188 189 i = pcmcia_request_configuration(link, &link->conf); 190 if (i != 0) 191 goto cs_failed; 192 193 /* Finally, report what we've done */ 194 dev_info(&link->dev, "index 0x%02x:", 195 link->conf.ConfigIndex); 196 if (link->conf.Attributes & CONF_ENABLE_IRQ) 197 printk(", irq %d", link->irq); 198 if (link->resource[0]) 199 printk(" & %pR", link->resource[0]); 200 if (link->resource[1]) 201 printk(" & %pR", link->resource[1]); 202 printk("\n"); 203 204 icard.para[0] = link->irq; 205 icard.para[1] = link->resource[0]->start; 206 icard.protocol = protocol; 207 icard.typ = ISDN_CTYPE_TELESPCMCIA; 208 209 i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard); 210 if (i < 0) { 211 printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n", 212 i, (unsigned int) link->resource[0]->start); 213 teles_cs_release(link); 214 return -ENODEV; 215 } 216 217 ((local_info_t*)link->priv)->cardnr = i; 218 return 0; 219 220cs_failed: 221 teles_cs_release(link); 222 return -ENODEV; 223} /* teles_cs_config */ 224 225/*====================================================================== 226 227 After a card is removed, teles_cs_release() will unregister the net 228 device, and release the PCMCIA configuration. If the device is 229 still open, this will be postponed until it is closed. 230 231======================================================================*/ 232 233static void teles_cs_release(struct pcmcia_device *link) 234{ 235 local_info_t *local = link->priv; 236 237 dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link); 238 239 if (local) { 240 if (local->cardnr >= 0) { 241 /* no unregister function with hisax */ 242 HiSax_closecard(local->cardnr); 243 } 244 } 245 246 pcmcia_disable_device(link); 247} /* teles_cs_release */ 248 249static int teles_suspend(struct pcmcia_device *link) 250{ 251 local_info_t *dev = link->priv; 252 253 dev->busy = 1; 254 255 return 0; 256} 257 258static int teles_resume(struct pcmcia_device *link) 259{ 260 local_info_t *dev = link->priv; 261 262 dev->busy = 0; 263 264 return 0; 265} 266 267 268static struct pcmcia_device_id teles_ids[] = { 269 PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119), 270 PCMCIA_DEVICE_NULL, 271}; 272MODULE_DEVICE_TABLE(pcmcia, teles_ids); 273 274static struct pcmcia_driver teles_cs_driver = { 275 .owner = THIS_MODULE, 276 .drv = { 277 .name = "teles_cs", 278 }, 279 .probe = teles_probe, 280 .remove = __devexit_p(teles_detach), 281 .id_table = teles_ids, 282 .suspend = teles_suspend, 283 .resume = teles_resume, 284}; 285 286static int __init init_teles_cs(void) 287{ 288 return pcmcia_register_driver(&teles_cs_driver); 289} 290 291static void __exit exit_teles_cs(void) 292{ 293 pcmcia_unregister_driver(&teles_cs_driver); 294} 295 296module_init(init_teles_cs); 297module_exit(exit_teles_cs); 298