Logo Search packages:      
Sourcecode: linux version File versions  Download package

ppc6lnx.c

/*
      ppc6lnx.c (c) 2001 Micro Solutions Inc.
            Released under the terms of the GNU General Public license

      ppc6lnx.c  is a par of the protocol driver for the Micro Solutions
            "BACKPACK" parallel port IDE adapter
            (Works on Series 6 drives)

*/

//***************************************************************************

// PPC 6 Code in C sanitized for LINUX
// Original x86 ASM by Ron, Converted to C by Clive

//***************************************************************************


#define port_stb                          1
#define port_afd                          2
#define cmd_stb                                 port_afd
#define port_init                         4
#define data_stb                          port_init
#define port_sel                          8
#define port_int                          16
#define port_dir                          0x20

#define ECR_EPP   0x80
#define ECR_BI    0x20

//***************************************************************************

//  60772 Commands

#define ACCESS_REG                        0x00
#define ACCESS_PORT                       0x40

#define ACCESS_READ                       0x00
#define ACCESS_WRITE                0x20

//  60772 Command Prefix

#define CMD_PREFIX_SET        0xe0        // Special command that modifies the next command's operation
#define CMD_PREFIX_RESET      0xc0        // Resets current cmd modifier reg bits
 #define PREFIX_IO16                0x01        // perform 16-bit wide I/O
 #define PREFIX_FASTWR        0x04        // enable PPC mode fast-write
 #define PREFIX_BLK                       0x08        // enable block transfer mode

// 60772 Registers

#define REG_STATUS                        0x00        // status register
 #define STATUS_IRQA                0x01        // Peripheral IRQA line
 #define STATUS_EEPROM_DO     0x40        // Serial EEPROM data bit
#define REG_VERSION                       0x01        // PPC version register (read)
#define REG_HWCFG                         0x02        // Hardware Config register
#define REG_RAMSIZE                       0x03        // Size of RAM Buffer
 #define RAMSIZE_128K               0x02
#define REG_EEPROM                        0x06        // EEPROM control register
 #define EEPROM_SK                        0x01        // eeprom SK bit
 #define EEPROM_DI                        0x02        // eeprom DI bit
 #define EEPROM_CS                        0x04        // eeprom CS bit
 #define EEPROM_EN                        0x08        // eeprom output enable
#define REG_BLKSIZE                       0x08        // Block transfer len (24 bit)

//***************************************************************************

typedef struct ppc_storage {
      u16   lpt_addr;                     // LPT base address
      u8    ppc_id;
      u8    mode;                               // operating mode
                              // 0 = PPC Uni SW
                              // 1 = PPC Uni FW
                              // 2 = PPC Bi SW
                              // 3 = PPC Bi FW
                              // 4 = EPP Byte
                              // 5 = EPP Word
                              // 6 = EPP Dword
      u8    ppc_flags;
      u8    org_data;                     // original LPT data port contents
      u8    org_ctrl;                     // original LPT control port contents
      u8    cur_ctrl;                     // current control port contents
} Interface;

//***************************************************************************

// ppc_flags

#define fifo_wait                         0x10

//***************************************************************************

// DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES

#define PPCMODE_UNI_SW        0
#define PPCMODE_UNI_FW        1
#define PPCMODE_BI_SW               2
#define PPCMODE_BI_FW               3
#define PPCMODE_EPP_BYTE      4
#define PPCMODE_EPP_WORD      5
#define PPCMODE_EPP_DWORD     6

//***************************************************************************

static int ppc6_select(Interface *ppc);
static void ppc6_deselect(Interface *ppc);
static void ppc6_send_cmd(Interface *ppc, u8 cmd);
static void ppc6_wr_data_byte(Interface *ppc, u8 data);
static u8 ppc6_rd_data_byte(Interface *ppc);
static u8 ppc6_rd_port(Interface *ppc, u8 port);
static void ppc6_wr_port(Interface *ppc, u8 port, u8 data);
static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count);
static void ppc6_wait_for_fifo(Interface *ppc);
static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count);
static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
static void ppc6_wr_extout(Interface *ppc, u8 regdata);
static int ppc6_open(Interface *ppc);
static void ppc6_close(Interface *ppc);

//***************************************************************************

static int ppc6_select(Interface *ppc)
{
      u8 i, j, k;

      i = inb(ppc->lpt_addr + 1);

      if (i & 1)
            outb(i, ppc->lpt_addr + 1);

      ppc->org_data = inb(ppc->lpt_addr);

      ppc->org_ctrl = inb(ppc->lpt_addr + 2) & 0x5F; // readback ctrl

      ppc->cur_ctrl = ppc->org_ctrl;

      ppc->cur_ctrl |= port_sel;

      outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

      if (ppc->org_data == 'b')
            outb('x', ppc->lpt_addr);

      outb('b', ppc->lpt_addr);
      outb('p', ppc->lpt_addr);
      outb(ppc->ppc_id, ppc->lpt_addr);
      outb(~ppc->ppc_id,ppc->lpt_addr);

      ppc->cur_ctrl &= ~port_sel;

      outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

      ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init;

      outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

      i = ppc->mode & 0x0C;

      if (i == 0)
            i = (ppc->mode & 2) | 1;

      outb(i, ppc->lpt_addr);

      ppc->cur_ctrl |= port_sel;

      outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

      // DELAY

      ppc->cur_ctrl |= port_afd;

      outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

      j = ((i & 0x08) << 4) | ((i & 0x07) << 3);

      k = inb(ppc->lpt_addr + 1) & 0xB8;

      if (j == k)
      {
            ppc->cur_ctrl &= ~port_afd;

            outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

            k = (inb(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8;

            if (j == k)
            {
                  if (i & 4)  // EPP
                        ppc->cur_ctrl &= ~(port_sel | port_init);
                  else                    // PPC/ECP
                        ppc->cur_ctrl &= ~port_sel;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  return(1);
            }
      }

      outb(ppc->org_ctrl, ppc->lpt_addr + 2);

      outb(ppc->org_data, ppc->lpt_addr);

      return(0); // FAIL
}

//***************************************************************************

static void ppc6_deselect(Interface *ppc)
{
      if (ppc->mode & 4)      // EPP
            ppc->cur_ctrl |= port_init;
      else                                            // PPC/ECP
            ppc->cur_ctrl |= port_sel;

      outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

      outb(ppc->org_data, ppc->lpt_addr);

      outb((ppc->org_ctrl | port_sel), ppc->lpt_addr + 2);

      outb(ppc->org_ctrl, ppc->lpt_addr + 2);
}

//***************************************************************************

static void ppc6_send_cmd(Interface *ppc, u8 cmd)
{
      switch(ppc->mode)
      {
            case PPCMODE_UNI_SW :
            case PPCMODE_UNI_FW :
            case PPCMODE_BI_SW :
            case PPCMODE_BI_FW :
            {
                  outb(cmd, ppc->lpt_addr);

                  ppc->cur_ctrl ^= cmd_stb;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  break;
            }

            case PPCMODE_EPP_BYTE :
            case PPCMODE_EPP_WORD :
            case PPCMODE_EPP_DWORD :
            {
                  outb(cmd, ppc->lpt_addr + 3);

                  break;
            }
      }
}

//***************************************************************************

static void ppc6_wr_data_byte(Interface *ppc, u8 data)
{
      switch(ppc->mode)
      {
            case PPCMODE_UNI_SW :
            case PPCMODE_UNI_FW :
            case PPCMODE_BI_SW :
            case PPCMODE_BI_FW :
            {
                  outb(data, ppc->lpt_addr);

                  ppc->cur_ctrl ^= data_stb;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  break;
            }

            case PPCMODE_EPP_BYTE :
            case PPCMODE_EPP_WORD :
            case PPCMODE_EPP_DWORD :
            {
                  outb(data, ppc->lpt_addr + 4);

                  break;
            }
      }
}

//***************************************************************************

static u8 ppc6_rd_data_byte(Interface *ppc)
{
      u8 data = 0;

      switch(ppc->mode)
      {
            case PPCMODE_UNI_SW :
            case PPCMODE_UNI_FW :
            {
                  ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  // DELAY

                  data = inb(ppc->lpt_addr + 1);

                  data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);

                  ppc->cur_ctrl |= port_stb;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  // DELAY

                  data |= inb(ppc->lpt_addr + 1) & 0xB8;

                  break;
            }

            case PPCMODE_BI_SW :
            case PPCMODE_BI_FW :
            {
                  ppc->cur_ctrl |= port_dir;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  data = inb(ppc->lpt_addr);

                  ppc->cur_ctrl &= ~port_stb;

                  outb(ppc->cur_ctrl,ppc->lpt_addr + 2);

                  ppc->cur_ctrl &= ~port_dir;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  break;
            }

            case PPCMODE_EPP_BYTE :
            case PPCMODE_EPP_WORD :
            case PPCMODE_EPP_DWORD :
            {
                  outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);

                  data = inb(ppc->lpt_addr + 4);

                  outb(ppc->cur_ctrl,ppc->lpt_addr + 2);

                  break;
            }
      }

      return(data);
}

//***************************************************************************

static u8 ppc6_rd_port(Interface *ppc, u8 port)
{
      ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_READ));

      return(ppc6_rd_data_byte(ppc));
}

//***************************************************************************

static void ppc6_wr_port(Interface *ppc, u8 port, u8 data)
{
      ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_WRITE));

      ppc6_wr_data_byte(ppc, data);
}

//***************************************************************************

static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count)
{
      switch(ppc->mode)
      {
            case PPCMODE_UNI_SW :
            case PPCMODE_UNI_FW :
            {
                  while(count)
                  {
                        u8 d;

                        ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;

                        outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                        // DELAY

                        d = inb(ppc->lpt_addr + 1);

                        d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);

                        ppc->cur_ctrl |= port_stb;

                        outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                        // DELAY

                        d |= inb(ppc->lpt_addr + 1) & 0xB8;

                        *data++ = d;
                        count--;
                  }

                  break;
            }

            case PPCMODE_BI_SW :
            case PPCMODE_BI_FW :
            {
                  ppc->cur_ctrl |= port_dir;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  ppc->cur_ctrl |= port_stb;

                  while(count)
                  {
                        ppc->cur_ctrl ^= data_stb;

                        outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                        *data++ = inb(ppc->lpt_addr);
                        count--;
                  }

                  ppc->cur_ctrl &= ~port_stb;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  ppc->cur_ctrl &= ~port_dir;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  break;
            }

            case PPCMODE_EPP_BYTE :
            {
                  outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);

                  // DELAY

                  while(count)
                  {
                        *data++ = inb(ppc->lpt_addr + 4);
                        count--;
                  }

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  break;
            }

            case PPCMODE_EPP_WORD :
            {
                  outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);

                  // DELAY

                  while(count > 1)
                  {
                        *((u16 *)data) = inw(ppc->lpt_addr + 4);
                        data  += 2;
                        count -= 2;
                  }

                  while(count)
                  {
                        *data++ = inb(ppc->lpt_addr + 4);
                        count--;
                  }

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  break;
            }

            case PPCMODE_EPP_DWORD :
            {
                  outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);

                  // DELAY

                  while(count > 3)
                  {
                        *((u32 *)data) = inl(ppc->lpt_addr + 4);
                        data  += 4;
                        count -= 4;
                  }

                  while(count)
                  {
                        *data++ = inb(ppc->lpt_addr + 4);
                        count--;
                  }

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  break;
            }
      }

}

//***************************************************************************

static void ppc6_wait_for_fifo(Interface *ppc)
{
      int i;

      if (ppc->ppc_flags & fifo_wait)
      {
            for(i=0; i<20; i++)
                  inb(ppc->lpt_addr + 1);
      }
}

//***************************************************************************

static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count)
{
      switch(ppc->mode)
      {
            case PPCMODE_UNI_SW :
            case PPCMODE_BI_SW :
            {
                  while(count--)
                  {
                        outb(*data++, ppc->lpt_addr);

                        ppc->cur_ctrl ^= data_stb;

                        outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
                  }

                  break;
            }

            case PPCMODE_UNI_FW :
            case PPCMODE_BI_FW :
            {
                  u8 this, last;

                  ppc6_send_cmd(ppc,(CMD_PREFIX_SET | PREFIX_FASTWR));

                  ppc->cur_ctrl |= port_stb;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  last = *data;

                  outb(last, ppc->lpt_addr);

                  while(count)
                  {
                        this = *data++;
                        count--;

                        if (this == last)
                        {
                              ppc->cur_ctrl ^= data_stb;

                              outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
                        }
                        else
                        {
                              outb(this, ppc->lpt_addr);

                              last = this;
                        }
                  }

                  ppc->cur_ctrl &= ~port_stb;

                  outb(ppc->cur_ctrl, ppc->lpt_addr + 2);

                  ppc6_send_cmd(ppc,(CMD_PREFIX_RESET | PREFIX_FASTWR));

                  break;
            }

            case PPCMODE_EPP_BYTE :
            {
                  while(count)
                  {
                        outb(*data++,ppc->lpt_addr + 4);
                        count--;
                  }

                  ppc6_wait_for_fifo(ppc);

                  break;
            }

            case PPCMODE_EPP_WORD :
            {
                  while(count > 1)
                  {
                        outw(*((u16 *)data),ppc->lpt_addr + 4);
                        data  += 2;
                        count -= 2;
                  }

                  while(count)
                  {
                        outb(*data++,ppc->lpt_addr + 4);
                        count--;
                  }

                  ppc6_wait_for_fifo(ppc);

                  break;
            }

            case PPCMODE_EPP_DWORD :
            {
                  while(count > 3)
                  {
                        outl(*((u32 *)data),ppc->lpt_addr + 4);
                        data  += 4;
                        count -= 4;
                  }

                  while(count)
                  {
                        outb(*data++,ppc->lpt_addr + 4);
                        count--;
                  }

                  ppc6_wait_for_fifo(ppc);

                  break;
            }
      }
}

//***************************************************************************

static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
{
      length = length << 1;

      ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
      ppc6_wr_data_byte(ppc,(u8)length);
      ppc6_wr_data_byte(ppc,(u8)(length >> 8));
      ppc6_wr_data_byte(ppc,0);

      ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));

      ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_READ));

      ppc6_rd_data_blk(ppc, data, length);

      ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
}

//***************************************************************************

static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
{
      length = length << 1;

      ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
      ppc6_wr_data_byte(ppc,(u8)length);
      ppc6_wr_data_byte(ppc,(u8)(length >> 8));
      ppc6_wr_data_byte(ppc,0);

      ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));

      ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_WRITE));

      ppc6_wr_data_blk(ppc, data, length);

      ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
}

//***************************************************************************

static void ppc6_wr_extout(Interface *ppc, u8 regdata)
{
      ppc6_send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE));

      ppc6_wr_data_byte(ppc, (u8)((regdata & 0x03) << 6));
}

//***************************************************************************

static int ppc6_open(Interface *ppc)
{
      int ret;

      ret = ppc6_select(ppc);

      if (ret == 0)
            return(ret);

      ppc->ppc_flags &= ~fifo_wait;

      ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE));
      ppc6_wr_data_byte(ppc, RAMSIZE_128K);

      ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_READ | REG_VERSION));

      if ((ppc6_rd_data_byte(ppc) & 0x3F) == 0x0C)
            ppc->ppc_flags |= fifo_wait;

      return(ret);
}

//***************************************************************************

static void ppc6_close(Interface *ppc)
{
      ppc6_deselect(ppc);
}

//***************************************************************************


Generated by  Doxygen 1.6.0   Back to index