Back to home page

OSCL-LXR

 
 

    


0001 /*
0002     ppc6lnx.c (c) 2001 Micro Solutions Inc.
0003         Released under the terms of the GNU General Public license
0004 
0005     ppc6lnx.c  is a par of the protocol driver for the Micro Solutions
0006         "BACKPACK" parallel port IDE adapter
0007         (Works on Series 6 drives)
0008 
0009 */
0010 
0011 //***************************************************************************
0012 
0013 // PPC 6 Code in C sanitized for LINUX
0014 // Original x86 ASM by Ron, Converted to C by Clive
0015 
0016 //***************************************************************************
0017 
0018 
0019 #define port_stb                    1
0020 #define port_afd                    2
0021 #define cmd_stb                     port_afd
0022 #define port_init                   4
0023 #define data_stb                    port_init
0024 #define port_sel                    8
0025 #define port_int                    16
0026 #define port_dir                    0x20
0027 
0028 #define ECR_EPP 0x80
0029 #define ECR_BI  0x20
0030 
0031 //***************************************************************************
0032 
0033 //  60772 Commands
0034 
0035 #define ACCESS_REG              0x00
0036 #define ACCESS_PORT             0x40
0037 
0038 #define ACCESS_READ             0x00
0039 #define ACCESS_WRITE            0x20
0040 
0041 //  60772 Command Prefix
0042 
0043 #define CMD_PREFIX_SET      0xe0        // Special command that modifies the next command's operation
0044 #define CMD_PREFIX_RESET    0xc0        // Resets current cmd modifier reg bits
0045  #define PREFIX_IO16            0x01        // perform 16-bit wide I/O
0046  #define PREFIX_FASTWR      0x04        // enable PPC mode fast-write
0047  #define PREFIX_BLK             0x08        // enable block transfer mode
0048 
0049 // 60772 Registers
0050 
0051 #define REG_STATUS              0x00        // status register
0052  #define STATUS_IRQA            0x01        // Peripheral IRQA line
0053  #define STATUS_EEPROM_DO   0x40        // Serial EEPROM data bit
0054 #define REG_VERSION             0x01        // PPC version register (read)
0055 #define REG_HWCFG                   0x02        // Hardware Config register
0056 #define REG_RAMSIZE             0x03        // Size of RAM Buffer
0057  #define RAMSIZE_128K           0x02
0058 #define REG_EEPROM              0x06        // EEPROM control register
0059  #define EEPROM_SK              0x01        // eeprom SK bit
0060  #define EEPROM_DI              0x02        // eeprom DI bit
0061  #define EEPROM_CS              0x04        // eeprom CS bit
0062  #define EEPROM_EN              0x08        // eeprom output enable
0063 #define REG_BLKSIZE             0x08        // Block transfer len (24 bit)
0064 
0065 //***************************************************************************
0066 
0067 typedef struct ppc_storage {
0068     u16 lpt_addr;               // LPT base address
0069     u8  ppc_id;
0070     u8  mode;                       // operating mode
0071                     // 0 = PPC Uni SW
0072                     // 1 = PPC Uni FW
0073                     // 2 = PPC Bi SW
0074                     // 3 = PPC Bi FW
0075                     // 4 = EPP Byte
0076                     // 5 = EPP Word
0077                     // 6 = EPP Dword
0078     u8  ppc_flags;
0079     u8  org_data;               // original LPT data port contents
0080     u8  org_ctrl;               // original LPT control port contents
0081     u8  cur_ctrl;               // current control port contents
0082 } Interface;
0083 
0084 //***************************************************************************
0085 
0086 // ppc_flags
0087 
0088 #define fifo_wait                   0x10
0089 
0090 //***************************************************************************
0091 
0092 // DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES
0093 
0094 #define PPCMODE_UNI_SW      0
0095 #define PPCMODE_UNI_FW      1
0096 #define PPCMODE_BI_SW           2
0097 #define PPCMODE_BI_FW           3
0098 #define PPCMODE_EPP_BYTE    4
0099 #define PPCMODE_EPP_WORD    5
0100 #define PPCMODE_EPP_DWORD   6
0101 
0102 //***************************************************************************
0103 
0104 static int ppc6_select(Interface *ppc);
0105 static void ppc6_deselect(Interface *ppc);
0106 static void ppc6_send_cmd(Interface *ppc, u8 cmd);
0107 static void ppc6_wr_data_byte(Interface *ppc, u8 data);
0108 static u8 ppc6_rd_data_byte(Interface *ppc);
0109 static u8 ppc6_rd_port(Interface *ppc, u8 port);
0110 static void ppc6_wr_port(Interface *ppc, u8 port, u8 data);
0111 static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count);
0112 static void ppc6_wait_for_fifo(Interface *ppc);
0113 static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count);
0114 static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
0115 static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
0116 static void ppc6_wr_extout(Interface *ppc, u8 regdata);
0117 static int ppc6_open(Interface *ppc);
0118 static void ppc6_close(Interface *ppc);
0119 
0120 //***************************************************************************
0121 
0122 static int ppc6_select(Interface *ppc)
0123 {
0124     u8 i, j, k;
0125 
0126     i = inb(ppc->lpt_addr + 1);
0127 
0128     if (i & 1)
0129         outb(i, ppc->lpt_addr + 1);
0130 
0131     ppc->org_data = inb(ppc->lpt_addr);
0132 
0133     ppc->org_ctrl = inb(ppc->lpt_addr + 2) & 0x5F; // readback ctrl
0134 
0135     ppc->cur_ctrl = ppc->org_ctrl;
0136 
0137     ppc->cur_ctrl |= port_sel;
0138 
0139     outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0140 
0141     if (ppc->org_data == 'b')
0142         outb('x', ppc->lpt_addr);
0143 
0144     outb('b', ppc->lpt_addr);
0145     outb('p', ppc->lpt_addr);
0146     outb(ppc->ppc_id, ppc->lpt_addr);
0147     outb(~ppc->ppc_id,ppc->lpt_addr);
0148 
0149     ppc->cur_ctrl &= ~port_sel;
0150 
0151     outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0152 
0153     ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init;
0154 
0155     outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0156 
0157     i = ppc->mode & 0x0C;
0158 
0159     if (i == 0)
0160         i = (ppc->mode & 2) | 1;
0161 
0162     outb(i, ppc->lpt_addr);
0163 
0164     ppc->cur_ctrl |= port_sel;
0165 
0166     outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0167 
0168     // DELAY
0169 
0170     ppc->cur_ctrl |= port_afd;
0171 
0172     outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0173 
0174     j = ((i & 0x08) << 4) | ((i & 0x07) << 3);
0175 
0176     k = inb(ppc->lpt_addr + 1) & 0xB8;
0177 
0178     if (j == k)
0179     {
0180         ppc->cur_ctrl &= ~port_afd;
0181 
0182         outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0183 
0184         k = (inb(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8;
0185 
0186         if (j == k)
0187         {
0188             if (i & 4)  // EPP
0189                 ppc->cur_ctrl &= ~(port_sel | port_init);
0190             else                // PPC/ECP
0191                 ppc->cur_ctrl &= ~port_sel;
0192 
0193             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0194 
0195             return(1);
0196         }
0197     }
0198 
0199     outb(ppc->org_ctrl, ppc->lpt_addr + 2);
0200 
0201     outb(ppc->org_data, ppc->lpt_addr);
0202 
0203     return(0); // FAIL
0204 }
0205 
0206 //***************************************************************************
0207 
0208 static void ppc6_deselect(Interface *ppc)
0209 {
0210     if (ppc->mode & 4)  // EPP
0211         ppc->cur_ctrl |= port_init;
0212     else                                // PPC/ECP
0213         ppc->cur_ctrl |= port_sel;
0214 
0215     outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0216 
0217     outb(ppc->org_data, ppc->lpt_addr);
0218 
0219     outb((ppc->org_ctrl | port_sel), ppc->lpt_addr + 2);
0220 
0221     outb(ppc->org_ctrl, ppc->lpt_addr + 2);
0222 }
0223 
0224 //***************************************************************************
0225 
0226 static void ppc6_send_cmd(Interface *ppc, u8 cmd)
0227 {
0228     switch(ppc->mode)
0229     {
0230         case PPCMODE_UNI_SW :
0231         case PPCMODE_UNI_FW :
0232         case PPCMODE_BI_SW :
0233         case PPCMODE_BI_FW :
0234         {
0235             outb(cmd, ppc->lpt_addr);
0236 
0237             ppc->cur_ctrl ^= cmd_stb;
0238 
0239             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0240 
0241             break;
0242         }
0243 
0244         case PPCMODE_EPP_BYTE :
0245         case PPCMODE_EPP_WORD :
0246         case PPCMODE_EPP_DWORD :
0247         {
0248             outb(cmd, ppc->lpt_addr + 3);
0249 
0250             break;
0251         }
0252     }
0253 }
0254 
0255 //***************************************************************************
0256 
0257 static void ppc6_wr_data_byte(Interface *ppc, u8 data)
0258 {
0259     switch(ppc->mode)
0260     {
0261         case PPCMODE_UNI_SW :
0262         case PPCMODE_UNI_FW :
0263         case PPCMODE_BI_SW :
0264         case PPCMODE_BI_FW :
0265         {
0266             outb(data, ppc->lpt_addr);
0267 
0268             ppc->cur_ctrl ^= data_stb;
0269 
0270             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0271 
0272             break;
0273         }
0274 
0275         case PPCMODE_EPP_BYTE :
0276         case PPCMODE_EPP_WORD :
0277         case PPCMODE_EPP_DWORD :
0278         {
0279             outb(data, ppc->lpt_addr + 4);
0280 
0281             break;
0282         }
0283     }
0284 }
0285 
0286 //***************************************************************************
0287 
0288 static u8 ppc6_rd_data_byte(Interface *ppc)
0289 {
0290     u8 data = 0;
0291 
0292     switch(ppc->mode)
0293     {
0294         case PPCMODE_UNI_SW :
0295         case PPCMODE_UNI_FW :
0296         {
0297             ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
0298 
0299             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0300 
0301             // DELAY
0302 
0303             data = inb(ppc->lpt_addr + 1);
0304 
0305             data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);
0306 
0307             ppc->cur_ctrl |= port_stb;
0308 
0309             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0310 
0311             // DELAY
0312 
0313             data |= inb(ppc->lpt_addr + 1) & 0xB8;
0314 
0315             break;
0316         }
0317 
0318         case PPCMODE_BI_SW :
0319         case PPCMODE_BI_FW :
0320         {
0321             ppc->cur_ctrl |= port_dir;
0322 
0323             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0324 
0325             ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb;
0326 
0327             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0328 
0329             data = inb(ppc->lpt_addr);
0330 
0331             ppc->cur_ctrl &= ~port_stb;
0332 
0333             outb(ppc->cur_ctrl,ppc->lpt_addr + 2);
0334 
0335             ppc->cur_ctrl &= ~port_dir;
0336 
0337             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0338 
0339             break;
0340         }
0341 
0342         case PPCMODE_EPP_BYTE :
0343         case PPCMODE_EPP_WORD :
0344         case PPCMODE_EPP_DWORD :
0345         {
0346             outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);
0347 
0348             data = inb(ppc->lpt_addr + 4);
0349 
0350             outb(ppc->cur_ctrl,ppc->lpt_addr + 2);
0351 
0352             break;
0353         }
0354     }
0355 
0356     return(data);
0357 }
0358 
0359 //***************************************************************************
0360 
0361 static u8 ppc6_rd_port(Interface *ppc, u8 port)
0362 {
0363     ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_READ));
0364 
0365     return(ppc6_rd_data_byte(ppc));
0366 }
0367 
0368 //***************************************************************************
0369 
0370 static void ppc6_wr_port(Interface *ppc, u8 port, u8 data)
0371 {
0372     ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_WRITE));
0373 
0374     ppc6_wr_data_byte(ppc, data);
0375 }
0376 
0377 //***************************************************************************
0378 
0379 static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count)
0380 {
0381     switch(ppc->mode)
0382     {
0383         case PPCMODE_UNI_SW :
0384         case PPCMODE_UNI_FW :
0385         {
0386             while(count)
0387             {
0388                 u8 d;
0389 
0390                 ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
0391 
0392                 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0393 
0394                 // DELAY
0395 
0396                 d = inb(ppc->lpt_addr + 1);
0397 
0398                 d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);
0399 
0400                 ppc->cur_ctrl |= port_stb;
0401 
0402                 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0403 
0404                 // DELAY
0405 
0406                 d |= inb(ppc->lpt_addr + 1) & 0xB8;
0407 
0408                 *data++ = d;
0409                 count--;
0410             }
0411 
0412             break;
0413         }
0414 
0415         case PPCMODE_BI_SW :
0416         case PPCMODE_BI_FW :
0417         {
0418             ppc->cur_ctrl |= port_dir;
0419 
0420             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0421 
0422             ppc->cur_ctrl |= port_stb;
0423 
0424             while(count)
0425             {
0426                 ppc->cur_ctrl ^= data_stb;
0427 
0428                 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0429 
0430                 *data++ = inb(ppc->lpt_addr);
0431                 count--;
0432             }
0433 
0434             ppc->cur_ctrl &= ~port_stb;
0435 
0436             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0437 
0438             ppc->cur_ctrl &= ~port_dir;
0439 
0440             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0441 
0442             break;
0443         }
0444 
0445         case PPCMODE_EPP_BYTE :
0446         {
0447             outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
0448 
0449             // DELAY
0450 
0451             while(count)
0452             {
0453                 *data++ = inb(ppc->lpt_addr + 4);
0454                 count--;
0455             }
0456 
0457             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0458 
0459             break;
0460         }
0461 
0462         case PPCMODE_EPP_WORD :
0463         {
0464             outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
0465 
0466             // DELAY
0467 
0468             while(count > 1)
0469             {
0470                 *((u16 *)data) = inw(ppc->lpt_addr + 4);
0471                 data  += 2;
0472                 count -= 2;
0473             }
0474 
0475             while(count)
0476             {
0477                 *data++ = inb(ppc->lpt_addr + 4);
0478                 count--;
0479             }
0480 
0481             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0482 
0483             break;
0484         }
0485 
0486         case PPCMODE_EPP_DWORD :
0487         {
0488             outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);
0489 
0490             // DELAY
0491 
0492             while(count > 3)
0493             {
0494                 *((u32 *)data) = inl(ppc->lpt_addr + 4);
0495                 data  += 4;
0496                 count -= 4;
0497             }
0498 
0499             while(count)
0500             {
0501                 *data++ = inb(ppc->lpt_addr + 4);
0502                 count--;
0503             }
0504 
0505             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0506 
0507             break;
0508         }
0509     }
0510 
0511 }
0512 
0513 //***************************************************************************
0514 
0515 static void ppc6_wait_for_fifo(Interface *ppc)
0516 {
0517     int i;
0518 
0519     if (ppc->ppc_flags & fifo_wait)
0520     {
0521         for(i=0; i<20; i++)
0522             inb(ppc->lpt_addr + 1);
0523     }
0524 }
0525 
0526 //***************************************************************************
0527 
0528 static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count)
0529 {
0530     switch(ppc->mode)
0531     {
0532         case PPCMODE_UNI_SW :
0533         case PPCMODE_BI_SW :
0534         {
0535             while(count--)
0536             {
0537                 outb(*data++, ppc->lpt_addr);
0538 
0539                 ppc->cur_ctrl ^= data_stb;
0540 
0541                 outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0542             }
0543 
0544             break;
0545         }
0546 
0547         case PPCMODE_UNI_FW :
0548         case PPCMODE_BI_FW :
0549         {
0550             u8 this, last;
0551 
0552             ppc6_send_cmd(ppc,(CMD_PREFIX_SET | PREFIX_FASTWR));
0553 
0554             ppc->cur_ctrl |= port_stb;
0555 
0556             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0557 
0558             last = *data;
0559 
0560             outb(last, ppc->lpt_addr);
0561 
0562             while(count)
0563             {
0564                 this = *data++;
0565                 count--;
0566 
0567                 if (this == last)
0568                 {
0569                     ppc->cur_ctrl ^= data_stb;
0570 
0571                     outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0572                 }
0573                 else
0574                 {
0575                     outb(this, ppc->lpt_addr);
0576 
0577                     last = this;
0578                 }
0579             }
0580 
0581             ppc->cur_ctrl &= ~port_stb;
0582 
0583             outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
0584 
0585             ppc6_send_cmd(ppc,(CMD_PREFIX_RESET | PREFIX_FASTWR));
0586 
0587             break;
0588         }
0589 
0590         case PPCMODE_EPP_BYTE :
0591         {
0592             while(count)
0593             {
0594                 outb(*data++,ppc->lpt_addr + 4);
0595                 count--;
0596             }
0597 
0598             ppc6_wait_for_fifo(ppc);
0599 
0600             break;
0601         }
0602 
0603         case PPCMODE_EPP_WORD :
0604         {
0605             while(count > 1)
0606             {
0607                 outw(*((u16 *)data),ppc->lpt_addr + 4);
0608                 data  += 2;
0609                 count -= 2;
0610             }
0611 
0612             while(count)
0613             {
0614                 outb(*data++,ppc->lpt_addr + 4);
0615                 count--;
0616             }
0617 
0618             ppc6_wait_for_fifo(ppc);
0619 
0620             break;
0621         }
0622 
0623         case PPCMODE_EPP_DWORD :
0624         {
0625             while(count > 3)
0626             {
0627                 outl(*((u32 *)data),ppc->lpt_addr + 4);
0628                 data  += 4;
0629                 count -= 4;
0630             }
0631 
0632             while(count)
0633             {
0634                 outb(*data++,ppc->lpt_addr + 4);
0635                 count--;
0636             }
0637 
0638             ppc6_wait_for_fifo(ppc);
0639 
0640             break;
0641         }
0642     }
0643 }
0644 
0645 //***************************************************************************
0646 
0647 static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
0648 {
0649     length = length << 1;
0650 
0651     ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
0652     ppc6_wr_data_byte(ppc,(u8)length);
0653     ppc6_wr_data_byte(ppc,(u8)(length >> 8));
0654     ppc6_wr_data_byte(ppc,0);
0655 
0656     ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));
0657 
0658     ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_READ));
0659 
0660     ppc6_rd_data_blk(ppc, data, length);
0661 
0662     ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
0663 }
0664 
0665 //***************************************************************************
0666 
0667 static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
0668 {
0669     length = length << 1;
0670 
0671     ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
0672     ppc6_wr_data_byte(ppc,(u8)length);
0673     ppc6_wr_data_byte(ppc,(u8)(length >> 8));
0674     ppc6_wr_data_byte(ppc,0);
0675 
0676     ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));
0677 
0678     ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_WRITE));
0679 
0680     ppc6_wr_data_blk(ppc, data, length);
0681 
0682     ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
0683 }
0684 
0685 //***************************************************************************
0686 
0687 static void ppc6_wr_extout(Interface *ppc, u8 regdata)
0688 {
0689     ppc6_send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE));
0690 
0691     ppc6_wr_data_byte(ppc, (u8)((regdata & 0x03) << 6));
0692 }
0693 
0694 //***************************************************************************
0695 
0696 static int ppc6_open(Interface *ppc)
0697 {
0698     int ret;
0699 
0700     ret = ppc6_select(ppc);
0701 
0702     if (ret == 0)
0703         return(ret);
0704 
0705     ppc->ppc_flags &= ~fifo_wait;
0706 
0707     ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE));
0708     ppc6_wr_data_byte(ppc, RAMSIZE_128K);
0709 
0710     ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_READ | REG_VERSION));
0711 
0712     if ((ppc6_rd_data_byte(ppc) & 0x3F) == 0x0C)
0713         ppc->ppc_flags |= fifo_wait;
0714 
0715     return(ret);
0716 }
0717 
0718 //***************************************************************************
0719 
0720 static void ppc6_close(Interface *ppc)
0721 {
0722     ppc6_deselect(ppc);
0723 }
0724 
0725 //***************************************************************************
0726