1fecae16eSAndy Shevchenko // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Support for common PCI multi-I/O cards (which is most of them)
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2001  Tim Waugh <twaugh@redhat.com>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Multi-function PCI cards are supposed to present separate logical
81da177e4SLinus Torvalds  * devices on the bus.  A common thing to do seems to be to just use
91da177e4SLinus Torvalds  * one logical device with lots of base address registers for both
101da177e4SLinus Torvalds  * parallel ports and serial ports.  This driver is for dealing with
111da177e4SLinus Torvalds  * that.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
1451dcdfecSAlan Cox #include <linux/interrupt.h>
154af5781aSAndy Shevchenko #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/parport.h>
171da177e4SLinus Torvalds #include <linux/parport_pc.h>
184af5781aSAndy Shevchenko #include <linux/pci.h>
194af5781aSAndy Shevchenko #include <linux/slab.h>
204af5781aSAndy Shevchenko #include <linux/types.h>
214af5781aSAndy Shevchenko 
221da177e4SLinus Torvalds #include <linux/8250_pci.h>
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds enum parport_pc_pci_cards {
251da177e4SLinus Torvalds 	titan_110l = 0,
261da177e4SLinus Torvalds 	titan_210l,
271da177e4SLinus Torvalds 	netmos_9xx5_combo,
2844e58a6aSMartin Schitter 	netmos_9855,
2950db9d8eSPhilippe De Muyter 	netmos_9855_2p,
307808edcdSNicos Gollan 	netmos_9900,
317808edcdSNicos Gollan 	netmos_9900_2p,
327808edcdSNicos Gollan 	netmos_99xx_1p,
331da177e4SLinus Torvalds 	avlab_1s1p,
341da177e4SLinus Torvalds 	avlab_1s2p,
351da177e4SLinus Torvalds 	avlab_2s1p,
361da177e4SLinus Torvalds 	siig_1s1p_10x,
371da177e4SLinus Torvalds 	siig_2s1p_10x,
381da177e4SLinus Torvalds 	siig_2p1s_20x,
391da177e4SLinus Torvalds 	siig_1s1p_20x,
401da177e4SLinus Torvalds 	siig_2s1p_20x,
41b9b24558SFrédéric Brière 	timedia_4078a,
42b9b24558SFrédéric Brière 	timedia_4079h,
43b9b24558SFrédéric Brière 	timedia_4085h,
44b9b24558SFrédéric Brière 	timedia_4088a,
45b9b24558SFrédéric Brière 	timedia_4089a,
46b9b24558SFrédéric Brière 	timedia_4095a,
47b9b24558SFrédéric Brière 	timedia_4096a,
48b9b24558SFrédéric Brière 	timedia_4078u,
49b9b24558SFrédéric Brière 	timedia_4079a,
50b9b24558SFrédéric Brière 	timedia_4085u,
51b9b24558SFrédéric Brière 	timedia_4079r,
52b9b24558SFrédéric Brière 	timedia_4079s,
53b9b24558SFrédéric Brière 	timedia_4079d,
54b9b24558SFrédéric Brière 	timedia_4079e,
55b9b24558SFrédéric Brière 	timedia_4079f,
56b9b24558SFrédéric Brière 	timedia_9079a,
57b9b24558SFrédéric Brière 	timedia_9079b,
58b9b24558SFrédéric Brière 	timedia_9079c,
59feb58142SEzequiel Garcia 	wch_ch353_1s1p,
606971c635SGuainluca Anzolin 	wch_ch353_2s1p,
61c9a104e2SColin Ian King 	wch_ch382_0s1p,
622fdd8c8cSSergej Pupykin 	wch_ch382_2s1p,
63ec8e3893SAndy Shevchenko 	brainboxes_5s1p,
64abd7bacaSStephen Chivers 	sunix_2s1p,
651da177e4SLinus Torvalds };
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds /* each element directly indexed from enum list, above */
681da177e4SLinus Torvalds struct parport_pc_pci {
691da177e4SLinus Torvalds 	int numports;
701da177e4SLinus Torvalds 	struct { /* BAR (base address registers) numbers in the config
711da177e4SLinus Torvalds                     space header */
721da177e4SLinus Torvalds 		int lo;
731da177e4SLinus Torvalds 		int hi; /* -1 if not there, >6 for offset-method (max
741da177e4SLinus Torvalds                            BAR is 6) */
751da177e4SLinus Torvalds 	} addr[4];
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds 	/* If set, this is called immediately after pci_enable_device.
781da177e4SLinus Torvalds 	 * If it returns non-zero, no probing will take place and the
791da177e4SLinus Torvalds 	 * ports will not be used. */
801da177e4SLinus Torvalds 	int (*preinit_hook) (struct pci_dev *pdev, struct parport_pc_pci *card,
811da177e4SLinus Torvalds 				int autoirq, int autodma);
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	/* If set, this is called after probing for ports.  If 'failed'
841da177e4SLinus Torvalds 	 * is non-zero we couldn't use any of the ports. */
851da177e4SLinus Torvalds 	void (*postinit_hook) (struct pci_dev *pdev,
861da177e4SLinus Torvalds 				struct parport_pc_pci *card, int failed);
871da177e4SLinus Torvalds };
881da177e4SLinus Torvalds 
89312facafSGreg Kroah-Hartman static int netmos_parallel_init(struct pci_dev *dev, struct parport_pc_pci *par,
90312facafSGreg Kroah-Hartman 				int autoirq, int autodma)
911da177e4SLinus Torvalds {
923abdbf90SJiri Slaby 	/* the rule described below doesn't hold for this device */
933abdbf90SJiri Slaby 	if (dev->device == PCI_DEVICE_ID_NETMOS_9835 &&
943abdbf90SJiri Slaby 			dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
953abdbf90SJiri Slaby 			dev->subsystem_device == 0x0299)
963abdbf90SJiri Slaby 		return -ENODEV;
977808edcdSNicos Gollan 
987808edcdSNicos Gollan 	if (dev->device == PCI_DEVICE_ID_NETMOS_9912) {
997808edcdSNicos Gollan 		par->numports = 1;
1007808edcdSNicos Gollan 	} else {
1011da177e4SLinus Torvalds 		/*
1021da177e4SLinus Torvalds 		 * Netmos uses the subdevice ID to indicate the number of parallel
1031da177e4SLinus Torvalds 		 * and serial ports.  The form is 0x00PS, where <P> is the number of
1041da177e4SLinus Torvalds 		 * parallel ports and <S> is the number of serial ports.
1051da177e4SLinus Torvalds 		 */
10650db9d8eSPhilippe De Muyter 		par->numports = (dev->subsystem_device & 0xf0) >> 4;
10750db9d8eSPhilippe De Muyter 		if (par->numports > ARRAY_SIZE(par->addr))
10850db9d8eSPhilippe De Muyter 			par->numports = ARRAY_SIZE(par->addr);
1097808edcdSNicos Gollan 	}
1107808edcdSNicos Gollan 
1111da177e4SLinus Torvalds 	return 0;
1121da177e4SLinus Torvalds }
1131da177e4SLinus Torvalds 
114312facafSGreg Kroah-Hartman static struct parport_pc_pci cards[] = {
1151da177e4SLinus Torvalds 	/* titan_110l */		{ 1, { { 3, -1 }, } },
1161da177e4SLinus Torvalds 	/* titan_210l */		{ 1, { { 3, -1 }, } },
1171da177e4SLinus Torvalds 	/* netmos_9xx5_combo */		{ 1, { { 2, -1 }, }, netmos_parallel_init },
11850db9d8eSPhilippe De Muyter 	/* netmos_9855 */		{ 1, { { 0, -1 }, }, netmos_parallel_init },
11950db9d8eSPhilippe De Muyter 	/* netmos_9855_2p */		{ 2, { { 0, -1 }, { 2, -1 }, } },
1207808edcdSNicos Gollan 	/* netmos_9900 */		{1, { { 3, 4 }, }, netmos_parallel_init },
1217808edcdSNicos Gollan 	/* netmos_9900_2p */		{2, { { 0, 1 }, { 3, 4 }, } },
1227808edcdSNicos Gollan 	/* netmos_99xx_1p */		{1, { { 0, 1 }, } },
1231da177e4SLinus Torvalds 	/* avlab_1s1p     */		{ 1, { { 1, 2}, } },
1241da177e4SLinus Torvalds 	/* avlab_1s2p     */		{ 2, { { 1, 2}, { 3, 4 },} },
1251da177e4SLinus Torvalds 	/* avlab_2s1p     */		{ 1, { { 2, 3}, } },
1261da177e4SLinus Torvalds 	/* siig_1s1p_10x */		{ 1, { { 3, 4 }, } },
1271da177e4SLinus Torvalds 	/* siig_2s1p_10x */		{ 1, { { 4, 5 }, } },
1281da177e4SLinus Torvalds 	/* siig_2p1s_20x */		{ 2, { { 1, 2 }, { 3, 4 }, } },
1291da177e4SLinus Torvalds 	/* siig_1s1p_20x */		{ 1, { { 1, 2 }, } },
1301da177e4SLinus Torvalds 	/* siig_2s1p_20x */		{ 1, { { 2, 3 }, } },
131b9b24558SFrédéric Brière 	/* timedia_4078a */		{ 1, { { 2, -1 }, } },
132b9b24558SFrédéric Brière 	/* timedia_4079h */             { 1, { { 2, 3 }, } },
133b9b24558SFrédéric Brière 	/* timedia_4085h */             { 2, { { 2, -1 }, { 4, -1 }, } },
134b9b24558SFrédéric Brière 	/* timedia_4088a */             { 2, { { 2, 3 }, { 4, 5 }, } },
135b9b24558SFrédéric Brière 	/* timedia_4089a */             { 2, { { 2, 3 }, { 4, 5 }, } },
136b9b24558SFrédéric Brière 	/* timedia_4095a */             { 2, { { 2, 3 }, { 4, 5 }, } },
137b9b24558SFrédéric Brière 	/* timedia_4096a */             { 2, { { 2, 3 }, { 4, 5 }, } },
138b9b24558SFrédéric Brière 	/* timedia_4078u */             { 1, { { 2, -1 }, } },
139b9b24558SFrédéric Brière 	/* timedia_4079a */             { 1, { { 2, 3 }, } },
140b9b24558SFrédéric Brière 	/* timedia_4085u */             { 2, { { 2, -1 }, { 4, -1 }, } },
141b9b24558SFrédéric Brière 	/* timedia_4079r */             { 1, { { 2, 3 }, } },
142b9b24558SFrédéric Brière 	/* timedia_4079s */             { 1, { { 2, 3 }, } },
143b9b24558SFrédéric Brière 	/* timedia_4079d */             { 1, { { 2, 3 }, } },
144b9b24558SFrédéric Brière 	/* timedia_4079e */             { 1, { { 2, 3 }, } },
145b9b24558SFrédéric Brière 	/* timedia_4079f */             { 1, { { 2, 3 }, } },
146b9b24558SFrédéric Brière 	/* timedia_9079a */             { 1, { { 2, 3 }, } },
147b9b24558SFrédéric Brière 	/* timedia_9079b */             { 1, { { 2, 3 }, } },
148b9b24558SFrédéric Brière 	/* timedia_9079c */             { 1, { { 2, 3 }, } },
149feb58142SEzequiel Garcia 	/* wch_ch353_1s1p*/             { 1, { { 1, -1}, } },
1506971c635SGuainluca Anzolin 	/* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
151c9a104e2SColin Ian King 	/* wch_ch382_0s1p*/		{ 1, { { 2, -1}, } },
1522fdd8c8cSSergej Pupykin 	/* wch_ch382_2s1p*/             { 1, { { 2, -1}, } },
153ec8e3893SAndy Shevchenko 	/* brainboxes_5s1p */           { 1, { { 3, -1 }, } },
154abd7bacaSStephen Chivers 	/* sunix_2s1p */                { 1, { { 3, -1 }, } },
1551da177e4SLinus Torvalds };
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds static struct pci_device_id parport_serial_pci_tbl[] = {
1581da177e4SLinus Torvalds 	/* PCI cards */
1591da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,
1601da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l },
1611da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L,
1621da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l },
1631da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735,
1641da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
1651da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745,
1661da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
1671da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
1681da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
1691da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845,
1701da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
1711da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
17250db9d8eSPhilippe De Muyter 	  0x1000, 0x0020, 0, 0, netmos_9855_2p },
17350db9d8eSPhilippe De Muyter 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
17450db9d8eSPhilippe De Muyter 	  0x1000, 0x0022, 0, 0, netmos_9855_2p },
17550db9d8eSPhilippe De Muyter 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
17644e58a6aSMartin Schitter 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 },
1777808edcdSNicos Gollan 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
1787808edcdSNicos Gollan 	  0xA000, 0x3011, 0, 0, netmos_9900 },
1797808edcdSNicos Gollan 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
1807808edcdSNicos Gollan 	  0xA000, 0x3012, 0, 0, netmos_9900 },
1817808edcdSNicos Gollan 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
1827808edcdSNicos Gollan 	  0xA000, 0x3020, 0, 0, netmos_9900_2p },
1837808edcdSNicos Gollan 	{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
1847808edcdSNicos Gollan 	  0xA000, 0x2000, 0, 0, netmos_99xx_1p },
1851da177e4SLinus Torvalds 	/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
18691bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2110,
18791bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
18891bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2111,
18991bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
19091bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2112,
19191bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
19291bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2140,
19391bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
19491bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2141,
19591bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
19691bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2142,
19791bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
19891bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2160,
19991bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
20091bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2161,
20191bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
20291bca4b3SRussell King 	{ PCI_VENDOR_ID_AFAVLAB, 0x2162,
20391bca4b3SRussell King 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
2041da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
2051da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
2061da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
2071da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
2081da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
2091da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
2101da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
2111da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
2121da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
2131da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
2141da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
2151da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
2161da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
2171da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
2181da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
2191da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
2201da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
2211da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
2221da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
2231da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
2241da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
2251da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
2261da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
2271da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
2281da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
2291da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
2301da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
2311da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
2321da177e4SLinus Torvalds 	{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
2331da177e4SLinus Torvalds 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
234b9b24558SFrédéric Brière 	/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
235b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a },
236b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h },
237b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h },
238b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a },
239b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a },
240b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a },
241b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a },
242b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u },
243b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a },
244b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u },
245b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r },
246b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s },
247b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d },
248b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e },
249b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f },
250b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
251b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
252b9b24558SFrédéric Brière 	{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
253abd7bacaSStephen Chivers 
2546971c635SGuainluca Anzolin 	/* WCH CARDS */
255feb58142SEzequiel Garcia 	{ 0x4348, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p},
2566971c635SGuainluca Anzolin 	{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
257c9a104e2SColin Ian King 	{ 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p},
2582fdd8c8cSSergej Pupykin 	{ 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p},
259abd7bacaSStephen Chivers 
260ec8e3893SAndy Shevchenko 	/* BrainBoxes PX272/PX306 MIO card */
261ec8e3893SAndy Shevchenko 	{ PCI_VENDOR_ID_INTASHIELD, 0x4100,
262ec8e3893SAndy Shevchenko 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p },
263ec8e3893SAndy Shevchenko 
264abd7bacaSStephen Chivers 	/*
265abd7bacaSStephen Chivers 	 * More SUNIX variations. At least one of these has part number
266abd7bacaSStephen Chivers 	 * '5079A but subdevice 0x102. That board reports 0x0708 as
267abd7bacaSStephen Chivers 	 * its PCI Class.
268abd7bacaSStephen Chivers 	 */
269abd7bacaSStephen Chivers 	{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
270abd7bacaSStephen Chivers 	  0x0102, 0, 0, sunix_2s1p },
271abd7bacaSStephen Chivers 
2721da177e4SLinus Torvalds 	{ 0, } /* terminate list */
2731da177e4SLinus Torvalds };
2741da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds /*
27705caac58SRussell King  * This table describes the serial "geometry" of these boards.  Any
27805caac58SRussell King  * quirks for these can be found in drivers/serial/8250_pci.c
27905caac58SRussell King  *
28005caac58SRussell King  * Cards not tested are marked n/t
28105caac58SRussell King  * If you have one of these cards and it works for you, please tell me..
2821da177e4SLinus Torvalds  */
283312facafSGreg Kroah-Hartman static struct pciserial_board pci_parport_serial_boards[] = {
28405caac58SRussell King 	[titan_110l] = {
28505caac58SRussell King 		.flags		= FL_BASE1 | FL_BASE_BARS,
28605caac58SRussell King 		.num_ports	= 1,
28705caac58SRussell King 		.base_baud	= 921600,
28805caac58SRussell King 		.uart_offset	= 8,
28905caac58SRussell King 	},
29005caac58SRussell King 	[titan_210l] = {
29105caac58SRussell King 		.flags		= FL_BASE1 | FL_BASE_BARS,
29205caac58SRussell King 		.num_ports	= 2,
29305caac58SRussell King 		.base_baud	= 921600,
29405caac58SRussell King 		.uart_offset	= 8,
29505caac58SRussell King 	},
29605caac58SRussell King 	[netmos_9xx5_combo] = {
29705caac58SRussell King 		.flags		= FL_BASE0 | FL_BASE_BARS,
29805caac58SRussell King 		.num_ports	= 1,
29905caac58SRussell King 		.base_baud	= 115200,
30005caac58SRussell King 		.uart_offset	= 8,
30105caac58SRussell King 	},
30205caac58SRussell King 	[netmos_9855] = {
30350db9d8eSPhilippe De Muyter 		.flags		= FL_BASE2 | FL_BASE_BARS,
30450db9d8eSPhilippe De Muyter 		.num_ports	= 1,
30550db9d8eSPhilippe De Muyter 		.base_baud	= 115200,
30650db9d8eSPhilippe De Muyter 		.uart_offset	= 8,
30750db9d8eSPhilippe De Muyter 	},
30850db9d8eSPhilippe De Muyter 	[netmos_9855_2p] = {
309c01106e5SChristian Pellegrin 		.flags		= FL_BASE4 | FL_BASE_BARS,
31005caac58SRussell King 		.num_ports	= 1,
31105caac58SRussell King 		.base_baud	= 115200,
31205caac58SRussell King 		.uart_offset	= 8,
31305caac58SRussell King 	},
3147808edcdSNicos Gollan 	[netmos_9900] = { /* n/t */
3157808edcdSNicos Gollan 		.flags		= FL_BASE0 | FL_BASE_BARS,
3167808edcdSNicos Gollan 		.num_ports	= 1,
3177808edcdSNicos Gollan 		.base_baud	= 115200,
3187808edcdSNicos Gollan 		.uart_offset	= 8,
3197808edcdSNicos Gollan 	},
3207808edcdSNicos Gollan 	[netmos_9900_2p] = { /* parallel only */ /* n/t */
3217808edcdSNicos Gollan 		.flags		= FL_BASE0,
3227808edcdSNicos Gollan 		.num_ports	= 0,
3237808edcdSNicos Gollan 		.base_baud	= 115200,
3247808edcdSNicos Gollan 		.uart_offset	= 8,
3257808edcdSNicos Gollan 	},
3267808edcdSNicos Gollan 	[netmos_99xx_1p] = { /* 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 	},
33205caac58SRussell King 	[avlab_1s1p] = { /* n/t */
33305caac58SRussell King 		.flags		= FL_BASE0 | FL_BASE_BARS,
33405caac58SRussell King 		.num_ports	= 1,
33505caac58SRussell King 		.base_baud	= 115200,
33605caac58SRussell King 		.uart_offset	= 8,
33705caac58SRussell King 	},
33805caac58SRussell King 	[avlab_1s2p] = { /* 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_2s1p] = { /* n/t */
34505caac58SRussell King 		.flags		= FL_BASE0 | FL_BASE_BARS,
34605caac58SRussell King 		.num_ports	= 2,
34705caac58SRussell King 		.base_baud	= 115200,
34805caac58SRussell King 		.uart_offset	= 8,
34905caac58SRussell King 	},
35005caac58SRussell King 	[siig_1s1p_10x] = {
35105caac58SRussell King 		.flags		= FL_BASE2,
35205caac58SRussell King 		.num_ports	= 1,
35305caac58SRussell King 		.base_baud	= 460800,
35405caac58SRussell King 		.uart_offset	= 8,
35505caac58SRussell King 	},
35605caac58SRussell King 	[siig_2s1p_10x] = {
35705caac58SRussell King 		.flags		= FL_BASE2,
35805caac58SRussell King 		.num_ports	= 1,
35905caac58SRussell King 		.base_baud	= 921600,
36005caac58SRussell King 		.uart_offset	= 8,
36105caac58SRussell King 	},
36205caac58SRussell King 	[siig_2p1s_20x] = {
36305caac58SRussell King 		.flags		= FL_BASE0,
36405caac58SRussell King 		.num_ports	= 1,
36505caac58SRussell King 		.base_baud	= 921600,
36605caac58SRussell King 		.uart_offset	= 8,
36705caac58SRussell King 	},
36805caac58SRussell King 	[siig_1s1p_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_2s1p_20x] = {
37505caac58SRussell King 		.flags		= FL_BASE0,
37605caac58SRussell King 		.num_ports	= 1,
37705caac58SRussell King 		.base_baud	= 921600,
37805caac58SRussell King 		.uart_offset	= 8,
37905caac58SRussell King 	},
380b9b24558SFrédéric Brière 	[timedia_4078a] = {
381b9b24558SFrédéric Brière 		.flags		= FL_BASE0|FL_BASE_BARS,
382b9b24558SFrédéric Brière 		.num_ports	= 1,
383b9b24558SFrédéric Brière 		.base_baud	= 921600,
384b9b24558SFrédéric Brière 		.uart_offset	= 8,
385b9b24558SFrédéric Brière 	},
386b9b24558SFrédéric Brière 	[timedia_4079h] = {
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_4085h] = {
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_4088a] = {
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_4089a] = {
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_4095a] = {
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_4096a] = {
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_4078u] = {
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_4079a] = {
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_4085u] = {
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_4079r] = {
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_4079s] = {
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_4079d] = {
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_4079e] = {
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_4079f] = {
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_9079a] = {
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_9079b] = {
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_9079c] = {
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 	},
488feb58142SEzequiel Garcia 	[wch_ch353_1s1p] = {
489feb58142SEzequiel Garcia 		.flags          = FL_BASE0|FL_BASE_BARS,
490feb58142SEzequiel Garcia 		.num_ports      = 1,
491feb58142SEzequiel Garcia 		.base_baud      = 115200,
492feb58142SEzequiel Garcia 		.uart_offset    = 8,
493feb58142SEzequiel Garcia 	},
4946971c635SGuainluca Anzolin 	[wch_ch353_2s1p] = {
4956971c635SGuainluca Anzolin 		.flags          = FL_BASE0|FL_BASE_BARS,
4966971c635SGuainluca Anzolin 		.num_ports      = 2,
4976971c635SGuainluca Anzolin 		.base_baud      = 115200,
4986971c635SGuainluca Anzolin 		.uart_offset    = 8,
4996971c635SGuainluca Anzolin 	},
500c9a104e2SColin Ian King 	[wch_ch382_0s1p] = {
501c9a104e2SColin Ian King 		.flags          = FL_BASE0,
502c9a104e2SColin Ian King 		.num_ports      = 0,
503c9a104e2SColin Ian King 		.base_baud      = 115200,
504c9a104e2SColin Ian King 		.uart_offset    = 8,
505c9a104e2SColin Ian King 	},
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 	if (board->num_ports == 0)
5437808edcdSNicos Gollan 		return 0;
5447808edcdSNicos Gollan 
54505caac58SRussell King 	serial = pciserial_init_ports(dev, board);
54605caac58SRussell King 	if (IS_ERR(serial))
54705caac58SRussell King 		return PTR_ERR(serial);
5481da177e4SLinus Torvalds 
54905caac58SRussell King 	priv->serial = serial;
55005caac58SRussell King 	return 0;
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds 
5531da177e4SLinus Torvalds /* Register the parallel port(s) of a PCI card. */
554312facafSGreg Kroah-Hartman static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
5551da177e4SLinus Torvalds {
5561da177e4SLinus Torvalds 	struct parport_pc_pci *card;
5571da177e4SLinus Torvalds 	struct parport_serial_private *priv = pci_get_drvdata (dev);
5587a171cdcSRussell King 	int n, success = 0;
5591da177e4SLinus Torvalds 
5601da177e4SLinus Torvalds 	priv->par = cards[id->driver_data];
5611da177e4SLinus Torvalds 	card = &priv->par;
5621da177e4SLinus Torvalds 	if (card->preinit_hook &&
5631da177e4SLinus Torvalds 	    card->preinit_hook (dev, card, PARPORT_IRQ_NONE, PARPORT_DMA_NONE))
5641da177e4SLinus Torvalds 		return -ENODEV;
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds 	for (n = 0; n < card->numports; n++) {
5671da177e4SLinus Torvalds 		struct parport *port;
5681da177e4SLinus Torvalds 		int lo = card->addr[n].lo;
5691da177e4SLinus Torvalds 		int hi = card->addr[n].hi;
5701da177e4SLinus Torvalds 		unsigned long io_lo, io_hi;
57151dcdfecSAlan Cox 		int irq;
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 		if (priv->num_par == ARRAY_SIZE (priv->port)) {
57482dfabf1SAndy Shevchenko 			dev_warn(&dev->dev,
57582dfabf1SAndy Shevchenko 				 "only %zu parallel ports supported (%d reported)\n",
5761da177e4SLinus Torvalds 				 ARRAY_SIZE(priv->port), card->numports);
5771da177e4SLinus Torvalds 			break;
5781da177e4SLinus Torvalds 		}
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds 		io_lo = pci_resource_start (dev, lo);
5811da177e4SLinus Torvalds 		io_hi = 0;
5821da177e4SLinus Torvalds 		if ((hi >= 0) && (hi <= 6))
5831da177e4SLinus Torvalds 			io_hi = pci_resource_start (dev, hi);
5841da177e4SLinus Torvalds 		else if (hi > 6)
5851da177e4SLinus Torvalds 			io_lo += hi; /* Reinterpret the meaning of
5861da177e4SLinus Torvalds                                         "hi" as an offset (see SYBA
5871da177e4SLinus Torvalds                                         def.) */
5881da177e4SLinus Torvalds 		/* TODO: test if sharing interrupts works */
58951dcdfecSAlan Cox 		irq = dev->irq;
59051dcdfecSAlan Cox 		if (irq == IRQ_NONE) {
59151dcdfecSAlan Cox 			dev_dbg(&dev->dev,
59251dcdfecSAlan Cox 				"PCI parallel port detected: I/O at %#lx(%#lx)\n",
59351dcdfecSAlan Cox 				io_lo, io_hi);
59451dcdfecSAlan Cox 			irq = PARPORT_IRQ_NONE;
59551dcdfecSAlan Cox 		} else {
59651dcdfecSAlan Cox 			dev_dbg(&dev->dev,
59751dcdfecSAlan Cox 				"PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
59851dcdfecSAlan Cox 				io_lo, io_hi, irq);
59951dcdfecSAlan Cox 		}
60051dcdfecSAlan Cox 		port = parport_pc_probe_port (io_lo, io_hi, irq,
60151dcdfecSAlan Cox 			      PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED);
6021da177e4SLinus Torvalds 		if (port) {
6031da177e4SLinus Torvalds 			priv->port[priv->num_par++] = port;
6041da177e4SLinus Torvalds 			success = 1;
6051da177e4SLinus Torvalds 		}
6061da177e4SLinus Torvalds 	}
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds 	if (card->postinit_hook)
6091da177e4SLinus Torvalds 		card->postinit_hook (dev, card, !success);
6101da177e4SLinus Torvalds 
6117a171cdcSRussell King 	return 0;
6121da177e4SLinus Torvalds }
6131da177e4SLinus Torvalds 
614312facafSGreg Kroah-Hartman static int parport_serial_pci_probe(struct pci_dev *dev,
6151da177e4SLinus Torvalds 				    const struct pci_device_id *id)
6161da177e4SLinus Torvalds {
6171da177e4SLinus Torvalds 	struct parport_serial_private *priv;
6181da177e4SLinus Torvalds 	int err;
6191da177e4SLinus Torvalds 
620ad8ce834SAndy Shevchenko 	priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
6211da177e4SLinus Torvalds 	if (!priv)
6221da177e4SLinus Torvalds 		return -ENOMEM;
623ad8ce834SAndy Shevchenko 
6241da177e4SLinus Torvalds 	pci_set_drvdata (dev, priv);
6251da177e4SLinus Torvalds 
626ad8ce834SAndy Shevchenko 	err = pcim_enable_device(dev);
627ad8ce834SAndy Shevchenko 	if (err)
6281da177e4SLinus Torvalds 		return err;
6291da177e4SLinus Torvalds 
63096edf537SAndy Shevchenko 	err = parport_register(dev, id);
63196edf537SAndy Shevchenko 	if (err)
63296edf537SAndy Shevchenko 		return err;
6331da177e4SLinus Torvalds 
63496edf537SAndy Shevchenko 	err = serial_register(dev, id);
63596edf537SAndy Shevchenko 	if (err) {
6361da177e4SLinus Torvalds 		int i;
6371da177e4SLinus Torvalds 		for (i = 0; i < priv->num_par; i++)
6381da177e4SLinus Torvalds 			parport_pc_unregister_port (priv->port[i]);
63996edf537SAndy Shevchenko 		return err;
6401da177e4SLinus Torvalds 	}
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds 	return 0;
6431da177e4SLinus Torvalds }
6441da177e4SLinus Torvalds 
645312facafSGreg Kroah-Hartman static void parport_serial_pci_remove(struct pci_dev *dev)
6461da177e4SLinus Torvalds {
6471da177e4SLinus Torvalds 	struct parport_serial_private *priv = pci_get_drvdata (dev);
6481da177e4SLinus Torvalds 	int i;
6491da177e4SLinus Torvalds 
65005caac58SRussell King 	// Serial ports
65105caac58SRussell King 	if (priv->serial)
65205caac58SRussell King 		pciserial_remove_ports(priv->serial);
65305caac58SRussell King 
6541da177e4SLinus Torvalds 	// Parallel ports
6551da177e4SLinus Torvalds 	for (i = 0; i < priv->num_par; i++)
6561da177e4SLinus Torvalds 		parport_pc_unregister_port (priv->port[i]);
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 	return;
6591da177e4SLinus Torvalds }
6601da177e4SLinus Torvalds 
6611089c911SAndy Shevchenko static int __maybe_unused parport_serial_pci_suspend(struct device *dev)
66205caac58SRussell King {
6639e18a80cSChuhong Yuan 	struct parport_serial_private *priv = dev_get_drvdata(dev);
66405caac58SRussell King 
66505caac58SRussell King 	if (priv->serial)
66605caac58SRussell King 		pciserial_suspend_ports(priv->serial);
66705caac58SRussell King 
66805caac58SRussell King 	/* FIXME: What about parport? */
66905caac58SRussell King 	return 0;
67005caac58SRussell King }
67105caac58SRussell King 
6721089c911SAndy Shevchenko static int __maybe_unused parport_serial_pci_resume(struct device *dev)
67305caac58SRussell King {
6749e18a80cSChuhong Yuan 	struct parport_serial_private *priv = dev_get_drvdata(dev);
67505caac58SRussell King 
67605caac58SRussell King 	if (priv->serial)
67705caac58SRussell King 		pciserial_resume_ports(priv->serial);
67805caac58SRussell King 
67905caac58SRussell King 	/* FIXME: What about parport? */
68005caac58SRussell King 	return 0;
68105caac58SRussell King }
6821089c911SAndy Shevchenko 
6831089c911SAndy Shevchenko static SIMPLE_DEV_PM_OPS(parport_serial_pm_ops,
6841089c911SAndy Shevchenko 			 parport_serial_pci_suspend, parport_serial_pci_resume);
68505caac58SRussell King 
6861da177e4SLinus Torvalds static struct pci_driver parport_serial_pci_driver = {
6871da177e4SLinus Torvalds 	.name		= "parport_serial",
6881da177e4SLinus Torvalds 	.id_table	= parport_serial_pci_tbl,
6891da177e4SLinus Torvalds 	.probe		= parport_serial_pci_probe,
690312facafSGreg Kroah-Hartman 	.remove		= parport_serial_pci_remove,
6911089c911SAndy Shevchenko 	.driver         = {
6921089c911SAndy Shevchenko 		.pm     = &parport_serial_pm_ops,
6931089c911SAndy Shevchenko 	},
6941da177e4SLinus Torvalds };
695b0b0a643SAndy Shevchenko module_pci_driver(parport_serial_pci_driver);
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>");
6981da177e4SLinus Torvalds MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards");
6991da177e4SLinus Torvalds MODULE_LICENSE("GPL");
700