GNU Linux-libre 4.9.309-gnu1
[releases.git] / drivers / staging / board / board.c
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
12 #include <linux/clkdev.h>
13 #include <linux/init.h>
14 #include <linux/irq.h>
15 #include <linux/device.h>
16 #include <linux/kernel.h>
17 #include <linux/of.h>
18 #include <linux/of_address.h>
19 #include <linux/of_irq.h>
20 #include <linux/platform_device.h>
21 #include <linux/pm_domain.h>
22
23 #include "board.h"
24
25 static struct device_node *irqc_node __initdata;
26 static unsigned int irqc_base __initdata;
27
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) {
34                 if (!of_address_to_resource(dn, 0, &res)) {
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 }
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 }
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 #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
139 static int board_staging_add_dev_domain(struct platform_device *pdev,
140                                         const char *domain)
141 {
142         struct device *dev = &pdev->dev;
143         struct of_phandle_args pd_args;
144         struct device_node *np;
145
146         np = of_find_node_by_path(domain);
147         if (!np) {
148                 pr_err("Cannot find domain node %s\n", domain);
149                 return -ENOENT;
150         }
151
152         pd_args.np = np;
153         pd_args.args_count = 0;
154
155         /* Initialization similar to device_pm_init_common() */
156         spin_lock_init(&dev->power.lock);
157         dev->power.early_init = true;
158
159         return of_genpd_add_device(&pd_args, dev);
160 }
161 #else
162 static inline int board_staging_add_dev_domain(struct platform_device *pdev,
163                                                const char *domain)
164 {
165         return 0;
166 }
167 #endif
168
169 int __init board_staging_register_device(const struct board_staging_dev *dev)
170 {
171         struct platform_device *pdev = dev->pdev;
172         unsigned int i;
173         int error;
174
175         pr_debug("Trying to register device %s\n", pdev->name);
176         if (board_staging_dt_node_available(pdev->resource,
177                                             pdev->num_resources)) {
178                 pr_warn("Skipping %s, already in DT\n", pdev->name);
179                 return -EEXIST;
180         }
181
182         board_staging_gic_fixup_resources(pdev->resource, pdev->num_resources);
183
184         for (i = 0; i < dev->nclocks; i++)
185                 board_staging_register_clock(&dev->clocks[i]);
186
187         if (dev->domain)
188                 board_staging_add_dev_domain(pdev, dev->domain);
189
190         error = platform_device_register(pdev);
191         if (error) {
192                 pr_err("Failed to register device %s (%d)\n", pdev->name,
193                        error);
194                 return error;
195         }
196
197         return error;
198 }
199
200 void __init board_staging_register_devices(const struct board_staging_dev *devs,
201                                            unsigned int ndevs)
202 {
203         unsigned int i;
204
205         for (i = 0; i < ndevs; i++)
206                 board_staging_register_device(&devs[i]);
207 }