diff options
Diffstat (limited to 'target/linux/brcm63xx/patches-2.6.27/005-change_pci_code_to_emulate_a_fake_cardbus_adapter.patch')
-rw-r--r-- | target/linux/brcm63xx/patches-2.6.27/005-change_pci_code_to_emulate_a_fake_cardbus_adapter.patch | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/target/linux/brcm63xx/patches-2.6.27/005-change_pci_code_to_emulate_a_fake_cardbus_adapter.patch b/target/linux/brcm63xx/patches-2.6.27/005-change_pci_code_to_emulate_a_fake_cardbus_adapter.patch new file mode 100644 index 0000000000..61198155ad --- /dev/null +++ b/target/linux/brcm63xx/patches-2.6.27/005-change_pci_code_to_emulate_a_fake_cardbus_adapter.patch @@ -0,0 +1,389 @@ +From 6891d3c1014cf56dc76ec583b69d341ea47984d6 Mon Sep 17 00:00:00 2001 +From: Maxime Bizon <mbizon@freebox.fr> +Date: Fri, 18 Jul 2008 20:34:35 +0200 +Subject: [PATCH] [MIPS] BCM63XX: Change PCI code to emulate a fake cardbus bridge. + +Signed-off-by: Maxime Bizon <mbizon@freebox.fr> +--- + arch/mips/pci/ops-bcm63xx.c | 288 +++++++++++++++++++++++++++++++++++++++++++ + arch/mips/pci/pci-bcm63xx.c | 44 +++++++ + 2 files changed, 332 insertions(+), 0 deletions(-) + +diff --git a/arch/mips/pci/ops-bcm63xx.c b/arch/mips/pci/ops-bcm63xx.c +index f8dce9d..822ae17 100644 +--- a/arch/mips/pci/ops-bcm63xx.c ++++ b/arch/mips/pci/ops-bcm63xx.c +@@ -177,3 +177,291 @@ struct pci_ops bcm63xx_pci_ops = { + .read = bcm63xx_pci_read, + .write = bcm63xx_pci_write + }; ++ ++#ifdef CONFIG_CARDBUS ++/* ++ * emulate configuration read access on a cardbus bridge ++ */ ++#define FAKE_CB_BRIDGE_SLOT 0x1e ++ ++static int fake_cb_bridge_bus_number = -1; ++ ++static struct { ++ u16 pci_command; ++ u8 cb_latency; ++ u8 subordinate_busn; ++ u8 cardbus_busn; ++ u8 pci_busn; ++ int bus_assigned; ++ u16 bridge_control; ++ ++ u32 mem_base0; ++ u32 mem_limit0; ++ u32 mem_base1; ++ u32 mem_limit1; ++ ++ u32 io_base0; ++ u32 io_limit0; ++ u32 io_base1; ++ u32 io_limit1; ++} fake_cb_bridge_regs; ++ ++static int fake_cb_bridge_read(int where, int size, u32 *val) ++{ ++ unsigned int reg; ++ u32 data; ++ ++ data = 0; ++ reg = where >> 2; ++ switch (reg) { ++ case (PCI_VENDOR_ID >> 2): ++ case (PCI_CB_SUBSYSTEM_VENDOR_ID >> 2): ++ /* create dummy vendor/device id from our cpu id */ ++ data = (bcm63xx_get_cpu_id() << 16) | PCI_VENDOR_ID_BROADCOM; ++ break; ++ ++ case (PCI_COMMAND >> 2): ++ data = (PCI_STATUS_DEVSEL_SLOW << 16); ++ data |= fake_cb_bridge_regs.pci_command; ++ break; ++ ++ case (PCI_CLASS_REVISION >> 2): ++ data = (PCI_CLASS_BRIDGE_CARDBUS << 16); ++ break; ++ ++ case (PCI_CACHE_LINE_SIZE >> 2): ++ data = (PCI_HEADER_TYPE_CARDBUS << 16); ++ break; ++ ++ case (PCI_INTERRUPT_LINE >> 2): ++ /* bridge control */ ++ data = (fake_cb_bridge_regs.bridge_control << 16); ++ /* pin:intA line:0xff */ ++ data |= (0x1 << 8) | 0xff; ++ break; ++ ++ case (PCI_CB_PRIMARY_BUS >> 2): ++ data = (fake_cb_bridge_regs.cb_latency << 24); ++ data |= (fake_cb_bridge_regs.subordinate_busn << 16); ++ data |= (fake_cb_bridge_regs.cardbus_busn << 8); ++ data |= fake_cb_bridge_regs.pci_busn; ++ break; ++ ++ case (PCI_CB_MEMORY_BASE_0 >> 2): ++ data = fake_cb_bridge_regs.mem_base0; ++ break; ++ ++ case (PCI_CB_MEMORY_LIMIT_0 >> 2): ++ data = fake_cb_bridge_regs.mem_limit0; ++ break; ++ ++ case (PCI_CB_MEMORY_BASE_1 >> 2): ++ data = fake_cb_bridge_regs.mem_base1; ++ break; ++ ++ case (PCI_CB_MEMORY_LIMIT_1 >> 2): ++ data = fake_cb_bridge_regs.mem_limit1; ++ break; ++ ++ case (PCI_CB_IO_BASE_0 >> 2): ++ /* | 1 for 32bits io support */ ++ data = fake_cb_bridge_regs.io_base0 | 0x1; ++ break; ++ ++ case (PCI_CB_IO_LIMIT_0 >> 2): ++ data = fake_cb_bridge_regs.io_limit0; ++ break; ++ ++ case (PCI_CB_IO_BASE_1 >> 2): ++ /* | 1 for 32bits io support */ ++ data = fake_cb_bridge_regs.io_base1 | 0x1; ++ break; ++ ++ case (PCI_CB_IO_LIMIT_1 >> 2): ++ data = fake_cb_bridge_regs.io_limit1; ++ break; ++ } ++ ++ *val = postprocess_read(data, where, size); ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++/* ++ * emulate configuration write access on a cardbus bridge ++ */ ++static int fake_cb_bridge_write(int where, int size, u32 val) ++{ ++ unsigned int reg; ++ u32 data, tmp; ++ int ret; ++ ++ ret = fake_cb_bridge_read((where & ~0x3), 4, &data); ++ if (ret != PCIBIOS_SUCCESSFUL) ++ return ret; ++ ++ data = preprocess_write(data, val, where, size); ++ ++ reg = where >> 2; ++ switch (reg) { ++ case (PCI_COMMAND >> 2): ++ fake_cb_bridge_regs.pci_command = (data & 0xffff); ++ break; ++ ++ case (PCI_CB_PRIMARY_BUS >> 2): ++ fake_cb_bridge_regs.cb_latency = (data >> 24) & 0xff; ++ fake_cb_bridge_regs.subordinate_busn = (data >> 16) & 0xff; ++ fake_cb_bridge_regs.cardbus_busn = (data >> 8) & 0xff; ++ fake_cb_bridge_regs.pci_busn = data & 0xff; ++ if (fake_cb_bridge_regs.cardbus_busn) ++ fake_cb_bridge_regs.bus_assigned = 1; ++ break; ++ ++ case (PCI_INTERRUPT_LINE >> 2): ++ tmp = (data >> 16) & 0xffff; ++ /* disable memory prefetch support */ ++ tmp &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; ++ tmp &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; ++ fake_cb_bridge_regs.bridge_control = tmp; ++ break; ++ ++ case (PCI_CB_MEMORY_BASE_0 >> 2): ++ fake_cb_bridge_regs.mem_base0 = data; ++ break; ++ ++ case (PCI_CB_MEMORY_LIMIT_0 >> 2): ++ fake_cb_bridge_regs.mem_limit0 = data; ++ break; ++ ++ case (PCI_CB_MEMORY_BASE_1 >> 2): ++ fake_cb_bridge_regs.mem_base1 = data; ++ break; ++ ++ case (PCI_CB_MEMORY_LIMIT_1 >> 2): ++ fake_cb_bridge_regs.mem_limit1 = data; ++ break; ++ ++ case (PCI_CB_IO_BASE_0 >> 2): ++ fake_cb_bridge_regs.io_base0 = data; ++ break; ++ ++ case (PCI_CB_IO_LIMIT_0 >> 2): ++ fake_cb_bridge_regs.io_limit0 = data; ++ break; ++ ++ case (PCI_CB_IO_BASE_1 >> 2): ++ fake_cb_bridge_regs.io_base1 = data; ++ break; ++ ++ case (PCI_CB_IO_LIMIT_1 >> 2): ++ fake_cb_bridge_regs.io_limit1 = data; ++ break; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int bcm63xx_cb_read(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *val) ++{ ++ /* snoop access to slot 0x1e on root bus, we fake a cardbus ++ * bridge at this location */ ++ if (!bus->parent && PCI_SLOT(devfn) == FAKE_CB_BRIDGE_SLOT) { ++ fake_cb_bridge_bus_number = bus->number; ++ return fake_cb_bridge_read(where, size, val); ++ } ++ ++ /* a configuration cycle for the device behind the cardbus ++ * bridge is actually done as a type 0 cycle on the primary ++ * bus. This means that only one device can be on the cardbus ++ * bus */ ++ if (fake_cb_bridge_regs.bus_assigned && ++ bus->number == fake_cb_bridge_regs.cardbus_busn && ++ PCI_SLOT(devfn) == 0) ++ return bcm63xx_do_cfg_read(0, 0, ++ PCI_DEVFN(CARDBUS_PCI_IDSEL, 0), ++ where, size, val); ++ ++ return PCIBIOS_DEVICE_NOT_FOUND; ++} ++ ++static int bcm63xx_cb_write(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 val) ++{ ++ if (!bus->parent && PCI_SLOT(devfn) == FAKE_CB_BRIDGE_SLOT) { ++ fake_cb_bridge_bus_number = bus->number; ++ return fake_cb_bridge_write(where, size, val); ++ } ++ ++ if (fake_cb_bridge_regs.bus_assigned && ++ bus->number == fake_cb_bridge_regs.cardbus_busn && ++ PCI_SLOT(devfn) == 0) ++ return bcm63xx_do_cfg_write(0, 0, ++ PCI_DEVFN(CARDBUS_PCI_IDSEL, 0), ++ where, size, val); ++ ++ return PCIBIOS_DEVICE_NOT_FOUND; ++} ++ ++struct pci_ops bcm63xx_cb_ops = { ++ .read = bcm63xx_cb_read, ++ .write = bcm63xx_cb_write, ++}; ++ ++/* ++ * only one IO window, so it cannot be shared by PCI and cardbus, use ++ * fixup to choose and detect unhandled configuration ++ */ ++static void bcm63xx_fixup(struct pci_dev *dev) ++{ ++ static int io_window = -1; ++ int i, found, new_io_window; ++ u32 val; ++ ++ /* look for any io resource */ ++ found = 0; ++ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { ++ if (pci_resource_flags(dev, i) & IORESOURCE_IO) { ++ found = 1; ++ break; ++ } ++ } ++ ++ if (!found) ++ return; ++ ++ /* skip our fake bus with only cardbus bridge on it */ ++ if (dev->bus->number == fake_cb_bridge_bus_number) ++ return; ++ ++ /* find on which bus the device is */ ++ if (fake_cb_bridge_regs.bus_assigned && ++ dev->bus->number == fake_cb_bridge_regs.cardbus_busn && ++ PCI_SLOT(dev->devfn) == 0) ++ new_io_window = 1; ++ else ++ new_io_window = 0; ++ ++ if (new_io_window == io_window) ++ return; ++ ++ if (io_window != -1) { ++ printk(KERN_ERR "bcm63xx: both PCI and cardbus devices " ++ "need IO, which hardware cannot do\n"); ++ return; ++ } ++ ++ printk(KERN_INFO "bcm63xx: PCI IO window assigned to %s\n", ++ (new_io_window == 0) ? "PCI" : "cardbus"); ++ ++ val = bcm_mpi_readl(MPI_L2PIOREMAP_REG); ++ if (io_window) ++ val |= MPI_L2PREMAP_IS_CARDBUS_MASK; ++ else ++ val &= ~MPI_L2PREMAP_IS_CARDBUS_MASK; ++ bcm_mpi_writel(val, MPI_L2PIOREMAP_REG); ++ ++ io_window = new_io_window; ++} ++ ++DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, bcm63xx_fixup); ++#endif +diff --git a/arch/mips/pci/pci-bcm63xx.c b/arch/mips/pci/pci-bcm63xx.c +index 52bac8e..601700d 100644 +--- a/arch/mips/pci/pci-bcm63xx.c ++++ b/arch/mips/pci/pci-bcm63xx.c +@@ -28,7 +28,11 @@ static struct resource bcm_pci_mem_resource = { + static struct resource bcm_pci_io_resource = { + .name = "bcm63xx PCI IO space", + .start = BCM_PCI_IO_BASE_PA, ++#ifdef CONFIG_CARDBUS ++ .end = BCM_PCI_IO_HALF_PA, ++#else + .end = BCM_PCI_IO_END_PA, ++#endif + .flags = IORESOURCE_IO + }; + +@@ -38,6 +42,33 @@ struct pci_controller bcm63xx_controller = { + .mem_resource = &bcm_pci_mem_resource, + }; + ++/* ++ * We handle cardbus via a fake Cardbus bridge, memory and io spaces ++ * have to be clearly separated from PCI one since we have different ++ * memory decoder. ++ */ ++#ifdef CONFIG_CARDBUS ++static struct resource bcm_cb_mem_resource = { ++ .name = "bcm63xx Cardbus memory space", ++ .start = BCM_CB_MEM_BASE_PA, ++ .end = BCM_CB_MEM_END_PA, ++ .flags = IORESOURCE_MEM ++}; ++ ++static struct resource bcm_cb_io_resource = { ++ .name = "bcm63xx Cardbus IO space", ++ .start = BCM_PCI_IO_HALF_PA + 1, ++ .end = BCM_PCI_IO_END_PA, ++ .flags = IORESOURCE_IO ++}; ++ ++struct pci_controller bcm63xx_cb_controller = { ++ .pci_ops = &bcm63xx_cb_ops, ++ .io_resource = &bcm_cb_io_resource, ++ .mem_resource = &bcm_cb_mem_resource, ++}; ++#endif ++ + static u32 bcm63xx_int_cfg_readl(u32 reg) + { + u32 tmp; +@@ -98,8 +129,17 @@ static int __init bcm63xx_pci_init(void) + val |= (CARDBUS_PCI_IDSEL << PCMCIA_C1_CBIDSEL_SHIFT); + bcm_pcmcia_writel(val, PCMCIA_C1_REG); + ++#ifdef CONFIG_CARDBUS ++ /* setup local bus to PCI access (Cardbus memory) */ ++ val = BCM_CB_MEM_BASE_PA & MPI_L2P_BASE_MASK; ++ bcm_mpi_writel(val, MPI_L2PMEMBASE2_REG); ++ bcm_mpi_writel(~(BCM_CB_MEM_SIZE - 1), MPI_L2PMEMRANGE2_REG); ++ val |= MPI_L2PREMAP_ENABLED_MASK | MPI_L2PREMAP_IS_CARDBUS_MASK; ++ bcm_mpi_writel(val, MPI_L2PMEMREMAP2_REG); ++#else + /* disable second access windows */ + bcm_mpi_writel(0, MPI_L2PMEMREMAP2_REG); ++#endif + + /* setup local bus to PCI access (IO memory), we have only 1 + * IO window for both PCI and cardbus, but it cannot handle +@@ -169,6 +209,10 @@ static int __init bcm63xx_pci_init(void) + + register_pci_controller(&bcm63xx_controller); + ++#ifdef CONFIG_CARDBUS ++ register_pci_controller(&bcm63xx_cb_controller); ++#endif ++ + /* mark memory space used for IO mapping as reserved */ + request_mem_region(BCM_PCI_IO_BASE_PA, BCM_PCI_IO_SIZE, + "bcm63xx PCI IO space"); +-- +1.5.4.3 + |