diff options
Diffstat (limited to 'arch/powerpc/sysdev')
| -rw-r--r-- | arch/powerpc/sysdev/Makefile | 1 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/xilinx_intc.c | 81 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/xilinx_pci.c | 132 |
3 files changed, 196 insertions, 18 deletions
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index b33b28a6fe12..2d1c87dd5d14 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile | |||
| @@ -34,6 +34,7 @@ obj-$(CONFIG_IPIC) += ipic.o | |||
| 34 | obj-$(CONFIG_4xx) += uic.o | 34 | obj-$(CONFIG_4xx) += uic.o |
| 35 | obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o | 35 | obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o |
| 36 | obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o | 36 | obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o |
| 37 | obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o | ||
| 37 | obj-$(CONFIG_OF_RTC) += of_rtc.o | 38 | obj-$(CONFIG_OF_RTC) += of_rtc.o |
| 38 | ifeq ($(CONFIG_PCI),y) | 39 | ifeq ($(CONFIG_PCI),y) |
| 39 | obj-$(CONFIG_4xx) += ppc4xx_pci.o | 40 | obj-$(CONFIG_4xx) += ppc4xx_pci.o |
diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c index c658b413c9b4..3ee1fd37bbfc 100644 --- a/arch/powerpc/sysdev/xilinx_intc.c +++ b/arch/powerpc/sysdev/xilinx_intc.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/of.h> | 25 | #include <linux/of.h> |
| 26 | #include <asm/io.h> | 26 | #include <asm/io.h> |
| 27 | #include <asm/processor.h> | 27 | #include <asm/processor.h> |
| 28 | #include <asm/i8259.h> | ||
| 28 | #include <asm/irq.h> | 29 | #include <asm/irq.h> |
| 29 | 30 | ||
| 30 | /* | 31 | /* |
| @@ -191,20 +192,14 @@ struct irq_host * __init | |||
| 191 | xilinx_intc_init(struct device_node *np) | 192 | xilinx_intc_init(struct device_node *np) |
| 192 | { | 193 | { |
| 193 | struct irq_host * irq; | 194 | struct irq_host * irq; |
| 194 | struct resource res; | ||
| 195 | void * regs; | 195 | void * regs; |
| 196 | int rc; | ||
| 197 | 196 | ||
| 198 | /* Find and map the intc registers */ | 197 | /* Find and map the intc registers */ |
| 199 | rc = of_address_to_resource(np, 0, &res); | 198 | regs = of_iomap(np, 0); |
| 200 | if (rc) { | 199 | if (!regs) { |
| 201 | printk(KERN_ERR __FILE__ ": of_address_to_resource() failed\n"); | 200 | pr_err("xilinx_intc: could not map registers\n"); |
| 202 | return NULL; | 201 | return NULL; |
| 203 | } | 202 | } |
| 204 | regs = ioremap(res.start, 32); | ||
| 205 | |||
| 206 | printk(KERN_INFO "Xilinx intc at 0x%08llx mapped to 0x%p\n", | ||
| 207 | (unsigned long long) res.start, regs); | ||
| 208 | 203 | ||
| 209 | /* Setup interrupt controller */ | 204 | /* Setup interrupt controller */ |
| 210 | out_be32(regs + XINTC_IER, 0); /* disable all irqs */ | 205 | out_be32(regs + XINTC_IER, 0); /* disable all irqs */ |
| @@ -217,6 +212,7 @@ xilinx_intc_init(struct device_node *np) | |||
| 217 | if (!irq) | 212 | if (!irq) |
| 218 | panic(__FILE__ ": Cannot allocate IRQ host\n"); | 213 | panic(__FILE__ ": Cannot allocate IRQ host\n"); |
| 219 | irq->host_data = regs; | 214 | irq->host_data = regs; |
| 215 | |||
| 220 | return irq; | 216 | return irq; |
| 221 | } | 217 | } |
| 222 | 218 | ||
| @@ -227,23 +223,70 @@ int xilinx_intc_get_irq(void) | |||
| 227 | return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR)); | 223 | return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR)); |
| 228 | } | 224 | } |
| 229 | 225 | ||
| 226 | #if defined(CONFIG_PPC_I8259) | ||
| 227 | /* | ||
| 228 | * Support code for cascading to 8259 interrupt controllers | ||
| 229 | */ | ||
| 230 | static void xilinx_i8259_cascade(unsigned int irq, struct irq_desc *desc) | ||
| 231 | { | ||
| 232 | unsigned int cascade_irq = i8259_irq(); | ||
| 233 | if (cascade_irq) | ||
| 234 | generic_handle_irq(cascade_irq); | ||
| 235 | |||
| 236 | /* Let xilinx_intc end the interrupt */ | ||
| 237 | desc->chip->ack(irq); | ||
| 238 | desc->chip->unmask(irq); | ||
| 239 | } | ||
| 240 | |||
| 241 | static void __init xilinx_i8259_setup_cascade(void) | ||
| 242 | { | ||
| 243 | struct device_node *cascade_node; | ||
| 244 | int cascade_irq; | ||
| 245 | |||
| 246 | /* Initialize i8259 controller */ | ||
| 247 | cascade_node = of_find_compatible_node(NULL, NULL, "chrp,iic"); | ||
| 248 | if (!cascade_node) | ||
| 249 | return; | ||
| 250 | |||
| 251 | cascade_irq = irq_of_parse_and_map(cascade_node, 0); | ||
| 252 | if (!cascade_irq) { | ||
| 253 | pr_err("virtex_ml510: Failed to map cascade interrupt\n"); | ||
| 254 | goto out; | ||
| 255 | } | ||
| 256 | |||
| 257 | i8259_init(cascade_node, 0); | ||
| 258 | set_irq_chained_handler(cascade_irq, xilinx_i8259_cascade); | ||
| 259 | |||
| 260 | /* Program irq 7 (usb/audio), 14/15 (ide) to level sensitive */ | ||
| 261 | /* This looks like a dirty hack to me --gcl */ | ||
| 262 | outb(0xc0, 0x4d0); | ||
| 263 | outb(0xc0, 0x4d1); | ||
| 264 | |||
| 265 | out: | ||
| 266 | of_node_put(cascade_node); | ||
| 267 | } | ||
| 268 | #else | ||
| 269 | static inline void xilinx_i8259_setup_cascade(void) { return; } | ||
| 270 | #endif /* defined(CONFIG_PPC_I8259) */ | ||
| 271 | |||
| 272 | static struct of_device_id xilinx_intc_match[] __initconst = { | ||
| 273 | { .compatible = "xlnx,opb-intc-1.00.c", }, | ||
| 274 | { .compatible = "xlnx,xps-intc-1.00.a", }, | ||
| 275 | {} | ||
| 276 | }; | ||
| 277 | |||
| 278 | /* | ||
| 279 | * Initialize master Xilinx interrupt controller | ||
| 280 | */ | ||
| 230 | void __init xilinx_intc_init_tree(void) | 281 | void __init xilinx_intc_init_tree(void) |
| 231 | { | 282 | { |
| 232 | struct device_node *np; | 283 | struct device_node *np; |
| 233 | 284 | ||
| 234 | /* find top level interrupt controller */ | 285 | /* find top level interrupt controller */ |
| 235 | for_each_compatible_node(np, NULL, "xlnx,opb-intc-1.00.c") { | 286 | for_each_matching_node(np, xilinx_intc_match) { |
| 236 | if (!of_get_property(np, "interrupts", NULL)) | 287 | if (!of_get_property(np, "interrupts", NULL)) |
| 237 | break; | 288 | break; |
| 238 | } | 289 | } |
| 239 | if (!np) { | ||
| 240 | for_each_compatible_node(np, NULL, "xlnx,xps-intc-1.00.a") { | ||
| 241 | if (!of_get_property(np, "interrupts", NULL)) | ||
| 242 | break; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | /* xilinx interrupt controller needs to be top level */ | ||
| 247 | BUG_ON(!np); | 290 | BUG_ON(!np); |
| 248 | 291 | ||
| 249 | master_irqhost = xilinx_intc_init(np); | 292 | master_irqhost = xilinx_intc_init(np); |
| @@ -251,4 +294,6 @@ void __init xilinx_intc_init_tree(void) | |||
| 251 | 294 | ||
| 252 | irq_set_default_host(master_irqhost); | 295 | irq_set_default_host(master_irqhost); |
| 253 | of_node_put(np); | 296 | of_node_put(np); |
| 297 | |||
| 298 | xilinx_i8259_setup_cascade(); | ||
| 254 | } | 299 | } |
diff --git a/arch/powerpc/sysdev/xilinx_pci.c b/arch/powerpc/sysdev/xilinx_pci.c new file mode 100644 index 000000000000..1453b0eed220 --- /dev/null +++ b/arch/powerpc/sysdev/xilinx_pci.c | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | /* | ||
| 2 | * PCI support for Xilinx plbv46_pci soft-core which can be used on | ||
| 3 | * Xilinx Virtex ML410 / ML510 boards. | ||
| 4 | * | ||
| 5 | * Copyright 2009 Roderick Colenbrander | ||
| 6 | * Copyright 2009 Secret Lab Technologies Ltd. | ||
| 7 | * | ||
| 8 | * The pci bridge fixup code was copied from ppc4xx_pci.c and was written | ||
| 9 | * by Benjamin Herrenschmidt. | ||
| 10 | * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. | ||
| 11 | * | ||
| 12 | * This file is licensed under the terms of the GNU General Public License | ||
| 13 | * version 2. This program is licensed "as is" without any warranty of any | ||
| 14 | * kind, whether express or implied. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/ioport.h> | ||
| 18 | #include <linux/of.h> | ||
| 19 | #include <linux/pci.h> | ||
| 20 | #include <mm/mmu_decl.h> | ||
| 21 | #include <asm/io.h> | ||
| 22 | #include <asm/xilinx_pci.h> | ||
| 23 | |||
| 24 | #define XPLB_PCI_ADDR 0x10c | ||
| 25 | #define XPLB_PCI_DATA 0x110 | ||
| 26 | #define XPLB_PCI_BUS 0x114 | ||
| 27 | |||
| 28 | #define PCI_HOST_ENABLE_CMD PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | ||
| 29 | |||
| 30 | static struct of_device_id xilinx_pci_match[] = { | ||
| 31 | { .compatible = "xlnx,plbv46-pci-1.03.a", }, | ||
| 32 | {} | ||
| 33 | }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * xilinx_pci_fixup_bridge - Block Xilinx PHB configuration. | ||
| 37 | */ | ||
| 38 | static void xilinx_pci_fixup_bridge(struct pci_dev *dev) | ||
| 39 | { | ||
| 40 | struct pci_controller *hose; | ||
| 41 | int i; | ||
| 42 | |||
| 43 | if (dev->devfn || dev->bus->self) | ||
| 44 | return; | ||
| 45 | |||
| 46 | hose = pci_bus_to_host(dev->bus); | ||
| 47 | if (!hose) | ||
| 48 | return; | ||
| 49 | |||
| 50 | if (!of_match_node(xilinx_pci_match, hose->dn)) | ||
| 51 | return; | ||
| 52 | |||
| 53 | /* Hide the PCI host BARs from the kernel as their content doesn't | ||
| 54 | * fit well in the resource management | ||
| 55 | */ | ||
| 56 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | ||
| 57 | dev->resource[i].start = 0; | ||
| 58 | dev->resource[i].end = 0; | ||
| 59 | dev->resource[i].flags = 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | dev_info(&dev->dev, "Hiding Xilinx plb-pci host bridge resources %s\n", | ||
| 63 | pci_name(dev)); | ||
| 64 | } | ||
| 65 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, xilinx_pci_fixup_bridge); | ||
| 66 | |||
| 67 | /** | ||
| 68 | * xilinx_pci_exclude_device - Don't do config access for non-root bus | ||
| 69 | * | ||
| 70 | * This is a hack. Config access to any bus other than bus 0 does not | ||
| 71 | * currently work on the ML510 so we prevent it here. | ||
| 72 | */ | ||
| 73 | static int | ||
| 74 | xilinx_pci_exclude_device(struct pci_controller *hose, u_char bus, u8 devfn) | ||
| 75 | { | ||
| 76 | return (bus != 0); | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * xilinx_pci_init - Find and register a Xilinx PCI host bridge | ||
| 81 | */ | ||
| 82 | void __init xilinx_pci_init(void) | ||
| 83 | { | ||
| 84 | struct pci_controller *hose; | ||
| 85 | struct resource r; | ||
| 86 | void __iomem *pci_reg; | ||
| 87 | struct device_node *pci_node; | ||
| 88 | |||
| 89 | pci_node = of_find_matching_node(NULL, xilinx_pci_match); | ||
| 90 | if(!pci_node) | ||
| 91 | return; | ||
| 92 | |||
| 93 | if (of_address_to_resource(pci_node, 0, &r)) { | ||
| 94 | pr_err("xilinx-pci: cannot resolve base address\n"); | ||
| 95 | return; | ||
| 96 | } | ||
| 97 | |||
| 98 | hose = pcibios_alloc_controller(pci_node); | ||
| 99 | if (!hose) { | ||
| 100 | pr_err("xilinx-pci: pcibios_alloc_controller() failed\n"); | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* Setup config space */ | ||
| 105 | setup_indirect_pci(hose, r.start + XPLB_PCI_ADDR, | ||
| 106 | r.start + XPLB_PCI_DATA, | ||
| 107 | PPC_INDIRECT_TYPE_SET_CFG_TYPE); | ||
| 108 | |||
| 109 | /* According to the xilinx plbv46_pci documentation the soft-core starts | ||
| 110 | * a self-init when the bus master enable bit is set. Without this bit | ||
| 111 | * set the pci bus can't be scanned. | ||
| 112 | */ | ||
| 113 | early_write_config_word(hose, 0, 0, PCI_COMMAND, PCI_HOST_ENABLE_CMD); | ||
| 114 | |||
| 115 | /* Set the max latency timer to 255 */ | ||
| 116 | early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0xff); | ||
| 117 | |||
| 118 | /* Set the max bus number to 255 */ | ||
| 119 | pci_reg = of_iomap(pci_node, 0); | ||
| 120 | out_8(pci_reg + XPLB_PCI_BUS, 0xff); | ||
| 121 | iounmap(pci_reg); | ||
| 122 | |||
| 123 | /* Nothing past the root bridge is working right now. By default | ||
| 124 | * exclude config access to anything except bus 0 */ | ||
| 125 | if (!ppc_md.pci_exclude_device) | ||
| 126 | ppc_md.pci_exclude_device = xilinx_pci_exclude_device; | ||
| 127 | |||
| 128 | /* Register the host bridge with the linux kernel! */ | ||
| 129 | pci_process_bridge_OF_ranges(hose, pci_node, 1); | ||
| 130 | |||
| 131 | pr_info("xilinx-pci: Registered PCI host bridge\n"); | ||
| 132 | } | ||
