summaryrefslogtreecommitdiffstats
path: root/drivers/ata/pata_parport
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
commitace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch)
treeb2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/ata/pata_parport
parentInitial commit. (diff)
downloadlinux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz
linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/ata/pata_parport')
-rw-r--r--drivers/ata/pata_parport/Kconfig141
-rw-r--r--drivers/ata/pata_parport/Makefile19
-rw-r--r--drivers/ata/pata_parport/aten.c144
-rw-r--r--drivers/ata/pata_parport/bpck.c507
-rw-r--r--drivers/ata/pata_parport/bpck6.c464
-rw-r--r--drivers/ata/pata_parport/comm.c205
-rw-r--r--drivers/ata/pata_parport/dstr.c235
-rw-r--r--drivers/ata/pata_parport/epat.c365
-rw-r--r--drivers/ata/pata_parport/epia.c312
-rw-r--r--drivers/ata/pata_parport/fit2.c138
-rw-r--r--drivers/ata/pata_parport/fit3.c189
-rw-r--r--drivers/ata/pata_parport/friq.c264
-rw-r--r--drivers/ata/pata_parport/frpw.c298
-rw-r--r--drivers/ata/pata_parport/kbic.c308
-rw-r--r--drivers/ata/pata_parport/ktti.c111
-rw-r--r--drivers/ata/pata_parport/on20.c147
-rw-r--r--drivers/ata/pata_parport/on26.c315
-rw-r--r--drivers/ata/pata_parport/pata_parport.c836
-rw-r--r--drivers/ata/pata_parport/pata_parport.h96
19 files changed, 5094 insertions, 0 deletions
diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
new file mode 100644
index 0000000000..2c953f5d13
--- /dev/null
+++ b/drivers/ata/pata_parport/Kconfig
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier: GPL-2.0
+
+comment "Parallel IDE protocol modules"
+ depends on PATA_PARPORT
+
+config PATA_PARPORT_ATEN
+ tristate "ATEN EH-100 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the ATEN EH-100 parallel port IDE
+ protocol. This protocol is used in some inexpensive low performance
+ parallel port kits made in Hong Kong.
+
+config PATA_PARPORT_BPCK
+ tristate "MicroSolutions backpack (Series 5) protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the Micro Solutions BACKPACK
+ parallel port Series 5 IDE protocol. (Most BACKPACK drives made
+ before 1999 were Series 5) Series 5 drives will NOT always have the
+ Series noted on the bottom of the drive. Series 6 drivers will.
+
+ In other words, if your BACKPACK drive doesn't say "Series 6" on the
+ bottom, enable this option.
+
+config PATA_PARPORT_BPCK6
+ tristate "MicroSolutions backpack (Series 6) protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the Micro Solutions BACKPACK
+ parallel port Series 6 IDE protocol. (Most BACKPACK drives made
+ after 1999 were Series 6) Series 6 drives will have the Series noted
+ on the bottom of the drive. Series 5 drivers don't always have it
+ noted.
+
+ In other words, if your BACKPACK drive says "Series 6" on the
+ bottom, enable this option.
+
+config PATA_PARPORT_COMM
+ tristate "DataStor Commuter protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the Commuter parallel port IDE
+ protocol from DataStor.
+
+config PATA_PARPORT_DSTR
+ tristate "DataStor EP-2000 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the EP-2000 parallel port IDE
+ protocol from DataStor
+
+config PATA_PARPORT_FIT2
+ tristate "FIT TD-2000 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the TD-2000 parallel port IDE
+ protocol from Fidelity International Technology. This is a simple
+ (low speed) adapter that is used in some portable hard drives.
+
+config PATA_PARPORT_FIT3
+ tristate "FIT TD-3000 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the TD-3000 parallel port IDE
+ protocol from Fidelity International Technology. This protocol is
+ used in newer models of their portable disk, CD-ROM and PD/CD
+ devices.
+
+config PATA_PARPORT_EPAT
+ tristate "Shuttle EPAT/EPEZ protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the EPAT parallel port IDE protocol.
+ EPAT is a parallel port IDE adapter manufactured by Shuttle
+ Technology and widely used in devices from major vendors such as
+ Hewlett-Packard, SyQuest, Imation and Avatar.
+
+config PATA_PARPORT_EPATC8
+ bool "Support c7/c8 chips"
+ depends on PATA_PARPORT_EPAT
+ help
+ This option enables support for the newer Shuttle EP1284 (aka c7 and
+ c8) chip. You need this if you are using any recent Imation SuperDisk
+ (LS-120) drive.
+
+config PATA_PARPORT_EPIA
+ tristate "Shuttle EPIA protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the (obsolete) EPIA parallel port
+ IDE protocol from Shuttle Technology. This adapter can still be
+ found in some no-name kits.
+
+config PATA_PARPORT_FRIQ
+ tristate "Freecom IQ ASIC-2 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for version 2 of the Freecom IQ parallel
+ port IDE adapter. This adapter is used by the Maxell Superdisk
+ drive.
+
+config PATA_PARPORT_FRPW
+ tristate "FreeCom power protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the Freecom power parallel port IDE
+ protocol.
+
+config PATA_PARPORT_KBIC
+ tristate "KingByte KBIC-951A/971A protocols"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the KBIC-951A and KBIC-971A parallel
+ port IDE protocols from KingByte Information Corp. KingByte's
+ adapters appear in many no-name portable disk and CD-ROM products,
+ especially in Europe.
+
+config PATA_PARPORT_KTTI
+ tristate "KT PHd protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the "PHd" parallel port IDE protocol
+ from KT Technology. This is a simple (low speed) adapter that is
+ used in some 2.5" portable hard drives.
+
+config PATA_PARPORT_ON20
+ tristate "OnSpec 90c20 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the (obsolete) 90c20 parallel port
+ IDE protocol from OnSpec (often marketed under the ValuStore brand
+ name).
+
+config PATA_PARPORT_ON26
+ tristate "OnSpec 90c26 protocol"
+ depends on PATA_PARPORT
+ help
+ This option enables support for the 90c26 parallel port IDE protocol
+ from OnSpec Electronics (often marketed under the ValuStore brand
+ name).
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
new file mode 100644
index 0000000000..0932c8d55b
--- /dev/null
+++ b/drivers/ata/pata_parport/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_PATA_PARPORT) += pata_parport.o
+
+obj-$(CONFIG_PATA_PARPORT_ATEN) += aten.o
+obj-$(CONFIG_PATA_PARPORT_BPCK) += bpck.o
+obj-$(CONFIG_PATA_PARPORT_COMM) += comm.o
+obj-$(CONFIG_PATA_PARPORT_DSTR) += dstr.o
+obj-$(CONFIG_PATA_PARPORT_KBIC) += kbic.o
+obj-$(CONFIG_PATA_PARPORT_EPAT) += epat.o
+obj-$(CONFIG_PATA_PARPORT_EPIA) += epia.o
+obj-$(CONFIG_PATA_PARPORT_FRPW) += frpw.o
+obj-$(CONFIG_PATA_PARPORT_FRIQ) += friq.o
+obj-$(CONFIG_PATA_PARPORT_FIT2) += fit2.o
+obj-$(CONFIG_PATA_PARPORT_FIT3) += fit3.o
+obj-$(CONFIG_PATA_PARPORT_ON20) += on20.o
+obj-$(CONFIG_PATA_PARPORT_ON26) += on26.o
+obj-$(CONFIG_PATA_PARPORT_KTTI) += ktti.o
+obj-$(CONFIG_PATA_PARPORT_BPCK6) += bpck6.o
diff --git a/drivers/ata/pata_parport/aten.c b/drivers/ata/pata_parport/aten.c
new file mode 100644
index 0000000000..620ce6c8da
--- /dev/null
+++ b/drivers/ata/pata_parport/aten.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-8 Grant R. Guenther <grant@torque.net>
+ *
+ * aten.c is a low-level protocol driver for the ATEN EH-100
+ * parallel port adapter. The EH-100 supports 4-bit and 8-bit
+ * modes only. There is also an EH-132 which supports EPP mode
+ * transfers. The EH-132 is not yet supported.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x08, 0x20 };
+
+static void aten_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont] + 0x80;
+
+ w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc);
+}
+
+static int aten_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = regr + cont_map[cont] + 0x40;
+
+ switch (pi->mode) {
+
+ case 0:
+ w0(r); w2(0xe); w2(6);
+ w2(7); w2(6); w2(0);
+ a = r1(); w0(0x10); b = r1(); w2(0xc);
+ return j44(a,b);
+
+ case 1:
+ r |= 0x10;
+ w0(r); w2(0xe); w2(6); w0(0xff);
+ w2(0x27); w2(0x26); w2(0x20);
+ a = r0();
+ w2(0x26); w2(0xc);
+ return a;
+ }
+ return -1;
+}
+
+static void aten_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b, c, d;
+
+ switch (pi->mode) {
+
+ case 0:
+ w0(0x48); w2(0xe); w2(6);
+ for (k = 0; k < count / 2; k++) {
+ w2(7); w2(6); w2(2);
+ a = r1(); w0(0x58); b = r1();
+ w2(0); d = r1(); w0(0x48); c = r1();
+ buf[2 * k] = j44(c, d);
+ buf[2 * k + 1] = j44(a, b);
+ }
+ w2(0xc);
+ break;
+
+ case 1:
+ w0(0x58); w2(0xe); w2(6);
+ for (k = 0; k < count / 2; k++) {
+ w2(0x27); w2(0x26); w2(0x22);
+ a = r0(); w2(0x20); b = r0();
+ buf[2 * k] = b;
+ buf[2 * k + 1] = a;
+ }
+ w2(0x26); w2(0xc);
+ break;
+ }
+}
+
+static void aten_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ w0(0x88); w2(0xe); w2(6);
+ for (k = 0; k < count / 2; k++) {
+ w0(buf[2 * k + 1]); w2(0xe); w2(6);
+ w0(buf[2 * k]); w2(7); w2(6);
+ }
+ w2(0xc);
+}
+
+static void aten_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xc);
+}
+
+static void aten_disconnect(struct pi_adapter *pi)
+{
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void aten_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[2] = { "4-bit", "8-bit" };
+
+ dev_info(&pi->dev,
+ "ATEN EH-100 at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol aten = {
+ .owner = THIS_MODULE,
+ .name = "aten",
+ .max_mode = 2,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = aten_write_regr,
+ .read_regr = aten_read_regr,
+ .write_block = aten_write_block,
+ .read_block = aten_read_block,
+ .connect = aten_connect,
+ .disconnect = aten_disconnect,
+ .log_adapter = aten_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("ATEN EH-100 parallel port IDE adapter protocol driver");
+module_pata_parport_driver(aten);
diff --git a/drivers/ata/pata_parport/bpck.c b/drivers/ata/pata_parport/bpck.c
new file mode 100644
index 0000000000..bba1eda65f
--- /dev/null
+++ b/drivers/ata/pata_parport/bpck.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1996-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * bpck.c is a low-level protocol driver for the MicroSolutions
+ * "backpack" parallel port IDE adapter.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#undef r2
+#undef w2
+#undef PC
+
+#define PC pi->private
+#define r2() (PC=(in_p(2) & 0xff))
+#define w2(byte) {out_p(2,byte); PC = byte;}
+#define t2(pat) {PC ^= pat; out_p(2,PC);}
+#define e2() {PC &= 0xfe; out_p(2,PC);}
+#define o2() {PC |= 1; out_p(2,PC);}
+
+#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ * cont = 2 - use internal bpck register addressing
+ */
+static int cont_map[3] = { 0x40, 0x48, 0 };
+
+static int bpck_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int r, l, h;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ w0(r & 0xf); w0(r); t2(2); t2(4);
+ l = r1();
+ t2(4);
+ h = r1();
+ return j44(l, h);
+ case 1:
+ w0(r & 0xf); w0(r); t2(2);
+ e2(); t2(0x20);
+ t2(4); h = r0();
+ t2(1); t2(0x20);
+ return h;
+ case 2:
+ case 3:
+ case 4:
+ w0(r); w2(9); w2(0); w2(0x20);
+ h = r4();
+ w2(0);
+ return h;
+
+ }
+ return -1;
+}
+
+static void bpck_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1: w0(r);
+ t2(2);
+ w0(val);
+ o2(); t2(4); t2(1);
+ break;
+ case 2:
+ case 3:
+ case 4: w0(r); w2(9); w2(0);
+ w0(val); w2(1); w2(3); w2(0);
+ break;
+
+ }
+}
+
+/* These macros access the bpck registers in native addressing */
+
+#define WR(r,v) bpck_write_regr(pi,2,r,v)
+#define RR(r) (bpck_read_regr(pi,2,r))
+
+static void bpck_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int i;
+
+ switch (pi->mode) {
+
+ case 0:
+ WR(4, 0x40);
+ w0(0x40); t2(2); t2(1);
+ for (i = 0; i < count; i++) {
+ w0(buf[i]);
+ t2(4);
+ }
+ WR(4, 0);
+ break;
+
+ case 1:
+ WR(4, 0x50);
+ w0(0x40); t2(2); t2(1);
+ for (i = 0; i < count; i++) {
+ w0(buf[i]);
+ t2(4);
+ }
+ WR(4, 0x10);
+ break;
+
+ case 2:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i = 0; i < count; i++)
+ w4(buf[i]);
+ w2(0);
+ WR(4, 8);
+ break;
+
+ case 3:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i = 0; i < count / 2; i++)
+ w4w(((u16 *)buf)[i]);
+ w2(0);
+ WR(4, 8);
+ break;
+
+ case 4:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i = 0; i < count / 4; i++)
+ w4l(((u32 *)buf)[i]);
+ w2(0);
+ WR(4, 8);
+ break;
+ }
+}
+
+static void bpck_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int i, l, h;
+
+ switch (pi->mode) {
+
+ case 0:
+ WR(4, 0x40);
+ w0(0x40); t2(2);
+ for (i = 0; i < count; i++) {
+ t2(4); l = r1();
+ t2(4); h = r1();
+ buf[i] = j44(l, h);
+ }
+ WR(4, 0);
+ break;
+
+ case 1:
+ WR(4, 0x50);
+ w0(0x40); t2(2); t2(0x20);
+ for (i = 0; i < count; i++) {
+ t2(4);
+ buf[i] = r0();
+ }
+ t2(1); t2(0x20);
+ WR(4, 0x10);
+ break;
+
+ case 2:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i = 0; i < count; i++)
+ buf[i] = r4();
+ w2(0);
+ WR(4, 8);
+ break;
+
+ case 3:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i = 0; i < count / 2; i++)
+ ((u16 *)buf)[i] = r4w();
+ w2(0);
+ WR(4, 8);
+ break;
+
+ case 4:
+ WR(4, 0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i = 0; i < count / 4; i++)
+ ((u32 *)buf)[i] = r4l();
+ w2(0);
+ WR(4, 8);
+ break;
+
+ }
+}
+
+static int bpck_probe_unit(struct pi_adapter *pi)
+{
+ int o1, o0, f7, id;
+ int t, s;
+
+ id = pi->unit;
+ s = 0;
+ w2(4); w2(0xe); r2(); t2(2);
+ o1 = r1()&0xf8;
+ o0 = r0();
+ w0(255-id); w2(4); w0(id);
+ t2(8); t2(8); t2(8);
+ t2(2); t = r1()&0xf8;
+ f7 = ((id % 8) == 7);
+ if ((f7) || (t != o1)) {
+ t2(2);
+ s = r1() & 0xf8;
+ }
+ if ((t == o1) && ((!f7) || (s == o1))) {
+ w2(0x4c); w0(o0);
+ return 0;
+ }
+ t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
+ return 1;
+}
+
+static void bpck_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ w0(0xff-pi->unit); w2(4); w0(pi->unit);
+ t2(8); t2(8); t2(8);
+ t2(2); t2(2);
+
+ switch (pi->mode) {
+ case 0:
+ t2(8); WR(4, 0);
+ break;
+ case 1:
+ t2(8); WR(4, 0x10);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ w2(0); WR(4, 8);
+ break;
+ }
+
+ WR(5,8);
+
+ /*
+ * Possibly wrong, purpose unknown (fiddle with ESS logic ???)
+ * if (pi->devtype == PI_PCD) {
+ */
+ WR(0x46, 0x10);
+ WR(0x4c, 0x38);
+ WR(0x4d, 0x88);
+ WR(0x46, 0xa0);
+ WR(0x41, 0);
+ WR(0x4e, 8);
+ /* } */
+}
+
+static void bpck_disconnect(struct pi_adapter *pi)
+{
+ w0(0);
+ if (pi->mode >= 2) {
+ w2(9); w2(0);
+ } else {
+ t2(2);
+ }
+ w2(0x4c); w0(pi->saved_r0);
+}
+
+static void bpck_force_spp(struct pi_adapter *pi)
+{
+ /* This fakes the EPP protocol to turn off EPP ... */
+ pi->saved_r0 = r0();
+ w0(0xff-pi->unit); w2(4); w0(pi->unit);
+ t2(8); t2(8); t2(8);
+ t2(2); t2(2);
+
+ w2(0);
+ w0(4); w2(9); w2(0);
+ w0(0); w2(1); w2(3); w2(0);
+ w0(0); w2(9); w2(0);
+ w2(0x4c); w0(pi->saved_r0);
+}
+
+#define TEST_LEN 16
+
+static int bpck_test_proto(struct pi_adapter *pi)
+{
+ int i, e, l, h, om;
+ char buf[TEST_LEN];
+
+ bpck_force_spp(pi);
+
+ switch (pi->mode) {
+
+ case 0:
+ bpck_connect(pi);
+ WR(0x13, 0x7f);
+ w0(0x13); t2(2);
+ for (i = 0; i < TEST_LEN; i++) {
+ t2(4); l = r1();
+ t2(4); h = r1();
+ buf[i] = j44(l, h);
+ }
+ bpck_disconnect(pi);
+ break;
+
+ case 1:
+ bpck_connect(pi);
+ WR(0x13, 0x7f);
+ w0(0x13); t2(2); t2(0x20);
+ for (i = 0; i < TEST_LEN; i++) {
+ t2(4);
+ buf[i] = r0();
+ }
+ t2(1); t2(0x20);
+ bpck_disconnect(pi);
+ break;
+
+ case 2:
+ case 3:
+ case 4:
+ om = pi->mode;
+ pi->mode = 0;
+ bpck_connect(pi);
+ WR(7, 3);
+ WR(4, 8);
+ bpck_disconnect(pi);
+
+ pi->mode = om;
+ bpck_connect(pi);
+ w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
+
+ switch (pi->mode) {
+ case 2:
+ for (i = 0; i < TEST_LEN; i++)
+ buf[i] = r4();
+ break;
+ case 3:
+ for (i = 0; i < TEST_LEN / 2; i++)
+ ((u16 *)buf)[i] = r4w();
+ break;
+ case 4:
+ for (i = 0; i < TEST_LEN / 4; i++)
+ ((u32 *)buf)[i] = r4l();
+ break;
+ }
+
+ w2(0);
+ WR(7, 0);
+ bpck_disconnect(pi);
+ break;
+
+ }
+
+ dev_dbg(&pi->dev, "bpck: 0x%x unit %d mode %d: ",
+ pi->port, pi->unit, pi->mode);
+ print_hex_dump_debug("bpck: ", DUMP_PREFIX_NONE, TEST_LEN, 1, buf,
+ TEST_LEN, false);
+
+ e = 0;
+ for (i = 0; i < TEST_LEN; i++) {
+ if (buf[i] != i + 1)
+ e++;
+ }
+
+ return e;
+}
+
+static void bpck_read_eeprom(struct pi_adapter *pi, char *buf)
+{
+ int i, j, k, p, v, f, om, od;
+
+ bpck_force_spp(pi);
+
+ om = pi->mode; od = pi->delay;
+ pi->mode = 0; pi->delay = 6;
+
+ bpck_connect(pi);
+
+ WR(4, 0);
+ for (i = 0; i < 64; i++) {
+ WR(6, 8);
+ WR(6, 0xc);
+ p = 0x100;
+ for (k = 0; k < 9; k++) {
+ f = (((i + 0x180) & p) != 0) * 2;
+ WR(6, f + 0xc);
+ WR(6, f + 0xd);
+ WR(6, f + 0xc);
+ p = (p >> 1);
+ }
+ for (j = 0; j < 2; j++) {
+ v = 0;
+ for (k = 0; k < 8; k++) {
+ WR(6, 0xc);
+ WR(6, 0xd);
+ WR(6, 0xc);
+ f = RR(0);
+ v = 2 * v + (f == 0x84);
+ }
+ buf[2 * i + 1 - j] = v;
+ }
+ }
+ WR(6, 8);
+ WR(6, 0);
+ WR(5, 8);
+
+ bpck_disconnect(pi);
+
+ if (om >= 2) {
+ bpck_connect(pi);
+ WR(7, 3);
+ WR(4, 8);
+ bpck_disconnect(pi);
+ }
+
+ pi->mode = om; pi->delay = od;
+}
+
+static int bpck_test_port(struct pi_adapter *pi)
+{
+ int i, r, m;
+
+ /* Check for 8-bit port */
+ w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
+ m = -1;
+ if (r == i)
+ m = 2;
+ if (r == (255-i))
+ m = 0;
+
+ w2(0xc);
+ i = r0();
+ w0(255-i);
+ r = r0();
+ w0(i);
+ if (r != (255-i))
+ m = -1;
+
+ if (m == 0) {
+ w2(6);
+ w2(0xc);
+ r = r0();
+ w0(0xaa);
+ w0(r);
+ w0(0xaa);
+ }
+ if (m == 2) {
+ w2(0x26);
+ w2(0xc);
+ }
+
+ if (m == -1)
+ return 0;
+
+ return 5;
+}
+
+static void bpck_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_str[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+ char scratch[128];
+
+ bpck_read_eeprom(pi,scratch);
+ print_hex_dump_bytes("bpck EEPROM: ", DUMP_PREFIX_NONE, scratch, 128);
+ dev_info(&pi->dev,
+ "backpack %8.8s unit %d at 0x%x, mode %d (%s), delay %d\n",
+ &scratch[110], pi->unit, pi->port, pi->mode,
+ mode_str[pi->mode], pi->delay);
+}
+
+static struct pi_protocol bpck = {
+ .owner = THIS_MODULE,
+ .name = "bpck",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 4,
+ .max_units = 255,
+ .write_regr = bpck_write_regr,
+ .read_regr = bpck_read_regr,
+ .write_block = bpck_write_block,
+ .read_block = bpck_read_block,
+ .connect = bpck_connect,
+ .disconnect = bpck_disconnect,
+ .test_port = bpck_test_port,
+ .probe_unit = bpck_probe_unit,
+ .test_proto = bpck_test_proto,
+ .log_adapter = bpck_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("MicroSolutions BACKPACK parallel port IDE adapter protocol driver");
+module_pata_parport_driver(bpck);
diff --git a/drivers/ata/pata_parport/bpck6.c b/drivers/ata/pata_parport/bpck6.c
new file mode 100644
index 0000000000..62c2b53325
--- /dev/null
+++ b/drivers/ata/pata_parport/bpck6.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 2001 Micro Solutions Inc.
+ *
+ * backpack.c is a low-level protocol driver for the Micro Solutions
+ * "BACKPACK" parallel port IDE adapter (works on Series 6 drives).
+ *
+ * Written by: Ken Hahn (linux-dev@micro-solutions.com)
+ * Clive Turvey (linux-dev@micro-solutions.com)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/parport.h>
+#include "pata_parport.h"
+
+/* 60772 Commands */
+#define ACCESS_REG 0x00
+#define ACCESS_PORT 0x40
+
+#define ACCESS_READ 0x00
+#define ACCESS_WRITE 0x20
+
+/* 60772 Command Prefix */
+#define CMD_PREFIX_SET 0xe0 // Special command that modifies next command's operation
+#define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits
+ #define PREFIX_IO16 0x01 // perform 16-bit wide I/O
+ #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write
+ #define PREFIX_BLK 0x08 // enable block transfer mode
+
+/* 60772 Registers */
+#define REG_STATUS 0x00 // status register
+ #define STATUS_IRQA 0x01 // Peripheral IRQA line
+ #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit
+#define REG_VERSION 0x01 // PPC version register (read)
+#define REG_HWCFG 0x02 // Hardware Config register
+#define REG_RAMSIZE 0x03 // Size of RAM Buffer
+ #define RAMSIZE_128K 0x02
+#define REG_EEPROM 0x06 // EEPROM control register
+ #define EEPROM_SK 0x01 // eeprom SK bit
+ #define EEPROM_DI 0x02 // eeprom DI bit
+ #define EEPROM_CS 0x04 // eeprom CS bit
+ #define EEPROM_EN 0x08 // eeprom output enable
+#define REG_BLKSIZE 0x08 // Block transfer len (24 bit)
+
+/* flags */
+#define fifo_wait 0x10
+
+/* DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES */
+#define PPCMODE_UNI_SW 0
+#define PPCMODE_UNI_FW 1
+#define PPCMODE_BI_SW 2
+#define PPCMODE_BI_FW 3
+#define PPCMODE_EPP_BYTE 4
+#define PPCMODE_EPP_WORD 5
+#define PPCMODE_EPP_DWORD 6
+
+static int mode_map[] = { PPCMODE_UNI_FW, PPCMODE_BI_FW, PPCMODE_EPP_BYTE,
+ PPCMODE_EPP_WORD, PPCMODE_EPP_DWORD };
+
+static void bpck6_send_cmd(struct pi_adapter *pi, u8 cmd)
+{
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_UNI_FW:
+ case PPCMODE_BI_SW:
+ case PPCMODE_BI_FW:
+ parport_write_data(pi->pardev->port, cmd);
+ parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_AUTOFD);
+ break;
+ case PPCMODE_EPP_BYTE:
+ case PPCMODE_EPP_WORD:
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_write_addr(pi->pardev->port, &cmd, 1, 0);
+ break;
+ }
+}
+
+static u8 bpck6_rd_data_byte(struct pi_adapter *pi)
+{
+ u8 data = 0;
+
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_UNI_FW:
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_INIT);
+ data = parport_read_status(pi->pardev->port);
+ data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+ data |= parport_read_status(pi->pardev->port) & 0xB8;
+ break;
+ case PPCMODE_BI_SW:
+ case PPCMODE_BI_FW:
+ parport_data_reverse(pi->pardev->port);
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT);
+ data = parport_read_data(pi->pardev->port);
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, 0);
+ parport_data_forward(pi->pardev->port);
+ break;
+ case PPCMODE_EPP_BYTE:
+ case PPCMODE_EPP_WORD:
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_read_data(pi->pardev->port, &data, 1, 0);
+ break;
+ }
+
+ return data;
+}
+
+static void bpck6_wr_data_byte(struct pi_adapter *pi, u8 data)
+{
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_UNI_FW:
+ case PPCMODE_BI_SW:
+ case PPCMODE_BI_FW:
+ parport_write_data(pi->pardev->port, data);
+ parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_INIT);
+ break;
+ case PPCMODE_EPP_BYTE:
+ case PPCMODE_EPP_WORD:
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_write_data(pi->pardev->port, &data, 1, 0);
+ break;
+ }
+}
+
+static int bpck6_read_regr(struct pi_adapter *pi, int cont, int reg)
+{
+ u8 port = cont ? reg | 8 : reg;
+
+ bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_READ);
+ return bpck6_rd_data_byte(pi);
+}
+
+static void bpck6_write_regr(struct pi_adapter *pi, int cont, int reg, int val)
+{
+ u8 port = cont ? reg | 8 : reg;
+
+ bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_WRITE);
+ bpck6_wr_data_byte(pi, val);
+}
+
+static void bpck6_wait_for_fifo(struct pi_adapter *pi)
+{
+ int i;
+
+ if (pi->private & fifo_wait) {
+ for (i = 0; i < 20; i++)
+ parport_read_status(pi->pardev->port);
+ }
+}
+
+static void bpck6_write_block(struct pi_adapter *pi, char *buf, int len)
+{
+ u8 this, last;
+
+ bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);
+ bpck6_wr_data_byte(pi, (u8)len);
+ bpck6_wr_data_byte(pi, (u8)(len >> 8));
+ bpck6_wr_data_byte(pi, 0);
+
+ bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);
+ bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_WRITE);
+
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_BI_SW:
+ while (len--) {
+ parport_write_data(pi->pardev->port, *buf++);
+ parport_frob_control(pi->pardev->port, 0,
+ PARPORT_CONTROL_INIT);
+ }
+ break;
+ case PPCMODE_UNI_FW:
+ case PPCMODE_BI_FW:
+ bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_FASTWR);
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+
+ last = *buf;
+
+ parport_write_data(pi->pardev->port, last);
+
+ while (len) {
+ this = *buf++;
+ len--;
+
+ if (this == last) {
+ parport_frob_control(pi->pardev->port, 0,
+ PARPORT_CONTROL_INIT);
+ } else {
+ parport_write_data(pi->pardev->port, this);
+ last = this;
+ }
+ }
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ 0);
+ bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_FASTWR);
+ break;
+ case PPCMODE_EPP_BYTE:
+ pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
+ len, PARPORT_EPP_FAST_8);
+ bpck6_wait_for_fifo(pi);
+ break;
+ case PPCMODE_EPP_WORD:
+ pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
+ len, PARPORT_EPP_FAST_16);
+ bpck6_wait_for_fifo(pi);
+ break;
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
+ len, PARPORT_EPP_FAST_32);
+ bpck6_wait_for_fifo(pi);
+ break;
+ }
+
+ bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);
+}
+
+static void bpck6_read_block(struct pi_adapter *pi, char *buf, int len)
+{
+ bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);
+ bpck6_wr_data_byte(pi, (u8)len);
+ bpck6_wr_data_byte(pi, (u8)(len >> 8));
+ bpck6_wr_data_byte(pi, 0);
+
+ bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);
+ bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_READ);
+
+ switch (mode_map[pi->mode]) {
+ case PPCMODE_UNI_SW:
+ case PPCMODE_UNI_FW:
+ while (len) {
+ u8 d;
+
+ parport_frob_control(pi->pardev->port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_INIT); /* DATA STROBE */
+ d = parport_read_status(pi->pardev->port);
+ d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);
+ parport_frob_control(pi->pardev->port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE);
+ d |= parport_read_status(pi->pardev->port) & 0xB8;
+ *buf++ = d;
+ len--;
+ }
+ break;
+ case PPCMODE_BI_SW:
+ case PPCMODE_BI_FW:
+ parport_data_reverse(pi->pardev->port);
+ while (len) {
+ parport_frob_control(pi->pardev->port,
+ PARPORT_CONTROL_STROBE,
+ PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT);
+ *buf++ = parport_read_data(pi->pardev->port);
+ len--;
+ }
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
+ 0);
+ parport_data_forward(pi->pardev->port);
+ break;
+ case PPCMODE_EPP_BYTE:
+ pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
+ PARPORT_EPP_FAST_8);
+ break;
+ case PPCMODE_EPP_WORD:
+ pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
+ PARPORT_EPP_FAST_16);
+ break;
+ case PPCMODE_EPP_DWORD:
+ pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
+ PARPORT_EPP_FAST_32);
+ break;
+ }
+
+ bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);
+}
+
+static int bpck6_open(struct pi_adapter *pi)
+{
+ u8 i, j, k;
+
+ pi->saved_r0 = parport_read_data(pi->pardev->port);
+ pi->saved_r2 = parport_read_control(pi->pardev->port) & 0x5F;
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
+ PARPORT_CONTROL_SELECT);
+ if (pi->saved_r0 == 'b')
+ parport_write_data(pi->pardev->port, 'x');
+ parport_write_data(pi->pardev->port, 'b');
+ parport_write_data(pi->pardev->port, 'p');
+ parport_write_data(pi->pardev->port, pi->unit);
+ parport_write_data(pi->pardev->port, ~pi->unit);
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0);
+ parport_write_control(pi->pardev->port, PARPORT_CONTROL_INIT);
+
+ i = mode_map[pi->mode] & 0x0C;
+ if (i == 0)
+ i = (mode_map[pi->mode] & 2) | 1;
+ parport_write_data(pi->pardev->port, i);
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
+ PARPORT_CONTROL_SELECT);
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ j = ((i & 0x08) << 4) | ((i & 0x07) << 3);
+ k = parport_read_status(pi->pardev->port) & 0xB8;
+ if (j != k)
+ goto fail;
+
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, 0);
+ k = (parport_read_status(pi->pardev->port) & 0xB8) ^ 0xB8;
+ if (j != k)
+ goto fail;
+
+ if (i & 4) {
+ /* EPP */
+ parport_frob_control(pi->pardev->port,
+ PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, 0);
+ } else {
+ /* PPC/ECP */
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0);
+ }
+
+ pi->private = 0;
+
+ bpck6_send_cmd(pi, ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE);
+ bpck6_wr_data_byte(pi, RAMSIZE_128K);
+
+ bpck6_send_cmd(pi, ACCESS_REG | ACCESS_READ | REG_VERSION);
+ if ((bpck6_rd_data_byte(pi) & 0x3F) == 0x0C)
+ pi->private |= fifo_wait;
+
+ return 1;
+
+fail:
+ parport_write_control(pi->pardev->port, pi->saved_r2);
+ parport_write_data(pi->pardev->port, pi->saved_r0);
+
+ return 0;
+}
+
+static void bpck6_deselect(struct pi_adapter *pi)
+{
+ if (mode_map[pi->mode] & 4) {
+ /* EPP */
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_INIT,
+ PARPORT_CONTROL_INIT);
+ } else {
+ /* PPC/ECP */
+ parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
+ PARPORT_CONTROL_SELECT);
+ }
+
+ parport_write_data(pi->pardev->port, pi->saved_r0);
+ parport_write_control(pi->pardev->port,
+ pi->saved_r2 | PARPORT_CONTROL_SELECT);
+ parport_write_control(pi->pardev->port, pi->saved_r2);
+}
+
+static void bpck6_wr_extout(struct pi_adapter *pi, u8 regdata)
+{
+ bpck6_send_cmd(pi, REG_VERSION | ACCESS_REG | ACCESS_WRITE);
+ bpck6_wr_data_byte(pi, (u8)((regdata & 0x03) << 6));
+}
+
+static void bpck6_connect(struct pi_adapter *pi)
+{
+ dev_dbg(&pi->dev, "connect\n");
+
+ bpck6_open(pi);
+ bpck6_wr_extout(pi, 0x3);
+}
+
+static void bpck6_disconnect(struct pi_adapter *pi)
+{
+ dev_dbg(&pi->dev, "disconnect\n");
+ bpck6_wr_extout(pi, 0x0);
+ bpck6_deselect(pi);
+}
+
+/* check for 8-bit port */
+static int bpck6_test_port(struct pi_adapter *pi)
+{
+ dev_dbg(&pi->dev, "PARPORT indicates modes=%x for lp=0x%lx\n",
+ pi->pardev->port->modes, pi->pardev->port->base);
+
+ /* look at the parport device to see what modes we can use */
+ if (pi->pardev->port->modes & PARPORT_MODE_EPP)
+ return 5; /* Can do EPP */
+ if (pi->pardev->port->modes & PARPORT_MODE_TRISTATE)
+ return 2;
+ return 1; /* Just flat SPP */
+}
+
+static int bpck6_probe_unit(struct pi_adapter *pi)
+{
+ int out, saved_mode;
+
+ dev_dbg(&pi->dev, "PROBE UNIT %x on port:%x\n", pi->unit, pi->port);
+
+ saved_mode = pi->mode;
+ /*LOWER DOWN TO UNIDIRECTIONAL*/
+ pi->mode = 0;
+
+ out = bpck6_open(pi);
+
+ dev_dbg(&pi->dev, "ppc_open returned %2x\n", out);
+
+ if (out) {
+ bpck6_deselect(pi);
+ dev_dbg(&pi->dev, "leaving probe\n");
+ pi->mode = saved_mode;
+ return 1;
+ }
+
+ dev_dbg(&pi->dev, "Failed open\n");
+ pi->mode = saved_mode;
+
+ return 0;
+}
+
+static void bpck6_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ dev_info(&pi->dev,
+ "Micro Solutions BACKPACK Drive unit %d at 0x%x, mode:%d (%s), delay %d\n",
+ pi->unit, pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol bpck6 = {
+ .owner = THIS_MODULE,
+ .name = "bpck6",
+ .max_mode = 5,
+ .epp_first = 2, /* 2-5 use epp (need 8 ports) */
+ .max_units = 255,
+ .write_regr = bpck6_write_regr,
+ .read_regr = bpck6_read_regr,
+ .write_block = bpck6_write_block,
+ .read_block = bpck6_read_block,
+ .connect = bpck6_connect,
+ .disconnect = bpck6_disconnect,
+ .test_port = bpck6_test_port,
+ .probe_unit = bpck6_probe_unit,
+ .log_adapter = bpck6_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Micro Solutions Inc.");
+MODULE_DESCRIPTION("Micro Solutions BACKPACK parallel port IDE adapter "
+ "(version 6 drives) protocol driver");
+module_pata_parport_driver(bpck6);
diff --git a/drivers/ata/pata_parport/comm.c b/drivers/ata/pata_parport/comm.c
new file mode 100644
index 0000000000..94b8d35210
--- /dev/null
+++ b/drivers/ata/pata_parport/comm.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * comm.c is a low-level protocol driver for some older models of the DataStor
+ * "Commuter" parallel to IDE adapter. Some of the parallel port devices
+ * marketed by Arista currently use this adapter.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+/*
+ * mode codes: 0 nybble reads, 8-bit writes
+ * 1 8-bit reads and writes
+ * 2 8-bit EPP mode
+ */
+
+#define j44(a, b) (((a >> 3) & 0x0f) | ((b << 1) & 0xf0))
+
+#define P1 w2(5);w2(0xd);w2(0xd);w2(5);w2(4);
+#define P2 w2(5);w2(7);w2(7);w2(5);w2(4);
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x08, 0x10 };
+
+static int comm_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int l, h, r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ w0(r); P1; w0(0);
+ w2(6); l = r1(); w0(0x80); h = r1(); w2(4);
+ return j44(l, h);
+
+ case 1:
+ w0(r+0x20); P1;
+ w0(0); w2(0x26); h = r0(); w2(4);
+ return h;
+
+ case 2:
+ case 3:
+ case 4:
+ w3(r+0x20); (void)r1();
+ w2(0x24); h = r4(); w2(4);
+ return h;
+ }
+
+ return -1;
+}
+
+static void comm_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(r); P1; w0(val); P2;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ w3(r); (void)r1(); w4(val);
+ break;
+ }
+}
+
+static void comm_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4); w0(0xff); w2(6);
+ w2(4); w0(0xaa); w2(6);
+ w2(4); w0(0x00); w2(6);
+ w2(4); w0(0x87); w2(6);
+ w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4);
+}
+
+static void comm_disconnect(struct pi_adapter *pi)
+{
+ w2(0); w2(0); w2(0); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void comm_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int i, l, h;
+
+ switch (pi->mode) {
+ case 0:
+ w0(0x48); P1;
+ for (i = 0; i < count; i++) {
+ w0(0); w2(6); l = r1();
+ w0(0x80); h = r1(); w2(4);
+ buf[i] = j44(l, h);
+ }
+ break;
+ case 1:
+ w0(0x68); P1; w0(0);
+ for (i = 0; i < count; i++) {
+ w2(0x26);
+ buf[i] = r0();
+ w2(0x24);
+ }
+ w2(4);
+ break;
+ case 2:
+ w3(0x68); (void)r1(); w2(0x24);
+ for (i = 0; i < count; i++)
+ buf[i] = r4();
+ w2(4);
+ break;
+ case 3:
+ w3(0x68); (void)r1(); w2(0x24);
+ for (i = 0; i < count / 2; i++)
+ ((u16 *)buf)[i] = r4w();
+ w2(4);
+ break;
+ case 4:
+ w3(0x68); (void)r1(); w2(0x24);
+ for (i = 0; i < count / 4; i++)
+ ((u32 *)buf)[i] = r4l();
+ w2(4);
+ break;
+ }
+}
+
+/* NB: Watch out for the byte swapped writes ! */
+static void comm_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(0x68); P1;
+ for (k = 0; k < count; k++) {
+ w2(5);
+ w0(buf[k ^ 1]);
+ w2(7);
+ }
+ w2(5); w2(4);
+ break;
+ case 2:
+ w3(0x48); (void)r1();
+ for (k = 0; k < count; k++)
+ w4(buf[k ^ 1]);
+ break;
+ case 3:
+ w3(0x48); (void)r1();
+ for (k = 0; k < count / 2; k++)
+ w4w(swab16(((u16 *)buf)[k]));
+ break;
+ case 4:
+ w3(0x48); (void)r1();
+ for (k = 0; k < count / 4; k++)
+ w4l(swab16(((u16 *)buf)[2 * k]) |
+ swab16(((u16 *)buf)[2 * k + 1]) << 16);
+ break;
+ }
+}
+
+static void comm_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ dev_info(&pi->dev,
+ "DataStor Commuter at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol comm = {
+ .owner = THIS_MODULE,
+ .name = "comm",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = comm_write_regr,
+ .read_regr = comm_read_regr,
+ .write_block = comm_write_block,
+ .read_block = comm_read_block,
+ .connect = comm_connect,
+ .disconnect = comm_disconnect,
+ .log_adapter = comm_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("DataStor Commuter parallel port IDE adapter protocol driver");
+module_pata_parport_driver(comm);
diff --git a/drivers/ata/pata_parport/dstr.c b/drivers/ata/pata_parport/dstr.c
new file mode 100644
index 0000000000..88930bb1f0
--- /dev/null
+++ b/drivers/ata/pata_parport/dstr.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * dstr.c is a low-level protocol driver for the DataStor EP2000 parallel
+ * to IDE adapter chip.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+/*
+ * mode codes: 0 nybble reads, 8-bit writes
+ * 1 8-bit reads and writes
+ * 2 8-bit EPP mode
+ * 3 EPP-16
+ * 4 EPP-32
+ */
+
+#define j44(a, b) (((a >> 3) & 0x07) | ((~a >> 4) & 0x08) | \
+ ((b << 1) & 0x70) | ((~b) & 0x80))
+
+#define P1 w2(5);w2(0xd);w2(5);w2(4);
+#define P2 w2(5);w2(7);w2(5);w2(4);
+#define P3 w2(6);w2(4);w2(6);w2(4);
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x20, 0x40 };
+
+static int dstr_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = regr + cont_map[cont];
+
+ w0(0x81); P1;
+ if (pi->mode)
+ w0(0x11);
+ else
+ w0(1);
+ P2; w0(r); P1;
+
+ switch (pi->mode) {
+ case 0:
+ w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4);
+ return j44(a, b);
+ case 1:
+ w0(0); w2(0x26); a = r0(); w2(4);
+ return a;
+ case 2:
+ case 3:
+ case 4:
+ w2(0x24); a = r4(); w2(4);
+ return a;
+ }
+
+ return -1;
+}
+
+static void dstr_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ w0(0x81); P1;
+ if (pi->mode >= 2)
+ w0(0x11);
+ else
+ w0(1);
+ P2; w0(r); P1;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(val); w2(5); w2(7); w2(5); w2(4);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ w4(val);
+ break;
+ }
+}
+
+#define CCP(x) \
+ do { \
+ w0(0xff); w2(0xc); w2(4); \
+ w0(0xaa); w0(0x55); w0(0); w0(0xff); \
+ w0(0x87); w0(0x78); \
+ w0(x); w2(5); w2(4); \
+ } while (0)
+
+static void dstr_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4); CCP(0xe0); w0(0xff);
+}
+
+static void dstr_disconnect(struct pi_adapter *pi)
+{
+ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void dstr_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b;
+
+ w0(0x81); P1;
+ if (pi->mode)
+ w0(0x19);
+ else
+ w0(9);
+ P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+ switch (pi->mode) {
+ case 0:
+ for (k = 0; k < count; k++) {
+ w2(6); a = r1(); w2(4);
+ w2(6); b = r1(); w2(4);
+ buf[k] = j44(a, b);
+ }
+ break;
+ case 1:
+ w0(0);
+ for (k = 0; k < count; k++) {
+ w2(0x26);
+ buf[k] = r0();
+ w2(0x24);
+ }
+ w2(4);
+ break;
+ case 2:
+ w2(0x24);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(4);
+ break;
+ case 3:
+ w2(0x24);
+ for (k = 0; k < count / 2; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(4);
+ break;
+ case 4:
+ w2(0x24);
+ for (k = 0; k < count / 4; k++)
+ ((u32 *)buf)[k] = r4l();
+ w2(4);
+ break;
+ }
+}
+
+static void dstr_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ w0(0x81); P1;
+ if (pi->mode)
+ w0(0x19);
+ else
+ w0(9);
+ P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ for (k = 0; k < count; k++) {
+ w2(5);
+ w0(buf[k]);
+ w2(7);
+ }
+ w2(5); w2(4);
+ break;
+ case 2:
+ w2(0xc5);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(0xc4);
+ break;
+ case 3:
+ w2(0xc5);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(0xc4);
+ break;
+ case 4:
+ w2(0xc5);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(0xc4);
+ break;
+ }
+}
+
+static void dstr_log_adapter(struct pi_adapter *pi)
+
+{
+ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ dev_info(&pi->dev,
+ "DataStor EP2000 at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol dstr = {
+ .owner = THIS_MODULE,
+ .name = "dstr",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = dstr_write_regr,
+ .read_regr = dstr_read_regr,
+ .write_block = dstr_write_block,
+ .read_block = dstr_read_block,
+ .connect = dstr_connect,
+ .disconnect = dstr_disconnect,
+ .log_adapter = dstr_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("DataStor EP2000 parallel port IDE adapter protocol driver");
+module_pata_parport_driver(dstr);
diff --git a/drivers/ata/pata_parport/epat.c b/drivers/ata/pata_parport/epat.c
new file mode 100644
index 0000000000..3cb54fcbf0
--- /dev/null
+++ b/drivers/ata/pata_parport/epat.c
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * This is the low level protocol driver for the EPAT parallel
+ * to IDE adapter from Shuttle Technologies. This adapter is
+ * used in many popular parallel port disk products such as the
+ * SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a, b) (((a >> 4) & 0x0f) + (b & 0xf0))
+#define j53(a, b) (((a >> 3) & 0x1f) + ((b << 4) & 0xe0))
+
+static int epatc8;
+
+module_param(epatc8, int, 0);
+MODULE_PARM_DESC(epatc8,
+ "support for the Shuttle EP1284 chip, "
+ "used in any recent Imation SuperDisk (LS-120) drive.");
+
+/*
+ * cont = 0 IDE register file
+ * cont = 1 IDE control registers
+ * cont = 2 internal EPAT registers
+ */
+static int cont_map[3] = { 0x18, 0x10, 0 };
+
+static void epat_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(0x60+r); w2(1); w0(val); w2(4);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ w3(0x40+r); w4(val);
+ break;
+ }
+}
+
+static int epat_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0:
+ w0(r); w2(1); w2(3);
+ a = r1(); w2(4); b = r1();
+ return j44(a, b);
+ case 1:
+ w0(0x40+r); w2(1); w2(4);
+ a = r1(); b = r2(); w0(0xff);
+ return j53(a, b);
+ case 2:
+ w0(0x20+r); w2(1); w2(0x25);
+ a = r0(); w2(4);
+ return a;
+ case 3:
+ case 4:
+ case 5:
+ w3(r); w2(0x24); a = r4(); w2(4);
+ return a;
+ }
+
+ return -1; /* never gets here */
+}
+
+static void epat_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, ph, a, b;
+
+ switch (pi->mode) {
+
+ case 0:
+ w0(7); w2(1); w2(3); w0(0xff);
+ ph = 0;
+ for (k = 0; k < count; k++) {
+ if (k == count-1)
+ w0(0xfd);
+ w2(6 + ph); a = r1();
+ if (a & 8) {
+ b = a;
+ } else {
+ w2(4+ph); b = r1();
+ }
+ buf[k] = j44(a, b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+
+ case 1:
+ w0(0x47); w2(1); w2(5); w0(0xff);
+ ph = 0;
+ for (k = 0; k < count; k++) {
+ if (k == count - 1)
+ w0(0xfd);
+ w2(4 + ph);
+ a = r1(); b = r2();
+ buf[k] = j53(a, b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+
+ case 2:
+ w0(0x27); w2(1); w2(0x25); w0(0);
+ ph = 0;
+ for (k = 0; k < count - 1; k++) {
+ w2(0x24 + ph);
+ buf[k] = r0();
+ ph = 1 - ph;
+ }
+ w2(0x26); w2(0x27);
+ buf[count - 1] = r0();
+ w2(0x25); w2(4);
+ break;
+
+ case 3:
+ w3(0x80); w2(0x24);
+ for (k = 0; k < count - 1; k++)
+ buf[k] = r4();
+ w2(4); w3(0xa0); w2(0x24);
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+
+ case 4:
+ w3(0x80); w2(0x24);
+ for (k = 0; k < count / 2 - 1; k++)
+ ((u16 *)buf)[k] = r4w();
+ buf[count - 2] = r4();
+ w2(4); w3(0xa0); w2(0x24);
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+
+ case 5:
+ w3(0x80); w2(0x24);
+ for (k = 0; k < count / 4 - 1; k++)
+ ((u32 *)buf)[k] = r4l();
+ for (k = count - 4; k < count - 1; k++)
+ buf[k] = r4();
+ w2(4); w3(0xa0); w2(0x24);
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ }
+}
+
+static void epat_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int ph, k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(0x67); w2(1); w2(5);
+ ph = 0;
+ for (k = 0; k < count; k++) {
+ w0(buf[k]);
+ w2(4 + ph);
+ ph = 1 - ph;
+ }
+ w2(7); w2(4);
+ break;
+ case 3:
+ w3(0xc0);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(4);
+ break;
+ case 4:
+ w3(0xc0);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(4);
+ break;
+ case 5:
+ w3(0xc0);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(4);
+ break;
+ }
+}
+
+/* these macros access the EPAT registers in native addressing */
+
+#define WR(r, v) epat_write_regr(pi, 2, r, v)
+#define RR(r) epat_read_regr(pi, 2, r)
+
+/* and these access the IDE task file */
+
+#define WRi(r, v) epat_write_regr(pi, 0, r, v)
+#define RRi(r) epat_read_regr(pi, 0, r)
+
+/* FIXME: the CPP stuff should be fixed to handle multiple EPATs on a chain */
+
+#define CPP(x) \
+ do { \
+ w2(4); w0(0x22); w0(0xaa); \
+ w0(0x55); w0(0); w0(0xff); \
+ w0(0x87); w0(0x78); w0(x); \
+ w2(4); w2(5); w2(4); w0(0xff); \
+ } while (0)
+
+static void epat_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ /* Initialize the chip */
+ CPP(0);
+
+ if (epatc8) {
+ CPP(0x40); CPP(0xe0);
+ w0(0); w2(1); w2(4);
+ WR(0x8, 0x12);
+ WR(0xc, 0x14);
+ WR(0x12, 0x10);
+ WR(0xe, 0xf);
+ WR(0xf, 4);
+ /* WR(0xe,0xa);WR(0xf,4); */
+ WR(0xe, 0xd);
+ WR(0xf, 0);
+ /* CPP(0x30); */
+ }
+
+ /* Connect to the chip */
+ CPP(0xe0);
+ w0(0); w2(1); w2(4); /* Idle into SPP */
+ if (pi->mode >= 3) {
+ w0(0); w2(1); w2(4); w2(0xc);
+ /* Request EPP */
+ w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4);
+ }
+
+ if (!epatc8) {
+ WR(8, 0x10);
+ WR(0xc, 0x14);
+ WR(0xa, 0x38);
+ WR(0x12, 0x10);
+ }
+}
+
+static void epat_disconnect(struct pi_adapter *pi)
+{
+ CPP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static int epat_test_proto(struct pi_adapter *pi)
+{
+ int k, j, f, cc;
+ int e[2] = { 0, 0 };
+ char scratch[512];
+
+ epat_connect(pi);
+ cc = RR(0xd);
+ epat_disconnect(pi);
+
+ epat_connect(pi);
+ for (j=0;j<2;j++) {
+ WRi(6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ WRi(2, k ^ 0xaa);
+ WRi(3, k ^ 0x55);
+ if (RRi(2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ }
+ epat_disconnect(pi);
+
+ f = 0;
+ epat_connect(pi);
+ WR(0x13, 1); WR(0x13, 0); WR(0xa, 0x11);
+ epat_read_block(pi, scratch, 512);
+
+ for (k = 0; k < 256; k++) {
+ if ((scratch[2 * k] & 0xff) != k)
+ f++;
+ if ((scratch[2 * k + 1] & 0xff) != 0xff - k)
+ f++;
+ }
+ epat_disconnect(pi);
+
+ dev_dbg(&pi->dev,
+ "epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n",
+ pi->port, pi->mode, cc, e[0], e[1], f);
+
+ return (e[0] && e[1]) || f;
+}
+
+static void epat_log_adapter(struct pi_adapter *pi)
+{
+ int ver;
+ char *mode_string[6] =
+ { "4-bit", "5/3", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ epat_connect(pi);
+ WR(0xa, 0x38); /* read the version code */
+ ver = RR(0xb);
+ epat_disconnect(pi);
+
+ dev_info(&pi->dev,
+ "Shuttle EPAT chip %x at 0x%x, mode %d (%s), delay %d\n",
+ ver, pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol epat = {
+ .owner = THIS_MODULE,
+ .name = "epat",
+ .max_mode = 6,
+ .epp_first = 3,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = epat_write_regr,
+ .read_regr = epat_read_regr,
+ .write_block = epat_write_block,
+ .read_block = epat_read_block,
+ .connect = epat_connect,
+ .disconnect = epat_disconnect,
+ .test_proto = epat_test_proto,
+ .log_adapter = epat_log_adapter,
+};
+
+static int __init epat_init(void)
+{
+#ifdef CONFIG_PATA_PARPORT_EPATC8
+ epatc8 = 1;
+#endif
+ return pata_parport_register_driver(&epat);
+}
+
+static void __exit epat_exit(void)
+{
+ pata_parport_unregister_driver(&epat);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Shuttle Technologies EPAT parallel port IDE adapter "
+ "protocol driver");
+module_init(epat_init)
+module_exit(epat_exit)
diff --git a/drivers/ata/pata_parport/epia.c b/drivers/ata/pata_parport/epia.c
new file mode 100644
index 0000000000..7aaba474c6
--- /dev/null
+++ b/drivers/ata/pata_parport/epia.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * epia.c is a low-level protocol driver for Shuttle Technologies
+ * EPIA parallel to IDE adapter chip. This device is now obsolete
+ * and has been replaced with the EPAT chip, which is supported
+ * by epat.c, however, some devices based on EPIA are still
+ * available.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+/*
+ * mode codes: 0 nybble reads on port 1, 8-bit writes
+ * 1 5/3 reads on ports 1 & 2, 8-bit writes
+ * 2 8-bit reads and writes
+ * 3 8-bit EPP mode
+ * 4 16-bit EPP
+ * 5 32-bit EPP
+ */
+
+#define j44(a, b) (((a >> 4) & 0x0f) + (b & 0xf0))
+#define j53(a, b) (((a >> 3) & 0x1f) + ((b << 4) & 0xe0))
+
+/*
+ * cont = 0 IDE register file
+ * cont = 1 IDE control registers
+ */
+static int cont_map[2] = { 0, 0x80 };
+
+static int epia_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ regr += cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ r = regr ^ 0x39;
+ w0(r); w2(1); w2(3); w0(r);
+ a = r1(); w2(1); b = r1(); w2(4);
+ return j44(a, b);
+ case 1:
+ r = regr ^ 0x31;
+ w0(r); w2(1); w0(r & 0x37);
+ w2(3); w2(5); w0(r | 0xf0);
+ a = r1(); b = r2(); w2(4);
+ return j53(a, b);
+ case 2:
+ r = regr^0x29;
+ w0(r); w2(1); w2(0X21); w2(0x23);
+ a = r0(); w2(4);
+ return a;
+ case 3:
+ case 4:
+ case 5:
+ w3(regr); w2(0x24); a = r4(); w2(4);
+ return a;
+ }
+
+ return -1;
+}
+
+static void epia_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r;
+
+ regr += cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ r = regr ^ 0x19;
+ w0(r); w2(1); w0(val); w2(3); w2(4);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ r = regr ^ 0x40;
+ w3(r); w4(val); w2(4);
+ break;
+ }
+}
+
+#define WR(r, v) epia_write_regr(pi, 0, r, v)
+#define RR(r) epia_read_regr(pi, 0, r)
+
+/*
+ * The use of register 0x84 is entirely unclear - it seems to control
+ * some EPP counters ... currently we know about 3 different block
+ * sizes: the standard 512 byte reads and writes, 12 byte writes and
+ * 2048 byte reads (the last two being used in the CDrom drivers.
+ */
+static void epia_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
+ w2(1); w2(4);
+ if (pi->mode >= 3) {
+ w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
+ w2(0x24); w2(0x26); w2(4);
+ }
+ WR(0x86, 8);
+}
+
+static void epia_disconnect(struct pi_adapter *pi)
+{
+ /* WR(0x84,0x10); */
+ w0(pi->saved_r0);
+ w2(1); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void epia_read_block(struct pi_adapter *pi, char *buf, int count)
+
+{
+ int k, ph, a, b;
+
+ switch (pi->mode) {
+ case 0:
+ w0(0x81); w2(1); w2(3); w0(0xc1);
+ ph = 1;
+ for (k = 0; k < count; k++) {
+ w2(2+ph); a = r1();
+ w2(4+ph); b = r1();
+ buf[k] = j44(a, b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+ case 1:
+ w0(0x91); w2(1); w0(0x10); w2(3);
+ w0(0x51); w2(5); w0(0xd1);
+ ph = 1;
+ for (k = 0; k < count; k++) {
+ w2(4 + ph);
+ a = r1(); b = r2();
+ buf[k] = j53(a, b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+ case 2:
+ w0(0x89); w2(1); w2(0x23); w2(0x21);
+ ph = 1;
+ for (k = 0; k < count; k++) {
+ w2(0x24 + ph);
+ buf[k] = r0();
+ ph = 1 - ph;
+ }
+ w2(6); w2(4);
+ break;
+ case 3:
+ if (count > 512)
+ WR(0x84, 3);
+ w3(0); w2(0x24);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(4); WR(0x84, 0);
+ break;
+ case 4:
+ if (count > 512)
+ WR(0x84, 3);
+ w3(0); w2(0x24);
+ for (k = 0; k < count / 2; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(4); WR(0x84, 0);
+ break;
+ case 5:
+ if (count > 512)
+ WR(0x84, 3);
+ w3(0); w2(0x24);
+ for (k = 0; k < count / 4; k++)
+ ((u32 *)buf)[k] = r4l();
+ w2(4); WR(0x84, 0);
+ break;
+ }
+}
+
+static void epia_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int ph, k, last, d;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(0xa1); w2(1); w2(3); w2(1); w2(5);
+ ph = 0; last = 0x8000;
+ for (k = 0; k < count; k++) {
+ d = buf[k];
+ if (d != last) {
+ last = d;
+ w0(d);
+ }
+ w2(4 + ph);
+ ph = 1 - ph;
+ }
+ w2(7); w2(4);
+ break;
+ case 3:
+ if (count < 512)
+ WR(0x84, 1);
+ w3(0x40);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ if (count < 512)
+ WR(0x84, 0);
+ break;
+ case 4:
+ if (count < 512)
+ WR(0x84, 1);
+ w3(0x40);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ if (count < 512)
+ WR(0x84, 0);
+ break;
+ case 5:
+ if (count < 512)
+ WR(0x84, 1);
+ w3(0x40);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ if (count < 512)
+ WR(0x84, 0);
+ break;
+ }
+}
+
+static int epia_test_proto(struct pi_adapter *pi)
+{
+ int j, k, f;
+ int e[2] = { 0, 0 };
+ char scratch[512];
+
+ epia_connect(pi);
+ for (j = 0; j < 2; j++) {
+ WR(6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ WR(2, k ^ 0xaa);
+ WR(3, k ^ 0x55);
+ if (RR(2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ WR(2, 1); WR(3, 1);
+ }
+ epia_disconnect(pi);
+
+ f = 0;
+ epia_connect(pi);
+ WR(0x84, 8);
+ epia_read_block(pi, scratch, 512);
+ for (k = 0; k < 256; k++) {
+ if ((scratch[2 * k] & 0xff) != ((k + 1) & 0xff))
+ f++;
+ if ((scratch[2 * k + 1] & 0xff) != ((-2 - k) & 0xff))
+ f++;
+ }
+ WR(0x84, 0);
+ epia_disconnect(pi);
+
+ dev_dbg(&pi->dev, "epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+ pi->port, pi->mode, e[0], e[1], f);
+
+ return (e[0] && e[1]) || f;
+}
+
+
+static void epia_log_adapter(struct pi_adapter *pi)
+{
+ char *mode[6] = { "4-bit", "5/3", "8-bit", "EPP-8", "EPP-16", "EPP-32"};
+
+ dev_info(&pi->dev,
+ "Shuttle EPIA at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode[pi->mode], pi->delay);
+}
+
+static struct pi_protocol epia = {
+ .owner = THIS_MODULE,
+ .name = "epia",
+ .max_mode = 6,
+ .epp_first = 3,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = epia_write_regr,
+ .read_regr = epia_read_regr,
+ .write_block = epia_write_block,
+ .read_block = epia_read_block,
+ .connect = epia_connect,
+ .disconnect = epia_disconnect,
+ .test_proto = epia_test_proto,
+ .log_adapter = epia_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Shuttle Technologies EPIA parallel port IDE adapter "
+ "protocol driver");
+module_pata_parport_driver(epia);
diff --git a/drivers/ata/pata_parport/fit2.c b/drivers/ata/pata_parport/fit2.c
new file mode 100644
index 0000000000..de79cf91ad
--- /dev/null
+++ b/drivers/ata/pata_parport/fit2.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1998 Grant R. Guenther <grant@torque.net>
+ *
+ * fit2.c is a low-level protocol driver for the older version
+ * of the Fidelity International Technology parallel port adapter.
+ * This adapter is used in their TransDisk 2000 and older TransDisk
+ * 3000 portable hard-drives. As far as I can tell, this device
+ * supports 4-bit mode _only_.
+ *
+ * Newer models of the FIT products use an enhanced protocol.
+ * The "fit3" protocol module should support current drives.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a, b) (((a >> 4) & 0x0f) | (b & 0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ *
+ * NB: The FIT adapter does not appear to use the control registers.
+ * So, we map ALT_STATUS to STATUS and NO-OP writes to the device
+ * control register - this means that IDE reset will not work on these
+ * devices.
+ */
+
+static void fit2_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ if (cont == 1)
+ return;
+ w2(0xc); w0(regr); w2(4); w0(val); w2(5); w0(0); w2(4);
+}
+
+static int fit2_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ if (cont) {
+ if (regr != 6)
+ return 0xff;
+ r = 7;
+ } else {
+ r = regr + 0x10;
+ }
+
+ w2(0xc); w0(r); w2(4); w2(5);
+ w0(0); a = r1();
+ w0(1); b = r1();
+ w2(4);
+
+ return j44(a, b);
+}
+
+static void fit2_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b, c, d;
+
+ w2(0xc); w0(0x10);
+
+ for (k = 0; k < count / 4; k++) {
+ w2(4); w2(5);
+ w0(0); a = r1(); w0(1); b = r1();
+ w0(3); c = r1(); w0(2); d = r1();
+ buf[4 * k + 0] = j44(a, b);
+ buf[4 * k + 1] = j44(d, c);
+
+ w2(4); w2(5);
+ a = r1(); w0(3); b = r1();
+ w0(1); c = r1(); w0(0); d = r1();
+ buf[4 * k + 2] = j44(d, c);
+ buf[4 * k + 3] = j44(a, b);
+ }
+
+ w2(4);
+}
+
+static void fit2_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ w2(0xc); w0(0);
+ for (k = 0; k < count / 2; k++) {
+ w2(4); w0(buf[2 * k]);
+ w2(5); w0(buf[2 * k + 1]);
+ }
+ w2(4);
+}
+
+static void fit2_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xcc);
+}
+
+static void fit2_disconnect(struct pi_adapter *pi)
+{
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void fit2_log_adapter(struct pi_adapter *pi)
+{
+ dev_info(&pi->dev, "FIT 2000 adapter at 0x%x, delay %d\n",
+ pi->port, pi->delay);
+
+}
+
+static struct pi_protocol fit2 = {
+ .owner = THIS_MODULE,
+ .name = "fit2",
+ .max_mode = 1,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = fit2_write_regr,
+ .read_regr = fit2_read_regr,
+ .write_block = fit2_write_block,
+ .read_block = fit2_read_block,
+ .connect = fit2_connect,
+ .disconnect = fit2_disconnect,
+ .log_adapter = fit2_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Fidelity International Technology parallel port IDE adapter"
+ "(older models) protocol driver");
+module_pata_parport_driver(fit2);
diff --git a/drivers/ata/pata_parport/fit3.c b/drivers/ata/pata_parport/fit3.c
new file mode 100644
index 0000000000..d2b81cf2e1
--- /dev/null
+++ b/drivers/ata/pata_parport/fit3.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1998 Grant R. Guenther <grant@torque.net>
+ *
+ * fit3.c is a low-level protocol driver for newer models
+ * of the Fidelity International Technology parallel port adapter.
+ * This adapter is used in their TransDisk 3000 portable
+ * hard-drives, as well as CD-ROM, PD-CD and other devices.
+ *
+ * The TD-2000 and certain older devices use a different protocol.
+ * Try the fit2 protocol module with them.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a, b) (((a >> 3) & 0x0f) | ((b << 1) & 0xf0))
+
+#define w7(byte) out_p(7, byte)
+#define r7() (in_p(7) & 0xff)
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+
+static void fit3_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ regr += cont << 3;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w2(0xc); w0(regr); w2(0x8); w2(0xc);
+ w0(val); w2(0xd);
+ w0(0); w2(0xc);
+ break;
+ case 2:
+ w2(0xc); w0(regr); w2(0x8); w2(0xc);
+ w4(val); w4(0);
+ w2(0xc);
+ break;
+ }
+}
+
+static int fit3_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b;
+
+ regr += cont << 3;
+
+ switch (pi->mode) {
+ case 0:
+ w2(0xc); w0(regr + 0x10); w2(0x8); w2(0xc);
+ w2(0xd); a = r1();
+ w2(0xf); b = r1();
+ w2(0xc);
+ return j44(a, b);
+ case 1:
+ w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
+ w2(0xec); w2(0xee); w2(0xef); a = r0();
+ w2(0xc);
+ return a;
+ case 2:
+ w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
+ w2(0xec);
+ a = r4(); b = r4();
+ w2(0xc);
+ return a;
+ }
+
+ return -1;
+}
+
+static void fit3_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b, c, d;
+
+ switch (pi->mode) {
+ case 0:
+ w2(0xc); w0(0x10); w2(0x8); w2(0xc);
+ for (k = 0; k < count / 2; k++) {
+ w2(0xd); a = r1();
+ w2(0xf); b = r1();
+ w2(0xc); c = r1();
+ w2(0xe); d = r1();
+ buf[2 * k] = j44(a, b);
+ buf[2 * k + 1] = j44(c, d);
+ }
+ w2(0xc);
+ break;
+ case 1:
+ w2(0xc); w0(0x90); w2(0x8); w2(0xc);
+ w2(0xec); w2(0xee);
+ for (k = 0; k < count / 2; k++) {
+ w2(0xef); a = r0();
+ w2(0xee); b = r0();
+ buf[2 * k] = a;
+ buf[2 * k + 1] = b;
+ }
+ w2(0xec);
+ w2(0xc);
+ break;
+ case 2:
+ w2(0xc); w0(0x90); w2(0x8); w2(0xc);
+ w2(0xec);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(0xc);
+ break;
+ }
+}
+
+static void fit3_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w2(0xc); w0(0); w2(0x8); w2(0xc);
+ for (k = 0; k < count / 2; k++) {
+ w0(buf[2 * k]); w2(0xd);
+ w0(buf[2 * k + 1]); w2(0xc);
+ }
+ break;
+ case 2:
+ w2(0xc); w0(0); w2(0x8); w2(0xc);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(0xc);
+ break;
+ }
+}
+
+static void fit3_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xc); w0(0); w2(0xa);
+ if (pi->mode == 2) {
+ w2(0xc); w0(0x9);
+ w2(0x8); w2(0xc);
+ }
+}
+
+static void fit3_disconnect(struct pi_adapter *pi)
+{
+ w2(0xc); w0(0xa); w2(0x8); w2(0xc);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void fit3_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[3] = { "4-bit", "8-bit", "EPP"};
+
+ dev_info(&pi->dev,
+ "FIT 3000 adapter at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol fit3 = {
+ .owner = THIS_MODULE,
+ .name = "fit3",
+ .max_mode = 3,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = fit3_write_regr,
+ .read_regr = fit3_read_regr,
+ .write_block = fit3_write_block,
+ .read_block = fit3_read_block,
+ .connect = fit3_connect,
+ .disconnect = fit3_disconnect,
+ .log_adapter = fit3_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Fidelity International Technology parallel port IDE adapter"
+ "(newer models) protocol driver");
+module_pata_parport_driver(fit3);
diff --git a/drivers/ata/pata_parport/friq.c b/drivers/ata/pata_parport/friq.c
new file mode 100644
index 0000000000..7abe2ff406
--- /dev/null
+++ b/drivers/ata/pata_parport/friq.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1998 Grant R. Guenther <grant@torque.net>
+ *
+ * friq.c is a low-level protocol driver for the Freecom "IQ"
+ * parallel port IDE adapter. Early versions of this adapter
+ * use the 'frpw' protocol.
+ *
+ * Freecom uses this adapter in a battery powered external
+ * CD-ROM drive. It is also used in LS-120 drives by
+ * Maxell and Panasonic, and other devices.
+ *
+ * The battery powered drive requires software support to
+ * control the power to the drive. This module enables the
+ * drive power when the high level driver (pcd) is loaded
+ * and disables it when the module is unloaded. Note, if
+ * the friq module is built in to the kernel, the power
+ * will never be switched off, so other means should be
+ * used to conserve battery power.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define CMD(x) \
+ do { \
+ w2(4); w0(0xff); w0(0xff); w0(0x73); w0(0x73); \
+ w0(0xc9); w0(0xc9); w0(0x26); \
+ w0(0x26); w0(x); w0(x); \
+ } while (0)
+
+#define j44(l, h) (((l >> 4) & 0x0f) | (h & 0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x08, 0x10 };
+
+static int friq_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int h, l, r;
+
+ r = regr + cont_map[cont];
+
+ CMD(r);
+ w2(6); l = r1();
+ w2(4); h = r1();
+ w2(4);
+
+ return j44(l, h);
+}
+
+static void friq_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ CMD(r);
+ w0(val);
+ w2(5); w2(7); w2(5); w2(4);
+}
+
+static void friq_read_block_int(struct pi_adapter *pi, char *buf, int count, int regr)
+{
+ int h, l, k, ph;
+
+ switch (pi->mode) {
+ case 0:
+ CMD(regr);
+ for (k = 0; k < count; k++) {
+ w2(6); l = r1();
+ w2(4); h = r1();
+ buf[k] = j44(l, h);
+ }
+ w2(4);
+ break;
+ case 1:
+ ph = 2;
+ CMD(regr + 0xc0);
+ w0(0xff);
+ for (k = 0; k < count; k++) {
+ w2(0xa4 + ph);
+ buf[k] = r0();
+ ph = 2 - ph;
+ }
+ w2(0xac); w2(0xa4); w2(4);
+ break;
+ case 2:
+ CMD(regr + 0x80);
+ for (k = 0; k < count - 2; k++)
+ buf[k] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ case 3:
+ CMD(regr + 0x80);
+ for (k = 0; k < count / 2 - 1; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ case 4:
+ CMD(regr + 0x80);
+ for (k = 0; k < count / 4 - 1; k++)
+ ((u32 *)buf)[k] = r4l();
+ buf[count - 4] = r4();
+ buf[count - 3] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ }
+}
+
+static void friq_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ friq_read_block_int(pi, buf, count, 0x08);
+}
+
+static void friq_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ CMD(8); w2(5);
+ for (k = 0; k < count; k++) {
+ w0(buf[k]);
+ w2(7); w2(5);
+ }
+ w2(4);
+ break;
+ case 2:
+ CMD(0xc8); w2(5);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(4);
+ break;
+ case 3:
+ CMD(0xc8); w2(5);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(4);
+ break;
+ case 4:
+ CMD(0xc8); w2(5);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(4);
+ break;
+}
+}
+
+static void friq_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4);
+}
+
+static void friq_disconnect(struct pi_adapter *pi)
+{
+ CMD(0x20);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static int friq_test_proto(struct pi_adapter *pi)
+{
+ int j, k, r;
+ int e[2] = { 0, 0 };
+ char scratch[512];
+
+ pi->saved_r0 = r0();
+ w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */
+ udelay(500);
+ w0(pi->saved_r0);
+
+ friq_connect(pi);
+ for (j = 0; j < 2; j++) {
+ friq_write_regr(pi, 0, 6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ friq_write_regr(pi, 0, 2, k ^ 0xaa);
+ friq_write_regr(pi, 0, 3, k ^ 0x55);
+ if (friq_read_regr(pi, 0, 2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ }
+ friq_disconnect(pi);
+
+ friq_connect(pi);
+ friq_read_block_int(pi, scratch, 512, 0x10);
+ r = 0;
+ for (k = 0; k < 128; k++) {
+ if (scratch[k] != k)
+ r++;
+ }
+ friq_disconnect(pi);
+
+ dev_dbg(&pi->dev,
+ "friq: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+ pi->port, pi->mode, e[0], e[1], r);
+
+ return r || (e[0] && e[1]);
+}
+
+static void friq_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[6] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32"};
+
+ dev_info(&pi->dev,
+ "Freecom IQ ASIC-2 adapter at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+
+ pi->private = 1;
+ friq_connect(pi);
+ CMD(0x9e); /* disable sleep timer */
+ friq_disconnect(pi);
+}
+
+static void friq_release_proto(struct pi_adapter *pi)
+{
+ if (pi->private) { /* turn off the power */
+ friq_connect(pi);
+ CMD(0x1d); CMD(0x1e);
+ friq_disconnect(pi);
+ pi->private = 0;
+ }
+}
+
+static struct pi_protocol friq = {
+ .owner = THIS_MODULE,
+ .name = "friq",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = friq_write_regr,
+ .read_regr = friq_read_regr,
+ .write_block = friq_write_block,
+ .read_block = friq_read_block,
+ .connect = friq_connect,
+ .disconnect = friq_disconnect,
+ .test_proto = friq_test_proto,
+ .log_adapter = friq_log_adapter,
+ .release_proto = friq_release_proto,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Freecom IQ parallel port IDE adapter protocol driver");
+module_pata_parport_driver(friq);
diff --git a/drivers/ata/pata_parport/frpw.c b/drivers/ata/pata_parport/frpw.c
new file mode 100644
index 0000000000..7fa9b98573
--- /dev/null
+++ b/drivers/ata/pata_parport/frpw.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1996-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * frpw.c is a low-level protocol driver for the Freecom "Power" parallel port
+ * IDE adapter.
+ *
+ * Some applications of this adapter may require a "printer" reset prior to
+ * loading the driver. This can be done by loading and unloading the "lp"
+ * driver, or it can be done by this driver if you define FRPW_HARD_RESET.
+ * The latter is not recommended as it may upset devices on other ports.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
+#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x08, 0x10 };
+
+static int frpw_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int h, l, r;
+
+ r = regr + cont_map[cont];
+
+ w2(4);
+ w0(r); cec4;
+ w2(6); l = r1();
+ w2(4); h = r1();
+ w2(4);
+
+ return j44(l, h);
+}
+
+static void frpw_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ w2(4); w0(r); cec4;
+ w0(val);
+ w2(5); w2(7); w2(5); w2(4);
+}
+
+static void frpw_read_block_int(struct pi_adapter *pi, char *buf, int count,
+ int regr)
+{
+ int h, l, k, ph;
+
+ switch (pi->mode) {
+ case 0:
+ w2(4); w0(regr); cec4;
+ for (k = 0; k < count; k++) {
+ w2(6); l = r1();
+ w2(4); h = r1();
+ buf[k] = j44(l, h);
+ }
+ w2(4);
+ break;
+
+ case 1:
+ ph = 2;
+ w2(4); w0(regr + 0xc0); cec4;
+ w0(0xff);
+ for (k = 0; k < count; k++) {
+ w2(0xa4 + ph);
+ buf[k] = r0();
+ ph = 2 - ph;
+ }
+ w2(0xac); w2(0xa4); w2(4);
+ break;
+
+ case 2:
+ w2(4); w0(regr + 0x80); cec4;
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(0xac); w2(0xa4);
+ w2(4);
+ break;
+
+ case 3:
+ w2(4); w0(regr + 0x80); cec4;
+ for (k = 0; k < count - 2; k++)
+ buf[k] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+
+ case 4:
+ w2(4); w0(regr + 0x80); cec4;
+ for (k = 0; k < count / 2 - 1; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+
+ case 5:
+ w2(4); w0(regr + 0x80); cec4;
+ for (k = 0; k < count / 4 - 1; k++)
+ ((u32 *)buf)[k] = r4l();
+ buf[count - 4] = r4();
+ buf[count - 3] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count - 2] = r4();
+ buf[count - 1] = r4();
+ w2(4);
+ break;
+ }
+}
+
+static void frpw_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ frpw_read_block_int(pi, buf, count, 0x08);
+}
+
+static void frpw_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w2(4); w0(8); cec4; w2(5);
+ for (k = 0; k < count; k++) {
+ w0(buf[k]);
+ w2(7); w2(5);
+ }
+ w2(4);
+ break;
+
+ case 3:
+ w2(4); w0(0xc8); cec4; w2(5);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(4);
+ break;
+
+ case 4:
+ w2(4); w0(0xc8); cec4; w2(5);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(4);
+ break;
+
+ case 5:
+ w2(4); w0(0xc8); cec4; w2(5);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(4);
+ break;
+ }
+}
+
+static void frpw_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4);
+}
+
+static void frpw_disconnect(struct pi_adapter *pi)
+{
+ w2(4); w0(0x20); cec4;
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+/*
+ * Stub logic to see if PNP string is available - used to distinguish
+ * between the Xilinx and ASIC implementations of the Freecom adapter.
+ * returns chip_type: 0 = Xilinx, 1 = ASIC
+ */
+static int frpw_test_pnp(struct pi_adapter *pi)
+{
+ int olddelay, a, b;
+
+#ifdef FRPW_HARD_RESET
+ w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */
+ mdelay(1500);
+#endif
+
+ olddelay = pi->delay;
+ pi->delay = 10;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4); w0(4); w2(6); w2(7);
+ a = r1() & 0xff; w2(4); b = r1() & 0xff;
+ w2(0xc); w2(0xe); w2(4);
+
+ pi->delay = olddelay;
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+
+ return ((~a & 0x40) && (b & 0x40));
+}
+
+/*
+ * We use the pi->private to remember the result of the PNP test.
+ * To make this work, private = port*2 + chip. Yes, I know it's a hack :-(
+ */
+static int frpw_test_proto(struct pi_adapter *pi)
+{
+ int j, k, r;
+ int e[2] = { 0, 0 };
+ char scratch[512];
+
+ if ((pi->private >> 1) != pi->port)
+ pi->private = frpw_test_pnp(pi) + 2*pi->port;
+
+ if (((pi->private & 0x1) == 0) && (pi->mode > 2)) {
+ dev_dbg(&pi->dev,
+ "frpw: Xilinx does not support mode %d\n", pi->mode);
+ return 1;
+ }
+
+ if (((pi->private & 0x1) == 1) && (pi->mode == 2)) {
+ dev_dbg(&pi->dev, "frpw: ASIC does not support mode 2\n");
+ return 1;
+ }
+
+ frpw_connect(pi);
+ for (j = 0; j < 2; j++) {
+ frpw_write_regr(pi, 0, 6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ frpw_write_regr(pi, 0, 2, k ^ 0xaa);
+ frpw_write_regr(pi, 0, 3, k ^ 0x55);
+ if (frpw_read_regr(pi, 0, 2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ }
+ frpw_disconnect(pi);
+
+ frpw_connect(pi);
+ frpw_read_block_int(pi, scratch, 512, 0x10);
+ r = 0;
+ for (k = 0; k < 128; k++) {
+ if (scratch[k] != k)
+ r++;
+ }
+ frpw_disconnect(pi);
+
+ dev_dbg(&pi->dev,
+ "frpw: port 0x%x, chip %ld, mode %d, test=(%d,%d,%d)\n",
+ pi->port, (pi->private%2), pi->mode, e[0], e[1], r);
+
+ return r || (e[0] && e[1]);
+}
+
+static void frpw_log_adapter(struct pi_adapter *pi)
+
+{
+ char *mode[6] = { "4-bit", "8-bit", "EPP", "EPP-8", "EPP-16", "EPP-32"};
+
+ dev_info(&pi->dev,
+ "Freecom (%s) adapter at 0x%x, mode %d (%s), delay %d\n",
+ ((pi->private & 0x1) == 0) ? "Xilinx" : "ASIC",
+ pi->port, pi->mode, mode[pi->mode], pi->delay);
+}
+
+static struct pi_protocol frpw = {
+ .owner = THIS_MODULE,
+ .name = "frpw",
+ .max_mode = 6,
+ .epp_first = 2,
+ .default_delay = 2,
+ .max_units = 1,
+ .write_regr = frpw_write_regr,
+ .read_regr = frpw_read_regr,
+ .write_block = frpw_write_block,
+ .read_block = frpw_read_block,
+ .connect = frpw_connect,
+ .disconnect = frpw_disconnect,
+ .test_proto = frpw_test_proto,
+ .log_adapter = frpw_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Freecom Power parallel port IDE adapter protocol driver");
+module_pata_parport_driver(frpw);
diff --git a/drivers/ata/pata_parport/kbic.c b/drivers/ata/pata_parport/kbic.c
new file mode 100644
index 0000000000..fca322627b
--- /dev/null
+++ b/drivers/ata/pata_parport/kbic.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * This is a low-level driver for the KBIC-951A and KBIC-971A
+ * parallel to IDE adapter chips from KingByte Information Systems.
+ *
+ * The chips are almost identical, however, the wakeup code
+ * required for the 971A interferes with the correct operation of
+ * the 951A, so this driver registers itself twice, once for
+ * each chip.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define r12w() (delay_p, inw(pi->port + 1) & 0xffff)
+
+#define j44(a, b) ((((a >> 4) & 0x0f) | (b & 0xf0)) ^ 0x88)
+#define j53(w) (((w >> 3) & 0x1f) | ((w >> 4) & 0xe0))
+
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x80, 0x40 };
+
+static int kbic_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, s;
+
+ s = cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ w0(regr | 0x18 | s); w2(4); w2(6); w2(4); w2(1); w0(8);
+ a = r1(); w0(0x28); b = r1(); w2(4);
+ return j44(a, b);
+ case 1:
+ w0(regr|0x38 | s); w2(4); w2(6); w2(4); w2(5); w0(8);
+ a = r12w(); w2(4);
+ return j53(a);
+ case 2:
+ w0(regr | 0x08 | s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1);
+ a = r0(); w2(4);
+ return a;
+ case 3:
+ case 4:
+ case 5:
+ w0(0x20 | s); w2(4); w2(6); w2(4); w3(regr);
+ a = r4(); b = r4(); w2(4); w2(0); w2(4);
+ return a;
+ }
+
+ return -1;
+}
+
+static void kbic_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int s = cont_map[cont];
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(regr | 0x10 | s); w2(4); w2(6); w2(4);
+ w0(val); w2(5); w2(4);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ w0(0x20 | s); w2(4); w2(6); w2(4); w3(regr);
+ w4(val); w4(val);
+ w2(4); w2(0); w2(4);
+ break;
+ }
+}
+
+static void k951_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4);
+}
+
+static void k951_disconnect(struct pi_adapter *pi)
+{
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+#define CCP(x) \
+ do { \
+ w2(0xc4); w0(0xaa); w0(0x55); \
+ w0(0); w0(0xff); w0(0x87); \
+ w0(0x78); w0(x); w2(0xc5); \
+ w2(0xc4); w0(0xff); \
+ } while (0)
+
+static void k971_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ CCP(0x20);
+ w2(4);
+}
+
+static void k971_disconnect(struct pi_adapter *pi)
+{
+ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+/*
+ * count must be congruent to 0 MOD 4, but all known applications
+ *have this property.
+ */
+static void kbic_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b;
+
+ switch (pi->mode) {
+ case 0:
+ w0(0x98); w2(4); w2(6); w2(4);
+ for (k = 0; k < count / 2; k++) {
+ w2(1); w0(8);
+ a = r1();
+ w0(0x28);
+ b = r1();
+ buf[2 * k] = j44(a, b);
+ w2(5);
+ b = r1();
+ w0(8);
+ a = r1();
+ buf[2 * k + 1] = j44(a, b);
+ w2(4);
+ }
+ break;
+ case 1:
+ w0(0xb8); w2(4); w2(6); w2(4);
+ for (k = 0; k < count / 4; k++) {
+ w0(0xb8);
+ w2(4); w2(5);
+ w0(8);
+ buf[4 * k] = j53(r12w());
+ w0(0xb8);
+ buf[4 * k + 1] = j53(r12w());
+ w2(4); w2(5);
+ buf[4 * k + 3] = j53(r12w());
+ w0(8);
+ buf[4 * k + 2] = j53(r12w());
+ }
+ w2(4);
+ break;
+ case 2:
+ w0(0x88); w2(4); w2(6); w2(4);
+ for (k = 0; k < count / 2; k++) {
+ w2(0xa0); w2(0xa1);
+ buf[2 * k] = r0();
+ w2(0xa5);
+ buf[2 * k + 1] = r0();
+ }
+ w2(4);
+ break;
+ case 3:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(4); w2(0); w2(4);
+ break;
+ case 4:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 2; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(4); w2(0); w2(4);
+ break;
+ case 5:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 4; k++)
+ ((u32 *)buf)[k] = r4l();
+ w2(4); w2(0); w2(4);
+ break;
+ }
+}
+
+static void kbic_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ case 2:
+ w0(0x90); w2(4); w2(6); w2(4);
+ for (k = 0; k < count / 2; k++) {
+ w0(buf[2 * k + 1]);
+ w2(0); w2(4);
+ w0(buf[2 * k]);
+ w2(5); w2(4);
+ }
+ break;
+ case 3:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 2; k++) {
+ w4(buf[2 * k + 1]);
+ w4(buf[2 * k]);
+ }
+ w2(4); w2(0); w2(4);
+ break;
+ case 4:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 2; k++)
+ w4w(swab16(((u16 *)buf)[k]));
+ w2(4); w2(0); w2(4);
+ break;
+ case 5:
+ w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k = 0; k < count / 4; k++)
+ w4l(swab16(((u16 *)buf)[2 * k]) |
+ swab16(((u16 *)buf)[2 * k + 1]) << 16);
+ w2(4); w2(0); w2(4);
+ break;
+ }
+}
+
+static void kbic_log_adapter(struct pi_adapter *pi, char *chip)
+{
+ char *mode[6] = { "4-bit", "5/3", "8-bit", "EPP-8", "EPP_16", "EPP-32"};
+
+ dev_info(&pi->dev, "KingByte %s at 0x%x, mode %d (%s), delay %d\n",
+ chip, pi->port, pi->mode, mode[pi->mode], pi->delay);
+}
+
+static void k951_log_adapter(struct pi_adapter *pi)
+{
+ kbic_log_adapter(pi, "KBIC-951A");
+}
+
+static void k971_log_adapter(struct pi_adapter *pi)
+{
+ kbic_log_adapter(pi, "KBIC-971A");
+}
+
+static struct pi_protocol k951 = {
+ .owner = THIS_MODULE,
+ .name = "k951",
+ .max_mode = 6,
+ .epp_first = 3,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = kbic_write_regr,
+ .read_regr = kbic_read_regr,
+ .write_block = kbic_write_block,
+ .read_block = kbic_read_block,
+ .connect = k951_connect,
+ .disconnect = k951_disconnect,
+ .log_adapter = k951_log_adapter,
+};
+
+static struct pi_protocol k971 = {
+ .owner = THIS_MODULE,
+ .name = "k971",
+ .max_mode = 6,
+ .epp_first = 3,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = kbic_write_regr,
+ .read_regr = kbic_read_regr,
+ .write_block = kbic_write_block,
+ .read_block = kbic_read_block,
+ .connect = k971_connect,
+ .disconnect = k971_disconnect,
+ .log_adapter = k971_log_adapter,
+};
+
+static int __init kbic_init(void)
+{
+ int rv;
+
+ rv = pata_parport_register_driver(&k951);
+ if (rv < 0)
+ return rv;
+ rv = pata_parport_register_driver(&k971);
+ if (rv < 0)
+ pata_parport_unregister_driver(&k951);
+ return rv;
+}
+
+static void __exit kbic_exit(void)
+{
+ pata_parport_unregister_driver(&k951);
+ pata_parport_unregister_driver(&k971);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("KingByte Information Systems KBIC-951A and KBIC-971A "
+ "parallel port IDE adapter protocol driver");
+module_init(kbic_init)
+module_exit(kbic_exit)
diff --git a/drivers/ata/pata_parport/ktti.c b/drivers/ata/pata_parport/ktti.c
new file mode 100644
index 0000000000..c078d19348
--- /dev/null
+++ b/drivers/ata/pata_parport/ktti.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1998 Grant R. Guenther <grant@torque.net>
+ *
+ * ktti.c is a low-level protocol driver for the KT Technology
+ * parallel port adapter. This adapter is used in the "PHd"
+ * portable hard-drives. As far as I can tell, this device
+ * supports 4-bit mode _only_.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define j44(a, b) (((a >> 4) & 0x0f) | (b & 0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+static int cont_map[2] = { 0x10, 0x08 };
+
+static void ktti_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = regr + cont_map[cont];
+
+ w0(r); w2(0xb); w2(0xa); w2(3); w2(6);
+ w0(val); w2(3); w0(0); w2(6); w2(0xb);
+}
+
+static int ktti_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = regr + cont_map[cont];
+
+ w0(r); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
+ a = r1(); w2(0xc); b = r1(); w2(9); w2(0xc); w2(9);
+ return j44(a, b);
+}
+
+static void ktti_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b;
+
+ for (k = 0; k < count / 2; k++) {
+ w0(0x10); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
+ a = r1(); w2(0xc); b = r1(); w2(9);
+ buf[2*k] = j44(a, b);
+ a = r1(); w2(0xc); b = r1(); w2(9);
+ buf[2*k+1] = j44(a, b);
+ }
+}
+
+static void ktti_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ for (k = 0; k < count / 2; k++) {
+ w0(0x10); w2(0xb); w2(0xa); w2(3); w2(6);
+ w0(buf[2 * k]); w2(3);
+ w0(buf[2 * k + 1]); w2(6);
+ w2(0xb);
+ }
+}
+
+static void ktti_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xb); w2(0xa); w0(0); w2(3); w2(6);
+}
+
+static void ktti_disconnect(struct pi_adapter *pi)
+{
+ w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void ktti_log_adapter(struct pi_adapter *pi)
+{
+ dev_info(&pi->dev, "KT adapter at 0x%x, delay %d\n",
+ pi->port, pi->delay);
+}
+
+static struct pi_protocol ktti = {
+ .owner = THIS_MODULE,
+ .name = "ktti",
+ .max_mode = 1,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = ktti_write_regr,
+ .read_regr = ktti_read_regr,
+ .write_block = ktti_write_block,
+ .read_block = ktti_read_block,
+ .connect = ktti_connect,
+ .disconnect = ktti_disconnect,
+ .log_adapter = ktti_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("KT Technology parallel port IDE adapter protocol driver");
+module_pata_parport_driver(ktti);
diff --git a/drivers/ata/pata_parport/on20.c b/drivers/ata/pata_parport/on20.c
new file mode 100644
index 0000000000..7c70e5b13a
--- /dev/null
+++ b/drivers/ata/pata_parport/on20.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1996-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * on20.c is a low-level protocol driver for the
+ * Onspec 90c20 parallel to IDE adapter.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+#define op(f) \
+ do { \
+ w2(4); w0(f); w2(5); w2(0xd); \
+ w2(5); w2(0xd); w2(5); w2(4); \
+ } while (0)
+
+#define vl(v) \
+ do { \
+ w2(4); w0(v); w2(5); \
+ w2(7); w2(5); w2(4); \
+ } while (0)
+
+#define j44(a, b) (((a >> 4) & 0x0f) | (b & 0xf0))
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+
+static int on20_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int h, l, r;
+
+ r = (regr << 2) + 1 + cont;
+
+ op(1); vl(r); op(0);
+
+ switch (pi->mode) {
+ case 0:
+ w2(4); w2(6); l = r1();
+ w2(4); w2(6); h = r1();
+ w2(4); w2(6); w2(4); w2(6); w2(4);
+ return j44(l, h);
+ case 1:
+ w2(4); w2(0x26); r = r0();
+ w2(4); w2(0x26); w2(4);
+ return r;
+ }
+
+ return -1;
+}
+
+static void on20_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = (regr << 2) + 1 + cont;
+
+ op(1); vl(r);
+ op(0); vl(val);
+ op(0); vl(val);
+}
+
+static void on20_connect(struct pi_adapter *pi)
+{
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4); w0(0); w2(0xc); w2(4); w2(6); w2(4); w2(6); w2(4);
+ if (pi->mode) {
+ op(2); vl(8); op(2); vl(9);
+ } else {
+ op(2); vl(0); op(2); vl(8);
+ }
+}
+
+static void on20_disconnect(struct pi_adapter *pi)
+{
+ w2(4); w0(7); w2(4); w2(0xc); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void on20_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, l, h;
+
+ op(1); vl(1); op(0);
+
+ for (k = 0; k < count; k++) {
+ if (pi->mode) {
+ w2(4); w2(0x26); buf[k] = r0();
+ } else {
+ w2(6); l = r1(); w2(4);
+ w2(6); h = r1(); w2(4);
+ buf[k] = j44(l, h);
+ }
+ }
+ w2(4);
+}
+
+static void on20_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ op(1); vl(1); op(0);
+
+ for (k = 0; k < count; k++) {
+ w2(5); w0(buf[k]); w2(7);
+ }
+ w2(4);
+}
+
+static void on20_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[2] = { "4-bit", "8-bit" };
+
+ dev_info(&pi->dev,
+ "OnSpec 90c20 at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol on20 = {
+ .owner = THIS_MODULE,
+ .name = "on20",
+ .max_mode = 2,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = on20_write_regr,
+ .read_regr = on20_read_regr,
+ .write_block = on20_write_block,
+ .read_block = on20_read_block,
+ .connect = on20_connect,
+ .disconnect = on20_disconnect,
+ .log_adapter = on20_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Onspec 90c20 parallel port IDE adapter protocol driver");
+module_pata_parport_driver(on20);
diff --git a/drivers/ata/pata_parport/on26.c b/drivers/ata/pata_parport/on26.c
new file mode 100644
index 0000000000..c88e5d6f20
--- /dev/null
+++ b/drivers/ata/pata_parport/on26.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (c) 1997-1998 Grant R. Guenther <grant@torque.net>
+ *
+ * on26.c is a low-level protocol driver for the
+ * OnSpec 90c26 parallel to IDE adapter chip.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "pata_parport.h"
+
+/*
+ * mode codes: 0 nybble reads, 8-bit writes
+ * 1 8-bit reads and writes
+ * 2 8-bit EPP mode
+ * 3 EPP-16
+ * 4 EPP-32
+ */
+
+#define j44(a, b) (((a >> 4) & 0x0f) | (b & 0xf0))
+
+#define P1 \
+ do { \
+ w2(5); w2(0xd); w2(5); w2(0xd); w2(5); w2(4); \
+ } while (0)
+
+#define P2 \
+ do { \
+ w2(5); w2(7); w2(5); w2(4); \
+ } while (0)
+
+/*
+ * cont = 0 - access the IDE register file
+ * cont = 1 - access the IDE command set
+ */
+
+static int on26_read_regr(struct pi_adapter *pi, int cont, int regr)
+{
+ int a, b, r;
+
+ r = (regr << 2) + 1 + cont;
+
+ switch (pi->mode) {
+ case 0:
+ w0(1); P1; w0(r); P2; w0(0); P1;
+ w2(6); a = r1(); w2(4);
+ w2(6); b = r1(); w2(4);
+ w2(6); w2(4); w2(6); w2(4);
+ return j44(a, b);
+ case 1:
+ w0(1); P1; w0(r); P2; w0(0); P1;
+ w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
+ return a;
+ case 2:
+ case 3:
+ case 4:
+ w3(1); w3(1); w2(5); w4(r); w2(4);
+ w3(0); w3(0); w2(0x24); a = r4(); w2(4);
+ w2(0x24); (void)r4(); w2(4);
+ return a;
+ }
+
+ return -1;
+}
+
+static void on26_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
+{
+ int r = (regr << 2) + 1 + cont;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(1); P1; w0(r); P2; w0(0); P1;
+ w0(val); P2; w0(val); P2;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ w3(1); w3(1); w2(5); w4(r); w2(4);
+ w3(0); w3(0);
+ w2(5); w4(val); w2(4);
+ w2(5); w4(val); w2(4);
+ break;
+ }
+}
+
+#define CCP(x) \
+ do { \
+ w0(0xfe); w0(0xaa); w0(0x55); w0(0); \
+ w0(0xff); w0(0x87); w0(0x78); w0(x); \
+ w2(4); w2(5); w2(4); w0(0xff); \
+ } while (0)
+
+static void on26_connect(struct pi_adapter *pi)
+{
+ int x;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ CCP(0x20);
+ if (pi->mode)
+ x = 9;
+ else
+ x = 8;
+
+ w0(2); P1; w0(8); P2;
+ w0(2); P1; w0(x); P2;
+}
+
+static void on26_disconnect(struct pi_adapter *pi)
+{
+ if (pi->mode >= 2) {
+ w3(4); w3(4); w3(4); w3(4);
+ } else {
+ w0(4); P1; w0(4); P1;
+ }
+ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+#define RESET_WAIT 200
+
+/* hard reset */
+static int on26_test_port(struct pi_adapter *pi)
+{
+ int i, m, d, x = 0, y = 0;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ d = pi->delay;
+ m = pi->mode;
+ pi->delay = 5;
+ pi->mode = 0;
+
+ w2(0xc);
+
+ CCP(0x30); CCP(0);
+
+ w0(0xfe); w0(0xaa); w0(0x55); w0(0); w0(0xff);
+ i = ((r1() & 0xf0) << 4); w0(0x87);
+ i |= (r1() & 0xf0); w0(0x78);
+ w0(0x20); w2(4); w2(5);
+ i |= ((r1() & 0xf0) >> 4);
+ w2(4); w0(0xff);
+
+ if (i == 0xb5f) {
+ w0(2); P1; w0(0); P2;
+ w0(3); P1; w0(0); P2;
+ w0(2); P1; w0(8); P2; udelay(100);
+ w0(2); P1; w0(0xa); P2; udelay(100);
+ w0(2); P1; w0(8); P2; udelay(1000);
+
+ on26_write_regr(pi, 0, 6, 0xa0);
+
+ for (i = 0; i < RESET_WAIT; i++) {
+ on26_write_regr(pi, 0, 6, 0xa0);
+ x = on26_read_regr(pi, 0, 7);
+ on26_write_regr(pi, 0, 6, 0xb0);
+ y = on26_read_regr(pi, 0, 7);
+ if (!((x & 0x80) || (y & 0x80)))
+ break;
+ mdelay(100);
+ }
+
+ if (i == RESET_WAIT)
+ dev_err(&pi->dev,
+ "on26: Device reset failed (%x,%x)\n", x, y);
+
+ w0(4); P1; w0(4); P1;
+ }
+
+ CCP(0x30);
+
+ pi->delay = d;
+ pi->mode = m;
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+
+ return 5;
+}
+
+static void on26_read_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k, a, b;
+
+ switch (pi->mode) {
+ case 0:
+ w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
+ udelay(10);
+ for (k = 0; k < count; k++) {
+ w2(6); a = r1();
+ w2(4); b = r1();
+ buf[k] = j44(a, b);
+ }
+ w0(2); P1; w0(8); P2;
+ break;
+ case 1:
+ w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
+ udelay(10);
+ for (k = 0; k < count / 2; k++) {
+ w2(0x26); buf[2 * k] = r0();
+ w2(0x24); buf[2 * k + 1] = r0();
+ }
+ w0(2); P1; w0(9); P2;
+ break;
+ case 2:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k = 0; k < count; k++)
+ buf[k] = r4();
+ w2(4);
+ break;
+ case 3:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k = 0; k < count / 2; k++)
+ ((u16 *)buf)[k] = r4w();
+ w2(4);
+ break;
+ case 4:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k = 0; k < count / 4; k++)
+ ((u32 *)buf)[k] = r4l();
+ w2(4);
+ break;
+ }
+}
+
+static void on26_write_block(struct pi_adapter *pi, char *buf, int count)
+{
+ int k;
+
+ switch (pi->mode) {
+ case 0:
+ case 1:
+ w0(1); P1; w0(1); P2;
+ w0(2); P1; w0(0x18 + pi->mode); P2; w0(0); P1;
+ udelay(10);
+ for (k = 0; k < count / 2; k++) {
+ w2(5); w0(buf[2 * k]);
+ w2(7); w0(buf[2 * k + 1]);
+ }
+ w2(5); w2(4);
+ w0(2); P1; w0(8 + pi->mode); P2;
+ break;
+ case 2:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k = 0; k < count; k++)
+ w4(buf[k]);
+ w2(0xc4);
+ break;
+ case 3:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k = 0; k < count / 2; k++)
+ w4w(((u16 *)buf)[k]);
+ w2(0xc4);
+ break;
+ case 4:
+ w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k = 0; k < count / 4; k++)
+ w4l(((u32 *)buf)[k]);
+ w2(0xc4);
+ break;
+ }
+}
+
+static void on26_log_adapter(struct pi_adapter *pi)
+{
+ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+ dev_info(&pi->dev,
+ "OnSpec 90c26 at 0x%x, mode %d (%s), delay %d\n",
+ pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static struct pi_protocol on26 = {
+ .owner = THIS_MODULE,
+ .name = "on26",
+ .max_mode = 5,
+ .epp_first = 2,
+ .default_delay = 1,
+ .max_units = 1,
+ .write_regr = on26_write_regr,
+ .read_regr = on26_read_regr,
+ .write_block = on26_write_block,
+ .read_block = on26_read_block,
+ .connect = on26_connect,
+ .disconnect = on26_disconnect,
+ .test_port = on26_test_port,
+ .log_adapter = on26_log_adapter,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
+MODULE_DESCRIPTION("Onspec 90c26 parallel port IDE adapter protocol driver");
+module_pata_parport_driver(on26);
diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c
new file mode 100644
index 0000000000..a7adfdcb5e
--- /dev/null
+++ b/drivers/ata/pata_parport/pata_parport.c
@@ -0,0 +1,836 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2023 Ondrej Zary
+ * based on paride.c by Grant R. Guenther <grant@torque.net>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/parport.h>
+#include "pata_parport.h"
+
+#define DRV_NAME "pata_parport"
+
+static DEFINE_IDR(parport_list);
+static DEFINE_IDR(protocols);
+static DEFINE_IDA(pata_parport_bus_dev_ids);
+static DEFINE_MUTEX(pi_mutex);
+
+static bool probe = true;
+module_param(probe, bool, 0644);
+MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
+
+/*
+ * libata drivers cannot sleep so this driver claims parport before activating
+ * the ata host and keeps it claimed (and protocol connected) until the ata
+ * host is removed. Unfortunately, this means that you cannot use any chained
+ * devices (neither other pata_parport devices nor a printer).
+ */
+static void pi_connect(struct pi_adapter *pi)
+{
+ parport_claim_or_block(pi->pardev);
+ pi->proto->connect(pi);
+}
+
+static void pi_disconnect(struct pi_adapter *pi)
+{
+ pi->proto->disconnect(pi);
+ parport_release(pi->pardev);
+}
+
+static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+ u8 tmp;
+
+ if (device == 0)
+ tmp = ATA_DEVICE_OBS;
+ else
+ tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+ pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
+ ata_sff_pause(ap);
+}
+
+static void pata_parport_set_devctl(struct ata_port *ap, u8 ctl)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ pi->proto->write_regr(pi, 1, 6, ctl);
+}
+
+static bool pata_parport_devchk(struct ata_port *ap, unsigned int device)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+ u8 nsect, lbal;
+
+ pata_parport_dev_select(ap, device);
+
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
+
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0xaa);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0x55);
+
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
+
+ nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+ lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+
+ return (nsect == 0x55) && (lbal == 0xaa);
+}
+
+static int pata_parport_wait_after_reset(struct ata_link *link,
+ unsigned int devmask,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ struct pi_adapter *pi = ap->host->private_data;
+ 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);
+ if (rc) {
+ /*
+ * some adapters return bogus values if master device is not
+ * present, so don't abort now if a slave device is present
+ */
+ if (!dev1)
+ return rc;
+ ret = -ENODEV;
+ }
+
+ /*
+ * if device 1 was found in ata_devchk, wait for register
+ * access briefly, then wait for BSY to clear.
+ */
+ if (dev1) {
+ int i;
+
+ pata_parport_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 = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+ lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+ if (nsect == 1 && lbal == 1)
+ break;
+ /* give drive a breather */
+ ata_msleep(ap, 50);
+ }
+
+ rc = ata_sff_wait_ready(link, deadline);
+ if (rc) {
+ if (rc != -ENODEV)
+ return rc;
+ ret = rc;
+ }
+ }
+
+ pata_parport_dev_select(ap, 0);
+ if (dev1)
+ pata_parport_dev_select(ap, 1);
+ if (dev0)
+ pata_parport_dev_select(ap, 0);
+
+ return ret;
+}
+
+static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
+ unsigned long deadline)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ /* software reset. causes dev0 to be selected */
+ pi->proto->write_regr(pi, 1, 6, ap->ctl);
+ udelay(20);
+ pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
+ udelay(20);
+ pi->proto->write_regr(pi, 1, 6, ap->ctl);
+ ap->last_ctl = ap->ctl;
+
+ /* wait the port to become ready */
+ return pata_parport_wait_after_reset(&ap->link, devmask, deadline);
+}
+
+static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ unsigned int devmask = 0;
+ int rc;
+ u8 err;
+
+ /* determine if device 0/1 are present */
+ if (pata_parport_devchk(ap, 0))
+ devmask |= (1 << 0);
+ if (pata_parport_devchk(ap, 1))
+ devmask |= (1 << 1);
+
+ /* select device 0 again */
+ pata_parport_dev_select(ap, 0);
+
+ /* issue bus reset */
+ rc = pata_parport_bus_softreset(ap, devmask, deadline);
+ if (rc && rc != -ENODEV) {
+ 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 (err != 0x81)
+ classes[1] = ata_sff_dev_classify(&link->device[1],
+ devmask & (1 << 1), &err);
+
+ return 0;
+}
+
+static u8 pata_parport_check_status(struct ata_port *ap)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ return pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
+}
+
+static u8 pata_parport_check_altstatus(struct ata_port *ap)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ return pi->proto->read_regr(pi, 1, 6);
+}
+
+static void pata_parport_tf_load(struct ata_port *ap,
+ const struct ata_taskfile *tf)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ if (tf->ctl != ap->last_ctl) {
+ pi->proto->write_regr(pi, 1, 6, tf->ctl);
+ ap->last_ctl = tf->ctl;
+ ata_wait_idle(ap);
+ }
+
+ if (tf->flags & ATA_TFLAG_ISADDR) {
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ pi->proto->write_regr(pi, 0, ATA_REG_FEATURE,
+ tf->hob_feature);
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT,
+ tf->hob_nsect);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL,
+ tf->hob_lbal);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAM,
+ tf->hob_lbam);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAH,
+ tf->hob_lbah);
+ }
+ pi->proto->write_regr(pi, 0, ATA_REG_FEATURE, tf->feature);
+ pi->proto->write_regr(pi, 0, ATA_REG_NSECT, tf->nsect);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAL, tf->lbal);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAM, tf->lbam);
+ pi->proto->write_regr(pi, 0, ATA_REG_LBAH, tf->lbah);
+ }
+
+ if (tf->flags & ATA_TFLAG_DEVICE)
+ pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tf->device);
+
+ ata_wait_idle(ap);
+}
+
+static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ tf->status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
+ tf->error = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
+ tf->nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+ tf->lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+ tf->lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
+ tf->lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
+ tf->device = pi->proto->read_regr(pi, 0, ATA_REG_DEVICE);
+
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ pi->proto->write_regr(pi, 1, 6, tf->ctl | ATA_HOB);
+ tf->hob_feature = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
+ tf->hob_nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+ tf->hob_lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+ tf->hob_lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
+ tf->hob_lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
+ pi->proto->write_regr(pi, 1, 6, tf->ctl);
+ ap->last_ctl = tf->ctl;
+ }
+}
+
+static void pata_parport_exec_command(struct ata_port *ap,
+ const struct ata_taskfile *tf)
+{
+ struct pi_adapter *pi = ap->host->private_data;
+
+ pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
+ ata_sff_pause(ap);
+}
+
+static unsigned int pata_parport_data_xfer(struct ata_queued_cmd *qc,
+ unsigned char *buf, unsigned int buflen, int rw)
+{
+ struct ata_port *ap = qc->dev->link->ap;
+ struct pi_adapter *pi = ap->host->private_data;
+
+ if (rw == READ)
+ pi->proto->read_block(pi, buf, buflen);
+ else
+ pi->proto->write_block(pi, buf, buflen);
+
+ return buflen;
+}
+
+static void pata_parport_drain_fifo(struct ata_queued_cmd *qc)
+{
+ int count;
+ struct ata_port *ap;
+ struct pi_adapter *pi;
+ char junk[2];
+
+ /* We only need to flush incoming data when a command was running */
+ if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE)
+ return;
+
+ ap = qc->ap;
+ pi = ap->host->private_data;
+ /* Drain up to 64K of data before we give up this recovery method */
+ for (count = 0; (pata_parport_check_status(ap) & ATA_DRQ)
+ && count < 65536; count += 2) {
+ pi->proto->read_block(pi, junk, 2);
+ }
+
+ if (count)
+ ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
+}
+
+static struct ata_port_operations pata_parport_port_ops = {
+ .inherits = &ata_sff_port_ops,
+
+ .softreset = pata_parport_softreset,
+ .hardreset = NULL,
+
+ .sff_dev_select = pata_parport_dev_select,
+ .sff_set_devctl = pata_parport_set_devctl,
+ .sff_check_status = pata_parport_check_status,
+ .sff_check_altstatus = pata_parport_check_altstatus,
+ .sff_tf_load = pata_parport_tf_load,
+ .sff_tf_read = pata_parport_tf_read,
+ .sff_exec_command = pata_parport_exec_command,
+ .sff_data_xfer = pata_parport_data_xfer,
+ .sff_drain_fifo = pata_parport_drain_fifo,
+};
+
+static const struct ata_port_info pata_parport_port_info = {
+ .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING,
+ .pio_mask = ATA_PIO0,
+ /* No DMA */
+ .port_ops = &pata_parport_port_ops,
+};
+
+static void pi_release(struct pi_adapter *pi)
+{
+ parport_unregister_device(pi->pardev);
+ if (pi->proto->release_proto)
+ pi->proto->release_proto(pi);
+ module_put(pi->proto->owner);
+}
+
+static int default_test_proto(struct pi_adapter *pi)
+{
+ int j, k;
+ int e[2] = { 0, 0 };
+
+ pi->proto->connect(pi);
+
+ for (j = 0; j < 2; j++) {
+ pi->proto->write_regr(pi, 0, 6, 0xa0 + j * 0x10);
+ for (k = 0; k < 256; k++) {
+ pi->proto->write_regr(pi, 0, 2, k ^ 0xaa);
+ pi->proto->write_regr(pi, 0, 3, k ^ 0x55);
+ if (pi->proto->read_regr(pi, 0, 2) != (k ^ 0xaa))
+ e[j]++;
+ }
+ }
+ pi->proto->disconnect(pi);
+
+ dev_dbg(&pi->dev, "%s: port 0x%x, mode %d, test=(%d,%d)\n",
+ pi->proto->name, pi->port, pi->mode, e[0], e[1]);
+
+ return e[0] && e[1]; /* not here if both > 0 */
+}
+
+static int pi_test_proto(struct pi_adapter *pi)
+{
+ int res;
+
+ parport_claim_or_block(pi->pardev);
+ if (pi->proto->test_proto)
+ res = pi->proto->test_proto(pi);
+ else
+ res = default_test_proto(pi);
+ parport_release(pi->pardev);
+
+ return res;
+}
+
+static bool pi_probe_mode(struct pi_adapter *pi, int max)
+{
+ int best, range;
+
+ if (pi->mode != -1) {
+ if (pi->mode >= max)
+ return false;
+ range = 3;
+ if (pi->mode >= pi->proto->epp_first)
+ range = 8;
+ if (range == 8 && pi->port % 8)
+ return false;
+ return !pi_test_proto(pi);
+ }
+ best = -1;
+ for (pi->mode = 0; pi->mode < max; pi->mode++) {
+ range = 3;
+ if (pi->mode >= pi->proto->epp_first)
+ range = 8;
+ if (range == 8 && pi->port % 8)
+ break;
+ if (!pi_test_proto(pi))
+ best = pi->mode;
+ }
+ pi->mode = best;
+ return best > -1;
+}
+
+static bool pi_probe_unit(struct pi_adapter *pi, int unit)
+{
+ int max, s, e;
+
+ s = unit;
+ e = s + 1;
+
+ if (s == -1) {
+ s = 0;
+ e = pi->proto->max_units;
+ }
+
+ if (pi->proto->test_port) {
+ parport_claim_or_block(pi->pardev);
+ max = pi->proto->test_port(pi);
+ parport_release(pi->pardev);
+ } else {
+ max = pi->proto->max_mode;
+ }
+
+ if (pi->proto->probe_unit) {
+ parport_claim_or_block(pi->pardev);
+ for (pi->unit = s; pi->unit < e; pi->unit++) {
+ if (pi->proto->probe_unit(pi)) {
+ parport_release(pi->pardev);
+ return pi_probe_mode(pi, max);
+ }
+ }
+ parport_release(pi->pardev);
+ return false;
+ }
+
+ return pi_probe_mode(pi, max);
+}
+
+static void pata_parport_dev_release(struct device *dev)
+{
+ struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
+
+ ida_free(&pata_parport_bus_dev_ids, dev->id);
+ kfree(pi);
+}
+
+static void pata_parport_bus_release(struct device *dev)
+{
+ /* nothing to do here but required to avoid warning on device removal */
+}
+
+static struct bus_type pata_parport_bus_type = {
+ .name = DRV_NAME,
+};
+
+static struct device pata_parport_bus = {
+ .init_name = DRV_NAME,
+ .release = pata_parport_bus_release,
+};
+
+static const struct scsi_host_template pata_parport_sht = {
+ PATA_PARPORT_SHT("pata_parport")
+};
+
+struct pi_device_match {
+ struct parport *parport;
+ struct pi_protocol *proto;
+};
+
+static int pi_find_dev(struct device *dev, void *data)
+{
+ struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
+ struct pi_device_match *match = data;
+
+ return pi->pardev->port == match->parport && pi->proto == match->proto;
+}
+
+static struct pi_adapter *pi_init_one(struct parport *parport,
+ struct pi_protocol *pr, int mode, int unit, int delay)
+{
+ struct pardev_cb par_cb = { };
+ const struct ata_port_info *ppi[] = { &pata_parport_port_info };
+ struct ata_host *host;
+ struct pi_adapter *pi;
+ struct pi_device_match match = { .parport = parport, .proto = pr };
+ int id;
+
+ /*
+ * Abort if there's a device already registered on the same parport
+ * using the same protocol.
+ */
+ if (bus_for_each_dev(&pata_parport_bus_type, NULL, &match, pi_find_dev))
+ return NULL;
+
+ id = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
+ if (id < 0)
+ return NULL;
+
+ pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
+ if (!pi) {
+ ida_free(&pata_parport_bus_dev_ids, id);
+ return NULL;
+ }
+
+ /* set up pi->dev before pi_probe_unit() so it can use dev_printk() */
+ pi->dev.parent = &pata_parport_bus;
+ pi->dev.bus = &pata_parport_bus_type;
+ pi->dev.driver = &pr->driver;
+ pi->dev.release = pata_parport_dev_release;
+ pi->dev.id = id;
+ dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
+ if (device_register(&pi->dev)) {
+ put_device(&pi->dev);
+ /* pata_parport_dev_release will do ida_free(dev->id) and kfree(pi) */
+ return NULL;
+ }
+
+ pi->proto = pr;
+
+ if (!try_module_get(pi->proto->owner))
+ goto out_unreg_dev;
+ if (pi->proto->init_proto && pi->proto->init_proto(pi) < 0)
+ goto out_module_put;
+
+ pi->delay = (delay == -1) ? pi->proto->default_delay : delay;
+ pi->mode = mode;
+ pi->port = parport->base;
+
+ par_cb.private = pi;
+ pi->pardev = parport_register_dev_model(parport, DRV_NAME, &par_cb, id);
+ if (!pi->pardev)
+ goto out_module_put;
+
+ if (!pi_probe_unit(pi, unit)) {
+ dev_info(&pi->dev, "Adapter not found\n");
+ goto out_unreg_parport;
+ }
+
+ pi->proto->log_adapter(pi);
+
+ host = ata_host_alloc_pinfo(&pi->pardev->dev, ppi, 1);
+ if (!host)
+ goto out_unreg_parport;
+ dev_set_drvdata(&pi->dev, host);
+ host->private_data = pi;
+
+ ata_port_desc(host->ports[0], "port %s", pi->pardev->port->name);
+ ata_port_desc(host->ports[0], "protocol %s", pi->proto->name);
+
+ pi_connect(pi);
+ if (ata_host_activate(host, 0, NULL, 0, &pata_parport_sht))
+ goto out_disconnect;
+
+ return pi;
+
+out_disconnect:
+ pi_disconnect(pi);
+out_unreg_parport:
+ parport_unregister_device(pi->pardev);
+ if (pi->proto->release_proto)
+ pi->proto->release_proto(pi);
+out_module_put:
+ module_put(pi->proto->owner);
+out_unreg_dev:
+ device_unregister(&pi->dev);
+ /* pata_parport_dev_release will do ida_free(dev->id) and kfree(pi) */
+ return NULL;
+}
+
+int pata_parport_register_driver(struct pi_protocol *pr)
+{
+ int error;
+ struct parport *parport;
+ int port_num;
+
+ pr->driver.bus = &pata_parport_bus_type;
+ pr->driver.name = pr->name;
+ error = driver_register(&pr->driver);
+ if (error)
+ return error;
+
+ mutex_lock(&pi_mutex);
+ error = idr_alloc(&protocols, pr, 0, 0, GFP_KERNEL);
+ if (error < 0) {
+ driver_unregister(&pr->driver);
+ mutex_unlock(&pi_mutex);
+ return error;
+ }
+
+ pr_info("pata_parport: protocol %s registered\n", pr->name);
+
+ if (probe) {
+ /* probe all parports using this protocol */
+ idr_for_each_entry(&parport_list, parport, port_num)
+ pi_init_one(parport, pr, -1, -1, -1);
+ }
+ mutex_unlock(&pi_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pata_parport_register_driver);
+
+void pata_parport_unregister_driver(struct pi_protocol *pr)
+{
+ struct pi_protocol *pr_iter;
+ int id = -1;
+
+ mutex_lock(&pi_mutex);
+ idr_for_each_entry(&protocols, pr_iter, id) {
+ if (pr_iter == pr)
+ break;
+ }
+ idr_remove(&protocols, id);
+ mutex_unlock(&pi_mutex);
+ driver_unregister(&pr->driver);
+}
+EXPORT_SYMBOL_GPL(pata_parport_unregister_driver);
+
+static ssize_t new_device_store(const struct bus_type *bus, const char *buf, size_t count)
+{
+ char port[12] = "auto";
+ char protocol[8] = "auto";
+ int mode = -1, unit = -1, delay = -1;
+ struct pi_protocol *pr, *pr_wanted;
+ struct device_driver *drv;
+ struct parport *parport;
+ int port_num, port_wanted, pr_num;
+ bool ok = false;
+
+ if (sscanf(buf, "%11s %7s %d %d %d",
+ port, protocol, &mode, &unit, &delay) < 1)
+ return -EINVAL;
+
+ if (sscanf(port, "parport%u", &port_wanted) < 1) {
+ if (strcmp(port, "auto")) {
+ pr_err("invalid port name %s\n", port);
+ return -EINVAL;
+ }
+ port_wanted = -1;
+ }
+
+ drv = driver_find(protocol, &pata_parport_bus_type);
+ if (!drv) {
+ if (strcmp(protocol, "auto")) {
+ pr_err("protocol %s not found\n", protocol);
+ return -EINVAL;
+ }
+ pr_wanted = NULL;
+ } else {
+ pr_wanted = container_of(drv, struct pi_protocol, driver);
+ }
+
+ mutex_lock(&pi_mutex);
+ /* walk all parports */
+ idr_for_each_entry(&parport_list, parport, port_num) {
+ if (port_num == port_wanted || port_wanted == -1) {
+ parport = parport_find_number(port_num);
+ if (!parport) {
+ pr_err("no such port %s\n", port);
+ mutex_unlock(&pi_mutex);
+ return -ENODEV;
+ }
+ /* walk all protocols */
+ idr_for_each_entry(&protocols, pr, pr_num) {
+ if (pr == pr_wanted || !pr_wanted)
+ if (pi_init_one(parport, pr, mode, unit,
+ delay))
+ ok = true;
+ }
+ parport_put_port(parport);
+ }
+ }
+ mutex_unlock(&pi_mutex);
+ if (!ok)
+ return -ENODEV;
+
+ return count;
+}
+static BUS_ATTR_WO(new_device);
+
+static void pi_remove_one(struct device *dev)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pi_adapter *pi = host->private_data;
+
+ ata_host_detach(host);
+ pi_disconnect(pi);
+ pi_release(pi);
+ device_unregister(dev);
+ /* pata_parport_dev_release will do ida_free(dev->id) and kfree(pi) */
+}
+
+static ssize_t delete_device_store(const struct bus_type *bus, const char *buf, size_t count)
+{
+ struct device *dev;
+
+ mutex_lock(&pi_mutex);
+ dev = bus_find_device_by_name(bus, NULL, buf);
+ if (!dev) {
+ mutex_unlock(&pi_mutex);
+ return -ENODEV;
+ }
+
+ pi_remove_one(dev);
+ put_device(dev);
+ mutex_unlock(&pi_mutex);
+
+ return count;
+}
+static BUS_ATTR_WO(delete_device);
+
+static void pata_parport_attach(struct parport *port)
+{
+ struct pi_protocol *pr;
+ int pr_num, id;
+
+ mutex_lock(&pi_mutex);
+ id = idr_alloc(&parport_list, port, port->number, port->number,
+ GFP_KERNEL);
+ if (id < 0) {
+ mutex_unlock(&pi_mutex);
+ return;
+ }
+
+ if (probe) {
+ /* probe this port using all protocols */
+ idr_for_each_entry(&protocols, pr, pr_num)
+ pi_init_one(port, pr, -1, -1, -1);
+ }
+ mutex_unlock(&pi_mutex);
+}
+
+static int pi_remove_port(struct device *dev, void *p)
+{
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pi_adapter *pi = host->private_data;
+
+ if (pi->pardev->port == p)
+ pi_remove_one(dev);
+
+ return 0;
+}
+
+static void pata_parport_detach(struct parport *port)
+{
+ mutex_lock(&pi_mutex);
+ bus_for_each_dev(&pata_parport_bus_type, NULL, port, pi_remove_port);
+ idr_remove(&parport_list, port->number);
+ mutex_unlock(&pi_mutex);
+}
+
+static struct parport_driver pata_parport_driver = {
+ .name = DRV_NAME,
+ .match_port = pata_parport_attach,
+ .detach = pata_parport_detach,
+ .devmodel = true,
+};
+
+static __init int pata_parport_init(void)
+{
+ int error;
+
+ error = bus_register(&pata_parport_bus_type);
+ if (error) {
+ pr_err("failed to register pata_parport bus, error: %d\n", error);
+ return error;
+ }
+
+ error = device_register(&pata_parport_bus);
+ if (error) {
+ pr_err("failed to register pata_parport bus, error: %d\n", error);
+ goto out_unregister_bus;
+ }
+
+ error = bus_create_file(&pata_parport_bus_type, &bus_attr_new_device);
+ if (error) {
+ pr_err("unable to create sysfs file, error: %d\n", error);
+ goto out_unregister_dev;
+ }
+
+ error = bus_create_file(&pata_parport_bus_type, &bus_attr_delete_device);
+ if (error) {
+ pr_err("unable to create sysfs file, error: %d\n", error);
+ goto out_remove_new;
+ }
+
+ error = parport_register_driver(&pata_parport_driver);
+ if (error) {
+ pr_err("unable to register parport driver, error: %d\n", error);
+ goto out_remove_del;
+ }
+
+ return 0;
+
+out_remove_del:
+ bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
+out_remove_new:
+ bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
+out_unregister_dev:
+ device_unregister(&pata_parport_bus);
+out_unregister_bus:
+ bus_unregister(&pata_parport_bus_type);
+ return error;
+}
+
+static __exit void pata_parport_exit(void)
+{
+ parport_unregister_driver(&pata_parport_driver);
+ bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
+ bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
+ device_unregister(&pata_parport_bus);
+ bus_unregister(&pata_parport_bus_type);
+}
+
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("driver for parallel port ATA adapters");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("paride");
+
+module_init(pata_parport_init);
+module_exit(pata_parport_exit);
diff --git a/drivers/ata/pata_parport/pata_parport.h b/drivers/ata/pata_parport/pata_parport.h
new file mode 100644
index 0000000000..bbfa4e63ee
--- /dev/null
+++ b/drivers/ata/pata_parport/pata_parport.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * pata_parport.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
+ * Under the terms of the GPL.
+ *
+ * This file defines the interface for parallel port IDE adapter chip drivers.
+ */
+
+#ifndef LINUX_PATA_PARPORT_H
+#define LINUX_PATA_PARPORT_H
+
+#include <linux/libata.h>
+
+struct pi_adapter {
+ struct device dev;
+ struct pi_protocol *proto; /* adapter protocol */
+ int port; /* base address of parallel port */
+ int mode; /* transfer mode in use */
+ int delay; /* adapter delay setting */
+ int unit; /* unit number for chained adapters */
+ int saved_r0; /* saved port state */
+ int saved_r2; /* saved port state */
+ unsigned long private; /* for protocol module */
+ struct pardevice *pardev; /* pointer to pardevice */
+};
+
+/* registers are addressed as (cont,regr)
+ * cont: 0 for command register file, 1 for control register(s)
+ * regr: 0-7 for register number.
+ */
+
+/* macros and functions exported to the protocol modules */
+#define delay_p (pi->delay ? udelay(pi->delay) : (void)0)
+#define out_p(offs, byte) do { outb(byte, pi->port + offs); delay_p; } while (0)
+#define in_p(offs) (delay_p, inb(pi->port + offs))
+
+#define w0(byte) out_p(0, byte)
+#define r0() in_p(0)
+#define w1(byte) out_p(1, byte)
+#define r1() in_p(1)
+#define w2(byte) out_p(2, byte)
+#define r2() in_p(2)
+#define w3(byte) out_p(3, byte)
+#define w4(byte) out_p(4, byte)
+#define r4() in_p(4)
+#define w4w(data) do { outw(data, pi->port + 4); delay_p; } while (0)
+#define w4l(data) do { outl(data, pi->port + 4); delay_p; } while (0)
+#define r4w() (delay_p, inw(pi->port + 4))
+#define r4l() (delay_p, inl(pi->port + 4))
+
+struct pi_protocol {
+ char name[8];
+
+ int max_mode;
+ int epp_first; /* modes >= this use 8 ports */
+
+ int default_delay;
+ int max_units; /* max chained units probed for */
+
+ void (*write_regr)(struct pi_adapter *pi, int cont, int regr, int val);
+ int (*read_regr)(struct pi_adapter *pi, int cont, int regr);
+ void (*write_block)(struct pi_adapter *pi, char *buf, int count);
+ void (*read_block)(struct pi_adapter *pi, char *buf, int count);
+
+ void (*connect)(struct pi_adapter *pi);
+ void (*disconnect)(struct pi_adapter *pi);
+
+ int (*test_port)(struct pi_adapter *pi);
+ int (*probe_unit)(struct pi_adapter *pi);
+ int (*test_proto)(struct pi_adapter *pi);
+ void (*log_adapter)(struct pi_adapter *pi);
+
+ int (*init_proto)(struct pi_adapter *pi);
+ void (*release_proto)(struct pi_adapter *pi);
+ struct module *owner;
+ struct device_driver driver;
+ struct scsi_host_template sht;
+};
+
+#define PATA_PARPORT_SHT ATA_PIO_SHT
+
+int pata_parport_register_driver(struct pi_protocol *pr);
+void pata_parport_unregister_driver(struct pi_protocol *pr);
+
+/**
+ * module_pata_parport_driver() - Helper macro for registering a pata_parport driver
+ * @__pi_protocol: pi_protocol struct
+ *
+ * Helper macro for pata_parport drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_pata_parport_driver(__pi_protocol) \
+ module_driver(__pi_protocol, pata_parport_register_driver, pata_parport_unregister_driver)
+
+#endif /* LINUX_PATA_PARPORT_H */