[Linux-parport] [PATCH 04/16] pata_parport: add bpck6 protocol driver

Ondrej Zary linux at zary.sk
Sat Mar 5 12:13:59 PST 2022


Add MicroSolutions backpack (Series 6) protocol driver.

Signed-off-by: Ondrej Zary <linux at 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 at micro-solutions.com)
+ *		    Clive Turvey (linux-dev at 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




More information about the Linux-parport mailing list