1*8be31971SDamien Le Moal // SPDX-License-Identifier: GPL-2.0-or-later 272f2b0b2SOndrej Zary /* 3*8be31971SDamien Le Moal * (c) 1997-1998 Grant R. Guenther <grant@torque.net> 4*8be31971SDamien Le Moal * 5*8be31971SDamien Le Moal * epia.c is a low-level protocol driver for Shuttle Technologies 6*8be31971SDamien Le Moal * EPIA parallel to IDE adapter chip. This device is now obsolete 7*8be31971SDamien Le Moal * and has been replaced with the EPAT chip, which is supported 8*8be31971SDamien Le Moal * by epat.c, however, some devices based on EPIA are still 9*8be31971SDamien Le Moal * available. 1072f2b0b2SOndrej Zary */ 1172f2b0b2SOndrej Zary 1272f2b0b2SOndrej Zary #include <linux/module.h> 1372f2b0b2SOndrej Zary #include <linux/init.h> 1472f2b0b2SOndrej Zary #include <linux/delay.h> 1572f2b0b2SOndrej Zary #include <linux/kernel.h> 1672f2b0b2SOndrej Zary #include <linux/types.h> 1772f2b0b2SOndrej Zary #include <linux/wait.h> 1872f2b0b2SOndrej Zary #include <asm/io.h> 19fe027ff9SOndrej Zary #include "pata_parport.h" 2072f2b0b2SOndrej Zary 21*8be31971SDamien Le Moal /* 22*8be31971SDamien Le Moal * mode codes: 0 nybble reads on port 1, 8-bit writes 23*8be31971SDamien Le Moal * 1 5/3 reads on ports 1 & 2, 8-bit writes 24*8be31971SDamien Le Moal * 2 8-bit reads and writes 25*8be31971SDamien Le Moal * 3 8-bit EPP mode 26*8be31971SDamien Le Moal * 4 16-bit EPP 27*8be31971SDamien Le Moal * 5 32-bit EPP 2872f2b0b2SOndrej Zary */ 2972f2b0b2SOndrej Zary 3072f2b0b2SOndrej Zary #define j44(a, b) (((a >> 4) & 0x0f) + (b & 0xf0)) 3172f2b0b2SOndrej Zary #define j53(a, b) (((a >> 3) & 0x1f) + ((b << 4) & 0xe0)) 3272f2b0b2SOndrej Zary 33*8be31971SDamien Le Moal /* 34*8be31971SDamien Le Moal * cont = 0 IDE register file 35*8be31971SDamien Le Moal * cont = 1 IDE control registers 3672f2b0b2SOndrej Zary */ 3772f2b0b2SOndrej Zary static int cont_map[2] = { 0, 0x80 }; 3872f2b0b2SOndrej Zary 39882ff0caSOndrej Zary static int epia_read_regr(struct pi_adapter *pi, int cont, int regr) 40*8be31971SDamien Le Moal { 41*8be31971SDamien Le Moal int a, b, r; 4272f2b0b2SOndrej Zary 4372f2b0b2SOndrej Zary regr += cont_map[cont]; 4472f2b0b2SOndrej Zary 4572f2b0b2SOndrej Zary switch (pi->mode) { 46*8be31971SDamien Le Moal case 0: 47*8be31971SDamien Le Moal r = regr ^ 0x39; 4872f2b0b2SOndrej Zary w0(r); w2(1); w2(3); w0(r); 4972f2b0b2SOndrej Zary a = r1(); w2(1); b = r1(); w2(4); 5072f2b0b2SOndrej Zary return j44(a, b); 51*8be31971SDamien Le Moal case 1: 52*8be31971SDamien Le Moal r = regr ^ 0x31; 5372f2b0b2SOndrej Zary w0(r); w2(1); w0(r & 0x37); 5472f2b0b2SOndrej Zary w2(3); w2(5); w0(r | 0xf0); 5572f2b0b2SOndrej Zary a = r1(); b = r2(); w2(4); 5672f2b0b2SOndrej Zary return j53(a, b); 57*8be31971SDamien Le Moal case 2: 58*8be31971SDamien Le Moal r = regr^0x29; 5972f2b0b2SOndrej Zary w0(r); w2(1); w2(0X21); w2(0x23); 6072f2b0b2SOndrej Zary a = r0(); w2(4); 6172f2b0b2SOndrej Zary return a; 6272f2b0b2SOndrej Zary case 3: 6372f2b0b2SOndrej Zary case 4: 64*8be31971SDamien Le Moal case 5: 65*8be31971SDamien Le Moal w3(regr); w2(0x24); a = r4(); w2(4); 6672f2b0b2SOndrej Zary return a; 6772f2b0b2SOndrej Zary } 68*8be31971SDamien Le Moal 6972f2b0b2SOndrej Zary return -1; 7072f2b0b2SOndrej Zary } 7172f2b0b2SOndrej Zary 72882ff0caSOndrej Zary static void epia_write_regr(struct pi_adapter *pi, int cont, int regr, int val) 73*8be31971SDamien Le Moal { 74*8be31971SDamien Le Moal int r; 7572f2b0b2SOndrej Zary 7672f2b0b2SOndrej Zary regr += cont_map[cont]; 7772f2b0b2SOndrej Zary 7872f2b0b2SOndrej Zary switch (pi->mode) { 7972f2b0b2SOndrej Zary case 0: 8072f2b0b2SOndrej Zary case 1: 81*8be31971SDamien Le Moal case 2: 82*8be31971SDamien Le Moal r = regr ^ 0x19; 8372f2b0b2SOndrej Zary w0(r); w2(1); w0(val); w2(3); w2(4); 8472f2b0b2SOndrej Zary break; 8572f2b0b2SOndrej Zary case 3: 8672f2b0b2SOndrej Zary case 4: 87*8be31971SDamien Le Moal case 5: 88*8be31971SDamien Le Moal r = regr ^ 0x40; 8972f2b0b2SOndrej Zary w3(r); w4(val); w2(4); 9072f2b0b2SOndrej Zary break; 9172f2b0b2SOndrej Zary } 9272f2b0b2SOndrej Zary } 9372f2b0b2SOndrej Zary 9472f2b0b2SOndrej Zary #define WR(r, v) epia_write_regr(pi, 0, r, v) 95*8be31971SDamien Le Moal #define RR(r) epia_read_regr(pi, 0, r) 9672f2b0b2SOndrej Zary 97*8be31971SDamien Le Moal /* 98*8be31971SDamien Le Moal * The use of register 0x84 is entirely unclear - it seems to control 99*8be31971SDamien Le Moal * some EPP counters ... currently we know about 3 different block 100*8be31971SDamien Le Moal * sizes: the standard 512 byte reads and writes, 12 byte writes and 101*8be31971SDamien Le Moal * 2048 byte reads (the last two being used in the CDrom drivers. 10272f2b0b2SOndrej Zary */ 103882ff0caSOndrej Zary static void epia_connect(struct pi_adapter *pi) 104*8be31971SDamien Le Moal { 105*8be31971SDamien Le Moal pi->saved_r0 = r0(); 10672f2b0b2SOndrej Zary pi->saved_r2 = r2(); 10772f2b0b2SOndrej Zary 10872f2b0b2SOndrej Zary w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0); 10972f2b0b2SOndrej Zary w2(1); w2(4); 11072f2b0b2SOndrej Zary if (pi->mode >= 3) { 11172f2b0b2SOndrej Zary w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4); 11272f2b0b2SOndrej Zary w2(0x24); w2(0x26); w2(4); 11372f2b0b2SOndrej Zary } 11472f2b0b2SOndrej Zary WR(0x86, 8); 11572f2b0b2SOndrej Zary } 11672f2b0b2SOndrej Zary 117882ff0caSOndrej Zary static void epia_disconnect(struct pi_adapter *pi) 118*8be31971SDamien Le Moal { 119*8be31971SDamien Le Moal /* WR(0x84,0x10); */ 12072f2b0b2SOndrej Zary w0(pi->saved_r0); 12172f2b0b2SOndrej Zary w2(1); w2(4); 12272f2b0b2SOndrej Zary w0(pi->saved_r0); 12372f2b0b2SOndrej Zary w2(pi->saved_r2); 12472f2b0b2SOndrej Zary } 12572f2b0b2SOndrej Zary 126882ff0caSOndrej Zary static void epia_read_block(struct pi_adapter *pi, char *buf, int count) 12772f2b0b2SOndrej Zary 128*8be31971SDamien Le Moal { 129*8be31971SDamien Le Moal int k, ph, a, b; 13072f2b0b2SOndrej Zary 13172f2b0b2SOndrej Zary switch (pi->mode) { 132*8be31971SDamien Le Moal case 0: 133*8be31971SDamien Le Moal w0(0x81); w2(1); w2(3); w0(0xc1); 13472f2b0b2SOndrej Zary ph = 1; 13572f2b0b2SOndrej Zary for (k = 0; k < count; k++) { 13672f2b0b2SOndrej Zary w2(2+ph); a = r1(); 13772f2b0b2SOndrej Zary w2(4+ph); b = r1(); 13872f2b0b2SOndrej Zary buf[k] = j44(a, b); 13972f2b0b2SOndrej Zary ph = 1 - ph; 14072f2b0b2SOndrej Zary } 14172f2b0b2SOndrej Zary w0(0); w2(4); 14272f2b0b2SOndrej Zary break; 143*8be31971SDamien Le Moal case 1: 144*8be31971SDamien Le Moal w0(0x91); w2(1); w0(0x10); w2(3); 14572f2b0b2SOndrej Zary w0(0x51); w2(5); w0(0xd1); 14672f2b0b2SOndrej Zary ph = 1; 14772f2b0b2SOndrej Zary for (k = 0; k < count; k++) { 14872f2b0b2SOndrej Zary w2(4 + ph); 14972f2b0b2SOndrej Zary a = r1(); b = r2(); 15072f2b0b2SOndrej Zary buf[k] = j53(a, b); 15172f2b0b2SOndrej Zary ph = 1 - ph; 15272f2b0b2SOndrej Zary } 15372f2b0b2SOndrej Zary w0(0); w2(4); 15472f2b0b2SOndrej Zary break; 155*8be31971SDamien Le Moal case 2: 156*8be31971SDamien Le Moal w0(0x89); w2(1); w2(0x23); w2(0x21); 15772f2b0b2SOndrej Zary ph = 1; 15872f2b0b2SOndrej Zary for (k = 0; k < count; k++) { 15972f2b0b2SOndrej Zary w2(0x24 + ph); 16072f2b0b2SOndrej Zary buf[k] = r0(); 16172f2b0b2SOndrej Zary ph = 1 - ph; 16272f2b0b2SOndrej Zary } 16372f2b0b2SOndrej Zary w2(6); w2(4); 16472f2b0b2SOndrej Zary break; 165*8be31971SDamien Le Moal case 3: 166*8be31971SDamien Le Moal if (count > 512) 167*8be31971SDamien Le Moal WR(0x84, 3); 16872f2b0b2SOndrej Zary w3(0); w2(0x24); 169*8be31971SDamien Le Moal for (k = 0; k < count; k++) 170*8be31971SDamien Le Moal buf[k] = r4(); 17172f2b0b2SOndrej Zary w2(4); WR(0x84, 0); 17272f2b0b2SOndrej Zary break; 173*8be31971SDamien Le Moal case 4: 174*8be31971SDamien Le Moal if (count > 512) 175*8be31971SDamien Le Moal WR(0x84, 3); 17672f2b0b2SOndrej Zary w3(0); w2(0x24); 177*8be31971SDamien Le Moal for (k = 0; k < count / 2; k++) 178*8be31971SDamien Le Moal ((u16 *)buf)[k] = r4w(); 17972f2b0b2SOndrej Zary w2(4); WR(0x84, 0); 18072f2b0b2SOndrej Zary break; 181*8be31971SDamien Le Moal case 5: 182*8be31971SDamien Le Moal if (count > 512) 183*8be31971SDamien Le Moal WR(0x84, 3); 18472f2b0b2SOndrej Zary w3(0); w2(0x24); 185*8be31971SDamien Le Moal for (k = 0; k < count / 4; k++) 186*8be31971SDamien Le Moal ((u32 *)buf)[k] = r4l(); 18772f2b0b2SOndrej Zary w2(4); WR(0x84, 0); 18872f2b0b2SOndrej Zary break; 18972f2b0b2SOndrej Zary } 19072f2b0b2SOndrej Zary } 19172f2b0b2SOndrej Zary 192882ff0caSOndrej Zary static void epia_write_block(struct pi_adapter *pi, char *buf, int count) 193*8be31971SDamien Le Moal { 194*8be31971SDamien Le Moal int ph, k, last, d; 19572f2b0b2SOndrej Zary 19672f2b0b2SOndrej Zary switch (pi->mode) { 19772f2b0b2SOndrej Zary case 0: 19872f2b0b2SOndrej Zary case 1: 199*8be31971SDamien Le Moal case 2: 200*8be31971SDamien Le Moal w0(0xa1); w2(1); w2(3); w2(1); w2(5); 20172f2b0b2SOndrej Zary ph = 0; last = 0x8000; 20272f2b0b2SOndrej Zary for (k = 0; k < count; k++) { 20372f2b0b2SOndrej Zary d = buf[k]; 204*8be31971SDamien Le Moal if (d != last) { 205*8be31971SDamien Le Moal last = d; 206*8be31971SDamien Le Moal w0(d); 207*8be31971SDamien Le Moal } 20872f2b0b2SOndrej Zary w2(4 + ph); 20972f2b0b2SOndrej Zary ph = 1 - ph; 21072f2b0b2SOndrej Zary } 21172f2b0b2SOndrej Zary w2(7); w2(4); 21272f2b0b2SOndrej Zary break; 213*8be31971SDamien Le Moal case 3: 214*8be31971SDamien Le Moal if (count < 512) 215*8be31971SDamien Le Moal WR(0x84, 1); 21672f2b0b2SOndrej Zary w3(0x40); 217*8be31971SDamien Le Moal for (k = 0; k < count; k++) 218*8be31971SDamien Le Moal w4(buf[k]); 219*8be31971SDamien Le Moal if (count < 512) 220*8be31971SDamien Le Moal WR(0x84, 0); 22172f2b0b2SOndrej Zary break; 222*8be31971SDamien Le Moal case 4: 223*8be31971SDamien Le Moal if (count < 512) 224*8be31971SDamien Le Moal WR(0x84, 1); 22572f2b0b2SOndrej Zary w3(0x40); 226*8be31971SDamien Le Moal for (k = 0; k < count / 2; k++) 227*8be31971SDamien Le Moal w4w(((u16 *)buf)[k]); 228*8be31971SDamien Le Moal if (count < 512) 229*8be31971SDamien Le Moal WR(0x84, 0); 23072f2b0b2SOndrej Zary break; 231*8be31971SDamien Le Moal case 5: 232*8be31971SDamien Le Moal if (count < 512) 233*8be31971SDamien Le Moal WR(0x84, 1); 23472f2b0b2SOndrej Zary w3(0x40); 235*8be31971SDamien Le Moal for (k = 0; k < count / 4; k++) 236*8be31971SDamien Le Moal w4l(((u32 *)buf)[k]); 237*8be31971SDamien Le Moal if (count < 512) 238*8be31971SDamien Le Moal WR(0x84, 0); 23972f2b0b2SOndrej Zary break; 24072f2b0b2SOndrej Zary } 24172f2b0b2SOndrej Zary } 24272f2b0b2SOndrej Zary 243b42251a8SOndrej Zary static int epia_test_proto(struct pi_adapter *pi) 244*8be31971SDamien Le Moal { 245*8be31971SDamien Le Moal int j, k, f; 24672f2b0b2SOndrej Zary int e[2] = { 0, 0 }; 247b42251a8SOndrej Zary char scratch[512]; 24872f2b0b2SOndrej Zary 24972f2b0b2SOndrej Zary epia_connect(pi); 25072f2b0b2SOndrej Zary for (j = 0; j < 2; j++) { 25172f2b0b2SOndrej Zary WR(6, 0xa0 + j * 0x10); 25272f2b0b2SOndrej Zary for (k = 0; k < 256; k++) { 25372f2b0b2SOndrej Zary WR(2, k ^ 0xaa); 25472f2b0b2SOndrej Zary WR(3, k ^ 0x55); 255*8be31971SDamien Le Moal if (RR(2) != (k ^ 0xaa)) 256*8be31971SDamien Le Moal e[j]++; 25772f2b0b2SOndrej Zary } 25872f2b0b2SOndrej Zary WR(2, 1); WR(3, 1); 25972f2b0b2SOndrej Zary } 26072f2b0b2SOndrej Zary epia_disconnect(pi); 26172f2b0b2SOndrej Zary 26272f2b0b2SOndrej Zary f = 0; 26372f2b0b2SOndrej Zary epia_connect(pi); 26472f2b0b2SOndrej Zary WR(0x84, 8); 26572f2b0b2SOndrej Zary epia_read_block(pi, scratch, 512); 26672f2b0b2SOndrej Zary for (k = 0; k < 256; k++) { 267*8be31971SDamien Le Moal if ((scratch[2 * k] & 0xff) != ((k + 1) & 0xff)) 268*8be31971SDamien Le Moal f++; 269*8be31971SDamien Le Moal if ((scratch[2 * k + 1] & 0xff) != ((-2 - k) & 0xff)) 270*8be31971SDamien Le Moal f++; 27172f2b0b2SOndrej Zary } 27272f2b0b2SOndrej Zary WR(0x84, 0); 27372f2b0b2SOndrej Zary epia_disconnect(pi); 27472f2b0b2SOndrej Zary 2755f1145d8SOndrej Zary dev_dbg(&pi->dev, "epia: port 0x%x, mode %d, test=(%d,%d,%d)\n", 276a4f2ff92SOndrej Zary pi->port, pi->mode, e[0], e[1], f); 27772f2b0b2SOndrej Zary 27872f2b0b2SOndrej Zary return (e[0] && e[1]) || f; 27972f2b0b2SOndrej Zary } 28072f2b0b2SOndrej Zary 28172f2b0b2SOndrej Zary 2825b77db9cSOndrej Zary static void epia_log_adapter(struct pi_adapter *pi) 283*8be31971SDamien Le Moal { 284*8be31971SDamien Le Moal char *mode[6] = { "4-bit", "5/3", "8-bit", "EPP-8", "EPP-16", "EPP-32"}; 28572f2b0b2SOndrej Zary 286*8be31971SDamien Le Moal dev_info(&pi->dev, 287*8be31971SDamien Le Moal "Shuttle EPIA at 0x%x, mode %d (%s), delay %d\n", 288*8be31971SDamien Le Moal pi->port, pi->mode, mode[pi->mode], pi->delay); 28972f2b0b2SOndrej Zary } 29072f2b0b2SOndrej Zary 29172f2b0b2SOndrej Zary static struct pi_protocol epia = { 29272f2b0b2SOndrej Zary .owner = THIS_MODULE, 29372f2b0b2SOndrej Zary .name = "epia", 29472f2b0b2SOndrej Zary .max_mode = 6, 29572f2b0b2SOndrej Zary .epp_first = 3, 29672f2b0b2SOndrej Zary .default_delay = 1, 29772f2b0b2SOndrej Zary .max_units = 1, 29872f2b0b2SOndrej Zary .write_regr = epia_write_regr, 29972f2b0b2SOndrej Zary .read_regr = epia_read_regr, 30072f2b0b2SOndrej Zary .write_block = epia_write_block, 30172f2b0b2SOndrej Zary .read_block = epia_read_block, 30272f2b0b2SOndrej Zary .connect = epia_connect, 30372f2b0b2SOndrej Zary .disconnect = epia_disconnect, 30472f2b0b2SOndrej Zary .test_proto = epia_test_proto, 30572f2b0b2SOndrej Zary .log_adapter = epia_log_adapter, 30672f2b0b2SOndrej Zary }; 30772f2b0b2SOndrej Zary 30872f2b0b2SOndrej Zary MODULE_LICENSE("GPL"); 3092c08ec0fSOndrej Zary module_pata_parport_driver(epia); 310