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