teles_cs.c 10.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
/*======================================================================

    A teles S0 PCMCIA client driver

    Based on skeleton by David Hinds, dhinds@allegro.stanford.edu
    Written by Christof Petig, christof.petig@wtal.de
    
    Also inspired by ELSA PCMCIA driver 
    by Klaus Lichtenwalder <Lichtenwalder@ACM.org>
    
    Extentions to new hisax_pcmcia by Karsten Keil

    minor changes to be compatible with kernel 2.4.x
    by Jan.Schubert@GMX.li

======================================================================*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/system.h>

#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include "hisax_cfg.h"

MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
MODULE_LICENSE("GPL");


/*====================================================================*/

/* Parameters that can be set with 'insmod' */

static int protocol = 2;        /* EURO-ISDN Default */
module_param(protocol, int, 0);

/*====================================================================*/

/*
   The event() function is this driver's Card Services event handler.
   It will be called by Card Services when an appropriate card status
   event is received.  The config() and release() entry points are
   used to configure or release a socket, in response to card insertion
   and ejection events.  They are invoked from the teles_cs event
   handler.
*/

60
static int teles_cs_config(struct pcmcia_device *link) __devinit ;
61
static void teles_cs_release(struct pcmcia_device *link);
L
Linus Torvalds 已提交
62 63 64 65 66 67 68

/*
   The attach() and detach() entry points are used to create and destroy
   "instances" of the driver, where each instance represents everything
   needed to manage one actual PCMCIA card.
*/

69
static void teles_detach(struct pcmcia_device *p_dev) __devexit ;
L
Linus Torvalds 已提交
70 71 72 73

/*
   A linked list of "instances" of the teles_cs device.  Each actual
   PCMCIA card corresponds to one device instance, and is described
74
   by one struct pcmcia_device structure (defined in ds.h).
L
Linus Torvalds 已提交
75 76

   You may not want to use a linked list for this -- for example, the
77
   memory card driver uses an array of struct pcmcia_device pointers, where minor
L
Linus Torvalds 已提交
78 79 80 81 82 83 84 85 86
   device numbers are used to derive the corresponding array index.
*/

/*
   A driver needs to provide a dev_node_t structure for each device
   on a card.  In some cases, there is only one device per card (for
   example, ethernet cards, modems).  In other cases, there may be
   many actual or logical devices (SCSI adapters, memory cards with
   multiple partitions).  The dev_node_t structures need to be kept
87
   in a linked list starting at the 'dev' field of a struct pcmcia_device
L
Linus Torvalds 已提交
88 89 90 91 92 93 94 95 96
   structure.  We allocate them in the card's private data structure,
   because they generally shouldn't be allocated dynamically.
   In this case, we also provide a flag to indicate if a device is
   "stopped" due to a power management event, or card ejection.  The
   device IO routines can use a flag like this to throttle IO to a
   card that is not ready to accept it.
*/

typedef struct local_info_t {
97
	struct pcmcia_device	*p_dev;
L
Linus Torvalds 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
    dev_node_t          node;
    int                 busy;
    int			cardnr;
} local_info_t;

/*======================================================================

    teles_attach() creates an "instance" of the driver, allocatingx
    local data structures for one device.  The device is registered
    with Card Services.

    The dev_link structure is initialized, but we don't actually
    configure the card at this point -- we wait until we receive a
    card insertion event.

======================================================================*/

115
static int __devinit teles_probe(struct pcmcia_device *link)
L
Linus Torvalds 已提交
116 117 118
{
    local_info_t *local;

119
    dev_dbg(&link->dev, "teles_attach()\n");
L
Linus Torvalds 已提交
120 121

    /* Allocate space for private device-specific data */
122
    local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
123
    if (!local) return -ENOMEM;
L
Linus Torvalds 已提交
124
    local->cardnr = -1;
125

126
    local->p_dev = link;
127
    link->priv = local;
L
Linus Torvalds 已提交
128 129

    /* Interrupt setup */
130
    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
L
Linus Torvalds 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    link->irq.Handler = NULL;

    /*
      General socket configuration defaults can go here.  In this
      client, we assume very little, and rely on the CIS for almost
      everything.  In most clients, many details (i.e., number, sizes,
      and attributes of IO windows) are fixed by the nature of the
      device, and can be hard-wired here.
    */
    link->io.NumPorts1 = 96;
    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
    link->io.IOAddrLines = 5;

    link->conf.Attributes = CONF_ENABLE_IRQ;
    link->conf.IntType = INT_MEMORY_AND_IO;

147
    return teles_cs_config(link);
L
Linus Torvalds 已提交
148 149 150 151 152 153 154 155 156 157 158
} /* teles_attach */

/*======================================================================

    This deletes a driver "instance".  The device is de-registered
    with Card Services.  If it has been released, all local data
    structures are freed.  Otherwise, the structures will be freed
    when the device is released.

======================================================================*/

159
static void __devexit teles_detach(struct pcmcia_device *link)
L
Linus Torvalds 已提交
160
{
161
	local_info_t *info = link->priv;
L
Linus Torvalds 已提交
162

163
	dev_dbg(&link->dev, "teles_detach(0x%p)\n", link);
L
Linus Torvalds 已提交
164

165 166
	info->busy = 1;
	teles_cs_release(link);
L
Linus Torvalds 已提交
167

168
	kfree(info);
L
Linus Torvalds 已提交
169 170 171 172 173 174 175 176 177 178
} /* teles_detach */

/*======================================================================

    teles_cs_config() is scheduled to run after a CARD_INSERTION event
    is received, to configure the PCMCIA socket, and to make the
    device available to the system.

======================================================================*/

179 180
static int teles_cs_configcheck(struct pcmcia_device *p_dev,
				cistpl_cftable_entry_t *cf,
181
				cistpl_cftable_entry_t *dflt,
182
				unsigned int vcc,
183
				void *priv_data)
L
Linus Torvalds 已提交
184
{
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
	int j;

	if ((cf->io.nwin > 0) && cf->io.win[0].base) {
		printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
		p_dev->io.BasePort1 = cf->io.win[0].base;
		if (!pcmcia_request_io(p_dev, &p_dev->io))
			return 0;
	} else {
		printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
		for (j = 0x2f0; j > 0x100; j -= 0x10) {
			p_dev->io.BasePort1 = j;
			if (!pcmcia_request_io(p_dev, &p_dev->io))
				return 0;
		}
	}
	return -ENODEV;
L
Linus Torvalds 已提交
201 202
}

203
static int __devinit teles_cs_config(struct pcmcia_device *link)
L
Linus Torvalds 已提交
204 205
{
    local_info_t *dev;
206
    int i;
L
Linus Torvalds 已提交
207 208
    IsdnCard_t icard;

209
    dev_dbg(&link->dev, "teles_config(0x%p)\n", link);
L
Linus Torvalds 已提交
210 211
    dev = link->priv;

212
    i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
213
    if (i != 0)
L
Linus Torvalds 已提交
214 215
	goto cs_failed;

216
    i = pcmcia_request_irq(link, &link->irq);
D
Dominik Brodowski 已提交
217
    if (i != 0) {
L
Linus Torvalds 已提交
218 219 220 221
        link->irq.AssignedIRQ = 0;
        goto cs_failed;
    }

222
    i = pcmcia_request_configuration(link, &link->conf);
223
    if (i != 0)
L
Linus Torvalds 已提交
224 225 226 227 228 229 230
      goto cs_failed;

    /* At this point, the dev_node_t structure(s) should be
       initialized and arranged in a linked list at link->dev. *//*  */
    sprintf(dev->node.dev_name, "teles");
    dev->node.major = dev->node.minor = 0x0;

231
    link->dev_node = &dev->node;
L
Linus Torvalds 已提交
232 233

    /* Finally, report what we've done */
234 235
    printk(KERN_INFO "%s: index 0x%02x:",
           dev->node.dev_name, link->conf.ConfigIndex);
L
Linus Torvalds 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
    if (link->conf.Attributes & CONF_ENABLE_IRQ)
        printk(", irq %d", link->irq.AssignedIRQ);
    if (link->io.NumPorts1)
        printk(", io 0x%04x-0x%04x", link->io.BasePort1,
               link->io.BasePort1+link->io.NumPorts1-1);
    if (link->io.NumPorts2)
        printk(" & 0x%04x-0x%04x", link->io.BasePort2,
               link->io.BasePort2+link->io.NumPorts2-1);
    printk("\n");

    icard.para[0] = link->irq.AssignedIRQ;
    icard.para[1] = link->io.BasePort1;
    icard.protocol = protocol;
    icard.typ = ISDN_CTYPE_TELESPCMCIA;
    
    i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
    if (i < 0) {
    	printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
    		i, link->io.BasePort1);
    	teles_cs_release(link);
256 257 258 259 260
	return -ENODEV;
    }

    ((local_info_t*)link->priv)->cardnr = i;
    return 0;
L
Linus Torvalds 已提交
261 262 263

cs_failed:
    teles_cs_release(link);
264
    return -ENODEV;
L
Linus Torvalds 已提交
265 266 267 268 269 270 271 272 273 274
} /* teles_cs_config */

/*======================================================================

    After a card is removed, teles_cs_release() will unregister the net
    device, and release the PCMCIA configuration.  If the device is
    still open, this will be postponed until it is closed.

======================================================================*/

275
static void teles_cs_release(struct pcmcia_device *link)
L
Linus Torvalds 已提交
276 277 278
{
    local_info_t *local = link->priv;

279
    dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link);
L
Linus Torvalds 已提交
280 281 282 283 284 285 286

    if (local) {
    	if (local->cardnr >= 0) {
    	    /* no unregister function with hisax */
	    HiSax_closecard(local->cardnr);
	}
    }
287

288
    pcmcia_disable_device(link);
L
Linus Torvalds 已提交
289 290
} /* teles_cs_release */

291
static int teles_suspend(struct pcmcia_device *link)
292 293 294 295 296 297 298 299
{
	local_info_t *dev = link->priv;

        dev->busy = 1;

	return 0;
}

300
static int teles_resume(struct pcmcia_device *link)
301 302 303 304 305 306 307 308
{
	local_info_t *dev = link->priv;

        dev->busy = 0;

	return 0;
}

L
Linus Torvalds 已提交
309

310 311 312 313 314 315
static struct pcmcia_device_id teles_ids[] = {
	PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
	PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, teles_ids);

L
Linus Torvalds 已提交
316 317 318 319 320
static struct pcmcia_driver teles_cs_driver = {
	.owner		= THIS_MODULE,
	.drv		= {
		.name	= "teles_cs",
	},
321
	.probe		= teles_probe,
322
	.remove		= __devexit_p(teles_detach),
323
	.id_table       = teles_ids,
324 325
	.suspend	= teles_suspend,
	.resume		= teles_resume,
L
Linus Torvalds 已提交
326 327 328 329 330 331 332 333 334 335 336 337 338 339
};

static int __init init_teles_cs(void)
{
	return pcmcia_register_driver(&teles_cs_driver);
}

static void __exit exit_teles_cs(void)
{
	pcmcia_unregister_driver(&teles_cs_driver);
}

module_init(init_teles_cs);
module_exit(exit_teles_cs);