blob: 7a07b15b451c93de3606e1ffe91586195943952d [file] [log] [blame]
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +01001/*
2 * Architecture specific OF callbacks.
3 */
4#include <linux/bootmem.h>
Paul Gortmaker69c60c82011-05-26 12:22:53 -04005#include <linux/export.h>
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +01006#include <linux/io.h>
Sebastian Andrzej Siewior19c4f5f2011-02-22 21:07:39 +01007#include <linux/interrupt.h>
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +01008#include <linux/list.h>
9#include <linux/of.h>
10#include <linux/of_fdt.h>
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +010011#include <linux/of_address.h>
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +010012#include <linux/of_platform.h>
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +010013#include <linux/of_irq.h>
Ivan Gorinov0ea601d72018-03-07 11:46:29 -080014#include <linux/libfdt.h>
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +010015#include <linux/slab.h>
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +010016#include <linux/pci.h>
17#include <linux/of_pci.h>
Florian Fainelli977cb762011-06-06 10:15:49 +020018#include <linux/initrd.h>
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +010019
Thomas Gleixnerf7a0c782015-04-14 10:30:08 +080020#include <asm/irqdomain.h>
Sebastian Andrzej Siewiorffb9fc62011-02-22 21:07:41 +010021#include <asm/hpet.h>
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +010022#include <asm/apic.h>
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +010023#include <asm/pci_x86.h>
Rob Herringba904f02013-09-09 18:13:22 -050024#include <asm/setup.h>
Jiang Liu95d76ac2014-06-09 16:19:48 +080025#include <asm/i8259.h>
Sebastian Andrzej Siewior19c4f5f2011-02-22 21:07:39 +010026
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +010027__initdata u64 initial_dtb;
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +010028char __initdata cmd_line[COMMAND_LINE_SIZE];
Sebastian Andrzej Siewior19c4f5f2011-02-22 21:07:39 +010029
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +010030int __initdata of_ioapic;
31
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +010032void __init early_init_dt_scan_chosen_arch(unsigned long node)
33{
34 BUG();
35}
36
37void __init early_init_dt_add_memory_arch(u64 base, u64 size)
38{
39 BUG();
40}
41
42void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
43{
44 return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS));
45}
46
47void __init add_dtb(u64 data)
48{
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +010049 initial_dtb = data + offsetof(struct setup_data, data);
50}
51
Sebastian Andrzej Siewior9079b352011-02-22 21:07:43 +010052/*
53 * CE4100 ids. Will be moved to machine_device_initcall() once we have it.
54 */
55static struct of_device_id __initdata ce4100_ids[] = {
56 { .compatible = "intel,ce4100-cp", },
57 { .compatible = "isa", },
58 { .compatible = "pci", },
59 {},
60};
61
62static int __init add_bus_probe(void)
63{
Sebastian Andrzej Siewior4a66b1d2011-02-24 09:52:42 +010064 if (!of_have_populated_dt())
Sebastian Andrzej Siewior9079b352011-02-22 21:07:43 +010065 return 0;
66
67 return of_platform_bus_probe(NULL, ce4100_ids, NULL);
68}
Paul Gortmakerd54b6752015-05-01 20:05:49 -040069device_initcall(add_bus_probe);
Sebastian Andrzej Siewior9079b352011-02-22 21:07:43 +010070
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +010071#ifdef CONFIG_PCI
Benjamin Herrenschmidt3d5fe5a2011-04-11 11:19:09 +100072struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
73{
74 struct device_node *np;
75
76 for_each_node_by_type(np, "pci") {
77 const void *prop;
78 unsigned int bus_min;
79
80 prop = of_get_property(np, "bus-range", NULL);
81 if (!prop)
82 continue;
83 bus_min = be32_to_cpup(prop);
84 if (bus->number == bus_min)
85 return np;
86 }
87 return NULL;
88}
89
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +010090static int x86_of_pci_irq_enable(struct pci_dev *dev)
91{
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +010092 u32 virq;
93 int ret;
94 u8 pin;
95
96 ret = pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
97 if (ret)
98 return ret;
99 if (!pin)
100 return 0;
101
Grant Likely16b84e52013-09-19 16:44:55 -0500102 virq = of_irq_parse_and_map_pci(dev, 0, 0);
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +0100103 if (virq == 0)
104 return -EINVAL;
105 dev->irq = virq;
106 return 0;
107}
108
109static void x86_of_pci_irq_disable(struct pci_dev *dev)
110{
111}
112
Paul Gortmaker148f9bb2013-06-18 18:23:59 -0400113void x86_of_pci_init(void)
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +0100114{
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +0100115 pcibios_enable_irq = x86_of_pci_irq_enable;
116 pcibios_disable_irq = x86_of_pci_irq_disable;
Sebastian Andrzej Siewior96e0a072011-02-22 21:07:42 +0100117}
118#endif
119
Sebastian Andrzej Siewiorffb9fc62011-02-22 21:07:41 +0100120static void __init dtb_setup_hpet(void)
121{
Sebastian Andrzej Siewior4a66b1d2011-02-24 09:52:42 +0100122#ifdef CONFIG_HPET_TIMER
Sebastian Andrzej Siewiorffb9fc62011-02-22 21:07:41 +0100123 struct device_node *dn;
124 struct resource r;
125 int ret;
126
127 dn = of_find_compatible_node(NULL, NULL, "intel,ce4100-hpet");
128 if (!dn)
129 return;
130 ret = of_address_to_resource(dn, 0, &r);
131 if (ret) {
132 WARN_ON(1);
133 return;
134 }
135 hpet_address = r.start;
Sebastian Andrzej Siewior4a66b1d2011-02-24 09:52:42 +0100136#endif
Sebastian Andrzej Siewiorffb9fc62011-02-22 21:07:41 +0100137}
138
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100139static void __init dtb_lapic_setup(void)
140{
141#ifdef CONFIG_X86_LOCAL_APIC
Thomas Gleixnera906fda2011-02-25 16:09:31 +0100142 struct device_node *dn;
143 struct resource r;
144 int ret;
145
146 dn = of_find_compatible_node(NULL, NULL, "intel,ce4100-lapic");
147 if (!dn)
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100148 return;
149
Thomas Gleixnera906fda2011-02-25 16:09:31 +0100150 ret = of_address_to_resource(dn, 0, &r);
151 if (WARN_ON(ret))
152 return;
153
154 /* Did the boot loader setup the local APIC ? */
Borislav Petkov93984fb2016-04-04 22:25:00 +0200155 if (!boot_cpu_has(X86_FEATURE_APIC)) {
Thomas Gleixnera906fda2011-02-25 16:09:31 +0100156 if (apic_force_enable(r.start))
157 return;
158 }
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100159 smp_found_config = 1;
160 pic_mode = 1;
Thomas Gleixnera906fda2011-02-25 16:09:31 +0100161 register_lapic_address(r.start);
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100162 generic_processor_info(boot_cpu_physical_apicid,
Thomas Gleixnera906fda2011-02-25 16:09:31 +0100163 GET_APIC_VERSION(apic_read(APIC_LVR)));
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100164#endif
165}
166
167#ifdef CONFIG_X86_IO_APIC
168static unsigned int ioapic_id;
169
Jiang Liufacd8fd2014-06-09 16:19:57 +0800170struct of_ioapic_type {
171 u32 out_type;
172 u32 trigger;
173 u32 polarity;
174};
175
176static struct of_ioapic_type of_ioapic_type[] =
177{
178 {
179 .out_type = IRQ_TYPE_EDGE_RISING,
180 .trigger = IOAPIC_EDGE,
181 .polarity = 1,
182 },
183 {
184 .out_type = IRQ_TYPE_LEVEL_LOW,
185 .trigger = IOAPIC_LEVEL,
186 .polarity = 0,
187 },
188 {
189 .out_type = IRQ_TYPE_LEVEL_HIGH,
190 .trigger = IOAPIC_LEVEL,
191 .polarity = 1,
192 },
193 {
194 .out_type = IRQ_TYPE_EDGE_FALLING,
195 .trigger = IOAPIC_EDGE,
196 .polarity = 0,
197 },
198};
199
Jiang Liud32932d2015-04-13 14:11:59 +0800200static int dt_irqdomain_alloc(struct irq_domain *domain, unsigned int virq,
201 unsigned int nr_irqs, void *arg)
Jiang Liufacd8fd2014-06-09 16:19:57 +0800202{
Ivan Gorinovb08a3582018-03-07 11:46:53 -0800203 struct irq_fwspec *fwspec = (struct irq_fwspec *)arg;
Jiang Liufacd8fd2014-06-09 16:19:57 +0800204 struct of_ioapic_type *it;
Jiang Liud32932d2015-04-13 14:11:59 +0800205 struct irq_alloc_info tmp;
Ivan Gorinovb08a3582018-03-07 11:46:53 -0800206 int type_index;
Jiang Liufacd8fd2014-06-09 16:19:57 +0800207
Ivan Gorinovb08a3582018-03-07 11:46:53 -0800208 if (WARN_ON(fwspec->param_count < 2))
Jiang Liufacd8fd2014-06-09 16:19:57 +0800209 return -EINVAL;
210
Ivan Gorinovb08a3582018-03-07 11:46:53 -0800211 type_index = fwspec->param[1];
212 if (type_index >= ARRAY_SIZE(of_ioapic_type))
213 return -EINVAL;
214
215 it = &of_ioapic_type[type_index];
Jiang Liud32932d2015-04-13 14:11:59 +0800216 ioapic_set_alloc_attr(&tmp, NUMA_NO_NODE, it->trigger, it->polarity);
217 tmp.ioapic_id = mpc_ioapic_id(mp_irqdomain_ioapic_idx(domain));
Ivan Gorinovb08a3582018-03-07 11:46:53 -0800218 tmp.ioapic_pin = fwspec->param[0];
Jiang Liufacd8fd2014-06-09 16:19:57 +0800219
Jiang Liud32932d2015-04-13 14:11:59 +0800220 return mp_irqdomain_alloc(domain, virq, nr_irqs, &tmp);
Jiang Liufacd8fd2014-06-09 16:19:57 +0800221}
222
Thomas Gleixnerf7a0c782015-04-14 10:30:08 +0800223static const struct irq_domain_ops ioapic_irq_domain_ops = {
224 .alloc = dt_irqdomain_alloc,
225 .free = mp_irqdomain_free,
226 .activate = mp_irqdomain_activate,
227 .deactivate = mp_irqdomain_deactivate,
Jiang Liufacd8fd2014-06-09 16:19:57 +0800228};
229
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100230static void __init dtb_add_ioapic(struct device_node *dn)
231{
232 struct resource r;
233 int ret;
Jiang Liufacd8fd2014-06-09 16:19:57 +0800234 struct ioapic_domain_cfg cfg = {
235 .type = IOAPIC_DOMAIN_DYNAMIC,
236 .ops = &ioapic_irq_domain_ops,
237 .dev = dn,
238 };
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100239
240 ret = of_address_to_resource(dn, 0, &r);
241 if (ret) {
242 printk(KERN_ERR "Can't obtain address from node %s.\n",
243 dn->full_name);
244 return;
245 }
Jiang Liufacd8fd2014-06-09 16:19:57 +0800246 mp_register_ioapic(++ioapic_id, r.start, gsi_top, &cfg);
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100247}
248
249static void __init dtb_ioapic_setup(void)
250{
251 struct device_node *dn;
252
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100253 for_each_compatible_node(dn, NULL, "intel,ce4100-ioapic")
254 dtb_add_ioapic(dn);
255
256 if (nr_ioapics) {
257 of_ioapic = 1;
258 return;
259 }
260 printk(KERN_ERR "Error: No information about IO-APIC in OF.\n");
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100261}
262#else
263static void __init dtb_ioapic_setup(void) {}
264#endif
265
266static void __init dtb_apic_setup(void)
267{
268 dtb_lapic_setup();
269 dtb_ioapic_setup();
270}
271
Sebastian Andrzej Siewior4a66b1d2011-02-24 09:52:42 +0100272#ifdef CONFIG_OF_FLATTREE
273static void __init x86_flattree_get_config(void)
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100274{
275 u32 size, map_len;
Rob Herring1bac1862014-03-31 15:17:31 -0500276 void *dt;
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100277
278 if (!initial_dtb)
279 return;
280
Rob Herring1bac1862014-03-31 15:17:31 -0500281 map_len = max(PAGE_SIZE - (initial_dtb & ~PAGE_MASK), (u64)128);
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100282
Ivan Gorinov0ea601d72018-03-07 11:46:29 -0800283 dt = early_memremap(initial_dtb, map_len);
284 size = fdt_totalsize(dt);
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100285 if (map_len < size) {
Juergen Gross8d4a40b2015-02-24 10:13:28 +0100286 early_memunmap(dt, map_len);
Ivan Gorinov0ea601d72018-03-07 11:46:29 -0800287 dt = early_memremap(initial_dtb, size);
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100288 map_len = size;
289 }
290
Ivan Gorinov0ea601d72018-03-07 11:46:29 -0800291 early_init_dt_verify(dt);
Rob Herring21c561b2013-08-28 09:57:57 -0500292 unflatten_and_copy_device_tree();
Juergen Gross8d4a40b2015-02-24 10:13:28 +0100293 early_memunmap(dt, map_len);
Sebastian Andrzej Siewior4a66b1d2011-02-24 09:52:42 +0100294}
295#else
296static inline void x86_flattree_get_config(void) { }
297#endif
298
Thomas Gleixnera906fda2011-02-25 16:09:31 +0100299void __init x86_dtb_init(void)
Sebastian Andrzej Siewior4a66b1d2011-02-24 09:52:42 +0100300{
301 x86_flattree_get_config();
302
303 if (!of_have_populated_dt())
304 return;
305
Sebastian Andrzej Siewiorffb9fc62011-02-22 21:07:41 +0100306 dtb_setup_hpet();
Sebastian Andrzej Siewior3879a6f2011-02-22 21:07:40 +0100307 dtb_apic_setup();
Sebastian Andrzej Siewiorda6b7372011-02-22 21:07:37 +0100308}