tracing/boot: Fix to check the histogram control param is a leaf node
[linux-2.6-microblaze.git] / drivers / soc / ti / ti_sci_pm_domains.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * TI SCI Generic Power Domain Driver
4  *
5  * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
6  *      J Keerthy <j-keerthy@ti.com>
7  *      Dave Gerlach <d-gerlach@ti.com>
8  */
9
10 #include <linux/err.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm_domain.h>
15 #include <linux/slab.h>
16 #include <linux/soc/ti/ti_sci_protocol.h>
17 #include <dt-bindings/soc/ti,sci_pm_domain.h>
18
19 /**
20  * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data
21  * @ti_sci: handle to TI SCI protocol driver that provides ops to
22  *          communicate with system control processor.
23  * @dev: pointer to dev for the driver for devm allocs
24  * @pd_list: list of all the power domains on the device
25  * @data: onecell data for genpd core
26  */
27 struct ti_sci_genpd_provider {
28         const struct ti_sci_handle *ti_sci;
29         struct device *dev;
30         struct list_head pd_list;
31         struct genpd_onecell_data data;
32 };
33
34 /**
35  * struct ti_sci_pm_domain: TI specific data needed for power domain
36  * @idx: index of the device that identifies it with the system
37  *       control processor.
38  * @exclusive: Permissions for exclusive request or shared request of the
39  *             device.
40  * @pd: generic_pm_domain for use with the genpd framework
41  * @node: link for the genpd list
42  * @parent: link to the parent TI SCI genpd provider
43  */
44 struct ti_sci_pm_domain {
45         int idx;
46         u8 exclusive;
47         struct generic_pm_domain pd;
48         struct list_head node;
49         struct ti_sci_genpd_provider *parent;
50 };
51
52 #define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
53
54 /*
55  * ti_sci_pd_power_off(): genpd power down hook
56  * @domain: pointer to the powerdomain to power off
57  */
58 static int ti_sci_pd_power_off(struct generic_pm_domain *domain)
59 {
60         struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
61         const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
62
63         return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx);
64 }
65
66 /*
67  * ti_sci_pd_power_on(): genpd power up hook
68  * @domain: pointer to the powerdomain to power on
69  */
70 static int ti_sci_pd_power_on(struct generic_pm_domain *domain)
71 {
72         struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain);
73         const struct ti_sci_handle *ti_sci = pd->parent->ti_sci;
74
75         if (pd->exclusive)
76                 return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci,
77                                                                 pd->idx);
78         else
79                 return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx);
80 }
81
82 /*
83  * ti_sci_pd_xlate(): translation service for TI SCI genpds
84  * @genpdspec: DT identification data for the genpd
85  * @data: genpd core data for all the powerdomains on the device
86  */
87 static struct generic_pm_domain *ti_sci_pd_xlate(
88                                         struct of_phandle_args *genpdspec,
89                                         void *data)
90 {
91         struct genpd_onecell_data *genpd_data = data;
92         unsigned int idx = genpdspec->args[0];
93
94         if (genpdspec->args_count != 1 && genpdspec->args_count != 2)
95                 return ERR_PTR(-EINVAL);
96
97         if (idx >= genpd_data->num_domains) {
98                 pr_err("%s: invalid domain index %u\n", __func__, idx);
99                 return ERR_PTR(-EINVAL);
100         }
101
102         if (!genpd_data->domains[idx])
103                 return ERR_PTR(-ENOENT);
104
105         genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive =
106                 genpdspec->args[1];
107
108         return genpd_data->domains[idx];
109 }
110
111 static const struct of_device_id ti_sci_pm_domain_matches[] = {
112         { .compatible = "ti,sci-pm-domain", },
113         { },
114 };
115 MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches);
116
117 static int ti_sci_pm_domain_probe(struct platform_device *pdev)
118 {
119         struct device *dev = &pdev->dev;
120         struct ti_sci_genpd_provider *pd_provider;
121         struct ti_sci_pm_domain *pd;
122         struct device_node *np = NULL;
123         struct of_phandle_args args;
124         int ret;
125         u32 max_id = 0;
126         int index;
127
128         pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL);
129         if (!pd_provider)
130                 return -ENOMEM;
131
132         pd_provider->ti_sci = devm_ti_sci_get_handle(dev);
133         if (IS_ERR(pd_provider->ti_sci))
134                 return PTR_ERR(pd_provider->ti_sci);
135
136         pd_provider->dev = dev;
137
138         INIT_LIST_HEAD(&pd_provider->pd_list);
139
140         /* Find highest device ID used for power domains */
141         while (1) {
142                 np = of_find_node_with_property(np, "power-domains");
143                 if (!np)
144                         break;
145
146                 index = 0;
147
148                 while (1) {
149                         ret = of_parse_phandle_with_args(np, "power-domains",
150                                                          "#power-domain-cells",
151                                                          index, &args);
152                         if (ret)
153                                 break;
154
155                         if (args.args_count >= 1 && args.np == dev->of_node) {
156                                 if (args.args[0] > max_id)
157                                         max_id = args.args[0];
158
159                                 pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
160                                 if (!pd)
161                                         return -ENOMEM;
162
163                                 pd->pd.name = devm_kasprintf(dev, GFP_KERNEL,
164                                                              "pd:%d",
165                                                              args.args[0]);
166                                 if (!pd->pd.name)
167                                         return -ENOMEM;
168
169                                 pd->pd.power_off = ti_sci_pd_power_off;
170                                 pd->pd.power_on = ti_sci_pd_power_on;
171                                 pd->idx = args.args[0];
172                                 pd->parent = pd_provider;
173
174                                 pm_genpd_init(&pd->pd, NULL, true);
175
176                                 list_add(&pd->node, &pd_provider->pd_list);
177                         }
178                         index++;
179                 }
180         }
181
182         pd_provider->data.domains =
183                 devm_kcalloc(dev, max_id + 1,
184                              sizeof(*pd_provider->data.domains),
185                              GFP_KERNEL);
186
187         pd_provider->data.num_domains = max_id + 1;
188         pd_provider->data.xlate = ti_sci_pd_xlate;
189
190         list_for_each_entry(pd, &pd_provider->pd_list, node)
191                 pd_provider->data.domains[pd->idx] = &pd->pd;
192
193         return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data);
194 }
195
196 static struct platform_driver ti_sci_pm_domains_driver = {
197         .probe = ti_sci_pm_domain_probe,
198         .driver = {
199                 .name = "ti_sci_pm_domains",
200                 .of_match_table = ti_sci_pm_domain_matches,
201         },
202 };
203 module_platform_driver(ti_sci_pm_domains_driver);
204 MODULE_LICENSE("GPL v2");
205 MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver");
206 MODULE_AUTHOR("Dave Gerlach");