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

io.c

/*
 * linux/arch/sh/boards/superh/microdev/io.c
 *
 * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
 * Copyright (C) 2003, 2004 SuperH, Inc.
 * Copyright (C) 2004 Paul Mundt
 *
 * SuperH SH4-202 MicroDev board support.
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See linux/COPYING for more information.
 */

#include <linux/init.h>
#include <linux/pci.h>
#include <linux/wait.h>
#include <asm/io.h>
#include <asm/microdev.h>

      /*
       *    we need to have a 'safe' address to re-direct all I/O requests
       *    that we do not explicitly wish to handle. This safe address
       *    must have the following properies:
       *
       *          * writes are ignored (no exception)
       *          * reads are benign (no side-effects)
       *          * accesses of width 1, 2 and 4-bytes are all valid.
       *
       *    The Processor Version Register (PVR) has these properties.
       */
#define     PVR   0xff000030  /* Processor Version Register */


#define     IO_IDE2_BASE            0x170ul     /* I/O base for SMSC FDC37C93xAPM IDE #2 */
#define     IO_IDE1_BASE            0x1f0ul     /* I/O base for SMSC FDC37C93xAPM IDE #1 */
#define IO_ISP1161_BASE       0x290ul /* I/O port for Philips ISP1161x USB chip */
#define IO_SERIAL2_BASE       0x2f8ul /* I/O base for SMSC FDC37C93xAPM Serial #2 */
#define     IO_LAN91C111_BASE 0x300ul     /* I/O base for SMSC LAN91C111 Ethernet chip */
#define     IO_IDE2_MISC            0x376ul     /* I/O misc for SMSC FDC37C93xAPM IDE #2 */
#define IO_SUPERIO_BASE       0x3f0ul /* I/O base for SMSC FDC37C93xAPM SuperIO chip */
#define     IO_IDE1_MISC            0x3f6ul     /* I/O misc for SMSC FDC37C93xAPM IDE #1 */
#define IO_SERIAL1_BASE       0x3f8ul /* I/O base for SMSC FDC37C93xAPM Serial #1 */

#define     IO_ISP1161_EXTENT 0x04ul      /* I/O extent for Philips ISP1161x USB chip */
#define     IO_LAN91C111_EXTENT     0x10ul      /* I/O extent for SMSC LAN91C111 Ethernet chip */
#define     IO_SUPERIO_EXTENT 0x02ul      /* I/O extent for SMSC FDC37C93xAPM SuperIO chip */
#define     IO_IDE_EXTENT           0x08ul      /* I/O extent for IDE Task Register set */
#define IO_SERIAL_EXTENT      0x10ul

#define     IO_LAN91C111_PHYS 0xa7500000ul      /* Physical address of SMSC LAN91C111 Ethernet chip */
#define     IO_ISP1161_PHYS         0xa7700000ul      /* Physical address of Philips ISP1161x USB chip */
#define     IO_SUPERIO_PHYS         0xa7800000ul      /* Physical address of SMSC FDC37C93xAPM SuperIO chip */

/*
 * map I/O ports to memory-mapped addresses
 */
static unsigned long microdev_isa_port2addr(unsigned long offset)
{
      unsigned long result;

      if ((offset >= IO_LAN91C111_BASE) &&
          (offset <  IO_LAN91C111_BASE + IO_LAN91C111_EXTENT)) {
                  /*
                   *    SMSC LAN91C111 Ethernet chip
                   */
            result = IO_LAN91C111_PHYS + offset - IO_LAN91C111_BASE;
      } else if ((offset >= IO_SUPERIO_BASE) &&
               (offset <  IO_SUPERIO_BASE + IO_SUPERIO_EXTENT)) {
                  /*
                   *    SMSC FDC37C93xAPM SuperIO chip
                   *
                   *    Configuration Registers
                   */
            result = IO_SUPERIO_PHYS + (offset << 1);
#if 0
      } else if (offset == KBD_DATA_REG || offset == KBD_CNTL_REG ||
               offset == KBD_STATUS_REG) {
                  /*
                   *    SMSC FDC37C93xAPM SuperIO chip
                   *
                   *    PS/2 Keyboard + Mouse (ports 0x60 and 0x64).
                   */
              result = IO_SUPERIO_PHYS + (offset << 1);
#endif
      } else if (((offset >= IO_IDE1_BASE) &&
                (offset <  IO_IDE1_BASE + IO_IDE_EXTENT)) ||
                (offset == IO_IDE1_MISC)) {
                  /*
                   *    SMSC FDC37C93xAPM SuperIO chip
                   *
                   *    IDE #1
                   */
              result = IO_SUPERIO_PHYS + (offset << 1);
      } else if (((offset >= IO_IDE2_BASE) &&
                (offset <  IO_IDE2_BASE + IO_IDE_EXTENT)) ||
                (offset == IO_IDE2_MISC)) {
                  /*
                   *    SMSC FDC37C93xAPM SuperIO chip
                   *
                   *    IDE #2
                   */
              result = IO_SUPERIO_PHYS + (offset << 1);
      } else if ((offset >= IO_SERIAL1_BASE) &&
               (offset <  IO_SERIAL1_BASE + IO_SERIAL_EXTENT)) {
                  /*
                   *    SMSC FDC37C93xAPM SuperIO chip
                   *
                   *    Serial #1
                   */
            result = IO_SUPERIO_PHYS + (offset << 1);
      } else if ((offset >= IO_SERIAL2_BASE) &&
               (offset <  IO_SERIAL2_BASE + IO_SERIAL_EXTENT)) {
                  /*
                   *    SMSC FDC37C93xAPM SuperIO chip
                   *
                   *    Serial #2
                   */
            result = IO_SUPERIO_PHYS + (offset << 1);
      } else if ((offset >= IO_ISP1161_BASE) &&
               (offset < IO_ISP1161_BASE + IO_ISP1161_EXTENT)) {
                  /*
                   *    Philips USB ISP1161x chip
                   */
            result = IO_ISP1161_PHYS + offset - IO_ISP1161_BASE;
      } else {
                  /*
                   *    safe default.
                   */
            printk("Warning: unexpected port in %s( offset = 0x%lx )\n",
                   __FUNCTION__, offset);
            result = PVR;
      }

      return result;
}

#define PORT2ADDR(x) (microdev_isa_port2addr(x))

static inline void delay(void)
{
#if defined(CONFIG_PCI)
      /* System board present, just make a dummy SRAM access.  (CS0 will be
         mapped to PCI memory, probably good to avoid it.) */
      ctrl_inw(0xa6800000);
#else
      /* CS0 will be mapped to flash, ROM etc so safe to access it. */
      ctrl_inw(0xa0000000);
#endif
}

unsigned char microdev_inb(unsigned long port)
{
#ifdef CONFIG_PCI
      if (port >= PCIBIOS_MIN_IO)
            return microdev_pci_inb(port);
#endif
      return *(volatile unsigned char*)PORT2ADDR(port);
}

unsigned short microdev_inw(unsigned long port)
{
#ifdef CONFIG_PCI
      if (port >= PCIBIOS_MIN_IO)
            return microdev_pci_inw(port);
#endif
      return *(volatile unsigned short*)PORT2ADDR(port);
}

unsigned int microdev_inl(unsigned long port)
{
#ifdef CONFIG_PCI
      if (port >= PCIBIOS_MIN_IO)
            return microdev_pci_inl(port);
#endif
      return *(volatile unsigned int*)PORT2ADDR(port);
}

void microdev_outw(unsigned short b, unsigned long port)
{
#ifdef CONFIG_PCI
      if (port >= PCIBIOS_MIN_IO) {
            microdev_pci_outw(b, port);
            return;
      }
#endif
      *(volatile unsigned short*)PORT2ADDR(port) = b;
}

void microdev_outb(unsigned char b, unsigned long port)
{
#ifdef CONFIG_PCI
      if (port >= PCIBIOS_MIN_IO) {
            microdev_pci_outb(b, port);
            return;
      }
#endif

      /*
       *    There is a board feature with the current SH4-202 MicroDev in
       *    that the 2 byte enables (nBE0 and nBE1) are tied together (and
       *    to the Chip Select Line (Ethernet_CS)). Due to this connectivity,
       *    it is not possible to safely perform 8-bit writes to the
       *    Ethernet registers, as 16-bits will be consumed from the Data
       *    lines (corrupting the other byte).  Hence, this function is
       *    written to implement 16-bit read/modify/write for all byte-wide
       *    accesses.
       *
       *    Note: there is no problem with byte READS (even or odd).
       *
       *                Sean McGoogan - 16th June 2003.
       */
      if ((port >= IO_LAN91C111_BASE) &&
          (port <  IO_LAN91C111_BASE + IO_LAN91C111_EXTENT)) {
                  /*
                   * Then are trying to perform a byte-write to the
                   * LAN91C111.  This needs special care.
                   */
            if (port % 2 == 1) {    /* is the port odd ? */
                  /* unset bit-0, i.e. make even */
                  const unsigned long evenPort = port-1;
                  unsigned short word;

                  /*
                   * do a 16-bit read/write to write to 'port',
                   * preserving even byte.
                   *
                   *    Even addresses are bits 0-7
                   *    Odd  addresses are bits 8-15
                   */
                  word = microdev_inw(evenPort);
                  word = (word & 0xffu) | (b << 8);
                  microdev_outw(word, evenPort);
            } else {
                  /* else, we are trying to do an even byte write */
                  unsigned short word;

                  /*
                   * do a 16-bit read/write to write to 'port',
                   * preserving odd byte.
                   *
                   *    Even addresses are bits 0-7
                   *    Odd  addresses are bits 8-15
                   */
                  word = microdev_inw(port);
                  word = (word & 0xff00u) | (b);
                  microdev_outw(word, port);
            }
      } else {
            *(volatile unsigned char*)PORT2ADDR(port) = b;
      }
}

void microdev_outl(unsigned int b, unsigned long port)
{
#ifdef CONFIG_PCI
      if (port >= PCIBIOS_MIN_IO) {
            microdev_pci_outl(b, port);
            return;
      }
#endif
      *(volatile unsigned int*)PORT2ADDR(port) = b;
}

unsigned char microdev_inb_p(unsigned long port)
{
      unsigned char v = microdev_inb(port);
      delay();
      return v;
}

unsigned short microdev_inw_p(unsigned long port)
{
      unsigned short v = microdev_inw(port);
      delay();
      return v;
}

unsigned int microdev_inl_p(unsigned long port)
{
      unsigned int v = microdev_inl(port);
      delay();
      return v;
}

void microdev_outb_p(unsigned char b, unsigned long port)
{
      microdev_outb(b, port);
      delay();
}

void microdev_outw_p(unsigned short b, unsigned long port)
{
      microdev_outw(b, port);
      delay();
}

void microdev_outl_p(unsigned int b, unsigned long port)
{
      microdev_outl(b, port);
      delay();
}

void microdev_insb(unsigned long port, void *buffer, unsigned long count)
{
      volatile unsigned char *port_addr;
      unsigned char *buf = buffer;

      port_addr = (volatile unsigned char *)PORT2ADDR(port);

      while (count--)
            *buf++ = *port_addr;
}

void microdev_insw(unsigned long port, void *buffer, unsigned long count)
{
      volatile unsigned short *port_addr;
      unsigned short *buf = buffer;

      port_addr = (volatile unsigned short *)PORT2ADDR(port);

      while (count--)
            *buf++ = *port_addr;
}

void microdev_insl(unsigned long port, void *buffer, unsigned long count)
{
      volatile unsigned long *port_addr;
      unsigned int *buf = buffer;

      port_addr = (volatile unsigned long *)PORT2ADDR(port);

      while (count--)
            *buf++ = *port_addr;
}

void microdev_outsb(unsigned long port, const void *buffer, unsigned long count)
{
      volatile unsigned char *port_addr;
      const unsigned char *buf = buffer;

      port_addr = (volatile unsigned char *)PORT2ADDR(port);

      while (count--)
            *port_addr = *buf++;
}

void microdev_outsw(unsigned long port, const void *buffer, unsigned long count)
{
      volatile unsigned short *port_addr;
      const unsigned short *buf = buffer;

      port_addr = (volatile unsigned short *)PORT2ADDR(port);

      while (count--)
            *port_addr = *buf++;
}

void microdev_outsl(unsigned long port, const void *buffer, unsigned long count)
{
      volatile unsigned long *port_addr;
      const unsigned int *buf = buffer;

      port_addr = (volatile unsigned long *)PORT2ADDR(port);

      while (count--)
            *port_addr = *buf++;
}

Generated by  Doxygen 1.6.0   Back to index