[PATCH] pata_pcmcia: add EBSA110's PCMCIA slot support

From: Bartlomiej Zolnierkiewicz
Date: Tue Mar 14 2017 - 13:50:59 EST



Add EBSA110's PCMCIA slot support.

Cc: Russell King <rmk+kernel@xxxxxxxxxxxxxxx>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@xxxxxxxxxxx>
---
Cross compile tested only.

drivers/ata/pata_pcmcia.c | 297 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 294 insertions(+), 3 deletions(-)

Index: b/drivers/ata/pata_pcmcia.c
===================================================================
--- a/drivers/ata/pata_pcmcia.c 2017-03-14 18:32:21.667263033 +0100
+++ b/drivers/ata/pata_pcmcia.c 2017-03-14 18:41:48.995277320 +0100
@@ -164,6 +164,286 @@ static struct ata_port_operations pcmcia
.sff_drain_fifo = pcmcia_8bit_drain_fifo,
};

+#ifdef CONFIG_ARCH_EBSA110
+static void pmcmia_ebsa110_dev_select(struct ata_port *ap, unsigned int device)
+{
+ u8 tmp;
+
+ if (device == 0)
+ tmp = ATA_DEVICE_OBS;
+ else
+ tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+ __outb16(tmp, (unsigned long)ap->ioaddr.device_addr);
+ ata_sff_pause(ap); /* needed; also flushes, for mmio */
+}
+
+static void pcmcia_ebsa110_set_devctl(struct ata_port *ap, u8 ctl)
+{
+ __outb16(ctl, (unsigned long)ap->ioaddr.ctl_addr);
+}
+
+static u8 pcmcia_ebsa110_check_status(struct ata_port *ap)
+{
+ return __inb16((unsigned long)ap->ioaddr.status_addr);
+}
+
+static u8 pcmcia_ebsa110_check_altstatus(struct ata_port *ap)
+{
+ return __inb16((unsigned long)ap->ioaddr.altstatus_addr);
+}
+
+static void pcmcia_ebsa110_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 (tf->ctl != ap->last_ctl) {
+ if (ioaddr->ctl_addr)
+ __outb16(tf->ctl, (unsigned long)ioaddr->ctl_addr);
+ ap->last_ctl = tf->ctl;
+ ata_wait_idle(ap);
+ }
+
+ if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+ WARN_ON_ONCE(!ioaddr->ctl_addr);
+ __outb16(tf->hob_feature, (unsigned long)ioaddr->feature_addr);
+ __outb16(tf->hob_nsect, (unsigned long)ioaddr->nsect_addr);
+ __outb16(tf->hob_lbal, (unsigned long)ioaddr->lbal_addr);
+ __outb16(tf->hob_lbam, (unsigned long)ioaddr->lbam_addr);
+ __outb16(tf->hob_lbah, (unsigned long)ioaddr->lbah_addr);
+ VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+ tf->hob_feature,
+ tf->hob_nsect,
+ tf->hob_lbal,
+ tf->hob_lbam,
+ tf->hob_lbah);
+ }
+
+ if (is_addr) {
+ __outb16(tf->feature, (unsigned long)ioaddr->feature_addr);
+ __outb16(tf->nsect, (unsigned long)ioaddr->nsect_addr);
+ __outb16(tf->lbal, (unsigned long)ioaddr->lbal_addr);
+ __outb16(tf->lbam, (unsigned long)ioaddr->lbam_addr);
+ __outb16(tf->lbah, (unsigned long)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) {
+ __outb16(tf->device, (unsigned long)ioaddr->device_addr);
+ VPRINTK("device 0x%X\n", tf->device);
+ }
+
+ ata_wait_idle(ap);
+}
+
+static void pcmcia_ebsa110_tf_read(struct ata_port *ap,
+ struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ tf->command = ata_sff_check_status(ap);
+ tf->feature = __inb16((unsigned long)ioaddr->error_addr);
+ tf->nsect = __inb16((unsigned long)ioaddr->nsect_addr);
+ tf->lbal = __inb16((unsigned long)ioaddr->lbal_addr);
+ tf->lbam = __inb16((unsigned long)ioaddr->lbam_addr);
+ tf->lbah = __inb16((unsigned long)ioaddr->lbah_addr);
+ tf->device = __inb16((unsigned long)ioaddr->device_addr);
+
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ if (likely(ioaddr->ctl_addr)) {
+ __outb16(tf->ctl | ATA_HOB, (unsigned long)ioaddr->ctl_addr);
+ tf->hob_feature = __inb16((unsigned long)ioaddr->error_addr);
+ tf->hob_nsect = __inb16((unsigned long)ioaddr->nsect_addr);
+ tf->hob_lbal = __inb16((unsigned long)ioaddr->lbal_addr);
+ tf->hob_lbam = __inb16((unsigned long)ioaddr->lbam_addr);
+ tf->hob_lbah = __inb16((unsigned long)ioaddr->lbah_addr);
+ __outb16(tf->ctl, (unsigned long)ioaddr->ctl_addr);
+ ap->last_ctl = tf->ctl;
+ } else
+ WARN_ON_ONCE(1);
+ }
+}
+
+static void pcmcia_ebsa110_exec_command(struct ata_port *ap,
+ const struct ata_taskfile *tf)
+{
+ DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command);
+
+ __outb16(tf->command, (unsigned long)ap->ioaddr.command_addr);
+ ata_sff_pause(ap);
+}
+
+static unsigned int pata_pcmcia_ebsa110_devchk(struct ata_port *ap,
+ unsigned int device)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ u8 nsect, lbal;
+
+ ap->ops->sff_dev_select(ap, device);
+
+ __outb16(0x55, (unsigned long)ioaddr->nsect_addr);
+ __outb16(0xaa, (unsigned long)ioaddr->lbal_addr);
+
+ __outb16(0xaa, (unsigned long)ioaddr->nsect_addr);
+ __outb16(0x55, (unsigned long)ioaddr->lbal_addr);
+
+ __outb16(0x55, (unsigned long)ioaddr->nsect_addr);
+ __outb16(0xaa, (unsigned long)ioaddr->lbal_addr);
+
+ nsect = __inb16((unsigned long)ioaddr->nsect_addr);
+ lbal = __inb16((unsigned long)ioaddr->lbal_addr);
+
+ if ((nsect == 0x55) && (lbal == 0xaa))
+ return 1; /* we found a device */
+
+ return 0; /* nothing found */
+}
+
+static int pata_pcmcia_ebsa110_wait_after_reset(struct ata_link *link,
+ unsigned int devmask,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int dev0 = devmask & (1 << 0);
+ unsigned int dev1 = devmask & (1 << 1);
+ int rc, ret = 0;
+
+ ata_msleep(ap, ATA_WAIT_AFTER_RESET);
+
+ /* always check readiness of the master device */
+ rc = ata_sff_wait_ready(link, deadline);
+ /* -ENODEV means the odd clown forgot the D7 pulldown resistor
+ * and TF status is 0xff, bail out on it too.
+ */
+ if (rc)
+ return rc;
+
+ /* if device 1 was found in ata_devchk, wait for register
+ * access briefly, then wait for BSY to clear.
+ */
+ if (dev1) {
+ int i;
+
+ ap->ops->sff_dev_select(ap, 1);
+
+ /* Wait for register access. Some ATAPI devices fail
+ * to set nsect/lbal after reset, so don't waste too
+ * much time on it. We're gonna wait for !BSY anyway.
+ */
+ for (i = 0; i < 2; i++) {
+ u8 nsect, lbal;
+
+ nsect = __inb16((unsigned long)ioaddr->nsect_addr);
+ lbal = __inb16((unsigned long)ioaddr->lbal_addr);
+ if ((nsect == 1) && (lbal == 1))
+ break;
+ ata_msleep(ap, 50); /* give drive a breather */
+ }
+
+ rc = ata_sff_wait_ready(link, deadline);
+ if (rc) {
+ if (rc != -ENODEV)
+ return rc;
+ ret = rc;
+ }
+ }
+
+ /* is all this really necessary? */
+ ap->ops->sff_dev_select(ap, 0);
+ if (dev1)
+ ap->ops->sff_dev_select(ap, 1);
+ if (dev0)
+ ap->ops->sff_dev_select(ap, 0);
+
+ return ret;
+}
+
+static int pata_pcmcia_ebsa110_bus_softreset(struct ata_port *ap,
+ unsigned int devmask,
+ unsigned long deadline)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ DPRINTK("ata%u: bus reset via SRST\n", ap->print_id);
+
+ /* software reset. causes dev0 to be selected */
+ __outb16(ap->ctl, (unsigned long)ioaddr->ctl_addr);
+ udelay(20); /* FIXME: flush */
+ __outb16(ap->ctl | ATA_SRST, (unsigned long)ioaddr->ctl_addr);
+ udelay(20); /* FIXME: flush */
+ __outb16(ap->ctl, (unsigned long)ioaddr->ctl_addr);
+ ap->last_ctl = ap->ctl;
+
+ /* wait the port to become ready */
+ return pata_pcmcia_ebsa110_wait_after_reset(&ap->link, devmask,
+ deadline);
+}
+
+static int pata_pcmcia_ebsa110_softreset(struct ata_link *link,
+ unsigned int *classes,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
+ unsigned int devmask = 0;
+ int rc;
+ u8 err;
+
+ DPRINTK("ENTER\n");
+
+ /* determine if device 0/1 are present */
+ if (pata_pcmcia_ebsa110_devchk(ap, 0))
+ devmask |= (1 << 0);
+ if (slave_possible && pata_pcmcia_ebsa110_devchk(ap, 1))
+ devmask |= (1 << 1);
+
+ /* select device 0 again */
+ ap->ops->sff_dev_select(ap, 0);
+
+ /* issue bus reset */
+ DPRINTK("about to softreset, devmask=%x\n", devmask);
+ rc = pata_pcmcia_ebsa110_bus_softreset(ap, devmask, deadline);
+ /* if link is occupied, -ENODEV too is an error */
+ if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
+ ata_link_err(link, "SRST failed (errno=%d)\n", rc);
+ return rc;
+ }
+
+ /* determine by signature whether we have ATA or ATAPI devices */
+ classes[0] = ata_sff_dev_classify(&link->device[0],
+ devmask & (1 << 0), &err);
+ if (slave_possible && err != 0x81)
+ classes[1] = ata_sff_dev_classify(&link->device[1],
+ devmask & (1 << 1), &err);
+
+ DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]);
+ return 0;
+}
+
+static struct ata_port_operations pcmcia_ebsa110_port_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_dev_select = pmcmia_ebsa110_dev_select,
+ .sff_set_devctl = pcmcia_ebsa110_set_devctl,
+ .sff_check_status = pcmcia_ebsa110_check_status,
+ .sff_check_altstatus = pcmcia_ebsa110_check_altstatus,
+ .sff_tf_load = pcmcia_ebsa110_tf_load,
+ .sff_tf_read = pcmcia_ebsa110_tf_read,
+ .sff_exec_command = pcmcia_ebsa110_exec_command,
+ .sff_data_xfer = ata_sff_data_xfer_noirq,
+ .softreset = pata_pcmcia_ebsa110_softreset,
+ .cable_detect = ata_cable_40wire,
+ .set_mode = pcmcia_set_mode,
+};
+#include <asm/mach-types.h>
+#endif

static int pcmcia_check_one_config(struct pcmcia_device *pdev, void *priv_data)
{
@@ -242,9 +522,20 @@ static int pcmcia_init_one(struct pcmcia
goto failed;

/* Success. Disable the IRQ nIEN line, do quirks */
- iowrite8(0x02, ctl_addr);
- if (is_kme)
- iowrite8(0x81, ctl_addr + 0x01);
+#ifdef CONFIG_ARCH_EBSA110
+ if (machine_is_ebsa110()) {
+ ops = &pcmcia_ebsa110_port_ops;
+
+ __outb16(0x02, (unsigned long)ctl_addr);
+ if (is_kme)
+ __outb16(0x81, (unsigned long)ctl_addr + 0x01);
+ } else
+#endif
+ {
+ iowrite8(0x02, ctl_addr);
+ if (is_kme)
+ iowrite8(0x81, ctl_addr + 0x01);
+ }

/* FIXME: Could be more ports at base + 0x10 but we only deal with
one right now */