162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * (c) 2001 Micro Solutions Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * backpack.c is a low-level protocol driver for the Micro Solutions
662306a36Sopenharmony_ci * "BACKPACK" parallel port IDE adapter (works on Series 6 drives).
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Written by: Ken Hahn (linux-dev@micro-solutions.com)
962306a36Sopenharmony_ci *             Clive Turvey (linux-dev@micro-solutions.com)
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci#include <linux/parport.h>
1762306a36Sopenharmony_ci#include "pata_parport.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* 60772 Commands */
2062306a36Sopenharmony_ci#define ACCESS_REG		0x00
2162306a36Sopenharmony_ci#define ACCESS_PORT		0x40
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define ACCESS_READ		0x00
2462306a36Sopenharmony_ci#define ACCESS_WRITE		0x20
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* 60772 Command Prefix */
2762306a36Sopenharmony_ci#define CMD_PREFIX_SET		0xe0	// Special command that modifies next command's operation
2862306a36Sopenharmony_ci#define CMD_PREFIX_RESET	0xc0	// Resets current cmd modifier reg bits
2962306a36Sopenharmony_ci #define PREFIX_IO16		0x01	// perform 16-bit wide I/O
3062306a36Sopenharmony_ci #define PREFIX_FASTWR		0x04	// enable PPC mode fast-write
3162306a36Sopenharmony_ci #define PREFIX_BLK		0x08	// enable block transfer mode
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* 60772 Registers */
3462306a36Sopenharmony_ci#define REG_STATUS		0x00	// status register
3562306a36Sopenharmony_ci #define STATUS_IRQA		0x01	// Peripheral IRQA line
3662306a36Sopenharmony_ci #define STATUS_EEPROM_DO	0x40	// Serial EEPROM data bit
3762306a36Sopenharmony_ci#define REG_VERSION		0x01	// PPC version register (read)
3862306a36Sopenharmony_ci#define REG_HWCFG		0x02	// Hardware Config register
3962306a36Sopenharmony_ci#define REG_RAMSIZE		0x03	// Size of RAM Buffer
4062306a36Sopenharmony_ci #define RAMSIZE_128K		0x02
4162306a36Sopenharmony_ci#define REG_EEPROM		0x06	// EEPROM control register
4262306a36Sopenharmony_ci #define EEPROM_SK		0x01	// eeprom SK bit
4362306a36Sopenharmony_ci #define EEPROM_DI		0x02	// eeprom DI bit
4462306a36Sopenharmony_ci #define EEPROM_CS		0x04	// eeprom CS bit
4562306a36Sopenharmony_ci #define EEPROM_EN		0x08	// eeprom output enable
4662306a36Sopenharmony_ci#define REG_BLKSIZE		0x08	// Block transfer len (24 bit)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* flags */
4962306a36Sopenharmony_ci#define fifo_wait		0x10
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES */
5262306a36Sopenharmony_ci#define PPCMODE_UNI_SW		0
5362306a36Sopenharmony_ci#define PPCMODE_UNI_FW		1
5462306a36Sopenharmony_ci#define PPCMODE_BI_SW		2
5562306a36Sopenharmony_ci#define PPCMODE_BI_FW		3
5662306a36Sopenharmony_ci#define PPCMODE_EPP_BYTE	4
5762306a36Sopenharmony_ci#define PPCMODE_EPP_WORD	5
5862306a36Sopenharmony_ci#define PPCMODE_EPP_DWORD	6
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int mode_map[] = { PPCMODE_UNI_FW, PPCMODE_BI_FW, PPCMODE_EPP_BYTE,
6162306a36Sopenharmony_ci			  PPCMODE_EPP_WORD, PPCMODE_EPP_DWORD };
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void bpck6_send_cmd(struct pi_adapter *pi, u8 cmd)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	switch (mode_map[pi->mode]) {
6662306a36Sopenharmony_ci	case PPCMODE_UNI_SW:
6762306a36Sopenharmony_ci	case PPCMODE_UNI_FW:
6862306a36Sopenharmony_ci	case PPCMODE_BI_SW:
6962306a36Sopenharmony_ci	case PPCMODE_BI_FW:
7062306a36Sopenharmony_ci		parport_write_data(pi->pardev->port, cmd);
7162306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_AUTOFD);
7262306a36Sopenharmony_ci		break;
7362306a36Sopenharmony_ci	case PPCMODE_EPP_BYTE:
7462306a36Sopenharmony_ci	case PPCMODE_EPP_WORD:
7562306a36Sopenharmony_ci	case PPCMODE_EPP_DWORD:
7662306a36Sopenharmony_ci		pi->pardev->port->ops->epp_write_addr(pi->pardev->port, &cmd, 1, 0);
7762306a36Sopenharmony_ci		break;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic u8 bpck6_rd_data_byte(struct pi_adapter *pi)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	u8 data = 0;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	switch (mode_map[pi->mode]) {
8662306a36Sopenharmony_ci	case PPCMODE_UNI_SW:
8762306a36Sopenharmony_ci	case PPCMODE_UNI_FW:
8862306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
8962306a36Sopenharmony_ci							PARPORT_CONTROL_INIT);
9062306a36Sopenharmony_ci		data = parport_read_status(pi->pardev->port);
9162306a36Sopenharmony_ci		data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);
9262306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
9362306a36Sopenharmony_ci							PARPORT_CONTROL_STROBE);
9462306a36Sopenharmony_ci		data |= parport_read_status(pi->pardev->port) & 0xB8;
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	case PPCMODE_BI_SW:
9762306a36Sopenharmony_ci	case PPCMODE_BI_FW:
9862306a36Sopenharmony_ci		parport_data_reverse(pi->pardev->port);
9962306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
10062306a36Sopenharmony_ci				PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT);
10162306a36Sopenharmony_ci		data = parport_read_data(pi->pardev->port);
10262306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, 0);
10362306a36Sopenharmony_ci		parport_data_forward(pi->pardev->port);
10462306a36Sopenharmony_ci		break;
10562306a36Sopenharmony_ci	case PPCMODE_EPP_BYTE:
10662306a36Sopenharmony_ci	case PPCMODE_EPP_WORD:
10762306a36Sopenharmony_ci	case PPCMODE_EPP_DWORD:
10862306a36Sopenharmony_ci		pi->pardev->port->ops->epp_read_data(pi->pardev->port, &data, 1, 0);
10962306a36Sopenharmony_ci		break;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return data;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void bpck6_wr_data_byte(struct pi_adapter *pi, u8 data)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	switch (mode_map[pi->mode]) {
11862306a36Sopenharmony_ci	case PPCMODE_UNI_SW:
11962306a36Sopenharmony_ci	case PPCMODE_UNI_FW:
12062306a36Sopenharmony_ci	case PPCMODE_BI_SW:
12162306a36Sopenharmony_ci	case PPCMODE_BI_FW:
12262306a36Sopenharmony_ci		parport_write_data(pi->pardev->port, data);
12362306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_INIT);
12462306a36Sopenharmony_ci		break;
12562306a36Sopenharmony_ci	case PPCMODE_EPP_BYTE:
12662306a36Sopenharmony_ci	case PPCMODE_EPP_WORD:
12762306a36Sopenharmony_ci	case PPCMODE_EPP_DWORD:
12862306a36Sopenharmony_ci		pi->pardev->port->ops->epp_write_data(pi->pardev->port, &data, 1, 0);
12962306a36Sopenharmony_ci		break;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int bpck6_read_regr(struct pi_adapter *pi, int cont, int reg)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	u8 port = cont ? reg | 8 : reg;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_READ);
13862306a36Sopenharmony_ci	return bpck6_rd_data_byte(pi);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void bpck6_write_regr(struct pi_adapter *pi, int cont, int reg, int val)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	u8 port = cont ? reg | 8 : reg;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_WRITE);
14662306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, val);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic void bpck6_wait_for_fifo(struct pi_adapter *pi)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int i;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (pi->private & fifo_wait) {
15462306a36Sopenharmony_ci		for (i = 0; i < 20; i++)
15562306a36Sopenharmony_ci			parport_read_status(pi->pardev->port);
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void bpck6_write_block(struct pi_adapter *pi, char *buf, int len)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	u8 this, last;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);
16462306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, (u8)len);
16562306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, (u8)(len >> 8));
16662306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, 0);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);
16962306a36Sopenharmony_ci	bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_WRITE);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	switch (mode_map[pi->mode]) {
17262306a36Sopenharmony_ci	case PPCMODE_UNI_SW:
17362306a36Sopenharmony_ci	case PPCMODE_BI_SW:
17462306a36Sopenharmony_ci		while (len--) {
17562306a36Sopenharmony_ci			parport_write_data(pi->pardev->port, *buf++);
17662306a36Sopenharmony_ci			parport_frob_control(pi->pardev->port, 0,
17762306a36Sopenharmony_ci							PARPORT_CONTROL_INIT);
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	case PPCMODE_UNI_FW:
18162306a36Sopenharmony_ci	case PPCMODE_BI_FW:
18262306a36Sopenharmony_ci		bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_FASTWR);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
18562306a36Sopenharmony_ci							PARPORT_CONTROL_STROBE);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		last = *buf;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		parport_write_data(pi->pardev->port, last);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		while (len) {
19262306a36Sopenharmony_ci			this = *buf++;
19362306a36Sopenharmony_ci			len--;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci			if (this == last) {
19662306a36Sopenharmony_ci				parport_frob_control(pi->pardev->port, 0,
19762306a36Sopenharmony_ci							PARPORT_CONTROL_INIT);
19862306a36Sopenharmony_ci			} else {
19962306a36Sopenharmony_ci				parport_write_data(pi->pardev->port, this);
20062306a36Sopenharmony_ci				last = this;
20162306a36Sopenharmony_ci			}
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
20562306a36Sopenharmony_ci							0);
20662306a36Sopenharmony_ci		bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_FASTWR);
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci	case PPCMODE_EPP_BYTE:
20962306a36Sopenharmony_ci		pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
21062306a36Sopenharmony_ci						len, PARPORT_EPP_FAST_8);
21162306a36Sopenharmony_ci		bpck6_wait_for_fifo(pi);
21262306a36Sopenharmony_ci		break;
21362306a36Sopenharmony_ci	case PPCMODE_EPP_WORD:
21462306a36Sopenharmony_ci		pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
21562306a36Sopenharmony_ci						len, PARPORT_EPP_FAST_16);
21662306a36Sopenharmony_ci		bpck6_wait_for_fifo(pi);
21762306a36Sopenharmony_ci		break;
21862306a36Sopenharmony_ci	case PPCMODE_EPP_DWORD:
21962306a36Sopenharmony_ci		pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,
22062306a36Sopenharmony_ci						len, PARPORT_EPP_FAST_32);
22162306a36Sopenharmony_ci		bpck6_wait_for_fifo(pi);
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void bpck6_read_block(struct pi_adapter *pi, char *buf, int len)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);
23162306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, (u8)len);
23262306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, (u8)(len >> 8));
23362306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, 0);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);
23662306a36Sopenharmony_ci	bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_READ);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	switch (mode_map[pi->mode]) {
23962306a36Sopenharmony_ci	case PPCMODE_UNI_SW:
24062306a36Sopenharmony_ci	case PPCMODE_UNI_FW:
24162306a36Sopenharmony_ci		while (len) {
24262306a36Sopenharmony_ci			u8 d;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci			parport_frob_control(pi->pardev->port,
24562306a36Sopenharmony_ci					PARPORT_CONTROL_STROBE,
24662306a36Sopenharmony_ci					PARPORT_CONTROL_INIT); /* DATA STROBE */
24762306a36Sopenharmony_ci			d = parport_read_status(pi->pardev->port);
24862306a36Sopenharmony_ci			d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);
24962306a36Sopenharmony_ci			parport_frob_control(pi->pardev->port,
25062306a36Sopenharmony_ci					PARPORT_CONTROL_STROBE,
25162306a36Sopenharmony_ci					PARPORT_CONTROL_STROBE);
25262306a36Sopenharmony_ci			d |= parport_read_status(pi->pardev->port) & 0xB8;
25362306a36Sopenharmony_ci			*buf++ = d;
25462306a36Sopenharmony_ci			len--;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci		break;
25762306a36Sopenharmony_ci	case PPCMODE_BI_SW:
25862306a36Sopenharmony_ci	case PPCMODE_BI_FW:
25962306a36Sopenharmony_ci		parport_data_reverse(pi->pardev->port);
26062306a36Sopenharmony_ci		while (len) {
26162306a36Sopenharmony_ci			parport_frob_control(pi->pardev->port,
26262306a36Sopenharmony_ci				PARPORT_CONTROL_STROBE,
26362306a36Sopenharmony_ci				PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT);
26462306a36Sopenharmony_ci			*buf++ = parport_read_data(pi->pardev->port);
26562306a36Sopenharmony_ci			len--;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,
26862306a36Sopenharmony_ci					0);
26962306a36Sopenharmony_ci		parport_data_forward(pi->pardev->port);
27062306a36Sopenharmony_ci		break;
27162306a36Sopenharmony_ci	case PPCMODE_EPP_BYTE:
27262306a36Sopenharmony_ci		pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
27362306a36Sopenharmony_ci						PARPORT_EPP_FAST_8);
27462306a36Sopenharmony_ci		break;
27562306a36Sopenharmony_ci	case PPCMODE_EPP_WORD:
27662306a36Sopenharmony_ci		pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
27762306a36Sopenharmony_ci						PARPORT_EPP_FAST_16);
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci	case PPCMODE_EPP_DWORD:
28062306a36Sopenharmony_ci		pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,
28162306a36Sopenharmony_ci						PARPORT_EPP_FAST_32);
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int bpck6_open(struct pi_adapter *pi)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	u8 i, j, k;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	pi->saved_r0 = parport_read_data(pi->pardev->port);
29362306a36Sopenharmony_ci	pi->saved_r2 = parport_read_control(pi->pardev->port) & 0x5F;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
29662306a36Sopenharmony_ci						PARPORT_CONTROL_SELECT);
29762306a36Sopenharmony_ci	if (pi->saved_r0 == 'b')
29862306a36Sopenharmony_ci		parport_write_data(pi->pardev->port, 'x');
29962306a36Sopenharmony_ci	parport_write_data(pi->pardev->port, 'b');
30062306a36Sopenharmony_ci	parport_write_data(pi->pardev->port, 'p');
30162306a36Sopenharmony_ci	parport_write_data(pi->pardev->port, pi->unit);
30262306a36Sopenharmony_ci	parport_write_data(pi->pardev->port, ~pi->unit);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0);
30562306a36Sopenharmony_ci	parport_write_control(pi->pardev->port, PARPORT_CONTROL_INIT);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	i = mode_map[pi->mode] & 0x0C;
30862306a36Sopenharmony_ci	if (i == 0)
30962306a36Sopenharmony_ci		i = (mode_map[pi->mode] & 2) | 1;
31062306a36Sopenharmony_ci	parport_write_data(pi->pardev->port, i);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
31362306a36Sopenharmony_ci						PARPORT_CONTROL_SELECT);
31462306a36Sopenharmony_ci	parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD,
31562306a36Sopenharmony_ci						PARPORT_CONTROL_AUTOFD);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	j = ((i & 0x08) << 4) | ((i & 0x07) << 3);
31862306a36Sopenharmony_ci	k = parport_read_status(pi->pardev->port) & 0xB8;
31962306a36Sopenharmony_ci	if (j != k)
32062306a36Sopenharmony_ci		goto fail;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, 0);
32362306a36Sopenharmony_ci	k = (parport_read_status(pi->pardev->port) & 0xB8) ^ 0xB8;
32462306a36Sopenharmony_ci	if (j != k)
32562306a36Sopenharmony_ci		goto fail;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (i & 4) {
32862306a36Sopenharmony_ci		/* EPP */
32962306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port,
33062306a36Sopenharmony_ci			PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, 0);
33162306a36Sopenharmony_ci	} else {
33262306a36Sopenharmony_ci		/* PPC/ECP */
33362306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	pi->private = 0;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	bpck6_send_cmd(pi, ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE);
33962306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, RAMSIZE_128K);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	bpck6_send_cmd(pi, ACCESS_REG | ACCESS_READ | REG_VERSION);
34262306a36Sopenharmony_ci	if ((bpck6_rd_data_byte(pi) & 0x3F) == 0x0C)
34362306a36Sopenharmony_ci		pi->private |= fifo_wait;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 1;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cifail:
34862306a36Sopenharmony_ci	parport_write_control(pi->pardev->port, pi->saved_r2);
34962306a36Sopenharmony_ci	parport_write_data(pi->pardev->port, pi->saved_r0);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void bpck6_deselect(struct pi_adapter *pi)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	if (mode_map[pi->mode] & 4) {
35762306a36Sopenharmony_ci		/* EPP */
35862306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_INIT,
35962306a36Sopenharmony_ci				     PARPORT_CONTROL_INIT);
36062306a36Sopenharmony_ci	} else {
36162306a36Sopenharmony_ci		/* PPC/ECP */
36262306a36Sopenharmony_ci		parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,
36362306a36Sopenharmony_ci				     PARPORT_CONTROL_SELECT);
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	parport_write_data(pi->pardev->port, pi->saved_r0);
36762306a36Sopenharmony_ci	parport_write_control(pi->pardev->port,
36862306a36Sopenharmony_ci			pi->saved_r2 | PARPORT_CONTROL_SELECT);
36962306a36Sopenharmony_ci	parport_write_control(pi->pardev->port, pi->saved_r2);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void bpck6_wr_extout(struct pi_adapter *pi, u8 regdata)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	bpck6_send_cmd(pi, REG_VERSION | ACCESS_REG | ACCESS_WRITE);
37562306a36Sopenharmony_ci	bpck6_wr_data_byte(pi, (u8)((regdata & 0x03) << 6));
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic void bpck6_connect(struct pi_adapter *pi)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	dev_dbg(&pi->dev, "connect\n");
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	bpck6_open(pi);
38362306a36Sopenharmony_ci	bpck6_wr_extout(pi, 0x3);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic void bpck6_disconnect(struct pi_adapter *pi)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	dev_dbg(&pi->dev, "disconnect\n");
38962306a36Sopenharmony_ci	bpck6_wr_extout(pi, 0x0);
39062306a36Sopenharmony_ci	bpck6_deselect(pi);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/* check for 8-bit port */
39462306a36Sopenharmony_cistatic int bpck6_test_port(struct pi_adapter *pi)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	dev_dbg(&pi->dev, "PARPORT indicates modes=%x for lp=0x%lx\n",
39762306a36Sopenharmony_ci		pi->pardev->port->modes, pi->pardev->port->base);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* look at the parport device to see what modes we can use */
40062306a36Sopenharmony_ci	if (pi->pardev->port->modes & PARPORT_MODE_EPP)
40162306a36Sopenharmony_ci		return 5; /* Can do EPP */
40262306a36Sopenharmony_ci	if (pi->pardev->port->modes & PARPORT_MODE_TRISTATE)
40362306a36Sopenharmony_ci		return 2;
40462306a36Sopenharmony_ci	return 1; /* Just flat SPP */
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic int bpck6_probe_unit(struct pi_adapter *pi)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	int out, saved_mode;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	dev_dbg(&pi->dev, "PROBE UNIT %x on port:%x\n", pi->unit, pi->port);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	saved_mode = pi->mode;
41462306a36Sopenharmony_ci	/*LOWER DOWN TO UNIDIRECTIONAL*/
41562306a36Sopenharmony_ci	pi->mode = 0;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	out = bpck6_open(pi);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	dev_dbg(&pi->dev, "ppc_open returned %2x\n", out);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (out) {
42262306a36Sopenharmony_ci		bpck6_deselect(pi);
42362306a36Sopenharmony_ci		dev_dbg(&pi->dev, "leaving probe\n");
42462306a36Sopenharmony_ci		pi->mode = saved_mode;
42562306a36Sopenharmony_ci		return 1;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	dev_dbg(&pi->dev, "Failed open\n");
42962306a36Sopenharmony_ci	pi->mode = saved_mode;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic void bpck6_log_adapter(struct pi_adapter *pi)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	dev_info(&pi->dev,
43962306a36Sopenharmony_ci		 "Micro Solutions BACKPACK Drive unit %d at 0x%x, mode:%d (%s), delay %d\n",
44062306a36Sopenharmony_ci		 pi->unit, pi->port, pi->mode, mode_string[pi->mode], pi->delay);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic struct pi_protocol bpck6 = {
44462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
44562306a36Sopenharmony_ci	.name		= "bpck6",
44662306a36Sopenharmony_ci	.max_mode	= 5,
44762306a36Sopenharmony_ci	.epp_first	= 2, /* 2-5 use epp (need 8 ports) */
44862306a36Sopenharmony_ci	.max_units	= 255,
44962306a36Sopenharmony_ci	.write_regr	= bpck6_write_regr,
45062306a36Sopenharmony_ci	.read_regr	= bpck6_read_regr,
45162306a36Sopenharmony_ci	.write_block	= bpck6_write_block,
45262306a36Sopenharmony_ci	.read_block	= bpck6_read_block,
45362306a36Sopenharmony_ci	.connect	= bpck6_connect,
45462306a36Sopenharmony_ci	.disconnect	= bpck6_disconnect,
45562306a36Sopenharmony_ci	.test_port	= bpck6_test_port,
45662306a36Sopenharmony_ci	.probe_unit	= bpck6_probe_unit,
45762306a36Sopenharmony_ci	.log_adapter	= bpck6_log_adapter,
45862306a36Sopenharmony_ci};
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
46162306a36Sopenharmony_ciMODULE_AUTHOR("Micro Solutions Inc.");
46262306a36Sopenharmony_ciMODULE_DESCRIPTION("Micro Solutions BACKPACK parallel port IDE adapter "
46362306a36Sopenharmony_ci		   "(version 6 drives) protocol driver");
46462306a36Sopenharmony_cimodule_pata_parport_driver(bpck6);
465