1
2/*
3 *   pata-isapnp.c - ISA PnP PATA controller driver.
4 *   Copyright 2005/2006 Red Hat Inc, all rights reserved.
5 *
6 *   Based in part on ide-pnp.c by Andrey Panin <pazke@donpac.ru>
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/isapnp.h>
12#include <linux/init.h>
13#include <linux/blkdev.h>
14#include <linux/delay.h>
15#include <scsi/scsi_host.h>
16#include <linux/ata.h>
17#include <linux/libata.h>
18
19#define DRV_NAME "pata_isapnp"
20#define DRV_VERSION "0.2.5"
21
22static struct scsi_host_template isapnp_sht = {
23	ATA_PIO_SHT(DRV_NAME),
24};
25
26static struct ata_port_operations isapnp_port_ops = {
27	.inherits	= &ata_sff_port_ops,
28	.cable_detect	= ata_cable_40wire,
29};
30
31static struct ata_port_operations isapnp_noalt_port_ops = {
32	.inherits	= &ata_sff_port_ops,
33	.cable_detect	= ata_cable_40wire,
34	/* No altstatus so we don't want to use the lost interrupt poll */
35	.lost_interrupt = ATA_OP_NULL,
36};
37
38/**
39 *	isapnp_init_one		-	attach an isapnp interface
40 *	@idev: PnP device
41 *	@dev_id: matching detect line
42 *
43 *	Register an ISA bus IDE interface. Such interfaces are PIO 0 and
44 *	non shared IRQ.
45 */
46
47static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev_id)
48{
49	struct ata_host *host;
50	struct ata_port *ap;
51	void __iomem *cmd_addr, *ctl_addr;
52	int irq = 0;
53	irq_handler_t handler = NULL;
54
55	if (pnp_port_valid(idev, 0) == 0)
56		return -ENODEV;
57
58	if (pnp_irq_valid(idev, 0)) {
59		irq = pnp_irq(idev, 0);
60		handler = ata_sff_interrupt;
61	}
62
63	/* allocate host */
64	host = ata_host_alloc(&idev->dev, 1);
65	if (!host)
66		return -ENOMEM;
67
68	/* acquire resources and fill host */
69	cmd_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 0), 8);
70	if (!cmd_addr)
71		return -ENOMEM;
72
73	ap = host->ports[0];
74
75	ap->ops = &isapnp_noalt_port_ops;
76	ap->pio_mask = ATA_PIO0;
77	ap->flags |= ATA_FLAG_SLAVE_POSS;
78
79	ap->ioaddr.cmd_addr = cmd_addr;
80
81	if (pnp_port_valid(idev, 1)) {
82		ctl_addr = devm_ioport_map(&idev->dev,
83					   pnp_port_start(idev, 1), 1);
84		ap->ioaddr.altstatus_addr = ctl_addr;
85		ap->ioaddr.ctl_addr = ctl_addr;
86		ap->ops = &isapnp_port_ops;
87	}
88
89	ata_sff_std_ports(&ap->ioaddr);
90
91	ata_port_desc(ap, "cmd 0x%llx ctl 0x%llx",
92		      (unsigned long long)pnp_port_start(idev, 0),
93		      (unsigned long long)pnp_port_start(idev, 1));
94
95	/* activate */
96	return ata_host_activate(host, irq, handler, 0,
97				 &isapnp_sht);
98}
99
100/**
101 *	isapnp_remove_one	-	unplug an isapnp interface
102 *	@idev: PnP device
103 *
104 *	Remove a previously configured PnP ATA port. Called only on module
105 *	unload events as the core does not currently deal with ISAPnP docking.
106 */
107
108static void isapnp_remove_one(struct pnp_dev *idev)
109{
110	struct device *dev = &idev->dev;
111	struct ata_host *host = dev_get_drvdata(dev);
112
113	ata_host_detach(host);
114}
115
116static struct pnp_device_id isapnp_devices[] = {
117  	/* Generic ESDI/IDE/ATA compatible hard disk controller */
118	{.id = "PNP0600", .driver_data = 0},
119	{.id = ""}
120};
121
122MODULE_DEVICE_TABLE(pnp, isapnp_devices);
123
124static struct pnp_driver isapnp_driver = {
125	.name		= DRV_NAME,
126	.id_table	= isapnp_devices,
127	.probe		= isapnp_init_one,
128	.remove		= isapnp_remove_one,
129};
130
131module_pnp_driver(isapnp_driver);
132MODULE_AUTHOR("Alan Cox");
133MODULE_DESCRIPTION("low-level driver for ISA PnP ATA");
134MODULE_LICENSE("GPL");
135MODULE_VERSION(DRV_VERSION);
136