linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH] pata_parport: paride replacement
@ 2022-03-05 20:13 Ondrej Zary
  2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
                   ` (16 more replies)
  0 siblings, 17 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:13 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Hello,
this is a RFC patch for the new pata_parport libata driver - a paride
replacement.

Protocol driver registration and device creation was changed since the
second preview - no more protocol numbers or parport I/O addresses.

All parports and all protocol drivers are now probed automatically unless
probe=0 parameter is used. So just "modprobe epat" is enough for a Imation
SuperDisk drive to work.

Manual device creation:
echo auto >/sys/bus/pata_parport/new_device
echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
echo "parport0 auto" >/sys/bus/pata_parport/new_device
echo "auto epat" >/sys/bus/pata_parport/new_device

Deleting devices:
echo pata_parport.0 >/sys/bus/pata_parport/delete_device

Haven't found any sane way to hook pi_connect() and pi_disconnect() to
libata. So pi_connect() is called on every protocol driver access and
pi_disconnect() is called by a timer that's activated after protocol driver
access.

Found that the EPP-32 mode is buggy in EPAT - and also in many (all?) other
protocol drivers - they don't handle non-multiple-of-4 block transfers
correctly. I'll fix that later.

The bpck_connect() in bpck driver seems to need to know if a CD drive is
attached (weird) but it's called before device detection. This probably
cannot be fixed without the HW.

I have only two devices and both have the same EPAT chip, unfortunately:
Imation SupserDisk
HP C4381A (drive was dead, replaced by TOSHIBA CD-ROM XM-6202B)

The both work:
# modprobe epat
[  122.635395] pata_parport: protocol epat registered
[  122.738114] epat pata_parport.0: epat, Shuttle EPAT chip c6 at 0x378, mode 5 (EPP-32), delay 1
[  122.789035] scsi host4: pata_parport-epat
[  122.789226] ata5: PATA max PIO0 port parport0 protocol epat
[  127.831534] ata5: link is slow to respond, please be patient (ready=0)
[  132.811623] ata5: device not ready (errno=-16), forcing hardreset
[  133.015024] ata5.00: ATAPI: TOSHIBA CD-ROM XM-6202B, 1108, max MWDMA2
[  133.023016] scsi 4:0:0:0: CD-ROM            TOSHIBA  CD-ROM XM-6202B  1108 PQ: 0 ANSI: 5
[  133.043817] scsi 4:0:0:0: Attached scsi generic sg1 type 5
[  133.088146] sr 4:0:0:0: [sr0] scsi3-mmc drive: 32x/32x cd/rw xa/form2 cdda tray
[  133.088163] cdrom: Uniform CD-ROM driver Revision: 3.20
[  133.125939] sr 4:0:0:0: Attached scsi CD-ROM sr0
# mount /dev/sr0 /mnt
mount: /mnt: WARNING: source write-protected, mounted read-only.
[  157.922575] ISO 9660 Extensions: Microsoft Joliet Level 3
[  157.991030] ISO 9660 Extensions: RRIP_1991A
# hdparm -t --direct /dev/sr0

/dev/sr0:
 Timing O_DIRECT disk reads:   2 MB in  3.08 seconds = 664.47 kB/sec



# modprobe epat
[  448.160910] pata_parport: protocol epat registered
[  448.267068] epat pata_parport.0: epat, Shuttle EPAT chip c6 at 0x378, mode 5 (EPP-32), delay 1
[  448.301624] scsi host4: pata_parport-epat
[  448.301769] ata6: PATA max PIO0 port parport0 protocol epat
[  448.533850] ata6.00: ATAPI: LS-120 COSM   04              UHD Floppy, 0270M09T, max PIO2
[  448.615500] scsi 4:0:0:0: Direct-Access     MATSHITA LS-120 COSM   04 0270 PQ: 0 ANSI: 5
[  448.651279] sd 4:0:0:0: Attached scsi generic sg1 type 0
[  448.686028] sd 4:0:0:0: [sdb] Media removed, stopped polling
[  448.717879] sd 4:0:0:0: [sdb] Attached SCSI removable disk
[  472.259786] sd 4:0:0:0: [sdb] Read Capacity(16) failed: Result: hostbyte=DID_ERROR driverbyte=DRIVER_OK
[  472.259805] sd 4:0:0:0: [sdb] Sense not available.
[  483.042442] sd 4:0:0:0: [sdb] 246528 512-byte logical blocks: (126 MB/120 MiB)
[  483.158446] sdb: detected capacity change from 0 to 246528
[  483.309771]  sdb:
# hdparm -t --direct /dev/sdb

/dev/sdb:
 Timing O_DIRECT disk reads:   2 MB in 44.19 seconds =  46.35 kB/sec



^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 01/16] pata_parport: add core driver (PARIDE replacement)
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
@ 2022-03-05 20:13 ` Ondrej Zary
  2022-03-07  5:52   ` Damien Le Moal
                     ` (4 more replies)
  2022-03-05 20:13 ` [PATCH 02/16] pata_parport: add aten protocol driver Ondrej Zary
                   ` (15 subsequent siblings)
  16 siblings, 5 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:13 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add pata_parport (PARIDE replacement) core libata driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/Kconfig                     |  25 +
 drivers/ata/Makefile                    |   2 +
 drivers/ata/pata_parport/Kconfig        |  10 +
 drivers/ata/pata_parport/Makefile       |   9 +
 drivers/ata/pata_parport/pata_parport.c | 809 ++++++++++++++++++++++++
 drivers/ata/pata_parport/pata_parport.h | 110 ++++
 6 files changed, 965 insertions(+)
 create mode 100644 drivers/ata/pata_parport/Kconfig
 create mode 100644 drivers/ata/pata_parport/Makefile
 create mode 100644 drivers/ata/pata_parport/pata_parport.c
 create mode 100644 drivers/ata/pata_parport/pata_parport.h

diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index cb54631fd950..de4548471398 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -1161,6 +1161,31 @@ config PATA_WINBOND_VLB
 	  Support for the Winbond W83759A controller on Vesa Local Bus
 	  systems.
 
+config PATA_PARPORT
+	tristate "Parallel port IDE device support (PARIDE replacement)"
+	depends on PARPORT_PC
+	help
+	  There are many external CD-ROM and disk devices that connect through
+	  your computer's parallel port. Most of them are actually IDE devices
+	  using a parallel port IDE adapter. This option enables the PATA_PARPORT
+	  subsystem which contains drivers for many of these external drives.
+	  Read <file:Documentation/admin-guide/blockdev/paride.rst> for more information.
+
+	  If you have said Y to the "Parallel-port support" configuration
+	  option, you may share a single port between your printer and other
+	  parallel port devices. Answer Y to build PATA_PARPORT support into your
+	  kernel, or M if you would like to build it as a loadable module. If
+	  your parallel port support is in a loadable module, you must build
+	  PATA_PARPORT as a module. If you built PATA_PARPORT support into your kernel,
+	  you may still build the individual protocol modules as loadable
+	  modules. If you build this support as a module, it will be called pata_parport.
+
+	  Unlike the old PARIDE, there are no high-level drivers needed.
+	  The IDE devices behind parallel port adapters are handled by the
+	  ATA layer.
+
+source "drivers/ata/pata_parport/Kconfig"
+
 comment "Generic fallback / legacy drivers"
 
 config PATA_ACPI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index b8aebfb14e82..bd6b5fdc004e 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -114,6 +114,8 @@ obj-$(CONFIG_PATA_SAMSUNG_CF)	+= pata_samsung_cf.o
 
 obj-$(CONFIG_PATA_PXA)		+= pata_pxa.o
 
+obj-$(CONFIG_PATA_PARPORT)	+= pata_parport/
+
 # Should be last but two libata driver
 obj-$(CONFIG_PATA_ACPI)		+= pata_acpi.o
 # Should be last but one libata driver
diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
new file mode 100644
index 000000000000..56dc6b25d5fa
--- /dev/null
+++ b/drivers/ata/pata_parport/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# PATA_PARPORT configuration
+#
+# PATA_PARPORT doesn't need PARPORT, but if PARPORT is configured as a module,
+# PATA_PARPORT must also be a module.
+# PATA_PARPORT only supports PC style parports. Tough for USB or other parports...
+
+comment "Parallel IDE protocol modules"
+	depends on PATA_PARPORT
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
new file mode 100644
index 000000000000..b105e1cb8dc6
--- /dev/null
+++ b/drivers/ata/pata_parport/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Parallel port IDE device drivers.
+#
+# 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+# Rewritten to use lists instead of if-statements.
+#
+
+obj-$(CONFIG_PATA_PARPORT)		+= pata_parport.o
diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c
new file mode 100644
index 000000000000..7f814062cedd
--- /dev/null
+++ b/drivers/ata/pata_parport/pata_parport.c
@@ -0,0 +1,809 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#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 = 1;
+module_param(probe, bool, 0644);
+MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
+
+static bool verbose;
+module_param(verbose, bool, 0644);
+MODULE_PARM_DESC(verbose, "Enable verbose messages (0=off [default], 1=on)");
+
+#define DISCONNECT_TIMEOUT	(HZ / 10)
+
+static void pi_connect(struct pi_adapter *pi)
+{
+	del_timer_sync(&pi->timer);
+	if (pi->claimed)
+		return;
+	pi->claimed = 1;
+	parport_claim_or_block(pi->pardev);
+	pi->proto->connect(pi);
+}
+
+static void pi_disconnect_later(struct pi_adapter *pi)
+{
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+}
+
+static void pi_disconnect_timer(struct timer_list *t)
+{
+	struct pi_adapter *pi = from_timer(pi, t, timer);
+
+	if (!pi->claimed)
+		return;
+	pi->proto->disconnect(pi);
+	parport_release(pi->pardev);
+	pi->claimed = 0;
+}
+
+/* functions taken from libata-sff.c and converted from direct port I/O */
+static unsigned int pata_parport_devchk(struct ata_port *ap, unsigned int device)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+	u8 nsect, lbal;
+
+	ap->ops->sff_dev_select(ap, device);
+
+	pi_connect(pi);
+	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, 055);
+	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);
+	pi_disconnect_later(pi);
+
+	if ((nsect == 0x55) && (lbal == 0xaa))
+		return 1;	/* we found a device */
+
+	return 0;		/* nothing found */
+}
+
+static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
+				      unsigned long deadline)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	/* software reset.  causes dev0 to be selected */
+	pi->proto->write_regr(pi, 1, 6, ap->ctl);
+	udelay(20);	/* FIXME: flush */
+	pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
+	udelay(20);	/* FIXME: flush */
+	pi->proto->write_regr(pi, 1, 6, ap->ctl);
+	ap->last_ctl = ap->ctl;
+	pi_disconnect_later(pi);
+
+	/* wait the port to become ready */
+	return ata_sff_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 slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
+	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 (slave_possible && pata_parport_devchk(ap, 1))
+		devmask |= (1 << 1);
+
+	/* select device 0 again */
+	ap->ops->sff_dev_select(ap, 0);
+
+	/* issue bus reset */
+	rc = pata_parport_bus_softreset(ap, devmask, deadline);
+	/* if link is occupied, -ENODEV too is an error */
+	if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
+		ata_link_err(link, "SRST failed (errno=%d)\n", rc);
+		return rc;
+	}
+
+	/* determine by signature whether we have ATA or ATAPI devices */
+	classes[0] = ata_sff_dev_classify(&link->device[0],
+					  devmask & (1 << 0), &err);
+	if (slave_possible && err != 0x81)
+		classes[1] = ata_sff_dev_classify(&link->device[1],
+						  devmask & (1 << 1), &err);
+
+	return 0;
+}
+
+static u8 pata_parport_check_status(struct ata_port *ap)
+{
+	u8 status;
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
+	pi_disconnect_later(pi);
+
+	return status;
+}
+
+static u8 pata_parport_check_altstatus(struct ata_port *ap)
+{
+	u8 altstatus;
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	altstatus = pi->proto->read_regr(pi, 1, 6);
+	pi_disconnect_later(pi);
+
+	return altstatus;
+}
+
+static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+	u8 tmp;
+
+	pi_connect(pi);
+	if (device == 0)
+		tmp = ATA_DEVICE_OBS;
+	else
+		tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
+	pi_disconnect_later(pi);
+	ata_sff_pause(ap);	/* needed; also flushes, for mmio */
+}
+
+static void pata_parport_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+	pi_connect(pi);
+	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 (is_addr && (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);
+	}
+
+	if (is_addr) {
+		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);
+	pi_disconnect_later(pi);
+}
+
+static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	tf->command = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
+	tf->feature = 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;
+	}
+	pi_disconnect_later(pi);
+}
+
+static void pata_parport_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
+	ata_sff_pause(ap);
+	pi_disconnect_later(pi);
+}
+
+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;
+
+	pi_connect(pi);
+	if (rw == READ)
+		pi->proto->read_block(pi, buf, buflen);
+	else
+		pi->proto->write_block(pi, buf, buflen);
+	pi_disconnect_later(pi);
+
+	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; (ap->ops->sff_check_status(ap) & ATA_DRQ)
+						&& count < 65536; count += 2) {
+		pi_connect(pi);
+		pi->proto->read_block(pi, junk, 2);
+		pi_disconnect_later(pi);
+	}
+
+	if (count)
+		ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
+}
+
+static void pata_parport_lost_interrupt(struct ata_port *ap)
+{
+	u8 status;
+	struct ata_queued_cmd *qc;
+
+	/* Only one outstanding command per SFF channel */
+	qc = ata_qc_from_tag(ap, ap->link.active_tag);
+	/* We cannot lose an interrupt on a non-existent or polled command */
+	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
+		return;
+	/* See if the controller thinks it is still busy - if so the command
+	   isn't a lost IRQ but is still in progress */
+	status = pata_parport_check_altstatus(ap);
+	if (status & ATA_BUSY)
+		return;
+
+	/* There was a command running, we are no longer busy and we have
+	   no interrupt. */
+	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
+	/* Run the host interrupt logic as if the interrupt had not been lost */
+	ata_sff_port_intr(ap, qc);
+}
+
+static struct ata_port_operations pata_parport_port_ops = {
+	.qc_prep		= ata_noop_qc_prep,
+	.qc_issue		= ata_sff_qc_issue,
+	.qc_fill_rtf		= ata_sff_qc_fill_rtf,
+
+	.freeze			= ata_sff_freeze,
+	.thaw			= ata_sff_thaw,
+	.prereset		= ata_sff_prereset,
+	.softreset		= pata_parport_softreset,
+	.postreset		= ata_sff_postreset,
+	.error_handler		= ata_sff_error_handler,
+	.sched_eh		= ata_std_sched_eh,
+	.end_eh			= ata_std_end_eh,
+
+	.sff_dev_select		= pata_parport_dev_select,
+	.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,
+
+	.lost_interrupt		= pata_parport_lost_interrupt,
+};
+
+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,
+};
+
+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, char *scratch)
+{
+	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);
+
+	if (verbose)
+		dev_info(&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, char *scratch)
+{
+	int res;
+
+	parport_claim_or_block(pi->pardev);
+	if (pi->proto->test_proto)
+		res = pi->proto->test_proto(pi, scratch, verbose);
+	else
+		res = default_test_proto(pi, scratch);
+	parport_release(pi->pardev);
+
+	return res;
+}
+
+static int pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
+{
+	int best, range;
+
+	if (pi->mode != -1) {
+		if (pi->mode >= max)
+			return 0;
+		range = 3;
+		if (pi->mode >= pi->proto->epp_first)
+			range = 8;
+		if ((range == 8) && (pi->port % 8))
+			return 0;
+		return (!pi_test_proto(pi, scratch));
+	}
+	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, scratch))
+			best = pi->mode;
+	}
+	pi->mode = best;
+	return (best > -1);
+}
+
+
+static int pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)
+{
+	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);
+				if (pi_probe_mode(pi, max, scratch))
+					return 1;
+				return 0;
+			}
+		parport_release(pi->pardev);
+		return 0;
+	}
+
+	if (!pi_probe_mode(pi, max, scratch))
+		return 0;
+	return 1;
+}
+
+static void pata_parport_dev_release(struct device *dev)
+{
+	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
+
+	kfree(pi);
+}
+
+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,
+};
+
+struct pi_adapter *pi_init_one(struct parport *parport, struct pi_protocol *pr,
+			       int mode, int unit, int delay)
+{
+	struct pardev_cb par_cb = { };
+	char scratch[512];
+	const struct ata_port_info *ppi[] = { &pata_parport_port_info };
+	struct ata_host *host;
+	struct pi_adapter *pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
+
+	if (!pi)
+		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 = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
+	if (pi->dev.id < 0)
+		return NULL; /* pata_parport_dev_release will do kfree(pi) */
+	dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
+	if (device_register(&pi->dev)) {
+		put_device(&pi->dev);
+		goto out_ida_free;
+	}
+
+	pi->proto = pr;
+
+	/* still racy */
+	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, dev_name(&pi->dev),
+						&par_cb, pi->dev.id);
+	if (!pi->pardev)
+		goto out_module_put;
+
+	if (!pi_probe_unit(pi, unit, scratch)) {
+		dev_info(&pi->dev, "Adapter not found\n");
+		goto out_unreg_parport;
+	}
+
+	pi->proto->log_adapter(pi, scratch, verbose);
+
+	host = ata_host_alloc_pinfo(&pi->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);
+
+	timer_setup(&pi->timer, pi_disconnect_timer, 0);
+
+	if (ata_host_activate(host, 0, NULL, 0, &pi->proto->sht))
+		goto out_unreg_parport;
+
+	return 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);
+out_ida_free:
+	ida_free(&pata_parport_bus_dev_ids, pi->dev.id);
+	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, 0, -1);
+	mutex_unlock(&pi_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(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);
+	pr_info("pata_parport: protocol %s removed\n", pr->name);
+	driver_unregister(&pr->driver);
+}
+EXPORT_SYMBOL(pata_parport_unregister_driver);
+
+static ssize_t new_device_store(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"))
+			port_wanted = -1;
+		else {
+			pr_err("invalid port name %s\n", port);
+			return -EINVAL;
+		}
+	}
+
+	drv = driver_find(protocol, &pata_parport_bus_type);
+	if (!drv) {
+		if (!strcmp(protocol, "auto"))
+			pr_wanted = NULL;
+		else {
+			pr_err("protocol %s not found\n", protocol);
+			return -EINVAL;
+		}
+	} 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);
+	del_timer_sync(&pi->timer);
+	pi_release(pi);
+	device_unregister(dev);
+	ida_free(&pata_parport_bus_dev_ids, dev->id);
+	/* pata_parport_dev_release will do kfree(pi) */
+}
+
+static ssize_t delete_device_store(struct bus_type *bus, const char *buf, size_t count)
+{
+	struct device *dev;
+	char device_name[32];
+	int fields;
+
+	fields = sscanf(buf, "%31s", device_name);
+	if (fields < 1)
+		return -EINVAL;
+
+	mutex_lock(&pi_mutex);
+	dev = bus_find_device_by_name(bus, NULL, device_name);
+	if (!dev) {
+		mutex_unlock(&pi_mutex);
+		return -ENODEV;
+	}
+
+	pi_remove_one(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, 0, -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);
+		return 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 000000000000..c4201b809b20
--- /dev/null
+++ b/drivers/ata/pata_parport/pata_parport.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ *	paride.h	(c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *				    Under the terms of the GPL.
+ *
+ * This file defines the interface for adapter chip drivers.
+ */
+
+#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 */
+	int claimed;			/* parport has already been claimed */
+	struct timer_list timer;	/* disconnect timer */
+};
+
+/* 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) & 0xff)
+#define w1(byte)		out_p(1, byte)
+#define r1()			(in_p(1) & 0xff)
+#define w2(byte)		out_p(2, byte)
+#define r2()			(in_p(2) & 0xff)
+#define w3(byte)		out_p(3, byte)
+#define w4(byte)		out_p(4, byte)
+#define r4()			(in_p(4) & 0xff)
+#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) & 0xffff)
+#define r4l()			(delay_p, inl(pi->port + 4) & 0xffffffff)
+
+static inline u16 pi_swab16(char *b, int k)
+{
+	union { u16 u; char t[2]; } r;
+
+	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
+	return r.u;
+}
+
+static inline u32 pi_swab32(char *b, int k)
+{
+	union { u32 u; char f[4]; } r;
+
+	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
+	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
+	return r.u;
+}
+
+struct pi_protocol {
+	char name[8];		/* name for this protocol */
+
+	int max_mode;		/* max mode number */
+	int epp_first;		/* modes >= this use 8 ports */
+
+	int default_delay;	/* delay parameter if not specified */
+	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, char *scratch, int verbose);
+	void (*log_adapter)(struct pi_adapter *pi, char *scratch, int verbose);
+
+	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
+
+extern int pata_parport_register_driver(struct pi_protocol *pr);
+extern 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)
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 02/16] pata_parport: add aten protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
  2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
@ 2022-03-05 20:13 ` Ondrej Zary
  2022-03-05 20:13 ` [PATCH 03/16] pata_parport: add bpck " Ondrej Zary
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:13 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add ATEN EH-100 protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  11 +++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/aten.c   | 139 ++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+)
 create mode 100644 drivers/ata/pata_parport/aten.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 56dc6b25d5fa..93b0ad65b523 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -8,3 +8,14 @@
 
 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. If you chose to build PATA_PARPORT
+	  support into your kernel, you may answer Y here to build in the
+	  protocol driver, otherwise you should answer M to build it as a
+	  loadable module. The module will be called aten.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index b105e1cb8dc6..7e821b629c58 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -7,3 +7,4 @@
 #
 
 obj-$(CONFIG_PATA_PARPORT)		+= pata_parport.o
+obj-$(CONFIG_PATA_PARPORT_ATEN)		+= aten.o
diff --git a/drivers/ata/pata_parport/aten.c b/drivers/ata/pata_parport/aten.c
new file mode 100644
index 000000000000..a811a471e942
--- /dev/null
+++ b/drivers/ata/pata_parport/aten.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	aten.c  (c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *			    Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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 *scratch, int verbose)
+{
+	char *mode_string[2] = { "4-bit", "8-bit" };
+
+	dev_info(&pi->dev, "aten, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-aten") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(aten);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 03/16] pata_parport: add bpck protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
  2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
  2022-03-05 20:13 ` [PATCH 02/16] pata_parport: add aten protocol driver Ondrej Zary
@ 2022-03-05 20:13 ` Ondrej Zary
  2022-03-05 20:13 ` [PATCH 04/16] pata_parport: add bpck6 " Ondrej Zary
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:13 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add MicroSolutions backpack (Series 5) protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  17 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/bpck.c   | 481 ++++++++++++++++++++++++++++++
 3 files changed, 499 insertions(+)
 create mode 100644 drivers/ata/pata_parport/bpck.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 93b0ad65b523..ed33a6a5c6fe 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -19,3 +19,20 @@ config PATA_PARPORT_ATEN
 	  support into your kernel, you may answer Y here to build in the
 	  protocol driver, otherwise you should answer M to build it as a
 	  loadable module. The module will be called aten.
+
+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.
+
+	  If you chose to build PATA_PARPORT support into your kernel, you may
+	  answer Y here to build in the protocol driver, otherwise you should
+	  answer M to build it as a loadable module.  The module will be
+	  called bpck.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 7e821b629c58..1d03e49aa29f 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -8,3 +8,4 @@
 
 obj-$(CONFIG_PATA_PARPORT)		+= pata_parport.o
 obj-$(CONFIG_PATA_PARPORT_ATEN)		+= aten.o
+obj-$(CONFIG_PATA_PARPORT_BPCK)		+= bpck.o
diff --git a/drivers/ata/pata_parport/bpck.c b/drivers/ata/pata_parport/bpck.c
new file mode 100644
index 000000000000..07ee353eae84
--- /dev/null
+++ b/drivers/ata/pata_parport/bpck.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	bpck.c	(c) 1996-8  Grant R. Guenther <grant@torque.net>
+ *			    Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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 = 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);
+
+	//// FIXME: devtype was removed and we're called before device detection
+//	if (pi->devtype == PI_PCD) {
+//		WR(0x46, 0x10);		/* fiddle with ESS logic ??? */
+//		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);
+}
+
+/* This fakes the EPP protocol to turn off EPP ... */
+static void bpck_force_spp(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);
+
+	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, char *scratch, int verbose)
+{
+	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;
+	}
+
+	if (verbose)
+		dev_info(&pi->dev, "bpck: 0x%x unit %d mode %d",
+		       pi->port, pi->unit, pi->mode);
+
+	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)	/* check for 8-bit port */
+{
+	int i, r, m;
+
+	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 *scratch, int verbose)
+{
+	static char * const mode_string[] = {
+		"4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+#ifdef DUMP_EEPROM
+	int i;
+#endif
+
+	bpck_read_eeprom(pi, scratch);
+
+#ifdef DUMP_EEPROM
+	if (verbose) {
+		for (i = 0; i < 128; i++)
+			if ((scratch[i] < ' ') || (scratch[i] > '~'))
+				scratch[i] = '.';
+		dev_info(&pi->dev, "bpck EEPROM: %64.64s\n", scratch);
+		dev_info(&pi->dev, "	      %64.64s\n", &scratch[64]);
+	}
+#endif
+
+	dev_info(&pi->dev, "bpck, backpack %8.8s unit %d at 0x%x, mode %d (%s), delay %d\n",
+		&scratch[110], pi->unit, pi->port, pi->mode,
+		mode_string[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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-bpck") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(bpck);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 04/16] pata_parport: add bpck6 protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (2 preceding siblings ...)
  2022-03-05 20:13 ` [PATCH 03/16] pata_parport: add bpck " Ondrej Zary
@ 2022-03-05 20:13 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 05/16] pata_parport: add comm " Ondrej Zary
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:13 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add MicroSolutions backpack (Series 6) protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig   |  18 ++
 drivers/ata/pata_parport/Makefile  |   1 +
 drivers/ata/pata_parport/bpck6.c   | 164 ++++++++++
 drivers/ata/pata_parport/ppc6lnx.c | 486 +++++++++++++++++++++++++++++
 4 files changed, 669 insertions(+)
 create mode 100644 drivers/ata/pata_parport/bpck6.c
 create mode 100644 drivers/ata/pata_parport/ppc6lnx.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index ed33a6a5c6fe..e88d9c0bedc6 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -36,3 +36,21 @@ config PATA_PARPORT_BPCK
 	  answer Y here to build in the protocol driver, otherwise you should
 	  answer M to build it as a loadable module.  The module will be
 	  called bpck.
+
+config PATA_PARPORT_BPCK6
+	tristate "MicroSolutions backpack (Series 6) protocol"
+	depends on PATA_PARPORT && !64BIT
+	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.
+
+	  If you chose to build PATA_PARPORT support into your kernel, you may
+	  answer Y here to build in the protocol driver, otherwise you should
+	  answer M to build it as a loadable module.  The module will be
+	  called bpck6.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 1d03e49aa29f..60522279aa16 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -9,3 +9,4 @@
 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_BPCK6)	+= bpck6.o
diff --git a/drivers/ata/pata_parport/bpck6.c b/drivers/ata/pata_parport/bpck6.c
new file mode 100644
index 000000000000..cd517c822ee2
--- /dev/null
+++ b/drivers/ata/pata_parport/bpck6.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	backpack.c (c) 2001 Micro Solutions Inc.
+ *		Released under the terms of the GNU General Public license
+ *
+ *	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/slab.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/parport.h>
+
+#include "ppc6lnx.c"
+#include "pata_parport.h"
+
+#define PPCSTRUCT(pi) ((struct ppc_storage *)(pi->private))
+
+#define ATAPI_DATA       0      /* data port */
+
+static int bpck6_read_regr(struct pi_adapter *pi, int cont, int reg)
+{
+	unsigned int out;
+
+	/* check for bad settings */
+	if (reg < 0 || reg > 7 || cont < 0 || cont > 2)
+		return -1;
+	out = ppc6_rd_port(PPCSTRUCT(pi), cont ? reg | 8 : reg);
+	return out;
+}
+
+static void bpck6_write_regr(struct pi_adapter *pi, int cont, int reg, int val)
+{
+	/* check for bad settings */
+	if (reg >= 0 && reg <= 7 && cont >= 0 && cont <= 1)
+		ppc6_wr_port(PPCSTRUCT(pi), cont ? reg | 8 : reg, (u8)val);
+}
+
+static void bpck6_write_block(struct pi_adapter *pi, char *buf, int len)
+{
+	ppc6_wr_port16_blk(PPCSTRUCT(pi), ATAPI_DATA, buf, (u32)len >> 1);
+}
+
+static void bpck6_read_block(struct pi_adapter *pi, char *buf, int len)
+{
+	ppc6_rd_port16_blk(PPCSTRUCT(pi), ATAPI_DATA, buf, (u32) len >> 1);
+}
+
+static void bpck6_connect(struct pi_adapter *pi)
+{
+	if (pi->mode >= 2)
+		PPCSTRUCT(pi)->mode = 4 + pi->mode - 2;
+	else if (pi->mode == 1)
+		PPCSTRUCT(pi)->mode = 3;
+	else
+		PPCSTRUCT(pi)->mode = 1;
+
+	ppc6_open(PPCSTRUCT(pi));
+	ppc6_wr_extout(PPCSTRUCT(pi), 0x3);
+}
+
+static void bpck6_disconnect(struct pi_adapter *pi)
+{
+	ppc6_wr_extout(PPCSTRUCT(pi), 0x0);
+	ppc6_close(PPCSTRUCT(pi));
+}
+
+static int bpck6_test_port(struct pi_adapter *pi)   /* check for 8-bit port */
+{
+	/* copy over duplicate stuff.. initialize state info */
+	PPCSTRUCT(pi)->ppc_id = pi->unit;
+	PPCSTRUCT(pi)->lpt_addr = pi->port;
+
+	/* look at the parport device to see if what modes we can use */
+	if (((struct pardevice *)(pi->pardev))->port->modes &
+	    (PARPORT_MODE_EPP))
+		return 5; /* Can do EPP*/
+	else if (((struct pardevice *)(pi->pardev))->port->modes &
+		 (PARPORT_MODE_TRISTATE))
+		return 2;
+	else /* Just flat SPP */
+		return 1;
+}
+
+static int bpck6_probe_unit(struct pi_adapter *pi)
+{
+	int out;
+
+	/* SET PPC UNIT NUMBER */
+	PPCSTRUCT(pi)->ppc_id = pi->unit;
+
+	/* LOWER DOWN TO UNIDIRECTIONAL */
+	PPCSTRUCT(pi)->mode = 1;
+
+	out = ppc6_open(PPCSTRUCT(pi));
+
+	if (out) {
+		ppc6_close(PPCSTRUCT(pi));
+		return 1;
+	}
+
+	return 0;
+}
+
+static void bpck6_log_adapter(struct pi_adapter *pi, char *scratch, int verbose)
+{
+	static char * const mode_string[] = {
+		"4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+	dev_info(&pi->dev, "bpck6, Micro Solutions BACKPACK Drive at 0x%x\n",
+		pi->port);
+	dev_info(&pi->dev, "Unit: %d Mode:%d (%s) Delay %d\n",
+		pi->unit, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static int bpck6_init_proto(struct pi_adapter *pi)
+{
+	struct ppc_storage *p = kzalloc(sizeof(struct ppc_storage), GFP_KERNEL);
+
+	if (p) {
+		pi->private = (unsigned long)p;
+		return 0;
+	}
+
+	return -ENOMEM;
+}
+
+static void bpck6_release_proto(struct pi_adapter *pi)
+{
+	kfree((void *)(pi->private));
+}
+
+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,
+	.init_proto	= bpck6_init_proto,
+	.release_proto	= bpck6_release_proto,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-bpck6") },
+};
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Micro Solutions Inc.");
+MODULE_DESCRIPTION("BACKPACK Protocol module, compatible with PARIDE");
+module_pata_parport_driver(bpck6);
diff --git a/drivers/ata/pata_parport/ppc6lnx.c b/drivers/ata/pata_parport/ppc6lnx.c
new file mode 100644
index 000000000000..52e0f08548c9
--- /dev/null
+++ b/drivers/ata/pata_parport/ppc6lnx.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	ppc6lnx.c (c) 2001 Micro Solutions Inc.
+ *		Released under the terms of the GNU General Public license
+ *
+ *	ppc6lnx.c  is a part of the protocol driver for the Micro Solutions
+ *		"BACKPACK" parallel port IDE adapter
+ *		(Works on Series 6 drives)
+ */
+
+/* PPC 6 Code in C sanitized for LINUX */
+/* Original x86 ASM by Ron, Converted to C by Clive */
+
+#define port_stb	1
+#define port_afd	2
+#define cmd_stb		port_afd
+#define port_init	4
+#define data_stb	port_init
+#define port_sel	8
+#define port_int	16
+#define port_dir	0x20
+
+#define ECR_EPP		0x80
+#define ECR_BI		0x20
+
+/* 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 cmd that modifies the 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) */
+
+struct ppc_storage {
+	u16	lpt_addr;		/* LPT base address */
+	u8	ppc_id;
+	u8	mode;			/* operating mode */
+					/* 0 = PPC Uni SW */
+					/* 1 = PPC Uni FW */
+					/* 2 = PPC Bi SW */
+					/* 3 = PPC Bi FW */
+					/* 4 = EPP Byte */
+					/* 5 = EPP Word */
+					/* 6 = EPP Dword */
+	u8	ppc_flags;
+	u8	org_data;		/* original LPT data port contents */
+	u8	org_ctrl;		/* original LPT control port contents */
+	u8	cur_ctrl;		/* current control port contents */
+};
+
+/* ppc_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 ppc6_select(struct ppc_storage *ppc);
+static void ppc6_deselect(struct ppc_storage *ppc);
+static void ppc6_send_cmd(struct ppc_storage *ppc, u8 cmd);
+static void ppc6_wr_data_byte(struct ppc_storage *ppc, u8 data);
+static u8 ppc6_rd_data_byte(struct ppc_storage *ppc);
+static u8 ppc6_rd_port(struct ppc_storage *ppc, u8 port);
+static void ppc6_wr_port(struct ppc_storage *ppc, u8 port, u8 data);
+static void ppc6_rd_data_blk(struct ppc_storage *ppc, u8 *data, long count);
+static void ppc6_wait_for_fifo(struct ppc_storage *ppc);
+static void ppc6_wr_data_blk(struct ppc_storage *ppc, u8 *data, long count);
+static void ppc6_rd_port16_blk(struct ppc_storage *ppc, u8 port, u8 *data, long length);
+static void ppc6_wr_port16_blk(struct ppc_storage *ppc, u8 port, u8 *data, long length);
+static void ppc6_wr_extout(struct ppc_storage *ppc, u8 regdata);
+static int ppc6_open(struct ppc_storage *ppc);
+static void ppc6_close(struct ppc_storage *ppc);
+
+static int ppc6_select(struct ppc_storage *ppc)
+{
+	u8 i, j, k;
+
+	i = inb(ppc->lpt_addr + 1);
+	if (i & 1)
+		outb(i, ppc->lpt_addr + 1);
+
+	ppc->org_data = inb(ppc->lpt_addr);
+	ppc->org_ctrl = inb(ppc->lpt_addr + 2) & 0x5F; /* readback ctrl */
+	ppc->cur_ctrl = ppc->org_ctrl;
+	ppc->cur_ctrl |= port_sel;
+	outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+	if (ppc->org_data == 'b')
+		outb('x', ppc->lpt_addr);
+	outb('b', ppc->lpt_addr);
+	outb('p', ppc->lpt_addr);
+	outb(ppc->ppc_id, ppc->lpt_addr);
+	outb(~ppc->ppc_id, ppc->lpt_addr);
+	ppc->cur_ctrl &= ~port_sel;
+	outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+	ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init;
+	outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+	i = ppc->mode & 0x0C;
+	if (i == 0)
+		i = (ppc->mode & 2) | 1;
+	outb(i, ppc->lpt_addr);
+	ppc->cur_ctrl |= port_sel;
+	outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+	/* DELAY */
+	ppc->cur_ctrl |= port_afd;
+	outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+	j = ((i & 0x08) << 4) | ((i & 0x07) << 3);
+	k = inb(ppc->lpt_addr + 1) & 0xB8;
+	if (j == k) {
+		ppc->cur_ctrl &= ~port_afd;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		k = (inb(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8;
+		if (j == k) {
+			if (i & 4)	/* EPP */
+				ppc->cur_ctrl &= ~(port_sel | port_init);
+			else		/* PPC/ECP */
+				ppc->cur_ctrl &= ~port_sel;
+
+			outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+
+			return 1;
+		}
+	}
+	outb(ppc->org_ctrl, ppc->lpt_addr + 2);
+	outb(ppc->org_data, ppc->lpt_addr);
+
+	return 0; /* FAIL */
+}
+
+static void ppc6_deselect(struct ppc_storage *ppc)
+{
+	if (ppc->mode & 4)	/* EPP */
+		ppc->cur_ctrl |= port_init;
+	else			/* PPC/ECP */
+		ppc->cur_ctrl |= port_sel;
+
+	outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+	outb(ppc->org_data, ppc->lpt_addr);
+	outb((ppc->org_ctrl | port_sel), ppc->lpt_addr + 2);
+	outb(ppc->org_ctrl, ppc->lpt_addr + 2);
+}
+
+static void ppc6_send_cmd(struct ppc_storage *ppc, u8 cmd)
+{
+	switch (ppc->mode) {
+	case PPCMODE_UNI_SW:
+	case PPCMODE_UNI_FW:
+	case PPCMODE_BI_SW:
+	case PPCMODE_BI_FW:
+		outb(cmd, ppc->lpt_addr);
+		ppc->cur_ctrl ^= cmd_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		break;
+	case PPCMODE_EPP_BYTE:
+	case PPCMODE_EPP_WORD:
+	case PPCMODE_EPP_DWORD:
+		outb(cmd, ppc->lpt_addr + 3);
+		break;
+	}
+}
+
+static void ppc6_wr_data_byte(struct ppc_storage *ppc, u8 data)
+{
+	switch (ppc->mode) {
+	case PPCMODE_UNI_SW:
+	case PPCMODE_UNI_FW:
+	case PPCMODE_BI_SW:
+	case PPCMODE_BI_FW:
+		outb(data, ppc->lpt_addr);
+		ppc->cur_ctrl ^= data_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		break;
+	case PPCMODE_EPP_BYTE:
+	case PPCMODE_EPP_WORD:
+	case PPCMODE_EPP_DWORD:
+		outb(data, ppc->lpt_addr + 4);
+		break;
+	}
+}
+
+static u8 ppc6_rd_data_byte(struct ppc_storage *ppc)
+{
+	u8 data = 0;
+
+	switch (ppc->mode) {
+	case PPCMODE_UNI_SW:
+	case PPCMODE_UNI_FW:
+		ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		/* DELAY */
+		data = inb(ppc->lpt_addr + 1);
+		data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);
+		ppc->cur_ctrl |= port_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		/* DELAY */
+		data |= inb(ppc->lpt_addr + 1) & 0xB8;
+		break;
+	case PPCMODE_BI_SW:
+	case PPCMODE_BI_FW:
+		ppc->cur_ctrl |= port_dir;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		data = inb(ppc->lpt_addr);
+		ppc->cur_ctrl &= ~port_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		ppc->cur_ctrl &= ~port_dir;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		break;
+	case PPCMODE_EPP_BYTE:
+	case PPCMODE_EPP_WORD:
+	case PPCMODE_EPP_DWORD:
+		outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
+		data = inb(ppc->lpt_addr + 4);
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		break;
+	}
+
+	return data;
+}
+
+static u8 ppc6_rd_port(struct ppc_storage *ppc, u8 port)
+{
+	ppc6_send_cmd(ppc, port | ACCESS_PORT | ACCESS_READ);
+
+	return ppc6_rd_data_byte(ppc);
+}
+
+static void ppc6_wr_port(struct ppc_storage *ppc, u8 port, u8 data)
+{
+	ppc6_send_cmd(ppc, port | ACCESS_PORT | ACCESS_WRITE);
+
+	ppc6_wr_data_byte(ppc, data);
+}
+
+static void ppc6_rd_data_blk(struct ppc_storage *ppc, u8 *data, long count)
+{
+	switch (ppc->mode) {
+	case PPCMODE_UNI_SW:
+	case PPCMODE_UNI_FW:
+		while (count) {
+			u8 d;
+
+			ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
+			outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+			/* DELAY */
+			d = inb(ppc->lpt_addr + 1);
+			d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);
+			ppc->cur_ctrl |= port_stb;
+			outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+			/* DELAY */
+			d |= inb(ppc->lpt_addr + 1) & 0xB8;
+			*data++ = d;
+			count--;
+		}
+		break;
+	case PPCMODE_BI_SW:
+	case PPCMODE_BI_FW:
+		ppc->cur_ctrl |= port_dir;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		ppc->cur_ctrl |= port_stb;
+		while (count) {
+			ppc->cur_ctrl ^= data_stb;
+			outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+			*data++ = inb(ppc->lpt_addr);
+			count--;
+		}
+		ppc->cur_ctrl &= ~port_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		ppc->cur_ctrl &= ~port_dir;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		break;
+	case PPCMODE_EPP_BYTE:
+		outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
+		/* DELAY */
+		while (count) {
+			*data++ = inb(ppc->lpt_addr + 4);
+			count--;
+		}
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		break;
+	case PPCMODE_EPP_WORD:
+		outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
+		/* DELAY */
+		while (count > 1) {
+			*((u16 *)data) = inw(ppc->lpt_addr + 4);
+			data  += 2;
+			count -= 2;
+		}
+		while (count) {
+			*data++ = inb(ppc->lpt_addr + 4);
+			count--;
+		}
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		break;
+	case PPCMODE_EPP_DWORD:
+		outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
+		/* DELAY */
+		while (count > 3) {
+			*((u32 *)data) = inl(ppc->lpt_addr + 4);
+			data  += 4;
+			count -= 4;
+		}
+		while (count) {
+			*data++ = inb(ppc->lpt_addr + 4);
+			count--;
+		}
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		break;
+	}
+}
+
+static void ppc6_wait_for_fifo(struct ppc_storage *ppc)
+{
+	int i;
+
+	if (ppc->ppc_flags & fifo_wait)
+		for (i = 0; i < 20; i++)
+			inb(ppc->lpt_addr + 1);
+}
+
+static void ppc6_wr_data_blk(struct ppc_storage *ppc, u8 *data, long count)
+{
+	u8 this, last;
+
+	switch (ppc->mode) {
+	case PPCMODE_UNI_SW:
+	case PPCMODE_BI_SW:
+		while (count--)	{
+			outb(*data++, ppc->lpt_addr);
+			ppc->cur_ctrl ^= data_stb;
+			outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		}
+		break;
+	case PPCMODE_UNI_FW:
+	case PPCMODE_BI_FW:
+		ppc6_send_cmd(ppc, CMD_PREFIX_SET | PREFIX_FASTWR);
+		ppc->cur_ctrl |= port_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		last = *data;
+		outb(last, ppc->lpt_addr);
+		while (count) {
+			this = *data++;
+			count--;
+
+			if (this == last) {
+				ppc->cur_ctrl ^= data_stb;
+				outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+			} else {
+				outb(this, ppc->lpt_addr);
+				last = this;
+			}
+		}
+		ppc->cur_ctrl &= ~port_stb;
+		outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
+		ppc6_send_cmd(ppc, CMD_PREFIX_RESET | PREFIX_FASTWR);
+		break;
+	case PPCMODE_EPP_BYTE:
+		while (count) {
+			outb(*data++, ppc->lpt_addr + 4);
+			count--;
+		}
+		ppc6_wait_for_fifo(ppc);
+		break;
+	case PPCMODE_EPP_WORD:
+		while (count > 1) {
+			outw(*((u16 *)data), ppc->lpt_addr + 4);
+			data  += 2;
+			count -= 2;
+		}
+		while (count) {
+			outb(*data++, ppc->lpt_addr + 4);
+			count--;
+		}
+		ppc6_wait_for_fifo(ppc);
+		break;
+	case PPCMODE_EPP_DWORD:
+		while (count > 3) {
+			outl(*((u32 *)data), ppc->lpt_addr + 4);
+			data  += 4;
+			count -= 4;
+		}
+		while (count) {
+			outb(*data++, ppc->lpt_addr + 4);
+			count--;
+		}
+		ppc6_wait_for_fifo(ppc);
+		break;
+	}
+}
+
+static void ppc6_rd_port16_blk(struct ppc_storage *ppc, u8 port, u8 *data, long length)
+{
+	length = length << 1;
+
+	ppc6_send_cmd(ppc, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);
+	ppc6_wr_data_byte(ppc, (u8)length);
+	ppc6_wr_data_byte(ppc, (u8)(length >> 8));
+	ppc6_wr_data_byte(ppc, 0);
+
+	ppc6_send_cmd(ppc, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);
+
+	ppc6_send_cmd(ppc, port | ACCESS_PORT | ACCESS_READ);
+
+	ppc6_rd_data_blk(ppc, data, length);
+
+	ppc6_send_cmd(ppc, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);
+}
+
+static void ppc6_wr_port16_blk(struct ppc_storage *ppc, u8 port, u8 *data, long length)
+{
+	length = length << 1;
+
+	ppc6_send_cmd(ppc, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);
+	ppc6_wr_data_byte(ppc, (u8)length);
+	ppc6_wr_data_byte(ppc, (u8)(length >> 8));
+	ppc6_wr_data_byte(ppc, 0);
+
+	ppc6_send_cmd(ppc, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);
+
+	ppc6_send_cmd(ppc, port | ACCESS_PORT | ACCESS_WRITE);
+
+	ppc6_wr_data_blk(ppc, data, length);
+
+	ppc6_send_cmd(ppc, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);
+}
+
+static void ppc6_wr_extout(struct ppc_storage *ppc, u8 regdata)
+{
+	ppc6_send_cmd(ppc, REG_VERSION | ACCESS_REG | ACCESS_WRITE);
+
+	ppc6_wr_data_byte(ppc, (regdata & 0x03) << 6);
+}
+
+static int ppc6_open(struct ppc_storage *ppc)
+{
+	int ret;
+
+	ret = ppc6_select(ppc);
+	if (ret == 0)
+		return ret;
+
+	ppc->ppc_flags &= ~fifo_wait;
+
+	ppc6_send_cmd(ppc, ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE);
+	ppc6_wr_data_byte(ppc, RAMSIZE_128K);
+
+	ppc6_send_cmd(ppc, ACCESS_REG | ACCESS_READ | REG_VERSION);
+
+	if ((ppc6_rd_data_byte(ppc) & 0x3F) == 0x0C)
+		ppc->ppc_flags |= fifo_wait;
+
+	return ret;
+}
+
+static void ppc6_close(struct ppc_storage *ppc)
+{
+	ppc6_deselect(ppc);
+}
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 05/16] pata_parport: add comm protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (3 preceding siblings ...)
  2022-03-05 20:13 ` [PATCH 04/16] pata_parport: add bpck6 " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 06/16] pata_parport: add dstr " Ondrej Zary
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add DataStor Commuter protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  10 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/comm.c   | 198 ++++++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/ata/pata_parport/comm.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index e88d9c0bedc6..3cc3d6ae4d45 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -54,3 +54,13 @@ config PATA_PARPORT_BPCK6
 	  answer Y here to build in the protocol driver, otherwise you should
 	  answer M to build it as a loadable module.  The module will be
 	  called bpck6.
+
+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. If you chose to build PATA_PARPORT support
+	  into your kernel, you may answer Y here to build in the protocol
+	  driver, otherwise you should answer M to build it as a loadable
+	  module. The module will be called comm.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 60522279aa16..fcb77a855ef6 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -10,3 +10,4 @@ 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_BPCK6)	+= bpck6.o
+obj-$(CONFIG_PATA_PARPORT_COMM)		+= comm.o
diff --git a/drivers/ata/pata_parport/comm.c b/drivers/ata/pata_parport/comm.c
new file mode 100644
index 000000000000..cca67e15b498
--- /dev/null
+++ b/drivers/ata/pata_parport/comm.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	comm.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *			      Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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	do { w2(5); w2(0xd); w2(0xd); w2(5); w2(4); } while (0)
+#define P2	do { w2(5); w2(7); w2(7); w2(5); w2(4); } while (0)
+
+/* 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(pi_swab16(buf, k));
+		break;
+	case 4:
+		w3(0x48); (void)r1();
+		for (k = 0; k < count / 4; k++)
+			w4l(pi_swab32(buf, k));
+		break;
+	}
+}
+
+static void comm_log_adapter(struct pi_adapter *pi, char *scratch, int verbose)
+{
+	static char * const mode_string[] = {
+		"4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+	dev_info(&pi->dev, "comm, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-comm") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(comm);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 06/16] pata_parport: add dstr protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (4 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 05/16] pata_parport: add comm " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 07/16] pata_parport: add fit2 " Ondrej Zary
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add DataStor EP-2000 protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  10 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/dstr.c   | 226 ++++++++++++++++++++++++++++++
 3 files changed, 237 insertions(+)
 create mode 100644 drivers/ata/pata_parport/dstr.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 3cc3d6ae4d45..67f203dfbdca 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -64,3 +64,13 @@ config PATA_PARPORT_COMM
 	  into your kernel, you may answer Y here to build in the protocol
 	  driver, otherwise you should answer M to build it as a loadable
 	  module. The module will be called comm.
+
+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. If you chose to build PATA_PARPORT support
+	  into your kernel, you may answer Y here to build in the protocol
+	  driver, otherwise you should answer M to build it as a loadable
+	  module. The module will be called dstr.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index fcb77a855ef6..bf81c4ca32ab 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_PATA_PARPORT_ATEN)		+= aten.o
 obj-$(CONFIG_PATA_PARPORT_BPCK)		+= bpck.o
 obj-$(CONFIG_PATA_PARPORT_BPCK6)	+= bpck6.o
 obj-$(CONFIG_PATA_PARPORT_COMM)		+= comm.o
+obj-$(CONFIG_PATA_PARPORT_DSTR)		+= dstr.o
diff --git a/drivers/ata/pata_parport/dstr.c b/drivers/ata/pata_parport/dstr.c
new file mode 100644
index 000000000000..2e1e078fa467
--- /dev/null
+++ b/drivers/ata/pata_parport/dstr.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	dstr.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *			      Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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	do { w2(5); w2(0xd); w2(5); w2(4); } while (0)
+#define P2	do { w2(5); w2(7); w2(5); w2(4); } while (0)
+#define P3      do { w2(6); w2(4); w2(6); w2(4); } while (0)
+
+/* 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 *scratch, int verbose)
+{
+	static char * const mode_string[] = {
+		"4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+	dev_info(&pi->dev, "dstr, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-dstr") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(dstr);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 07/16] pata_parport: add fit2 protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (5 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 06/16] pata_parport: add dstr " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 08/16] pata_parport: add fit3 " Ondrej Zary
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add FIT TD-2000 protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  11 +++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/fit2.c   | 135 ++++++++++++++++++++++++++++++
 3 files changed, 147 insertions(+)
 create mode 100644 drivers/ata/pata_parport/fit2.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 67f203dfbdca..db8edb288db4 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -74,3 +74,14 @@ config PATA_PARPORT_DSTR
 	  into your kernel, you may answer Y here to build in the protocol
 	  driver, otherwise you should answer M to build it as a loadable
 	  module. The module will be called dstr.
+
+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. If
+	  you chose to build PATA_PARPORT support into your kernel, you may answer Y
+	  here to build in the protocol driver, otherwise you should answer M
+	  to build it as a loadable module. The module will be called ktti.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index bf81c4ca32ab..27d854f52ad7 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_PATA_PARPORT_BPCK)		+= bpck.o
 obj-$(CONFIG_PATA_PARPORT_BPCK6)	+= bpck6.o
 obj-$(CONFIG_PATA_PARPORT_COMM)		+= comm.o
 obj-$(CONFIG_PATA_PARPORT_DSTR)		+= dstr.o
+obj-$(CONFIG_PATA_PARPORT_FIT2)		+= fit2.o
diff --git a/drivers/ata/pata_parport/fit2.c b/drivers/ata/pata_parport/fit2.c
new file mode 100644
index 000000000000..92cee479108c
--- /dev/null
+++ b/drivers/ata/pata_parport/fit2.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	fit2.c	(c) 1998  Grant R. Guenther <grant@torque.net>
+ *			  Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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, char *scratch, int verbose)
+{
+	dev_info(&pi->dev, "fit2, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-fit2") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(fit2);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 08/16] pata_parport: add fit3 protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (6 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 07/16] pata_parport: add fit2 " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 09/16] pata_parport: add epat " Ondrej Zary
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add FIT TD-3000 protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  12 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/fit3.c   | 195 ++++++++++++++++++++++++++++++
 3 files changed, 208 insertions(+)
 create mode 100644 drivers/ata/pata_parport/fit3.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index db8edb288db4..a03c626e4e6c 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -85,3 +85,15 @@ config PATA_PARPORT_FIT2
 	  you chose to build PATA_PARPORT support into your kernel, you may answer Y
 	  here to build in the protocol driver, otherwise you should answer M
 	  to build it as a loadable module. The module will be called ktti.
+
+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. If you chose to build PATA_PARPORT support into your kernel, you
+	  may answer Y here to build in the protocol driver, otherwise you
+	  should answer M to build it as a loadable module. The module will be
+	  called fit3.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 27d854f52ad7..140f6f142eaa 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_PATA_PARPORT_BPCK6)	+= bpck6.o
 obj-$(CONFIG_PATA_PARPORT_COMM)		+= comm.o
 obj-$(CONFIG_PATA_PARPORT_DSTR)		+= dstr.o
 obj-$(CONFIG_PATA_PARPORT_FIT2)		+= fit2.o
+obj-$(CONFIG_PATA_PARPORT_FIT3)		+= fit3.o
diff --git a/drivers/ata/pata_parport/fit3.c b/drivers/ata/pata_parport/fit3.c
new file mode 100644
index 000000000000..7c874dc8b41b
--- /dev/null
+++ b/drivers/ata/pata_parport/fit3.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	fit3.c	(c) 1998  Grant R. Guenther <grant@torque.net>
+ *			  Under the terms of the GNU General Public License.
+ *
+ *	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.
+ *
+ *	NB:  The FIT adapters do not appear to support 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.
+ */
+
+#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 <linux/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)
+{
+	if (cont == 1)
+		return;
+
+	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;
+
+	if (cont) {
+		if (regr != 6)
+			return 0xff;
+		regr = 7;
+	}
+
+	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 *scratch, int verbose)
+{
+	static char * const mode_string[] = { "4-bit", "8-bit", "EPP" };
+
+	dev_info(&pi->dev, "fit3, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-fit3") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(fit3);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 09/16] pata_parport: add epat protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (7 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 08/16] pata_parport: add fit3 " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 10/16] pata_parport: add epia " Ondrej Zary
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add Shuttle EPAT/EPEZ protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  12 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/epat.c   | 320 ++++++++++++++++++++++++++++++
 3 files changed, 333 insertions(+)
 create mode 100644 drivers/ata/pata_parport/epat.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index a03c626e4e6c..6c218486c3b8 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -97,3 +97,15 @@ config PATA_PARPORT_FIT3
 	  may answer Y here to build in the protocol driver, otherwise you
 	  should answer M to build it as a loadable module. The module will be
 	  called fit3.
+
+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. If you chose to build
+	  PATA_PARPORT support into your kernel, you may answer Y here to build in
+	  the protocol driver, otherwise you should answer M to build it as a
+	  loadable module. The module will be called epat.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 140f6f142eaa..459307f4a096 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_PATA_PARPORT_COMM)		+= comm.o
 obj-$(CONFIG_PATA_PARPORT_DSTR)		+= dstr.o
 obj-$(CONFIG_PATA_PARPORT_FIT2)		+= fit2.o
 obj-$(CONFIG_PATA_PARPORT_FIT3)		+= fit3.o
+obj-$(CONFIG_PATA_PARPORT_EPAT)		+= epat.o
diff --git a/drivers/ata/pata_parport/epat.c b/drivers/ata/pata_parport/epat.c
new file mode 100644
index 000000000000..2103e272c82a
--- /dev/null
+++ b/drivers/ata/pata_parport/epat.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	epat.c  (c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *			    Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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 = 1;
+module_param(epatc8, int, 0);
+MODULE_PARM_DESC(epatc8, "Support for the Shuttle EP1284 chip, used in any recent Imation SuperDisk (LS-120) drive (0=off, 1=on [default])");
+
+/* 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, char *scratch, int verbose)
+{
+	int k, j, f, cc;
+	int e[2] = { 0, 0 };
+
+	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);
+
+	if (verbose)
+		dev_info(&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, char *scratch, int verbose)
+{
+	int ver;
+	static char * const mode_string[] = {
+		"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, "epat, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-epat") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(epat);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 10/16] pata_parport: add epia protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (8 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 09/16] pata_parport: add epat " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 11/16] pata_parport: add friq " Ondrej Zary
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add Shuttle EPIA protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  11 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/epia.c   | 308 ++++++++++++++++++++++++++++++
 3 files changed, 320 insertions(+)
 create mode 100644 drivers/ata/pata_parport/epia.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 6c218486c3b8..832ab8b00511 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -109,3 +109,14 @@ config PATA_PARPORT_EPAT
 	  PATA_PARPORT support into your kernel, you may answer Y here to build in
 	  the protocol driver, otherwise you should answer M to build it as a
 	  loadable module. The module will be called epat.
+
+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. If you chose to build PATA_PARPORT support
+	  into your kernel, you may answer Y here to build in the protocol
+	  driver, otherwise you should answer M to build it as a loadable
+	  module. The module will be called epia.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 459307f4a096..6e72778f45cb 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_PATA_PARPORT_DSTR)		+= dstr.o
 obj-$(CONFIG_PATA_PARPORT_FIT2)		+= fit2.o
 obj-$(CONFIG_PATA_PARPORT_FIT3)		+= fit3.o
 obj-$(CONFIG_PATA_PARPORT_EPAT)		+= epat.o
+obj-$(CONFIG_PATA_PARPORT_EPIA)		+= epia.o
diff --git a/drivers/ata/pata_parport/epia.c b/drivers/ata/pata_parport/epia.c
new file mode 100644
index 000000000000..7f3db02fd243
--- /dev/null
+++ b/drivers/ata/pata_parport/epia.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	epia.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *			      Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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, char *scratch, int verbose)
+{
+	int j, k, f;
+	int e[2] = { 0, 0 };
+
+	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);
+
+	if (verbose)
+		dev_info(&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 *scratch, int verbose)
+{
+	static char * const mode_string[] = {
+		"4-bit", "5/3", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+	dev_info(&pi->dev, "epia, Shuttle EPIA at 0x%x, mode %d (%s), delay %d\n",
+		pi->port, pi->mode, mode_string[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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-epia") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(epia);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 11/16] pata_parport: add friq protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (9 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 10/16] pata_parport: add epia " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 12/16] pata_parport: add frpw " Ondrej Zary
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add Freecom IQ ASIC-2 protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  12 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/friq.c   | 263 ++++++++++++++++++++++++++++++
 3 files changed, 276 insertions(+)
 create mode 100644 drivers/ata/pata_parport/friq.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 832ab8b00511..8a569ee0bf6a 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -120,3 +120,15 @@ config PATA_PARPORT_EPIA
 	  into your kernel, you may answer Y here to build in the protocol
 	  driver, otherwise you should answer M to build it as a loadable
 	  module. The module will be called epia.
+
+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.  If you chose to build PATA_PARPORT support into your kernel, you
+	  may answer Y here to build in the protocol driver, otherwise you
+	  should answer M to build it as a loadable module. The module will be
+	  called friq. You must also have a high-level driver for the type
+	  of device that you want to support.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 6e72778f45cb..20d05e525c95 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_PATA_PARPORT_FIT2)		+= fit2.o
 obj-$(CONFIG_PATA_PARPORT_FIT3)		+= fit3.o
 obj-$(CONFIG_PATA_PARPORT_EPAT)		+= epat.o
 obj-$(CONFIG_PATA_PARPORT_EPIA)		+= epia.o
+obj-$(CONFIG_PATA_PARPORT_FRIQ)		+= friq.o
diff --git a/drivers/ata/pata_parport/friq.c b/drivers/ata/pata_parport/friq.c
new file mode 100644
index 000000000000..73dabeb7c3d3
--- /dev/null
+++ b/drivers/ata/pata_parport/friq.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	friq.c	(c) 1998    Grant R. Guenther <grant@torque.net>
+ *			    Under the terms of the GNU General Public License
+ *
+ *	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 <linux/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, char *scratch, int verbose)
+{
+	int j, k, r;
+	int e[2] = { 0, 0 };
+
+	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);
+
+	if (verbose)
+		dev_info(&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 *scratch, int verbose)
+{
+	static char * const mode_string[] = {
+		"4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+	dev_info(&pi->dev, "friq, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-friq") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(friq);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 12/16] pata_parport: add frpw protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (10 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 11/16] pata_parport: add friq " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 13/16] pata_parport: add kbic " Ondrej Zary
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add FreeCom power protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  10 +
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/frpw.c   | 292 ++++++++++++++++++++++++++++++
 3 files changed, 303 insertions(+)
 create mode 100644 drivers/ata/pata_parport/frpw.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 8a569ee0bf6a..d8e5feaaae7a 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -132,3 +132,13 @@ config PATA_PARPORT_FRIQ
 	  should answer M to build it as a loadable module. The module will be
 	  called friq. You must also have a high-level driver for the type
 	  of device that you want to support.
+
+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. If you chose to build PATA_PARPORT support into your kernel, you
+	  may answer Y here to build in the protocol driver, otherwise you
+	  should answer M to build it as a loadable module. The module will be
+	  called frpw.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 20d05e525c95..136374be9613 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_PATA_PARPORT_FIT3)		+= fit3.o
 obj-$(CONFIG_PATA_PARPORT_EPAT)		+= epat.o
 obj-$(CONFIG_PATA_PARPORT_EPIA)		+= epia.o
 obj-$(CONFIG_PATA_PARPORT_FRIQ)		+= friq.o
+obj-$(CONFIG_PATA_PARPORT_FRPW)		+= frpw.o
diff --git a/drivers/ata/pata_parport/frpw.c b/drivers/ata/pata_parport/frpw.c
new file mode 100644
index 000000000000..5151d3c26361
--- /dev/null
+++ b/drivers/ata/pata_parport/frpw.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	frpw.c	(c) 1996-8  Grant R. Guenther <grant@torque.net>
+ *			    Under the terms of the GNU General Public License
+ *
+ *	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 <linux/io.h>
+
+#include "pata_parport.h"
+
+#define cec4		do { w2(0xc); w2(0xe); w2(0xe); w2(0xc); w2(4); w2(4); w2(4); } 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 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, char *scratch, int verbose)
+{
+	int j, k, r;
+	int e[2] = { 0, 0 };
+
+	if ((pi->private>>1) != pi->port)
+		pi->private = frpw_test_pnp(pi) + 2 * pi->port;
+
+	if (((pi->private % 2) == 0) && (pi->mode > 2)) {
+		if (verbose)
+			dev_info(&pi->dev, "frpw: Xilinx does not support mode %d\n",
+				pi->mode);
+		return 1;
+	}
+
+	if (((pi->private % 2) == 1) && (pi->mode == 2)) {
+		if (verbose)
+			dev_info(&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);
+
+	if (verbose)
+		dev_info(&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 *scratch, int verbose)
+{
+	static char * const mode_string[] = {
+		"4-bit", "8-bit", "EPP", "EPP-8", "EPP-16", "EPP-32" };
+
+	dev_info(&pi->dev, "frpw, Freecom (%s) adapter at 0x%x, mode %d (%s), delay %d\n",
+		((pi->private % 2) == 0) ? "Xilinx" : "ASIC", pi->port,
+		pi->mode, mode_string[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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-frpw") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(frpw);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 13/16] pata_parport: add kbic protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (11 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 12/16] pata_parport: add frpw " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 14/16] pata_parport: add ktti " Ondrej Zary
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add KingByte KBIC-951A/971A protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  12 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/kbic.c   | 292 ++++++++++++++++++++++++++++++
 3 files changed, 305 insertions(+)
 create mode 100644 drivers/ata/pata_parport/kbic.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index d8e5feaaae7a..004e5cf708f1 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -142,3 +142,15 @@ config PATA_PARPORT_FRPW
 	  may answer Y here to build in the protocol driver, otherwise you
 	  should answer M to build it as a loadable module. The module will be
 	  called frpw.
+
+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. If you chose to build PATA_PARPORT support into your
+	  kernel, you may answer Y here to build in the protocol driver,
+	  otherwise you should answer M to build it as a loadable module. The
+	  module will be called kbic.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 136374be9613..e82fded22da3 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_PATA_PARPORT_EPAT)		+= epat.o
 obj-$(CONFIG_PATA_PARPORT_EPIA)		+= epia.o
 obj-$(CONFIG_PATA_PARPORT_FRIQ)		+= friq.o
 obj-$(CONFIG_PATA_PARPORT_FRPW)		+= frpw.o
+obj-$(CONFIG_PATA_PARPORT_KBIC)		+= kbic.o
diff --git a/drivers/ata/pata_parport/kbic.c b/drivers/ata/pata_parport/kbic.c
new file mode 100644
index 000000000000..49f8a88db2a2
--- /dev/null
+++ b/drivers/ata/pata_parport/kbic.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	kbic.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *			      Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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);
+}
+
+/* counts 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(pi_swab16(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(pi_swab32(buf, k));
+		w2(4); w2(0); w2(4);
+		break;
+	}
+}
+
+static void kbic_log_adapter(struct pi_adapter *pi, char *scratch, int verbose, char *chip)
+{
+	static char * const mode_string[] = {
+		"4-bit", "5/3", "8-bit", "EPP-8", "EPP_16", "EPP-32"};
+
+	dev_info(&pi->dev, "kbic, KingByte %s at 0x%x, mode %d (%s), delay %d\n",
+		chip, pi->port, pi->mode, mode_string[pi->mode], pi->delay);
+}
+
+static void k951_log_adapter(struct pi_adapter *pi, char *scratch, int verbose)
+{
+	kbic_log_adapter(pi, scratch, verbose, "KBIC-951A");
+}
+
+static void k971_log_adapter(struct pi_adapter *pi, char *scratch, int verbose)
+{
+	kbic_log_adapter(pi, scratch, verbose, "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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-kbic") },
+};
+
+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_init(kbic_init)
+module_exit(kbic_exit)
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 14/16] pata_parport: add ktti protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (12 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 13/16] pata_parport: add kbic " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 15/16] pata_parport: add on20 " Ondrej Zary
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add KT PHd protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  11 +++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/ktti.c   | 112 ++++++++++++++++++++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 drivers/ata/pata_parport/ktti.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 004e5cf708f1..4505a037591a 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -154,3 +154,14 @@ config PATA_PARPORT_KBIC
 	  kernel, you may answer Y here to build in the protocol driver,
 	  otherwise you should answer M to build it as a loadable module. The
 	  module will be called kbic.
+
+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. If you chose to build PATA_PARPORT
+	  support into your kernel, you may answer Y here to build in the
+	  protocol driver, otherwise you should answer M to build it as a
+	  loadable module. The module will be called ktti.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index e82fded22da3..0c31cf55c96b 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_PATA_PARPORT_EPIA)		+= epia.o
 obj-$(CONFIG_PATA_PARPORT_FRIQ)		+= friq.o
 obj-$(CONFIG_PATA_PARPORT_FRPW)		+= frpw.o
 obj-$(CONFIG_PATA_PARPORT_KBIC)		+= kbic.o
+obj-$(CONFIG_PATA_PARPORT_KTTI)		+= ktti.o
diff --git a/drivers/ata/pata_parport/ktti.c b/drivers/ata/pata_parport/ktti.c
new file mode 100644
index 000000000000..4604bbd41b18
--- /dev/null
+++ b/drivers/ata/pata_parport/ktti.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	ktti.c	(c) 1998  Grant R. Guenther <grant@torque.net>
+ *			  Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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, char *scratch, int verbose)
+{
+	dev_info(&pi->dev, "ktti, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-ktti") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(ktti);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 15/16] pata_parport: add on20 protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (13 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 14/16] pata_parport: add ktti " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-05 20:14 ` [PATCH 16/16] pata_parport: add on26 " Ondrej Zary
  2022-03-06  8:58 ` [RFC PATCH] pata_parport: paride replacement Christoph Hellwig
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add OnSpec 90c20 protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  11 +++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/on20.c   | 136 ++++++++++++++++++++++++++++++
 3 files changed, 148 insertions(+)
 create mode 100644 drivers/ata/pata_parport/on20.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index 4505a037591a..c18757464b1d 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -165,3 +165,14 @@ config PATA_PARPORT_KTTI
 	  support into your kernel, you may answer Y here to build in the
 	  protocol driver, otherwise you should answer M to build it as a
 	  loadable module. The module will be called ktti.
+
+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). If you chose to build PATA_PARPORT support into your kernel, you
+	  may answer Y here to build in the protocol driver, otherwise you
+	  should answer M to build it as a loadable module.The module will
+	  be called on20.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 0c31cf55c96b..4358d1d946cb 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_PATA_PARPORT_FRIQ)		+= friq.o
 obj-$(CONFIG_PATA_PARPORT_FRPW)		+= frpw.o
 obj-$(CONFIG_PATA_PARPORT_KBIC)		+= kbic.o
 obj-$(CONFIG_PATA_PARPORT_KTTI)		+= ktti.o
+obj-$(CONFIG_PATA_PARPORT_ON20)		+= on20.o
diff --git a/drivers/ata/pata_parport/on20.c b/drivers/ata/pata_parport/on20.c
new file mode 100644
index 000000000000..2c0c6f9841dd
--- /dev/null
+++ b/drivers/ata/pata_parport/on20.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	on20.c	(c) 1996-8  Grant R. Guenther <grant@torque.net>
+ *			    Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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 *scratch, int verbose)
+{
+	static char * const mode_string[] = { "4-bit", "8-bit" };
+
+	dev_info(&pi->dev, "on20, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-on20") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(on20);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 16/16] pata_parport: add on26 protocol driver
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (14 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 15/16] pata_parport: add on20 " Ondrej Zary
@ 2022-03-05 20:14 ` Ondrej Zary
  2022-03-06  8:58 ` [RFC PATCH] pata_parport: paride replacement Christoph Hellwig
  16 siblings, 0 replies; 27+ messages in thread
From: Ondrej Zary @ 2022-03-05 20:14 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Add OnSpec 90c26 protocol driver.

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 drivers/ata/pata_parport/Kconfig  |  11 ++
 drivers/ata/pata_parport/Makefile |   1 +
 drivers/ata/pata_parport/on26.c   | 302 ++++++++++++++++++++++++++++++
 3 files changed, 314 insertions(+)
 create mode 100644 drivers/ata/pata_parport/on26.c

diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
index c18757464b1d..5d3440f372fc 100644
--- a/drivers/ata/pata_parport/Kconfig
+++ b/drivers/ata/pata_parport/Kconfig
@@ -176,3 +176,14 @@ config PATA_PARPORT_ON20
 	  may answer Y here to build in the protocol driver, otherwise you
 	  should answer M to build it as a loadable module.The module will
 	  be called on20.
+
+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). If you chose to build PATA_PARPORT support into your kernel, you
+	  may answer Y here to build in the protocol driver, otherwise you
+	  should answer M to build it as a loadable module. The module will be
+	  called on26.
diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
index 4358d1d946cb..2b03a5a9131a 100644
--- a/drivers/ata/pata_parport/Makefile
+++ b/drivers/ata/pata_parport/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_PATA_PARPORT_FRPW)		+= frpw.o
 obj-$(CONFIG_PATA_PARPORT_KBIC)		+= kbic.o
 obj-$(CONFIG_PATA_PARPORT_KTTI)		+= ktti.o
 obj-$(CONFIG_PATA_PARPORT_ON20)		+= on20.o
+obj-$(CONFIG_PATA_PARPORT_ON26)		+= on26.o
diff --git a/drivers/ata/pata_parport/on26.c b/drivers/ata/pata_parport/on26.c
new file mode 100644
index 000000000000..1f224bb38b4a
--- /dev/null
+++ b/drivers/ata/pata_parport/on26.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	on26.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *			      Under the terms of the GNU General Public License.
+ *
+ *	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 <linux/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);
+	x = 8;
+	if (pi->mode)
+		x = 9;
+
+	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
+
+static int on26_test_port(struct pi_adapter *pi)  /* hard reset */
+{
+	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 *scratch, int verbose)
+{
+	static char * const mode_string[] = {
+		"4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
+
+	dev_info(&pi->dev, "on26, 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,
+	.sht		= { PATA_PARPORT_SHT("pata_parport-on26") },
+};
+
+MODULE_LICENSE("GPL");
+module_pata_parport_driver(on26);
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [RFC PATCH] pata_parport: paride replacement
  2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
                   ` (15 preceding siblings ...)
  2022-03-05 20:14 ` [PATCH 16/16] pata_parport: add on26 " Ondrej Zary
@ 2022-03-06  8:58 ` Christoph Hellwig
  2022-03-06 10:36   ` Ondrej Zary
  16 siblings, 1 reply; 27+ messages in thread
From: Christoph Hellwig @ 2022-03-06  8:58 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Damien Le Moal, Christoph Hellwig, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

Hi Ondrej,

I just took a quick glance and it seems like the actual protocol
modules still are basically almost exactly the same ones as the
paride ones.  Is there a way to just keep the existing modules?

The only big thing I noticed is the host template, but at least
for the transitional periode we could probably allocate that
dynamically in the core.  I think would reduce the amount of code
churn nicely and make review much easier.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC PATCH] pata_parport: paride replacement
  2022-03-06  8:58 ` [RFC PATCH] pata_parport: paride replacement Christoph Hellwig
@ 2022-03-06 10:36   ` Ondrej Zary
  2022-03-08  6:54     ` Christoph Hellwig
  0 siblings, 1 reply; 27+ messages in thread
From: Ondrej Zary @ 2022-03-06 10:36 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On Sunday 06 March 2022 09:58:25 Christoph Hellwig wrote:
> Hi Ondrej,
> 
> I just took a quick glance and it seems like the actual protocol
> modules still are basically almost exactly the same ones as the
> paride ones.  Is there a way to just keep the existing modules?
> 
> The only big thing I noticed is the host template, but at least
> for the transitional periode we could probably allocate that
> dynamically in the core.  I think would reduce the amount of code
> churn nicely and make review much easier.

Yes, only small changes in the protocol modules regarding (un)registration.

Getting the original modules work with pata_parport (like in 1st preview) required some hacks that break paride (disabling EXPORT_SYMBOLs in paride).

Maybe the protocol modules can be moved (git mv) from paride and then patched? A copy would be better but there's no "git cp".

-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 01/16] pata_parport: add core driver (PARIDE replacement)
  2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
@ 2022-03-07  5:52   ` Damien Le Moal
  2022-03-07  8:55     ` Ondrej Zary
  2022-03-07 19:49   ` Sergey Shtylyov
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 27+ messages in thread
From: Damien Le Moal @ 2022-03-07  5:52 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On 3/6/22 05:13, Ondrej Zary wrote:
> Add pata_parport (PARIDE replacement) core libata driver.
> 
> Signed-off-by: Ondrej Zary <linux@zary.sk>
> ---
>  drivers/ata/Kconfig                     |  25 +
>  drivers/ata/Makefile                    |   2 +
>  drivers/ata/pata_parport/Kconfig        |  10 +
>  drivers/ata/pata_parport/Makefile       |   9 +
>  drivers/ata/pata_parport/pata_parport.c | 809 ++++++++++++++++++++++++
>  drivers/ata/pata_parport/pata_parport.h | 110 ++++
>  6 files changed, 965 insertions(+)
>  create mode 100644 drivers/ata/pata_parport/Kconfig
>  create mode 100644 drivers/ata/pata_parport/Makefile
>  create mode 100644 drivers/ata/pata_parport/pata_parport.c
>  create mode 100644 drivers/ata/pata_parport/pata_parport.h
> 
> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
> index cb54631fd950..de4548471398 100644
> --- a/drivers/ata/Kconfig
> +++ b/drivers/ata/Kconfig
> @@ -1161,6 +1161,31 @@ config PATA_WINBOND_VLB
>  	  Support for the Winbond W83759A controller on Vesa Local Bus
>  	  systems.
>  
> +config PATA_PARPORT
> +	tristate "Parallel port IDE device support (PARIDE replacement)"

I would remove the "(PARIDE replacement)" from the title. If really
needed, mention it in the description that this support used to be
implemented by PARIDE. But I do not think that is necessary. The last
paragraph of the description implies that.

> +	depends on PARPORT_PC
> +	help
> +	  There are many external CD-ROM and disk devices that connect through
> +	  your computer's parallel port. Most of them are actually IDE devices
> +	  using a parallel port IDE adapter. This option enables the PATA_PARPORT
> +	  subsystem which contains drivers for many of these external drives.
> +	  Read <file:Documentation/admin-guide/blockdev/paride.rst> for more information.
> +
> +	  If you have said Y to the "Parallel-port support" configuration
> +	  option, you may share a single port between your printer and other
> +	  parallel port devices. Answer Y to build PATA_PARPORT support into your
> +	  kernel, or M if you would like to build it as a loadable module. If
> +	  your parallel port support is in a loadable module, you must build
> +	  PATA_PARPORT as a module. If you built PATA_PARPORT support into your kernel,
> +	  you may still build the individual protocol modules as loadable
> +	  modules. If you build this support as a module, it will be called pata_parport.
> +
> +	  Unlike the old PARIDE, there are no high-level drivers needed.
> +	  The IDE devices behind parallel port adapters are handled by the
> +	  ATA layer.

Several lines are too long. Rewrap please.

> +
> +source "drivers/ata/pata_parport/Kconfig"
> +
>  comment "Generic fallback / legacy drivers"
>  
>  config PATA_ACPI
> diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
> index b8aebfb14e82..bd6b5fdc004e 100644
> --- a/drivers/ata/Makefile
> +++ b/drivers/ata/Makefile
> @@ -114,6 +114,8 @@ obj-$(CONFIG_PATA_SAMSUNG_CF)	+= pata_samsung_cf.o
>  
>  obj-$(CONFIG_PATA_PXA)		+= pata_pxa.o
>  
> +obj-$(CONFIG_PATA_PARPORT)	+= pata_parport/
> +
>  # Should be last but two libata driver
>  obj-$(CONFIG_PATA_ACPI)		+= pata_acpi.o
>  # Should be last but one libata driver
> diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
> new file mode 100644
> index 000000000000..56dc6b25d5fa
> --- /dev/null
> +++ b/drivers/ata/pata_parport/Kconfig
> @@ -0,0 +1,10 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# PATA_PARPORT configuration
> +#
> +# PATA_PARPORT doesn't need PARPORT, but if PARPORT is configured as a module,
> +# PATA_PARPORT must also be a module.

This does not parse. If PARPORT is not needed, then why does how PARPORT
is compiled matter ? Something is not consistent here.

> +# PATA_PARPORT only supports PC style parports. Tough for USB or other parports...

The second sentence does not parse. What are you trying to say ?

> +
> +comment "Parallel IDE protocol modules"
> +	depends on PATA_PARPORT
> diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
> new file mode 100644
> index 000000000000..b105e1cb8dc6
> --- /dev/null
> +++ b/drivers/ata/pata_parport/Makefile
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for Parallel port IDE device drivers.
> +#
> +# 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
> +# Rewritten to use lists instead of if-statements.
> +#
> +
> +obj-$(CONFIG_PATA_PARPORT)		+= pata_parport.o
> diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c
> new file mode 100644
> index 000000000000..7f814062cedd
> --- /dev/null
> +++ b/drivers/ata/pata_parport/pata_parport.c
> @@ -0,0 +1,809 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#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 = 1;

s/1/true to match the type.

> +module_param(probe, bool, 0644);
> +MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
> +
> +static bool verbose;
> +module_param(verbose, bool, 0644);
> +MODULE_PARM_DESC(verbose, "Enable verbose messages (0=off [default], 1=on)");

Is this really necessary ? pr_debug() and all ata_xxx_debug() functions
can be enabled dynamically.

> +
> +#define DISCONNECT_TIMEOUT	(HZ / 10)
> +
> +static void pi_connect(struct pi_adapter *pi)
> +{
> +	del_timer_sync(&pi->timer);
> +	if (pi->claimed)
> +		return;

Nit: I would reverse the condition to avoid the return. Or add a block
line to separate the following hunk. Better readability this way, I think.

> +	pi->claimed = 1;
> +	parport_claim_or_block(pi->pardev);
> +	pi->proto->connect(pi);
> +}
> +
> +static void pi_disconnect_later(struct pi_adapter *pi)
> +{
> +	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> +}

Is this really needed ? Not sure it helps in any way. At least the name
should be changed to reflect what this does. May be something like
pi_start_disconnect_timeout() ?

> +
> +static void pi_disconnect_timer(struct timer_list *t)
> +{
> +	struct pi_adapter *pi = from_timer(pi, t, timer);
> +
> +	if (!pi->claimed)
> +		return;

A blank line here would be nice for readability.

> +	pi->proto->disconnect(pi);
> +	parport_release(pi->pardev);
> +	pi->claimed = 0;
> +}
> +
> +/* functions taken from libata-sff.c and converted from direct port I/O */
> +static unsigned int pata_parport_devchk(struct ata_port *ap, unsigned int device)

Should this be a bool function ?

> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +	u8 nsect, lbal;
> +
> +	ap->ops->sff_dev_select(ap, device);
> +
> +	pi_connect(pi);
> +	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, 055);
> +	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);
> +	pi_disconnect_later(pi);
> +
> +	if ((nsect == 0x55) && (lbal == 0xaa))
> +		return 1;	/* we found a device */
> +
> +	return 0;		/* nothing found */
> +}
> +
> +static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
> +				      unsigned long deadline)
> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +
> +	pi_connect(pi);
> +	/* software reset.  causes dev0 to be selected */
> +	pi->proto->write_regr(pi, 1, 6, ap->ctl);
> +	udelay(20);	/* FIXME: flush */

Then please fix it.

> +	pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
> +	udelay(20);	/* FIXME: flush */

Here too.

> +	pi->proto->write_regr(pi, 1, 6, ap->ctl);
> +	ap->last_ctl = ap->ctl;
> +	pi_disconnect_later(pi);
> +
> +	/* wait the port to become ready */
> +	return ata_sff_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 slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
> +	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 (slave_possible && pata_parport_devchk(ap, 1))
> +		devmask |= (1 << 1);
> +
> +	/* select device 0 again */
> +	ap->ops->sff_dev_select(ap, 0);
> +
> +	/* issue bus reset */
> +	rc = pata_parport_bus_softreset(ap, devmask, deadline);
> +	/* if link is occupied, -ENODEV too is an error */

Please merge this comment with the one above. That will make the code
more readable (I know this is the style used by many drivers, but let's
not repeat the weird style :)).

> +	if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
> +		ata_link_err(link, "SRST failed (errno=%d)\n", rc);
> +		return rc;
> +	}
> +
> +	/* determine by signature whether we have ATA or ATAPI devices */
> +	classes[0] = ata_sff_dev_classify(&link->device[0],
> +					  devmask & (1 << 0), &err);
> +	if (slave_possible && err != 0x81)
> +		classes[1] = ata_sff_dev_classify(&link->device[1],
> +						  devmask & (1 << 1), &err);
> +
> +	return 0;
> +}
> +
> +static u8 pata_parport_check_status(struct ata_port *ap)
> +{
> +	u8 status;
> +	struct pi_adapter *pi = ap->host->private_data;
> +
> +	pi_connect(pi);
> +	status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
> +	pi_disconnect_later(pi);
> +
> +	return status;
> +}
> +
> +static u8 pata_parport_check_altstatus(struct ata_port *ap)
> +{
> +	u8 altstatus;
> +	struct pi_adapter *pi = ap->host->private_data;
> +
> +	pi_connect(pi);
> +	altstatus = pi->proto->read_regr(pi, 1, 6);
> +	pi_disconnect_later(pi);
> +
> +	return altstatus;
> +}
> +
> +static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +	u8 tmp;
> +
> +	pi_connect(pi);
> +	if (device == 0)
> +		tmp = ATA_DEVICE_OBS;
> +	else
> +		tmp = ATA_DEVICE_OBS | ATA_DEV1;
> +
> +	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
> +	pi_disconnect_later(pi);
> +	ata_sff_pause(ap);	/* needed; also flushes, for mmio */

The comment does not parse... Why is it needed ?
And pleasde move the comment above the call, with a blank line
separating it from the previous lines.

> +}
> +
> +static void pata_parport_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
> +
> +	pi_connect(pi);
> +	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 (is_addr && (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);
> +	}
> +
> +	if (is_addr) {

Move this if above the previous one. Doing so, you can remove the need
for is_addr var and simplify the previous if condition.

That said, many pata drivers check that flag but it is always set by
libata core. So checking it seems useless. Need to dig into this.


> +		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);
> +	pi_disconnect_later(pi);
> +}
> +
> +static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +
> +	pi_connect(pi);
> +	tf->command = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
> +	tf->feature = 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;
> +	}
> +	pi_disconnect_later(pi);
> +}
> +
> +static void pata_parport_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +
> +	pi_connect(pi);
> +	pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
> +	ata_sff_pause(ap);
> +	pi_disconnect_later(pi);
> +}
> +
> +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;
> +
> +	pi_connect(pi);
> +	if (rw == READ)
> +		pi->proto->read_block(pi, buf, buflen);
> +	else
> +		pi->proto->write_block(pi, buf, buflen);
> +	pi_disconnect_later(pi);
> +
> +	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; (ap->ops->sff_check_status(ap) & ATA_DRQ)
> +						&& count < 65536; count += 2) {
> +		pi_connect(pi);
> +		pi->proto->read_block(pi, junk, 2);
> +		pi_disconnect_later(pi);
> +	}
> +
> +	if (count)
> +		ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
> +}
> +
> +static void pata_parport_lost_interrupt(struct ata_port *ap)
> +{
> +	u8 status;
> +	struct ata_queued_cmd *qc;
> +
> +	/* Only one outstanding command per SFF channel */
> +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
> +	/* We cannot lose an interrupt on a non-existent or polled command */
> +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
> +		return;
> +	/* See if the controller thinks it is still busy - if so the command
> +	   isn't a lost IRQ but is still in progress */

Please use multi-line comment style:

/*
 * See if...
 * ...
 */

> +	status = pata_parport_check_altstatus(ap);
> +	if (status & ATA_BUSY)
> +		return;
> +
> +	/* There was a command running, we are no longer busy and we have
> +	   no interrupt. */

same

> +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
> +	/* Run the host interrupt logic as if the interrupt had not been lost */
> +	ata_sff_port_intr(ap, qc);
> +}
> +
> +static struct ata_port_operations pata_parport_port_ops = {
> +	.qc_prep		= ata_noop_qc_prep,
> +	.qc_issue		= ata_sff_qc_issue,
> +	.qc_fill_rtf		= ata_sff_qc_fill_rtf,
> +
> +	.freeze			= ata_sff_freeze,
> +	.thaw			= ata_sff_thaw,
> +	.prereset		= ata_sff_prereset,
> +	.softreset		= pata_parport_softreset,
> +	.postreset		= ata_sff_postreset,
> +	.error_handler		= ata_sff_error_handler,
> +	.sched_eh		= ata_std_sched_eh,
> +	.end_eh			= ata_std_end_eh,
> +
> +	.sff_dev_select		= pata_parport_dev_select,
> +	.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,
> +
> +	.lost_interrupt		= pata_parport_lost_interrupt,
> +};
> +
> +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,
> +};
> +
> +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, char *scratch)
> +{
> +	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);
> +
> +	if (verbose)
> +		dev_info(&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, char *scratch)
> +{
> +	int res;
> +
> +	parport_claim_or_block(pi->pardev);
> +	if (pi->proto->test_proto)
> +		res = pi->proto->test_proto(pi, scratch, verbose);
> +	else
> +		res = default_test_proto(pi, scratch);
> +	parport_release(pi->pardev);
> +
> +	return res;
> +}
> +
> +static int pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
> +{
> +	int best, range;
> +
> +	if (pi->mode != -1) {
> +		if (pi->mode >= max)
> +			return 0;
> +		range = 3;
> +		if (pi->mode >= pi->proto->epp_first)
> +			range = 8;
> +		if ((range == 8) && (pi->port % 8))

extra internal parenthesis not needed.

> +			return 0;
> +		return (!pi_test_proto(pi, scratch));
> +	}
> +	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))

same

> +			break;
> +		if (!pi_test_proto(pi, scratch))
> +			best = pi->mode;
> +	}
> +	pi->mode = best;
> +	return (best > -1);

same

> +}
> +
> +
> +static int pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)
> +{
> +	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;

Please add curly bracket for the else too.

> +
> +	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);
> +				if (pi_probe_mode(pi, max, scratch))
> +					return 1;
> +				return 0;
> +			}

Please add curly bracket for the for too. Technically not needed, but
since the "if" is over multiple lines, this will make the code more
readable (I had to recheck why the following parport_release() call was
indented like this...).

> +		parport_release(pi->pardev);
> +		return 0;
> +	}
> +
> +	if (!pi_probe_mode(pi, max, scratch))
> +		return 0;
> +	return 1;

return pi_probe_mode(pi, max, scratch); ?

> +}
> +
> +static void pata_parport_dev_release(struct device *dev)
> +{
> +	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
> +
> +	kfree(pi);
> +}
> +
> +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,
> +};
> +
> +struct pi_adapter *pi_init_one(struct parport *parport, struct pi_protocol *pr,
> +			       int mode, int unit, int delay)
> +{
> +	struct pardev_cb par_cb = { };
> +	char scratch[512];
> +	const struct ata_port_info *ppi[] = { &pata_parport_port_info };
> +	struct ata_host *host;
> +	struct pi_adapter *pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);

I would prefer hainving the pi = kzalloc() initialization right above
the "if (!pi)" below.

> +
> +	if (!pi)
> +		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 = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
> +	if (pi->dev.id < 0)
> +		return NULL; /* pata_parport_dev_release will do kfree(pi) */
> +	dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
> +	if (device_register(&pi->dev)) {
> +		put_device(&pi->dev);
> +		goto out_ida_free;
> +	}
> +
> +	pi->proto = pr;
> +
> +	/* still racy */

What is racy ?

> +	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, dev_name(&pi->dev),
> +						&par_cb, pi->dev.id);
> +	if (!pi->pardev)
> +		goto out_module_put;
> +
> +	if (!pi_probe_unit(pi, unit, scratch)) {
> +		dev_info(&pi->dev, "Adapter not found\n");
> +		goto out_unreg_parport;
> +	}
> +
> +	pi->proto->log_adapter(pi, scratch, verbose);
> +
> +	host = ata_host_alloc_pinfo(&pi->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);
> +
> +	timer_setup(&pi->timer, pi_disconnect_timer, 0);
> +
> +	if (ata_host_activate(host, 0, NULL, 0, &pi->proto->sht))
> +		goto out_unreg_parport;
> +
> +	return 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);
> +out_ida_free:
> +	ida_free(&pata_parport_bus_dev_ids, pi->dev.id);
> +	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 */

Please move the comment inside the if and use curly brackets for the if
since its statement is multi-line.

> +		idr_for_each_entry(&parport_list, parport, port_num)
> +			pi_init_one(parport, pr, -1, 0, -1);
> +	mutex_unlock(&pi_mutex);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(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)

Add curly brackets.

> +		if (pr_iter == pr)
> +			break;
> +	idr_remove(&protocols, id);
> +	mutex_unlock(&pi_mutex);
> +	pr_info("pata_parport: protocol %s removed\n", pr->name);

I do not think this message is needed.

> +	driver_unregister(&pr->driver);
> +}
> +EXPORT_SYMBOL(pata_parport_unregister_driver);
> +
> +static ssize_t new_device_store(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"))

Curly brackets please.
> +			port_wanted = -1;
> +		else {
> +			pr_err("invalid port name %s\n", port);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	drv = driver_find(protocol, &pata_parport_bus_type);
> +	if (!drv) {
> +		if (!strcmp(protocol, "auto"))
> +			pr_wanted = NULL;

same.

> +		else {
> +			pr_err("protocol %s not found\n", protocol);
> +			return -EINVAL;
> +		}
> +	} else
> +		pr_wanted = container_of(drv, struct pi_protocol, driver);

Here too.

> +
> +	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)

Add curly brackets to this one too.

> +				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);
> +	del_timer_sync(&pi->timer);
> +	pi_release(pi);
> +	device_unregister(dev);
> +	ida_free(&pata_parport_bus_dev_ids, dev->id);
> +	/* pata_parport_dev_release will do kfree(pi) */
> +}
> +
> +static ssize_t delete_device_store(struct bus_type *bus, const char *buf, size_t count)
> +{
> +	struct device *dev;
> +	char device_name[32];
> +	int fields;
> +
> +	fields = sscanf(buf, "%31s", device_name);
> +	if (fields < 1)
> +		return -EINVAL;
> +
> +	mutex_lock(&pi_mutex);
> +	dev = bus_find_device_by_name(bus, NULL, device_name);
> +	if (!dev) {
> +		mutex_unlock(&pi_mutex);
> +		return -ENODEV;
> +	}
> +
> +	pi_remove_one(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, 0, -1);

Curly brackets and comment under the if.

> +	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);
> +		return error;

Bug. This should not be here.

> +		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 000000000000..c4201b809b20
> --- /dev/null
> +++ b/drivers/ata/pata_parport/pata_parport.h
> @@ -0,0 +1,110 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + *	paride.h	(c) 1997-8  Grant R. Guenther <grant@torque.net>
> + *				    Under the terms of the GPL.
> + *
> + * This file defines the interface for adapter chip drivers.

Which adapter ? "Parallel port IDE adapter chip", no ?

> + */
> +
> +#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 */
> +	int claimed;			/* parport has already been claimed */

Make this a bool ?

> +	struct timer_list timer;	/* disconnect timer */
> +};
> +
> +/* 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) & 0xff)
> +#define w1(byte)		out_p(1, byte)
> +#define r1()			(in_p(1) & 0xff)
> +#define w2(byte)		out_p(2, byte)
> +#define r2()			(in_p(2) & 0xff)
> +#define w3(byte)		out_p(3, byte)
> +#define w4(byte)		out_p(4, byte)
> +#define r4()			(in_p(4) & 0xff)
> +#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) & 0xffff)
> +#define r4l()			(delay_p, inl(pi->port + 4) & 0xffffffff)

Why not align this with the others ?

> +
> +static inline u16 pi_swab16(char *b, int k)
> +{
> +	union { u16 u; char t[2]; } r;
> +
> +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
> +	return r.u;
> +}
> +
> +static inline u32 pi_swab32(char *b, int k)
> +{
> +	union { u32 u; char f[4]; } r;
> +
> +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
> +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
> +	return r.u;
> +}
> +
> +struct pi_protocol {
> +	char name[8];		/* name for this protocol */
> +
> +	int max_mode;		/* max mode number */
> +	int epp_first;		/* modes >= this use 8 ports */
> +
> +	int default_delay;	/* delay parameter if not specified */
> +	int max_units;		/* max chained units probed for */

Except maybe epp_first, I find the comments for these fields useless: no
information added.

> +
> +	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, char *scratch, int verbose);
> +	void (*log_adapter)(struct pi_adapter *pi, char *scratch, int verbose);
> +
> +	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
> +
> +extern int pata_parport_register_driver(struct pi_protocol *pr);
> +extern void pata_parport_unregister_driver(struct pi_protocol *pr);

No need for extern.

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

Make this a static inline ?

-- 
Damien Le Moal
Western Digital Research

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 01/16] pata_parport: add core driver (PARIDE replacement)
  2022-03-07  5:52   ` Damien Le Moal
@ 2022-03-07  8:55     ` Ondrej Zary
  2022-03-07  9:42       ` Damien Le Moal
  0 siblings, 1 reply; 27+ messages in thread
From: Ondrej Zary @ 2022-03-07  8:55 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On Monday 07 March 2022, Damien Le Moal wrote:
> On 3/6/22 05:13, Ondrej Zary wrote:
> > Add pata_parport (PARIDE replacement) core libata driver.
> > 
> > Signed-off-by: Ondrej Zary <linux@zary.sk>
> > ---
> >  drivers/ata/Kconfig                     |  25 +
> >  drivers/ata/Makefile                    |   2 +
> >  drivers/ata/pata_parport/Kconfig        |  10 +
> >  drivers/ata/pata_parport/Makefile       |   9 +
> >  drivers/ata/pata_parport/pata_parport.c | 809 ++++++++++++++++++++++++
> >  drivers/ata/pata_parport/pata_parport.h | 110 ++++
> >  6 files changed, 965 insertions(+)
> >  create mode 100644 drivers/ata/pata_parport/Kconfig
> >  create mode 100644 drivers/ata/pata_parport/Makefile
> >  create mode 100644 drivers/ata/pata_parport/pata_parport.c
> >  create mode 100644 drivers/ata/pata_parport/pata_parport.h
> > 
> > diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
> > index cb54631fd950..de4548471398 100644
> > --- a/drivers/ata/Kconfig
> > +++ b/drivers/ata/Kconfig
> > @@ -1161,6 +1161,31 @@ config PATA_WINBOND_VLB
> >  	  Support for the Winbond W83759A controller on Vesa Local Bus
> >  	  systems.
> >  
> > +config PATA_PARPORT
> > +	tristate "Parallel port IDE device support (PARIDE replacement)"
> 
> I would remove the "(PARIDE replacement)" from the title. If really
> needed, mention it in the description that this support used to be
> implemented by PARIDE. But I do not think that is necessary. The last
> paragraph of the description implies that.
> 
> > +	depends on PARPORT_PC
> > +	help
> > +	  There are many external CD-ROM and disk devices that connect through
> > +	  your computer's parallel port. Most of them are actually IDE devices
> > +	  using a parallel port IDE adapter. This option enables the PATA_PARPORT
> > +	  subsystem which contains drivers for many of these external drives.
> > +	  Read <file:Documentation/admin-guide/blockdev/paride.rst> for more information.
> > +
> > +	  If you have said Y to the "Parallel-port support" configuration
> > +	  option, you may share a single port between your printer and other
> > +	  parallel port devices. Answer Y to build PATA_PARPORT support into your
> > +	  kernel, or M if you would like to build it as a loadable module. If
> > +	  your parallel port support is in a loadable module, you must build
> > +	  PATA_PARPORT as a module. If you built PATA_PARPORT support into your kernel,
> > +	  you may still build the individual protocol modules as loadable
> > +	  modules. If you build this support as a module, it will be called pata_parport.
> > +
> > +	  Unlike the old PARIDE, there are no high-level drivers needed.
> > +	  The IDE devices behind parallel port adapters are handled by the
> > +	  ATA layer.
> 
> Several lines are too long. Rewrap please.
> 
> > +
> > +source "drivers/ata/pata_parport/Kconfig"
> > +
> >  comment "Generic fallback / legacy drivers"
> >  
> >  config PATA_ACPI
> > diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
> > index b8aebfb14e82..bd6b5fdc004e 100644
> > --- a/drivers/ata/Makefile
> > +++ b/drivers/ata/Makefile
> > @@ -114,6 +114,8 @@ obj-$(CONFIG_PATA_SAMSUNG_CF)	+= pata_samsung_cf.o
> >  
> >  obj-$(CONFIG_PATA_PXA)		+= pata_pxa.o
> >  
> > +obj-$(CONFIG_PATA_PARPORT)	+= pata_parport/
> > +
> >  # Should be last but two libata driver
> >  obj-$(CONFIG_PATA_ACPI)		+= pata_acpi.o
> >  # Should be last but one libata driver
> > diff --git a/drivers/ata/pata_parport/Kconfig b/drivers/ata/pata_parport/Kconfig
> > new file mode 100644
> > index 000000000000..56dc6b25d5fa
> > --- /dev/null
> > +++ b/drivers/ata/pata_parport/Kconfig
> > @@ -0,0 +1,10 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +#
> > +# PATA_PARPORT configuration
> > +#
> > +# PATA_PARPORT doesn't need PARPORT, but if PARPORT is configured as a module,
> > +# PATA_PARPORT must also be a module.
> 
> This does not parse. If PARPORT is not needed, then why does how PARPORT
> is compiled matter ? Something is not consistent here.
> 
> > +# PATA_PARPORT only supports PC style parports. Tough for USB or other parports...
> 
> The second sentence does not parse. What are you trying to say ?

It's copied from paride.

> > +
> > +comment "Parallel IDE protocol modules"
> > +	depends on PATA_PARPORT
> > diff --git a/drivers/ata/pata_parport/Makefile b/drivers/ata/pata_parport/Makefile
> > new file mode 100644
> > index 000000000000..b105e1cb8dc6
> > --- /dev/null
> > +++ b/drivers/ata/pata_parport/Makefile
> > @@ -0,0 +1,9 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +#
> > +# Makefile for Parallel port IDE device drivers.
> > +#
> > +# 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
> > +# Rewritten to use lists instead of if-statements.
> > +#
> > +
> > +obj-$(CONFIG_PATA_PARPORT)		+= pata_parport.o
> > diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c
> > new file mode 100644
> > index 000000000000..7f814062cedd
> > --- /dev/null
> > +++ b/drivers/ata/pata_parport/pata_parport.c
> > @@ -0,0 +1,809 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +#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 = 1;
> 
> s/1/true to match the type.
> 
> > +module_param(probe, bool, 0644);
> > +MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
> > +
> > +static bool verbose;
> > +module_param(verbose, bool, 0644);
> > +MODULE_PARM_DESC(verbose, "Enable verbose messages (0=off [default], 1=on)");
> 
> Is this really necessary ? pr_debug() and all ata_xxx_debug() functions
> can be enabled dynamically.

The verbose messages are for adapter chip detection. That happens before the
ata host is registered.

> > +
> > +#define DISCONNECT_TIMEOUT	(HZ / 10)
> > +
> > +static void pi_connect(struct pi_adapter *pi)
> > +{
> > +	del_timer_sync(&pi->timer);
> > +	if (pi->claimed)
> > +		return;
> 
> Nit: I would reverse the condition to avoid the return. Or add a block
> line to separate the following hunk. Better readability this way, I think.
> 
> > +	pi->claimed = 1;
> > +	parport_claim_or_block(pi->pardev);
> > +	pi->proto->connect(pi);
> > +}
> > +
> > +static void pi_disconnect_later(struct pi_adapter *pi)
> > +{
> > +	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> > +}
> 
> Is this really needed ? Not sure it helps in any way. At least the name
> should be changed to reflect what this does. May be something like
> pi_start_disconnect_timeout() ?

Disconnect needs to happen after end of an operation so the parport is freed
for device(s) (like a printer) connected to pass-through port of a paride
device. There could be even multiple paride devices chained on one parport.

Disconnecting immediately at the end of each function does not work
(probably breaks the protocol?)

The old high-level drivers (pd, pf...) did connect and disconnect manually
on each operation (command or data transfer) but it does not seem to be
possible with libata.

> > +
> > +static void pi_disconnect_timer(struct timer_list *t)
> > +{
> > +	struct pi_adapter *pi = from_timer(pi, t, timer);
> > +
> > +	if (!pi->claimed)
> > +		return;
> 
> A blank line here would be nice for readability.
> 
> > +	pi->proto->disconnect(pi);
> > +	parport_release(pi->pardev);
> > +	pi->claimed = 0;
> > +}
> > +
> > +/* functions taken from libata-sff.c and converted from direct port I/O */
> > +static unsigned int pata_parport_devchk(struct ata_port *ap, unsigned int device)
> 
> Should this be a bool function ?

I don't know. It's copied from libata-sff.c
 
> > +{
> > +	struct pi_adapter *pi = ap->host->private_data;
> > +	u8 nsect, lbal;
> > +
> > +	ap->ops->sff_dev_select(ap, device);
> > +
> > +	pi_connect(pi);
> > +	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, 055);
> > +	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);
> > +	pi_disconnect_later(pi);
> > +
> > +	if ((nsect == 0x55) && (lbal == 0xaa))
> > +		return 1;	/* we found a device */
> > +
> > +	return 0;		/* nothing found */
> > +}
> > +
> > +static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
> > +				      unsigned long deadline)
> > +{
> > +	struct pi_adapter *pi = ap->host->private_data;
> > +
> > +	pi_connect(pi);
> > +	/* software reset.  causes dev0 to be selected */
> > +	pi->proto->write_regr(pi, 1, 6, ap->ctl);
> > +	udelay(20);	/* FIXME: flush */
> 
> Then please fix it.

I don't know what that means. It's copied from libata-sff.c

> > +	pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
> > +	udelay(20);	/* FIXME: flush */
> 
> Here too.
> 
> > +	pi->proto->write_regr(pi, 1, 6, ap->ctl);
> > +	ap->last_ctl = ap->ctl;
> > +	pi_disconnect_later(pi);
> > +
> > +	/* wait the port to become ready */
> > +	return ata_sff_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 slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
> > +	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 (slave_possible && pata_parport_devchk(ap, 1))
> > +		devmask |= (1 << 1);
> > +
> > +	/* select device 0 again */
> > +	ap->ops->sff_dev_select(ap, 0);
> > +
> > +	/* issue bus reset */
> > +	rc = pata_parport_bus_softreset(ap, devmask, deadline);
> > +	/* if link is occupied, -ENODEV too is an error */
> 
> Please merge this comment with the one above. That will make the code
> more readable (I know this is the style used by many drivers, but let's
> not repeat the weird style :)).
> 
> > +	if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
> > +		ata_link_err(link, "SRST failed (errno=%d)\n", rc);
> > +		return rc;
> > +	}
> > +
> > +	/* determine by signature whether we have ATA or ATAPI devices */
> > +	classes[0] = ata_sff_dev_classify(&link->device[0],
> > +					  devmask & (1 << 0), &err);
> > +	if (slave_possible && err != 0x81)
> > +		classes[1] = ata_sff_dev_classify(&link->device[1],
> > +						  devmask & (1 << 1), &err);
> > +
> > +	return 0;
> > +}
> > +
> > +static u8 pata_parport_check_status(struct ata_port *ap)
> > +{
> > +	u8 status;
> > +	struct pi_adapter *pi = ap->host->private_data;
> > +
> > +	pi_connect(pi);
> > +	status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
> > +	pi_disconnect_later(pi);
> > +
> > +	return status;
> > +}
> > +
> > +static u8 pata_parport_check_altstatus(struct ata_port *ap)
> > +{
> > +	u8 altstatus;
> > +	struct pi_adapter *pi = ap->host->private_data;
> > +
> > +	pi_connect(pi);
> > +	altstatus = pi->proto->read_regr(pi, 1, 6);
> > +	pi_disconnect_later(pi);
> > +
> > +	return altstatus;
> > +}
> > +
> > +static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
> > +{
> > +	struct pi_adapter *pi = ap->host->private_data;
> > +	u8 tmp;
> > +
> > +	pi_connect(pi);
> > +	if (device == 0)
> > +		tmp = ATA_DEVICE_OBS;
> > +	else
> > +		tmp = ATA_DEVICE_OBS | ATA_DEV1;
> > +
> > +	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
> > +	pi_disconnect_later(pi);
> > +	ata_sff_pause(ap);	/* needed; also flushes, for mmio */
> 
> The comment does not parse... Why is it needed ?
> And pleasde move the comment above the call, with a blank line
> separating it from the previous lines.

I don't know. It's copied from libata-sff.c

> > +}
> > +
> > +static void pata_parport_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
> > +{
> > +	struct pi_adapter *pi = ap->host->private_data;
> > +	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
> > +
> > +	pi_connect(pi);
> > +	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 (is_addr && (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);
> > +	}
> > +
> > +	if (is_addr) {
> 
> Move this if above the previous one. Doing so, you can remove the need
> for is_addr var and simplify the previous if condition.
> 
> That said, many pata drivers check that flag but it is always set by
> libata core. So checking it seems useless. Need to dig into this.

It's copied from libata-sff.c which is almost libata core.

> > +		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);
> > +	pi_disconnect_later(pi);
> > +}
> > +
> > +static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
> > +{
> > +	struct pi_adapter *pi = ap->host->private_data;
> > +
> > +	pi_connect(pi);
> > +	tf->command = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
> > +	tf->feature = 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;
> > +	}
> > +	pi_disconnect_later(pi);
> > +}
> > +
> > +static void pata_parport_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
> > +{
> > +	struct pi_adapter *pi = ap->host->private_data;
> > +
> > +	pi_connect(pi);
> > +	pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
> > +	ata_sff_pause(ap);
> > +	pi_disconnect_later(pi);
> > +}
> > +
> > +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;
> > +
> > +	pi_connect(pi);
> > +	if (rw == READ)
> > +		pi->proto->read_block(pi, buf, buflen);
> > +	else
> > +		pi->proto->write_block(pi, buf, buflen);
> > +	pi_disconnect_later(pi);
> > +
> > +	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; (ap->ops->sff_check_status(ap) & ATA_DRQ)
> > +						&& count < 65536; count += 2) {
> > +		pi_connect(pi);
> > +		pi->proto->read_block(pi, junk, 2);
> > +		pi_disconnect_later(pi);
> > +	}
> > +
> > +	if (count)
> > +		ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
> > +}
> > +
> > +static void pata_parport_lost_interrupt(struct ata_port *ap)
> > +{
> > +	u8 status;
> > +	struct ata_queued_cmd *qc;
> > +
> > +	/* Only one outstanding command per SFF channel */
> > +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
> > +	/* We cannot lose an interrupt on a non-existent or polled command */
> > +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
> > +		return;
> > +	/* See if the controller thinks it is still busy - if so the command
> > +	   isn't a lost IRQ but is still in progress */
> 
> Please use multi-line comment style:
> 
> /*
>  * See if...
>  * ...
>  */
> 
> > +	status = pata_parport_check_altstatus(ap);
> > +	if (status & ATA_BUSY)
> > +		return;
> > +
> > +	/* There was a command running, we are no longer busy and we have
> > +	   no interrupt. */
> 
> same
> 
> > +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
> > +	/* Run the host interrupt logic as if the interrupt had not been lost */
> > +	ata_sff_port_intr(ap, qc);
> > +}
> > +
> > +static struct ata_port_operations pata_parport_port_ops = {
> > +	.qc_prep		= ata_noop_qc_prep,
> > +	.qc_issue		= ata_sff_qc_issue,
> > +	.qc_fill_rtf		= ata_sff_qc_fill_rtf,
> > +
> > +	.freeze			= ata_sff_freeze,
> > +	.thaw			= ata_sff_thaw,
> > +	.prereset		= ata_sff_prereset,
> > +	.softreset		= pata_parport_softreset,
> > +	.postreset		= ata_sff_postreset,
> > +	.error_handler		= ata_sff_error_handler,
> > +	.sched_eh		= ata_std_sched_eh,
> > +	.end_eh			= ata_std_end_eh,
> > +
> > +	.sff_dev_select		= pata_parport_dev_select,
> > +	.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,
> > +
> > +	.lost_interrupt		= pata_parport_lost_interrupt,
> > +};
> > +
> > +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,
> > +};
> > +
> > +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, char *scratch)
> > +{
> > +	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);
> > +
> > +	if (verbose)
> > +		dev_info(&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, char *scratch)
> > +{
> > +	int res;
> > +
> > +	parport_claim_or_block(pi->pardev);
> > +	if (pi->proto->test_proto)
> > +		res = pi->proto->test_proto(pi, scratch, verbose);
> > +	else
> > +		res = default_test_proto(pi, scratch);
> > +	parport_release(pi->pardev);
> > +
> > +	return res;
> > +}
> > +
> > +static int pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
> > +{
> > +	int best, range;
> > +
> > +	if (pi->mode != -1) {
> > +		if (pi->mode >= max)
> > +			return 0;
> > +		range = 3;
> > +		if (pi->mode >= pi->proto->epp_first)
> > +			range = 8;
> > +		if ((range == 8) && (pi->port % 8))
> 
> extra internal parenthesis not needed.
> 
> > +			return 0;
> > +		return (!pi_test_proto(pi, scratch));
> > +	}
> > +	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))
> 
> same
> 
> > +			break;
> > +		if (!pi_test_proto(pi, scratch))
> > +			best = pi->mode;
> > +	}
> > +	pi->mode = best;
> > +	return (best > -1);
> 
> same
> 
> > +}
> > +
> > +
> > +static int pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)
> > +{
> > +	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;
> 
> Please add curly bracket for the else too.
> 
> > +
> > +	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);
> > +				if (pi_probe_mode(pi, max, scratch))
> > +					return 1;
> > +				return 0;
> > +			}
> 
> Please add curly bracket for the for too. Technically not needed, but
> since the "if" is over multiple lines, this will make the code more
> readable (I had to recheck why the following parport_release() call was
> indented like this...).
> 
> > +		parport_release(pi->pardev);
> > +		return 0;
> > +	}
> > +
> > +	if (!pi_probe_mode(pi, max, scratch))
> > +		return 0;
> > +	return 1;
> 
> return pi_probe_mode(pi, max, scratch); ?
> 
> > +}
> > +
> > +static void pata_parport_dev_release(struct device *dev)
> > +{
> > +	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
> > +
> > +	kfree(pi);
> > +}
> > +
> > +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,
> > +};
> > +
> > +struct pi_adapter *pi_init_one(struct parport *parport, struct pi_protocol *pr,
> > +			       int mode, int unit, int delay)
> > +{
> > +	struct pardev_cb par_cb = { };
> > +	char scratch[512];
> > +	const struct ata_port_info *ppi[] = { &pata_parport_port_info };
> > +	struct ata_host *host;
> > +	struct pi_adapter *pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
> 
> I would prefer hainving the pi = kzalloc() initialization right above
> the "if (!pi)" below.
> 
> > +
> > +	if (!pi)
> > +		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 = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
> > +	if (pi->dev.id < 0)
> > +		return NULL; /* pata_parport_dev_release will do kfree(pi) */
> > +	dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
> > +	if (device_register(&pi->dev)) {
> > +		put_device(&pi->dev);
> > +		goto out_ida_free;
> > +	}
> > +
> > +	pi->proto = pr;
> > +
> > +	/* still racy */
> 
> What is racy ?

I don't know - the comment came from paride. I'll probably just delete it.

> > +	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, dev_name(&pi->dev),
> > +						&par_cb, pi->dev.id);
> > +	if (!pi->pardev)
> > +		goto out_module_put;
> > +
> > +	if (!pi_probe_unit(pi, unit, scratch)) {
> > +		dev_info(&pi->dev, "Adapter not found\n");
> > +		goto out_unreg_parport;
> > +	}
> > +
> > +	pi->proto->log_adapter(pi, scratch, verbose);
> > +
> > +	host = ata_host_alloc_pinfo(&pi->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);
> > +
> > +	timer_setup(&pi->timer, pi_disconnect_timer, 0);
> > +
> > +	if (ata_host_activate(host, 0, NULL, 0, &pi->proto->sht))
> > +		goto out_unreg_parport;
> > +
> > +	return 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);
> > +out_ida_free:
> > +	ida_free(&pata_parport_bus_dev_ids, pi->dev.id);
> > +	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 */
> 
> Please move the comment inside the if and use curly brackets for the if
> since its statement is multi-line.
> 
> > +		idr_for_each_entry(&parport_list, parport, port_num)
> > +			pi_init_one(parport, pr, -1, 0, -1);
> > +	mutex_unlock(&pi_mutex);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(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)
> 
> Add curly brackets.
> 
> > +		if (pr_iter == pr)
> > +			break;
> > +	idr_remove(&protocols, id);
> > +	mutex_unlock(&pi_mutex);
> > +	pr_info("pata_parport: protocol %s removed\n", pr->name);
> 
> I do not think this message is needed.
> 
> > +	driver_unregister(&pr->driver);
> > +}
> > +EXPORT_SYMBOL(pata_parport_unregister_driver);
> > +
> > +static ssize_t new_device_store(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"))
> 
> Curly brackets please.
> > +			port_wanted = -1;
> > +		else {
> > +			pr_err("invalid port name %s\n", port);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	drv = driver_find(protocol, &pata_parport_bus_type);
> > +	if (!drv) {
> > +		if (!strcmp(protocol, "auto"))
> > +			pr_wanted = NULL;
> 
> same.
> 
> > +		else {
> > +			pr_err("protocol %s not found\n", protocol);
> > +			return -EINVAL;
> > +		}
> > +	} else
> > +		pr_wanted = container_of(drv, struct pi_protocol, driver);
> 
> Here too.
> 
> > +
> > +	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)
> 
> Add curly brackets to this one too.
> 
> > +				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);
> > +	del_timer_sync(&pi->timer);
> > +	pi_release(pi);
> > +	device_unregister(dev);
> > +	ida_free(&pata_parport_bus_dev_ids, dev->id);
> > +	/* pata_parport_dev_release will do kfree(pi) */
> > +}
> > +
> > +static ssize_t delete_device_store(struct bus_type *bus, const char *buf, size_t count)
> > +{
> > +	struct device *dev;
> > +	char device_name[32];
> > +	int fields;
> > +
> > +	fields = sscanf(buf, "%31s", device_name);
> > +	if (fields < 1)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&pi_mutex);
> > +	dev = bus_find_device_by_name(bus, NULL, device_name);
> > +	if (!dev) {
> > +		mutex_unlock(&pi_mutex);
> > +		return -ENODEV;
> > +	}
> > +
> > +	pi_remove_one(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, 0, -1);
> 
> Curly brackets and comment under the if.
> 
> > +	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);
> > +		return error;
> 
> Bug. This should not be here.
> 
> > +		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 000000000000..c4201b809b20
> > --- /dev/null
> > +++ b/drivers/ata/pata_parport/pata_parport.h
> > @@ -0,0 +1,110 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + *	paride.h	(c) 1997-8  Grant R. Guenther <grant@torque.net>
> > + *				    Under the terms of the GPL.
> > + *
> > + * This file defines the interface for adapter chip drivers.
> 
> Which adapter ? "Parallel port IDE adapter chip", no ?
> 
> > + */
> > +
> > +#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 */
> > +	int claimed;			/* parport has already been claimed */
> 
> Make this a bool ?
> 
> > +	struct timer_list timer;	/* disconnect timer */
> > +};
> > +
> > +/* 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) & 0xff)
> > +#define w1(byte)		out_p(1, byte)
> > +#define r1()			(in_p(1) & 0xff)
> > +#define w2(byte)		out_p(2, byte)
> > +#define r2()			(in_p(2) & 0xff)
> > +#define w3(byte)		out_p(3, byte)
> > +#define w4(byte)		out_p(4, byte)
> > +#define r4()			(in_p(4) & 0xff)
> > +#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) & 0xffff)
> > +#define r4l()			(delay_p, inl(pi->port + 4) & 0xffffffff)
> 
> Why not align this with the others ?

It's aligned properly in the file, only looks bad in a diff.

> > +
> > +static inline u16 pi_swab16(char *b, int k)
> > +{
> > +	union { u16 u; char t[2]; } r;
> > +
> > +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
> > +	return r.u;
> > +}
> > +
> > +static inline u32 pi_swab32(char *b, int k)
> > +{
> > +	union { u32 u; char f[4]; } r;
> > +
> > +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
> > +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
> > +	return r.u;
> > +}
> > +
> > +struct pi_protocol {
> > +	char name[8];		/* name for this protocol */
> > +
> > +	int max_mode;		/* max mode number */
> > +	int epp_first;		/* modes >= this use 8 ports */
> > +
> > +	int default_delay;	/* delay parameter if not specified */
> > +	int max_units;		/* max chained units probed for */
> 
> Except maybe epp_first, I find the comments for these fields useless: no
> information added.
> 
> > +
> > +	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, char *scratch, int verbose);
> > +	void (*log_adapter)(struct pi_adapter *pi, char *scratch, int verbose);
> > +
> > +	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
> > +
> > +extern int pata_parport_register_driver(struct pi_protocol *pr);
> > +extern void pata_parport_unregister_driver(struct pi_protocol *pr);
> 
> No need for extern.
> 
> > +
> > +/**
> > + * 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)
> 
> Make this a static inline ?

It's a macro that expands to other macro (defined in include/linux/device/driver.h)
that expands to __init and __exit functions, module_init() and
module_exit() so we can use:

module_pata_parport_driver(on26);

instead of:

static int __init on26_init(void)
{
	return pata_parport_register_driver(&on26);
}

static void __exit on26_exit(void)
{
	pata_parport_unregister_driver(&on26);
}

module_init(on26_init)
module_exit(on26_exit)

-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 01/16] pata_parport: add core driver (PARIDE replacement)
  2022-03-07  8:55     ` Ondrej Zary
@ 2022-03-07  9:42       ` Damien Le Moal
  0 siblings, 0 replies; 27+ messages in thread
From: Damien Le Moal @ 2022-03-07  9:42 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On 3/7/22 17:55, Ondrej Zary wrote:
>>> +module_param(probe, bool, 0644);
>>> +MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
>>> +
>>> +static bool verbose;
>>> +module_param(verbose, bool, 0644);
>>> +MODULE_PARM_DESC(verbose, "Enable verbose messages (0=off [default], 1=on)");
>>
>> Is this really necessary ? pr_debug() and all ata_xxx_debug() functions
>> can be enabled dynamically.
> 
> The verbose messages are for adapter chip detection. That happens before the
> ata host is registered.

Sure. But you can still use pr_debug(). So instead of

if (verbose)
	dev_info();

simply use:

dev_dbg()

And these calls can be enabled with the standard dynamic debug control.

> 
>>> +
>>> +#define DISCONNECT_TIMEOUT	(HZ / 10)
>>> +
>>> +static void pi_connect(struct pi_adapter *pi)
>>> +{
>>> +	del_timer_sync(&pi->timer);
>>> +	if (pi->claimed)
>>> +		return;
>>
>> Nit: I would reverse the condition to avoid the return. Or add a block
>> line to separate the following hunk. Better readability this way, I think.
>>
>>> +	pi->claimed = 1;
>>> +	parport_claim_or_block(pi->pardev);
>>> +	pi->proto->connect(pi);
>>> +}
>>> +
>>> +static void pi_disconnect_later(struct pi_adapter *pi)
>>> +{
>>> +	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
>>> +}
>>
>> Is this really needed ? Not sure it helps in any way. At least the name
>> should be changed to reflect what this does. May be something like
>> pi_start_disconnect_timeout() ?
> 
> Disconnect needs to happen after end of an operation so the parport is freed
> for device(s) (like a printer) connected to pass-through port of a paride
> device. There could be even multiple paride devices chained on one parport.
> 
> Disconnecting immediately at the end of each function does not work
> (probably breaks the protocol?)
> 
> The old high-level drivers (pd, pf...) did connect and disconnect manually
> on each operation (command or data transfer) but it does not seem to be
> possible with libata.

I am not questioning how you handle the disconnect. I was only
commenting that the inline function may not be that usefull and you
could simply directly use the mod_timer() calls directly.

or

rename pi_disconnect_later() to something that better explains what the
function does.

> 
>>> +
>>> +static void pi_disconnect_timer(struct timer_list *t)
>>> +{
>>> +	struct pi_adapter *pi = from_timer(pi, t, timer);
>>> +
>>> +	if (!pi->claimed)
>>> +		return;
>>
>> A blank line here would be nice for readability.
>>
>>> +	pi->proto->disconnect(pi);
>>> +	parport_release(pi->pardev);
>>> +	pi->claimed = 0;
>>> +}
>>> +
>>> +/* functions taken from libata-sff.c and converted from direct port I/O */
>>> +static unsigned int pata_parport_devchk(struct ata_port *ap, unsigned int device)
>>
>> Should this be a bool function ?
> 
> I don't know. It's copied from libata-sff.c
>  
>>> +{
>>> +	struct pi_adapter *pi = ap->host->private_data;
>>> +	u8 nsect, lbal;
>>> +
>>> +	ap->ops->sff_dev_select(ap, device);
>>> +
>>> +	pi_connect(pi);
>>> +	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, 055);
>>> +	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);
>>> +	pi_disconnect_later(pi);
>>> +
>>> +	if ((nsect == 0x55) && (lbal == 0xaa))
>>> +		return 1;	/* we found a device */
>>> +
>>> +	return 0;		/* nothing found */
>>> +}
>>> +
>>> +static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
>>> +				      unsigned long deadline)
>>> +{
>>> +	struct pi_adapter *pi = ap->host->private_data;
>>> +
>>> +	pi_connect(pi);
>>> +	/* software reset.  causes dev0 to be selected */
>>> +	pi->proto->write_regr(pi, 1, 6, ap->ctl);
>>> +	udelay(20);	/* FIXME: flush */
>>
>> Then please fix it.
> 
> I don't know what that means. It's copied from libata-sff.c

OK. Will have a look there too.

> 
>>> +	pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
>>> +	udelay(20);	/* FIXME: flush */
>>
>> Here too.
>>
>>> +	pi->proto->write_regr(pi, 1, 6, ap->ctl);
>>> +	ap->last_ctl = ap->ctl;
>>> +	pi_disconnect_later(pi);
>>> +
>>> +	/* wait the port to become ready */
>>> +	return ata_sff_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 slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
>>> +	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 (slave_possible && pata_parport_devchk(ap, 1))
>>> +		devmask |= (1 << 1);
>>> +
>>> +	/* select device 0 again */
>>> +	ap->ops->sff_dev_select(ap, 0);
>>> +
>>> +	/* issue bus reset */
>>> +	rc = pata_parport_bus_softreset(ap, devmask, deadline);
>>> +	/* if link is occupied, -ENODEV too is an error */
>>
>> Please merge this comment with the one above. That will make the code
>> more readable (I know this is the style used by many drivers, but let's
>> not repeat the weird style :)).
>>
>>> +	if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
>>> +		ata_link_err(link, "SRST failed (errno=%d)\n", rc);
>>> +		return rc;
>>> +	}
>>> +
>>> +	/* determine by signature whether we have ATA or ATAPI devices */
>>> +	classes[0] = ata_sff_dev_classify(&link->device[0],
>>> +					  devmask & (1 << 0), &err);
>>> +	if (slave_possible && err != 0x81)
>>> +		classes[1] = ata_sff_dev_classify(&link->device[1],
>>> +						  devmask & (1 << 1), &err);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static u8 pata_parport_check_status(struct ata_port *ap)
>>> +{
>>> +	u8 status;
>>> +	struct pi_adapter *pi = ap->host->private_data;
>>> +
>>> +	pi_connect(pi);
>>> +	status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
>>> +	pi_disconnect_later(pi);
>>> +
>>> +	return status;
>>> +}
>>> +
>>> +static u8 pata_parport_check_altstatus(struct ata_port *ap)
>>> +{
>>> +	u8 altstatus;
>>> +	struct pi_adapter *pi = ap->host->private_data;
>>> +
>>> +	pi_connect(pi);
>>> +	altstatus = pi->proto->read_regr(pi, 1, 6);
>>> +	pi_disconnect_later(pi);
>>> +
>>> +	return altstatus;
>>> +}
>>> +
>>> +static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
>>> +{
>>> +	struct pi_adapter *pi = ap->host->private_data;
>>> +	u8 tmp;
>>> +
>>> +	pi_connect(pi);
>>> +	if (device == 0)
>>> +		tmp = ATA_DEVICE_OBS;
>>> +	else
>>> +		tmp = ATA_DEVICE_OBS | ATA_DEV1;
>>> +
>>> +	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
>>> +	pi_disconnect_later(pi);
>>> +	ata_sff_pause(ap);	/* needed; also flushes, for mmio */
>>
>> The comment does not parse... Why is it needed ?
>> And pleasde move the comment above the call, with a blank line
>> separating it from the previous lines.
> 
> I don't know. It's copied from libata-sff.c

Drop the comment then.

> 
>>> +}
>>> +
>>> +static void pata_parport_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
>>> +{
>>> +	struct pi_adapter *pi = ap->host->private_data;
>>> +	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
>>> +
>>> +	pi_connect(pi);
>>> +	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 (is_addr && (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);
>>> +	}
>>> +
>>> +	if (is_addr) {
>>
>> Move this if above the previous one. Doing so, you can remove the need
>> for is_addr var and simplify the previous if condition.
>>
>> That said, many pata drivers check that flag but it is always set by
>> libata core. So checking it seems useless. Need to dig into this.
> 
> It's copied from libata-sff.c which is almost libata core.

Yes. Saw the same pattern in different drivers. Need to figure out what
is going on here. That flag really seem totally useless. Will check.
Keep the flag use for now, but at least rewrite as suggested to avoid
the need for that is_addr variable.

>>> +	struct timer_list timer;	/* disconnect timer */
>>> +};
>>> +
>>> +/* 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) & 0xff)
>>> +#define w1(byte)		out_p(1, byte)
>>> +#define r1()			(in_p(1) & 0xff)
>>> +#define w2(byte)		out_p(2, byte)
>>> +#define r2()			(in_p(2) & 0xff)
>>> +#define w3(byte)		out_p(3, byte)
>>> +#define w4(byte)		out_p(4, byte)
>>> +#define r4()			(in_p(4) & 0xff)
>>> +#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) & 0xffff)
>>> +#define r4l()			(delay_p, inl(pi->port + 4) & 0xffffffff)
>>
>> Why not align this with the others ?
> 
> It's aligned properly in the file, only looks bad in a diff.
> 

OK.

>>> +
>>> +/**
>>> + * 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)
>>
>> Make this a static inline ?
> 
> It's a macro that expands to other macro (defined in include/linux/device/driver.h)
> that expands to __init and __exit functions, module_init() and
> module_exit() so we can use:
> 
> module_pata_parport_driver(on26);
> 
> instead of:
> 
> static int __init on26_init(void)
> {
> 	return pata_parport_register_driver(&on26);
> }
> 
> static void __exit on26_exit(void)
> {
> 	pata_parport_unregister_driver(&on26);
> }
> 
> module_init(on26_init)
> module_exit(on26_exit)

OK.


-- 
Damien Le Moal
Western Digital Research

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 01/16] pata_parport: add core driver (PARIDE replacement)
  2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
  2022-03-07  5:52   ` Damien Le Moal
@ 2022-03-07 19:49   ` Sergey Shtylyov
  2022-03-08  6:59   ` Christoph Hellwig
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 27+ messages in thread
From: Sergey Shtylyov @ 2022-03-07 19:49 UTC (permalink / raw)
  To: Ondrej Zary, Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Hello!

On 3/5/22 11:13 PM, Ondrej Zary wrote:

> Add pata_parport (PARIDE replacement) core libata driver.
> 
> Signed-off-by: Ondrej Zary <linux@zary.sk>
> ---
>  drivers/ata/Kconfig                     |  25 +
>  drivers/ata/Makefile                    |   2 +
>  drivers/ata/pata_parport/Kconfig        |  10 +
>  drivers/ata/pata_parport/Makefile       |   9 +
>  drivers/ata/pata_parport/pata_parport.c | 809 ++++++++++++++++++++++++
>  drivers/ata/pata_parport/pata_parport.h | 110 ++++

   I'd like to suggest to just name the new subdirectory 'parport'.
And it looks like I'll need to update my MAINTAINBERS entry to include this driver... :-)

[...]
> diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c
> new file mode 100644
> index 000000000000..7f814062cedd
> --- /dev/null
> +++ b/drivers/ata/pata_parport/pata_parport.c
> @@ -0,0 +1,809 @@
[...]
> +static bool probe = 1;

   s/1/true/.

[...]
> +/* functions taken from libata-sff.c and converted from direct port I/O */
> +static unsigned int pata_parport_devchk(struct ata_port *ap, unsigned int device)

   Should return bool now, see e.g..:

https://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git/commit/?h=for-next&id=1336aa88d8553292604878c53538297fbc65bf0a

> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +	u8 nsect, lbal;
> +
> +	ap->ops->sff_dev_select(ap, device);

   Could call your sff-dev_select() methid directly here?

[...]
> +static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
> +				      unsigned long deadline)
> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +
> +	pi_connect(pi);
> +	/* software reset.  causes dev0 to be selected */
> +	pi->proto->write_regr(pi, 1, 6, ap->ctl);
> +	udelay(20);	/* FIXME: flush */

   I don't think this FIXME applies to your driver...

[...]
> +static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,
> +				  unsigned long deadline)
> +{
> +	struct ata_port *ap = link->ap;
> +	unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;

   Isn't this flag always set in your driver?

> +	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 (slave_possible && pata_parport_devchk(ap, 1))
> +		devmask |= (1 << 1);
> +
> +	/* select device 0 again */
> +	ap->ops->sff_dev_select(ap, 0);

   Again, could you call this directly?

> +
> +	/* issue bus reset */
> +	rc = pata_parport_bus_softreset(ap, devmask, deadline);
> +	/* if link is occupied, -ENODEV too is an error */
> +	if (rc && (rc != -ENODEV || sata_scr_valid(link))) {

   It's a PATA driver, why call sata_scr_valid() at all?

[...]
> +static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +	u8 tmp;
> +
> +	pi_connect(pi);

   Why not call it after this *if*?

> +	if (device == 0)
> +		tmp = ATA_DEVICE_OBS;
> +	else
> +		tmp = ATA_DEVICE_OBS | ATA_DEV1;
> +
> +	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
> +	pi_disconnect_later(pi);
> +	ata_sff_pause(ap);	/* needed; also flushes, for mmio */

   Does this comment make sense in your driver?

[...]
> +static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
> +{
> +	struct pi_adapter *pi = ap->host->private_data;
> +
> +	pi_connect(pi);
> +	tf->command = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);

   Use tf->status please, see:

https://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git/commit/?h=for-next&id=efcef265fd83d9a68a68926abecb3e1dd3e260a8

> +	tf->feature = pi->proto->read_regr(pi, 0, ATA_REG_ERR);

   Use tf->error as well please.

[...]
> +static void pata_parport_lost_interrupt(struct ata_port *ap)
> +{
> +	u8 status;
> +	struct ata_queued_cmd *qc;
> +
> +	/* Only one outstanding command per SFF channel */
> +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
> +	/* We cannot lose an interrupt on a non-existent or polled command */
> +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
> +		return;
> +	/* See if the controller thinks it is still busy - if so the command
> +	   isn't a lost IRQ but is still in progress */
> +	status = pata_parport_check_altstatus(ap);
> +	if (status & ATA_BUSY)
> +		return;
> +
> +	/* There was a command running, we are no longer busy and we have
> +	   no interrupt. */
> +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
> +	/* Run the host interrupt logic as if the interrupt had not been lost */
> +	ata_sff_port_intr(ap, qc);
> +}

   Hm, it looks like ata_sff_lost_interrupt() could be used instead...

> +
> +static struct ata_port_operations pata_parport_port_ops = {

   Maybe inherit from ata_sff_port_ops? 

[...]
> +static int default_test_proto(struct pi_adapter *pi, char *scratch)
> +{
> +	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);
> +
> +	if (verbose)
> +		dev_info(&pi->dev, "%s: port 0x%x, mode  %d, test=(%d,%d)\n",

   Whyu 2 spaces after "mode"?

> +		       pi->proto->name, pi->port,
> +		       pi->mode, e[0], e[1]);
> +
> +	return (e[0] && e[1]);	/* not here if both > 0 */

   No need for parens.

> +}

   This function kinda duplicates pata_parport_devchk()? :-)

[...]
> +static int pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
> +{
> +	int best, range;
> +
> +	if (pi->mode != -1) {
> +		if (pi->mode >= max)
> +			return 0;
> +		range = 3;
> +		if (pi->mode >= pi->proto->epp_first)
> +			range = 8;
> +		if ((range == 8) && (pi->port % 8))

   No need for inner parens...

> +			return 0;
> +		return (!pi_test_proto(pi, scratch));

   No need for outer parens, this time... :-)

> +	}
> +	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))

   No need for inner parens...

> +			break;
> +		if (!pi_test_proto(pi, scratch))
> +			best = pi->mode;
> +	}
> +	pi->mode = best;
> +	return (best > -1);

   No need for parens...

> +}
> +
> +

   Isn't one empty line enough?

> +static int pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)

   Looks like it's worth making this function return bool?

[...]
> +static ssize_t new_device_store(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"))
> +			port_wanted = -1;
> +		else {

   Need {} on both branches.

> +			pr_err("invalid port name %s\n", port);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	drv = driver_find(protocol, &pata_parport_bus_type);
> +	if (!drv) {
> +		if (!strcmp(protocol, "auto"))
> +			pr_wanted = NULL;
> +		else {

   Same here.

> +			pr_err("protocol %s not found\n", protocol);
> +			return -EINVAL;
> +		}
> +	} else

   And here.

> +		pr_wanted = container_of(drv, struct pi_protocol, driver);
[...]
> +static ssize_t delete_device_store(struct bus_type *bus, const char *buf, size_t count)
> +{
> +	struct device *dev;
> +	char device_name[32];
> +	int fields;
> +
> +	fields = sscanf(buf, "%31s", device_name);
> +	if (fields < 1)

   Strange variable name where you expect only one field... And you don't even
use it after this check, so hardly needed at all...

[...]
> diff --git a/drivers/ata/pata_parport/pata_parport.h b/drivers/ata/pata_parport/pata_parport.h
> new file mode 100644
> index 000000000000..c4201b809b20
> --- /dev/null
> +++ b/drivers/ata/pata_parport/pata_parport.h
> @@ -0,0 +1,110 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + *	paride.h	(c) 1997-8  Grant R. Guenther <grant@torque.net>

   Doesn't match the file name anymore...

> + *				    Under the terms of the GPL.
> + *
> + * This file defines the interface for adapter chip drivers.
> + */
> +
> +#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 */
> +	int claimed;			/* parport has already been claimed */

   Use bool instead?

> +	struct timer_list timer;	/* disconnect timer */
> +};
> +
> +/* 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))

   Hm, why not pass pi as an extra parameter?

> +
> +#define w0(byte)		out_p(0, byte)
> +#define r0()			(in_p(0) & 0xff)

   Why mask the result of inb()?

> +#define w1(byte)		out_p(1, byte)
> +#define r1()			(in_p(1) & 0xff)
> +#define w2(byte)		out_p(2, byte)
> +#define r2()			(in_p(2) & 0xff)
> +#define w3(byte)		out_p(3, byte)
> +#define w4(byte)		out_p(4, byte)
> +#define r4()			(in_p(4) & 0xff)
> +#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) & 0xffff)
> +#define r4l()			(delay_p, inl(pi->port + 4) & 0xffffffff)

   Again, why mask these?

> +
> +static inline u16 pi_swab16(char *b, int k)
> +{
> +	union { u16 u; char t[2]; } r;
> +
> +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
> +	return r.u;

   Hm, swab16() instead?

> +}
> +
> +static inline u32 pi_swab32(char *b, int k)
> +{
> +	union { u32 u; char f[4]; } r;
> +
> +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
> +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
> +	return r.u;

   And swab32() here instead?

> +}
[...]

MNR, Sergey

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC PATCH] pata_parport: paride replacement
  2022-03-06 10:36   ` Ondrej Zary
@ 2022-03-08  6:54     ` Christoph Hellwig
  0 siblings, 0 replies; 27+ messages in thread
From: Christoph Hellwig @ 2022-03-08  6:54 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Christoph Hellwig, Damien Le Moal, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Sun, Mar 06, 2022 at 11:36:36AM +0100, Ondrej Zary wrote:
> On Sunday 06 March 2022 09:58:25 Christoph Hellwig wrote:
> > Hi Ondrej,
> > 
> > I just took a quick glance and it seems like the actual protocol
> > modules still are basically almost exactly the same ones as the
> > paride ones.  Is there a way to just keep the existing modules?
> > 
> > The only big thing I noticed is the host template, but at least
> > for the transitional periode we could probably allocate that
> > dynamically in the core.  I think would reduce the amount of code
> > churn nicely and make review much easier.
> 
> Yes, only small changes in the protocol modules regarding (un)registration.
> 
> Getting the original modules work with pata_parport (like in 1st preview) required some hacks that break paride (disabling EXPORT_SYMBOLs in paride).
> 
> Maybe the protocol modules can be moved (git mv) from paride and then patched? A copy would be better but there's no "git cp".

Hmm, how would be break the old PARIDE code?  You'd need the new libata
support exlusive to the old PARIDE code so that only one of them can
export the registration symbols at a time.  The git-mv can happen
once the old paride code is removed after a release or two.

> 
> -- 
> Ondrej Zary
---end quoted text---

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 01/16] pata_parport: add core driver (PARIDE replacement)
  2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
  2022-03-07  5:52   ` Damien Le Moal
  2022-03-07 19:49   ` Sergey Shtylyov
@ 2022-03-08  6:59   ` Christoph Hellwig
  2022-03-08 17:52   ` kernel test robot
  2022-03-09  8:44   ` kernel test robot
  4 siblings, 0 replies; 27+ messages in thread
From: Christoph Hellwig @ 2022-03-08  6:59 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Damien Le Moal, Christoph Hellwig, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

> +
> +static void pata_parport_dev_release(struct device *dev)
> +{
> +	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
> +
> +	kfree(pi);
> +}
>
> +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,
> +};

Hmm, wouldn't it make sense to let the libata device hang off the device
in struct pardevice?

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 01/16] pata_parport: add core driver (PARIDE replacement)
  2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
                     ` (2 preceding siblings ...)
  2022-03-08  6:59   ` Christoph Hellwig
@ 2022-03-08 17:52   ` kernel test robot
  2022-03-09  8:44   ` kernel test robot
  4 siblings, 0 replies; 27+ messages in thread
From: kernel test robot @ 2022-03-08 17:52 UTC (permalink / raw)
  To: Ondrej Zary, Damien Le Moal
  Cc: kbuild-all, Christoph Hellwig, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

Hi Ondrej,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on hch-configfs/for-next]
[also build test WARNING on linux/master linus/master v5.17-rc7 next-20220308]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Ondrej-Zary/pata_parport-add-core-driver-PARIDE-replacement/20220307-142912
base:   git://git.infradead.org/users/hch/configfs.git for-next
config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20220309/202203090134.f9NhVHMa-lkp@intel.com/config)
compiler: sh4-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/05539e2a04721c2710e7d1f6ae49926474bdf918
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Ondrej-Zary/pata_parport-add-core-driver-PARIDE-replacement/20220307-142912
        git checkout 05539e2a04721c2710e7d1f6ae49926474bdf918
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=sh SHELL=/bin/bash drivers/ata/pata_parport/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/ata/pata_parport/pata_parport.c:341:6: warning: no previous prototype for 'pi_release' [-Wmissing-prototypes]
     341 | void pi_release(struct pi_adapter *pi)
         |      ^~~~~~~~~~
>> drivers/ata/pata_parport/pata_parport.c:462:6: warning: no previous prototype for 'pata_parport_bus_release' [-Wmissing-prototypes]
     462 | void pata_parport_bus_release(struct device *dev)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/ata/pata_parport/pata_parport.c:476:20: warning: no previous prototype for 'pi_init_one' [-Wmissing-prototypes]
     476 | struct pi_adapter *pi_init_one(struct parport *parport, struct pi_protocol *pr,
         |                    ^~~~~~~~~~~


vim +/pi_release +341 drivers/ata/pata_parport/pata_parport.c

   340	
 > 341	void pi_release(struct pi_adapter *pi)
   342	{
   343		parport_unregister_device(pi->pardev);
   344		if (pi->proto->release_proto)
   345			pi->proto->release_proto(pi);
   346		module_put(pi->proto->owner);
   347	}
   348	
   349	static int default_test_proto(struct pi_adapter *pi, char *scratch)
   350	{
   351		int j, k;
   352		int e[2] = { 0, 0 };
   353	
   354		pi->proto->connect(pi);
   355	
   356		for (j = 0; j < 2; j++) {
   357			pi->proto->write_regr(pi, 0, 6, 0xa0 + j * 0x10);
   358			for (k = 0; k < 256; k++) {
   359				pi->proto->write_regr(pi, 0, 2, k ^ 0xaa);
   360				pi->proto->write_regr(pi, 0, 3, k ^ 0x55);
   361				if (pi->proto->read_regr(pi, 0, 2) != (k ^ 0xaa))
   362					e[j]++;
   363			}
   364		}
   365		pi->proto->disconnect(pi);
   366	
   367		if (verbose)
   368			dev_info(&pi->dev, "%s: port 0x%x, mode  %d, test=(%d,%d)\n",
   369			       pi->proto->name, pi->port,
   370			       pi->mode, e[0], e[1]);
   371	
   372		return (e[0] && e[1]);	/* not here if both > 0 */
   373	}
   374	
   375	static int pi_test_proto(struct pi_adapter *pi, char *scratch)
   376	{
   377		int res;
   378	
   379		parport_claim_or_block(pi->pardev);
   380		if (pi->proto->test_proto)
   381			res = pi->proto->test_proto(pi, scratch, verbose);
   382		else
   383			res = default_test_proto(pi, scratch);
   384		parport_release(pi->pardev);
   385	
   386		return res;
   387	}
   388	
   389	static int pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
   390	{
   391		int best, range;
   392	
   393		if (pi->mode != -1) {
   394			if (pi->mode >= max)
   395				return 0;
   396			range = 3;
   397			if (pi->mode >= pi->proto->epp_first)
   398				range = 8;
   399			if ((range == 8) && (pi->port % 8))
   400				return 0;
   401			return (!pi_test_proto(pi, scratch));
   402		}
   403		best = -1;
   404		for (pi->mode = 0; pi->mode < max; pi->mode++) {
   405			range = 3;
   406			if (pi->mode >= pi->proto->epp_first)
   407				range = 8;
   408			if ((range == 8) && (pi->port % 8))
   409				break;
   410			if (!pi_test_proto(pi, scratch))
   411				best = pi->mode;
   412		}
   413		pi->mode = best;
   414		return (best > -1);
   415	}
   416	
   417	
   418	static int pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)
   419	{
   420		int max, s, e;
   421	
   422		s = unit;
   423		e = s + 1;
   424	
   425		if (s == -1) {
   426			s = 0;
   427			e = pi->proto->max_units;
   428		}
   429	
   430		if (pi->proto->test_port) {
   431			parport_claim_or_block(pi->pardev);
   432			max = pi->proto->test_port(pi);
   433			parport_release(pi->pardev);
   434		} else
   435			max = pi->proto->max_mode;
   436	
   437		if (pi->proto->probe_unit) {
   438			parport_claim_or_block(pi->pardev);
   439			for (pi->unit = s; pi->unit < e; pi->unit++)
   440				if (pi->proto->probe_unit(pi)) {
   441					parport_release(pi->pardev);
   442					if (pi_probe_mode(pi, max, scratch))
   443						return 1;
   444					return 0;
   445				}
   446			parport_release(pi->pardev);
   447			return 0;
   448		}
   449	
   450		if (!pi_probe_mode(pi, max, scratch))
   451			return 0;
   452		return 1;
   453	}
   454	
   455	static void pata_parport_dev_release(struct device *dev)
   456	{
   457		struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
   458	
   459		kfree(pi);
   460	}
   461	
 > 462	void pata_parport_bus_release(struct device *dev)
   463	{
   464		/* nothing to do here but required to avoid warning on device removal */
   465	}
   466	
   467	static struct bus_type pata_parport_bus_type = {
   468		.name = DRV_NAME,
   469	};
   470	
   471	static struct device pata_parport_bus = {
   472		.init_name = DRV_NAME,
   473		.release = pata_parport_bus_release,
   474	};
   475	
 > 476	struct pi_adapter *pi_init_one(struct parport *parport, struct pi_protocol *pr,
   477				       int mode, int unit, int delay)
   478	{
   479		struct pardev_cb par_cb = { };
   480		char scratch[512];
   481		const struct ata_port_info *ppi[] = { &pata_parport_port_info };
   482		struct ata_host *host;
   483		struct pi_adapter *pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
   484	
   485		if (!pi)
   486			return NULL;
   487	
   488		/* set up pi->dev before pi_probe_unit() so it can use dev_printk() */
   489		pi->dev.parent = &pata_parport_bus;
   490		pi->dev.bus = &pata_parport_bus_type;
   491		pi->dev.driver = &pr->driver;
   492		pi->dev.release = pata_parport_dev_release;
   493		pi->dev.id = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
   494		if (pi->dev.id < 0)
   495			return NULL; /* pata_parport_dev_release will do kfree(pi) */
   496		dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
   497		if (device_register(&pi->dev)) {
   498			put_device(&pi->dev);
   499			goto out_ida_free;
   500		}
   501	
   502		pi->proto = pr;
   503	
   504		/* still racy */
   505		if (!try_module_get(pi->proto->owner))
   506			goto out_unreg_dev;
   507		if (pi->proto->init_proto && pi->proto->init_proto(pi) < 0)
   508			goto out_module_put;
   509	
   510		pi->delay = (delay == -1) ? pi->proto->default_delay : delay;
   511		pi->mode = mode;
   512		pi->port = parport->base;
   513	
   514		par_cb.private = pi;
   515		pi->pardev = parport_register_dev_model(parport, dev_name(&pi->dev),
   516							&par_cb, pi->dev.id);
   517		if (!pi->pardev)
   518			goto out_module_put;
   519	
   520		if (!pi_probe_unit(pi, unit, scratch)) {
   521			dev_info(&pi->dev, "Adapter not found\n");
   522			goto out_unreg_parport;
   523		}
   524	
   525		pi->proto->log_adapter(pi, scratch, verbose);
   526	
   527		host = ata_host_alloc_pinfo(&pi->dev, ppi, 1);
   528		if (!host)
   529			goto out_unreg_parport;
   530		dev_set_drvdata(&pi->dev, host);
   531		host->private_data = pi;
   532	
   533		ata_port_desc(host->ports[0], "port %s", pi->pardev->port->name);
   534		ata_port_desc(host->ports[0], "protocol %s", pi->proto->name);
   535	
   536		timer_setup(&pi->timer, pi_disconnect_timer, 0);
   537	
   538		if (ata_host_activate(host, 0, NULL, 0, &pi->proto->sht))
   539			goto out_unreg_parport;
   540	
   541		return pi;
   542	
   543	out_unreg_parport:
   544		parport_unregister_device(pi->pardev);
   545		if (pi->proto->release_proto)
   546			pi->proto->release_proto(pi);
   547	out_module_put:
   548		module_put(pi->proto->owner);
   549	out_unreg_dev:
   550		device_unregister(&pi->dev);
   551	out_ida_free:
   552		ida_free(&pata_parport_bus_dev_ids, pi->dev.id);
   553		return NULL;
   554	}
   555	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 01/16] pata_parport: add core driver (PARIDE replacement)
  2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
                     ` (3 preceding siblings ...)
  2022-03-08 17:52   ` kernel test robot
@ 2022-03-09  8:44   ` kernel test robot
  4 siblings, 0 replies; 27+ messages in thread
From: kernel test robot @ 2022-03-09  8:44 UTC (permalink / raw)
  To: Ondrej Zary, Damien Le Moal
  Cc: kbuild-all, Christoph Hellwig, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

Hi Ondrej,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on hch-configfs/for-next]
[also build test ERROR on linux/master linus/master v5.17-rc7 next-20220308]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Ondrej-Zary/pata_parport-add-core-driver-PARIDE-replacement/20220307-142912
base:   git://git.infradead.org/users/hch/configfs.git for-next
config: ia64-allyesconfig (https://download.01.org/0day-ci/archive/20220309/202203091613.dsoQnVk9-lkp@intel.com/config)
compiler: ia64-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/05539e2a04721c2710e7d1f6ae49926474bdf918
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Ondrej-Zary/pata_parport-add-core-driver-PARIDE-replacement/20220307-142912
        git checkout 05539e2a04721c2710e7d1f6ae49926474bdf918
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=ia64 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   ia64-linux-ld: drivers/block/paride/paride.o: in function `pi_release':
>> (.text+0x600): multiple definition of `pi_release'; drivers/ata/pata_parport/pata_parport.o:(.text+0x36c0): first defined here

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2022-03-09  8:45 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-05 20:13 [RFC PATCH] pata_parport: paride replacement Ondrej Zary
2022-03-05 20:13 ` [PATCH 01/16] pata_parport: add core driver (PARIDE replacement) Ondrej Zary
2022-03-07  5:52   ` Damien Le Moal
2022-03-07  8:55     ` Ondrej Zary
2022-03-07  9:42       ` Damien Le Moal
2022-03-07 19:49   ` Sergey Shtylyov
2022-03-08  6:59   ` Christoph Hellwig
2022-03-08 17:52   ` kernel test robot
2022-03-09  8:44   ` kernel test robot
2022-03-05 20:13 ` [PATCH 02/16] pata_parport: add aten protocol driver Ondrej Zary
2022-03-05 20:13 ` [PATCH 03/16] pata_parport: add bpck " Ondrej Zary
2022-03-05 20:13 ` [PATCH 04/16] pata_parport: add bpck6 " Ondrej Zary
2022-03-05 20:14 ` [PATCH 05/16] pata_parport: add comm " Ondrej Zary
2022-03-05 20:14 ` [PATCH 06/16] pata_parport: add dstr " Ondrej Zary
2022-03-05 20:14 ` [PATCH 07/16] pata_parport: add fit2 " Ondrej Zary
2022-03-05 20:14 ` [PATCH 08/16] pata_parport: add fit3 " Ondrej Zary
2022-03-05 20:14 ` [PATCH 09/16] pata_parport: add epat " Ondrej Zary
2022-03-05 20:14 ` [PATCH 10/16] pata_parport: add epia " Ondrej Zary
2022-03-05 20:14 ` [PATCH 11/16] pata_parport: add friq " Ondrej Zary
2022-03-05 20:14 ` [PATCH 12/16] pata_parport: add frpw " Ondrej Zary
2022-03-05 20:14 ` [PATCH 13/16] pata_parport: add kbic " Ondrej Zary
2022-03-05 20:14 ` [PATCH 14/16] pata_parport: add ktti " Ondrej Zary
2022-03-05 20:14 ` [PATCH 15/16] pata_parport: add on20 " Ondrej Zary
2022-03-05 20:14 ` [PATCH 16/16] pata_parport: add on26 " Ondrej Zary
2022-03-06  8:58 ` [RFC PATCH] pata_parport: paride replacement Christoph Hellwig
2022-03-06 10:36   ` Ondrej Zary
2022-03-08  6:54     ` Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).