diff mbox

pata_pcmcia: add EBSA110's PCMCIA slot support

Message ID 3458240.9SL7iGif3L@amdc3058 (mailing list archive)
State New, archived
Headers show

Commit Message

Bartlomiej Zolnierkiewicz March 14, 2017, 5:50 p.m. UTC
Add EBSA110's PCMCIA slot support.

Cc: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
---
Cross compile tested only.

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

Comments

Tejun Heo March 16, 2017, 9:26 p.m. UTC | #1
Hello,

On Tue, Mar 14, 2017 at 06:50:43PM +0100, Bartlomiej Zolnierkiewicz wrote:
> +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,
> +};

Heh, that's a fat driver for a sff device.  I suppose this is mostly
copied from the matching ide driver but it'd be nice to explain why it
needs duplicate most standard functions.  Is it because PCMCIA
assigned address doesn't fall under the usual read/io boundary on the
arch?

Thanks.
Bartlomiej Zolnierkiewicz March 17, 2017, 11:59 a.m. UTC | #2
Hi,

On Thursday, March 16, 2017 05:26:54 PM Tejun Heo wrote:
> Hello,
> 
> On Tue, Mar 14, 2017 at 06:50:43PM +0100, Bartlomiej Zolnierkiewicz wrote:
> > +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,
> > +};
> 
> Heh, that's a fat driver for a sff device.  I suppose this is mostly
> copied from the matching ide driver but it'd be nice to explain why it
> needs duplicate most standard functions.  Is it because PCMCIA
> assigned address doesn't fall under the usual read/io boundary on the
> arch?

There is no support for this device in the upstream ide driver but
Russell has a hacky patch to make it work by redefining inb()/outb()
operations globally for the whole ide subsystem, please see:

https://www.spinics.net/lists/arm-kernel/msg567454.html

and

arch/arm/mach-ebsa110/io.c

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
Tejun Heo March 17, 2017, 1:39 p.m. UTC | #3
Hello,

On Fri, Mar 17, 2017 at 12:59:56PM +0100, Bartlomiej Zolnierkiewicz wrote:
> There is no support for this device in the upstream ide driver but
> Russell has a hacky patch to make it work by redefining inb()/outb()
> operations globally for the whole ide subsystem, please see:
> 
> https://www.spinics.net/lists/arm-kernel/msg567454.html

Well, that mail says that the hardware is a museum piece.  What's the
point here?  This can't even be tested, can it?

Thanks.
Bartlomiej Zolnierkiewicz March 17, 2017, 2:08 p.m. UTC | #4
On Friday, March 17, 2017 09:39:00 AM Tejun Heo wrote:
> Hello,
> 
> On Fri, Mar 17, 2017 at 12:59:56PM +0100, Bartlomiej Zolnierkiewicz wrote:
> > There is no support for this device in the upstream ide driver but
> > Russell has a hacky patch to make it work by redefining inb()/outb()
> > operations globally for the whole ide subsystem, please see:
> > 
> > https://www.spinics.net/lists/arm-kernel/msg567454.html
> 
> Well, that mail says that the hardware is a museum piece.  What's the
> point here?  This can't even be tested, can it?

To be honest, the main point here is to make Russell happy. ;-)

This is the second time he brought EBSA110 issue while talking about
conversion of ARM platforms to use libata PATA so I've finally decided
to do the patch just to have a reference example of how to deal with
this and similar devices in a proper way.

Even though the patch can't be tested EBSA110 is still a supported ARM
platform and the patch should work fine in theory (+ it cannot break
anything).  OTOH it adds an extra code which has some cost from the
long-term maintenance POV.  Anyway, it is up to you to decide what to
do with it. :-)

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
Tejun Heo March 17, 2017, 2:13 p.m. UTC | #5
Hello,

On Fri, Mar 17, 2017 at 03:08:56PM +0100, Bartlomiej Zolnierkiewicz wrote:
> To be honest, the main point here is to make Russell happy. ;-)
> 
> This is the second time he brought EBSA110 issue while talking about
> conversion of ARM platforms to use libata PATA so I've finally decided
> to do the patch just to have a reference example of how to deal with
> this and similar devices in a proper way.
> 
> Even though the patch can't be tested EBSA110 is still a supported ARM
> platform and the patch should work fine in theory (+ it cannot break
> anything).  OTOH it adds an extra code which has some cost from the
> long-term maintenance POV.  Anyway, it is up to you to decide what to
> do with it. :-)

It's fairly low impact and I don't think it's gonna add noticeable
maintenance overhead at all, so that part is fine but it's super weird
to add code which we know not to have any users.  It looks like we
can't even test it.

What's the end game here?  Is it part of the effort to remove ide?  If
so, I have no objection to that but think it isn't a pressing issue
either and the ultimate decision has to come from Dave.

Thanks.
Bartlomiej Zolnierkiewicz March 17, 2017, 3:51 p.m. UTC | #6
On Friday, March 17, 2017 10:13:13 AM Tejun Heo wrote:
> Hello,
> 
> On Fri, Mar 17, 2017 at 03:08:56PM +0100, Bartlomiej Zolnierkiewicz wrote:
> > To be honest, the main point here is to make Russell happy. ;-)
> > 
> > This is the second time he brought EBSA110 issue while talking about
> > conversion of ARM platforms to use libata PATA so I've finally decided
> > to do the patch just to have a reference example of how to deal with
> > this and similar devices in a proper way.
> > 
> > Even though the patch can't be tested EBSA110 is still a supported ARM
> > platform and the patch should work fine in theory (+ it cannot break
> > anything).  OTOH it adds an extra code which has some cost from the
> > long-term maintenance POV.  Anyway, it is up to you to decide what to
> > do with it. :-)
> 
> It's fairly low impact and I don't think it's gonna add noticeable
> maintenance overhead at all, so that part is fine but it's super weird
> to add code which we know not to have any users.  It looks like we
> can't even test it.

Well, this is more general problem.  We keep things like ARM EBSA110
platform support in the kernel for some reason.. maybe we shouldn't?

> What's the end game here?  Is it part of the effort to remove ide?  If
> so, I have no objection to that but think it isn't a pressing issue
> either and the ultimate decision has to come from Dave.

I'm just helping in migrating users that are still using ide to libata,
enhancing libata's hardware support in the process. I would prefer to
have ide removed in the long-term (once there are no known users of
it) but Dave has a valid point (see below).  This particular patch is
more a by-product of the above work than a part of it itself.

Dave's current opinion is that we can't prove that all configurations
currently supported by ide are supported by libata.  Since it can never
be really proved (as there are ide drivers for hardware that cannot be
tested because of having no active users) he is fine with ide staying
in the kernel forever. Which may be not as bad for me personally as
I have a lot of contributions there. ;-)

Theoretically another option is to finish ide's evolution into "ide
compat" layer for SCSI/libata. This would give us full git history from
ide to libata support + thin compatibility layer. However with libata
PATA availability and hardware being obsolete there is no real need
for it and the cost to finish such transition is much higher than cost
of ide staying in the kernel forever. Moreover it would need to happen
outside of the upstream kernel and there are no guarantees that it
would be ever merged (ide is in deep maintenance policy and treated
as a "safe fallback" for libata; SCSI & libata changes would need to
be agreed on).

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
Bartlomiej Zolnierkiewicz March 20, 2017, 1 p.m. UTC | #7
On Friday, March 17, 2017 04:51:13 PM Bartlomiej Zolnierkiewicz wrote:

> > What's the end game here?  Is it part of the effort to remove ide?  If
> > so, I have no objection to that but think it isn't a pressing issue
> > either and the ultimate decision has to come from Dave.
> 
> I'm just helping in migrating users that are still using ide to libata,
> enhancing libata's hardware support in the process. I would prefer to
> have ide removed in the long-term (once there are no known users of
> it) but Dave has a valid point (see below).  This particular patch is
> more a by-product of the above work than a part of it itself.
> 
> Dave's current opinion is that we can't prove that all configurations
> currently supported by ide are supported by libata.  Since it can never
> be really proved (as there are ide drivers for hardware that cannot be
> tested because of having no active users) he is fine with ide staying
> in the kernel forever. Which may be not as bad for me personally as
> I have a lot of contributions there. ;-)

I've given some more thought on this over the weekend.

I completely agree with Dave's opinion now. He has a good understanding
of a long-term kernel development process (his opinions on ide were right
back in 2009 and 2005).

We keep support for hardware that has no active users "just in case" until
the maintenance cost becomes too high or it blocks (in major way) some new
developments.  This is not the case with ide (even with all active users
ported to libata we will still be left with some support for hardware which
can no longer be tested) and it should stay unless some of above conditions
change. My personal wish to have coherent PATA support in the kernel doesn't
really matter here.

I would apologize for being wrong on this and I'm sorry for waisting
people's time.

Coming back to the pata_pcmcia patch, Tejun, please just ignore it. It is
now in mailing list's archives and this is sufficient to reference it if
ever needed.

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
diff mbox

Patch

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 */