438 lines
11 KiB
C
438 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* pata-legacy.c - Legacy port PATA/SATA controller driver.
|
|
* Copyright 2005/2006 Red Hat, all rights reserved.
|
|
*
|
|
* An ATA driver for the legacy ATA ports.
|
|
*
|
|
* This driver handles legacy (that is "ISA side") IDE ports found
|
|
* on PC class systems. There are three hybrid devices that are exceptions:
|
|
* The Cyrix 5510/5520 where a pre SFF ATA device is on the bridge and
|
|
* the MPIIX where the tuning is PCI side but the IDE is "ISA side".
|
|
*/
|
|
|
|
#include <linux/async.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/init.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/delay.h>
|
|
#include <scsi/scsi_host.h>
|
|
#include <linux/ata.h>
|
|
#include <linux/libata.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#define DRV_NAME "pata_legacy"
|
|
#define DRV_VERSION "0.6.5"
|
|
|
|
#define NR_HOST 6
|
|
|
|
static int all;
|
|
module_param(all, int, 0444);
|
|
MODULE_PARM_DESC(all,
|
|
"Set to probe unclaimed pri/sec ISA port ranges even if PCI");
|
|
|
|
static int probe_all;
|
|
module_param(probe_all, int, 0);
|
|
MODULE_PARM_DESC(probe_all,
|
|
"Set to probe tertiary+ ISA port ranges even if PCI");
|
|
|
|
static int probe_mask = ~0;
|
|
module_param(probe_mask, int, 0);
|
|
MODULE_PARM_DESC(probe_mask, "Probe mask for legacy ISA PATA ports");
|
|
|
|
static int autospeed;
|
|
module_param(autospeed, int, 0);
|
|
MODULE_PARM_DESC(autospeed, "Chip present that snoops speed changes");
|
|
|
|
static int pio_mask = ATA_PIO4;
|
|
module_param(pio_mask, int, 0);
|
|
MODULE_PARM_DESC(pio_mask, "PIO range for autospeed devices");
|
|
|
|
static int iordy_mask = 0xFFFFFFFF;
|
|
module_param(iordy_mask, int, 0);
|
|
MODULE_PARM_DESC(iordy_mask, "Use IORDY if available");
|
|
|
|
enum controller {
|
|
BIOS = 0,
|
|
SNOOP = 1,
|
|
|
|
UNKNOWN = -1
|
|
};
|
|
|
|
struct legacy_data {
|
|
unsigned long timing;
|
|
u8 clock[2];
|
|
u8 last;
|
|
int fast;
|
|
enum controller type;
|
|
struct platform_device *platform_dev;
|
|
};
|
|
|
|
struct legacy_probe {
|
|
unsigned char *name;
|
|
unsigned long port;
|
|
unsigned int irq;
|
|
unsigned int slot;
|
|
enum controller type;
|
|
unsigned long private;
|
|
};
|
|
|
|
struct legacy_controller {
|
|
const char *name;
|
|
struct ata_port_operations *ops;
|
|
unsigned int pio_mask;
|
|
unsigned int flags;
|
|
unsigned int pflags;
|
|
int (*setup)(struct platform_device *, struct legacy_probe *probe,
|
|
struct legacy_data *data);
|
|
};
|
|
|
|
static int legacy_port[NR_HOST] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 };
|
|
|
|
static struct legacy_probe probe_list[NR_HOST];
|
|
static struct legacy_data legacy_data[NR_HOST];
|
|
static struct ata_host *legacy_host[NR_HOST];
|
|
|
|
/**
|
|
* legacy_probe_add - Add interface to probe list
|
|
* @port: Controller port
|
|
* @irq: IRQ number
|
|
* @type: Controller type
|
|
* @private: Controller specific info
|
|
*
|
|
* Add an entry into the probe list for ATA controllers. This is used
|
|
* to add the default ISA slots and then to build up the table
|
|
* further according to other ISA/Weird device scans
|
|
*
|
|
* An I/O port list is used to keep ordering stable and sane, as we
|
|
* don't have any good way to talk about ordering otherwise
|
|
*/
|
|
|
|
static int legacy_probe_add(unsigned long port, unsigned int irq,
|
|
enum controller type, unsigned long private)
|
|
{
|
|
struct legacy_probe *lp = &probe_list[0];
|
|
int i;
|
|
struct legacy_probe *free = NULL;
|
|
|
|
for (i = 0; i < NR_HOST; i++) {
|
|
if (lp->port == 0 && free == NULL)
|
|
free = lp;
|
|
/* Matching port, or the correct slot for ordering */
|
|
if (lp->port == port || legacy_port[i] == port) {
|
|
if (!(probe_mask & 1 << i))
|
|
return -1;
|
|
free = lp;
|
|
break;
|
|
}
|
|
lp++;
|
|
}
|
|
if (free == NULL) {
|
|
printk(KERN_ERR "pata_legacy: Too many interfaces.\n");
|
|
return -1;
|
|
}
|
|
/* Fill in the entry for later probing */
|
|
free->port = port;
|
|
free->irq = irq;
|
|
free->type = type;
|
|
free->private = private;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* legacy_set_mode - mode setting
|
|
* @link: IDE link
|
|
* @unused: Device that failed when error is returned
|
|
*
|
|
* Use a non standard set_mode function. We don't want to be tuned.
|
|
*
|
|
* The BIOS configured everything. Our job is not to fiddle. Just use
|
|
* whatever PIO the hardware is using and leave it at that. When we
|
|
* get some kind of nice user driven API for control then we can
|
|
* expand on this as per hdparm in the base kernel.
|
|
*/
|
|
|
|
static int legacy_set_mode(struct ata_link *link, struct ata_device **unused)
|
|
{
|
|
struct ata_device *dev;
|
|
|
|
ata_for_each_dev(dev, link, ENABLED) {
|
|
ata_dev_info(dev, "configured for PIO\n");
|
|
dev->pio_mode = XFER_PIO_0;
|
|
dev->xfer_mode = XFER_PIO_0;
|
|
dev->xfer_shift = ATA_SHIFT_PIO;
|
|
dev->flags |= ATA_DFLAG_PIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct scsi_host_template legacy_sht = {
|
|
ATA_PIO_SHT(DRV_NAME),
|
|
};
|
|
|
|
static const struct ata_port_operations legacy_base_port_ops = {
|
|
.inherits = &ata_sff_port_ops,
|
|
.cable_detect = ata_cable_40wire,
|
|
};
|
|
|
|
/*
|
|
* These ops are used if the user indicates the hardware
|
|
* snoops the commands to decide on the mode and handles the
|
|
* mode selection "magically" itself. Several legacy controllers
|
|
* do this. The mode range can be set if it is not 0x1F by setting
|
|
* pio_mask as well.
|
|
*/
|
|
|
|
static struct ata_port_operations simple_port_ops = {
|
|
.inherits = &legacy_base_port_ops,
|
|
.sff_data_xfer = ata_sff_data_xfer32,
|
|
};
|
|
|
|
static struct ata_port_operations legacy_port_ops = {
|
|
.inherits = &legacy_base_port_ops,
|
|
.sff_data_xfer = ata_sff_data_xfer32,
|
|
.set_mode = legacy_set_mode,
|
|
};
|
|
|
|
static struct legacy_controller controllers[] = {
|
|
{"BIOS", &legacy_port_ops, ATA_PIO4,
|
|
ATA_FLAG_NO_IORDY, 0, NULL },
|
|
{"Snooping", &simple_port_ops, ATA_PIO4,
|
|
0, 0, NULL },
|
|
};
|
|
|
|
/**
|
|
* probe_chip_type - Discover controller
|
|
* @probe: Probe entry to check
|
|
*
|
|
* Probe an ATA port and identify the type of controller. We don't
|
|
* check if the controller appears to be driveless at this point.
|
|
*/
|
|
|
|
static __init int probe_chip_type(struct legacy_probe *probe)
|
|
{
|
|
int mask = 1 << probe->slot;
|
|
|
|
if (autospeed & mask)
|
|
return SNOOP;
|
|
return BIOS;
|
|
}
|
|
|
|
|
|
/**
|
|
* legacy_init_one - attach a legacy interface
|
|
* @probe: probe record
|
|
*
|
|
* Register an ISA bus IDE interface. Such interfaces are PIO and we
|
|
* assume do not support IRQ sharing.
|
|
*/
|
|
|
|
static __init int legacy_init_one(struct legacy_probe *probe)
|
|
{
|
|
struct legacy_controller *controller = &controllers[probe->type];
|
|
int pio_modes = controller->pio_mask;
|
|
unsigned long io = probe->port;
|
|
u32 mask = (1 << probe->slot);
|
|
struct ata_port_operations *ops = controller->ops;
|
|
struct legacy_data *ld = &legacy_data[probe->slot];
|
|
struct ata_host *host = NULL;
|
|
struct ata_port *ap;
|
|
struct platform_device *pdev;
|
|
struct ata_device *dev;
|
|
void __iomem *io_addr, *ctrl_addr;
|
|
u32 iordy = (iordy_mask & mask) ? 0: ATA_FLAG_NO_IORDY;
|
|
int ret;
|
|
|
|
iordy |= controller->flags;
|
|
|
|
pdev = platform_device_register_simple(DRV_NAME, probe->slot, NULL, 0);
|
|
if (IS_ERR(pdev))
|
|
return PTR_ERR(pdev);
|
|
|
|
ret = -EBUSY;
|
|
if (devm_request_region(&pdev->dev, io, 8, "pata_legacy") == NULL ||
|
|
devm_request_region(&pdev->dev, io + 0x0206, 1,
|
|
"pata_legacy") == NULL)
|
|
goto fail;
|
|
|
|
ret = -ENOMEM;
|
|
io_addr = devm_ioport_map(&pdev->dev, io, 8);
|
|
ctrl_addr = devm_ioport_map(&pdev->dev, io + 0x0206, 1);
|
|
if (!io_addr || !ctrl_addr)
|
|
goto fail;
|
|
ld->type = probe->type;
|
|
if (controller->setup)
|
|
if (controller->setup(pdev, probe, ld) < 0)
|
|
goto fail;
|
|
host = ata_host_alloc(&pdev->dev, 1);
|
|
if (!host)
|
|
goto fail;
|
|
ap = host->ports[0];
|
|
|
|
ap->ops = ops;
|
|
ap->pio_mask = pio_modes;
|
|
ap->flags |= ATA_FLAG_SLAVE_POSS | iordy;
|
|
ap->pflags |= controller->pflags;
|
|
ap->ioaddr.cmd_addr = io_addr;
|
|
ap->ioaddr.altstatus_addr = ctrl_addr;
|
|
ap->ioaddr.ctl_addr = ctrl_addr;
|
|
ata_sff_std_ports(&ap->ioaddr);
|
|
ap->host->private_data = ld;
|
|
|
|
ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", io, io + 0x0206);
|
|
|
|
ret = ata_host_activate(host, probe->irq, ata_sff_interrupt, 0,
|
|
&legacy_sht);
|
|
if (ret)
|
|
goto fail;
|
|
async_synchronize_full();
|
|
ld->platform_dev = pdev;
|
|
|
|
/* Nothing found means we drop the port as its probably not there */
|
|
|
|
ret = -ENODEV;
|
|
ata_for_each_dev(dev, &ap->link, ALL) {
|
|
if (!ata_dev_absent(dev)) {
|
|
legacy_host[probe->slot] = host;
|
|
ld->platform_dev = pdev;
|
|
return 0;
|
|
}
|
|
}
|
|
ata_host_detach(host);
|
|
fail:
|
|
platform_device_unregister(pdev);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* legacy_check_special_cases - ATA special cases
|
|
* @p: PCI device to check
|
|
* @primary: set this if we find an ATA master
|
|
* @secondary: set this if we find an ATA secondary
|
|
*
|
|
* A small number of vendors implemented early PCI ATA interfaces
|
|
* on bridge logic without the ATA interface being PCI visible.
|
|
* Where we have a matching PCI driver we must skip the relevant
|
|
* device here. If we don't know about it then the legacy driver
|
|
* is the right driver anyway.
|
|
*/
|
|
|
|
static void __init legacy_check_special_cases(struct pci_dev *p, int *primary,
|
|
int *secondary)
|
|
{
|
|
/* Cyrix CS5510 pre SFF MWDMA ATA on the bridge */
|
|
if (p->vendor == 0x1078 && p->device == 0x0000) {
|
|
*primary = *secondary = 1;
|
|
return;
|
|
}
|
|
/* Cyrix CS5520 pre SFF MWDMA ATA on the bridge */
|
|
if (p->vendor == 0x1078 && p->device == 0x0002) {
|
|
*primary = *secondary = 1;
|
|
return;
|
|
}
|
|
/* Intel MPIIX - PIO ATA on non PCI side of bridge */
|
|
if (p->vendor == 0x8086 && p->device == 0x1234) {
|
|
u16 r;
|
|
pci_read_config_word(p, 0x6C, &r);
|
|
if (r & 0x8000) {
|
|
/* ATA port enabled */
|
|
if (r & 0x4000)
|
|
*secondary = 1;
|
|
else
|
|
*primary = 1;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* legacy_init - attach legacy interfaces
|
|
*
|
|
* Attach legacy IDE interfaces by scanning the usual IRQ/port suspects.
|
|
* Right now we do not scan the ide0 and ide1 address but should do so
|
|
* for non PCI systems or systems with no PCI IDE legacy mode devices.
|
|
* If you fix that note there are special cases to consider like CS5510/20.
|
|
*/
|
|
|
|
static __init int legacy_init(void)
|
|
{
|
|
int i;
|
|
int ct = 0;
|
|
int primary = 0;
|
|
int secondary = 0;
|
|
int pci_present = 0;
|
|
struct legacy_probe *pl = &probe_list[0];
|
|
int slot = 0;
|
|
|
|
struct pci_dev *p = NULL;
|
|
|
|
for_each_pci_dev(p) {
|
|
int r;
|
|
/* Check for any overlap of the system ATA mappings. Native
|
|
mode controllers stuck on these addresses or some devices
|
|
in 'raid' mode won't be found by the storage class test */
|
|
for (r = 0; r < 6; r++) {
|
|
if (pci_resource_start(p, r) == 0x1f0)
|
|
primary = 1;
|
|
if (pci_resource_start(p, r) == 0x170)
|
|
secondary = 1;
|
|
}
|
|
/* Check for special cases */
|
|
legacy_check_special_cases(p, &primary, &secondary);
|
|
|
|
/* If PCI bus is present then don't probe for tertiary
|
|
legacy ports */
|
|
pci_present = 1;
|
|
}
|
|
|
|
if (primary == 0 || all)
|
|
legacy_probe_add(0x1F0, 14, UNKNOWN, 0);
|
|
if (secondary == 0 || all)
|
|
legacy_probe_add(0x170, 15, UNKNOWN, 0);
|
|
|
|
if (probe_all || !pci_present) {
|
|
/* ISA extra ports */
|
|
legacy_probe_add(0x1E8, 11, UNKNOWN, 0);
|
|
legacy_probe_add(0x168, 10, UNKNOWN, 0);
|
|
legacy_probe_add(0x1E0, 8, UNKNOWN, 0);
|
|
legacy_probe_add(0x160, 12, UNKNOWN, 0);
|
|
}
|
|
|
|
for (i = 0; i < NR_HOST; i++, pl++) {
|
|
if (pl->port == 0)
|
|
continue;
|
|
if (pl->type == UNKNOWN)
|
|
pl->type = probe_chip_type(pl);
|
|
pl->slot = slot++;
|
|
if (legacy_init_one(pl) == 0)
|
|
ct++;
|
|
}
|
|
if (ct != 0)
|
|
return 0;
|
|
return -ENODEV;
|
|
}
|
|
|
|
static __exit void legacy_exit(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NR_HOST; i++) {
|
|
struct legacy_data *ld = &legacy_data[i];
|
|
|
|
if (legacy_host[i])
|
|
ata_host_detach(legacy_host[i]);
|
|
platform_device_unregister(ld->platform_dev);
|
|
}
|
|
}
|
|
|
|
MODULE_AUTHOR("Alan Cox");
|
|
MODULE_DESCRIPTION("low-level driver for legacy ATA");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(DRV_VERSION);
|
|
|
|
module_init(legacy_init);
|
|
module_exit(legacy_exit);
|