]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/soc/qcom/rpmpd.c
Merge tag 'tee-misc-for-v5.1' of https://git.linaro.org/people/jens.wiklander/linux...
[linux.git] / drivers / soc / qcom / rpmpd.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. */
3
4 #include <linux/err.h>
5 #include <linux/init.h>
6 #include <linux/kernel.h>
7 #include <linux/mutex.h>
8 #include <linux/pm_domain.h>
9 #include <linux/of.h>
10 #include <linux/of_device.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm_opp.h>
13 #include <linux/soc/qcom/smd-rpm.h>
14
15 #include <dt-bindings/power/qcom-rpmpd.h>
16
17 #define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd)
18
19 /* Resource types */
20 #define RPMPD_SMPA 0x61706d73
21 #define RPMPD_LDOA 0x616f646c
22
23 /* Operation Keys */
24 #define KEY_CORNER              0x6e726f63 /* corn */
25 #define KEY_ENABLE              0x6e657773 /* swen */
26 #define KEY_FLOOR_CORNER        0x636676   /* vfc */
27
28 #define MAX_RPMPD_STATE         6
29
30 #define DEFINE_RPMPD_CORNER_SMPA(_platform, _name, _active, r_id)               \
31         static struct rpmpd _platform##_##_active;                      \
32         static struct rpmpd _platform##_##_name = {                     \
33                 .pd = { .name = #_name, },                              \
34                 .peer = &_platform##_##_active,                         \
35                 .res_type = RPMPD_SMPA,                                 \
36                 .res_id = r_id,                                         \
37                 .key = KEY_CORNER,                                      \
38         };                                                              \
39         static struct rpmpd _platform##_##_active = {                   \
40                 .pd = { .name = #_active, },                            \
41                 .peer = &_platform##_##_name,                           \
42                 .active_only = true,                                    \
43                 .res_type = RPMPD_SMPA,                                 \
44                 .res_id = r_id,                                         \
45                 .key = KEY_CORNER,                                      \
46         }
47
48 #define DEFINE_RPMPD_CORNER_LDOA(_platform, _name, r_id)                        \
49         static struct rpmpd _platform##_##_name = {                     \
50                 .pd = { .name = #_name, },                              \
51                 .res_type = RPMPD_LDOA,                                 \
52                 .res_id = r_id,                                         \
53                 .key = KEY_CORNER,                                      \
54         }
55
56 #define DEFINE_RPMPD_VFC(_platform, _name, r_id, r_type)                \
57         static struct rpmpd _platform##_##_name = {                     \
58                 .pd = { .name = #_name, },                              \
59                 .res_type = r_type,                                     \
60                 .res_id = r_id,                                         \
61                 .key = KEY_FLOOR_CORNER,                                \
62         }
63
64 #define DEFINE_RPMPD_VFC_SMPA(_platform, _name, r_id)                   \
65         DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_SMPA)
66
67 #define DEFINE_RPMPD_VFC_LDOA(_platform, _name, r_id)                   \
68         DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_LDOA)
69
70 struct rpmpd_req {
71         __le32 key;
72         __le32 nbytes;
73         __le32 value;
74 };
75
76 struct rpmpd {
77         struct generic_pm_domain pd;
78         struct rpmpd *peer;
79         const bool active_only;
80         unsigned int corner;
81         bool enabled;
82         const char *res_name;
83         const int res_type;
84         const int res_id;
85         struct qcom_smd_rpm *rpm;
86         __le32 key;
87 };
88
89 struct rpmpd_desc {
90         struct rpmpd **rpmpds;
91         size_t num_pds;
92 };
93
94 static DEFINE_MUTEX(rpmpd_lock);
95
96 /* msm8996 RPM Power domains */
97 DEFINE_RPMPD_CORNER_SMPA(msm8996, vddcx, vddcx_ao, 1);
98 DEFINE_RPMPD_CORNER_SMPA(msm8996, vddmx, vddmx_ao, 2);
99 DEFINE_RPMPD_CORNER_LDOA(msm8996, vddsscx, 26);
100
101 DEFINE_RPMPD_VFC_SMPA(msm8996, vddcx_vfc, 1);
102 DEFINE_RPMPD_VFC_LDOA(msm8996, vddsscx_vfc, 26);
103
104 static struct rpmpd *msm8996_rpmpds[] = {
105         [MSM8996_VDDCX] =       &msm8996_vddcx,
106         [MSM8996_VDDCX_AO] =    &msm8996_vddcx_ao,
107         [MSM8996_VDDCX_VFC] =   &msm8996_vddcx_vfc,
108         [MSM8996_VDDMX] =       &msm8996_vddmx,
109         [MSM8996_VDDMX_AO] =    &msm8996_vddmx_ao,
110         [MSM8996_VDDSSCX] =     &msm8996_vddsscx,
111         [MSM8996_VDDSSCX_VFC] = &msm8996_vddsscx_vfc,
112 };
113
114 static const struct rpmpd_desc msm8996_desc = {
115         .rpmpds = msm8996_rpmpds,
116         .num_pds = ARRAY_SIZE(msm8996_rpmpds),
117 };
118
119 static const struct of_device_id rpmpd_match_table[] = {
120         { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc },
121         { }
122 };
123
124 static int rpmpd_send_enable(struct rpmpd *pd, bool enable)
125 {
126         struct rpmpd_req req = {
127                 .key = KEY_ENABLE,
128                 .nbytes = cpu_to_le32(sizeof(u32)),
129                 .value = cpu_to_le32(enable),
130         };
131
132         return qcom_rpm_smd_write(pd->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
133                                   pd->res_type, pd->res_id, &req, sizeof(req));
134 }
135
136 static int rpmpd_send_corner(struct rpmpd *pd, int state, unsigned int corner)
137 {
138         struct rpmpd_req req = {
139                 .key = pd->key,
140                 .nbytes = cpu_to_le32(sizeof(u32)),
141                 .value = cpu_to_le32(corner),
142         };
143
144         return qcom_rpm_smd_write(pd->rpm, state, pd->res_type, pd->res_id,
145                                   &req, sizeof(req));
146 };
147
148 static void to_active_sleep(struct rpmpd *pd, unsigned int corner,
149                             unsigned int *active, unsigned int *sleep)
150 {
151         *active = corner;
152
153         if (pd->active_only)
154                 *sleep = 0;
155         else
156                 *sleep = *active;
157 }
158
159 static int rpmpd_aggregate_corner(struct rpmpd *pd)
160 {
161         int ret;
162         struct rpmpd *peer = pd->peer;
163         unsigned int active_corner, sleep_corner;
164         unsigned int this_active_corner = 0, this_sleep_corner = 0;
165         unsigned int peer_active_corner = 0, peer_sleep_corner = 0;
166
167         to_active_sleep(pd, pd->corner, &this_active_corner, &this_sleep_corner);
168
169         if (peer && peer->enabled)
170                 to_active_sleep(peer, peer->corner, &peer_active_corner,
171                                 &peer_sleep_corner);
172
173         active_corner = max(this_active_corner, peer_active_corner);
174
175         ret = rpmpd_send_corner(pd, QCOM_SMD_RPM_ACTIVE_STATE, active_corner);
176         if (ret)
177                 return ret;
178
179         sleep_corner = max(this_sleep_corner, peer_sleep_corner);
180
181         return rpmpd_send_corner(pd, QCOM_SMD_RPM_SLEEP_STATE, sleep_corner);
182 }
183
184 static int rpmpd_power_on(struct generic_pm_domain *domain)
185 {
186         int ret;
187         struct rpmpd *pd = domain_to_rpmpd(domain);
188
189         mutex_lock(&rpmpd_lock);
190
191         ret = rpmpd_send_enable(pd, true);
192         if (ret)
193                 goto out;
194
195         pd->enabled = true;
196
197         if (pd->corner)
198                 ret = rpmpd_aggregate_corner(pd);
199
200 out:
201         mutex_unlock(&rpmpd_lock);
202
203         return ret;
204 }
205
206 static int rpmpd_power_off(struct generic_pm_domain *domain)
207 {
208         int ret;
209         struct rpmpd *pd = domain_to_rpmpd(domain);
210
211         mutex_lock(&rpmpd_lock);
212
213         ret = rpmpd_send_enable(pd, false);
214         if (!ret)
215                 pd->enabled = false;
216
217         mutex_unlock(&rpmpd_lock);
218
219         return ret;
220 }
221
222 static int rpmpd_set_performance(struct generic_pm_domain *domain,
223                                  unsigned int state)
224 {
225         int ret = 0;
226         struct rpmpd *pd = domain_to_rpmpd(domain);
227
228         if (state > MAX_RPMPD_STATE)
229                 goto out;
230
231         mutex_lock(&rpmpd_lock);
232
233         pd->corner = state;
234
235         if (!pd->enabled && pd->key != KEY_FLOOR_CORNER)
236                 goto out;
237
238         ret = rpmpd_aggregate_corner(pd);
239
240 out:
241         mutex_unlock(&rpmpd_lock);
242
243         return ret;
244 }
245
246 static unsigned int rpmpd_get_performance(struct generic_pm_domain *genpd,
247                                           struct dev_pm_opp *opp)
248 {
249         return dev_pm_opp_get_level(opp);
250 }
251
252 static int rpmpd_probe(struct platform_device *pdev)
253 {
254         int i;
255         size_t num;
256         struct genpd_onecell_data *data;
257         struct qcom_smd_rpm *rpm;
258         struct rpmpd **rpmpds;
259         const struct rpmpd_desc *desc;
260
261         rpm = dev_get_drvdata(pdev->dev.parent);
262         if (!rpm) {
263                 dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
264                 return -ENODEV;
265         }
266
267         desc = of_device_get_match_data(&pdev->dev);
268         if (!desc)
269                 return -EINVAL;
270
271         rpmpds = desc->rpmpds;
272         num = desc->num_pds;
273
274         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
275         if (!data)
276                 return -ENOMEM;
277
278         data->domains = devm_kcalloc(&pdev->dev, num, sizeof(*data->domains),
279                                      GFP_KERNEL);
280         data->num_domains = num;
281
282         for (i = 0; i < num; i++) {
283                 if (!rpmpds[i]) {
284                         dev_warn(&pdev->dev, "rpmpds[] with empty entry at index=%d\n",
285                                  i);
286                         continue;
287                 }
288
289                 rpmpds[i]->rpm = rpm;
290                 rpmpds[i]->pd.power_off = rpmpd_power_off;
291                 rpmpds[i]->pd.power_on = rpmpd_power_on;
292                 rpmpds[i]->pd.set_performance_state = rpmpd_set_performance;
293                 rpmpds[i]->pd.opp_to_performance_state = rpmpd_get_performance;
294                 pm_genpd_init(&rpmpds[i]->pd, NULL, true);
295
296                 data->domains[i] = &rpmpds[i]->pd;
297         }
298
299         return of_genpd_add_provider_onecell(pdev->dev.of_node, data);
300 }
301
302 static struct platform_driver rpmpd_driver = {
303         .driver = {
304                 .name = "qcom-rpmpd",
305                 .of_match_table = rpmpd_match_table,
306                 .suppress_bind_attrs = true,
307         },
308         .probe = rpmpd_probe,
309 };
310
311 static int __init rpmpd_init(void)
312 {
313         return platform_driver_register(&rpmpd_driver);
314 }
315 core_initcall(rpmpd_init);