Path: blob/master/arch/powerpc/sysdev/indirect_pci.c
10817 views
/*1* Support for indirect PCI bridges.2*3* Copyright (C) 1998 Gabriel Paubert.4*5* This program is free software; you can redistribute it and/or6* modify it under the terms of the GNU General Public License7* as published by the Free Software Foundation; either version8* 2 of the License, or (at your option) any later version.9*/1011#include <linux/kernel.h>12#include <linux/pci.h>13#include <linux/delay.h>14#include <linux/string.h>15#include <linux/init.h>1617#include <asm/io.h>18#include <asm/prom.h>19#include <asm/pci-bridge.h>20#include <asm/machdep.h>2122static int23indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,24int len, u32 *val)25{26struct pci_controller *hose = pci_bus_to_host(bus);27volatile void __iomem *cfg_data;28u8 cfg_type = 0;29u32 bus_no, reg;3031if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK) {32if (bus->number != hose->first_busno)33return PCIBIOS_DEVICE_NOT_FOUND;34if (devfn != 0)35return PCIBIOS_DEVICE_NOT_FOUND;36}3738if (ppc_md.pci_exclude_device)39if (ppc_md.pci_exclude_device(hose, bus->number, devfn))40return PCIBIOS_DEVICE_NOT_FOUND;4142if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE)43if (bus->number != hose->first_busno)44cfg_type = 1;4546bus_no = (bus->number == hose->first_busno) ?47hose->self_busno : bus->number;4849if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG)50reg = ((offset & 0xf00) << 16) | (offset & 0xfc);51else52reg = offset & 0xfc;5354if (hose->indirect_type & PPC_INDIRECT_TYPE_BIG_ENDIAN)55out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |56(devfn << 8) | reg | cfg_type));57else58out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |59(devfn << 8) | reg | cfg_type));6061/*62* Note: the caller has already checked that offset is63* suitably aligned and that len is 1, 2 or 4.64*/65cfg_data = hose->cfg_data + (offset & 3);66switch (len) {67case 1:68*val = in_8(cfg_data);69break;70case 2:71*val = in_le16(cfg_data);72break;73default:74*val = in_le32(cfg_data);75break;76}77return PCIBIOS_SUCCESSFUL;78}7980static int81indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,82int len, u32 val)83{84struct pci_controller *hose = pci_bus_to_host(bus);85volatile void __iomem *cfg_data;86u8 cfg_type = 0;87u32 bus_no, reg;8889if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK) {90if (bus->number != hose->first_busno)91return PCIBIOS_DEVICE_NOT_FOUND;92if (devfn != 0)93return PCIBIOS_DEVICE_NOT_FOUND;94}9596if (ppc_md.pci_exclude_device)97if (ppc_md.pci_exclude_device(hose, bus->number, devfn))98return PCIBIOS_DEVICE_NOT_FOUND;99100if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE)101if (bus->number != hose->first_busno)102cfg_type = 1;103104bus_no = (bus->number == hose->first_busno) ?105hose->self_busno : bus->number;106107if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG)108reg = ((offset & 0xf00) << 16) | (offset & 0xfc);109else110reg = offset & 0xfc;111112if (hose->indirect_type & PPC_INDIRECT_TYPE_BIG_ENDIAN)113out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |114(devfn << 8) | reg | cfg_type));115else116out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |117(devfn << 8) | reg | cfg_type));118119/* suppress setting of PCI_PRIMARY_BUS */120if (hose->indirect_type & PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS)121if ((offset == PCI_PRIMARY_BUS) &&122(bus->number == hose->first_busno))123val &= 0xffffff00;124125/* Workaround for PCI_28 Errata in 440EPx/GRx */126if ((hose->indirect_type & PPC_INDIRECT_TYPE_BROKEN_MRM) &&127offset == PCI_CACHE_LINE_SIZE) {128val = 0;129}130131/*132* Note: the caller has already checked that offset is133* suitably aligned and that len is 1, 2 or 4.134*/135cfg_data = hose->cfg_data + (offset & 3);136switch (len) {137case 1:138out_8(cfg_data, val);139break;140case 2:141out_le16(cfg_data, val);142break;143default:144out_le32(cfg_data, val);145break;146}147return PCIBIOS_SUCCESSFUL;148}149150static struct pci_ops indirect_pci_ops =151{152.read = indirect_read_config,153.write = indirect_write_config,154};155156void __init157setup_indirect_pci(struct pci_controller* hose,158resource_size_t cfg_addr,159resource_size_t cfg_data, u32 flags)160{161resource_size_t base = cfg_addr & PAGE_MASK;162void __iomem *mbase;163164mbase = ioremap(base, PAGE_SIZE);165hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK);166if ((cfg_data & PAGE_MASK) != base)167mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);168hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK);169hose->ops = &indirect_pci_ops;170hose->indirect_type = flags;171}172173174