hc
2023-12-11 d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d
kernel/drivers/soc/ti/ti_sci_pm_domains.c
....@@ -1,156 +1,111 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * TI SCI Generic Power Domain Driver
34 *
45 * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
56 * J Keerthy <j-keerthy@ti.com>
67 * Dave Gerlach <d-gerlach@ti.com>
7
- *
8
- * This program is free software; you can redistribute it and/or
9
- * modify it under the terms of the GNU General Public License
10
- * version 2 as published by the Free Software Foundation.
11
- *
12
- * This program is distributed in the hope that it will be useful,
13
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- * GNU General Public License for more details.
168 */
179
1810 #include <linux/err.h>
1911 #include <linux/module.h>
20
-#include <linux/mutex.h>
2112 #include <linux/of.h>
2213 #include <linux/platform_device.h>
2314 #include <linux/pm_domain.h>
2415 #include <linux/slab.h>
2516 #include <linux/soc/ti/ti_sci_protocol.h>
17
+#include <dt-bindings/soc/ti,sci_pm_domain.h>
2618
2719 /**
28
- * struct ti_sci_genpd_dev_data: holds data needed for every device attached
29
- * to this genpd
30
- * @idx: index of the device that identifies it with the system
31
- * control processor.
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
3226 */
33
-struct ti_sci_genpd_dev_data {
34
- int idx;
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;
3532 };
3633
3734 /**
3835 * struct ti_sci_pm_domain: TI specific data needed for power domain
39
- * @ti_sci: handle to TI SCI protocol driver that provides ops to
40
- * communicate with system control processor.
41
- * @dev: pointer to dev for the driver for devm allocs
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.
4240 * @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
4343 */
4444 struct ti_sci_pm_domain {
45
- const struct ti_sci_handle *ti_sci;
46
- struct device *dev;
45
+ int idx;
46
+ u8 exclusive;
4747 struct generic_pm_domain pd;
48
+ struct list_head node;
49
+ struct ti_sci_genpd_provider *parent;
4850 };
4951
5052 #define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd)
5153
52
-/**
53
- * ti_sci_dev_id(): get prepopulated ti_sci id from struct dev
54
- * @dev: pointer to device associated with this genpd
55
- *
56
- * Returns device_id stored from ti,sci_id property
54
+/*
55
+ * ti_sci_pd_power_off(): genpd power down hook
56
+ * @domain: pointer to the powerdomain to power off
5757 */
58
-static int ti_sci_dev_id(struct device *dev)
58
+static int ti_sci_pd_power_off(struct generic_pm_domain *domain)
5959 {
60
- struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
61
- struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
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;
6262
63
- return sci_dev_data->idx;
63
+ return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx);
6464 }
6565
66
-/**
67
- * ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle
68
- * @dev: pointer to device associated with this genpd
69
- *
70
- * Returns ti_sci_handle to be used to communicate with system
71
- * control processor.
66
+/*
67
+ * ti_sci_pd_power_on(): genpd power up hook
68
+ * @domain: pointer to the powerdomain to power on
7269 */
73
-static const struct ti_sci_handle *ti_sci_dev_to_sci_handle(struct device *dev)
70
+static int ti_sci_pd_power_on(struct generic_pm_domain *domain)
7471 {
75
- struct generic_pm_domain *pd = pd_to_genpd(dev->pm_domain);
76
- struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(pd);
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;
7774
78
- return ti_sci_genpd->ti_sci;
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);
7980 }
8081
81
-/**
82
- * ti_sci_dev_start(): genpd device start hook called to turn device on
83
- * @dev: pointer to device associated with this genpd to be powered on
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
8486 */
85
-static int ti_sci_dev_start(struct device *dev)
87
+static struct generic_pm_domain *ti_sci_pd_xlate(
88
+ struct of_phandle_args *genpdspec,
89
+ void *data)
8690 {
87
- const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
88
- int idx = ti_sci_dev_id(dev);
91
+ struct genpd_onecell_data *genpd_data = data;
92
+ unsigned int idx = genpdspec->args[0];
8993
90
- return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
91
-}
94
+ if (genpdspec->args_count != 1 && genpdspec->args_count != 2)
95
+ return ERR_PTR(-EINVAL);
9296
93
-/**
94
- * ti_sci_dev_stop(): genpd device stop hook called to turn device off
95
- * @dev: pointer to device associated with this genpd to be powered off
96
- */
97
-static int ti_sci_dev_stop(struct device *dev)
98
-{
99
- const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
100
- int idx = ti_sci_dev_id(dev);
97
+ if (idx >= genpd_data->num_domains) {
98
+ pr_err("%s: invalid domain index %u\n", __func__, idx);
99
+ return ERR_PTR(-EINVAL);
100
+ }
101101
102
- return ti_sci->ops.dev_ops.put_device(ti_sci, idx);
103
-}
102
+ if (!genpd_data->domains[idx])
103
+ return ERR_PTR(-ENOENT);
104104
105
-static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
106
- struct device *dev)
107
-{
108
- struct device_node *np = dev->of_node;
109
- struct of_phandle_args pd_args;
110
- struct ti_sci_pm_domain *ti_sci_genpd = genpd_to_ti_sci_pd(domain);
111
- const struct ti_sci_handle *ti_sci = ti_sci_genpd->ti_sci;
112
- struct ti_sci_genpd_dev_data *sci_dev_data;
113
- struct generic_pm_domain_data *genpd_data;
114
- int idx, ret = 0;
105
+ genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive =
106
+ genpdspec->args[1];
115107
116
- ret = of_parse_phandle_with_args(np, "power-domains",
117
- "#power-domain-cells", 0, &pd_args);
118
- if (ret < 0)
119
- return ret;
120
-
121
- if (pd_args.args_count != 1)
122
- return -EINVAL;
123
-
124
- idx = pd_args.args[0];
125
-
126
- /*
127
- * Check the validity of the requested idx, if the index is not valid
128
- * the PMMC will return a NAK here and we will not allocate it.
129
- */
130
- ret = ti_sci->ops.dev_ops.is_valid(ti_sci, idx);
131
- if (ret)
132
- return -EINVAL;
133
-
134
- sci_dev_data = kzalloc(sizeof(*sci_dev_data), GFP_KERNEL);
135
- if (!sci_dev_data)
136
- return -ENOMEM;
137
-
138
- sci_dev_data->idx = idx;
139
-
140
- genpd_data = dev_gpd_data(dev);
141
- genpd_data->data = sci_dev_data;
142
-
143
- return 0;
144
-}
145
-
146
-static void ti_sci_pd_detach_dev(struct generic_pm_domain *domain,
147
- struct device *dev)
148
-{
149
- struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
150
- struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
151
-
152
- kfree(sci_dev_data);
153
- genpd_data->data = NULL;
108
+ return genpd_data->domains[idx];
154109 }
155110
156111 static const struct of_device_id ti_sci_pm_domain_matches[] = {
....@@ -162,33 +117,82 @@
162117 static int ti_sci_pm_domain_probe(struct platform_device *pdev)
163118 {
164119 struct device *dev = &pdev->dev;
165
- struct device_node *np = dev->of_node;
166
- struct ti_sci_pm_domain *ti_sci_pd;
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;
167124 int ret;
125
+ u32 max_id = 0;
126
+ int index;
168127
169
- ti_sci_pd = devm_kzalloc(dev, sizeof(*ti_sci_pd), GFP_KERNEL);
170
- if (!ti_sci_pd)
128
+ pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL);
129
+ if (!pd_provider)
171130 return -ENOMEM;
172131
173
- ti_sci_pd->ti_sci = devm_ti_sci_get_handle(dev);
174
- if (IS_ERR(ti_sci_pd->ti_sci))
175
- return PTR_ERR(ti_sci_pd->ti_sci);
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);
176135
177
- ti_sci_pd->dev = dev;
136
+ pd_provider->dev = dev;
178137
179
- ti_sci_pd->pd.name = "ti_sci_pd";
138
+ INIT_LIST_HEAD(&pd_provider->pd_list);
180139
181
- ti_sci_pd->pd.attach_dev = ti_sci_pd_attach_dev;
182
- ti_sci_pd->pd.detach_dev = ti_sci_pd_detach_dev;
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;
183145
184
- ti_sci_pd->pd.dev_ops.start = ti_sci_dev_start;
185
- ti_sci_pd->pd.dev_ops.stop = ti_sci_dev_stop;
146
+ index = 0;
186147
187
- pm_genpd_init(&ti_sci_pd->pd, NULL, true);
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;
188154
189
- ret = of_genpd_add_provider_simple(np, &ti_sci_pd->pd);
155
+ if (args.args_count >= 1 && args.np == dev->of_node) {
156
+ if (args.args[0] > max_id)
157
+ max_id = args.args[0];
190158
191
- return ret;
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
+ if (!pd_provider->data.domains)
187
+ return -ENOMEM;
188
+
189
+ pd_provider->data.num_domains = max_id + 1;
190
+ pd_provider->data.xlate = ti_sci_pd_xlate;
191
+
192
+ list_for_each_entry(pd, &pd_provider->pd_list, node)
193
+ pd_provider->data.domains[pd->idx] = &pd->pd;
194
+
195
+ return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data);
192196 }
193197
194198 static struct platform_driver ti_sci_pm_domains_driver = {