Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | /* * PCI support for Xilinx plbv46_pci soft-core which can be used on * Xilinx Virtex ML410 / ML510 boards. * * Copyright 2009 Roderick Colenbrander * Copyright 2009 Secret Lab Technologies Ltd. * * The pci bridge fixup code was copied from ppc4xx_pci.c and was written * by Benjamin Herrenschmidt. * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */ #include <linux/ioport.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/pci.h> #include <linux/io.h> #define XPLB_PCI_ADDR 0x10c #define XPLB_PCI_DATA 0x110 #define XPLB_PCI_BUS 0x114 #define PCI_HOST_ENABLE_CMD (PCI_COMMAND_SERR | PCI_COMMAND_PARITY | \ PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY) static const struct of_device_id xilinx_pci_match[] = { { .compatible = "xlnx,plbv46-pci-1.03.a", }, {} }; /** * xilinx_pci_fixup_bridge - Block Xilinx PHB configuration. */ static void xilinx_pci_fixup_bridge(struct pci_dev *dev) { struct pci_controller *hose; int i; if (dev->devfn || dev->bus->self) return; hose = pci_bus_to_host(dev->bus); if (!hose) return; if (!of_match_node(xilinx_pci_match, hose->dn)) return; /* Hide the PCI host BARs from the kernel as their content doesn't * fit well in the resource management */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { dev->resource[i].start = 0; dev->resource[i].end = 0; dev->resource[i].flags = 0; } dev_info(&dev->dev, "Hiding Xilinx plb-pci host bridge resources %s\n", pci_name(dev)); } DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, xilinx_pci_fixup_bridge); #ifdef DEBUG /** * xilinx_pci_exclude_device - Don't do config access for non-root bus * * This is a hack. Config access to any bus other than bus 0 does not * currently work on the ML510 so we prevent it here. */ static int xilinx_pci_exclude_device(struct pci_controller *hose, u_char bus, u8 devfn) { return (bus != 0); } /** * xilinx_early_pci_scan - List pci config space for available devices * * List pci devices in very early phase. */ static void __init xilinx_early_pci_scan(struct pci_controller *hose) { u32 bus = 0; u32 val, dev, func, offset; /* Currently we have only 2 device connected - up-to 32 devices */ for (dev = 0; dev < 2; dev++) { /* List only first function number - up-to 8 functions */ for (func = 0; func < 1; func++) { pr_info("%02x:%02x:%02x", bus, dev, func); /* read the first 64 standardized bytes */ /* Up-to 192 bytes can be list of capabilities */ for (offset = 0; offset < 64; offset += 4) { early_read_config_dword(hose, bus, PCI_DEVFN(dev, func), offset, &val); if (offset == 0 && val == 0xFFFFFFFF) { pr_cont("\nABSENT"); break; } if (!(offset % 0x10)) pr_cont("\n%04x: ", offset); pr_cont("%08x ", val); } pr_info("\n"); } } } #else static void __init xilinx_early_pci_scan(struct pci_controller *hose) { } #endif /** * xilinx_pci_init - Find and register a Xilinx PCI host bridge */ void __init xilinx_pci_init(void) { struct pci_controller *hose; struct resource r; void __iomem *pci_reg; struct device_node *pci_node; pci_node = of_find_matching_node(NULL, xilinx_pci_match); if (!pci_node) return; if (of_address_to_resource(pci_node, 0, &r)) { pr_err("xilinx-pci: cannot resolve base address\n"); return; } hose = pcibios_alloc_controller(pci_node); if (!hose) { pr_err("xilinx-pci: pcibios_alloc_controller() failed\n"); return; } /* Setup config space */ setup_indirect_pci(hose, r.start + XPLB_PCI_ADDR, r.start + XPLB_PCI_DATA, INDIRECT_TYPE_SET_CFG_TYPE); /* According to the xilinx plbv46_pci documentation the soft-core starts * a self-init when the bus master enable bit is set. Without this bit * set the pci bus can't be scanned. */ early_write_config_word(hose, 0, 0, PCI_COMMAND, PCI_HOST_ENABLE_CMD); /* Set the max latency timer to 255 */ early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0xff); /* Set the max bus number to 255, and bus/subbus no's to 0 */ pci_reg = of_iomap(pci_node, 0); WARN_ON(!pci_reg); out_be32(pci_reg + XPLB_PCI_BUS, 0x000000ff); iounmap(pci_reg); /* Register the host bridge with the linux kernel! */ pci_process_bridge_OF_ranges(hose, pci_node, INDIRECT_TYPE_SET_CFG_TYPE); pr_info("xilinx-pci: Registered PCI host bridge\n"); xilinx_early_pci_scan(hose); } |