| Daniel Hellstrom | 26893c1 | 2011-05-23 21:04:47 +0000 | [diff] [blame] | 1 | /* | 
|  | 2 | * leon_pci.c: LEON Host PCI support | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2011 Aeroflex Gaisler AB, Daniel Hellstrom | 
|  | 5 | * | 
|  | 6 | * Code is partially derived from pcic.c | 
|  | 7 | */ | 
|  | 8 |  | 
|  | 9 | #include <linux/of_device.h> | 
|  | 10 | #include <linux/kernel.h> | 
|  | 11 | #include <linux/pci.h> | 
|  | 12 | #include <asm/leon.h> | 
|  | 13 | #include <asm/leon_pci.h> | 
|  | 14 |  | 
|  | 15 | /* The LEON architecture does not rely on a BIOS or bootloader to setup | 
|  | 16 | * PCI for us. The Linux generic routines are used to setup resources, | 
|  | 17 | * reset values of confuration-space registers settings ae preseved. | 
|  | 18 | */ | 
|  | 19 | void leon_pci_init(struct platform_device *ofdev, struct leon_pci_info *info) | 
|  | 20 | { | 
|  | 21 | struct pci_bus *root_bus; | 
|  | 22 |  | 
|  | 23 | root_bus = pci_scan_bus_parented(&ofdev->dev, 0, info->ops, info); | 
|  | 24 | if (root_bus) { | 
|  | 25 | root_bus->resource[0] = &info->io_space; | 
|  | 26 | root_bus->resource[1] = &info->mem_space; | 
|  | 27 | root_bus->resource[2] = NULL; | 
|  | 28 |  | 
|  | 29 | /* Init all PCI devices into PCI tree */ | 
|  | 30 | pci_bus_add_devices(root_bus); | 
|  | 31 |  | 
|  | 32 | /* Setup IRQs of all devices using custom routines */ | 
|  | 33 | pci_fixup_irqs(pci_common_swizzle, info->map_irq); | 
|  | 34 |  | 
|  | 35 | /* Assign devices with resources */ | 
|  | 36 | pci_assign_unassigned_resources(); | 
|  | 37 | } | 
|  | 38 | } | 
|  | 39 |  | 
|  | 40 | /* PCI Memory and Prefetchable Memory is direct-mapped. However I/O Space is | 
|  | 41 | * accessed through a Window which is translated to low 64KB in PCI space, the | 
|  | 42 | * first 4KB is not used so 60KB is available. | 
|  | 43 | * | 
|  | 44 | * This function is used by generic code to translate resource addresses into | 
|  | 45 | * PCI addresses. | 
|  | 46 | */ | 
|  | 47 | void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, | 
|  | 48 | struct resource *res) | 
|  | 49 | { | 
|  | 50 | struct leon_pci_info *info = dev->bus->sysdata; | 
|  | 51 |  | 
|  | 52 | region->start = res->start; | 
|  | 53 | region->end = res->end; | 
|  | 54 |  | 
|  | 55 | if (res->flags & IORESOURCE_IO) { | 
|  | 56 | region->start -= (info->io_space.start - 0x1000); | 
|  | 57 | region->end -= (info->io_space.start - 0x1000); | 
|  | 58 | } | 
|  | 59 | } | 
|  | 60 | EXPORT_SYMBOL(pcibios_resource_to_bus); | 
|  | 61 |  | 
|  | 62 | /* see pcibios_resource_to_bus() comment */ | 
|  | 63 | void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, | 
|  | 64 | struct pci_bus_region *region) | 
|  | 65 | { | 
|  | 66 | struct leon_pci_info *info = dev->bus->sysdata; | 
|  | 67 |  | 
|  | 68 | res->start = region->start; | 
|  | 69 | res->end = region->end; | 
|  | 70 |  | 
|  | 71 | if (res->flags & IORESOURCE_IO) { | 
|  | 72 | res->start += (info->io_space.start - 0x1000); | 
|  | 73 | res->end += (info->io_space.start - 0x1000); | 
|  | 74 | } | 
|  | 75 | } | 
|  | 76 | EXPORT_SYMBOL(pcibios_bus_to_resource); | 
|  | 77 |  | 
|  | 78 | void __devinit pcibios_fixup_bus(struct pci_bus *pbus) | 
|  | 79 | { | 
|  | 80 | struct leon_pci_info *info = pbus->sysdata; | 
|  | 81 | struct pci_dev *dev; | 
|  | 82 | int i, has_io, has_mem; | 
|  | 83 | u16 cmd; | 
|  | 84 |  | 
|  | 85 | /* Generic PCI bus probing sets these to point at | 
|  | 86 | * &io{port,mem}_resouce which is wrong for us. | 
|  | 87 | */ | 
|  | 88 | if (pbus->self == NULL) { | 
|  | 89 | pbus->resource[0] = &info->io_space; | 
|  | 90 | pbus->resource[1] = &info->mem_space; | 
|  | 91 | pbus->resource[2] = NULL; | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | list_for_each_entry(dev, &pbus->devices, bus_list) { | 
|  | 95 | /* | 
|  | 96 | * We can not rely on that the bootloader has enabled I/O | 
|  | 97 | * or memory access to PCI devices. Instead we enable it here | 
|  | 98 | * if the device has BARs of respective type. | 
|  | 99 | */ | 
|  | 100 | has_io = has_mem = 0; | 
|  | 101 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | 
|  | 102 | unsigned long f = dev->resource[i].flags; | 
|  | 103 | if (f & IORESOURCE_IO) | 
|  | 104 | has_io = 1; | 
|  | 105 | else if (f & IORESOURCE_MEM) | 
|  | 106 | has_mem = 1; | 
|  | 107 | } | 
|  | 108 | /* ROM BARs are mapped into 32-bit memory space */ | 
|  | 109 | if (dev->resource[PCI_ROM_RESOURCE].end != 0) { | 
|  | 110 | dev->resource[PCI_ROM_RESOURCE].flags |= | 
|  | 111 | IORESOURCE_ROM_ENABLE; | 
|  | 112 | has_mem = 1; | 
|  | 113 | } | 
|  | 114 | pci_bus_read_config_word(pbus, dev->devfn, PCI_COMMAND, &cmd); | 
|  | 115 | if (has_io && !(cmd & PCI_COMMAND_IO)) { | 
|  | 116 | #ifdef CONFIG_PCI_DEBUG | 
|  | 117 | printk(KERN_INFO "LEONPCI: Enabling I/O for dev %s\n", | 
|  | 118 | pci_name(dev)); | 
|  | 119 | #endif | 
|  | 120 | cmd |= PCI_COMMAND_IO; | 
|  | 121 | pci_bus_write_config_word(pbus, dev->devfn, PCI_COMMAND, | 
|  | 122 | cmd); | 
|  | 123 | } | 
|  | 124 | if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) { | 
|  | 125 | #ifdef CONFIG_PCI_DEBUG | 
|  | 126 | printk(KERN_INFO "LEONPCI: Enabling MEMORY for dev" | 
|  | 127 | "%s\n", pci_name(dev)); | 
|  | 128 | #endif | 
|  | 129 | cmd |= PCI_COMMAND_MEMORY; | 
|  | 130 | pci_bus_write_config_word(pbus, dev->devfn, PCI_COMMAND, | 
|  | 131 | cmd); | 
|  | 132 | } | 
|  | 133 | } | 
|  | 134 | } | 
|  | 135 |  | 
|  | 136 | /* | 
|  | 137 | * Other archs parse arguments here. | 
|  | 138 | */ | 
|  | 139 | char * __devinit pcibios_setup(char *str) | 
|  | 140 | { | 
|  | 141 | return str; | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 | resource_size_t pcibios_align_resource(void *data, const struct resource *res, | 
|  | 145 | resource_size_t size, resource_size_t align) | 
|  | 146 | { | 
|  | 147 | return res->start; | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | int pcibios_enable_device(struct pci_dev *dev, int mask) | 
|  | 151 | { | 
|  | 152 | return pci_enable_resources(dev, mask); | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) | 
|  | 156 | { | 
|  | 157 | /* | 
|  | 158 | * Currently the OpenBoot nodes are not connected with the PCI device, | 
|  | 159 | * this is because the LEON PROM does not create PCI nodes. Eventually | 
|  | 160 | * this will change and the same approach as pcic.c can be used to | 
|  | 161 | * match PROM nodes with pci devices. | 
|  | 162 | */ | 
|  | 163 | return NULL; | 
|  | 164 | } | 
|  | 165 | EXPORT_SYMBOL(pci_device_to_OF_node); | 
|  | 166 |  | 
|  | 167 | void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) | 
|  | 168 | { | 
|  | 169 | #ifdef CONFIG_PCI_DEBUG | 
|  | 170 | printk(KERN_DEBUG "LEONPCI: Assigning IRQ %02d to %s\n", irq, | 
|  | 171 | pci_name(dev)); | 
|  | 172 | #endif | 
|  | 173 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); | 
|  | 174 | } | 
|  | 175 |  | 
|  | 176 | /* in/out routines taken from pcic.c | 
|  | 177 | * | 
|  | 178 | * This probably belongs here rather than ioport.c because | 
|  | 179 | * we do not want this crud linked into SBus kernels. | 
|  | 180 | * Also, think for a moment about likes of floppy.c that | 
|  | 181 | * include architecture specific parts. They may want to redefine ins/outs. | 
|  | 182 | * | 
|  | 183 | * We do not use horrible macros here because we want to | 
|  | 184 | * advance pointer by sizeof(size). | 
|  | 185 | */ | 
|  | 186 | void outsb(unsigned long addr, const void *src, unsigned long count) | 
|  | 187 | { | 
|  | 188 | while (count) { | 
|  | 189 | count -= 1; | 
|  | 190 | outb(*(const char *)src, addr); | 
|  | 191 | src += 1; | 
|  | 192 | /* addr += 1; */ | 
|  | 193 | } | 
|  | 194 | } | 
|  | 195 | EXPORT_SYMBOL(outsb); | 
|  | 196 |  | 
|  | 197 | void outsw(unsigned long addr, const void *src, unsigned long count) | 
|  | 198 | { | 
|  | 199 | while (count) { | 
|  | 200 | count -= 2; | 
|  | 201 | outw(*(const short *)src, addr); | 
|  | 202 | src += 2; | 
|  | 203 | /* addr += 2; */ | 
|  | 204 | } | 
|  | 205 | } | 
|  | 206 | EXPORT_SYMBOL(outsw); | 
|  | 207 |  | 
|  | 208 | void outsl(unsigned long addr, const void *src, unsigned long count) | 
|  | 209 | { | 
|  | 210 | while (count) { | 
|  | 211 | count -= 4; | 
|  | 212 | outl(*(const long *)src, addr); | 
|  | 213 | src += 4; | 
|  | 214 | /* addr += 4; */ | 
|  | 215 | } | 
|  | 216 | } | 
|  | 217 | EXPORT_SYMBOL(outsl); | 
|  | 218 |  | 
|  | 219 | void insb(unsigned long addr, void *dst, unsigned long count) | 
|  | 220 | { | 
|  | 221 | while (count) { | 
|  | 222 | count -= 1; | 
|  | 223 | *(unsigned char *)dst = inb(addr); | 
|  | 224 | dst += 1; | 
|  | 225 | /* addr += 1; */ | 
|  | 226 | } | 
|  | 227 | } | 
|  | 228 | EXPORT_SYMBOL(insb); | 
|  | 229 |  | 
|  | 230 | void insw(unsigned long addr, void *dst, unsigned long count) | 
|  | 231 | { | 
|  | 232 | while (count) { | 
|  | 233 | count -= 2; | 
|  | 234 | *(unsigned short *)dst = inw(addr); | 
|  | 235 | dst += 2; | 
|  | 236 | /* addr += 2; */ | 
|  | 237 | } | 
|  | 238 | } | 
|  | 239 | EXPORT_SYMBOL(insw); | 
|  | 240 |  | 
|  | 241 | void insl(unsigned long addr, void *dst, unsigned long count) | 
|  | 242 | { | 
|  | 243 | while (count) { | 
|  | 244 | count -= 4; | 
|  | 245 | /* | 
|  | 246 | * XXX I am sure we are in for an unaligned trap here. | 
|  | 247 | */ | 
|  | 248 | *(unsigned long *)dst = inl(addr); | 
|  | 249 | dst += 4; | 
|  | 250 | /* addr += 4; */ | 
|  | 251 | } | 
|  | 252 | } | 
|  | 253 | EXPORT_SYMBOL(insl); |