linux/drivers/block/paride/ppc6lnx.c
<<
>>
Prefs
   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