/* backpack.c (c) 2001 Micro Solutions Inc. Released under the terms of the GNU General Public license backpack.c is a low-level protocol driver for the Micro Solutions "BACKPACK" parallel port IDE adapter (Works on Series 6 drives) Written by: Ken Hahn (linux-dev@micro-solutions.com) Clive Turvey (linux-dev@micro-solutions.com) */ #include #include #include #include #include #include "pata_parport.h" #include "ppc6lnx.c" static void bpck6_send_cmd(struct pi_adapter *pi, u8 cmd) { switch (mode_map[pi->mode]) { case PPCMODE_UNI_SW: case PPCMODE_UNI_FW: case PPCMODE_BI_SW: case PPCMODE_BI_FW: parport_write_data(pi->pardev->port, cmd); parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_AUTOFD); break; case PPCMODE_EPP_BYTE: case PPCMODE_EPP_WORD: case PPCMODE_EPP_DWORD: pi->pardev->port->ops->epp_write_addr(pi->pardev->port, &cmd, 1, 0); break; } } static u8 bpck6_rd_data_byte(struct pi_adapter *pi) { u8 data = 0; switch (mode_map[pi->mode]) { case PPCMODE_UNI_SW: case PPCMODE_UNI_FW: parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_INIT); data = parport_read_status(pi->pardev->port); data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3); parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE); data |= parport_read_status(pi->pardev->port) & 0xB8; break; case PPCMODE_BI_SW: case PPCMODE_BI_FW: parport_data_reverse(pi->pardev->port); parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT); data = parport_read_data(pi->pardev->port); parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, 0); parport_data_forward(pi->pardev->port); break; case PPCMODE_EPP_BYTE: case PPCMODE_EPP_WORD: case PPCMODE_EPP_DWORD: pi->pardev->port->ops->epp_read_data(pi->pardev->port, &data, 1, 0); break; } return data; } static void bpck6_wr_data_byte(struct pi_adapter *pi, u8 data) { switch (mode_map[pi->mode]) { case PPCMODE_UNI_SW: case PPCMODE_UNI_FW: case PPCMODE_BI_SW: case PPCMODE_BI_FW: parport_write_data(pi->pardev->port, data); parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_INIT); break; case PPCMODE_EPP_BYTE: case PPCMODE_EPP_WORD: case PPCMODE_EPP_DWORD: pi->pardev->port->ops->epp_write_data(pi->pardev->port, &data, 1, 0); break; } } static int bpck6_read_regr(struct pi_adapter *pi, int cont, int reg) { u8 port = cont ? reg | 8 : reg; bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_READ); return bpck6_rd_data_byte(pi); } static void bpck6_write_regr(struct pi_adapter *pi, int cont, int reg, int val) { u8 port = cont ? reg | 8 : reg; bpck6_send_cmd(pi, port | ACCESS_PORT | ACCESS_WRITE); bpck6_wr_data_byte(pi, val); } static void bpck6_wait_for_fifo(struct pi_adapter *pi) { int i; if (pi->private & fifo_wait) { for (i = 0; i < 20; i++) parport_read_status(pi->pardev->port); } } static void bpck6_write_block(struct pi_adapter *pi, char *buf, int len) { u8 this, last; bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE); bpck6_wr_data_byte(pi, (u8)len); bpck6_wr_data_byte(pi, (u8)(len >> 8)); bpck6_wr_data_byte(pi, 0); bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK); bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_WRITE); switch (mode_map[pi->mode]) { case PPCMODE_UNI_SW: case PPCMODE_BI_SW: while (len--) { parport_write_data(pi->pardev->port, *buf++); parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_INIT); } break; case PPCMODE_UNI_FW: case PPCMODE_BI_FW: bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_FASTWR); parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE); last = *buf; parport_write_data(pi->pardev->port, last); while (len) { this = *buf++; len--; if (this == last) { parport_frob_control(pi->pardev->port, 0, PARPORT_CONTROL_INIT); } else { parport_write_data(pi->pardev->port, this); last = this; } } parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, 0); bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_FASTWR); break; case PPCMODE_EPP_BYTE: pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf, len, PARPORT_EPP_FAST_8); bpck6_wait_for_fifo(pi); break; case PPCMODE_EPP_WORD: pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf, len, PARPORT_EPP_FAST_16); bpck6_wait_for_fifo(pi); break; case PPCMODE_EPP_DWORD: pi->pardev->port->ops->epp_write_data(pi->pardev->port, buf, len, PARPORT_EPP_FAST_32); bpck6_wait_for_fifo(pi); break; } bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK); } static void bpck6_read_block(struct pi_adapter *pi, char *buf, int len) { bpck6_send_cmd(pi, REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE); bpck6_wr_data_byte(pi, (u8)len); bpck6_wr_data_byte(pi, (u8)(len >> 8)); bpck6_wr_data_byte(pi, 0); bpck6_send_cmd(pi, CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK); bpck6_send_cmd(pi, ATA_REG_DATA | ACCESS_PORT | ACCESS_READ); switch (mode_map[pi->mode]) { case PPCMODE_UNI_SW: case PPCMODE_UNI_FW: while (len) { u8 d; parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_INIT); /* DATA STROBE */ d = parport_read_status(pi->pardev->port); d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3); parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE); d |= parport_read_status(pi->pardev->port) & 0xB8; *buf++ = d; len--; } break; case PPCMODE_BI_SW: case PPCMODE_BI_FW: parport_data_reverse(pi->pardev->port); while (len) { parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT); *buf++ = parport_read_data(pi->pardev->port); len--; } parport_frob_control(pi->pardev->port, PARPORT_CONTROL_STROBE, 0); parport_data_forward(pi->pardev->port); break; case PPCMODE_EPP_BYTE: pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len, PARPORT_EPP_FAST_8); break; case PPCMODE_EPP_WORD: pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len, PARPORT_EPP_FAST_16); break; case PPCMODE_EPP_DWORD: pi->pardev->port->ops->epp_read_data(pi->pardev->port, buf, len, PARPORT_EPP_FAST_32); break; } bpck6_send_cmd(pi, CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK); } static int bpck6_open(struct pi_adapter *pi) { u8 i, j, k; pi->saved_r0 = parport_read_data(pi->pardev->port); pi->saved_r2 = parport_read_control(pi->pardev->port) & 0x5F; parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, PARPORT_CONTROL_SELECT); if (pi->saved_r0 == 'b') parport_write_data(pi->pardev->port, 'x'); parport_write_data(pi->pardev->port, 'b'); parport_write_data(pi->pardev->port, 'p'); parport_write_data(pi->pardev->port, pi->unit); parport_write_data(pi->pardev->port, ~pi->unit); parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0); parport_write_control(pi->pardev->port, PARPORT_CONTROL_INIT); i = mode_map[pi->mode] & 0x0C; if (i == 0) i = (mode_map[pi->mode] & 2) | 1; parport_write_data(pi->pardev->port, i); parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, PARPORT_CONTROL_SELECT); parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, PARPORT_CONTROL_AUTOFD); j = ((i & 0x08) << 4) | ((i & 0x07) << 3); k = parport_read_status(pi->pardev->port) & 0xB8; if (j == k) { parport_frob_control(pi->pardev->port, PARPORT_CONTROL_AUTOFD, 0); k = (parport_read_status(pi->pardev->port) & 0xB8) ^ 0xB8; if (j == k) { if (i & 4) // EPP parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT, 0); else // PPC/ECP parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, 0); pi->private = 0; bpck6_send_cmd(pi, ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE); bpck6_wr_data_byte(pi, RAMSIZE_128K); bpck6_send_cmd(pi, ACCESS_REG | ACCESS_READ | REG_VERSION); if ((bpck6_rd_data_byte(pi) & 0x3F) == 0x0C) pi->private |= fifo_wait; return 1; } } parport_write_control(pi->pardev->port, pi->saved_r2); parport_write_data(pi->pardev->port, pi->saved_r0); return 0; // FAIL } static void bpck6_deselect(struct pi_adapter *pi) { if (mode_map[pi->mode] & 4) // EPP parport_frob_control(pi->pardev->port, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT); else // PPC/ECP parport_frob_control(pi->pardev->port, PARPORT_CONTROL_SELECT, PARPORT_CONTROL_SELECT); parport_write_data(pi->pardev->port, pi->saved_r0); parport_write_control(pi->pardev->port, pi->saved_r2 | PARPORT_CONTROL_SELECT); parport_write_control(pi->pardev->port, pi->saved_r2); } static void bpck6_wr_extout(struct pi_adapter *pi, u8 regdata) { bpck6_send_cmd(pi, REG_VERSION | ACCESS_REG | ACCESS_WRITE); bpck6_wr_data_byte(pi, (u8)((regdata & 0x03) << 6)); } static void bpck6_connect(struct pi_adapter *pi) { dev_dbg(&pi->dev, "connect\n"); bpck6_open(pi); bpck6_wr_extout(pi, 0x3); } static void bpck6_disconnect(struct pi_adapter *pi) { dev_dbg(&pi->dev, "disconnect\n"); bpck6_wr_extout(pi, 0x0); bpck6_deselect(pi); } static int bpck6_test_port(struct pi_adapter *pi) /* check for 8-bit port */ { dev_dbg(&pi->dev, "PARPORT indicates modes=%x for lp=0x%lx\n", pi->pardev->port->modes, pi->pardev->port->base); /* look at the parport device to see what modes we can use */ if (pi->pardev->port->modes & PARPORT_MODE_EPP) return 5; /* Can do EPP */ if (pi->pardev->port->modes & PARPORT_MODE_TRISTATE) return 2; return 1; /* Just flat SPP */ } static int bpck6_probe_unit(struct pi_adapter *pi) { int out, saved_mode; dev_dbg(&pi->dev, "PROBE UNIT %x on port:%x\n", pi->unit, pi->port); saved_mode = pi->mode; /*LOWER DOWN TO UNIDIRECTIONAL*/ pi->mode = 0; out = bpck6_open(pi); dev_dbg(&pi->dev, "ppc_open returned %2x\n", out); if(out) { bpck6_deselect(pi); dev_dbg(&pi->dev, "leaving probe\n"); pi->mode = saved_mode; return(1); } else { dev_dbg(&pi->dev, "Failed open\n"); pi->mode = saved_mode; return(0); } } static void bpck6_log_adapter(struct pi_adapter *pi) { char *mode_string[5]= {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"}; dev_info(&pi->dev, "Micro Solutions BACKPACK Drive unit %d at 0x%x, mode:%d (%s), delay %d\n", pi->unit, pi->port, pi->mode, mode_string[pi->mode], pi->delay); } static struct pi_protocol bpck6 = { .owner = THIS_MODULE, .name = "bpck6", .max_mode = 5, .epp_first = 2, /* 2-5 use epp (need 8 ports) */ .max_units = 255, .write_regr = bpck6_write_regr, .read_regr = bpck6_read_regr, .write_block = bpck6_write_block, .read_block = bpck6_read_block, .connect = bpck6_connect, .disconnect = bpck6_disconnect, .test_port = bpck6_test_port, .probe_unit = bpck6_probe_unit, .log_adapter = bpck6_log_adapter, }; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Micro Solutions Inc."); MODULE_DESCRIPTION("BACKPACK Protocol module, compatible with PARIDE"); module_pata_parport_driver(bpck6);