]> Git Repo - linux.git/blob - drivers/soc/qcom/rpmhpd.c
keys: Make the KEY_NEED_* perms an enum rather than a mask
[linux.git] / drivers / soc / qcom / rpmhpd.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 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/slab.h>
10 #include <linux/of.h>
11 #include <linux/of_device.h>
12 #include <linux/platform_device.h>
13 #include <linux/pm_opp.h>
14 #include <soc/qcom/cmd-db.h>
15 #include <soc/qcom/rpmh.h>
16 #include <dt-bindings/power/qcom-rpmpd.h>
17
18 #define domain_to_rpmhpd(domain) container_of(domain, struct rpmhpd, pd)
19
20 #define RPMH_ARC_MAX_LEVELS     16
21
22 /**
23  * struct rpmhpd - top level RPMh power domain resource data structure
24  * @dev:                rpmh power domain controller device
25  * @pd:                 generic_pm_domain corrresponding to the power domain
26  * @peer:               A peer power domain in case Active only Voting is
27  *                      supported
28  * @active_only:        True if it represents an Active only peer
29  * @level:              An array of level (vlvl) to corner (hlvl) mappings
30  *                      derived from cmd-db
31  * @level_count:        Number of levels supported by the power domain. max
32  *                      being 16 (0 - 15)
33  * @enabled:            true if the power domain is enabled
34  * @res_name:           Resource name used for cmd-db lookup
35  * @addr:               Resource address as looped up using resource name from
36  *                      cmd-db
37  */
38 struct rpmhpd {
39         struct device   *dev;
40         struct generic_pm_domain pd;
41         struct generic_pm_domain *parent;
42         struct rpmhpd   *peer;
43         const bool      active_only;
44         unsigned int    corner;
45         unsigned int    active_corner;
46         u32             level[RPMH_ARC_MAX_LEVELS];
47         size_t          level_count;
48         bool            enabled;
49         const char      *res_name;
50         u32             addr;
51 };
52
53 struct rpmhpd_desc {
54         struct rpmhpd **rpmhpds;
55         size_t num_pds;
56 };
57
58 static DEFINE_MUTEX(rpmhpd_lock);
59
60 /* SDM845 RPMH powerdomains */
61
62 static struct rpmhpd sdm845_ebi = {
63         .pd = { .name = "ebi", },
64         .res_name = "ebi.lvl",
65 };
66
67 static struct rpmhpd sdm845_lmx = {
68         .pd = { .name = "lmx", },
69         .res_name = "lmx.lvl",
70 };
71
72 static struct rpmhpd sdm845_lcx = {
73         .pd = { .name = "lcx", },
74         .res_name = "lcx.lvl",
75 };
76
77 static struct rpmhpd sdm845_gfx = {
78         .pd = { .name = "gfx", },
79         .res_name = "gfx.lvl",
80 };
81
82 static struct rpmhpd sdm845_mss = {
83         .pd = { .name = "mss", },
84         .res_name = "mss.lvl",
85 };
86
87 static struct rpmhpd sdm845_mx_ao;
88 static struct rpmhpd sdm845_mx = {
89         .pd = { .name = "mx", },
90         .peer = &sdm845_mx_ao,
91         .res_name = "mx.lvl",
92 };
93
94 static struct rpmhpd sdm845_mx_ao = {
95         .pd = { .name = "mx_ao", },
96         .active_only = true,
97         .peer = &sdm845_mx,
98         .res_name = "mx.lvl",
99 };
100
101 static struct rpmhpd sdm845_cx_ao;
102 static struct rpmhpd sdm845_cx = {
103         .pd = { .name = "cx", },
104         .peer = &sdm845_cx_ao,
105         .parent = &sdm845_mx.pd,
106         .res_name = "cx.lvl",
107 };
108
109 static struct rpmhpd sdm845_cx_ao = {
110         .pd = { .name = "cx_ao", },
111         .active_only = true,
112         .peer = &sdm845_cx,
113         .parent = &sdm845_mx_ao.pd,
114         .res_name = "cx.lvl",
115 };
116
117 static struct rpmhpd *sdm845_rpmhpds[] = {
118         [SDM845_EBI] = &sdm845_ebi,
119         [SDM845_MX] = &sdm845_mx,
120         [SDM845_MX_AO] = &sdm845_mx_ao,
121         [SDM845_CX] = &sdm845_cx,
122         [SDM845_CX_AO] = &sdm845_cx_ao,
123         [SDM845_LMX] = &sdm845_lmx,
124         [SDM845_LCX] = &sdm845_lcx,
125         [SDM845_GFX] = &sdm845_gfx,
126         [SDM845_MSS] = &sdm845_mss,
127 };
128
129 static const struct rpmhpd_desc sdm845_desc = {
130         .rpmhpds = sdm845_rpmhpds,
131         .num_pds = ARRAY_SIZE(sdm845_rpmhpds),
132 };
133
134 /* SM8150 RPMH powerdomains */
135
136 static struct rpmhpd sm8150_mmcx_ao;
137 static struct rpmhpd sm8150_mmcx = {
138         .pd = { .name = "mmcx", },
139         .peer = &sm8150_mmcx_ao,
140         .res_name = "mmcx.lvl",
141 };
142
143 static struct rpmhpd sm8150_mmcx_ao = {
144         .pd = { .name = "mmcx_ao", },
145         .active_only = true,
146         .peer = &sm8150_mmcx,
147         .res_name = "mmcx.lvl",
148 };
149
150 static struct rpmhpd *sm8150_rpmhpds[] = {
151         [SM8150_MSS] = &sdm845_mss,
152         [SM8150_EBI] = &sdm845_ebi,
153         [SM8150_LMX] = &sdm845_lmx,
154         [SM8150_LCX] = &sdm845_lcx,
155         [SM8150_GFX] = &sdm845_gfx,
156         [SM8150_MX] = &sdm845_mx,
157         [SM8150_MX_AO] = &sdm845_mx_ao,
158         [SM8150_CX] = &sdm845_cx,
159         [SM8150_CX_AO] = &sdm845_cx_ao,
160         [SM8150_MMCX] = &sm8150_mmcx,
161         [SM8150_MMCX_AO] = &sm8150_mmcx_ao,
162 };
163
164 static const struct rpmhpd_desc sm8150_desc = {
165         .rpmhpds = sm8150_rpmhpds,
166         .num_pds = ARRAY_SIZE(sm8150_rpmhpds),
167 };
168
169 /* SC7180 RPMH powerdomains */
170 static struct rpmhpd *sc7180_rpmhpds[] = {
171         [SC7180_CX] = &sdm845_cx,
172         [SC7180_CX_AO] = &sdm845_cx_ao,
173         [SC7180_GFX] = &sdm845_gfx,
174         [SC7180_MX] = &sdm845_mx,
175         [SC7180_MX_AO] = &sdm845_mx_ao,
176         [SC7180_LMX] = &sdm845_lmx,
177         [SC7180_LCX] = &sdm845_lcx,
178         [SC7180_MSS] = &sdm845_mss,
179 };
180
181 static const struct rpmhpd_desc sc7180_desc = {
182         .rpmhpds = sc7180_rpmhpds,
183         .num_pds = ARRAY_SIZE(sc7180_rpmhpds),
184 };
185
186 static const struct of_device_id rpmhpd_match_table[] = {
187         { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc },
188         { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc },
189         { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc },
190         { }
191 };
192
193 static int rpmhpd_send_corner(struct rpmhpd *pd, int state,
194                               unsigned int corner, bool sync)
195 {
196         struct tcs_cmd cmd = {
197                 .addr = pd->addr,
198                 .data = corner,
199         };
200
201         /*
202          * Wait for an ack only when we are increasing the
203          * perf state of the power domain
204          */
205         if (sync)
206                 return rpmh_write(pd->dev, state, &cmd, 1);
207         else
208                 return rpmh_write_async(pd->dev, state, &cmd, 1);
209 }
210
211 static void to_active_sleep(struct rpmhpd *pd, unsigned int corner,
212                             unsigned int *active, unsigned int *sleep)
213 {
214         *active = corner;
215
216         if (pd->active_only)
217                 *sleep = 0;
218         else
219                 *sleep = *active;
220 }
221
222 /*
223  * This function is used to aggregate the votes across the active only
224  * resources and its peers. The aggregated votes are sent to RPMh as
225  * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes
226  * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh
227  * on system sleep).
228  * We send ACTIVE_ONLY votes for resources without any peers. For others,
229  * which have an active only peer, all 3 votes are sent.
230  */
231 static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner)
232 {
233         int ret;
234         struct rpmhpd *peer = pd->peer;
235         unsigned int active_corner, sleep_corner;
236         unsigned int this_active_corner = 0, this_sleep_corner = 0;
237         unsigned int peer_active_corner = 0, peer_sleep_corner = 0;
238
239         to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner);
240
241         if (peer && peer->enabled)
242                 to_active_sleep(peer, peer->corner, &peer_active_corner,
243                                 &peer_sleep_corner);
244
245         active_corner = max(this_active_corner, peer_active_corner);
246
247         ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner,
248                                  active_corner > pd->active_corner);
249         if (ret)
250                 return ret;
251
252         pd->active_corner = active_corner;
253
254         if (peer) {
255                 peer->active_corner = active_corner;
256
257                 ret = rpmhpd_send_corner(pd, RPMH_WAKE_ONLY_STATE,
258                                          active_corner, false);
259                 if (ret)
260                         return ret;
261
262                 sleep_corner = max(this_sleep_corner, peer_sleep_corner);
263
264                 return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner,
265                                           false);
266         }
267
268         return ret;
269 }
270
271 static int rpmhpd_power_on(struct generic_pm_domain *domain)
272 {
273         struct rpmhpd *pd = domain_to_rpmhpd(domain);
274         int ret = 0;
275
276         mutex_lock(&rpmhpd_lock);
277
278         if (pd->corner)
279                 ret = rpmhpd_aggregate_corner(pd, pd->corner);
280
281         if (!ret)
282                 pd->enabled = true;
283
284         mutex_unlock(&rpmhpd_lock);
285
286         return ret;
287 }
288
289 static int rpmhpd_power_off(struct generic_pm_domain *domain)
290 {
291         struct rpmhpd *pd = domain_to_rpmhpd(domain);
292         int ret = 0;
293
294         mutex_lock(&rpmhpd_lock);
295
296         ret = rpmhpd_aggregate_corner(pd, pd->level[0]);
297
298         if (!ret)
299                 pd->enabled = false;
300
301         mutex_unlock(&rpmhpd_lock);
302
303         return ret;
304 }
305
306 static int rpmhpd_set_performance_state(struct generic_pm_domain *domain,
307                                         unsigned int level)
308 {
309         struct rpmhpd *pd = domain_to_rpmhpd(domain);
310         int ret = 0, i;
311
312         mutex_lock(&rpmhpd_lock);
313
314         for (i = 0; i < pd->level_count; i++)
315                 if (level <= pd->level[i])
316                         break;
317
318         /*
319          * If the level requested is more than that supported by the
320          * max corner, just set it to max anyway.
321          */
322         if (i == pd->level_count)
323                 i--;
324
325         if (pd->enabled) {
326                 ret = rpmhpd_aggregate_corner(pd, i);
327                 if (ret)
328                         goto out;
329         }
330
331         pd->corner = i;
332 out:
333         mutex_unlock(&rpmhpd_lock);
334
335         return ret;
336 }
337
338 static unsigned int rpmhpd_get_performance_state(struct generic_pm_domain *genpd,
339                                                  struct dev_pm_opp *opp)
340 {
341         return dev_pm_opp_get_level(opp);
342 }
343
344 static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
345 {
346         int i;
347         const u16 *buf;
348
349         buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count);
350         if (IS_ERR(buf))
351                 return PTR_ERR(buf);
352
353         /* 2 bytes used for each command DB aux data entry */
354         rpmhpd->level_count >>= 1;
355
356         if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS)
357                 return -EINVAL;
358
359         for (i = 0; i < rpmhpd->level_count; i++) {
360                 rpmhpd->level[i] = buf[i];
361
362                 /*
363                  * The AUX data may be zero padded.  These 0 valued entries at
364                  * the end of the map must be ignored.
365                  */
366                 if (i > 0 && rpmhpd->level[i] == 0) {
367                         rpmhpd->level_count = i;
368                         break;
369                 }
370                 pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i,
371                          rpmhpd->level[i]);
372         }
373
374         return 0;
375 }
376
377 static int rpmhpd_probe(struct platform_device *pdev)
378 {
379         int i, ret;
380         size_t num_pds;
381         struct device *dev = &pdev->dev;
382         struct genpd_onecell_data *data;
383         struct rpmhpd **rpmhpds;
384         const struct rpmhpd_desc *desc;
385
386         desc = of_device_get_match_data(dev);
387         if (!desc)
388                 return -EINVAL;
389
390         rpmhpds = desc->rpmhpds;
391         num_pds = desc->num_pds;
392
393         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
394         if (!data)
395                 return -ENOMEM;
396
397         data->domains = devm_kcalloc(dev, num_pds, sizeof(*data->domains),
398                                      GFP_KERNEL);
399         if (!data->domains)
400                 return -ENOMEM;
401
402         data->num_domains = num_pds;
403
404         for (i = 0; i < num_pds; i++) {
405                 if (!rpmhpds[i]) {
406                         dev_warn(dev, "rpmhpds[%d] is empty\n", i);
407                         continue;
408                 }
409
410                 rpmhpds[i]->dev = dev;
411                 rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name);
412                 if (!rpmhpds[i]->addr) {
413                         dev_err(dev, "Could not find RPMh address for resource %s\n",
414                                 rpmhpds[i]->res_name);
415                         return -ENODEV;
416                 }
417
418                 ret = cmd_db_read_slave_id(rpmhpds[i]->res_name);
419                 if (ret != CMD_DB_HW_ARC) {
420                         dev_err(dev, "RPMh slave ID mismatch\n");
421                         return -EINVAL;
422                 }
423
424                 ret = rpmhpd_update_level_mapping(rpmhpds[i]);
425                 if (ret)
426                         return ret;
427
428                 rpmhpds[i]->pd.power_off = rpmhpd_power_off;
429                 rpmhpds[i]->pd.power_on = rpmhpd_power_on;
430                 rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance_state;
431                 rpmhpds[i]->pd.opp_to_performance_state = rpmhpd_get_performance_state;
432                 pm_genpd_init(&rpmhpds[i]->pd, NULL, true);
433
434                 data->domains[i] = &rpmhpds[i]->pd;
435         }
436
437         /* Add subdomains */
438         for (i = 0; i < num_pds; i++) {
439                 if (!rpmhpds[i])
440                         continue;
441                 if (rpmhpds[i]->parent)
442                         pm_genpd_add_subdomain(rpmhpds[i]->parent,
443                                                &rpmhpds[i]->pd);
444         }
445
446         return of_genpd_add_provider_onecell(pdev->dev.of_node, data);
447 }
448
449 static struct platform_driver rpmhpd_driver = {
450         .driver = {
451                 .name = "qcom-rpmhpd",
452                 .of_match_table = rpmhpd_match_table,
453                 .suppress_bind_attrs = true,
454         },
455         .probe = rpmhpd_probe,
456 };
457
458 static int __init rpmhpd_init(void)
459 {
460         return platform_driver_register(&rpmhpd_driver);
461 }
462 core_initcall(rpmhpd_init);
This page took 0.062763 seconds and 4 git commands to generate.