1/*
2 * linux/drivers/ide/ide-pnp.c
3 *
4 * This file provides autodetection for ISA PnP IDE interfaces.
5 * It was tested with "ESS ES1868 Plug and Play AudioDrive" IDE interface.
6 *
7 * Copyright (C) 2000 Andrey Panin <pazke@orbita.don.sitek.net>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * You should have received a copy of the GNU General Public License
15 * (for example /usr/src/linux/COPYING); if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#include <linux/ide.h>
20#include <linux/init.h>
21
22#include <linux/isapnp.h>
23
24#ifndef PREPARE_FUNC
25#define PREPARE_FUNC(dev)  (dev->prepare)
26#define ACTIVATE_FUNC(dev)  (dev->activate)
27#define DEACTIVATE_FUNC(dev)  (dev->deactivate)
28#endif
29
30#define DEV_IO(dev, index) (dev->resource[index].start)
31#define DEV_IRQ(dev, index) (dev->irq_resource[index].start)
32
33#define DEV_NAME(dev) (dev->bus->name ? dev->bus->name : "ISA PnP")
34
35#define GENERIC_HD_DATA		0
36#define GENERIC_HD_ERROR	1
37#define GENERIC_HD_NSECTOR	2
38#define GENERIC_HD_SECTOR	3
39#define GENERIC_HD_LCYL		4
40#define GENERIC_HD_HCYL		5
41#define GENERIC_HD_SELECT	6
42#define GENERIC_HD_STATUS	7
43
44static int generic_ide_offsets[IDE_NR_PORTS] __initdata = {
45	GENERIC_HD_DATA, GENERIC_HD_ERROR, GENERIC_HD_NSECTOR,
46	GENERIC_HD_SECTOR, GENERIC_HD_LCYL, GENERIC_HD_HCYL,
47	GENERIC_HD_SELECT, GENERIC_HD_STATUS, -1, -1
48};
49
50/* ISA PnP device table entry */
51struct pnp_dev_t {
52	unsigned short card_vendor, card_device, vendor, device;
53	int (*init_fn)(struct pci_dev *dev, int enable);
54};
55
56/* Generic initialisation function for ISA PnP IDE interface */
57static int __init pnpide_generic_init(struct pci_dev *dev, int enable)
58{
59	hw_regs_t hw;
60	int index;
61
62	if (!enable)
63		return 0;
64
65	if (!(DEV_IO(dev, 0) && DEV_IO(dev, 1) && DEV_IRQ(dev, 0)))
66		return 1;
67
68	ide_setup_ports(&hw, (ide_ioreg_t) DEV_IO(dev, 0),
69			generic_ide_offsets, (ide_ioreg_t) DEV_IO(dev, 1),
70			0, NULL, DEV_IRQ(dev, 0));
71
72	index = ide_register_hw(&hw, NULL);
73
74	if (index != -1) {
75	    	printk("ide%d: %s IDE interface\n", index, DEV_NAME(dev));
76		return 0;
77	}
78
79	return 1;
80}
81
82/* Add your devices here :)) */
83struct pnp_dev_t idepnp_devices[] __initdata = {
84  	/* Generic ESDI/IDE/ATA compatible hard disk controller */
85	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
86		ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0600),
87		pnpide_generic_init },
88	{	0 }
89};
90
91#ifdef MODULE
92#define NR_PNP_DEVICES 8
93struct pnp_dev_inst {
94	struct pci_dev *dev;
95	struct pnp_dev_t *dev_type;
96};
97static struct pnp_dev_inst devices[NR_PNP_DEVICES];
98static int pnp_ide_dev_idx = 0;
99#endif
100
101/*
102 * Probe for ISA PnP IDE interfaces.
103 */
104void __init pnpide_init(int enable)
105{
106	struct pci_dev *dev = NULL;
107	struct pnp_dev_t *dev_type;
108
109	if (!isapnp_present())
110		return;
111
112#ifdef MODULE
113	/* Module unload, deactivate all registered devices. */
114	if (!enable) {
115		int i;
116		for (i = 0; i < pnp_ide_dev_idx; i++) {
117			devices[i].dev_type->init_fn(dev, 0);
118
119			if (DEACTIVATE_FUNC(devices[i].dev))
120				DEACTIVATE_FUNC(devices[i].dev)(devices[i].dev);
121		}
122		return;
123	}
124#endif
125	for (dev_type = idepnp_devices; dev_type->vendor; dev_type++) {
126		while ((dev = isapnp_find_dev(NULL, dev_type->vendor,
127			dev_type->device, dev))) {
128
129			if (dev->active)
130				continue;
131
132       			if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) {
133				printk("ide: %s prepare failed\n", DEV_NAME(dev));
134				continue;
135			}
136
137			if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) {
138				printk("ide: %s activate failed\n", DEV_NAME(dev));
139				continue;
140			}
141
142			/* Call device initialization function */
143			if (dev_type->init_fn(dev, 1)) {
144				if (DEACTIVATE_FUNC(dev))
145					DEACTIVATE_FUNC(dev)(dev);
146			} else {
147#ifdef MODULE
148				/*
149				 * Register device in the array to
150				 * deactivate it on a module unload.
151				 */
152				if (pnp_ide_dev_idx >= NR_PNP_DEVICES)
153					return;
154				devices[pnp_ide_dev_idx].dev = dev;
155				devices[pnp_ide_dev_idx].dev_type = dev_type;
156				pnp_ide_dev_idx++;
157#endif
158			}
159		}
160	}
161}
162