162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * (c) 1997-1998  Grant R. Guenther <grant@torque.net>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * epia.c is a low-level protocol driver for Shuttle Technologies
662306a36Sopenharmony_ci * EPIA parallel to IDE adapter chip.  This device is now obsolete
762306a36Sopenharmony_ci * and has been replaced with the EPAT chip, which is supported
862306a36Sopenharmony_ci * by epat.c, however, some devices based on EPIA are still
962306a36Sopenharmony_ci * available.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/wait.h>
1862306a36Sopenharmony_ci#include <asm/io.h>
1962306a36Sopenharmony_ci#include "pata_parport.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * mode codes:  0  nybble reads on port 1, 8-bit writes
2362306a36Sopenharmony_ci *		1  5/3 reads on ports 1 & 2, 8-bit writes
2462306a36Sopenharmony_ci *		2  8-bit reads and writes
2562306a36Sopenharmony_ci *		3  8-bit EPP mode
2662306a36Sopenharmony_ci *		4  16-bit EPP
2762306a36Sopenharmony_ci *		5  32-bit EPP
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define j44(a, b)	(((a >> 4) & 0x0f) + (b & 0xf0))
3162306a36Sopenharmony_ci#define j53(a, b)	(((a >> 3) & 0x1f) + ((b << 4) & 0xe0))
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * cont =  0   IDE register file
3562306a36Sopenharmony_ci * cont =  1   IDE control registers
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_cistatic int cont_map[2] = { 0, 0x80 };
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int epia_read_regr(struct pi_adapter *pi, int cont, int regr)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	int a, b, r;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	regr += cont_map[cont];
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	switch (pi->mode)  {
4662306a36Sopenharmony_ci	case 0:
4762306a36Sopenharmony_ci		r = regr ^ 0x39;
4862306a36Sopenharmony_ci		w0(r); w2(1); w2(3); w0(r);
4962306a36Sopenharmony_ci		a = r1(); w2(1); b = r1(); w2(4);
5062306a36Sopenharmony_ci		return j44(a, b);
5162306a36Sopenharmony_ci	case 1:
5262306a36Sopenharmony_ci		r = regr ^ 0x31;
5362306a36Sopenharmony_ci		w0(r); w2(1); w0(r & 0x37);
5462306a36Sopenharmony_ci		w2(3); w2(5); w0(r | 0xf0);
5562306a36Sopenharmony_ci		a = r1(); b = r2(); w2(4);
5662306a36Sopenharmony_ci		return j53(a, b);
5762306a36Sopenharmony_ci	case 2:
5862306a36Sopenharmony_ci		r = regr^0x29;
5962306a36Sopenharmony_ci		w0(r); w2(1); w2(0X21); w2(0x23);
6062306a36Sopenharmony_ci		a = r0(); w2(4);
6162306a36Sopenharmony_ci		return a;
6262306a36Sopenharmony_ci	case 3:
6362306a36Sopenharmony_ci	case 4:
6462306a36Sopenharmony_ci	case 5:
6562306a36Sopenharmony_ci		w3(regr); w2(0x24); a = r4(); w2(4);
6662306a36Sopenharmony_ci		return a;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return -1;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void epia_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	int  r;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	regr += cont_map[cont];
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	switch (pi->mode)  {
7962306a36Sopenharmony_ci	case 0:
8062306a36Sopenharmony_ci	case 1:
8162306a36Sopenharmony_ci	case 2:
8262306a36Sopenharmony_ci		r = regr ^ 0x19;
8362306a36Sopenharmony_ci		w0(r); w2(1); w0(val); w2(3); w2(4);
8462306a36Sopenharmony_ci		break;
8562306a36Sopenharmony_ci	case 3:
8662306a36Sopenharmony_ci	case 4:
8762306a36Sopenharmony_ci	case 5:
8862306a36Sopenharmony_ci		r = regr ^ 0x40;
8962306a36Sopenharmony_ci		w3(r); w4(val); w2(4);
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define WR(r, v)	epia_write_regr(pi, 0, r, v)
9562306a36Sopenharmony_ci#define RR(r)		epia_read_regr(pi, 0, r)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * The use of register 0x84 is entirely unclear - it seems to control
9962306a36Sopenharmony_ci * some EPP counters ...  currently we know about 3 different block
10062306a36Sopenharmony_ci * sizes:  the standard 512 byte reads and writes, 12 byte writes and
10162306a36Sopenharmony_ci * 2048 byte reads (the last two being used in the CDrom drivers.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cistatic void epia_connect(struct pi_adapter *pi)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	pi->saved_r0 = r0();
10662306a36Sopenharmony_ci	pi->saved_r2 = r2();
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
10962306a36Sopenharmony_ci	w2(1); w2(4);
11062306a36Sopenharmony_ci	if (pi->mode >= 3) {
11162306a36Sopenharmony_ci		w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
11262306a36Sopenharmony_ci		w2(0x24); w2(0x26); w2(4);
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci	WR(0x86, 8);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void epia_disconnect(struct pi_adapter *pi)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	/* WR(0x84,0x10); */
12062306a36Sopenharmony_ci	w0(pi->saved_r0);
12162306a36Sopenharmony_ci	w2(1); w2(4);
12262306a36Sopenharmony_ci	w0(pi->saved_r0);
12362306a36Sopenharmony_ci	w2(pi->saved_r2);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void epia_read_block(struct pi_adapter *pi, char *buf, int count)
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	int k, ph, a, b;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	switch (pi->mode) {
13262306a36Sopenharmony_ci	case 0:
13362306a36Sopenharmony_ci		w0(0x81); w2(1); w2(3); w0(0xc1);
13462306a36Sopenharmony_ci		ph = 1;
13562306a36Sopenharmony_ci		for (k = 0; k < count; k++) {
13662306a36Sopenharmony_ci			w2(2+ph); a = r1();
13762306a36Sopenharmony_ci			w2(4+ph); b = r1();
13862306a36Sopenharmony_ci			buf[k] = j44(a, b);
13962306a36Sopenharmony_ci			ph = 1 - ph;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci		w0(0); w2(4);
14262306a36Sopenharmony_ci		break;
14362306a36Sopenharmony_ci	case 1:
14462306a36Sopenharmony_ci		w0(0x91); w2(1); w0(0x10); w2(3);
14562306a36Sopenharmony_ci		w0(0x51); w2(5); w0(0xd1);
14662306a36Sopenharmony_ci		ph = 1;
14762306a36Sopenharmony_ci		for (k = 0; k < count; k++) {
14862306a36Sopenharmony_ci			w2(4 + ph);
14962306a36Sopenharmony_ci			a = r1(); b = r2();
15062306a36Sopenharmony_ci			buf[k] = j53(a, b);
15162306a36Sopenharmony_ci			ph = 1 - ph;
15262306a36Sopenharmony_ci		}
15362306a36Sopenharmony_ci		w0(0); w2(4);
15462306a36Sopenharmony_ci		break;
15562306a36Sopenharmony_ci	case 2:
15662306a36Sopenharmony_ci		w0(0x89); w2(1); w2(0x23); w2(0x21);
15762306a36Sopenharmony_ci		ph = 1;
15862306a36Sopenharmony_ci		for (k = 0; k < count; k++) {
15962306a36Sopenharmony_ci			w2(0x24 + ph);
16062306a36Sopenharmony_ci			buf[k] = r0();
16162306a36Sopenharmony_ci			ph = 1 - ph;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci		w2(6); w2(4);
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci	case 3:
16662306a36Sopenharmony_ci		if (count > 512)
16762306a36Sopenharmony_ci			WR(0x84, 3);
16862306a36Sopenharmony_ci		w3(0); w2(0x24);
16962306a36Sopenharmony_ci		for (k = 0; k < count; k++)
17062306a36Sopenharmony_ci			buf[k] = r4();
17162306a36Sopenharmony_ci		w2(4); WR(0x84, 0);
17262306a36Sopenharmony_ci		break;
17362306a36Sopenharmony_ci	case 4:
17462306a36Sopenharmony_ci		if (count > 512)
17562306a36Sopenharmony_ci			WR(0x84, 3);
17662306a36Sopenharmony_ci		w3(0); w2(0x24);
17762306a36Sopenharmony_ci		for (k = 0; k < count / 2; k++)
17862306a36Sopenharmony_ci			((u16 *)buf)[k] = r4w();
17962306a36Sopenharmony_ci		w2(4); WR(0x84, 0);
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	case 5:
18262306a36Sopenharmony_ci		if (count > 512)
18362306a36Sopenharmony_ci			WR(0x84, 3);
18462306a36Sopenharmony_ci		w3(0); w2(0x24);
18562306a36Sopenharmony_ci		for (k = 0; k < count / 4; k++)
18662306a36Sopenharmony_ci			((u32 *)buf)[k] = r4l();
18762306a36Sopenharmony_ci		w2(4); WR(0x84, 0);
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void epia_write_block(struct pi_adapter *pi, char *buf, int count)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	int ph, k, last, d;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	switch (pi->mode) {
19762306a36Sopenharmony_ci	case 0:
19862306a36Sopenharmony_ci	case 1:
19962306a36Sopenharmony_ci	case 2:
20062306a36Sopenharmony_ci		w0(0xa1); w2(1); w2(3); w2(1); w2(5);
20162306a36Sopenharmony_ci		ph = 0;  last = 0x8000;
20262306a36Sopenharmony_ci		for (k = 0; k < count; k++) {
20362306a36Sopenharmony_ci			d = buf[k];
20462306a36Sopenharmony_ci			if (d != last) {
20562306a36Sopenharmony_ci				last = d;
20662306a36Sopenharmony_ci				w0(d);
20762306a36Sopenharmony_ci			}
20862306a36Sopenharmony_ci			w2(4 + ph);
20962306a36Sopenharmony_ci			ph = 1 - ph;
21062306a36Sopenharmony_ci		}
21162306a36Sopenharmony_ci		w2(7); w2(4);
21262306a36Sopenharmony_ci		break;
21362306a36Sopenharmony_ci	case 3:
21462306a36Sopenharmony_ci		if (count < 512)
21562306a36Sopenharmony_ci			WR(0x84, 1);
21662306a36Sopenharmony_ci		w3(0x40);
21762306a36Sopenharmony_ci		for (k = 0; k < count; k++)
21862306a36Sopenharmony_ci			w4(buf[k]);
21962306a36Sopenharmony_ci		if (count < 512)
22062306a36Sopenharmony_ci			WR(0x84, 0);
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci	case 4:
22362306a36Sopenharmony_ci		if (count < 512)
22462306a36Sopenharmony_ci			WR(0x84, 1);
22562306a36Sopenharmony_ci		w3(0x40);
22662306a36Sopenharmony_ci		for (k = 0; k < count / 2; k++)
22762306a36Sopenharmony_ci			w4w(((u16 *)buf)[k]);
22862306a36Sopenharmony_ci		if (count < 512)
22962306a36Sopenharmony_ci			WR(0x84, 0);
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci	case 5:
23262306a36Sopenharmony_ci		if (count < 512)
23362306a36Sopenharmony_ci			WR(0x84, 1);
23462306a36Sopenharmony_ci		w3(0x40);
23562306a36Sopenharmony_ci		for (k = 0; k < count / 4; k++)
23662306a36Sopenharmony_ci			w4l(((u32 *)buf)[k]);
23762306a36Sopenharmony_ci		if (count < 512)
23862306a36Sopenharmony_ci			WR(0x84, 0);
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int epia_test_proto(struct pi_adapter *pi)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	int j, k, f;
24662306a36Sopenharmony_ci	int e[2] = { 0, 0 };
24762306a36Sopenharmony_ci	char scratch[512];
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	epia_connect(pi);
25062306a36Sopenharmony_ci	for (j = 0; j < 2; j++) {
25162306a36Sopenharmony_ci		WR(6, 0xa0 + j * 0x10);
25262306a36Sopenharmony_ci		for (k = 0; k < 256; k++) {
25362306a36Sopenharmony_ci			WR(2, k ^ 0xaa);
25462306a36Sopenharmony_ci			WR(3, k ^ 0x55);
25562306a36Sopenharmony_ci			if (RR(2) != (k ^ 0xaa))
25662306a36Sopenharmony_ci				e[j]++;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci		WR(2, 1); WR(3, 1);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci	epia_disconnect(pi);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	f = 0;
26362306a36Sopenharmony_ci	epia_connect(pi);
26462306a36Sopenharmony_ci	WR(0x84, 8);
26562306a36Sopenharmony_ci	epia_read_block(pi, scratch, 512);
26662306a36Sopenharmony_ci	for (k = 0; k < 256; k++) {
26762306a36Sopenharmony_ci		if ((scratch[2 * k] & 0xff) != ((k + 1) & 0xff))
26862306a36Sopenharmony_ci			f++;
26962306a36Sopenharmony_ci		if ((scratch[2 * k + 1] & 0xff) != ((-2 - k) & 0xff))
27062306a36Sopenharmony_ci			f++;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci	WR(0x84, 0);
27362306a36Sopenharmony_ci	epia_disconnect(pi);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	dev_dbg(&pi->dev, "epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
27662306a36Sopenharmony_ci		pi->port, pi->mode, e[0], e[1], f);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return (e[0] && e[1]) || f;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic void epia_log_adapter(struct pi_adapter *pi)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	char *mode[6] = { "4-bit", "5/3", "8-bit", "EPP-8", "EPP-16", "EPP-32"};
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	dev_info(&pi->dev,
28762306a36Sopenharmony_ci		 "Shuttle EPIA at 0x%x, mode %d (%s), delay %d\n",
28862306a36Sopenharmony_ci		 pi->port, pi->mode, mode[pi->mode], pi->delay);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic struct pi_protocol epia = {
29262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
29362306a36Sopenharmony_ci	.name		= "epia",
29462306a36Sopenharmony_ci	.max_mode	= 6,
29562306a36Sopenharmony_ci	.epp_first	= 3,
29662306a36Sopenharmony_ci	.default_delay	= 1,
29762306a36Sopenharmony_ci	.max_units	= 1,
29862306a36Sopenharmony_ci	.write_regr	= epia_write_regr,
29962306a36Sopenharmony_ci	.read_regr	= epia_read_regr,
30062306a36Sopenharmony_ci	.write_block	= epia_write_block,
30162306a36Sopenharmony_ci	.read_block	= epia_read_block,
30262306a36Sopenharmony_ci	.connect	= epia_connect,
30362306a36Sopenharmony_ci	.disconnect	= epia_disconnect,
30462306a36Sopenharmony_ci	.test_proto	= epia_test_proto,
30562306a36Sopenharmony_ci	.log_adapter	= epia_log_adapter,
30662306a36Sopenharmony_ci};
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
30962306a36Sopenharmony_ciMODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
31062306a36Sopenharmony_ciMODULE_DESCRIPTION("Shuttle Technologies EPIA parallel port IDE adapter "
31162306a36Sopenharmony_ci		   "protocol driver");
31262306a36Sopenharmony_cimodule_pata_parport_driver(epia);
313