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 * on26.c is a low-level protocol driver for the
662306a36Sopenharmony_ci * OnSpec 90c26 parallel to IDE adapter chip.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/wait.h>
1562306a36Sopenharmony_ci#include <asm/io.h>
1662306a36Sopenharmony_ci#include "pata_parport.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * mode codes:  0  nybble reads, 8-bit writes
2062306a36Sopenharmony_ci *		1  8-bit reads and writes
2162306a36Sopenharmony_ci *		2  8-bit EPP mode
2262306a36Sopenharmony_ci *		3  EPP-16
2362306a36Sopenharmony_ci *		4  EPP-32
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define j44(a, b)	(((a >> 4) & 0x0f) | (b & 0xf0))
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define P1						      \
2962306a36Sopenharmony_ci	do {						      \
3062306a36Sopenharmony_ci		w2(5); w2(0xd); w2(5); w2(0xd); w2(5); w2(4); \
3162306a36Sopenharmony_ci	} while (0)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define P2					\
3462306a36Sopenharmony_ci	do {					\
3562306a36Sopenharmony_ci		w2(5); w2(7); w2(5); w2(4);	\
3662306a36Sopenharmony_ci	} while (0)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * cont = 0 - access the IDE register file
4062306a36Sopenharmony_ci * cont = 1 - access the IDE command set
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int on26_read_regr(struct pi_adapter *pi, int cont, int regr)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	int a, b, r;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	r = (regr << 2) + 1 + cont;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	switch (pi->mode) {
5062306a36Sopenharmony_ci	case 0:
5162306a36Sopenharmony_ci		w0(1); P1; w0(r); P2; w0(0); P1;
5262306a36Sopenharmony_ci		w2(6); a = r1(); w2(4);
5362306a36Sopenharmony_ci		w2(6); b = r1(); w2(4);
5462306a36Sopenharmony_ci		w2(6); w2(4); w2(6); w2(4);
5562306a36Sopenharmony_ci		return j44(a, b);
5662306a36Sopenharmony_ci	case 1:
5762306a36Sopenharmony_ci		w0(1); P1; w0(r); P2; w0(0); P1;
5862306a36Sopenharmony_ci		w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
5962306a36Sopenharmony_ci		return a;
6062306a36Sopenharmony_ci	case 2:
6162306a36Sopenharmony_ci	case 3:
6262306a36Sopenharmony_ci	case 4:
6362306a36Sopenharmony_ci		w3(1); w3(1); w2(5); w4(r); w2(4);
6462306a36Sopenharmony_ci		w3(0); w3(0); w2(0x24); a = r4(); w2(4);
6562306a36Sopenharmony_ci		w2(0x24); (void)r4(); w2(4);
6662306a36Sopenharmony_ci		return a;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return -1;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void on26_write_regr(struct pi_adapter *pi, int cont, int regr, int val)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	int r = (regr << 2) + 1 + cont;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	switch (pi->mode) {
7762306a36Sopenharmony_ci	case 0:
7862306a36Sopenharmony_ci	case 1:
7962306a36Sopenharmony_ci		w0(1); P1; w0(r); P2; w0(0); P1;
8062306a36Sopenharmony_ci		w0(val); P2; w0(val); P2;
8162306a36Sopenharmony_ci		break;
8262306a36Sopenharmony_ci	case 2:
8362306a36Sopenharmony_ci	case 3:
8462306a36Sopenharmony_ci	case 4:
8562306a36Sopenharmony_ci		w3(1); w3(1); w2(5); w4(r); w2(4);
8662306a36Sopenharmony_ci		w3(0); w3(0);
8762306a36Sopenharmony_ci		w2(5); w4(val); w2(4);
8862306a36Sopenharmony_ci		w2(5); w4(val); w2(4);
8962306a36Sopenharmony_ci		break;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define CCP(x)						\
9462306a36Sopenharmony_ci	do {						\
9562306a36Sopenharmony_ci		w0(0xfe); w0(0xaa); w0(0x55); w0(0);	\
9662306a36Sopenharmony_ci		w0(0xff); w0(0x87); w0(0x78); w0(x);	\
9762306a36Sopenharmony_ci		w2(4); w2(5); w2(4); w0(0xff);		\
9862306a36Sopenharmony_ci	} while (0)
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void on26_connect(struct pi_adapter *pi)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	int x;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	pi->saved_r0 = r0();
10562306a36Sopenharmony_ci	pi->saved_r2 = r2();
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	CCP(0x20);
10862306a36Sopenharmony_ci	if (pi->mode)
10962306a36Sopenharmony_ci		x = 9;
11062306a36Sopenharmony_ci	else
11162306a36Sopenharmony_ci		x = 8;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	w0(2); P1; w0(8); P2;
11462306a36Sopenharmony_ci	w0(2); P1; w0(x); P2;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void on26_disconnect(struct pi_adapter *pi)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	if (pi->mode >= 2) {
12062306a36Sopenharmony_ci		w3(4); w3(4); w3(4); w3(4);
12162306a36Sopenharmony_ci	} else {
12262306a36Sopenharmony_ci		w0(4); P1; w0(4); P1;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	CCP(0x30);
12562306a36Sopenharmony_ci	w0(pi->saved_r0);
12662306a36Sopenharmony_ci	w2(pi->saved_r2);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define	RESET_WAIT  200
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* hard reset */
13262306a36Sopenharmony_cistatic int on26_test_port(struct pi_adapter *pi)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	int i, m, d, x = 0, y = 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	pi->saved_r0 = r0();
13762306a36Sopenharmony_ci	pi->saved_r2 = r2();
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	d = pi->delay;
14062306a36Sopenharmony_ci	m = pi->mode;
14162306a36Sopenharmony_ci	pi->delay = 5;
14262306a36Sopenharmony_ci	pi->mode = 0;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	w2(0xc);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	CCP(0x30); CCP(0);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	w0(0xfe); w0(0xaa); w0(0x55); w0(0); w0(0xff);
14962306a36Sopenharmony_ci	i = ((r1() & 0xf0) << 4); w0(0x87);
15062306a36Sopenharmony_ci	i |= (r1() & 0xf0); w0(0x78);
15162306a36Sopenharmony_ci	w0(0x20); w2(4); w2(5);
15262306a36Sopenharmony_ci	i |= ((r1() & 0xf0) >> 4);
15362306a36Sopenharmony_ci	w2(4); w0(0xff);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (i == 0xb5f) {
15662306a36Sopenharmony_ci		w0(2); P1; w0(0);   P2;
15762306a36Sopenharmony_ci		w0(3); P1; w0(0);   P2;
15862306a36Sopenharmony_ci		w0(2); P1; w0(8);   P2; udelay(100);
15962306a36Sopenharmony_ci		w0(2); P1; w0(0xa); P2; udelay(100);
16062306a36Sopenharmony_ci		w0(2); P1; w0(8);   P2; udelay(1000);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		on26_write_regr(pi, 0, 6, 0xa0);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		for (i = 0; i < RESET_WAIT; i++) {
16562306a36Sopenharmony_ci			on26_write_regr(pi, 0, 6, 0xa0);
16662306a36Sopenharmony_ci			x = on26_read_regr(pi, 0, 7);
16762306a36Sopenharmony_ci			on26_write_regr(pi, 0, 6, 0xb0);
16862306a36Sopenharmony_ci			y = on26_read_regr(pi, 0, 7);
16962306a36Sopenharmony_ci			if (!((x & 0x80) || (y & 0x80)))
17062306a36Sopenharmony_ci				break;
17162306a36Sopenharmony_ci			mdelay(100);
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		if (i == RESET_WAIT)
17562306a36Sopenharmony_ci			dev_err(&pi->dev,
17662306a36Sopenharmony_ci				"on26: Device reset failed (%x,%x)\n", x, y);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		w0(4); P1; w0(4); P1;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	CCP(0x30);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	pi->delay = d;
18462306a36Sopenharmony_ci	pi->mode = m;
18562306a36Sopenharmony_ci	w0(pi->saved_r0);
18662306a36Sopenharmony_ci	w2(pi->saved_r2);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return 5;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void on26_read_block(struct pi_adapter *pi, char *buf, int count)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	int k, a, b;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	switch (pi->mode) {
19662306a36Sopenharmony_ci	case 0:
19762306a36Sopenharmony_ci		w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
19862306a36Sopenharmony_ci		udelay(10);
19962306a36Sopenharmony_ci		for (k = 0; k < count; k++) {
20062306a36Sopenharmony_ci			w2(6); a = r1();
20162306a36Sopenharmony_ci			w2(4); b = r1();
20262306a36Sopenharmony_ci			buf[k] = j44(a, b);
20362306a36Sopenharmony_ci		}
20462306a36Sopenharmony_ci		w0(2); P1; w0(8); P2;
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	case 1:
20762306a36Sopenharmony_ci		w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
20862306a36Sopenharmony_ci		udelay(10);
20962306a36Sopenharmony_ci		for (k = 0; k < count / 2; k++) {
21062306a36Sopenharmony_ci			w2(0x26); buf[2 * k] = r0();
21162306a36Sopenharmony_ci			w2(0x24); buf[2 * k + 1] = r0();
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci		w0(2); P1; w0(9); P2;
21462306a36Sopenharmony_ci		break;
21562306a36Sopenharmony_ci	case 2:
21662306a36Sopenharmony_ci		w3(1); w3(1); w2(5); w4(1); w2(4);
21762306a36Sopenharmony_ci		w3(0); w3(0); w2(0x24);
21862306a36Sopenharmony_ci		udelay(10);
21962306a36Sopenharmony_ci		for (k = 0; k < count; k++)
22062306a36Sopenharmony_ci			buf[k] = r4();
22162306a36Sopenharmony_ci		w2(4);
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	case 3:
22462306a36Sopenharmony_ci		w3(1); w3(1); w2(5); w4(1); w2(4);
22562306a36Sopenharmony_ci		w3(0); w3(0); w2(0x24);
22662306a36Sopenharmony_ci		udelay(10);
22762306a36Sopenharmony_ci		for (k = 0; k < count / 2; k++)
22862306a36Sopenharmony_ci			((u16 *)buf)[k] = r4w();
22962306a36Sopenharmony_ci		w2(4);
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci	case 4:
23262306a36Sopenharmony_ci		w3(1); w3(1); w2(5); w4(1); w2(4);
23362306a36Sopenharmony_ci		w3(0); w3(0); w2(0x24);
23462306a36Sopenharmony_ci		udelay(10);
23562306a36Sopenharmony_ci		for (k = 0; k < count / 4; k++)
23662306a36Sopenharmony_ci			((u32 *)buf)[k] = r4l();
23762306a36Sopenharmony_ci		w2(4);
23862306a36Sopenharmony_ci		break;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void on26_write_block(struct pi_adapter *pi, char *buf, int count)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	int k;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	switch (pi->mode) {
24762306a36Sopenharmony_ci	case 0:
24862306a36Sopenharmony_ci	case 1:
24962306a36Sopenharmony_ci		w0(1); P1; w0(1); P2;
25062306a36Sopenharmony_ci		w0(2); P1; w0(0x18 + pi->mode); P2; w0(0); P1;
25162306a36Sopenharmony_ci		udelay(10);
25262306a36Sopenharmony_ci		for (k = 0; k < count / 2; k++) {
25362306a36Sopenharmony_ci			w2(5); w0(buf[2 * k]);
25462306a36Sopenharmony_ci			w2(7); w0(buf[2 * k + 1]);
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci		w2(5); w2(4);
25762306a36Sopenharmony_ci		w0(2); P1; w0(8 + pi->mode); P2;
25862306a36Sopenharmony_ci		break;
25962306a36Sopenharmony_ci	case 2:
26062306a36Sopenharmony_ci		w3(1); w3(1); w2(5); w4(1); w2(4);
26162306a36Sopenharmony_ci		w3(0); w3(0); w2(0xc5);
26262306a36Sopenharmony_ci		udelay(10);
26362306a36Sopenharmony_ci		for (k = 0; k < count; k++)
26462306a36Sopenharmony_ci			w4(buf[k]);
26562306a36Sopenharmony_ci		w2(0xc4);
26662306a36Sopenharmony_ci		break;
26762306a36Sopenharmony_ci	case 3:
26862306a36Sopenharmony_ci		w3(1); w3(1); w2(5); w4(1); w2(4);
26962306a36Sopenharmony_ci		w3(0); w3(0); w2(0xc5);
27062306a36Sopenharmony_ci		udelay(10);
27162306a36Sopenharmony_ci		for (k = 0; k < count / 2; k++)
27262306a36Sopenharmony_ci			w4w(((u16 *)buf)[k]);
27362306a36Sopenharmony_ci		w2(0xc4);
27462306a36Sopenharmony_ci		break;
27562306a36Sopenharmony_ci	case 4:
27662306a36Sopenharmony_ci		w3(1); w3(1); w2(5); w4(1); w2(4);
27762306a36Sopenharmony_ci		w3(0); w3(0); w2(0xc5);
27862306a36Sopenharmony_ci		udelay(10);
27962306a36Sopenharmony_ci		for (k = 0; k < count / 4; k++)
28062306a36Sopenharmony_ci			w4l(((u32 *)buf)[k]);
28162306a36Sopenharmony_ci		w2(0xc4);
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void on26_log_adapter(struct pi_adapter *pi)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	dev_info(&pi->dev,
29162306a36Sopenharmony_ci		 "OnSpec 90c26 at 0x%x, mode %d (%s), delay %d\n",
29262306a36Sopenharmony_ci		 pi->port, pi->mode, mode_string[pi->mode], pi->delay);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic struct pi_protocol on26 = {
29662306a36Sopenharmony_ci	.owner		= THIS_MODULE,
29762306a36Sopenharmony_ci	.name		= "on26",
29862306a36Sopenharmony_ci	.max_mode	= 5,
29962306a36Sopenharmony_ci	.epp_first	= 2,
30062306a36Sopenharmony_ci	.default_delay	= 1,
30162306a36Sopenharmony_ci	.max_units	= 1,
30262306a36Sopenharmony_ci	.write_regr	= on26_write_regr,
30362306a36Sopenharmony_ci	.read_regr	= on26_read_regr,
30462306a36Sopenharmony_ci	.write_block	= on26_write_block,
30562306a36Sopenharmony_ci	.read_block	= on26_read_block,
30662306a36Sopenharmony_ci	.connect	= on26_connect,
30762306a36Sopenharmony_ci	.disconnect	= on26_disconnect,
30862306a36Sopenharmony_ci	.test_port	= on26_test_port,
30962306a36Sopenharmony_ci	.log_adapter	= on26_log_adapter,
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
31362306a36Sopenharmony_ciMODULE_AUTHOR("Grant R. Guenther <grant@torque.net>");
31462306a36Sopenharmony_ciMODULE_DESCRIPTION("Onspec 90c26 parallel port IDE adapter protocol driver");
31562306a36Sopenharmony_cimodule_pata_parport_driver(on26);
316