Commit | Line | Data |
---|---|---|
72ee8626 GU |
1 | /* |
2 | * Copyright (C) 2014 Magnus Damm | |
3 | * Copyright (C) 2015 Glider bvba | |
4 | * | |
5 | * This file is subject to the terms and conditions of the GNU General Public | |
6 | * License. See the file "COPYING" in the main directory of this archive | |
7 | * for more details. | |
8 | */ | |
9 | ||
10 | #define pr_fmt(fmt) "board_staging: " fmt | |
11 | ||
225d68d8 | 12 | #include <linux/clkdev.h> |
382063d9 | 13 | #include <linux/init.h> |
72ee8626 | 14 | #include <linux/irq.h> |
382063d9 MD |
15 | #include <linux/device.h> |
16 | #include <linux/kernel.h> | |
17 | #include <linux/of.h> | |
18 | #include <linux/of_address.h> | |
72ee8626 | 19 | #include <linux/of_irq.h> |
225d68d8 GU |
20 | #include <linux/platform_device.h> |
21 | #include <linux/pm_domain.h> | |
72ee8626 | 22 | |
382063d9 MD |
23 | #include "board.h" |
24 | ||
72ee8626 GU |
25 | static struct device_node *irqc_node __initdata; |
26 | static unsigned int irqc_base __initdata; | |
27 | ||
382063d9 MD |
28 | static bool find_by_address(u64 base_address) |
29 | { | |
30 | struct device_node *dn = of_find_all_nodes(NULL); | |
31 | struct resource res; | |
32 | ||
33 | while (dn) { | |
13c11072 | 34 | if (!of_address_to_resource(dn, 0, &res)) { |
382063d9 MD |
35 | if (res.start == base_address) { |
36 | of_node_put(dn); | |
37 | return true; | |
38 | } | |
39 | } | |
40 | dn = of_find_all_nodes(dn); | |
41 | } | |
42 | ||
43 | return false; | |
44 | } | |
45 | ||
46 | bool __init board_staging_dt_node_available(const struct resource *resource, | |
47 | unsigned int num_resources) | |
48 | { | |
49 | unsigned int i; | |
50 | ||
51 | for (i = 0; i < num_resources; i++) { | |
52 | const struct resource *r = resource + i; | |
53 | ||
54 | if (resource_type(r) == IORESOURCE_MEM) | |
55 | if (find_by_address(r->start)) | |
56 | return true; /* DT node available */ | |
57 | } | |
58 | ||
59 | return false; /* Nothing found */ | |
60 | } | |
72ee8626 GU |
61 | |
62 | int __init board_staging_gic_setup_xlate(const char *gic_match, | |
63 | unsigned int base) | |
64 | { | |
65 | WARN_ON(irqc_node); | |
66 | ||
67 | irqc_node = of_find_compatible_node(NULL, NULL, gic_match); | |
68 | ||
69 | WARN_ON(!irqc_node); | |
70 | if (!irqc_node) | |
71 | return -ENOENT; | |
72 | ||
73 | irqc_base = base; | |
74 | return 0; | |
75 | } | |
76 | ||
77 | static void __init gic_fixup_resource(struct resource *res) | |
78 | { | |
79 | struct of_phandle_args irq_data; | |
80 | unsigned int hwirq = res->start; | |
81 | unsigned int virq; | |
82 | ||
83 | if (resource_type(res) != IORESOURCE_IRQ || !irqc_node) | |
84 | return; | |
85 | ||
86 | irq_data.np = irqc_node; | |
87 | irq_data.args_count = 3; | |
88 | irq_data.args[0] = 0; | |
89 | irq_data.args[1] = hwirq - irqc_base; | |
90 | switch (res->flags & | |
91 | (IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE | | |
92 | IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL)) { | |
93 | case IORESOURCE_IRQ_LOWEDGE: | |
94 | irq_data.args[2] = IRQ_TYPE_EDGE_FALLING; | |
95 | break; | |
96 | case IORESOURCE_IRQ_HIGHEDGE: | |
97 | irq_data.args[2] = IRQ_TYPE_EDGE_RISING; | |
98 | break; | |
99 | case IORESOURCE_IRQ_LOWLEVEL: | |
100 | irq_data.args[2] = IRQ_TYPE_LEVEL_LOW; | |
101 | break; | |
102 | case IORESOURCE_IRQ_HIGHLEVEL: | |
103 | default: | |
104 | irq_data.args[2] = IRQ_TYPE_LEVEL_HIGH; | |
105 | break; | |
106 | } | |
107 | ||
108 | virq = irq_create_of_mapping(&irq_data); | |
109 | if (WARN_ON(!virq)) | |
110 | return; | |
111 | ||
112 | pr_debug("hwirq %u -> virq %u\n", hwirq, virq); | |
113 | res->start = virq; | |
114 | } | |
115 | ||
116 | void __init board_staging_gic_fixup_resources(struct resource *res, | |
117 | unsigned int nres) | |
118 | { | |
119 | unsigned int i; | |
120 | ||
121 | for (i = 0; i < nres; i++) | |
122 | gic_fixup_resource(&res[i]); | |
123 | } | |
225d68d8 GU |
124 | |
125 | int __init board_staging_register_clock(const struct board_staging_clk *bsc) | |
126 | { | |
127 | int error; | |
128 | ||
129 | pr_debug("Aliasing clock %s for con_id %s dev_id %s\n", bsc->clk, | |
130 | bsc->con_id, bsc->dev_id); | |
131 | error = clk_add_alias(bsc->con_id, bsc->dev_id, bsc->clk, NULL); | |
132 | if (error) | |
133 | pr_err("Failed to alias clock %s (%d)\n", bsc->clk, error); | |
134 | ||
135 | return error; | |
136 | } | |
137 | ||
138 | int __init board_staging_register_device(const struct board_staging_dev *dev) | |
139 | { | |
140 | struct platform_device *pdev = dev->pdev; | |
141 | unsigned int i; | |
142 | int error; | |
143 | ||
144 | pr_debug("Trying to register device %s\n", pdev->name); | |
145 | if (board_staging_dt_node_available(pdev->resource, | |
146 | pdev->num_resources)) { | |
147 | pr_warn("Skipping %s, already in DT\n", pdev->name); | |
148 | return -EEXIST; | |
149 | } | |
150 | ||
151 | board_staging_gic_fixup_resources(pdev->resource, pdev->num_resources); | |
152 | ||
153 | for (i = 0; i < dev->nclocks; i++) | |
154 | board_staging_register_clock(&dev->clocks[i]); | |
155 | ||
156 | error = platform_device_register(pdev); | |
157 | if (error) { | |
158 | pr_err("Failed to register device %s (%d)\n", pdev->name, | |
159 | error); | |
160 | return error; | |
161 | } | |
162 | ||
163 | if (dev->domain) | |
164 | __pm_genpd_name_add_device(dev->domain, &pdev->dev, NULL); | |
165 | ||
166 | return error; | |
167 | } | |
168 | ||
169 | void __init board_staging_register_devices(const struct board_staging_dev *devs, | |
170 | unsigned int ndevs) | |
171 | { | |
172 | unsigned int i; | |
173 | ||
174 | for (i = 0; i < ndevs; i++) | |
175 | board_staging_register_device(&devs[i]); | |
176 | } |