11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * A PCMCIA client driver for AVM B1/M1/M2
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright 1999 by Carsten Paeth <calle@calle.de>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This software may be used and distributed according to the terms
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of the GNU General Public License, incorporated herein by reference.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ptrace.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serial.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/major.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/cistpl.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/ciscode.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/ds.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/cisreg.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/capi.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/b1lli.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/b1pcmcia.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*====================================================================*/
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Carsten Paeth");
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*====================================================================*/
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4015b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowskistatic int avmcs_config(struct pcmcia_device *link);
41fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowskistatic void avmcs_release(struct pcmcia_device *link);
42cc3b4866bee996c922e875b8c8efe9f0d8803aaeDominik Brodowskistatic void avmcs_detach(struct pcmcia_device *p_dev);
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4415b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowskistatic int avmcs_probe(struct pcmcia_device *p_dev)
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
46475be4d85a274d0961593db41cf85689db1d583cJoe Perches	/* General socket configuration */
47475be4d85a274d0961593db41cf85689db1d583cJoe Perches	p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
48475be4d85a274d0961593db41cf85689db1d583cJoe Perches	p_dev->config_index = 1;
49475be4d85a274d0961593db41cf85689db1d583cJoe Perches	p_dev->config_regs = PRESENT_OPTION;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51475be4d85a274d0961593db41cf85689db1d583cJoe Perches	return avmcs_config(p_dev);
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_attach */
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowskistatic void avmcs_detach(struct pcmcia_device *link)
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
57cc3b4866bee996c922e875b8c8efe9f0d8803aaeDominik Brodowski	avmcs_release(link);
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_detach */
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6000990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowskistatic int avmcs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6200990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowski	p_dev->resource[0]->end = 16;
6300990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowski	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
6400990e7ce0b0e596fe41d9c64d6933ea70084003Dominik Brodowski	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
655fcd4da0090828bd34a1956cb322a483c6bf163cDominik Brodowski
6690abdc3b973229bae98dd96649d9f7106cc177a4Dominik Brodowski	return pcmcia_request_io(p_dev);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6915b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowskistatic int avmcs_config(struct pcmcia_device *link)
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
71475be4d85a274d0961593db41cf85689db1d583cJoe Perches	int i = -1;
72475be4d85a274d0961593db41cf85689db1d583cJoe Perches	char devname[128];
73475be4d85a274d0961593db41cf85689db1d583cJoe Perches	int cardtype;
74475be4d85a274d0961593db41cf85689db1d583cJoe Perches	int (*addcard)(unsigned int port, unsigned irq);
75475be4d85a274d0961593db41cf85689db1d583cJoe Perches
76475be4d85a274d0961593db41cf85689db1d583cJoe Perches	devname[0] = 0;
77475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (link->prod_id[1])
78475be4d85a274d0961593db41cf85689db1d583cJoe Perches		strlcpy(devname, link->prod_id[1], sizeof(devname));
7950db3fdbbc98260fb538c1cc3f8cc597ba7bffe7Dominik Brodowski
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
81475be4d85a274d0961593db41cf85689db1d583cJoe Perches	 * find IO port
82475be4d85a274d0961593db41cf85689db1d583cJoe Perches	 */
83475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
84475be4d85a274d0961593db41cf85689db1d583cJoe Perches		return -ENODEV;
85475be4d85a274d0961593db41cf85689db1d583cJoe Perches
86475be4d85a274d0961593db41cf85689db1d583cJoe Perches	do {
87475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (!link->irq) {
88475be4d85a274d0961593db41cf85689db1d583cJoe Perches			/* undo */
89475be4d85a274d0961593db41cf85689db1d583cJoe Perches			pcmcia_disable_device(link);
90475be4d85a274d0961593db41cf85689db1d583cJoe Perches			break;
91475be4d85a274d0961593db41cf85689db1d583cJoe Perches		}
92475be4d85a274d0961593db41cf85689db1d583cJoe Perches
93475be4d85a274d0961593db41cf85689db1d583cJoe Perches		/*
94475be4d85a274d0961593db41cf85689db1d583cJoe Perches		 * configure the PCMCIA socket
95475be4d85a274d0961593db41cf85689db1d583cJoe Perches		 */
96475be4d85a274d0961593db41cf85689db1d583cJoe Perches		i = pcmcia_enable_device(link);
97475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (i != 0) {
98475be4d85a274d0961593db41cf85689db1d583cJoe Perches			pcmcia_disable_device(link);
99475be4d85a274d0961593db41cf85689db1d583cJoe Perches			break;
100475be4d85a274d0961593db41cf85689db1d583cJoe Perches		}
101475be4d85a274d0961593db41cf85689db1d583cJoe Perches
102475be4d85a274d0961593db41cf85689db1d583cJoe Perches	} while (0);
103475be4d85a274d0961593db41cf85689db1d583cJoe Perches
104475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (devname[0]) {
105475be4d85a274d0961593db41cf85689db1d583cJoe Perches		char *s = strrchr(devname, ' ');
106475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (!s)
107475be4d85a274d0961593db41cf85689db1d583cJoe Perches			s = devname;
108475be4d85a274d0961593db41cf85689db1d583cJoe Perches		else s++;
109475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (strcmp("M1", s) == 0) {
110475be4d85a274d0961593db41cf85689db1d583cJoe Perches			cardtype = AVM_CARDTYPE_M1;
111475be4d85a274d0961593db41cf85689db1d583cJoe Perches		} else if (strcmp("M2", s) == 0) {
112475be4d85a274d0961593db41cf85689db1d583cJoe Perches			cardtype = AVM_CARDTYPE_M2;
113475be4d85a274d0961593db41cf85689db1d583cJoe Perches		} else {
114475be4d85a274d0961593db41cf85689db1d583cJoe Perches			cardtype = AVM_CARDTYPE_B1;
115475be4d85a274d0961593db41cf85689db1d583cJoe Perches		}
116475be4d85a274d0961593db41cf85689db1d583cJoe Perches	} else
117475be4d85a274d0961593db41cf85689db1d583cJoe Perches		cardtype = AVM_CARDTYPE_B1;
118475be4d85a274d0961593db41cf85689db1d583cJoe Perches
119475be4d85a274d0961593db41cf85689db1d583cJoe Perches	/* If any step failed, release any partially configured state */
1204c89e88bfde6a3c179790e21004f24e09a058290Dominik Brodowski	if (i != 0) {
121475be4d85a274d0961593db41cf85689db1d583cJoe Perches		avmcs_release(link);
122475be4d85a274d0961593db41cf85689db1d583cJoe Perches		return -ENODEV;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
126475be4d85a274d0961593db41cf85689db1d583cJoe Perches	switch (cardtype) {
127475be4d85a274d0961593db41cf85689db1d583cJoe Perches	case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
128475be4d85a274d0961593db41cf85689db1d583cJoe Perches	case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
130475be4d85a274d0961593db41cf85689db1d583cJoe Perches	case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
131475be4d85a274d0961593db41cf85689db1d583cJoe Perches	}
132475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
133475be4d85a274d0961593db41cf85689db1d583cJoe Perches		dev_err(&link->dev,
134475be4d85a274d0961593db41cf85689db1d583cJoe Perches			"avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
135475be4d85a274d0961593db41cf85689db1d583cJoe Perches			(unsigned int) link->resource[0]->start, link->irq);
136475be4d85a274d0961593db41cf85689db1d583cJoe Perches		avmcs_release(link);
137475be4d85a274d0961593db41cf85689db1d583cJoe Perches		return -ENODEV;
138475be4d85a274d0961593db41cf85689db1d583cJoe Perches	}
139475be4d85a274d0961593db41cf85689db1d583cJoe Perches	return 0;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_config */
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
144fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowskistatic void avmcs_release(struct pcmcia_device *link)
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1469a017a910346afd88ec2e065989903bf211a7d37Dominik Brodowski	b1pcmcia_delcard(link->resource[0]->start, link->irq);
147fba395eee7d3f342ca739c20f5b3ee635d0420a0Dominik Brodowski	pcmcia_disable_device(link);
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_release */
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15125f8f54f6e178acfd503a95441b0ea05c525f751Joe Perchesstatic const struct pcmcia_device_id avmcs_ids[] = {
152a13bcf0d5abaf21a1eb7a988915ab97152f57f78Dominik Brodowski	PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
153a13bcf0d5abaf21a1eb7a988915ab97152f57f78Dominik Brodowski	PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
154a13bcf0d5abaf21a1eb7a988915ab97152f57f78Dominik Brodowski	PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
155a13bcf0d5abaf21a1eb7a988915ab97152f57f78Dominik Brodowski	PCMCIA_DEVICE_NULL
156a13bcf0d5abaf21a1eb7a988915ab97152f57f78Dominik Brodowski};
157a13bcf0d5abaf21a1eb7a988915ab97152f57f78Dominik BrodowskiMODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
158a13bcf0d5abaf21a1eb7a988915ab97152f57f78Dominik Brodowski
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pcmcia_driver avmcs_driver = {
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner	= THIS_MODULE,
1612e9b981a7c63ee8278df6823f8389d69dad1a499Dominik Brodowski	.name		= "avm_cs",
16215b99ac1729503db9e6dc642a50b9b6cb3bf51f9Dominik Brodowski	.probe = avmcs_probe,
163cc3b4866bee996c922e875b8c8efe9f0d8803aaeDominik Brodowski	.remove	= avmcs_detach,
164a13bcf0d5abaf21a1eb7a988915ab97152f57f78Dominik Brodowski	.id_table = avmcs_ids,
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init avmcs_init(void)
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return pcmcia_register_driver(&avmcs_driver);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit avmcs_exit(void)
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pcmcia_unregister_driver(&avmcs_driver);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(avmcs_init);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(avmcs_exit);
179