11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Support for common PCI multi-I/O cards (which is most of them)
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 2001  Tim Waugh <twaugh@redhat.com>
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or
71da177e4SLinus Torvalds  * modify it under the terms of the GNU General Public License
81da177e4SLinus Torvalds  * as published by the Free Software Foundation; either version
91da177e4SLinus Torvalds  * 2 of the License, or (at your option) any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * Multi-function PCI cards are supposed to present separate logical
131da177e4SLinus Torvalds  * devices on the bus.  A common thing to do seems to be to just use
141da177e4SLinus Torvalds  * one logical device with lots of base address registers for both
151da177e4SLinus Torvalds  * parallel ports and serial ports.  This driver is for dealing with
161da177e4SLinus Torvalds  * that.
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  */
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include <linux/types.h>
211da177e4SLinus Torvalds #include <linux/module.h>
221da177e4SLinus Torvalds #include <linux/init.h>
235a0e3ad6STejun Heo #include <linux/slab.h>
241da177e4SLinus Torvalds #include <linux/pci.h>
2551dcdfecSAlan Cox #include <linux/interrupt.h>
261da177e4SLinus Torvalds #include <linux/parport.h>
271da177e4SLinus Torvalds #include <linux/parport_pc.h>
281da177e4SLinus Torvalds #include <linux/8250_pci.h>
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds enum parport_pc_pci_cards {
311da177e4SLinus Torvalds 	titan_110l = 0,
321da177e4SLinus Torvalds 	titan_210l,
331da177e4SLinus Torvalds 	netmos_9xx5_combo,
3444e58a6aSMartin Schitter 	netmos_9855,
3550db9d8eSPhilippe De Muyter 	netmos_9855_2p,
367808edcdSNicos Gollan 	netmos_9900,
377808edcdSNicos Gollan 	netmos_9900_2p,
387808edcdSNicos Gollan 	netmos_99xx_1p,
391da177e4SLinus Torvalds 	avlab_1s1p,
401da177e4SLinus Torvalds 	avlab_1s2p,
411da177e4SLinus Torvalds 	avlab_2s1p,
421da177e4SLinus Torvalds 	siig_1s1p_10x,
431da177e4SLinus Torvalds 	siig_2s1p_10x,
441da177e4SLinus Torvalds 	siig_2p1s_20x,
451da177e4SLinus Torvalds 	siig_1s1p_20x,
461da177e4SLinus Torvalds 	siig_2s1p_20x,
47b9b24558SFrédéric Brière 	timedia_4078a,
48b9b24558SFrédéric Brière 	timedia_4079h,
49b9b24558SFrédéric Brière 	timedia_4085h,
50b9b24558SFrédéric Brière 	timedia_4088a,
51b9b24558SFrédéric Brière 	timedia_4089a,
52b9b24558SFrédéric Brière 	timedia_4095a,
53b9b24558SFrédéric Brière 	timedia_4096a,
54b9b24558SFrédéric Brière 	timedia_4078u,
55b9b24558SFrédéric Brière 	timedia_4079a,
56b9b24558SFrédéric Brière 	timedia_4085u,
57b9b24558SFrédéric Brière 	timedia_4079r,
58b9b24558SFrédéric Brière 	timedia_4079s,
59b9b24558SFrédéric Brière 	timedia_4079d,
60b9b24558SFrédéric Brière 	timedia_4079e,
61b9b24558SFrédéric Brière 	timedia_4079f,
62b9b24558SFrédéric Brière 	timedia_9079a,
63b9b24558SFrédéric Brière 	timedia_9079b,
64b9b24558SFrédéric Brière 	timedia_9079c,
65feb58142SEzequiel Garcia 	wch_ch353_1s1p,
666971c635SGuainluca Anzolin 	wch_ch353_2s1p,
672fdd8c8cSSergej Pupykin 	wch_ch382_2s1p,
68ec8e3893SAndy Shevchenko 	brainboxes_5s1p,
69abd7bacaSStephen Chivers 	sunix_2s1p,
701da177e4SLinus Torvalds };
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds /* each element directly indexed from enum list, above */
731da177e4SLinus Torvalds struct parport_pc_pci {
741da177e4SLinus Torvalds 	int numports;
751da177e4SLinus Torvalds 	struct { /* BAR (base address registers) numbers in the config
761da177e4SLinus Torvalds                     space header */
771da177e4SLinus Torvalds 		int lo;
781da177e4SLinus Torvalds 		int hi; /* -1 if not there, >6 for offset-method (max
791da177e4SLinus Torvalds                            BAR is 6) */
801da177e4SLinus Torvalds 	} addr[4];
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	/* If set, this is called immediately after pci_enable_device.
831da177e4SLinus Torvalds 	 * If it returns non-zero, no probing will take place and the
841da177e4SLinus Torvalds 	 * ports will not be used. */
851da177e4SLinus Torvalds 	int (*preinit_hook) (struct pci_dev *pdev, struct parport_pc_pci *card,
861da177e4SLinus Torvalds 				int autoirq, int autodma);
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	/* If set, this is called after probing for ports.  If 'failed'
891da177e4SLinus Torvalds 	 * is non-zero we couldn't use any of the ports. */
901da177e4SLinus Torvalds 	void (*postinit_hook) (struct pci_dev *pdev,
911da177e4SLinus Torvalds 				struct parport_pc_pci *card, int failed);
921da177e4SLinus Torvalds };
931da177e4SLinus Torvalds 
94312facafSGreg Kroah-Hartman static int netmos_parallel_init(struct pci_dev *dev, struct parport_pc_pci *par,
95312facafSGreg Kroah-Hartman 				int autoirq, int autodma)
961da177e4SLinus Torvalds {
973abdbf90SJiri Slaby 	/* the rule described below doesn't hold for this device */
983abdbf90SJiri Slaby 	if (dev->device == PCI_DEVICE_ID_NETMOS_9835 &&
993abdbf90SJiri Slaby 			dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
1003abdbf90SJiri Slaby 			dev->subsystem_device == 0x0299)
1013abdbf90SJiri Slaby 		return -ENODEV;
1027808edcdSNicos Gollan 
1037808edcdSNicos Gollan 	if (dev->device == PCI_DEVICE_ID_NETMOS_9912) {
1047808edcdSNicos Gollan 		par->numports = 1;
1057808edcdSNicos Gollan 	} else {
1061da177e4SLinus Torvalds 		/*
1071da177e4SLinus Torvalds 		 * Netmos uses the subdevice ID to indicate the number of parallel
1081da177e4SLinus Torvalds 		 * and serial ports.  The form is 0x00PS, where <P> is the number of
1091da177e4SLinus Torvalds 		 * parallel ports and <S> is the number of serial ports.
1101da177e4SLinus Torvalds 		 */
11150db9d8eSPhilippe De Muyter 		par->numports = (dev->subsystem_device & 0xf0) >> 4;
11250db9d8eSPhilippe De Muyter 		if (par->numports > ARRAY_SIZE(par->addr))
11350db9d8eSPhilippe De Muyter 			par->numports = ARRAY_SIZE(par->addr);
1147808edcdSNicos Gollan 	}
1157808edcdSNicos Gollan 
1161da177e4SLinus Torvalds 	return 0;
1171da177e4SLinus Torvalds }
1181da177e4SLinus Torvalds 
119312facafSGreg Kroah-Hartman static struct parport_pc_pci cards[] = {
1201da177e4SLinus Torvalds 	/* titan_110l */		{ 1, { { 3, -1 }, } },
1211da177e4SLinus Torvalds 	/* titan_210l */		{ 1, { { 3, -1 }, } },
1221da177e4SLinus Torvalds 	/* netmos_9xx5_combo */		{ 1, { { 2, -1 }, }, netmos_parallel_init },
12350db9d8eSPhilippe De Muyter 	/* netmos_9855 */		{ 1, { { 0, -1 }, }, netmos_parallel_init },
12450db9d8eSPhilippe De Muyter 	/* netmos_9855_2p */		{ 2, { { 0, -1 }, { 2, -1 }, } },
1257808edcdSNicos Gollan 	/* netmos_9900 */		{1, { { 3, 4 }, }, netmos_parallel_init },
1267808edcdSNicos Gollan 	/* netmos_9900_2p */		{2, { { 0, 1 }, { 3, 4 }, } },
1277808edcdSNicos Gollan 	/* netmos_99xx_1p */		{1, { { 0, 1 }, } },
1281da177e4SLinus Torvalds 	/* avlab_1s1p     */		{ 1, { { 1, 2}, } },
1291da177e4SLinus Torvalds 	/* avlab_1s2p     */		{ 2, { { 1, 2}, { 3, 4 },} },
1301da177e4SLinus Torvalds 	/* avlab_2s1p     */		{ 1, { { 2, 3}, } },
1311da177e4SLinus Torvalds 	/* siig_1s1p_10x */		{ 1, { { 3, 4 }, } },
1321da177e4SLinus Torvalds 	/* siig_2s1p_10x */		{ 1, { { 4, 5 }, } },
1331da177e4SLinus Torvalds 	/* siig_2p1s_20x */		{ 2, { { 1, 2 }, { 3, 4 }, } },
1341da177e4SLinus Torvalds 	/* siig_1s1p_20x */		{ 1, { { 1, 2 }, } },
1351da177e4SLinus Torvalds 	/* siig_2s1p_20x */		{ 1, { { 2, 3 }, } },
136b9b24558SFrédéric Brière 	/* timedia_4078a */		{ 1, { { 2, -1 }, } },
137b9b24558SFrédéric Brière 	/* timedia_4079h */             { 1, { { 2, 3 }, } },
138b9b24558SFrédéric Brière 	/* timedia_4085h */             { 2, { { 2, -1 }, { 4, -1 }, } },
139b9b24558SFrédéric Brière 	/* timedia_4088a */             { 2, { { 2, 3 }, { 4, 5 }, } },
140b9b24558SFrédéric Brière 	/* timedia_4089a */             { 2, { { 2, 3 }, { 4, 5 }, } },
141b9b24558SFrédéric Brière 	/* timedia_4095a */             { 2, { { 2, 3 }, { 4, 5 }, } },
142b9b24558SFrédéric Brière 	/* timedia_4096a */             { 2, { { 2, 3 }, { 4, 5 }, } },
143b9b24558SFrédéric Brière 	/* timedia_4078u */             { 1, { { 2, -1 }, } },
144b9b24558SFrédéric Brière 	/* timedia_4079a */             { 1, { { 2, 3 }, } },
145b9b24558SFrédéric Brière 	/* timedia_4085u */             { 2, { { 2, -1 }, { 4, -1 }, } },
146b9b24558SFrédéric Brière 	/* timedia_4079r */             { 1, { { 2, 3 }, } },
147b9b24558SFrédéric Brière 	/* timedia_4079s */             { 1, { { 2, 3 }, } },
148b9b24558SFrédéric Brière 	/* timedia_4079d */             { 1, { { 2, 3 }, } },
149b9b24558SFrédéric Brière 	/* timedia_4079e */             { 1, { { 2, 3 }, } },
150b9b24558SFrédéric Brière 	/* timedia_4079f */             { 1, { { 2, 3 }, } },
151b9b24558SFrédéric Brière 	/* timedia_9079a */             { 1, { { 2, 3 }, } },
152b9b24558SFrédéric Brière 	/* timedia_9079b */             { 1, { { 2, 3 }, } },
153b9b24558SFrédéric Brière 	/* timedia_9079c */             { 1, { { 2, 3 }, } },
154feb58142SEzequiel Garcia 	/* wch_ch353_1s1p*/             { 1, { { 1, -1}, } },
1556971c635SGuainluca Anzolin 	/* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
1562fdd8c8cSSergej Pupykin 	/* wch_ch382_2s1p*/             { 1, { { 2, -1}, } },
157ec8e3893SAndy Shevchenko 	/* brainboxes_5s1p */           { 1, { { 3, -1 }, } },
158abd7bacaSStephen Chivers 	/* sunix_2s1p */                { 1, { { 3, -1 }, } },
1591da177e4SLinus Torvalds };
1601da177e4SLinus Torvalds 
161abd7bacaSStephen Chivers #define PCI_VENDOR_ID_SUNIX		0x1fd4
162abd7bacaSStephen Chivers #define PCI_DEVICE_ID_SUNIX_1999	0x1999
163abd7bacaSStephen Chivers 
1641da177e4SLinus Torvalds static struct pci_device_id parport_serial_pci_tbl[] = {
1651da177e4SLinus Torvalds 	/* PCI cards */
1661da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,
1671da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l },
1681da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L,
1691da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l },
1701da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735,
1711da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
1721da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745,
1731da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
1741da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
1751da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
1761da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845,
1771da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
1781da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
17950db9d8eSPhilippe De Muyter 	  0x1000, 0x0020, 0, 0, netmos_9855_2p },
18050db9d8eSPhilippe De Muyter 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
18150db9d8eSPhilippe De Muyter 	  0x1000, 0x0022, 0, 0, netmos_9855_2p },
18250db9d8eSPhilippe De Muyter 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
18344e58a6aSMartin Schitter 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 },
1847808edcdSNicos Gollan 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
1857808edcdSNicos Gollan 	  0xA000, 0x3011, 0, 0, netmos_9900 },
1867808edcdSNicos Gollan 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
1877808edcdSNicos Gollan 	  0xA000, 0x3012, 0, 0, netmos_9900 },
1887808edcdSNicos Gollan 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
1897808edcdSNicos Gollan 	  0xA000, 0x3020, 0, 0, netmos_9900_2p },
1907808edcdSNicos Gollan 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
1917808edcdSNicos Gollan 	  0xA000, 0x2000, 0, 0, netmos_99xx_1p },
1921da177e4SLinus Torvalds 	/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
19391bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2110,
19491bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
19591bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2111,
19691bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
19791bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2112,
19891bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
19991bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2140,
20091bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
20191bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2141,
20291bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
20391bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2142,
20491bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
20591bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2160,
20691bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
20791bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2161,
20891bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
20991bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2162,
21091bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
2111da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
2121da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
2131da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
2141da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
2151da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
2161da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
2171da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
2181da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
2191da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
2201da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
2211da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
2221da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
2231da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
2241da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
2251da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
2261da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
2271da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
2281da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
2291da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
2301da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
2311da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
2321da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
2331da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
2341da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
2351da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
2361da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
2371da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
2381da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
2391da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
2401da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
241b9b24558SFrédéric Brière 	/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
242b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a },
243b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h },
244b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h },
245b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a },
246b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a },
247b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a },
248b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a },
249b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u },
250b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a },
251b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u },
252b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r },
253b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s },
254b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d },
255b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e },
256b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f },
257b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
258b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
259b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
260abd7bacaSStephen Chivers 
2616971c635SGuainluca Anzolin 	/* WCH CARDS */
262feb58142SEzequiel Garcia 	{ 0x4348, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p},
2636971c635SGuainluca Anzolin 	{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
2642fdd8c8cSSergej Pupykin 	{ 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p},
265abd7bacaSStephen Chivers 
266ec8e3893SAndy Shevchenko 	/* BrainBoxes PX272/PX306 MIO card */
267ec8e3893SAndy Shevchenko 	{ PCI_VENDOR_ID_INTASHIELD, 0x4100,
268ec8e3893SAndy Shevchenko 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p },
269ec8e3893SAndy Shevchenko 
270abd7bacaSStephen Chivers 	/*
271abd7bacaSStephen Chivers 	 * More SUNIX variations. At least one of these has part number
272abd7bacaSStephen Chivers 	 * '5079A but subdevice 0x102. That board reports 0x0708 as
273abd7bacaSStephen Chivers 	 * its PCI Class.
274abd7bacaSStephen Chivers 	 */
275abd7bacaSStephen Chivers 	{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
276abd7bacaSStephen Chivers 	  0x0102, 0, 0, sunix_2s1p },
277abd7bacaSStephen Chivers 
2781da177e4SLinus Torvalds 	{ 0, } /* terminate list */
2791da177e4SLinus Torvalds };
2801da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds /*
28305caac58SRussell King  * This table describes the serial "geometry" of these boards.  Any
28405caac58SRussell King  * quirks for these can be found in drivers/serial/8250_pci.c
28505caac58SRussell King  *
28605caac58SRussell King  * Cards not tested are marked n/t
28705caac58SRussell King  * If you have one of these cards and it works for you, please tell me..
2881da177e4SLinus Torvalds  */
289312facafSGreg Kroah-Hartman static struct pciserial_board pci_parport_serial_boards[] = {
29005caac58SRussell King 	[titan_110l] = {
29105caac58SRussell King 		.flags		= FL_BASE1 | FL_BASE_BARS,
29205caac58SRussell King 		.num_ports	= 1,
29305caac58SRussell King 		.base_baud	= 921600,
29405caac58SRussell King 		.uart_offset	= 8,
29505caac58SRussell King 	},
29605caac58SRussell King 	[titan_210l] = {
29705caac58SRussell King 		.flags		= FL_BASE1 | FL_BASE_BARS,
29805caac58SRussell King 		.num_ports	= 2,
29905caac58SRussell King 		.base_baud	= 921600,
30005caac58SRussell King 		.uart_offset	= 8,
30105caac58SRussell King 	},
30205caac58SRussell King 	[netmos_9xx5_combo] = {
30305caac58SRussell King 		.flags		= FL_BASE0 | FL_BASE_BARS,
30405caac58SRussell King 		.num_ports	= 1,
30505caac58SRussell King 		.base_baud	= 115200,
30605caac58SRussell King 		.uart_offset	= 8,
30705caac58SRussell King 	},
30805caac58SRussell King 	[netmos_9855] = {
30950db9d8eSPhilippe De Muyter 		.flags		= FL_BASE2 | FL_BASE_BARS,
31050db9d8eSPhilippe De Muyter 		.num_ports	= 1,
31150db9d8eSPhilippe De Muyter 		.base_baud	= 115200,
31250db9d8eSPhilippe De Muyter 		.uart_offset	= 8,
31350db9d8eSPhilippe De Muyter 	},
31450db9d8eSPhilippe De Muyter 	[netmos_9855_2p] = {
315c01106e5SChristian Pellegrin 		.flags		= FL_BASE4 | FL_BASE_BARS,
31605caac58SRussell King 		.num_ports	= 1,
31705caac58SRussell King 		.base_baud	= 115200,
31805caac58SRussell King 		.uart_offset	= 8,
31905caac58SRussell King 	},
3207808edcdSNicos Gollan 	[netmos_9900] = { /* n/t */
3217808edcdSNicos Gollan 		.flags		= FL_BASE0 | FL_BASE_BARS,
3227808edcdSNicos Gollan 		.num_ports	= 1,
3237808edcdSNicos Gollan 		.base_baud	= 115200,
3247808edcdSNicos Gollan 		.uart_offset	= 8,
3257808edcdSNicos Gollan 	},
3267808edcdSNicos Gollan 	[netmos_9900_2p] = { /* parallel only */ /* n/t */
3277808edcdSNicos Gollan 		.flags		= FL_BASE0,
3287808edcdSNicos Gollan 		.num_ports	= 0,
3297808edcdSNicos Gollan 		.base_baud	= 115200,
3307808edcdSNicos Gollan 		.uart_offset	= 8,
3317808edcdSNicos Gollan 	},
3327808edcdSNicos Gollan 	[netmos_99xx_1p] = { /* parallel only */ /* n/t */
3337808edcdSNicos Gollan 		.flags		= FL_BASE0,
3347808edcdSNicos Gollan 		.num_ports	= 0,
3357808edcdSNicos Gollan 		.base_baud	= 115200,
3367808edcdSNicos Gollan 		.uart_offset	= 8,
3377808edcdSNicos Gollan 	},
33805caac58SRussell King 	[avlab_1s1p] = { /* n/t */
33905caac58SRussell King 		.flags		= FL_BASE0 | FL_BASE_BARS,
34005caac58SRussell King 		.num_ports	= 1,
34105caac58SRussell King 		.base_baud	= 115200,
34205caac58SRussell King 		.uart_offset	= 8,
34305caac58SRussell King 	},
34405caac58SRussell King 	[avlab_1s2p] = { /* n/t */
34505caac58SRussell King 		.flags		= FL_BASE0 | FL_BASE_BARS,
34605caac58SRussell King 		.num_ports	= 1,
34705caac58SRussell King 		.base_baud	= 115200,
34805caac58SRussell King 		.uart_offset	= 8,
34905caac58SRussell King 	},
35005caac58SRussell King 	[avlab_2s1p] = { /* n/t */
35105caac58SRussell King 		.flags		= FL_BASE0 | FL_BASE_BARS,
35205caac58SRussell King 		.num_ports	= 2,
35305caac58SRussell King 		.base_baud	= 115200,
35405caac58SRussell King 		.uart_offset	= 8,
35505caac58SRussell King 	},
35605caac58SRussell King 	[siig_1s1p_10x] = {
35705caac58SRussell King 		.flags		= FL_BASE2,
35805caac58SRussell King 		.num_ports	= 1,
35905caac58SRussell King 		.base_baud	= 460800,
36005caac58SRussell King 		.uart_offset	= 8,
36105caac58SRussell King 	},
36205caac58SRussell King 	[siig_2s1p_10x] = {
36305caac58SRussell King 		.flags		= FL_BASE2,
36405caac58SRussell King 		.num_ports	= 1,
36505caac58SRussell King 		.base_baud	= 921600,
36605caac58SRussell King 		.uart_offset	= 8,
36705caac58SRussell King 	},
36805caac58SRussell King 	[siig_2p1s_20x] = {
36905caac58SRussell King 		.flags		= FL_BASE0,
37005caac58SRussell King 		.num_ports	= 1,
37105caac58SRussell King 		.base_baud	= 921600,
37205caac58SRussell King 		.uart_offset	= 8,
37305caac58SRussell King 	},
37405caac58SRussell King 	[siig_1s1p_20x] = {
37505caac58SRussell King 		.flags		= FL_BASE0,
37605caac58SRussell King 		.num_ports	= 1,
37705caac58SRussell King 		.base_baud	= 921600,
37805caac58SRussell King 		.uart_offset	= 8,
37905caac58SRussell King 	},
38005caac58SRussell King 	[siig_2s1p_20x] = {
38105caac58SRussell King 		.flags		= FL_BASE0,
38205caac58SRussell King 		.num_ports	= 1,
38305caac58SRussell King 		.base_baud	= 921600,
38405caac58SRussell King 		.uart_offset	= 8,
38505caac58SRussell King 	},
386b9b24558SFrédéric Brière 	[timedia_4078a] = {
387b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
388b9b24558SFrédéric Brière 		.num_ports	= 1,
389b9b24558SFrédéric Brière 		.base_baud	= 921600,
390b9b24558SFrédéric Brière 		.uart_offset	= 8,
391b9b24558SFrédéric Brière 	},
392b9b24558SFrédéric Brière 	[timedia_4079h] = {
393b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
394b9b24558SFrédéric Brière 		.num_ports	= 1,
395b9b24558SFrédéric Brière 		.base_baud	= 921600,
396b9b24558SFrédéric Brière 		.uart_offset	= 8,
397b9b24558SFrédéric Brière 	},
398b9b24558SFrédéric Brière 	[timedia_4085h] = {
399b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
400b9b24558SFrédéric Brière 		.num_ports	= 1,
401b9b24558SFrédéric Brière 		.base_baud	= 921600,
402b9b24558SFrédéric Brière 		.uart_offset	= 8,
403b9b24558SFrédéric Brière 	},
404b9b24558SFrédéric Brière 	[timedia_4088a] = {
405b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
406b9b24558SFrédéric Brière 		.num_ports	= 1,
407b9b24558SFrédéric Brière 		.base_baud	= 921600,
408b9b24558SFrédéric Brière 		.uart_offset	= 8,
409b9b24558SFrédéric Brière 	},
410b9b24558SFrédéric Brière 	[timedia_4089a] = {
411b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
412b9b24558SFrédéric Brière 		.num_ports	= 1,
413b9b24558SFrédéric Brière 		.base_baud	= 921600,
414b9b24558SFrédéric Brière 		.uart_offset	= 8,
415b9b24558SFrédéric Brière 	},
416b9b24558SFrédéric Brière 	[timedia_4095a] = {
417b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
418b9b24558SFrédéric Brière 		.num_ports	= 1,
419b9b24558SFrédéric Brière 		.base_baud	= 921600,
420b9b24558SFrédéric Brière 		.uart_offset	= 8,
421b9b24558SFrédéric Brière 	},
422b9b24558SFrédéric Brière 	[timedia_4096a] = {
423b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
424b9b24558SFrédéric Brière 		.num_ports	= 1,
425b9b24558SFrédéric Brière 		.base_baud	= 921600,
426b9b24558SFrédéric Brière 		.uart_offset	= 8,
427b9b24558SFrédéric Brière 	},
428b9b24558SFrédéric Brière 	[timedia_4078u] = {
429b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
430b9b24558SFrédéric Brière 		.num_ports	= 1,
431b9b24558SFrédéric Brière 		.base_baud	= 921600,
432b9b24558SFrédéric Brière 		.uart_offset	= 8,
433b9b24558SFrédéric Brière 	},
434b9b24558SFrédéric Brière 	[timedia_4079a] = {
435b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
436b9b24558SFrédéric Brière 		.num_ports	= 1,
437b9b24558SFrédéric Brière 		.base_baud	= 921600,
438b9b24558SFrédéric Brière 		.uart_offset	= 8,
439b9b24558SFrédéric Brière 	},
440b9b24558SFrédéric Brière 	[timedia_4085u] = {
441b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
442b9b24558SFrédéric Brière 		.num_ports	= 1,
443b9b24558SFrédéric Brière 		.base_baud	= 921600,
444b9b24558SFrédéric Brière 		.uart_offset	= 8,
445b9b24558SFrédéric Brière 	},
446b9b24558SFrédéric Brière 	[timedia_4079r] = {
447b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
448b9b24558SFrédéric Brière 		.num_ports	= 1,
449b9b24558SFrédéric Brière 		.base_baud	= 921600,
450b9b24558SFrédéric Brière 		.uart_offset	= 8,
451b9b24558SFrédéric Brière 	},
452b9b24558SFrédéric Brière 	[timedia_4079s] = {
453b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
454b9b24558SFrédéric Brière 		.num_ports	= 1,
455b9b24558SFrédéric Brière 		.base_baud	= 921600,
456b9b24558SFrédéric Brière 		.uart_offset	= 8,
457b9b24558SFrédéric Brière 	},
458b9b24558SFrédéric Brière 	[timedia_4079d] = {
459b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
460b9b24558SFrédéric Brière 		.num_ports	= 1,
461b9b24558SFrédéric Brière 		.base_baud	= 921600,
462b9b24558SFrédéric Brière 		.uart_offset	= 8,
463b9b24558SFrédéric Brière 	},
464b9b24558SFrédéric Brière 	[timedia_4079e] = {
465b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
466b9b24558SFrédéric Brière 		.num_ports	= 1,
467b9b24558SFrédéric Brière 		.base_baud	= 921600,
468b9b24558SFrédéric Brière 		.uart_offset	= 8,
469b9b24558SFrédéric Brière 	},
470b9b24558SFrédéric Brière 	[timedia_4079f] = {
471b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
472b9b24558SFrédéric Brière 		.num_ports	= 1,
473b9b24558SFrédéric Brière 		.base_baud	= 921600,
474b9b24558SFrédéric Brière 		.uart_offset	= 8,
475b9b24558SFrédéric Brière 	},
476b9b24558SFrédéric Brière 	[timedia_9079a] = {
477b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
478b9b24558SFrédéric Brière 		.num_ports	= 1,
479b9b24558SFrédéric Brière 		.base_baud	= 921600,
480b9b24558SFrédéric Brière 		.uart_offset	= 8,
481b9b24558SFrédéric Brière 	},
482b9b24558SFrédéric Brière 	[timedia_9079b] = {
483b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
484b9b24558SFrédéric Brière 		.num_ports	= 1,
485b9b24558SFrédéric Brière 		.base_baud	= 921600,
486b9b24558SFrédéric Brière 		.uart_offset	= 8,
487b9b24558SFrédéric Brière 	},
488b9b24558SFrédéric Brière 	[timedia_9079c] = {
489b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
490b9b24558SFrédéric Brière 		.num_ports	= 1,
491b9b24558SFrédéric Brière 		.base_baud	= 921600,
492b9b24558SFrédéric Brière 		.uart_offset	= 8,
493b9b24558SFrédéric Brière 	},
494feb58142SEzequiel Garcia 	[wch_ch353_1s1p] = {
495feb58142SEzequiel Garcia 		.flags          = FL_BASE0|FL_BASE_BARS,
496feb58142SEzequiel Garcia 		.num_ports      = 1,
497feb58142SEzequiel Garcia 		.base_baud      = 115200,
498feb58142SEzequiel Garcia 		.uart_offset    = 8,
499feb58142SEzequiel Garcia 	},
5006971c635SGuainluca Anzolin 	[wch_ch353_2s1p] = {
5016971c635SGuainluca Anzolin 		.flags          = FL_BASE0|FL_BASE_BARS,
5026971c635SGuainluca Anzolin 		.num_ports      = 2,
5036971c635SGuainluca Anzolin 		.base_baud      = 115200,
5046971c635SGuainluca Anzolin 		.uart_offset    = 8,
5056971c635SGuainluca Anzolin 	},
5062fdd8c8cSSergej Pupykin 	[wch_ch382_2s1p] = {
5072fdd8c8cSSergej Pupykin 		.flags          = FL_BASE0,
5082fdd8c8cSSergej Pupykin 		.num_ports      = 2,
5092fdd8c8cSSergej Pupykin 		.base_baud      = 115200,
5102fdd8c8cSSergej Pupykin 		.uart_offset    = 8,
5112fdd8c8cSSergej Pupykin 		.first_offset   = 0xC0,
5122fdd8c8cSSergej Pupykin 	},
513ec8e3893SAndy Shevchenko 	[brainboxes_5s1p] = {
514ec8e3893SAndy Shevchenko 		.flags		= FL_BASE2,
515ec8e3893SAndy Shevchenko 		.num_ports	= 5,
516ec8e3893SAndy Shevchenko 		.base_baud	= 921600,
517ec8e3893SAndy Shevchenko 		.uart_offset	= 8,
518ec8e3893SAndy Shevchenko 	},
519abd7bacaSStephen Chivers 	[sunix_2s1p] = {
520abd7bacaSStephen Chivers 		.flags		= FL_BASE0|FL_BASE_BARS,
521abd7bacaSStephen Chivers 		.num_ports	= 2,
522abd7bacaSStephen Chivers 		.base_baud	= 921600,
523abd7bacaSStephen Chivers 		.uart_offset	= 8,
524abd7bacaSStephen Chivers 	},
5251da177e4SLinus Torvalds };
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds struct parport_serial_private {
52805caac58SRussell King 	struct serial_private	*serial;
5291da177e4SLinus Torvalds 	int num_par;
5301da177e4SLinus Torvalds 	struct parport *port[PARPORT_MAX];
5311da177e4SLinus Torvalds 	struct parport_pc_pci par;
5321da177e4SLinus Torvalds };
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds /* Register the serial port(s) of a PCI card. */
535312facafSGreg Kroah-Hartman static int serial_register(struct pci_dev *dev, const struct pci_device_id *id)
5361da177e4SLinus Torvalds {
5371da177e4SLinus Torvalds 	struct parport_serial_private *priv = pci_get_drvdata (dev);
53805caac58SRussell King 	struct pciserial_board *board;
53905caac58SRussell King 	struct serial_private *serial;
5401da177e4SLinus Torvalds 
54105caac58SRussell King 	board = &pci_parport_serial_boards[id->driver_data];
5427808edcdSNicos Gollan 
5437808edcdSNicos Gollan 	if (board->num_ports == 0)
5447808edcdSNicos Gollan 		return 0;
5457808edcdSNicos Gollan 
54605caac58SRussell King 	serial = pciserial_init_ports(dev, board);
5471da177e4SLinus Torvalds 
54805caac58SRussell King 	if (IS_ERR(serial))
54905caac58SRussell King 		return PTR_ERR(serial);
5501da177e4SLinus Torvalds 
55105caac58SRussell King 	priv->serial = serial;
55205caac58SRussell King 	return 0;
5531da177e4SLinus Torvalds }
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds /* Register the parallel port(s) of a PCI card. */
556312facafSGreg Kroah-Hartman static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
5571da177e4SLinus Torvalds {
5581da177e4SLinus Torvalds 	struct parport_pc_pci *card;
5591da177e4SLinus Torvalds 	struct parport_serial_private *priv = pci_get_drvdata (dev);
5607a171cdcSRussell King 	int n, success = 0;
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds 	priv->par = cards[id->driver_data];
5631da177e4SLinus Torvalds 	card = &priv->par;
5641da177e4SLinus Torvalds 	if (card->preinit_hook &&
5651da177e4SLinus Torvalds 	    card->preinit_hook (dev, card, PARPORT_IRQ_NONE, PARPORT_DMA_NONE))
5661da177e4SLinus Torvalds 		return -ENODEV;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	for (n = 0; n < card->numports; n++) {
5691da177e4SLinus Torvalds 		struct parport *port;
5701da177e4SLinus Torvalds 		int lo = card->addr[n].lo;
5711da177e4SLinus Torvalds 		int hi = card->addr[n].hi;
5721da177e4SLinus Torvalds 		unsigned long io_lo, io_hi;
57351dcdfecSAlan Cox 		int irq;
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds 		if (priv->num_par == ARRAY_SIZE (priv->port)) {
5761da177e4SLinus Torvalds 			printk (KERN_WARNING
577f4f64e9dSAndrew Morton 				"parport_serial: %s: only %zu parallel ports "
5781da177e4SLinus Torvalds 				"supported (%d reported)\n", pci_name (dev),
5791da177e4SLinus Torvalds 				ARRAY_SIZE(priv->port), card->numports);
5801da177e4SLinus Torvalds 			break;
5811da177e4SLinus Torvalds 		}
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds 		io_lo = pci_resource_start (dev, lo);
5841da177e4SLinus Torvalds 		io_hi = 0;
5851da177e4SLinus Torvalds 		if ((hi >= 0) && (hi <= 6))
5861da177e4SLinus Torvalds 			io_hi = pci_resource_start (dev, hi);
5871da177e4SLinus Torvalds 		else if (hi > 6)
5881da177e4SLinus Torvalds 			io_lo += hi; /* Reinterpret the meaning of
5891da177e4SLinus Torvalds                                         "hi" as an offset (see SYBA
5901da177e4SLinus Torvalds                                         def.) */
5911da177e4SLinus Torvalds 		/* TODO: test if sharing interrupts works */
59251dcdfecSAlan Cox 		irq = dev->irq;
59351dcdfecSAlan Cox 		if (irq == IRQ_NONE) {
59451dcdfecSAlan Cox 			dev_dbg(&dev->dev,
59551dcdfecSAlan Cox 			"PCI parallel port detected: I/O at %#lx(%#lx)\n",
59651dcdfecSAlan Cox 				io_lo, io_hi);
59751dcdfecSAlan Cox 			irq = PARPORT_IRQ_NONE;
59851dcdfecSAlan Cox 		} else {
59951dcdfecSAlan Cox 			dev_dbg(&dev->dev,
60051dcdfecSAlan Cox 		"PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
60151dcdfecSAlan Cox 				io_lo, io_hi, irq);
60251dcdfecSAlan Cox 		}
60351dcdfecSAlan Cox 		port = parport_pc_probe_port (io_lo, io_hi, irq,
60451dcdfecSAlan Cox 			      PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED);
6051da177e4SLinus Torvalds 		if (port) {
6061da177e4SLinus Torvalds 			priv->port[priv->num_par++] = port;
6071da177e4SLinus Torvalds 			success = 1;
6081da177e4SLinus Torvalds 		}
6091da177e4SLinus Torvalds 	}
6101da177e4SLinus Torvalds 
6111da177e4SLinus Torvalds 	if (card->postinit_hook)
6121da177e4SLinus Torvalds 		card->postinit_hook (dev, card, !success);
6131da177e4SLinus Torvalds 
6147a171cdcSRussell King 	return 0;
6151da177e4SLinus Torvalds }
6161da177e4SLinus Torvalds 
617312facafSGreg Kroah-Hartman static int parport_serial_pci_probe(struct pci_dev *dev,
6181da177e4SLinus Torvalds 				    const struct pci_device_id *id)
6191da177e4SLinus Torvalds {
6201da177e4SLinus Torvalds 	struct parport_serial_private *priv;
6211da177e4SLinus Torvalds 	int err;
6221da177e4SLinus Torvalds 
623dd00cc48SYoann Padioleau 	priv = kzalloc (sizeof *priv, GFP_KERNEL);
6241da177e4SLinus Torvalds 	if (!priv)
6251da177e4SLinus Torvalds 		return -ENOMEM;
6261da177e4SLinus Torvalds 	pci_set_drvdata (dev, priv);
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	err = pci_enable_device (dev);
6291da177e4SLinus Torvalds 	if (err) {
6301da177e4SLinus Torvalds 		kfree (priv);
6311da177e4SLinus Torvalds 		return err;
6321da177e4SLinus Torvalds 	}
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	if (parport_register (dev, id)) {
6351da177e4SLinus Torvalds 		kfree (priv);
6361da177e4SLinus Torvalds 		return -ENODEV;
6371da177e4SLinus Torvalds 	}
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds 	if (serial_register (dev, id)) {
6401da177e4SLinus Torvalds 		int i;
6411da177e4SLinus Torvalds 		for (i = 0; i < priv->num_par; i++)
6421da177e4SLinus Torvalds 			parport_pc_unregister_port (priv->port[i]);
6431da177e4SLinus Torvalds 		kfree (priv);
6441da177e4SLinus Torvalds 		return -ENODEV;
6451da177e4SLinus Torvalds 	}
6461da177e4SLinus Torvalds 
6471da177e4SLinus Torvalds 	return 0;
6481da177e4SLinus Torvalds }
6491da177e4SLinus Torvalds 
650312facafSGreg Kroah-Hartman static void parport_serial_pci_remove(struct pci_dev *dev)
6511da177e4SLinus Torvalds {
6521da177e4SLinus Torvalds 	struct parport_serial_private *priv = pci_get_drvdata (dev);
6531da177e4SLinus Torvalds 	int i;
6541da177e4SLinus Torvalds 
65505caac58SRussell King 	// Serial ports
65605caac58SRussell King 	if (priv->serial)
65705caac58SRussell King 		pciserial_remove_ports(priv->serial);
65805caac58SRussell King 
6591da177e4SLinus Torvalds 	// Parallel ports
6601da177e4SLinus Torvalds 	for (i = 0; i < priv->num_par; i++)
6611da177e4SLinus Torvalds 		parport_pc_unregister_port (priv->port[i]);
6621da177e4SLinus Torvalds 
6631da177e4SLinus Torvalds 	kfree (priv);
6641da177e4SLinus Torvalds 	return;
6651da177e4SLinus Torvalds }
6661da177e4SLinus Torvalds 
6677dd7d691SAlexey Dobriyan #ifdef CONFIG_PM
66805caac58SRussell King static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
66905caac58SRussell King {
67005caac58SRussell King 	struct parport_serial_private *priv = pci_get_drvdata(dev);
67105caac58SRussell King 
67205caac58SRussell King 	if (priv->serial)
67305caac58SRussell King 		pciserial_suspend_ports(priv->serial);
67405caac58SRussell King 
67505caac58SRussell King 	/* FIXME: What about parport? */
67605caac58SRussell King 
67705caac58SRussell King 	pci_save_state(dev);
67805caac58SRussell King 	pci_set_power_state(dev, pci_choose_state(dev, state));
67905caac58SRussell King 	return 0;
68005caac58SRussell King }
68105caac58SRussell King 
68205caac58SRussell King static int parport_serial_pci_resume(struct pci_dev *dev)
68305caac58SRussell King {
68405caac58SRussell King 	struct parport_serial_private *priv = pci_get_drvdata(dev);
68510f8a598SRandy Dunlap 	int err;
68605caac58SRussell King 
68705caac58SRussell King 	pci_set_power_state(dev, PCI_D0);
68805caac58SRussell King 	pci_restore_state(dev);
68905caac58SRussell King 
69005caac58SRussell King 	/*
69105caac58SRussell King 	 * The device may have been disabled.  Re-enable it.
69205caac58SRussell King 	 */
69310f8a598SRandy Dunlap 	err = pci_enable_device(dev);
69410f8a598SRandy Dunlap 	if (err) {
69510f8a598SRandy Dunlap 		printk(KERN_ERR "parport_serial: %s: error enabling "
69610f8a598SRandy Dunlap 			"device for resume (%d)\n", pci_name(dev), err);
69710f8a598SRandy Dunlap 		return err;
69810f8a598SRandy Dunlap 	}
69905caac58SRussell King 
70005caac58SRussell King 	if (priv->serial)
70105caac58SRussell King 		pciserial_resume_ports(priv->serial);
70205caac58SRussell King 
70305caac58SRussell King 	/* FIXME: What about parport? */
70405caac58SRussell King 
70505caac58SRussell King 	return 0;
70605caac58SRussell King }
7077dd7d691SAlexey Dobriyan #endif
70805caac58SRussell King 
7091da177e4SLinus Torvalds static struct pci_driver parport_serial_pci_driver = {
7101da177e4SLinus Torvalds 	.name		= "parport_serial",
7111da177e4SLinus Torvalds 	.id_table	= parport_serial_pci_tbl,
7121da177e4SLinus Torvalds 	.probe		= parport_serial_pci_probe,
713312facafSGreg Kroah-Hartman 	.remove		= parport_serial_pci_remove,
7147dd7d691SAlexey Dobriyan #ifdef CONFIG_PM
71505caac58SRussell King 	.suspend	= parport_serial_pci_suspend,
71605caac58SRussell King 	.resume		= parport_serial_pci_resume,
7177dd7d691SAlexey Dobriyan #endif
7181da177e4SLinus Torvalds };
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds 
7211da177e4SLinus Torvalds static int __init parport_serial_init (void)
7221da177e4SLinus Torvalds {
72393b47684SRichard Knutsson 	return pci_register_driver (&parport_serial_pci_driver);
7241da177e4SLinus Torvalds }
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds static void __exit parport_serial_exit (void)
7271da177e4SLinus Torvalds {
7281da177e4SLinus Torvalds 	pci_unregister_driver (&parport_serial_pci_driver);
7291da177e4SLinus Torvalds 	return;
7301da177e4SLinus Torvalds }
7311da177e4SLinus Torvalds 
7321da177e4SLinus Torvalds MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>");
7331da177e4SLinus Torvalds MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards");
7341da177e4SLinus Torvalds MODULE_LICENSE("GPL");
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds module_init(parport_serial_init);
7371da177e4SLinus Torvalds module_exit(parport_serial_exit);
738