1/* 2 ppc6lnx.c (c) 2001 Micro Solutions Inc. 3 Released under the terms of the GNU General Public license 4 5 ppc6lnx.c is a par of the protocol driver for the Micro Solutions 6 "BACKPACK" parallel port IDE adapter 7 (Works on Series 6 drives) 8 9*/ 10 11//*************************************************************************** 12 13// PPC 6 Code in C sanitized for LINUX 14// Original x86 ASM by Ron, Converted to C by Clive 15 16//*************************************************************************** 17 18 19#define port_stb 1 20#define port_afd 2 21#define cmd_stb port_afd 22#define port_init 4 23#define data_stb port_init 24#define port_sel 8 25#define port_int 16 26#define port_dir 0x20 27 28#define ECR_EPP 0x80 29#define ECR_BI 0x20 30 31//*************************************************************************** 32 33// 60772 Commands 34 35#define ACCESS_REG 0x00 36#define ACCESS_PORT 0x40 37 38#define ACCESS_READ 0x00 39#define ACCESS_WRITE 0x20 40 41// 60772 Command Prefix 42 43#define CMD_PREFIX_SET 0xe0 // Special command that modifies the next command's operation 44#define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits 45 #define PREFIX_IO16 0x01 // perform 16-bit wide I/O 46 #define PREFIX_FASTWR 0x04 // enable PPC mode fast-write 47 #define PREFIX_BLK 0x08 // enable block transfer mode 48 49// 60772 Registers 50 51#define REG_STATUS 0x00 // status register 52 #define STATUS_IRQA 0x01 // Peripheral IRQA line 53 #define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit 54#define REG_VERSION 0x01 // PPC version register (read) 55#define REG_HWCFG 0x02 // Hardware Config register 56#define REG_RAMSIZE 0x03 // Size of RAM Buffer 57 #define RAMSIZE_128K 0x02 58#define REG_EEPROM 0x06 // EEPROM control register 59 #define EEPROM_SK 0x01 // eeprom SK bit 60 #define EEPROM_DI 0x02 // eeprom DI bit 61 #define EEPROM_CS 0x04 // eeprom CS bit 62 #define EEPROM_EN 0x08 // eeprom output enable 63#define REG_BLKSIZE 0x08 // Block transfer len (24 bit) 64 65//*************************************************************************** 66 67typedef struct ppc_storage { 68 u16 lpt_addr; // LPT base address 69 u8 ppc_id; 70 u8 mode; // operating mode 71 // 0 = PPC Uni SW 72 // 1 = PPC Uni FW 73 // 2 = PPC Bi SW 74 // 3 = PPC Bi FW 75 // 4 = EPP Byte 76 // 5 = EPP Word 77 // 6 = EPP Dword 78 u8 ppc_flags; 79 u8 org_data; // original LPT data port contents 80 u8 org_ctrl; // original LPT control port contents 81 u8 cur_ctrl; // current control port contents 82} Interface; 83 84//*************************************************************************** 85 86// ppc_flags 87 88#define fifo_wait 0x10 89 90//*************************************************************************** 91 92// DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES 93 94#define PPCMODE_UNI_SW 0 95#define PPCMODE_UNI_FW 1 96#define PPCMODE_BI_SW 2 97#define PPCMODE_BI_FW 3 98#define PPCMODE_EPP_BYTE 4 99#define PPCMODE_EPP_WORD 5 100#define PPCMODE_EPP_DWORD 6 101 102//*************************************************************************** 103 104static int ppc6_select(Interface *ppc); 105static void ppc6_deselect(Interface *ppc); 106static void ppc6_send_cmd(Interface *ppc, u8 cmd); 107static void ppc6_wr_data_byte(Interface *ppc, u8 data); 108static u8 ppc6_rd_data_byte(Interface *ppc); 109static u8 ppc6_rd_port(Interface *ppc, u8 port); 110static void ppc6_wr_port(Interface *ppc, u8 port, u8 data); 111static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count); 112static void ppc6_wait_for_fifo(Interface *ppc); 113static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count); 114static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length); 115static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length); 116static void ppc6_wr_extout(Interface *ppc, u8 regdata); 117static int ppc6_open(Interface *ppc); 118static void ppc6_close(Interface *ppc); 119 120//*************************************************************************** 121 122static int ppc6_select(Interface *ppc) 123{ 124 u8 i, j, k; 125 126 i = inb(ppc->lpt_addr + 1); 127 128 if (i & 1) 129 outb(i, ppc->lpt_addr + 1); 130 131 ppc->org_data = inb(ppc->lpt_addr); 132 133 ppc->org_ctrl = inb(ppc->lpt_addr + 2) & 0x5F; // readback ctrl 134 135 ppc->cur_ctrl = ppc->org_ctrl; 136 137 ppc->cur_ctrl |= port_sel; 138 139 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 140 141 if (ppc->org_data == 'b') 142 outb('x', ppc->lpt_addr); 143 144 outb('b', ppc->lpt_addr); 145 outb('p', ppc->lpt_addr); 146 outb(ppc->ppc_id, ppc->lpt_addr); 147 outb(~ppc->ppc_id,ppc->lpt_addr); 148 149 ppc->cur_ctrl &= ~port_sel; 150 151 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 152 153 ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init; 154 155 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 156 157 i = ppc->mode & 0x0C; 158 159 if (i == 0) 160 i = (ppc->mode & 2) | 1; 161 162 outb(i, ppc->lpt_addr); 163 164 ppc->cur_ctrl |= port_sel; 165 166 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 167 168 // DELAY 169 170 ppc->cur_ctrl |= port_afd; 171 172 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 173 174 j = ((i & 0x08) << 4) | ((i & 0x07) << 3); 175 176 k = inb(ppc->lpt_addr + 1) & 0xB8; 177 178 if (j == k) 179 { 180 ppc->cur_ctrl &= ~port_afd; 181 182 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 183 184 k = (inb(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8; 185 186 if (j == k) 187 { 188 if (i & 4) // EPP 189 ppc->cur_ctrl &= ~(port_sel | port_init); 190 else // PPC/ECP 191 ppc->cur_ctrl &= ~port_sel; 192 193 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 194 195 return(1); 196 } 197 } 198 199 outb(ppc->org_ctrl, ppc->lpt_addr + 2); 200 201 outb(ppc->org_data, ppc->lpt_addr); 202 203 return(0); // FAIL 204} 205 206//*************************************************************************** 207 208static void ppc6_deselect(Interface *ppc) 209{ 210 if (ppc->mode & 4) // EPP 211 ppc->cur_ctrl |= port_init; 212 else // PPC/ECP 213 ppc->cur_ctrl |= port_sel; 214 215 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 216 217 outb(ppc->org_data, ppc->lpt_addr); 218 219 outb((ppc->org_ctrl | port_sel), ppc->lpt_addr + 2); 220 221 outb(ppc->org_ctrl, ppc->lpt_addr + 2); 222} 223 224//*************************************************************************** 225 226static void ppc6_send_cmd(Interface *ppc, u8 cmd) 227{ 228 switch(ppc->mode) 229 { 230 case PPCMODE_UNI_SW : 231 case PPCMODE_UNI_FW : 232 case PPCMODE_BI_SW : 233 case PPCMODE_BI_FW : 234 { 235 outb(cmd, ppc->lpt_addr); 236 237 ppc->cur_ctrl ^= cmd_stb; 238 239 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 240 241 break; 242 } 243 244 case PPCMODE_EPP_BYTE : 245 case PPCMODE_EPP_WORD : 246 case PPCMODE_EPP_DWORD : 247 { 248 outb(cmd, ppc->lpt_addr + 3); 249 250 break; 251 } 252 } 253} 254 255//*************************************************************************** 256 257static void ppc6_wr_data_byte(Interface *ppc, u8 data) 258{ 259 switch(ppc->mode) 260 { 261 case PPCMODE_UNI_SW : 262 case PPCMODE_UNI_FW : 263 case PPCMODE_BI_SW : 264 case PPCMODE_BI_FW : 265 { 266 outb(data, ppc->lpt_addr); 267 268 ppc->cur_ctrl ^= data_stb; 269 270 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 271 272 break; 273 } 274 275 case PPCMODE_EPP_BYTE : 276 case PPCMODE_EPP_WORD : 277 case PPCMODE_EPP_DWORD : 278 { 279 outb(data, ppc->lpt_addr + 4); 280 281 break; 282 } 283 } 284} 285 286//*************************************************************************** 287 288static u8 ppc6_rd_data_byte(Interface *ppc) 289{ 290 u8 data = 0; 291 292 switch(ppc->mode) 293 { 294 case PPCMODE_UNI_SW : 295 case PPCMODE_UNI_FW : 296 { 297 ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb; 298 299 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 300 301 // DELAY 302 303 data = inb(ppc->lpt_addr + 1); 304 305 data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3); 306 307 ppc->cur_ctrl |= port_stb; 308 309 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 310 311 // DELAY 312 313 data |= inb(ppc->lpt_addr + 1) & 0xB8; 314 315 break; 316 } 317 318 case PPCMODE_BI_SW : 319 case PPCMODE_BI_FW : 320 { 321 ppc->cur_ctrl |= port_dir; 322 323 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 324 325 ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb; 326 327 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 328 329 data = inb(ppc->lpt_addr); 330 331 ppc->cur_ctrl &= ~port_stb; 332 333 outb(ppc->cur_ctrl,ppc->lpt_addr + 2); 334 335 ppc->cur_ctrl &= ~port_dir; 336 337 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 338 339 break; 340 } 341 342 case PPCMODE_EPP_BYTE : 343 case PPCMODE_EPP_WORD : 344 case PPCMODE_EPP_DWORD : 345 { 346 outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2); 347 348 data = inb(ppc->lpt_addr + 4); 349 350 outb(ppc->cur_ctrl,ppc->lpt_addr + 2); 351 352 break; 353 } 354 } 355 356 return(data); 357} 358 359//*************************************************************************** 360 361static u8 ppc6_rd_port(Interface *ppc, u8 port) 362{ 363 ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_READ)); 364 365 return(ppc6_rd_data_byte(ppc)); 366} 367 368//*************************************************************************** 369 370static void ppc6_wr_port(Interface *ppc, u8 port, u8 data) 371{ 372 ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_WRITE)); 373 374 ppc6_wr_data_byte(ppc, data); 375} 376 377//*************************************************************************** 378 379static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count) 380{ 381 switch(ppc->mode) 382 { 383 case PPCMODE_UNI_SW : 384 case PPCMODE_UNI_FW : 385 { 386 while(count) 387 { 388 u8 d; 389 390 ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb; 391 392 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 393 394 // DELAY 395 396 d = inb(ppc->lpt_addr + 1); 397 398 d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3); 399 400 ppc->cur_ctrl |= port_stb; 401 402 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 403 404 // DELAY 405 406 d |= inb(ppc->lpt_addr + 1) & 0xB8; 407 408 *data++ = d; 409 count--; 410 } 411 412 break; 413 } 414 415 case PPCMODE_BI_SW : 416 case PPCMODE_BI_FW : 417 { 418 ppc->cur_ctrl |= port_dir; 419 420 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 421 422 ppc->cur_ctrl |= port_stb; 423 424 while(count) 425 { 426 ppc->cur_ctrl ^= data_stb; 427 428 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 429 430 *data++ = inb(ppc->lpt_addr); 431 count--; 432 } 433 434 ppc->cur_ctrl &= ~port_stb; 435 436 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 437 438 ppc->cur_ctrl &= ~port_dir; 439 440 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 441 442 break; 443 } 444 445 case PPCMODE_EPP_BYTE : 446 { 447 outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2); 448 449 // DELAY 450 451 while(count) 452 { 453 *data++ = inb(ppc->lpt_addr + 4); 454 count--; 455 } 456 457 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 458 459 break; 460 } 461 462 case PPCMODE_EPP_WORD : 463 { 464 outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2); 465 466 // DELAY 467 468 while(count > 1) 469 { 470 *((u16 *)data) = inw(ppc->lpt_addr + 4); 471 data += 2; 472 count -= 2; 473 } 474 475 while(count) 476 { 477 *data++ = inb(ppc->lpt_addr + 4); 478 count--; 479 } 480 481 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 482 483 break; 484 } 485 486 case PPCMODE_EPP_DWORD : 487 { 488 outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2); 489 490 // DELAY 491 492 while(count > 3) 493 { 494 *((u32 *)data) = inl(ppc->lpt_addr + 4); 495 data += 4; 496 count -= 4; 497 } 498 499 while(count) 500 { 501 *data++ = inb(ppc->lpt_addr + 4); 502 count--; 503 } 504 505 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 506 507 break; 508 } 509 } 510 511} 512 513//*************************************************************************** 514 515static void ppc6_wait_for_fifo(Interface *ppc) 516{ 517 int i; 518 519 if (ppc->ppc_flags & fifo_wait) 520 { 521 for(i=0; i<20; i++) 522 inb(ppc->lpt_addr + 1); 523 } 524} 525 526//*************************************************************************** 527 528static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count) 529{ 530 switch(ppc->mode) 531 { 532 case PPCMODE_UNI_SW : 533 case PPCMODE_BI_SW : 534 { 535 while(count--) 536 { 537 outb(*data++, ppc->lpt_addr); 538 539 ppc->cur_ctrl ^= data_stb; 540 541 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 542 } 543 544 break; 545 } 546 547 case PPCMODE_UNI_FW : 548 case PPCMODE_BI_FW : 549 { 550 u8 this, last; 551 552 ppc6_send_cmd(ppc,(CMD_PREFIX_SET | PREFIX_FASTWR)); 553 554 ppc->cur_ctrl |= port_stb; 555 556 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 557 558 last = *data; 559 560 outb(last, ppc->lpt_addr); 561 562 while(count) 563 { 564 this = *data++; 565 count--; 566 567 if (this == last) 568 { 569 ppc->cur_ctrl ^= data_stb; 570 571 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 572 } 573 else 574 { 575 outb(this, ppc->lpt_addr); 576 577 last = this; 578 } 579 } 580 581 ppc->cur_ctrl &= ~port_stb; 582 583 outb(ppc->cur_ctrl, ppc->lpt_addr + 2); 584 585 ppc6_send_cmd(ppc,(CMD_PREFIX_RESET | PREFIX_FASTWR)); 586 587 break; 588 } 589 590 case PPCMODE_EPP_BYTE : 591 { 592 while(count) 593 { 594 outb(*data++,ppc->lpt_addr + 4); 595 count--; 596 } 597 598 ppc6_wait_for_fifo(ppc); 599 600 break; 601 } 602 603 case PPCMODE_EPP_WORD : 604 { 605 while(count > 1) 606 { 607 outw(*((u16 *)data),ppc->lpt_addr + 4); 608 data += 2; 609 count -= 2; 610 } 611 612 while(count) 613 { 614 outb(*data++,ppc->lpt_addr + 4); 615 count--; 616 } 617 618 ppc6_wait_for_fifo(ppc); 619 620 break; 621 } 622 623 case PPCMODE_EPP_DWORD : 624 { 625 while(count > 3) 626 { 627 outl(*((u32 *)data),ppc->lpt_addr + 4); 628 data += 4; 629 count -= 4; 630 } 631 632 while(count) 633 { 634 outb(*data++,ppc->lpt_addr + 4); 635 count--; 636 } 637 638 ppc6_wait_for_fifo(ppc); 639 640 break; 641 } 642 } 643} 644 645//*************************************************************************** 646 647static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length) 648{ 649 length = length << 1; 650 651 ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE)); 652 ppc6_wr_data_byte(ppc,(u8)length); 653 ppc6_wr_data_byte(ppc,(u8)(length >> 8)); 654 ppc6_wr_data_byte(ppc,0); 655 656 ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK)); 657 658 ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_READ)); 659 660 ppc6_rd_data_blk(ppc, data, length); 661 662 ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK)); 663} 664 665//*************************************************************************** 666 667static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length) 668{ 669 length = length << 1; 670 671 ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE)); 672 ppc6_wr_data_byte(ppc,(u8)length); 673 ppc6_wr_data_byte(ppc,(u8)(length >> 8)); 674 ppc6_wr_data_byte(ppc,0); 675 676 ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK)); 677 678 ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_WRITE)); 679 680 ppc6_wr_data_blk(ppc, data, length); 681 682 ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK)); 683} 684 685//*************************************************************************** 686 687static void ppc6_wr_extout(Interface *ppc, u8 regdata) 688{ 689 ppc6_send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE)); 690 691 ppc6_wr_data_byte(ppc, (u8)((regdata & 0x03) << 6)); 692} 693 694//*************************************************************************** 695 696static int ppc6_open(Interface *ppc) 697{ 698 int ret; 699 700 ret = ppc6_select(ppc); 701 702 if (ret == 0) 703 return(ret); 704 705 ppc->ppc_flags &= ~fifo_wait; 706 707 ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE)); 708 ppc6_wr_data_byte(ppc, RAMSIZE_128K); 709 710 ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_READ | REG_VERSION)); 711 712 if ((ppc6_rd_data_byte(ppc) & 0x3F) == 0x0C) 713 ppc->ppc_flags |= fifo_wait; 714 715 return(ret); 716} 717 718//*************************************************************************** 719 720static void ppc6_close(Interface *ppc) 721{ 722 ppc6_deselect(ppc); 723} 724 725//*************************************************************************** 726 727