Resguardo inicial

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