465 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * (c) 2001 Micro Solutions Inc.4 *5 * backpack.c is a low-level protocol driver for the Micro Solutions6 * "BACKPACK" parallel port IDE adapter (works on Series 6 drives).7 *8 * Written by: Ken Hahn (linux-dev@micro-solutions.com)9 * Clive Turvey (linux-dev@micro-solutions.com)10 */11 12#include <linux/module.h>13#include <linux/init.h>14#include <linux/kernel.h>15#include <linux/types.h>16#include <linux/parport.h>17#include "pata_parport.h"18 19/* 60772 Commands */20#define ACCESS_REG 0x0021#define ACCESS_PORT 0x4022 23#define ACCESS_READ 0x0024#define ACCESS_WRITE 0x2025 26/* 60772 Command Prefix */27#define CMD_PREFIX_SET 0xe0 // Special command that modifies next command's operation28#define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits29 #define PREFIX_IO16 0x01 // perform 16-bit wide I/O30 #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write31 #define PREFIX_BLK 0x08 // enable block transfer mode32 33/* 60772 Registers */34#define REG_STATUS 0x00 // status register35 #define STATUS_IRQA 0x01 // Peripheral IRQA line36 #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit37#define REG_VERSION 0x01 // PPC version register (read)38#define REG_HWCFG 0x02 // Hardware Config register39#define REG_RAMSIZE 0x03 // Size of RAM Buffer40 #define RAMSIZE_128K 0x0241#define REG_EEPROM 0x06 // EEPROM control register42 #define EEPROM_SK 0x01 // eeprom SK bit43 #define EEPROM_DI 0x02 // eeprom DI bit44 #define EEPROM_CS 0x04 // eeprom CS bit45 #define EEPROM_EN 0x08 // eeprom output enable46#define REG_BLKSIZE 0x08 // Block transfer len (24 bit)47 48/* flags */49#define fifo_wait 0x1050 51/* DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES */52#define PPCMODE_UNI_SW 053#define PPCMODE_UNI_FW 154#define PPCMODE_BI_SW 255#define PPCMODE_BI_FW 356#define PPCMODE_EPP_BYTE 457#define PPCMODE_EPP_WORD 558#define PPCMODE_EPP_DWORD 659 60static int mode_map[] = { PPCMODE_UNI_FW, PPCMODE_BI_FW, PPCMODE_EPP_BYTE,61 PPCMODE_EPP_WORD, PPCMODE_EPP_DWORD };62 63static void bpck6_send_cmd(struct pi_adapter *pi, u8 cmd)64{65 switch (mode_map[pi->mode]) {66 case PPCMODE_UNI_SW:67 case PPCMODE_UNI_FW:68 case PPCMODE_BI_SW:69 case PPCMODE_BI_FW:70 parport_write_data(pi->pardev->port, cmd);71 parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_AUTOFD);72 break;73 case PPCMODE_EPP_BYTE:74 case PPCMODE_EPP_WORD:75 case PPCMODE_EPP_DWORD:76 pi->pardev->port->ops->epp_write_addr(pi->pardev->port, &cmd, 1, 0);77 break;78 }79}80 81static u8 bpck6_rd_data_byte(struct pi_adapter *pi)82{83 u8 data = 0;84 85 switch (mode_map[pi->mode]) {86 case PPCMODE_UNI_SW:87 case PPCMODE_UNI_FW:88 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,89 PARPORT_CONTROL_INIT);90 data = parport_read_status(pi->pardev->port);91 data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);92 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,93 PARPORT_CONTROL_STROBE);94 data |= parport_read_status(pi->pardev->port) & 0xB8;95 break;96 case PPCMODE_BI_SW:97 case PPCMODE_BI_FW:98 parport_data_reverse(pi->pardev->port);99 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,100 PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT);101 data = parport_read_data(pi->pardev->port);102 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, 0);103 parport_data_forward(pi->pardev->port);104 break;105 case PPCMODE_EPP_BYTE:106 case PPCMODE_EPP_WORD:107 case PPCMODE_EPP_DWORD:108 pi->pardev->port->ops->epp_read_data(pi->pardev->port, &data, 1, 0);109 break;110 }111 112 return data;113}114 115static void bpck6_wr_data_byte(struct pi_adapter *pi, u8 data)116{117 switch (mode_map[pi->mode]) {118 case PPCMODE_UNI_SW:119 case PPCMODE_UNI_FW:120 case PPCMODE_BI_SW:121 case PPCMODE_BI_FW:122 parport_write_data(pi->pardev->port, data);123 parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_INIT);124 break;125 case PPCMODE_EPP_BYTE:126 case PPCMODE_EPP_WORD:127 case PPCMODE_EPP_DWORD:128 pi->pardev->port->ops->epp_write_data(pi->pardev->port, &data, 1, 0);129 break;130 }131}132 133static int bpck6_read_regr(struct pi_adapter *pi, int cont, int reg)134{135 u8 port = cont ? reg | 8 : reg;136 137 bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_READ);138 return bpck6_rd_data_byte(pi);139}140 141static void bpck6_write_regr(struct pi_adapter *pi, int cont, int reg, int val)142{143 u8 port = cont ? reg | 8 : reg;144 145 bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_WRITE);146 bpck6_wr_data_byte(pi, val);147}148 149static void bpck6_wait_for_fifo(struct pi_adapter *pi)150{151 int i;152 153 if (pi->private & fifo_wait) {154 for (i = 0; i < 20; i++)155 parport_read_status(pi->pardev->port);156 }157}158 159static void bpck6_write_block(struct pi_adapter *pi, char *buf, int len)160{161 u8 this, last;162 163 bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);164 bpck6_wr_data_byte(pi, (u8)len);165 bpck6_wr_data_byte(pi, (u8)(len >> 8));166 bpck6_wr_data_byte(pi, 0);167 168 bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);169 bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_WRITE);170 171 switch (mode_map[pi->mode]) {172 case PPCMODE_UNI_SW:173 case PPCMODE_BI_SW:174 while (len--) {175 parport_write_data(pi->pardev->port, *buf++);176 parport_frob_control(pi->pardev->port, 0,177 PARPORT_CONTROL_INIT);178 }179 break;180 case PPCMODE_UNI_FW:181 case PPCMODE_BI_FW:182 bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_FASTWR);183 184 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,185 PARPORT_CONTROL_STROBE);186 187 last = *buf;188 189 parport_write_data(pi->pardev->port, last);190 191 while (len) {192 this = *buf++;193 len--;194 195 if (this == last) {196 parport_frob_control(pi->pardev->port, 0,197 PARPORT_CONTROL_INIT);198 } else {199 parport_write_data(pi->pardev->port, this);200 last = this;201 }202 }203 204 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,205 0);206 bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_FASTWR);207 break;208 case PPCMODE_EPP_BYTE:209 pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,210 len, PARPORT_EPP_FAST_8);211 bpck6_wait_for_fifo(pi);212 break;213 case PPCMODE_EPP_WORD:214 pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,215 len, PARPORT_EPP_FAST_16);216 bpck6_wait_for_fifo(pi);217 break;218 case PPCMODE_EPP_DWORD:219 pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf,220 len, PARPORT_EPP_FAST_32);221 bpck6_wait_for_fifo(pi);222 break;223 }224 225 bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);226}227 228static void bpck6_read_block(struct pi_adapter *pi, char *buf, int len)229{230 bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE);231 bpck6_wr_data_byte(pi, (u8)len);232 bpck6_wr_data_byte(pi, (u8)(len >> 8));233 bpck6_wr_data_byte(pi, 0);234 235 bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK);236 bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_READ);237 238 switch (mode_map[pi->mode]) {239 case PPCMODE_UNI_SW:240 case PPCMODE_UNI_FW:241 while (len) {242 u8 d;243 244 parport_frob_control(pi->pardev->port,245 PARPORT_CONTROL_STROBE,246 PARPORT_CONTROL_INIT); /* DATA STROBE */247 d = parport_read_status(pi->pardev->port);248 d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);249 parport_frob_control(pi->pardev->port,250 PARPORT_CONTROL_STROBE,251 PARPORT_CONTROL_STROBE);252 d |= parport_read_status(pi->pardev->port) & 0xB8;253 *buf++ = d;254 len--;255 }256 break;257 case PPCMODE_BI_SW:258 case PPCMODE_BI_FW:259 parport_data_reverse(pi->pardev->port);260 while (len) {261 parport_frob_control(pi->pardev->port,262 PARPORT_CONTROL_STROBE,263 PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT);264 *buf++ = parport_read_data(pi->pardev->port);265 len--;266 }267 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE,268 0);269 parport_data_forward(pi->pardev->port);270 break;271 case PPCMODE_EPP_BYTE:272 pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,273 PARPORT_EPP_FAST_8);274 break;275 case PPCMODE_EPP_WORD:276 pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,277 PARPORT_EPP_FAST_16);278 break;279 case PPCMODE_EPP_DWORD:280 pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len,281 PARPORT_EPP_FAST_32);282 break;283 }284 285 bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK);286}287 288static int bpck6_open(struct pi_adapter *pi)289{290 u8 i, j, k;291 292 pi->saved_r0 = parport_read_data(pi->pardev->port);293 pi->saved_r2 = parport_read_control(pi->pardev->port) & 0x5F;294 295 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,296 PARPORT_CONTROL_SELECT);297 if (pi->saved_r0 == 'b')298 parport_write_data(pi->pardev->port, 'x');299 parport_write_data(pi->pardev->port, 'b');300 parport_write_data(pi->pardev->port, 'p');301 parport_write_data(pi->pardev->port, pi->unit);302 parport_write_data(pi->pardev->port, ~pi->unit);303 304 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0);305 parport_write_control(pi->pardev->port, PARPORT_CONTROL_INIT);306 307 i = mode_map[pi->mode] & 0x0C;308 if (i == 0)309 i = (mode_map[pi->mode] & 2) | 1;310 parport_write_data(pi->pardev->port, i);311 312 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,313 PARPORT_CONTROL_SELECT);314 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD,315 PARPORT_CONTROL_AUTOFD);316 317 j = ((i & 0x08) << 4) | ((i & 0x07) << 3);318 k = parport_read_status(pi->pardev->port) & 0xB8;319 if (j != k)320 goto fail;321 322 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, 0);323 k = (parport_read_status(pi->pardev->port) & 0xB8) ^ 0xB8;324 if (j != k)325 goto fail;326 327 if (i & 4) {328 /* EPP */329 parport_frob_control(pi->pardev->port,330 PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, 0);331 } else {332 /* PPC/ECP */333 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0);334 }335 336 pi->private = 0;337 338 bpck6_send_cmd(pi, ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE);339 bpck6_wr_data_byte(pi, RAMSIZE_128K);340 341 bpck6_send_cmd(pi, ACCESS_REG | ACCESS_READ | REG_VERSION);342 if ((bpck6_rd_data_byte(pi) & 0x3F) == 0x0C)343 pi->private |= fifo_wait;344 345 return 1;346 347fail:348 parport_write_control(pi->pardev->port, pi->saved_r2);349 parport_write_data(pi->pardev->port, pi->saved_r0);350 351 return 0;352}353 354static void bpck6_deselect(struct pi_adapter *pi)355{356 if (mode_map[pi->mode] & 4) {357 /* EPP */358 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_INIT,359 PARPORT_CONTROL_INIT);360 } else {361 /* PPC/ECP */362 parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT,363 PARPORT_CONTROL_SELECT);364 }365 366 parport_write_data(pi->pardev->port, pi->saved_r0);367 parport_write_control(pi->pardev->port,368 pi->saved_r2 | PARPORT_CONTROL_SELECT);369 parport_write_control(pi->pardev->port, pi->saved_r2);370}371 372static void bpck6_wr_extout(struct pi_adapter *pi, u8 regdata)373{374 bpck6_send_cmd(pi, REG_VERSION | ACCESS_REG | ACCESS_WRITE);375 bpck6_wr_data_byte(pi, (u8)((regdata & 0x03) << 6));376}377 378static void bpck6_connect(struct pi_adapter *pi)379{380 dev_dbg(&pi->dev, "connect\n");381 382 bpck6_open(pi);383 bpck6_wr_extout(pi, 0x3);384}385 386static void bpck6_disconnect(struct pi_adapter *pi)387{388 dev_dbg(&pi->dev, "disconnect\n");389 bpck6_wr_extout(pi, 0x0);390 bpck6_deselect(pi);391}392 393/* check for 8-bit port */394static int bpck6_test_port(struct pi_adapter *pi)395{396 dev_dbg(&pi->dev, "PARPORT indicates modes=%x for lp=0x%lx\n",397 pi->pardev->port->modes, pi->pardev->port->base);398 399 /* look at the parport device to see what modes we can use */400 if (pi->pardev->port->modes & PARPORT_MODE_EPP)401 return 5; /* Can do EPP */402 if (pi->pardev->port->modes & PARPORT_MODE_TRISTATE)403 return 2;404 return 1; /* Just flat SPP */405}406 407static int bpck6_probe_unit(struct pi_adapter *pi)408{409 int out, saved_mode;410 411 dev_dbg(&pi->dev, "PROBE UNIT %x on port:%x\n", pi->unit, pi->port);412 413 saved_mode = pi->mode;414 /*LOWER DOWN TO UNIDIRECTIONAL*/415 pi->mode = 0;416 417 out = bpck6_open(pi);418 419 dev_dbg(&pi->dev, "ppc_open returned %2x\n", out);420 421 if (out) {422 bpck6_deselect(pi);423 dev_dbg(&pi->dev, "leaving probe\n");424 pi->mode = saved_mode;425 return 1;426 }427 428 dev_dbg(&pi->dev, "Failed open\n");429 pi->mode = saved_mode;430 431 return 0;432}433 434static void bpck6_log_adapter(struct pi_adapter *pi)435{436 char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" };437 438 dev_info(&pi->dev,439 "Micro Solutions BACKPACK Drive unit %d at 0x%x, mode:%d (%s), delay %d\n",440 pi->unit, pi->port, pi->mode, mode_string[pi->mode], pi->delay);441}442 443static struct pi_protocol bpck6 = {444 .owner = THIS_MODULE,445 .name = "bpck6",446 .max_mode = 5,447 .epp_first = 2, /* 2-5 use epp (need 8 ports) */448 .max_units = 255,449 .write_regr = bpck6_write_regr,450 .read_regr = bpck6_read_regr,451 .write_block = bpck6_write_block,452 .read_block = bpck6_read_block,453 .connect = bpck6_connect,454 .disconnect = bpck6_disconnect,455 .test_port = bpck6_test_port,456 .probe_unit = bpck6_probe_unit,457 .log_adapter = bpck6_log_adapter,458};459 460MODULE_LICENSE("GPL");461MODULE_AUTHOR("Micro Solutions Inc.");462MODULE_DESCRIPTION("Micro Solutions BACKPACK parallel port IDE adapter "463 "(version 6 drives) protocol driver");464module_pata_parport_driver(bpck6);465