| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * Architecture specific OF callbacks. | 
|  | 3 | */ | 
|  | 4 | #include <linux/bootmem.h> | 
|  | 5 | #include <linux/io.h> | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 6 | #include <linux/interrupt.h> | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 7 | #include <linux/list.h> | 
|  | 8 | #include <linux/of.h> | 
|  | 9 | #include <linux/of_fdt.h> | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 10 | #include <linux/of_address.h> | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 11 | #include <linux/of_platform.h> | 
| Sebastian Andrzej Siewior | 96e0a07 | 2011-02-22 21:07:42 +0100 | [diff] [blame] | 12 | #include <linux/of_irq.h> | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 13 | #include <linux/slab.h> | 
| Sebastian Andrzej Siewior | 96e0a07 | 2011-02-22 21:07:42 +0100 | [diff] [blame] | 14 | #include <linux/pci.h> | 
|  | 15 | #include <linux/of_pci.h> | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 16 |  | 
| Sebastian Andrzej Siewior | ffb9fc6 | 2011-02-22 21:07:41 +0100 | [diff] [blame] | 17 | #include <asm/hpet.h> | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 18 | #include <asm/irq_controller.h> | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 19 | #include <asm/apic.h> | 
| Sebastian Andrzej Siewior | 96e0a07 | 2011-02-22 21:07:42 +0100 | [diff] [blame] | 20 | #include <asm/pci_x86.h> | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 21 |  | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 22 | __initdata u64 initial_dtb; | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 23 | char __initdata cmd_line[COMMAND_LINE_SIZE]; | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 24 | static LIST_HEAD(irq_domains); | 
|  | 25 | static DEFINE_RAW_SPINLOCK(big_irq_lock); | 
|  | 26 |  | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 27 | int __initdata of_ioapic; | 
|  | 28 |  | 
| Thomas Gleixner | a906fda | 2011-02-25 16:09:31 +0100 | [diff] [blame] | 29 | #ifdef CONFIG_X86_IO_APIC | 
| Sebastian Andrzej Siewior | 4a66b1d | 2011-02-24 09:52:42 +0100 | [diff] [blame] | 30 | static void add_interrupt_host(struct irq_domain *ih) | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 31 | { | 
|  | 32 | unsigned long flags; | 
|  | 33 |  | 
|  | 34 | raw_spin_lock_irqsave(&big_irq_lock, flags); | 
|  | 35 | list_add(&ih->l, &irq_domains); | 
|  | 36 | raw_spin_unlock_irqrestore(&big_irq_lock, flags); | 
|  | 37 | } | 
| Thomas Gleixner | a906fda | 2011-02-25 16:09:31 +0100 | [diff] [blame] | 38 | #endif | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 39 |  | 
|  | 40 | static struct irq_domain *get_ih_from_node(struct device_node *controller) | 
|  | 41 | { | 
|  | 42 | struct irq_domain *ih, *found = NULL; | 
|  | 43 | unsigned long flags; | 
|  | 44 |  | 
|  | 45 | raw_spin_lock_irqsave(&big_irq_lock, flags); | 
|  | 46 | list_for_each_entry(ih, &irq_domains, l) { | 
|  | 47 | if (ih->controller ==  controller) { | 
|  | 48 | found = ih; | 
|  | 49 | break; | 
|  | 50 | } | 
|  | 51 | } | 
|  | 52 | raw_spin_unlock_irqrestore(&big_irq_lock, flags); | 
|  | 53 | return found; | 
|  | 54 | } | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 55 |  | 
|  | 56 | unsigned int irq_create_of_mapping(struct device_node *controller, | 
|  | 57 | const u32 *intspec, unsigned int intsize) | 
|  | 58 | { | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 59 | struct irq_domain *ih; | 
|  | 60 | u32 virq, type; | 
|  | 61 | int ret; | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 62 |  | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 63 | ih = get_ih_from_node(controller); | 
|  | 64 | if (!ih) | 
|  | 65 | return 0; | 
|  | 66 | ret = ih->xlate(ih, intspec, intsize, &virq, &type); | 
|  | 67 | if (ret) | 
| Thomas Gleixner | 00a30b2 | 2011-03-24 22:53:10 +0100 | [diff] [blame] | 68 | return 0; | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 69 | if (type == IRQ_TYPE_NONE) | 
|  | 70 | return virq; | 
| Thomas Gleixner | 07611db | 2011-03-24 21:41:57 +0100 | [diff] [blame] | 71 | irq_set_irq_type(virq, type); | 
| Sebastian Andrzej Siewior | 19c4f5f | 2011-02-22 21:07:39 +0100 | [diff] [blame] | 72 | return virq; | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 73 | } | 
|  | 74 | EXPORT_SYMBOL_GPL(irq_create_of_mapping); | 
|  | 75 |  | 
|  | 76 | unsigned long pci_address_to_pio(phys_addr_t address) | 
|  | 77 | { | 
|  | 78 | /* | 
|  | 79 | * The ioport address can be directly used by inX / outX | 
|  | 80 | */ | 
|  | 81 | BUG_ON(address >= (1 << 16)); | 
|  | 82 | return (unsigned long)address; | 
|  | 83 | } | 
|  | 84 | EXPORT_SYMBOL_GPL(pci_address_to_pio); | 
|  | 85 |  | 
|  | 86 | void __init early_init_dt_scan_chosen_arch(unsigned long node) | 
|  | 87 | { | 
|  | 88 | BUG(); | 
|  | 89 | } | 
|  | 90 |  | 
|  | 91 | void __init early_init_dt_add_memory_arch(u64 base, u64 size) | 
|  | 92 | { | 
|  | 93 | BUG(); | 
|  | 94 | } | 
|  | 95 |  | 
|  | 96 | void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) | 
|  | 97 | { | 
|  | 98 | return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS)); | 
|  | 99 | } | 
|  | 100 |  | 
|  | 101 | void __init add_dtb(u64 data) | 
|  | 102 | { | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 103 | initial_dtb = data + offsetof(struct setup_data, data); | 
|  | 104 | } | 
|  | 105 |  | 
| Sebastian Andrzej Siewior | 9079b35 | 2011-02-22 21:07:43 +0100 | [diff] [blame] | 106 | /* | 
|  | 107 | * CE4100 ids. Will be moved to machine_device_initcall() once we have it. | 
|  | 108 | */ | 
|  | 109 | static struct of_device_id __initdata ce4100_ids[] = { | 
|  | 110 | { .compatible = "intel,ce4100-cp", }, | 
|  | 111 | { .compatible = "isa", }, | 
|  | 112 | { .compatible = "pci", }, | 
|  | 113 | {}, | 
|  | 114 | }; | 
|  | 115 |  | 
|  | 116 | static int __init add_bus_probe(void) | 
|  | 117 | { | 
| Sebastian Andrzej Siewior | 4a66b1d | 2011-02-24 09:52:42 +0100 | [diff] [blame] | 118 | if (!of_have_populated_dt()) | 
| Sebastian Andrzej Siewior | 9079b35 | 2011-02-22 21:07:43 +0100 | [diff] [blame] | 119 | return 0; | 
|  | 120 |  | 
|  | 121 | return of_platform_bus_probe(NULL, ce4100_ids, NULL); | 
|  | 122 | } | 
|  | 123 | module_init(add_bus_probe); | 
|  | 124 |  | 
| Sebastian Andrzej Siewior | 96e0a07 | 2011-02-22 21:07:42 +0100 | [diff] [blame] | 125 | #ifdef CONFIG_PCI | 
|  | 126 | static int x86_of_pci_irq_enable(struct pci_dev *dev) | 
|  | 127 | { | 
|  | 128 | struct of_irq oirq; | 
|  | 129 | u32 virq; | 
|  | 130 | int ret; | 
|  | 131 | u8 pin; | 
|  | 132 |  | 
|  | 133 | ret = pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); | 
|  | 134 | if (ret) | 
|  | 135 | return ret; | 
|  | 136 | if (!pin) | 
|  | 137 | return 0; | 
|  | 138 |  | 
|  | 139 | ret = of_irq_map_pci(dev, &oirq); | 
|  | 140 | if (ret) | 
|  | 141 | return ret; | 
|  | 142 |  | 
|  | 143 | virq = irq_create_of_mapping(oirq.controller, oirq.specifier, | 
|  | 144 | oirq.size); | 
|  | 145 | if (virq == 0) | 
|  | 146 | return -EINVAL; | 
|  | 147 | dev->irq = virq; | 
|  | 148 | return 0; | 
|  | 149 | } | 
|  | 150 |  | 
|  | 151 | static void x86_of_pci_irq_disable(struct pci_dev *dev) | 
|  | 152 | { | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | void __cpuinit x86_of_pci_init(void) | 
|  | 156 | { | 
|  | 157 | struct device_node *np; | 
|  | 158 |  | 
|  | 159 | pcibios_enable_irq = x86_of_pci_irq_enable; | 
|  | 160 | pcibios_disable_irq = x86_of_pci_irq_disable; | 
|  | 161 |  | 
|  | 162 | for_each_node_by_type(np, "pci") { | 
|  | 163 | const void *prop; | 
|  | 164 | struct pci_bus *bus; | 
|  | 165 | unsigned int bus_min; | 
|  | 166 | struct device_node *child; | 
|  | 167 |  | 
|  | 168 | prop = of_get_property(np, "bus-range", NULL); | 
|  | 169 | if (!prop) | 
|  | 170 | continue; | 
|  | 171 | bus_min = be32_to_cpup(prop); | 
|  | 172 |  | 
|  | 173 | bus = pci_find_bus(0, bus_min); | 
|  | 174 | if (!bus) { | 
|  | 175 | printk(KERN_ERR "Can't find a node for bus %s.\n", | 
|  | 176 | np->full_name); | 
|  | 177 | continue; | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 | if (bus->self) | 
|  | 181 | bus->self->dev.of_node = np; | 
|  | 182 | else | 
|  | 183 | bus->dev.of_node = np; | 
|  | 184 |  | 
|  | 185 | for_each_child_of_node(np, child) { | 
|  | 186 | struct pci_dev *dev; | 
|  | 187 | u32 devfn; | 
|  | 188 |  | 
|  | 189 | prop = of_get_property(child, "reg", NULL); | 
|  | 190 | if (!prop) | 
|  | 191 | continue; | 
|  | 192 |  | 
|  | 193 | devfn = (be32_to_cpup(prop) >> 8) & 0xff; | 
|  | 194 | dev = pci_get_slot(bus, devfn); | 
|  | 195 | if (!dev) | 
|  | 196 | continue; | 
|  | 197 | dev->dev.of_node = child; | 
|  | 198 | pci_dev_put(dev); | 
|  | 199 | } | 
|  | 200 | } | 
|  | 201 | } | 
|  | 202 | #endif | 
|  | 203 |  | 
| Sebastian Andrzej Siewior | ffb9fc6 | 2011-02-22 21:07:41 +0100 | [diff] [blame] | 204 | static void __init dtb_setup_hpet(void) | 
|  | 205 | { | 
| Sebastian Andrzej Siewior | 4a66b1d | 2011-02-24 09:52:42 +0100 | [diff] [blame] | 206 | #ifdef CONFIG_HPET_TIMER | 
| Sebastian Andrzej Siewior | ffb9fc6 | 2011-02-22 21:07:41 +0100 | [diff] [blame] | 207 | struct device_node *dn; | 
|  | 208 | struct resource r; | 
|  | 209 | int ret; | 
|  | 210 |  | 
|  | 211 | dn = of_find_compatible_node(NULL, NULL, "intel,ce4100-hpet"); | 
|  | 212 | if (!dn) | 
|  | 213 | return; | 
|  | 214 | ret = of_address_to_resource(dn, 0, &r); | 
|  | 215 | if (ret) { | 
|  | 216 | WARN_ON(1); | 
|  | 217 | return; | 
|  | 218 | } | 
|  | 219 | hpet_address = r.start; | 
| Sebastian Andrzej Siewior | 4a66b1d | 2011-02-24 09:52:42 +0100 | [diff] [blame] | 220 | #endif | 
| Sebastian Andrzej Siewior | ffb9fc6 | 2011-02-22 21:07:41 +0100 | [diff] [blame] | 221 | } | 
|  | 222 |  | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 223 | static void __init dtb_lapic_setup(void) | 
|  | 224 | { | 
|  | 225 | #ifdef CONFIG_X86_LOCAL_APIC | 
| Thomas Gleixner | a906fda | 2011-02-25 16:09:31 +0100 | [diff] [blame] | 226 | struct device_node *dn; | 
|  | 227 | struct resource r; | 
|  | 228 | int ret; | 
|  | 229 |  | 
|  | 230 | dn = of_find_compatible_node(NULL, NULL, "intel,ce4100-lapic"); | 
|  | 231 | if (!dn) | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 232 | return; | 
|  | 233 |  | 
| Thomas Gleixner | a906fda | 2011-02-25 16:09:31 +0100 | [diff] [blame] | 234 | ret = of_address_to_resource(dn, 0, &r); | 
|  | 235 | if (WARN_ON(ret)) | 
|  | 236 | return; | 
|  | 237 |  | 
|  | 238 | /* Did the boot loader setup the local APIC ? */ | 
|  | 239 | if (!cpu_has_apic) { | 
|  | 240 | if (apic_force_enable(r.start)) | 
|  | 241 | return; | 
|  | 242 | } | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 243 | smp_found_config = 1; | 
|  | 244 | pic_mode = 1; | 
| Thomas Gleixner | a906fda | 2011-02-25 16:09:31 +0100 | [diff] [blame] | 245 | register_lapic_address(r.start); | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 246 | generic_processor_info(boot_cpu_physical_apicid, | 
| Thomas Gleixner | a906fda | 2011-02-25 16:09:31 +0100 | [diff] [blame] | 247 | GET_APIC_VERSION(apic_read(APIC_LVR))); | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 248 | #endif | 
|  | 249 | } | 
|  | 250 |  | 
|  | 251 | #ifdef CONFIG_X86_IO_APIC | 
|  | 252 | static unsigned int ioapic_id; | 
|  | 253 |  | 
|  | 254 | static void __init dtb_add_ioapic(struct device_node *dn) | 
|  | 255 | { | 
|  | 256 | struct resource r; | 
|  | 257 | int ret; | 
|  | 258 |  | 
|  | 259 | ret = of_address_to_resource(dn, 0, &r); | 
|  | 260 | if (ret) { | 
|  | 261 | printk(KERN_ERR "Can't obtain address from node %s.\n", | 
|  | 262 | dn->full_name); | 
|  | 263 | return; | 
|  | 264 | } | 
|  | 265 | mp_register_ioapic(++ioapic_id, r.start, gsi_top); | 
|  | 266 | } | 
|  | 267 |  | 
|  | 268 | static void __init dtb_ioapic_setup(void) | 
|  | 269 | { | 
|  | 270 | struct device_node *dn; | 
|  | 271 |  | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 272 | for_each_compatible_node(dn, NULL, "intel,ce4100-ioapic") | 
|  | 273 | dtb_add_ioapic(dn); | 
|  | 274 |  | 
|  | 275 | if (nr_ioapics) { | 
|  | 276 | of_ioapic = 1; | 
|  | 277 | return; | 
|  | 278 | } | 
|  | 279 | printk(KERN_ERR "Error: No information about IO-APIC in OF.\n"); | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 280 | } | 
|  | 281 | #else | 
|  | 282 | static void __init dtb_ioapic_setup(void) {} | 
|  | 283 | #endif | 
|  | 284 |  | 
|  | 285 | static void __init dtb_apic_setup(void) | 
|  | 286 | { | 
|  | 287 | dtb_lapic_setup(); | 
|  | 288 | dtb_ioapic_setup(); | 
|  | 289 | } | 
|  | 290 |  | 
| Sebastian Andrzej Siewior | 4a66b1d | 2011-02-24 09:52:42 +0100 | [diff] [blame] | 291 | #ifdef CONFIG_OF_FLATTREE | 
|  | 292 | static void __init x86_flattree_get_config(void) | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 293 | { | 
|  | 294 | u32 size, map_len; | 
|  | 295 | void *new_dtb; | 
|  | 296 |  | 
|  | 297 | if (!initial_dtb) | 
|  | 298 | return; | 
|  | 299 |  | 
|  | 300 | map_len = max(PAGE_SIZE - (initial_dtb & ~PAGE_MASK), | 
|  | 301 | (u64)sizeof(struct boot_param_header)); | 
|  | 302 |  | 
|  | 303 | initial_boot_params = early_memremap(initial_dtb, map_len); | 
|  | 304 | size = be32_to_cpu(initial_boot_params->totalsize); | 
|  | 305 | if (map_len < size) { | 
|  | 306 | early_iounmap(initial_boot_params, map_len); | 
|  | 307 | initial_boot_params = early_memremap(initial_dtb, size); | 
|  | 308 | map_len = size; | 
|  | 309 | } | 
|  | 310 |  | 
|  | 311 | new_dtb = alloc_bootmem(size); | 
|  | 312 | memcpy(new_dtb, initial_boot_params, size); | 
|  | 313 | early_iounmap(initial_boot_params, map_len); | 
|  | 314 |  | 
|  | 315 | initial_boot_params = new_dtb; | 
|  | 316 |  | 
|  | 317 | /* root level address cells */ | 
|  | 318 | of_scan_flat_dt(early_init_dt_scan_root, NULL); | 
|  | 319 |  | 
|  | 320 | unflatten_device_tree(); | 
| Sebastian Andrzej Siewior | 4a66b1d | 2011-02-24 09:52:42 +0100 | [diff] [blame] | 321 | } | 
|  | 322 | #else | 
|  | 323 | static inline void x86_flattree_get_config(void) { } | 
|  | 324 | #endif | 
|  | 325 |  | 
| Thomas Gleixner | a906fda | 2011-02-25 16:09:31 +0100 | [diff] [blame] | 326 | void __init x86_dtb_init(void) | 
| Sebastian Andrzej Siewior | 4a66b1d | 2011-02-24 09:52:42 +0100 | [diff] [blame] | 327 | { | 
|  | 328 | x86_flattree_get_config(); | 
|  | 329 |  | 
|  | 330 | if (!of_have_populated_dt()) | 
|  | 331 | return; | 
|  | 332 |  | 
| Sebastian Andrzej Siewior | ffb9fc6 | 2011-02-22 21:07:41 +0100 | [diff] [blame] | 333 | dtb_setup_hpet(); | 
| Sebastian Andrzej Siewior | 3879a6f | 2011-02-22 21:07:40 +0100 | [diff] [blame] | 334 | dtb_apic_setup(); | 
| Sebastian Andrzej Siewior | da6b737 | 2011-02-22 21:07:37 +0100 | [diff] [blame] | 335 | } | 
| Sebastian Andrzej Siewior | bcc7c12 | 2011-02-22 21:07:44 +0100 | [diff] [blame] | 336 |  | 
|  | 337 | #ifdef CONFIG_X86_IO_APIC | 
|  | 338 |  | 
|  | 339 | struct of_ioapic_type { | 
|  | 340 | u32 out_type; | 
|  | 341 | u32 trigger; | 
|  | 342 | u32 polarity; | 
|  | 343 | }; | 
|  | 344 |  | 
|  | 345 | static struct of_ioapic_type of_ioapic_type[] = | 
|  | 346 | { | 
|  | 347 | { | 
|  | 348 | .out_type	= IRQ_TYPE_EDGE_RISING, | 
|  | 349 | .trigger	= IOAPIC_EDGE, | 
|  | 350 | .polarity	= 1, | 
|  | 351 | }, | 
|  | 352 | { | 
|  | 353 | .out_type	= IRQ_TYPE_LEVEL_LOW, | 
|  | 354 | .trigger	= IOAPIC_LEVEL, | 
|  | 355 | .polarity	= 0, | 
|  | 356 | }, | 
|  | 357 | { | 
|  | 358 | .out_type	= IRQ_TYPE_LEVEL_HIGH, | 
|  | 359 | .trigger	= IOAPIC_LEVEL, | 
|  | 360 | .polarity	= 1, | 
|  | 361 | }, | 
|  | 362 | { | 
|  | 363 | .out_type	= IRQ_TYPE_EDGE_FALLING, | 
|  | 364 | .trigger	= IOAPIC_EDGE, | 
|  | 365 | .polarity	= 0, | 
|  | 366 | }, | 
|  | 367 | }; | 
|  | 368 |  | 
|  | 369 | static int ioapic_xlate(struct irq_domain *id, const u32 *intspec, u32 intsize, | 
|  | 370 | u32 *out_hwirq, u32 *out_type) | 
|  | 371 | { | 
| Suresh Siddha | c040aae | 2011-05-18 16:31:38 -0700 | [diff] [blame] | 372 | struct mp_ioapic_gsi *gsi_cfg; | 
| Sebastian Andrzej Siewior | bcc7c12 | 2011-02-22 21:07:44 +0100 | [diff] [blame] | 373 | struct io_apic_irq_attr attr; | 
|  | 374 | struct of_ioapic_type *it; | 
|  | 375 | u32 line, idx, type; | 
|  | 376 |  | 
|  | 377 | if (intsize < 2) | 
|  | 378 | return -EINVAL; | 
|  | 379 |  | 
|  | 380 | line = *intspec; | 
|  | 381 | idx = (u32) id->priv; | 
| Suresh Siddha | c040aae | 2011-05-18 16:31:38 -0700 | [diff] [blame] | 382 | gsi_cfg = mp_ioapic_gsi_routing(idx); | 
|  | 383 | *out_hwirq = line + gsi_cfg->gsi_base; | 
| Sebastian Andrzej Siewior | bcc7c12 | 2011-02-22 21:07:44 +0100 | [diff] [blame] | 384 |  | 
|  | 385 | intspec++; | 
|  | 386 | type = *intspec; | 
|  | 387 |  | 
|  | 388 | if (type >= ARRAY_SIZE(of_ioapic_type)) | 
|  | 389 | return -EINVAL; | 
|  | 390 |  | 
|  | 391 | it = of_ioapic_type + type; | 
|  | 392 | *out_type = it->out_type; | 
|  | 393 |  | 
|  | 394 | set_io_apic_irq_attr(&attr, idx, line, it->trigger, it->polarity); | 
|  | 395 |  | 
| Sebastian Andrzej Siewior | 2044359 | 2011-04-27 16:30:52 +0200 | [diff] [blame] | 396 | return io_apic_setup_irq_pin_once(*out_hwirq, cpu_to_node(0), &attr); | 
| Sebastian Andrzej Siewior | bcc7c12 | 2011-02-22 21:07:44 +0100 | [diff] [blame] | 397 | } | 
|  | 398 |  | 
|  | 399 | static void __init ioapic_add_ofnode(struct device_node *np) | 
|  | 400 | { | 
|  | 401 | struct resource r; | 
|  | 402 | int i, ret; | 
|  | 403 |  | 
|  | 404 | ret = of_address_to_resource(np, 0, &r); | 
|  | 405 | if (ret) { | 
|  | 406 | printk(KERN_ERR "Failed to obtain address for %s\n", | 
|  | 407 | np->full_name); | 
|  | 408 | return; | 
|  | 409 | } | 
|  | 410 |  | 
|  | 411 | for (i = 0; i < nr_ioapics; i++) { | 
| Suresh Siddha | d537143 | 2011-05-18 16:31:37 -0700 | [diff] [blame] | 412 | if (r.start == mpc_ioapic_addr(i)) { | 
| Sebastian Andrzej Siewior | bcc7c12 | 2011-02-22 21:07:44 +0100 | [diff] [blame] | 413 | struct irq_domain *id; | 
|  | 414 |  | 
|  | 415 | id = kzalloc(sizeof(*id), GFP_KERNEL); | 
|  | 416 | BUG_ON(!id); | 
|  | 417 | id->controller = np; | 
|  | 418 | id->xlate = ioapic_xlate; | 
|  | 419 | id->priv = (void *)i; | 
|  | 420 | add_interrupt_host(id); | 
|  | 421 | return; | 
|  | 422 | } | 
|  | 423 | } | 
|  | 424 | printk(KERN_ERR "IOxAPIC at %s is not registered.\n", np->full_name); | 
|  | 425 | } | 
|  | 426 |  | 
|  | 427 | void __init x86_add_irq_domains(void) | 
|  | 428 | { | 
|  | 429 | struct device_node *dp; | 
|  | 430 |  | 
| Sebastian Andrzej Siewior | 4a66b1d | 2011-02-24 09:52:42 +0100 | [diff] [blame] | 431 | if (!of_have_populated_dt()) | 
| Sebastian Andrzej Siewior | bcc7c12 | 2011-02-22 21:07:44 +0100 | [diff] [blame] | 432 | return; | 
|  | 433 |  | 
|  | 434 | for_each_node_with_property(dp, "interrupt-controller") { | 
|  | 435 | if (of_device_is_compatible(dp, "intel,ce4100-ioapic")) | 
|  | 436 | ioapic_add_ofnode(dp); | 
|  | 437 | } | 
|  | 438 | } | 
|  | 439 | #else | 
|  | 440 | void __init x86_add_irq_domains(void) { } | 
|  | 441 | #endif |