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

chip_ti_1520.c

/*
 * $Id: chip_ti_1520.c,v 1.136 2009-01-28 12:59:19 potyra Exp $
 *
 * Copyright (C) 2003-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include "config.h"

#include <assert.h>
#include "fixme.h"
#include <stdio.h>
#include <string.h>

#include "pci.h"
#include "glue-shm.h"
#include "glue-log.h"

#include "chip_ti_1520.h"

#define CHIP_(x) chip_ti_1520_ ## x
#define CHIP "chip_ti_1520"

struct cpssp {
      /*
       * Config
       */

      /*
       * Signals
       */
      /* PCI bus */
      struct sig_pci_bus_main *port_pci_bus;
      struct sig_boolean_or *port_int[2];
      /* Cardbus 0 */
      struct sig_boolean *port_cb0_power;
      struct sig_boolean *port_cb0_reset_hash_;
      struct sig_pci_bus_main *port_cb0;
      int state_cb0_conn;
      /* Cardbus 1 */
      struct sig_boolean *port_cb1_power;
      struct sig_boolean *port_cb1_reset_hash_;
      struct sig_pci_bus_main *port_cb1;
      int state_cb1_conn;

      /*
       * State
       */
      enum chip_ti_1520_region {
            REGION_NONE,
            REGION_CHIP_LEGACY,
            REGION_CHIP_REGISTER0,
            REGION_CHIP_REGISTER1,
            REGION_PCI,
            REGION_CB0,
            REGION_CB0_CSPACE,
            REGION_CB1,
            REGION_CB1_CSPACE,
      } selected;
      unsigned int selected_reg;
      int powered[2];
      uint8_t int_set[2];
      uint32_t config_space[2][64]; /* Chip has two PCI functions */
      uint32_t cardbus_regs[2][9];
      uint8_t current_exca_reg;
      uint8_t exca_regs[128];
      uint8_t page_regs[2][5];
};

/* some PCI config space offsets */
#define CB_SOCKET_BASE_ADDR   0x10
#define CAPABILITY_POINTER    0x14
#define SECONDARY_STATUS      0x16
#define PCI_BUS_NUM           0x18
#define CB_BUS_NUM            0x19
#define SUB_BUS_NUM           0x1A
#define CB_LATENCY_TIMER      0x1B
#define MEMORY_BASE_0         0x1C
#define MEMORY_LIMIT_0        0x20
#define MEMORY_BASE_1         0x24
#define MEMORY_LIMIT_1        0x28
#define IO_BASE_0       0x2C
#define IO_LIMIT_0            0x30
#define IO_BASE_1       0x34
#define IO_LIMIT_1            0x38
#define INT_LINE        0x3C
#define INT_PIN               0x3D
#define BRIDGE_CONTROL        0x3E
#define SUBSYSTEM_VENDOR_ID   0x40
#define SUBSYSTEM_ID          0x42
#define PCCARD_16_BASE_ADDR   0x44
#define SYSTEM_CONTROL        0x80
#define MULTI_FUNCTION_ROUTING      0x8C
#define RETRY_STATUS          0x90
#define CARD_CONTROL          0x91
#define DEVICE_CONTROL        0x92
#define DIAGNOSTIC            0x93
#define CAPABILITY_ID         0xA0
#define NEXT_ITEM_POINTER     0xA1
#define PWR_MNG_CAPABILITIES  0xA2
#define PWR_MNG_CONTROL_STATUS      0xA4
#define PWR_MNG_CTRLSTAT_EXT  0xA6
#define PWR_MNG_DATA          0xA7
#define GP_EVENT_STATUS       0xA8
#define GP_EVENT_ENABLE       0xAA
#define GP_INPUT        0xAC
#define GP_OUTPUT       0xAE
#define SBUS_DATA       0xB0
#define SBUS_INDEX            0xB1
#define SBUS_SLAVE_ADDR       0xB2
#define SBUS_CONTROL_STATUS   0xB3

#define SOCKETADDR(cpssp,func)      (cpssp->config_space[func][CB_SOCKET_BASE_ADDR>>2])
#define MEMADDR_0(cpssp,func) (cpssp->config_space[func][MEMORY_BASE_0>>2])
#define MEMLIMIT_0(cpssp,func)      (cpssp->config_space[func][MEMORY_LIMIT_0>>2] + 4095)
#define MEMADDR_1(cpssp,func) (cpssp->config_space[func][MEMORY_BASE_1>>2])
#define MEMLIMIT_1(cpssp,func)      (cpssp->config_space[func][MEMORY_LIMIT_1>>2] + 4095)
#define SZWINDOW_0(cpssp, func)     (MEMLIMIT_0(cpssp, func) - MEMADDR_0(cpssp, func) + 1)
#define SZWINDOW_1(cpssp, func)     (MEMLIMIT_1(cpssp, func) - MEMADDR_1(cpssp, func) + 1)
#define CB_BUS(cpssp,func)    (pci_getconfigb(cpssp->config_space[func], CB_BUS_NUM))
#define SUB_BUS(cpssp,func)   (pci_getconfigb(cpssp->config_space[func], SUB_BUS_NUM))
#define CARDBUSREG(cpssp,func,reg) (cpssp->cardbus_regs[func][(reg) >> 2])
#define LEGACYADDR(cpssp)           (cpssp->config_space[0][PCCARD_16_BASE_ADDR>>2] & 0xFFFF)

/* socket event register */
#define PWREVENT(cpssp, func) (cpssp->cardbus_regs[func][0] & (1 << 3))
#define CD2EVENT(cpssp, func) (cpssp->cardbus_regs[func][0] & (1 << 2))
#define CD1EVENT(cpssp, func) (cpssp->cardbus_regs[func][0] & (1 << 1))
#define CSTSEVENT(cpssp, func)      (cpssp->cardbus_regs[func][0] & (1 << 0))
/* socket mask register */
#define PWRMASK(cpssp, func)  (cpssp->cardbus_regs[func][1] & (1 << 3))
#define CD2MASK(cpssp, func)  (cpssp->cardbus_regs[func][1] & (1 << 2))
#define CD1MASK(cpssp, func)  (cpssp->cardbus_regs[func][1] & (1 << 1))
#define CSTSMASK(cpssp, func) (cpssp->cardbus_regs[func][1] & (1 << 0))
/* socket present state register */
#define CBCARD(cpssp, func)   (cpssp->cardbus_regs[func][2] & (1 << 5))
#define PC16BITCARD(cpssp, func)    (cpssp->cardbus_regs[func][2] & (1 << 4))
#define PWRCYCLE(cpssp, func) (cpssp->cardbus_regs[func][2] & (1 << 3))
#define CDETECT2(cpssp, func) (cpssp->cardbus_regs[func][2] & (1 << 2))
#define CDETECT1(cpssp, func) (cpssp->cardbus_regs[func][2] & (1 << 1))
#define CARDSTS(cpssp, func)  (cpssp->cardbus_regs[func][2] & (1 << 0))

#define FCTINTERRUPT(cpssp, func)   (cpssp->config_space[func][RETRY_STATUS>>2] |= (1 << 8))
#define TIEDINTPINS(cpssp)    (cpssp->config_space[0][SYSTEM_CONTROL>>2] & (1 << 29))

static void
chip_ti_1520_irq_update(struct cpssp *cpssp, unsigned int func)
{
      unsigned int val;

      if (TIEDINTPINS(cpssp)) {
            if ((CSTSEVENT(cpssp, 0) && CSTSMASK(cpssp, 0))
             || (CD1EVENT(cpssp, 0) && CD1MASK(cpssp, 0))
             || (CD2EVENT(cpssp, 0) && CD2MASK(cpssp, 0))
             || (PWRCYCLE(cpssp, 0) && PWRMASK(cpssp, 0))
             || cpssp->int_set[0]
             || (CSTSEVENT(cpssp, 1) && CSTSMASK(cpssp, 1))
             || (CD1EVENT(cpssp, 1) && CD1MASK(cpssp, 1))
             || (CD2EVENT(cpssp, 1) && CD2MASK(cpssp, 1))
             || (PWRCYCLE(cpssp, 1) && PWRMASK(cpssp, 1))
             || cpssp->int_set[1]) {
                  val = 1;
            } else {
                  val = 0;
            }
            sig_boolean_or_set(cpssp->port_int[0], cpssp, val);

      } else {
            if ((CSTSEVENT(cpssp, func) && CSTSMASK(cpssp, func))
             || (CD1EVENT(cpssp, func) && CD1MASK(cpssp, func))
             || (CD2EVENT(cpssp, func) && CD2MASK(cpssp, func))
             || (PWRCYCLE(cpssp, func) && PWRMASK(cpssp, func))
             || cpssp->int_set[func]) {
                  val = 1;
            } else {
                  val = 0;
            }
            sig_boolean_or_set(cpssp->port_int[func], cpssp, val);
      }
}

static void
chip_ti_1520__conn_set(struct cpssp *cpssp, unsigned int func, unsigned int val)
{
      uint32_t regval;

      if (val) {
            /*
             * New Device Attached
             */
            /* Socket Present State */
            regval = 1 << 29; /* Socket supports 5V */
            regval |= 1 << 28; /* Socket supports 3.3V */
            regval |= 1 << 11; /* 3.3V Card */
            regval |= 1 << 5; /* Cardbus Card */
            regval |= 1 << 0; /* CSTSCHG */
            cpssp->cardbus_regs[func][2] |= regval;
            regval = 1 << 2; /* CDETECT2 */
            regval |= 1 << 1; /* CDETECT1 */
            cpssp->cardbus_regs[func][2] &= ~regval;
            
            /* Socket Event */
            cpssp->cardbus_regs[func][0] |= 0x7;
            
            /* ExCA registers */
            cpssp->exca_regs[0x01 + func * 0x40] |= 0xC;
            cpssp->exca_regs[0x03 + func * 0x40] |= 1 << 5;

      } else {
            /*
             * Device Detached
             */
            /* Socket Present State */
            regval = 1 << 2; /* CDETECT2 */
            regval |= 1 << 1; /* CDETECT1 */
            cpssp->cardbus_regs[func][2] |= regval;
            
            /* Socket Event */
            cpssp->cardbus_regs[func][0] |= 0x7;

            /* ExCA registers */
            cpssp->exca_regs[0x01 + func * 0x40] &= 0xF3;
            
            /* update socket state */
            cpssp->powered[func] = 0;
            switch (func) {
            case 0:
                  sig_boolean_set(cpssp->port_cb0_power, cpssp, 0);
                  break;
            case 1:
                  sig_boolean_set(cpssp->port_cb1_power, cpssp, 0);
                  break;
            default:
                  assert(0); /* Cannot happen. */
            }
      }

      chip_ti_1520_irq_update(cpssp, func);
}

static void
_chip_ti_1520_exca_register_write(struct cpssp *cpssp, int pos, uint8_t val)
{
      uint8_t tmp;

      assert(pos < 128);
      
      switch (pos) {
      case 0x00:
      case 0x40:
            tmp = val & 0x3F;
            break;
      case 0x01:
      case 0x41:
            /* read only */
            return;
      case 0x02:
      case 0x42:
            tmp = val & 0xB3;
            break;
      case 0x04:
      case 0x44:
            tmp = val & 0x0F;
            return;
      
      case 0x06:
      case 0x46:
            tmp = val & 0xDF;
            break;
      case 0x13:
      case 0x1B:
      case 0x23:
      case 0x2B:
      case 0x33:
      case 0x53:
      case 0x5B:
      case 0x63:
      case 0x6B:
      case 0x73:
            tmp = val & 0xAF;
            break;
      case 0x16:
      case 0x56:
            tmp = val & 0x32;
            break;
      case 0x1E:
      case 0x5E:
            tmp = val & 0x1F;
            break;
      case 0x17:
      case 0x1F:
      case 0x26 ... 0x27:
      case 0x2E ... 0x2F:
      case 0x3A ... 0x3F:
      case 0x57:
      case 0x5F:
      case 0x66 ... 0x67:
      case 0x6E ... 0x6F:
      case 0x7A ... 0x7F:
            /* reserved */
            return;
      default:
            /* anything else is completely writable */
            tmp = val;
            break;
      }
      if ((pos == 0x1E) || (pos == 0x5E)) {
            /* these registers are actually the same on the chip */
            cpssp->exca_regs[0x1E] = tmp;
            cpssp->exca_regs[0x5E] = tmp;
      } else if ((pos == 0x04) || (pos == 0x44)) {
            /* this can be reset by writing a 1 */
            cpssp->exca_regs[pos] &= ~tmp;
      } else {
            cpssp->exca_regs[pos] = tmp;
      }
}

static void
_chip_ti_1520_cardbus_register_writel(
      struct cpssp *cpssp,
      unsigned int func,
      unsigned int offset,
      uint32_t val
)
{
      switch (offset) {
      case 0x00:
            /* Socket Event */
            /* 6-2 */
            cpssp->cardbus_regs[func][0] &= ~(val & 0x0F);
            chip_ti_1520_irq_update(cpssp, func);
            break;

      case 0x04:
            /* Socket Mask */
            /* 6-3 */
            cpssp->cardbus_regs[func][1] = val & 0x0F;
            break;

      case 0x08:
            /* Socket Present State */
            /* 6-4 */
            break;

      case 0x0C:
            /* Socket Force Event */
            /* 6-6 */
            if (val & 0xB0) {
                  cpssp->cardbus_regs[func][2] |= val & 0xB0;
            }
            if (val & (1 << 14)) {
                  /* Force card re-interrogation */
                  if ((func == 0
                    && cpssp->state_cb0_conn)
                   || (func == 1
                    && cpssp->state_cb1_conn)) {
                        /* update socket present state register */
                        /* FIXME this forces a 3V cardbus card */
                        cpssp->cardbus_regs[func][2] |= 1 << 11;
                        cpssp->cardbus_regs[func][2] |= 1 << 5;
                        cpssp->cardbus_regs[func][2] &= ~0x06;
                        /* update socket event register */
                        cpssp->cardbus_regs[func][0] |= 0x06;
                        /* generate interrupt if not masked */
                  }
            }
            if (val & (1 << 7)) {
                  cpssp->cardbus_regs[func][2] |= 1 << 7;
            }
            if (val & (1 << 5)) {
                  cpssp->cardbus_regs[func][2] |= 1 << 5;
            }
            if (val & (1 << 4)) {
                  cpssp->cardbus_regs[func][2] |= 1 << 4;
            }
            if (val & (1 << 3)) {
                  cpssp->cardbus_regs[func][0] |= 1 << 3;
            }
            if (val & (1 << 2)) {
                  cpssp->cardbus_regs[func][0] |= 1 << 2;
            }
            if (val & (1 << 1)) {
                  cpssp->cardbus_regs[func][0] |= 1 << 1;
            }
            if (val & (1 << 0)) {
                  cpssp->cardbus_regs[func][0] |= 1 << 0;
            }

            chip_ti_1520_irq_update(cpssp, func);
            break;

      case 0x10:
            /* Socket Control */
            /* 6-8 */
            cpssp->cardbus_regs[func][4] = val & 0x02F7;
            if ((val & 0x03) && (val & 0x30)) {
                  /* set power up bit -- FIXME add bad voltage requests too */
                  cpssp->cardbus_regs[func][2] |= 1 << 3;
                  cpssp->cardbus_regs[func][0] |= 1 << 3;
                  cpssp->powered[func] = 1;
                  switch (func) {
                  case 0:
                        sig_boolean_set(cpssp->port_cb0_power, cpssp, 1);
                        break;
                  case 1:
                        sig_boolean_set(cpssp->port_cb1_power, cpssp, 1);
                        break;
                  default:
                        assert(0); /* Cannot happen. */
                  }
            } else if (!(val & 0x33)) {
                  cpssp->cardbus_regs[func][2] &= ~(1 << 3);
                  cpssp->cardbus_regs[func][0] |= 1 << 3;
                  cpssp->powered[func] = 0;
                  switch (func) {
                  case 0:
                        sig_boolean_set(cpssp->port_cb0_power, cpssp, 0);
                        break;
                  case 1:
                        sig_boolean_set(cpssp->port_cb1_power, cpssp, 0);
                        break;
                  default:
                        assert(0); /* Cannot happen. */
                  }
            }
            chip_ti_1520_irq_update(cpssp, func);
            break;

      case 0x14:
      case 0x18:
      case 0x1C:
            /* Reserved */
            break;

      case 0x20:
            /* Socket Power-Management */
            /* 6-9 */
            cpssp->cardbus_regs[func][8] = val & 0x010001;
            break;

      default:
            /* Reserved */
            break;
      }
}

static void
chip_ti_1520_legacy_in(
      struct cpssp *cpssp,
      uint32_t *valp,
      uint16_t port,
      unsigned int bs
)
{
      *valp = 0x00000000;
      if ((bs >> 0) & 1) {
            *valp |= cpssp->current_exca_reg << 0;
      }
      if ((bs >> 1) & 1) {
            *valp |= cpssp->exca_regs[cpssp->current_exca_reg] << 8;
      }
      if ((bs >> 2) & 1) {
            /* Nothing there... */
      }
      if ((bs >> 3) & 1) {
            /* Nothing there... */
      }
}

static void
chip_ti_1520_legacy_out(
      struct cpssp *cpssp,
      uint32_t val,
      uint16_t port,
      unsigned int bs
)
{
      if ((bs >> 0) & 1) {
            cpssp->current_exca_reg = val >> 0;
      }
      if ((bs >> 1) & 1) {
            _chip_ti_1520_exca_register_write(cpssp, cpssp->current_exca_reg, val);
      }
      if ((bs >> 2) & 1) {
            /* Nothing there... */
      }
      if ((bs >> 3) & 1) {
            /* Nothing there... */
      }
}

static void
chip_ti_1520_register_read(
      struct cpssp *cpssp,
      unsigned int func,
      uint32_t *valp,
      uint32_t addr,
      unsigned int bs
)
{
      unsigned int offset;

      offset = addr & 0xfff;
      
      if (offset < 0x800) {
            /* CardBus Registers */
            if ((bs >> 0) & 1) {
                  *valp &= ~(0xff << 0);
                  *valp |= CARDBUSREG(cpssp, func, offset) & (0xff << 0);
            }
            if ((bs >> 1) & 1) {
                  *valp &= ~(0xff << 8);
                  *valp |= CARDBUSREG(cpssp, func, offset) & (0xff << 8);
            }
            if ((bs >> 2) & 1) {
                  *valp &= ~(0xff << 16);
                  *valp |= CARDBUSREG(cpssp, func, offset) & (0xff << 16);
            }
            if ((bs >> 3) & 1) {
                  *valp &= ~(0xff << 24);
                  *valp |= CARDBUSREG(cpssp, func, offset) & (0xff << 24);
            }

      } else if (0x800 <= offset && offset < 0x840) {
            /* ExCA Compatibility Registers */
            if ((bs >> 0) & 1) {
                  *valp &= ~(0xff << 0);
                  *valp |= cpssp->exca_regs[offset - 0x800 + func * 0x40 + 0] << 0;
                  if (offset == 0x804
                   && (cpssp->exca_regs[0x1E] & 0x2) == 0) {
                        /* this register can be reset when read */
                        cpssp->exca_regs[0x1E] = 0;
                        cpssp->exca_regs[0x5E] = 0;
                  }
            }
            if ((bs >> 1) & 1) {
                  *valp &= ~(0xff << 8);
                  *valp |= cpssp->exca_regs[offset - 0x800 + func * 0x40 + 1] << 8;
            }
            if ((bs >> 2) & 1) {
                  *valp &= ~(0xff << 16);
                  *valp |= cpssp->exca_regs[offset - 0x800 + func * 0x40 + 2] << 16;
            }
            if ((bs >> 3) & 1) {
                  *valp &= ~(0xff << 24);
                  *valp |= cpssp->exca_regs[offset - 0x800 + func * 0x40 + 3] << 24;
            }

      } else if (0x840 <= offset && offset < 0x845) {
            /* Memory Window Page Registers */
            /* Not addressable via ExCA-Registers although they
             * are only used together with ExCA functions.
             * These registers are only accessible through the
             * memory mapped version of the registers. */
            if ((bs >> 0) & 1) {
                  *valp &= ~(0xff << 0);
                  *valp |= cpssp->page_regs[func][offset - 0x840 + 0] << 0;
            }
            if ((bs >> 1) & 1) {
                  *valp &= ~(0xff << 8);
                  *valp |= cpssp->page_regs[func][offset - 0x840 + 1] << 8;
            }
            if ((bs >> 2) & 1) {
                  *valp &= ~(0xff << 16);
                  *valp |= cpssp->page_regs[func][offset - 0x840 + 2] << 16;
            }
            if ((bs >> 3) & 1) {
                  *valp &= ~(0xff << 24);
                  *valp |= cpssp->page_regs[func][offset - 0x840 + 3] << 24;
            }

      } else {
            fixme();
      }
}

static void
chip_ti_1520_register_write(
      struct cpssp *cpssp,
      unsigned int func,
      uint32_t val,
      uint32_t addr,
      unsigned int bs
)
{
      unsigned int offset;
      uint32_t tmp;
      
      assert((addr & 3) == 0);
      offset = addr & 0xfff;
      
      if (offset < 0x800) {
            /* CardBus Registers */
            tmp = CARDBUSREG(cpssp, func, offset);
            if ((bs >> 0) & 1) {
                  tmp &= ~(0xff << 0);
                  tmp |= val & (0xff << 0);
            }
            if ((bs >> 1) & 1) {
                  tmp &= ~(0xff << 8);
                  tmp |= val & (0xff << 8);
            }
            if ((bs >> 2) & 1) {
                  tmp &= ~(0xff << 16);
                  tmp |= val & (0xff << 16);
            }
            if ((bs >> 3) & 1) {
                  tmp &= ~(0xff << 24);
                  tmp |= val & (0xff << 24);
            }
            _chip_ti_1520_cardbus_register_writel(cpssp, func, offset, tmp);

      } else if (0x800 <= offset && offset < 0x840) {
            /* ExCA Compatibility Registers */
            if ((bs >> 0) & 1) {
                  _chip_ti_1520_exca_register_write(cpssp,
                              (offset & 0x3f) + func * 0x40 + 0,
                              (val >> 0) & 0xff);
            }
            if ((bs >> 1) & 1) {
                  _chip_ti_1520_exca_register_write(cpssp,
                              (offset & 0x3f) + func * 0x40 + 1,
                              (val >> 8) & 0xff);
            }
            if ((bs >> 2) & 1) {
                  _chip_ti_1520_exca_register_write(cpssp,
                              (offset & 0x3f) + func * 0x40 + 2,
                              (val >> 16) & 0xff);
            }
            if ((bs >> 3) & 1) {
                  _chip_ti_1520_exca_register_write(cpssp,
                              (offset & 0x3f) + func * 0x40 + 3,
                              (val >> 24) & 0xff);
            }

      } else if (offset == 0x840) {
            /* Memory Window Page Registers */
            if ((bs >> 0) & 1) {
                  cpssp->page_regs[func][offset - 0x840 + 0]
                        = (val >> 0) & 0xff;
            }
            if ((bs >> 1) & 1) {
                  cpssp->page_regs[func][offset - 0x840 + 1]
                        = (val >> 8) & 0xff;
            }
            if ((bs >> 2) & 1) {
                  cpssp->page_regs[func][offset - 0x840 + 2]
                        = (val >> 16) & 0xff;
            }
            if ((bs >> 3) & 1) {
                  cpssp->page_regs[func][offset - 0x840 + 3]
                        = (val >> 24) & 0xff;
            }
      } else if (offset == 0x844) {
            /* Memory Window Page Registers */
            if ((bs >> 0) & 1) {
                  cpssp->page_regs[func][offset - 0x840 + 0]
                        = (val >> 0) & 0xff;
            }
            if ((bs >> 1) & 1) {
                  /* Not used. */
            }
            if ((bs >> 2) & 1) {
                  /* Not used. */
            }
            if ((bs >> 3) & 1) {
                  /* Not used. */
            }

      } else {
            fixme();
      }
}

static void
_chip_ti_1520_cwritel(
      struct cpssp *cpssp,
      unsigned int func,
      unsigned char addr,
      uint32_t val
)
{
      unsigned long tmp;
      uint32_t oaddr;
      uint32_t naddr;
            
      assert(func == 0 || func == 1);

      switch (addr) {
      case PCI_COMMAND:
            tmp = pci_getconfigl(cpssp->config_space[func], addr);
            tmp &= ~(val & 0xF9000000UL); /* reset bits that can be cleared */
            val &= 0x00000167UL;          /* filter out read-only bits */
            val |= 0x02100000UL;          /* OR in the default '1's */
            val |= (tmp & 0xF9000000UL);  /* and OR in the set/reset bits */
            pci_setconfigl(cpssp->config_space[func], addr, val);
            
            val &= 0xFFFF0000UL;    /* some bits are global for both functions */
            tmp = pci_getconfigl(cpssp->config_space[(func + 1) % 2], addr);
            tmp &= 0x0000FFFFUL;
            val |= tmp;
            pci_setconfigl(cpssp->config_space[(func + 1) % 2], addr, val);
            break;
            
      case PCI_CACHE_LINE_SIZE:
            val &= 0x0000FFFFUL;
            val |= 0x00820000UL;
            pci_setconfigl(cpssp->config_space[func], addr, val);
            
            val &= 0xFFFF00FF;
            tmp = pci_getconfigl(cpssp->config_space[(func + 1) % 2], addr);
            tmp &= 0x0000FF00;
            val |= tmp;
            pci_setconfigl(cpssp->config_space[(func + 1) % 2], addr, val);
            break;
            
      case CB_SOCKET_BASE_ADDR: /* Request 4 KB Memory Space for CardBus registers */
            oaddr = SOCKETADDR(cpssp, func);
            cpssp->config_space[func][addr>>2] = pci_requestspace(
                        val,
                        4096,
                        PCI_BASE_ADDRESS_SPACE_MEMORY
                        | PCI_BASE_ADDRESS_MEM_TYPE_32);
            naddr = SOCKETADDR(cpssp, func);
            if (oaddr != naddr) {
                  /* Remap old and new regions */
                  sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp,
                              oaddr, 4096);
                  sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp,
                              naddr, 4096);
            }
            break;
            
      case CAPABILITY_POINTER:
            tmp = pci_getconfigl(cpssp->config_space[func], addr);
            tmp &= ~(val & 0xF9000000UL);
            val = 0x020000A0UL;
            val |= (tmp & 0xF9000000UL);
            pci_setconfigl(cpssp->config_space[func], addr, val);
            break;
            
      case PCI_BUS_NUM:
            pci_setconfigl(cpssp->config_space[func], addr, val);
            
            val &= 0xFF0000FFUL;
            tmp = pci_getconfigl(cpssp->config_space[(func + 1) % 2], addr);
            tmp &= 0x00FFFF00UL;
            val |= tmp;
            pci_setconfigl(cpssp->config_space[(func + 1) % 2], addr, val);
            break;
            
      case MEMORY_BASE_0:
            oaddr = MEMADDR_0(cpssp, func);
            cpssp->config_space[func][addr>>2] = val&0xFFFFF000UL;
            naddr = MEMADDR_0(cpssp, func);

            if (oaddr != naddr) {
                  if ((MEMLIMIT_0(cpssp, func) - oaddr) < 0) {
                        /* Memory window was deactivated ... */
                        if (0 < SZWINDOW_0(cpssp, func)) {
                              /* ... and is now activated */
                              /* remap new region */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          naddr,
                                          SZWINDOW_0(cpssp, func));
                        } else {
                              /* ... and is still deactivated */
                              /* do nothing */
                        }
                  } else {
                        /* Memory window was activated ... */
                        if (0 < SZWINDOW_0(cpssp, func)) {
                              /* ... and is still activated */
                              /* remap old and new regions */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          oaddr,
                                          MEMLIMIT_0(cpssp, func) - oaddr);
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          naddr,
                                          SZWINDOW_0(cpssp, func));
                        } else {
                              /* ... and is now deactivated */
                              /* remap old region */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          oaddr,
                                          MEMLIMIT_0(cpssp, func) - oaddr);
                        }
                  }
            }
            break;
            
      case MEMORY_LIMIT_0:
            oaddr = MEMLIMIT_0(cpssp, func);
            cpssp->config_space[func][addr>>2] = val&0xFFFFF000UL;
            naddr = MEMLIMIT_0(cpssp, func);

            if (oaddr != naddr) {
                  if ((oaddr - MEMADDR_0(cpssp, func)) < 0) {
                        /* Memory window was deactivated ... */
                        if (0 < SZWINDOW_0(cpssp, func)) {
                              /* ... and is now activated */
                              /* remap new region */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          MEMADDR_0(cpssp, func),
                                          SZWINDOW_0(cpssp, func));
                        } else {
                              /* ... and is still deactivated */
                              /* do nothing */
                        }
                  } else {
                        /* Memory window was activated ... */
                        if (0 < SZWINDOW_0(cpssp, func)) {
                              /* ... and is still activated */
                              /* remap old and new regions */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          MEMADDR_0(cpssp, func),
                                          oaddr - MEMADDR_0(cpssp, func));
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          MEMADDR_0(cpssp, func),
                                          SZWINDOW_0(cpssp, func));
                        } else {
                              /* ... and is now deactivated */
                              /* remap old region */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          MEMADDR_0(cpssp, func),
                                          oaddr - MEMADDR_0(cpssp, func));
                        }
                  }
            }
            break;
            
      case MEMORY_BASE_1:
            oaddr = MEMADDR_1(cpssp, func);
            cpssp->config_space[func][addr>>2] = val&0xFFFFF000UL;
            naddr = MEMADDR_1(cpssp, func);

            if (oaddr != naddr) {
                  if ((MEMLIMIT_1(cpssp, func) - oaddr) < 0) {
                        /* Memory window was deactivated ... */
                        if (0 < SZWINDOW_1(cpssp, func)) {
                              /* ... and is now activated */
                              /* remap new region */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          naddr,
                                          SZWINDOW_1(cpssp, func));
                        } else {
                              /* ... and is still deactivated */
                              /* do nothing */
                        }
                  } else {
                        /* Memory window was activated ... */
                        if (0 < SZWINDOW_1(cpssp, func)) {
                              /* ... and is still activated */
                              /* remap old and new regions */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          oaddr,
                                          MEMLIMIT_1(cpssp, func) - oaddr);
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          naddr,
                                          SZWINDOW_1(cpssp, func));
                        } else {
                              /* ... and is now deactivated */
                              /* remap old region */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          oaddr,
                                          MEMLIMIT_1(cpssp, func) - oaddr);
                        }
                  }
            }
            break;
            
      case MEMORY_LIMIT_1:
            oaddr = MEMLIMIT_1(cpssp, func);
            cpssp->config_space[func][addr >> 2] = val & 0xFFFFF000UL;
            naddr = MEMLIMIT_1(cpssp, func);

            if (oaddr != naddr) {
                  if (oaddr - MEMADDR_1(cpssp, func) < 0) {
                        /* Memory window was deactivated ... */
                        if (0 < SZWINDOW_1(cpssp, func)) {
                              /* ... and is now activated */
                              /* remap new region */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          MEMADDR_1(cpssp, func),
                                          SZWINDOW_1(cpssp, func));
                        } else {
                              /* ... and is still deactivated */
                              /* do nothing */
                        }
                  } else {
                        /* Memory window was activated ... */
                        if (0 < SZWINDOW_1(cpssp, func)) {
                              /* ... and is still activated */
                              /* remap old and new regions */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          MEMADDR_1(cpssp, func),
                                          oaddr - MEMADDR_1(cpssp, func));
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          MEMADDR_1(cpssp, func),
                                          SZWINDOW_1(cpssp, func));
                        } else {
                              /* ... and is now deactivated */
                              /* remap old region */
                              sig_pci_bus_unmap(cpssp->port_pci_bus,
                                          cpssp,
                                          MEMADDR_1(cpssp, func),
                                          oaddr - MEMADDR_1(cpssp, func));
                        }
                  }
            }
            break;
            
      case IO_BASE_0:
      case IO_BASE_1:
            pci_setconfigl(cpssp->config_space[func], addr,
                         val & 0xFFFFFFFCUL);
            break;
            
      case IO_LIMIT_0:
      case IO_LIMIT_1:
            pci_setconfigl(cpssp->config_space[func], addr,
                         val & 0x0000FFFCUL);
            break;
            
      case PCI_INTERRUPT_LINE: /* FIXME WALDI: value of readonly bits depends on bridge config */
            val &= 0x07EF00FFUL;
            tmp = pci_getconfigl(cpssp->config_space[func], addr) & 0xFF00UL;
            val |= tmp;
            pci_setconfigl(cpssp->config_space[func], addr, val);
            
            val &= 0x00230000UL;
            tmp = pci_getconfigl(cpssp->config_space[(func + 1) % 2], addr);
            tmp &= 0xFFDCFFFFUL;
            val |= tmp;
            pci_setconfigl(cpssp->config_space[(func + 1) % 2], addr, val);
            break;
            
      case SUBSYSTEM_VENDOR_ID:
            tmp = pci_getconfigl(cpssp->config_space[0], SYSTEM_CONTROL);
            if (tmp & 0x00000020UL) { /* register in read-only mode */
                  break;
            }
            pci_setconfigl(cpssp->config_space[func], addr, val);
            pci_setconfigl(cpssp->config_space[(func + 1) % 2], addr, val);
            break;
            
      case PCCARD_16_BASE_ADDR:
            val |= 0x00000001UL;
            pci_setconfigl(cpssp->config_space[func], addr, val);
            pci_setconfigl(cpssp->config_space[(func + 1) % 2], addr, val);
            break;
            
      case SYSTEM_CONTROL:
            tmp = pci_getconfigl(cpssp->config_space[func], addr);
            tmp &= ~(val & 0x02000000UL);
            val &= 0xEF7FC07FUL;
            val |= 0x00001000UL;
            val |= (tmp & 0x02000000UL);
            pci_setconfigl(cpssp->config_space[func], addr, val);

            tmp = pci_getconfigl(cpssp->config_space[(func + 1) % 2], addr);
            tmp &= 0x16FF31CDUL;
            val = (val & 0xE900CE32UL) | tmp;
            pci_setconfigl(cpssp->config_space[(func + 1) % 2], addr, val);
            break;
            
      case MULTI_FUNCTION_ROUTING:
            val &= 0x0FFFFFFFUL;
            pci_setconfigl(cpssp->config_space[0], addr, val);
            pci_setconfigl(cpssp->config_space[1], addr, val);
            break;
            
      case RETRY_STATUS:
            tmp = pci_getconfigl(cpssp->config_space[func], addr);
            tmp &= ~(val & 0x0000012AUL);
            val &= 0xBFEFE7EAUL;
            val |= 0x40000000UL;
            val |= (tmp & 0x0000012AUL);
            pci_setconfigl(cpssp->config_space[func], addr, val);

            tmp = pci_getconfigl(cpssp->config_space[(func + 1) % 2], addr);
            tmp &= 0x61B67FB7UL;
            val = (val & 0x9E498048UL) | tmp;
            pci_setconfigl(cpssp->config_space[(func + 1) % 2], addr, val);
            break;
            
      case CAPABILITY_ID:
            val &= 0x80000000UL;
            val |= 0x7E120001UL;
            pci_setconfigl(cpssp->config_space[func], addr, val);
            break;
            
      case PWR_MNG_CONTROL_STATUS:
            tmp = pci_getconfigl(cpssp->config_space[func], addr);
            tmp &= ~(val & 0x00008000UL);
            val &= 0x00000103UL;
            val |= 0x00C00000UL;
            val |= (tmp & 0x00008000UL);
            pci_setconfigl(cpssp->config_space[0], addr, val);
            pci_setconfigl(cpssp->config_space[1], addr, val);
            break;
            
      case GP_EVENT_STATUS:
            if (func == 1) {
                  break;
            }
            tmp = pci_getconfigl(cpssp->config_space[func], addr);
            tmp &= ~(val & 0x0000C91FUL);
            val &= 0xC91F0000UL;
            val |= (tmp & 0x0000C91FUL);
            pci_setconfigl(cpssp->config_space[0], addr, val);
            break;
            
      case GP_INPUT:
            if (func == 1) {
                  break;
            }
            tmp = pci_getconfigl(cpssp->config_space[0], addr);
            val &= 0x001F0000UL;
            val |= (tmp & 0x0000001F);
            pci_setconfigl(cpssp->config_space[0], addr, val);
            break;
            
      case SBUS_DATA:
            if (func == 1) {
                  break;
            }
            tmp = pci_getconfigl(cpssp->config_space[0], addr);
            tmp &= ~(val & 0x0B000000UL);
            val &= 0x84FFFFFFUL;
            val |= (tmp & 0x0B000000UL);
            pci_setconfigl(cpssp->config_space[0], addr, val);
            break;
            
      case PCI_CLASS_REVISION:
      case PCI_VENDOR_ID:
            /* All the registers above are write-protected or
             * hard-wired to 0. That's why we ignore the write. */
            break;
            
      case 0x48 ... 0x7C:
      case 0x84 ... 0x88:
      case 0x94 ... 0x9C:
      case 0xB4 ... 0xFC:
            /* These registers are marked as reserved */
            break;
            
      default:
            break;
      }
}

static int
chip_ti_1520__forward_cread0(
      struct cpssp *cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t *valp
)
{
      unsigned int f;

      assert((addr & 3) == 0);

      addr &= 0x7ff;

      f = (addr >> 8) & 0x7;
      if (f & 0x6) {
            return -1;
      }
      
      *valp = 0x00000000;
      if ((bs >> 0) & 1) {
            *valp |= pci_getconfigb(cpssp->config_space[f], addr + 0) << 0;
      }
      if ((bs >> 1) & 1) {
            *valp |= pci_getconfigb(cpssp->config_space[f], addr + 1) << 8;
      }
      if ((bs >> 2) & 1) {
            *valp |= pci_getconfigb(cpssp->config_space[f], addr + 2) << 16;
      }
      if ((bs >> 3) & 1) {
            *valp |= pci_getconfigb(cpssp->config_space[f], addr + 3) << 24;
      }

      return 0;
}

static int
chip_ti_1520__forward_cwrite0(
      struct cpssp *cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t val
)
{
      unsigned int f;
      uint32_t val32;

      assert((addr & 3) == 0);
      
      addr &= 0x7ff;

      f = (addr >> 8) & 0x7;
      if (f & 0x6) {
            return -1;
      }
      
      val32 = cpssp->config_space[f][addr >> 2];
      if ((bs >> 0) & 1) {
            val32 &= ~(0xff << 0);
            val32 |= val & (0xff << 0);
      }
      if ((bs >> 1) & 1) {
            val32 &= ~(0xff << 8);
            val32 |= val & (0xff << 8);
      }
      if ((bs >> 2) & 1) {
            val32 &= ~(0xff << 16);
            val32 |= val & (0xff << 16);
      }
      if ((bs >> 3) & 1) {
            val32 &= ~(0xff << 24);
            val32 |= val & (0xff << 24);
      }
      _chip_ti_1520_cwritel(cpssp, f, addr, val32);

      return 0;
}

static enum chip_ti_1520_region
chip_ti_1520__region(
      struct cpssp *cpssp,
      int orig,
      unsigned int type,
      uint32_t addr
)
{
      enum chip_ti_1520_region region;

      switch (type) {
      case SIG_PCI_BUS_C0R:
      case SIG_PCI_BUS_C0W:
            /* Don't react on main bus c0r/c0w cycles. */
            /* Need id_sel signal. */
            region = REGION_NONE;
            break;

      case SIG_PCI_BUS_C1R:
      case SIG_PCI_BUS_C1W: {
            uint32_t bus;

            bus = (addr >> 16) & 0xff;

            if (bus == CB_BUS(cpssp, 0)) {
                  region = REGION_CB0_CSPACE;
            } else if (bus == CB_BUS(cpssp, 1)) {
                  region = REGION_CB1_CSPACE;
            } else if (bus <= SUB_BUS(cpssp, 0)) {
                  region = REGION_CB0;
            } else if (bus <= SUB_BUS(cpssp, 1)) {
                  region = REGION_CB1;
            } else {
                  region = REGION_NONE;
            }
            break;
          }
      case SIG_PCI_BUS_IOR:
      case SIG_PCI_BUS_IOW:
            if ((cpssp->config_space[0][PCI_COMMAND >> 2] & PCI_COMMAND_IO)
             && pci_getconfigl(cpssp->config_space[0], IO_BASE_0) <= addr
             && addr < pci_getconfigl(cpssp->config_space[0], IO_LIMIT_0)) {
                  /* Forward to CardBus0 */
                  region = REGION_CB0;

            } else if ((cpssp->config_space[0][PCI_COMMAND >> 2] & PCI_COMMAND_IO)
                  && pci_getconfigl(cpssp->config_space[0], IO_BASE_1) <= addr
                  && addr < pci_getconfigl(cpssp->config_space[0], IO_LIMIT_1)) {
                  /* Forward to CardBus0 */
                  region = REGION_CB0;

            } else if ((cpssp->config_space[1][PCI_COMMAND >> 2] & PCI_COMMAND_IO)
                  && pci_getconfigl(cpssp->config_space[1], IO_BASE_0) <= addr
                  && addr < pci_getconfigl(cpssp->config_space[1], IO_LIMIT_0)) {
                  /* Forward to CardBus1 */
                  region = REGION_CB1;

            } else if ((cpssp->config_space[1][PCI_COMMAND >> 2] & PCI_COMMAND_IO)
                  && pci_getconfigl(cpssp->config_space[1], IO_BASE_1) <= addr
                  && addr < pci_getconfigl(cpssp->config_space[1], IO_LIMIT_1)) {
                  /* Forward to CardBus1 */
                  region = REGION_CB1;

            } else if (pci_getconfigl(cpssp->config_space[0], PCCARD_16_BASE_ADDR) <= addr
                  && addr < pci_getconfigl(cpssp->config_space[0], PCCARD_16_BASE_ADDR) + 4) {
                  /* Forward to Chip's Legacy Registers */
                  region = REGION_CHIP_LEGACY;

            } else {
                  /* Forward to PCI Bus */
                  region = REGION_PCI;
            }
            break;

      case SIG_PCI_BUS_MR:
      case SIG_PCI_BUS_MW:
            if ((cpssp->config_space[0][PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY)
             && ((MEMADDR_0(cpssp, 0) <= addr && addr <= MEMLIMIT_0(cpssp, 0))
              || (MEMADDR_1(cpssp, 0) <= addr && addr <= MEMLIMIT_1(cpssp, 0)))) {
                  /* Forward to CardBus0 */
                  region = REGION_CB0;

            } else if ((cpssp->config_space[1][PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY)
                  && ((MEMADDR_0(cpssp, 1) <= addr && addr <= MEMLIMIT_0(cpssp, 1))
                   || (MEMADDR_1(cpssp, 1) <= addr && addr <= MEMLIMIT_1(cpssp, 1)))) {
                  /* Forward to CardBus1 */
                  region = REGION_CB1;

            } else if ((cpssp->config_space[0][PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY)
                  && SOCKETADDR(cpssp, 0) <= addr
                  && addr < SOCKETADDR(cpssp, 0) + 0x1000) {
                  /* Forward to Chip Register Bank 0 */
                  region = REGION_CHIP_REGISTER0;

            } else if ((cpssp->config_space[1][PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY)
                  && SOCKETADDR(cpssp, 1) <= addr
                  && addr < SOCKETADDR(cpssp, 1) + 0x1000) {
                  /* Forward to Chip Register Bank 1 */
                  region = REGION_CHIP_REGISTER1;

            } else {
                  /* Forward to PCI Bus */
                  region = REGION_PCI;
            }
            break;

      default:
            fprintf(stderr, "%s type %d\n", __FUNCTION__, type);
            assert(0);
      }

      if (region == REGION_PCI
       && orig == -1) {
            region = REGION_NONE;

      } else if (region == REGION_CB0
            && (orig == 0
             || ! cpssp->powered[0])) {
            region = REGION_NONE;

      } else if (region == REGION_CB1
            && (orig == 1
             || ! cpssp->powered[1])) {
            region = REGION_NONE;
      }

      return region;
}

static int
chip_ti_1520__forward_type_addr(
      struct cpssp *cpssp,
      int orig,
      unsigned int type,
      uint32_t addr
)
{
      assert(cpssp->selected == REGION_NONE);

      cpssp->selected = chip_ti_1520__region(cpssp, orig, type, addr);
      cpssp->selected_reg = addr;

      switch (cpssp->selected) {
      case REGION_NONE:
            return -1;

      case REGION_CHIP_LEGACY:
      case REGION_CHIP_REGISTER0:
      case REGION_CHIP_REGISTER1:
            return 0;

      case REGION_PCI:
            return sig_pci_bus_main_type_addr(cpssp->port_pci_bus, cpssp,
                        type, addr);

      case REGION_CB0:
            return sig_pci_bus_main_type_addr(cpssp->port_cb0, cpssp,
                        type, addr);

      case REGION_CB0_CSPACE: {
            uint32_t dev;
            uint32_t reg;

            dev = (addr >> 11) & 0x1f;
            reg = (addr >>  0) & 0x7fc;
            if (dev < 32 - 11) {
                  dev = 1 << (dev + 11);
            } else {
                  dev = 0;
            }

            return sig_pci_bus_main_type_addr(cpssp->port_cb0, cpssp,
                        type == SIG_PCI_BUS_C1R
                              ? SIG_PCI_BUS_C0R
                              : SIG_PCI_BUS_C0W,
                        dev | reg);
          }
      case REGION_CB1:
            return sig_pci_bus_main_type_addr(cpssp->port_cb1, cpssp,
                        type, addr);

      case REGION_CB1_CSPACE: {
            uint32_t dev;
            uint32_t reg;

            dev = (addr >> 11) & 0x1f;
            reg = (addr >>  0) & 0x7fc;
            if (dev < 32 - 11) {
                  dev = 1 << (dev + 11);
            } else {
                  dev = 0;
            }

            return sig_pci_bus_main_type_addr(cpssp->port_cb1, cpssp,
                        type == SIG_PCI_BUS_C1R
                              ? SIG_PCI_BUS_C0R
                              : SIG_PCI_BUS_C0W,
                        dev | reg);
          }
      default:
            assert(0); /* Cannot happen. */
      }
}

static int
chip_ti_1520__forward_read_data(
      struct cpssp *cpssp,
      int orig,
      unsigned int bs,
      uint32_t *valp
)
{
      switch (cpssp->selected) {
      case REGION_NONE: /* none */
            return -1;
      case REGION_CHIP_LEGACY:
            cpssp->selected = REGION_NONE;
            chip_ti_1520_legacy_in(cpssp, valp, cpssp->selected_reg, bs);
            return 0;
      case REGION_CHIP_REGISTER0:
            cpssp->selected = REGION_NONE;
            chip_ti_1520_register_read(cpssp, 0, valp, cpssp->selected_reg, bs);
            return 0;
      case REGION_CHIP_REGISTER1:
            cpssp->selected = REGION_NONE;
            chip_ti_1520_register_read(cpssp, 1, valp, cpssp->selected_reg, bs);
            return 0;
      case REGION_PCI:
            cpssp->selected = REGION_NONE;
            return sig_pci_bus_main_read_data(cpssp->port_pci_bus, cpssp,
                        bs, valp);
      case REGION_CB0:
      case REGION_CB0_CSPACE:
            cpssp->selected = REGION_NONE;
            return sig_pci_bus_main_read_data(cpssp->port_cb0, cpssp,
                        bs, valp);
      case REGION_CB1:
      case REGION_CB1_CSPACE:
            cpssp->selected = REGION_NONE;
            return sig_pci_bus_main_read_data(cpssp->port_cb1, cpssp,
                        bs, valp);
      default:
            assert(0); /* Cannot happen. */
      }
}

static int
chip_ti_1520__forward_write_data(
      struct cpssp *cpssp,
      int orig,
      unsigned int bs,
      uint32_t val
)
{
      switch (cpssp->selected) {
      case REGION_NONE: /* none */
            return -1;
      case REGION_CHIP_LEGACY:
            cpssp->selected = REGION_NONE;
            chip_ti_1520_legacy_out(cpssp, val, cpssp->selected_reg, bs);
            return 0;
      case REGION_CHIP_REGISTER0:
            cpssp->selected = REGION_NONE;
            chip_ti_1520_register_write(cpssp, 0, val, cpssp->selected_reg, bs);
            return 0;
      case REGION_CHIP_REGISTER1:
            cpssp->selected = REGION_NONE;
            chip_ti_1520_register_write(cpssp, 1, val, cpssp->selected_reg, bs);
            return 0;
      case REGION_PCI:
            cpssp->selected = REGION_NONE;
            return sig_pci_bus_main_write_data(cpssp->port_pci_bus, cpssp,
                        bs, val);
      case REGION_CB0:
      case REGION_CB0_CSPACE:
            cpssp->selected = REGION_NONE;
            return sig_pci_bus_main_write_data(cpssp->port_cb0, cpssp,
                        bs, val);
      case REGION_CB1:
      case REGION_CB1_CSPACE:
            cpssp->selected = REGION_NONE;
            return sig_pci_bus_main_write_data(cpssp->port_cb1, cpssp,
                        bs, val);
      default:
            assert(0); /* Cannot happen. */
      }
}

static int
chip_ti_1520__forward_read(
      struct cpssp *cpssp,
      int orig,
      uint32_t pa,
      unsigned int bs,
      uint32_t *valp
)
{
      switch (chip_ti_1520__region(cpssp, orig, SIG_PCI_BUS_MR, pa)) {
      case REGION_CHIP_LEGACY:
            assert(0); /* Cannot happen. */
      case REGION_CHIP_REGISTER0:
            chip_ti_1520_register_read(cpssp, 0, valp, pa, bs);
            return 0;
      case REGION_CHIP_REGISTER1:
            chip_ti_1520_register_read(cpssp, 1, valp, pa, bs);
            return 0;
      case REGION_PCI:
            return sig_pci_bus_mr(cpssp->port_pci_bus, cpssp, pa, bs, valp);
      case REGION_CB0:
            return sig_pci_bus_mr(cpssp->port_cb0, cpssp, pa, bs, valp);
      case REGION_CB1:
            return sig_pci_bus_mr(cpssp->port_cb1, cpssp, pa, bs, valp);
      case REGION_NONE:
            return -1;
      default:
            assert(0); /* Cannot happen. */
      }
}

static int
chip_ti_1520__forward_write(
      struct cpssp *cpssp,
      int orig,
      uint32_t pa,
      unsigned int bs,
      uint32_t val
)
{
      switch (chip_ti_1520__region(cpssp, orig, SIG_PCI_BUS_MW, pa)) {
      case REGION_CHIP_LEGACY:
            assert(0); /* Cannot happen. */
      case REGION_CHIP_REGISTER0:
            chip_ti_1520_register_write(cpssp, 0, val, pa, bs);
            return 0;
      case REGION_CHIP_REGISTER1:
            chip_ti_1520_register_write(cpssp, 1, val, pa, bs);
            return 0;
      case REGION_PCI:
            return sig_pci_bus_mw(cpssp->port_pci_bus, cpssp, pa, bs, val);
      case REGION_CB0:
            return sig_pci_bus_mw(cpssp->port_cb0, cpssp, pa, bs, val);
      case REGION_CB1:
            return sig_pci_bus_mw(cpssp->port_cb1, cpssp, pa, bs, val);
      case REGION_NONE:
            return -1;
      default:
            assert(0); /* Cannot happen. */
      }
}

static int
chip_ti_1520_cread0(void *_cpssp, uint32_t addr, unsigned int bs, uint32_t *valp)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_cread0(cpssp, addr, bs, valp);
}

static int
chip_ti_1520_cwrite0(void *_cpssp, uint32_t addr, unsigned int bs, uint32_t val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_cwrite0(cpssp, addr, bs, val);
}

static int
chip_ti_1520_cread1(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t *valp
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;
      unsigned int bus;
      unsigned int dev;

      bus = (addr >> 16) & 0xff;

      if (bus == CB_BUS(cpssp, 0)) {
            dev = (addr >> 11) & 0x1f;
            addr &= 0x7fc;
            if (! cpssp->powered[0]) {
                  return 1;
            } else {
                  return sig_pci_bus_main_c0r(cpssp->port_cb0, cpssp,
                              (1 << (11 + dev)) | addr, bs, valp);
            }
      } else if (bus == CB_BUS(cpssp, 1)) {
            dev = (addr >> 11) & 0x1f;
            addr &= 0x7fc;
            if (! cpssp->powered[1]) {
                  return 1;
            } else {
                  return sig_pci_bus_main_c0r(cpssp->port_cb1, cpssp,
                              (1 << (11 + dev)) | addr, bs, valp);
            }
      } else if (bus <= SUB_BUS(cpssp, 0)) {
            if (! cpssp->powered[0]) {
                  return 1;
            } else {
                  return sig_pci_bus_c1r(cpssp->port_cb0, cpssp,
                              addr, bs, valp);
            }
      } else if (bus <= SUB_BUS(cpssp, 1)) {
            if (! cpssp->powered[1]) {
                  return 1;
            } else {
                  return sig_pci_bus_c1r(cpssp->port_cb1, cpssp,
                              addr, bs, valp);
            }
      }

      return -1;
}

static int
chip_ti_1520_cwrite1(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t val
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;
      unsigned int bus;
      unsigned int dev;

      bus = (addr >> 16) & 0xff;

      if (bus == CB_BUS(cpssp, 0)) {
            dev = (addr >> 11) & 0x1f;
            addr &= 0x7fc;
            if (! cpssp->powered[0]) {
                  return 1;
            } else {
                  return sig_pci_bus_main_c0w(cpssp->port_cb0, cpssp,
                              (1 << (11 + dev)) | addr, bs, val);
            }
      } else if (bus == CB_BUS(cpssp, 1)) {
            dev = (addr >> 11) & 0x1f;
            addr &= 0x7fc;
            if (! cpssp->powered[1]) {
                  return 1;
            } else {
                  return sig_pci_bus_main_c0w(cpssp->port_cb1, cpssp,
                              (1 << (11 + dev)) | addr, bs, val);
            }
      } else if (bus <= SUB_BUS(cpssp, 0)) {
            if (! cpssp->powered[0]) {
                  return 1;
            } else {
                  return sig_pci_bus_c1w(cpssp->port_cb0, cpssp,
                              addr, bs, val);
            }
      } else if (bus <= SUB_BUS(cpssp, 1)) {
            if (! cpssp->powered[1]) {
                  return 1;
            } else {
                  return sig_pci_bus_c1w(cpssp->port_cb1, cpssp,
                              addr, bs, val);
            }
      }

      return -1;
}

static int
chip_ti_1520_type_addr(void *_cpssp, unsigned int type, uint32_t addr)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_type_addr(cpssp, -1, type, addr);
}

static int
chip_ti_1520_read_data(void *_cpssp, unsigned int bs, uint32_t *valp)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_read_data(cpssp, -1, bs, valp);
}

static int
chip_ti_1520_write_data(void *_cpssp, unsigned int bs, uint32_t val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_write_data(cpssp, -1, bs, val);
}

static int
chip_ti_1520_read(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t *valp
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;
      
      return chip_ti_1520__forward_read(cpssp, -1, addr, bs, valp);
}

static int
chip_ti_1520_write(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t val
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;
      
      return chip_ti_1520__forward_write(cpssp, -1, addr, bs, val);
}

static int
chip_ti_1520_map(
      void *_cpssp,
      unsigned long pa,
      unsigned int len,
      char **haddr_mr_p,
      char **haddr_mw_p
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;
      unsigned int func;
      
      for (func = 0; func < 2; func++) {
            if ((cpssp->config_space[0][PCI_COMMAND >> 2]
                              & PCI_COMMAND_MEMORY) == 0) {
                  continue;
            }
            if (SOCKETADDR(cpssp, func) <= pa
             && pa < SOCKETADDR(cpssp, func) + 0x1000) {
                  *haddr_mr_p = NULL;
                  *haddr_mw_p = NULL;
                  return 0;
            }
            if (MEMADDR_0(cpssp, func) <= pa
             && pa <= MEMLIMIT_0(cpssp, func)) {
                  *haddr_mr_p = NULL;
                  *haddr_mw_p = NULL;
                  return 0;
            }
            if (MEMADDR_1(cpssp, func) <= pa
             && pa <= MEMLIMIT_1(cpssp, func)) {
                  *haddr_mr_p = NULL;
                  *haddr_mw_p = NULL;
                  return 0;
            }
      }
      
      return 1;
}

static int
chip_ti_1520_cb0_in(
      void *_cpssp,
      uint32_t port,
      unsigned int bs,
      uint32_t *valp
)
{
      fixme();
      return 0;
}

static int
chip_ti_1520_cb0_out(
      void *_cpssp,
      uint32_t port,
      unsigned int bs,
      uint32_t val
)
{
      fixme();
      return 0;
}

static int
chip_ti_1520_cb0_read(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t *valp
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_read(cpssp, 0, addr, bs, valp);
}

static int
chip_ti_1520_cb0_write(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t val
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_write(cpssp, 0, addr, bs, val);
}

static int
chip_ti_1520_cb0_map(
      void *_cpssp,
      unsigned long pa,
      unsigned int len,
      char **haddr_mr_p,
      char **haddr_mw_p
)
{
      /* FIXME */
      return -1;
}

static void
chip_ti_1520_cb0_irq_set(void *_cpssp, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      if (val) {
            FCTINTERRUPT(cpssp, 0);
      }
      cpssp->int_set[0] = val;
      chip_ti_1520_irq_update(cpssp, 0);
}

static void
chip_ti_1520_cb0_conn_set(void *_cpssp, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      cpssp->state_cb0_conn = val;
      chip_ti_1520__conn_set(cpssp, 0, val);
}

static int
chip_ti_1520_cb1_in(
      void *_cpssp,
      uint32_t port,
      unsigned int bs,
      uint32_t *valp
)
{
      fixme();
      return 0;
}

static int
chip_ti_1520_cb1_out(
      void *_cpssp,
      uint32_t port,
      unsigned int bs,
      uint32_t val
)
{
      fixme();
      return 0;
}

static int
chip_ti_1520_cb1_read(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t *valp
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_read(cpssp, 1, addr, bs, valp);
}

static int
chip_ti_1520_cb1_write(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t val
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      return chip_ti_1520__forward_write(cpssp, 1, addr, bs, val);
}

static int
chip_ti_1520_cb1_map(
      void *_cpssp,
      unsigned long pa,
      unsigned int len,
      char **haddr_mr_p,
      char **haddr_mw_p
)
{
      /* FIXME */
      return -1;
}

static void
chip_ti_1520_cb1_irq_set(void *_cpssp, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      if (val) {
            FCTINTERRUPT(cpssp, 1);
      }
      cpssp->int_set[1] = val;
      chip_ti_1520_irq_update(cpssp, 1);
}

static void
chip_ti_1520_cb1_conn_set(void *_cpssp, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      cpssp->state_cb1_conn = val;
      chip_ti_1520__conn_set(cpssp, 1, val);
}

static void
chip_ti_1520_power_set(void *_cpssp, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      if (! val) {
            /* Power off CardBus devices. */
            cpssp->powered[0] = 0;
            sig_boolean_set(cpssp->port_cb0_power, cpssp, 0);
            cpssp->powered[1] = 0;
            sig_boolean_set(cpssp->port_cb1_power, cpssp, 0);
      }

      /* More to do... */
      /* FIXME */
}

static void
chip_ti_1520_n_reset_set(void *_cpssp, unsigned int n_val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;
      
      memset(cpssp->config_space[0], 0, 256);
      pci_setconfigw(cpssp->config_space[0], PCI_VENDOR_ID, 0x104C);
      pci_setconfigw(cpssp->config_space[0], PCI_DEVICE_ID, 0xAC55);
      pci_setconfigw(cpssp->config_space[0], PCI_COMMAND, 0x0000);
      pci_setconfigw(cpssp->config_space[0], PCI_STATUS, 0x0210);
      pci_setconfigl(cpssp->config_space[0], PCI_CLASS_REVISION,
                  0x00060700 << 8 | 0x01);
      pci_setconfigb(cpssp->config_space[0], PCI_CACHE_LINE_SIZE, 0);
      pci_setconfigb(cpssp->config_space[0], PCI_LATENCY_TIMER, 0);
      pci_setconfigb(cpssp->config_space[0], PCI_HEADER_TYPE, 0x82);
      pci_setconfigb(cpssp->config_space[0], PCI_BIST, 0);

      pci_setconfigb(cpssp->config_space[0], CAPABILITY_POINTER, 0xA0);
      pci_setconfigw(cpssp->config_space[0], SECONDARY_STATUS, 0x0200);
      pci_setconfigb(cpssp->config_space[0], INT_LINE, 0x00);
      pci_setconfigb(cpssp->config_space[0], INT_PIN, 0x01);
                              /* FIXME WALDI: this is too easy,
                                actually this value depends on a
                                number of other register settings */
      pci_setconfigw(cpssp->config_space[0], BRIDGE_CONTROL, 0x0340);
      pci_setconfigl(cpssp->config_space[0], PCCARD_16_BASE_ADDR, 0x01);
      pci_setconfigl(cpssp->config_space[0], SYSTEM_CONTROL, 0x00449060);
      pci_setconfigl(cpssp->config_space[0], MULTI_FUNCTION_ROUTING, 0x22);
      pci_setconfigb(cpssp->config_space[0], RETRY_STATUS, 0xC0);
      pci_setconfigb(cpssp->config_space[0], DEVICE_CONTROL, 0x60);
      pci_setconfigb(cpssp->config_space[0], DIAGNOSTIC, 0x60);
      pci_setconfigb(cpssp->config_space[0], CAPABILITY_ID, 0x01);
      pci_setconfigw(cpssp->config_space[0], PWR_MNG_CAPABILITIES, 0xFE12);
      pci_setconfigb(cpssp->config_space[0], PWR_MNG_CTRLSTAT_EXT, 0xC0);

      /* copy config space for second function */
      memcpy(cpssp->config_space[1], cpssp->config_space[0], 256);
      /* overwrite values that differ from function 0 */
      pci_setconfigb(cpssp->config_space[1], INT_PIN, 0x02);
      /* reset card registers */
      memset(cpssp->cardbus_regs, 0, sizeof(cpssp->cardbus_regs));
      memset(cpssp->exca_regs, 0, sizeof(cpssp->exca_regs));
      memset(cpssp->page_regs, 0, sizeof(cpssp->page_regs));

      /* initialize readonly bits */
      cpssp->exca_regs[0x00] = 0x84;
      cpssp->exca_regs[0x40] = 0x84;
}
      
void
CHIP_(init)(
      unsigned int nr,
      struct sig_boolean *port_power,
      struct sig_boolean *port_reset_hash_,
      /* PCI bus */
      struct sig_pci_bus_idsel *port_idsel,
      struct sig_pci_bus_main *port_pci_bus,
      struct sig_boolean_or *port_intA,
      struct sig_boolean_or *port_intB,
      /* Cardbus 0 */
      struct sig_boolean *port_cb0_power,
      struct sig_boolean *port_cb0_reset_hash_,
      struct sig_pci_bus_main *port_cb0,
      struct sig_boolean_or *port_cb0_int,
      struct sig_boolean *port_cb0_conn,
      /* Cardbus 1 */
      struct sig_boolean *port_cb1_power,
      struct sig_boolean *port_cb1_reset_hash_,
      struct sig_pci_bus_main *port_cb1,
      struct sig_boolean_or *port_cb1_int,
      struct sig_boolean *port_cb1_conn
)
{
      static const struct sig_boolean_funcs power_funcs = {
            .set = chip_ti_1520_power_set,
      };
      static const struct sig_boolean_funcs reset_hash__funcs = {
            .set = chip_ti_1520_n_reset_set,
      };
      static const struct sig_pci_bus_idsel_funcs idsel_funcs = {
            .c0r =            chip_ti_1520_cread0,
            .c0w =            chip_ti_1520_cwrite0,
      };
      static const struct sig_pci_bus_main_funcs pci_bus_funcs = {
            .type_addr =      chip_ti_1520_type_addr,
            .read_data =      chip_ti_1520_read_data,
            .write_data =     chip_ti_1520_write_data,

            .c1r =            chip_ti_1520_cread1,
            .c1w =            chip_ti_1520_cwrite1,

            .mr =       chip_ti_1520_read,
            .mw =       chip_ti_1520_write,
            .map =            chip_ti_1520_map,
      }; 
      static const struct sig_pci_bus_main_funcs cb0_funcs = {
            .ior =            chip_ti_1520_cb0_in,
            .iow =            chip_ti_1520_cb0_out,

            .mr =       chip_ti_1520_cb0_read,
            .mw =       chip_ti_1520_cb0_write,
            .map =            chip_ti_1520_cb0_map,
      };
      static const struct sig_boolean_or_funcs cb0_int_funcs = {
            .set =            chip_ti_1520_cb0_irq_set,
      };
      static const struct sig_boolean_funcs cb0_conn_funcs = {
            .set =            chip_ti_1520_cb0_conn_set,
      };
      static const struct sig_pci_bus_main_funcs cb1_funcs = {
            .ior =            chip_ti_1520_cb1_in,
            .iow =            chip_ti_1520_cb1_out,

            .mr =       chip_ti_1520_cb1_read,
            .mw =       chip_ti_1520_cb1_write,
            .map =            chip_ti_1520_cb1_map,
      };
      static const struct sig_boolean_or_funcs cb1_int_funcs = {
            .set =            chip_ti_1520_cb1_irq_set,
      };
      static const struct sig_boolean_funcs cb1_conn_funcs = {
            .set =            chip_ti_1520_cb1_conn_set,
      };
      struct cpssp *cpssp;

      cpssp = shm_map(CHIP, nr, sizeof(*cpssp), 0);

      cpssp->powered[0] = 0;
      cpssp->powered[1] = 0;

      /* Out */
      cpssp->port_int[0] = port_intA;
      sig_boolean_or_connect_out(port_intA, cpssp, 0);

      cpssp->port_int[1] = port_intB;
      sig_boolean_or_connect_out(port_intB, cpssp, 0);

      cpssp->port_cb0_power = port_cb0_power;

      cpssp->port_cb0_reset_hash_ = port_cb0_reset_hash_;

      cpssp->port_cb1_power = port_cb1_power;

      cpssp->port_cb1_reset_hash_ = port_cb1_reset_hash_;

      /* Call */
      sig_pci_bus_idsel_connect(port_idsel, cpssp, &idsel_funcs);

      cpssp->port_pci_bus = port_pci_bus;
      sig_pci_bus_main_connect(port_pci_bus, cpssp, &pci_bus_funcs);

      cpssp->port_cb0 = port_cb0;
      sig_pci_bus_main_connect(port_cb0, cpssp, &cb0_funcs);

      cpssp->port_cb1 = port_cb1;
      sig_pci_bus_main_connect(port_cb1, cpssp, &cb1_funcs);

      /* In */
      sig_boolean_connect_in(port_power, cpssp, &power_funcs);

      sig_boolean_connect_in(port_reset_hash_, cpssp, &reset_hash__funcs);

      sig_boolean_or_connect_in(port_cb0_int, cpssp, &cb0_int_funcs);

      sig_boolean_connect_in(port_cb0_conn, cpssp, &cb0_conn_funcs);

      sig_boolean_or_connect_in(port_cb1_int, cpssp, &cb1_int_funcs);

      sig_boolean_connect_in(port_cb1_conn, cpssp, &cb1_conn_funcs);

}

unsigned int
CHIP_(create)(void)
{
      static unsigned int nr = 0;
      struct cpssp *cpssp;

      shm_create(CHIP, nr, sizeof(*cpssp));
      cpssp = shm_map(CHIP, nr, sizeof(*cpssp), 0);

      shm_unmap(cpssp, sizeof(*cpssp));

      return nr++;
}

void
CHIP_(destroy)(unsigned int nr)
{
      struct cpssp *cpssp;

      cpssp = shm_map(CHIP, nr, sizeof(*cpssp), 0);

      shm_unmap(cpssp, sizeof(*cpssp));
      shm_destroy(CHIP, nr);
}

Generated by  Doxygen 1.6.0   Back to index