// 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 0 x00
#define ACCESS_PORT 0 x40
#define ACCESS_READ 0 x00
#define ACCESS_WRITE 0 x20
/* 60772 Command Prefix */
#define CMD_PREFIX_SET 0 xe0 // Special command that modifies next command's operation
#define CMD_PREFIX_RESET 0 xc0 // Resets current cmd modifier reg bits
#define PREFIX_IO16 0 x01 // perform 16-bit wide I/O
#define PREFIX_FASTWR 0 x04 // enable PPC mode fast-write
#define PREFIX_BLK 0 x08 // enable block transfer mode
/* 60772 Registers */
#define REG_STATUS 0 x00 // status register
#define STATUS_IRQA 0 x01 // Peripheral IRQA line
#define STATUS_EEPROM_DO 0 x40 // Serial EEPROM data bit
#define REG_VERSION 0 x01 // PPC version register (read)
#define REG_HWCFG 0 x02 // Hardware Config register
#define REG_RAMSIZE 0 x03 // Size of RAM Buffer
#define RAMSIZE_128K 0 x02
#define REG_EEPROM 0 x06 // EEPROM control register
#define EEPROM_SK 0 x01 // eeprom SK bit
#define EEPROM_DI 0 x02 // eeprom DI bit
#define EEPROM_CS 0 x04 // eeprom CS bit
#define EEPROM_EN 0 x08 // eeprom output enable
#define REG_BLKSIZE 0 x08 // Block transfer len (24 bit)
/* flags */
#define fifo_wait 0 x10
/* 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 & 0 x80) >> 1 ) | ((data & 0 x38) >> 3 );
parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
PARPORT_CONTROL_STROBE);
data |= parport_read_status(pi->pardev->port) & 0 xB8;
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 & 0 x80) >> 1 ) | ((d & 0 x38) >> 3 );
parport_frob_control(pi->pardev->port,
PARPORT_CONTROL_STROBE,
PARPORT_CONTROL_STROBE);
d |= parport_read_status(pi->pardev->port) & 0 xB8;
*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) & 0 x5F;
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] & 0 x0C;
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 & 0 x08) << 4 ) | ((i & 0 x07) << 3 );
k = parport_read_status(pi->pardev->port) & 0 xB8;
if (j != k)
goto fail;
parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, 0 );
k = (parport_read_status(pi->pardev->port) & 0 xB8) ^ 0 xB8;
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) & 0 x3F) == 0 x0C)
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 & 0 x03) << 6 ));
}
static void bpck6_connect(struct pi_adapter *pi)
{
dev_dbg(&pi->dev, "connect\n" );
bpck6_open(pi);
bpck6_wr_extout(pi, 0 x3);
}
static void bpck6_disconnect(struct pi_adapter *pi)
{
dev_dbg(&pi->dev, "disconnect\n" );
bpck6_wr_extout(pi, 0 x0);
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);
Messung V0.5 in Prozent C=95 H=89 G=91
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland