> > So in theory we can persuade libata to drive original MFM/RLL disks with > > relatively few changes > > Crazy :) Would look something like this (but with geometry handling and some setup work) /** * An original IDE/ST412/ST506 driver for libata * * Information for hardware archeologists * - IDE is the original pre ATA and 'EIDE' specification interface * emulating ST412 with some oddments nailed on * - ST412 is the normal PC/AT attachment * - ST506 is the prehistoric version * * This driver will not (currently) handle ST506 unless you set the * precomp value. Nor will it handle the original '8 head' protocol, nor * drives not capable of the 35uS stepper rate. I might fix this for * fun one day if I can find an old enough drive that still works * * How we work the compatibility ATA to ST412 * ------------------------------------------ * * Mostly this works as pass through to libata as IDE and ATA were * designed to be compatible in the IDE->ATA direction. The ST412 * interface ctrl register maps to the ATA ctl register (which is why it * has lots of 'obsolete' bits). nIEN and SRST map to the IRQ mask * bit on the controller and the controller soft reset. * * Original IDE works on the same command set as ST412 as far as we * care (we don't do formatting etc). Some later IDE drives insist * that we tell them their geometry (Initialize Drive) and they also * support better error handling (DIAGNOSE), but we can do that as * it'll just get rejected by the MFM era controller. * * The drive select is also cunning arranged so that '512 bytes with ECC' * to the controller is in fact 0xA0 (aka the 'obsolete' bits in ATA) * * The big limit on original IDE is that only the BIOS knows the c/h/s * parameters for the drive. For MFM/RLL you can sneak a peek at the * partition table and guess but not alas for early IDE as it requires * you set the geometry before it'll let you look! * * ST506 * ----- * * ST506 requires we set 8 v 16 heads correctly and that we provide * write precomp and a couple of other additional values. If you have * a specific application involving rescuing such an ancient disk in * a lab somewhere then let me know, although its probably easier and * safer to read a disk that old in a data recovery lab */ #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/libata.h> #include <linux/platform_device.h> #define DRV_NAME "pata_hd" #define DRV_VERSION "0.0.1" #define NR_HOST 2 struct platform_device *atahd_device[NR_HOST]; static struct ata_host *atahd_host[NR_HOST]; struct atahd_disk { /* Geometry for faking IDENTIFY */ u16 cyls; u8 heads; u8 sectors; /* Not currently supported - 35uS always used */ u8 seekrate; /* Precompensation for older ST506 */ u8 wprecomp; unsigned int legacy:1; /* Drive is actually ATA-1+ */ unsigned int ide:1; /* Drive is original IDE */ unsigned int lba:1; /* Original IDE in LBA mode */ u8 bios_ident; /* BIOS id of drive */ } static struct atahd_param { struct atahd_disk param[2]; /* Drive parameters */ int device; /* Tracking current device */ int present; /* Mask of present devices */ }; static struct atahd_param atahd_param[NR_HOST]; static int nr_atahd_host; static int bios_ident = 0x80; /** * atahd_tf_load - send taskfile registers to host controller * @ap: Port to which output is sent * @tf: ATA taskfile register set * * Outputs ATA taskfile to ST506/414 host controller. * * LOCKING: * Inherited from caller. */ void atahd_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; if (!p->param[p->device].legacy) { ata_tf_load(ap, tf); return; } if (tf->ctl != ap->last_ctl) { iowrite8(tf->ctl, ioaddr->ctl_addr); ap->last_ctl = tf->ctl; ata_wait_idle(ap); } if (is_addr) { /* ST-506 requires the precomp value is loaded, ST-412, IDE and later it is done by the drive. This causes no problem for ST-506 driving IDE but for IDE driving ST we need to fill in the blanks. Later ST-506 drives don't generally use the hardware precomp signal either */ if (p->param[p->device].wprecomp) iowrite8(p->param[p.device].wprecomp, ioaddr->feature_addr); else iowrite8(tf->feature, ioaddr->feature_addr); iowrite8(tf->nsect, ioaddr->nsect_addr); iowrite8(tf->lbal, ioaddr->lbal_addr); iowrite8(tf->lbam, ioaddr->lbam_addr); iowrite8(tf->lbah, ioaddr->lbah_addr); VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n", tf->feature, tf->nsect, tf->lbal, tf->lbam, tf->lbah); } if (tf->flags & ATA_TFLAG_DEVICE) { iowrite8(tf->device, ioaddr->device_addr); VPRINTK("device 0x%X\n", tf->device); } ata_wait_idle(ap); } /** * atahd_data_xfer - Transfer data by PIO * @adev: device to target * @buf: data buffer * @buflen: buffer length * @write_data: read/write * * Transfer data from/to the device data register by PIO. * * LOCKING: * Inherited from caller. */ static int atahd_data_xfer(struct ata_device *adev, unsigned char *buf, unsigned int buflen, int write_data) { int r = 0; unsigned long flags; local_irq_save(flags); r = ata_data_xfer(adev, buf, buflen, write_data); local_irq_restore(flags); return r; } #if 0 static void atahd_id_data() { BUG_ON(write_data); memset(buf, 0, 512); id[0] = 0x8000; /* ATA */ id[1] = d->cyls; id[2] = 0xC837; id[3] = d->heads; id[6] = d->sectors; memset(id + 10, " ", 20); /* FIXME: Set some kind of unique serial */ memcpy(id + 23, "ATAHD001", 8); memcpy(id + 27, "ATA HD EMULATION OF MFM/RLL ", 40); id[47] = 0x8000; id[49] = 0x30; } #endif static unigned int atahd_emulate_id(struct ata_queued_cmd *qc) { struct atahd_param *p = qc->ap->private_data; u16 *id = kzalloc(512, GFP_ATOMIC); if (id == NULL) return AC_ERR_INVALID; ata_dev_select(qc->ap, qc->dev->devno, 1, 0); /* Fill in the data block */ ata_qc_complete(qc); kfree(id); return 0; } static unsigned int atahd_qc_issue_prot(struct ata_queued_cmd *qc) { struct atahd_param *p = qc->ap->private_data; /* Non legacy devices need no care and attention */ if (!p->param[p->device].legacy) return ata_qc_issue_prot(qc); /* ST412/ST506 controller path [or early equivalen IDE] */ switch(qc->tf.command) { /* Don't do read/write multi - the 1010 can do it but we've no idea how much SRAM is on the board */ case ATA_CMD_PIO_READ: case ATA_CMD_PIO_WRITE: case ATA_CMD_VERIFY: case 0x10: /* pre-ATA command 0x10: Restore */ return ata_qc_issue_prot(qc); /* Must fake */ case ATA_CMD_ID_ATA: return atahd_emulate_id(qc); /* May work on some very early IDE but will abort on ST412 which is fine */ case ATA_CMD_INIT_DEV_PARAMS: return ata_qc_issue_prot(qc); /* Also internally the controller supports formatting etc */ default: return AC_ERR_INVALID; } } static struct scsi_host_template atahd_sht = { .module = THIS_MODULE, .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .can_queue = ATA_DEF_QUEUE, .this_id = ATA_SHT_THIS_ID, .sg_tablesize = LIBATA_MAX_PRD, .cmd_per_lun = ATA_SHT_CMD_PER_LUN, .emulated = ATA_SHT_EMULATED, .use_clustering = ATA_SHT_USE_CLUSTERING, .proc_name = DRV_NAME, .dma_boundary = ATA_DMA_BOUNDARY, .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, }; static struct ata_port_operations atahd_port_ops = { .port_disable = ata_port_disable, .tf_load = atahd_tf_load, .tf_read = ata_tf_read, .check_status = atahd_check_status, .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = atahd_qc_issue_prot, .data_xfer = atahd_data_xfer, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, .port_start = ata_sff_port_start, }; /** * st416_poll - run polled command * @port; I/O port base * @cmd command byte * * Issue a polled no-data command to the ST412 interface. We * cannot do register games here. An ST412 has the registers on * the controller not on the drive. Instead we issue command 0x10 * 'RESTORE, 35uS stepping'. A working ST412 drive will assert TRKZ * and the command finish. A working ATA drive may either nop the * command or return aborted (as its a retired command). A bust ST412 * drive or missing device will timeout or finish the steppjng and * fail to assert TRKZ * * This code path is only used during booting and early probing. */ static int st416_poll(unsigned long port, u8 cmd, u8 *err) { u8 status; u8 err; unsigned long timeout = jiffies + 5 * HZ; iowrite8(cmd, port + ATA_REG_CMD); ndelay(400); *err = 0; while (time_before(jiffies, timeout)) { status = ioread8(port + ATA_REG_STATUS); if ((status & ATA_BUSY) == 0) { if (status & ATA_ERR) { *err = ioread8(port + ATA_REG_ERR); /* Aborted the command */ if (err & ATA_ABORTED) return 0; /* Drive didn't step back to track zero - missing or bust */ if (*err & 0x01) return -ENODEV; return 0; } return 0; } } return -ENODEV; } /* * Probe sequence for ST412 and later devices * * Firstly we select the device. It should respond. If it does not * then it is not present (ST412). We then issue the RESTORE command * which is retired pre-ATA. ATA will either no-op it, or abort it. * ST412 will track the drive back to track zero. If this fails with * a timeout or TRKZ then the drive is bust or missing. * * Thus * 0x10 ABORT -> ATA * 0x10 no TRKZ -> Bust/Missing * 0x10 OK -> ST412 or ATA * * We then issue 0x90 which is mandatory for all ATA * * 0x90 ABORT -> ST412/Early IDE * 0x90 OK -> Probably ATA * * If libata gains the ability to handle IDENTIFY being refused (eg * for early ATA) we can skip everything past the TRKZ check */ static int st416_probe_drive(unsigned long io, int dev, struct atahd_param *p) { unsigned long timeout = jiffies + 7 * HZ; p->param[dev].legacty = 0; /* ST506/412: Restore 512 bytes with ECC setting ATA: This sequence is intentionally the ATA drive select */ if (dev) iowrite8(ATA_DEVICE_OBS | ATA_DEV1, io + ATA_REG_DEVICE); iowrite8(ATA_DEVICE_OBS, io + ATA_REG_DEVICE); while (time_before(jiffies, timeout)) { status = ioread8(port + ATA_REG_STATUS); if ((status & (ATA_DRDY|ATA_BUSY)) == ATA_DRDY) { /* ST506/412: Set the seek stepper: Restore drive 0, 35uS step IDE: Harmless no-op */ if (st416_poll(io, 0x10, &err) == 0) { /* Try issuing EDD - mandatory for all ATA but causes an abort with ST412 / early IDE */ if (st416_poll(io, 0x90, &err) == 0) { if (err & ATA_ABORTED) p->param[dev].legacy = 1; } p->present |= 1 << dev; p->param[dev].bios_ident = bios_ident++; return 0; } return -ENODEV; } } return -ETIMEDOUT; } static void st416_setup(unsigned long io, unsigned long ctl, struct atahd_param *p) { u8 err; /* Reset the ST506/412 controller: For ATA this does a reset on the attached devices but the rest of the logic is the same */ iowrite8(ATA_NIEN|ATA_SRST, ctl); msleep(10); iowrite8(ATA_NIEN, ctl); st416_probe_drive(io, 0, p); st416_probe_drive(io, 1, p); } /** * atahd_init_one - attach a qdi interface * @io: I/O port start * @irq: interrupt line * * Register an ISA bus IDE interface. Such interfaces are PIO and we * assume do not support IRQ sharing. */ static __init int atahd_init_one(unsigned long port, unsigned long io, int irq) { struct platform_device *pdev; struct ata_host *host; struct ata_port *ap; void __iomem *io_addr, *ctl_addr; int ret; /* * Look for a controller. It should SRST and respond sanely * to a RESTORE command. Should do more checks here and in * pata_legacy for controller presence. */ if (st416_setup(io_addr, ctl_addr, &atahd_param[nr_atahd_host]) < 0) return 0; /* * Fill in a probe structure first of all */ pdev = platform_device_register_simple(DRV_NAME, nr_atahd_host, NULL, 0); if (IS_ERR(pdev)) return PTR_ERR(pdev); ret = -ENOMEM; io_addr = devm_ioport_map(&pdev->dev, io, 8); ctl_addr = devm_ioport_map(&pdev->dev, io + 0x206, 1); if (!io_addr || !ctl_addr) goto fail; ret = -ENOMEM; host = ata_host_alloc(&pdev->dev, 1); if (!host) goto fail; ap = host->ports[0]; ap->ops = &atahd_port_ops; ap->pio_mask = 0x01; ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY; ap->ioaddr.cmd_addr = io_addr; ap->ioaddr.altstatus_addr = ctl_addr; ap->ioaddr.ctl_addr = ctl_addr; ata_std_ports(&ap->ioaddr); /* activate */ ret = ata_host_activate(host, irq, ata_interrupt, 0, &atahd_sht); if (ret) goto fail; ap->private_data = &atahd_param[nr_atahd_host]; atahd_host[nr_atahd_host++] = host; return 0; fail: platform_device_unregister(pdev); return ret; } /** * atahd_init - attach qdi interfaces * * Attach qdi IDE interfaces by scanning the ports it may occupy. */ static __init int atahd_init(void) { unsigned long flags; static const unsigned long atahd_port[2] = { 0x170, 0x1F0 }; static const int atahd_irq[2] = { 14, 15 }; int ct = 0; int i; for (i = 0; i < 2; i++) { unsigned long port = atahd_port[i]; if (atahd_init_one(port, port + 0x206, atahd_irq[i]) == 0) ct++; } if (ct != 0) return 0; return -ENODEV; } static __exit void atahd_exit(void) { int i; for (i = 0; i < nr_atahd_host; i++) { ata_host_detach(atahd_host[i]); platform_device_unregister(atahd_device[i]); } } MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for PC/AT hard disk"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); module_init(atahd_init); module_exit(atahd_exit); - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html