0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0019
0020 #include <linux/dtpm.h>
0021 #include <linux/init.h>
0022 #include <linux/kernel.h>
0023 #include <linux/powercap.h>
0024 #include <linux/slab.h>
0025 #include <linux/mutex.h>
0026 #include <linux/of.h>
0027
0028 #include "dtpm_subsys.h"
0029
0030 #define DTPM_POWER_LIMIT_FLAG 0
0031
0032 static const char *constraint_name[] = {
0033 "Instantaneous",
0034 };
0035
0036 static DEFINE_MUTEX(dtpm_lock);
0037 static struct powercap_control_type *pct;
0038 static struct dtpm *root;
0039
0040 static int get_time_window_us(struct powercap_zone *pcz, int cid, u64 *window)
0041 {
0042 return -ENOSYS;
0043 }
0044
0045 static int set_time_window_us(struct powercap_zone *pcz, int cid, u64 window)
0046 {
0047 return -ENOSYS;
0048 }
0049
0050 static int get_max_power_range_uw(struct powercap_zone *pcz, u64 *max_power_uw)
0051 {
0052 struct dtpm *dtpm = to_dtpm(pcz);
0053
0054 *max_power_uw = dtpm->power_max - dtpm->power_min;
0055
0056 return 0;
0057 }
0058
0059 static int __get_power_uw(struct dtpm *dtpm, u64 *power_uw)
0060 {
0061 struct dtpm *child;
0062 u64 power;
0063 int ret = 0;
0064
0065 if (dtpm->ops) {
0066 *power_uw = dtpm->ops->get_power_uw(dtpm);
0067 return 0;
0068 }
0069
0070 *power_uw = 0;
0071
0072 list_for_each_entry(child, &dtpm->children, sibling) {
0073 ret = __get_power_uw(child, &power);
0074 if (ret)
0075 break;
0076 *power_uw += power;
0077 }
0078
0079 return ret;
0080 }
0081
0082 static int get_power_uw(struct powercap_zone *pcz, u64 *power_uw)
0083 {
0084 return __get_power_uw(to_dtpm(pcz), power_uw);
0085 }
0086
0087 static void __dtpm_rebalance_weight(struct dtpm *dtpm)
0088 {
0089 struct dtpm *child;
0090
0091 list_for_each_entry(child, &dtpm->children, sibling) {
0092
0093 pr_debug("Setting weight '%d' for '%s'\n",
0094 child->weight, child->zone.name);
0095
0096 child->weight = DIV64_U64_ROUND_CLOSEST(
0097 child->power_max * 1024, dtpm->power_max);
0098
0099 __dtpm_rebalance_weight(child);
0100 }
0101 }
0102
0103 static void __dtpm_sub_power(struct dtpm *dtpm)
0104 {
0105 struct dtpm *parent = dtpm->parent;
0106
0107 while (parent) {
0108 parent->power_min -= dtpm->power_min;
0109 parent->power_max -= dtpm->power_max;
0110 parent->power_limit -= dtpm->power_limit;
0111 parent = parent->parent;
0112 }
0113 }
0114
0115 static void __dtpm_add_power(struct dtpm *dtpm)
0116 {
0117 struct dtpm *parent = dtpm->parent;
0118
0119 while (parent) {
0120 parent->power_min += dtpm->power_min;
0121 parent->power_max += dtpm->power_max;
0122 parent->power_limit += dtpm->power_limit;
0123 parent = parent->parent;
0124 }
0125 }
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136 int dtpm_update_power(struct dtpm *dtpm)
0137 {
0138 int ret;
0139
0140 __dtpm_sub_power(dtpm);
0141
0142 ret = dtpm->ops->update_power_uw(dtpm);
0143 if (ret)
0144 pr_err("Failed to update power for '%s': %d\n",
0145 dtpm->zone.name, ret);
0146
0147 if (!test_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags))
0148 dtpm->power_limit = dtpm->power_max;
0149
0150 __dtpm_add_power(dtpm);
0151
0152 if (root)
0153 __dtpm_rebalance_weight(root);
0154
0155 return ret;
0156 }
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169 int dtpm_release_zone(struct powercap_zone *pcz)
0170 {
0171 struct dtpm *dtpm = to_dtpm(pcz);
0172 struct dtpm *parent = dtpm->parent;
0173
0174 if (!list_empty(&dtpm->children))
0175 return -EBUSY;
0176
0177 if (parent)
0178 list_del(&dtpm->sibling);
0179
0180 __dtpm_sub_power(dtpm);
0181
0182 if (dtpm->ops)
0183 dtpm->ops->release(dtpm);
0184 else
0185 kfree(dtpm);
0186
0187 return 0;
0188 }
0189
0190 static int get_power_limit_uw(struct powercap_zone *pcz,
0191 int cid, u64 *power_limit)
0192 {
0193 *power_limit = to_dtpm(pcz)->power_limit;
0194
0195 return 0;
0196 }
0197
0198
0199
0200
0201
0202
0203
0204 static int __set_power_limit_uw(struct dtpm *dtpm, int cid, u64 power_limit)
0205 {
0206 struct dtpm *child;
0207 int ret = 0;
0208 u64 power;
0209
0210
0211
0212
0213
0214 if (power_limit == dtpm->power_max) {
0215 clear_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags);
0216 } else {
0217 set_bit(DTPM_POWER_LIMIT_FLAG, &dtpm->flags);
0218 }
0219
0220 pr_debug("Setting power limit for '%s': %llu uW\n",
0221 dtpm->zone.name, power_limit);
0222
0223
0224
0225
0226 if (dtpm->ops) {
0227 dtpm->power_limit = dtpm->ops->set_power_uw(dtpm, power_limit);
0228 } else {
0229 dtpm->power_limit = 0;
0230
0231 list_for_each_entry(child, &dtpm->children, sibling) {
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241 if (power_limit == dtpm->power_max) {
0242 power = child->power_max;
0243 } else if (power_limit == dtpm->power_min) {
0244 power = child->power_min;
0245 } else {
0246 power = DIV_ROUND_CLOSEST_ULL(
0247 power_limit * child->weight, 1024);
0248 }
0249
0250 pr_debug("Setting power limit for '%s': %llu uW\n",
0251 child->zone.name, power);
0252
0253 ret = __set_power_limit_uw(child, cid, power);
0254 if (!ret)
0255 ret = get_power_limit_uw(&child->zone, cid, &power);
0256
0257 if (ret)
0258 break;
0259
0260 dtpm->power_limit += power;
0261 }
0262 }
0263
0264 return ret;
0265 }
0266
0267 static int set_power_limit_uw(struct powercap_zone *pcz,
0268 int cid, u64 power_limit)
0269 {
0270 struct dtpm *dtpm = to_dtpm(pcz);
0271 int ret;
0272
0273
0274
0275
0276
0277 power_limit = clamp_val(power_limit, dtpm->power_min, dtpm->power_max);
0278
0279 ret = __set_power_limit_uw(dtpm, cid, power_limit);
0280
0281 pr_debug("%s: power limit: %llu uW, power max: %llu uW\n",
0282 dtpm->zone.name, dtpm->power_limit, dtpm->power_max);
0283
0284 return ret;
0285 }
0286
0287 static const char *get_constraint_name(struct powercap_zone *pcz, int cid)
0288 {
0289 return constraint_name[cid];
0290 }
0291
0292 static int get_max_power_uw(struct powercap_zone *pcz, int id, u64 *max_power)
0293 {
0294 *max_power = to_dtpm(pcz)->power_max;
0295
0296 return 0;
0297 }
0298
0299 static struct powercap_zone_constraint_ops constraint_ops = {
0300 .set_power_limit_uw = set_power_limit_uw,
0301 .get_power_limit_uw = get_power_limit_uw,
0302 .set_time_window_us = set_time_window_us,
0303 .get_time_window_us = get_time_window_us,
0304 .get_max_power_uw = get_max_power_uw,
0305 .get_name = get_constraint_name,
0306 };
0307
0308 static struct powercap_zone_ops zone_ops = {
0309 .get_max_power_range_uw = get_max_power_range_uw,
0310 .get_power_uw = get_power_uw,
0311 .release = dtpm_release_zone,
0312 };
0313
0314
0315
0316
0317
0318
0319 void dtpm_init(struct dtpm *dtpm, struct dtpm_ops *ops)
0320 {
0321 if (dtpm) {
0322 INIT_LIST_HEAD(&dtpm->children);
0323 INIT_LIST_HEAD(&dtpm->sibling);
0324 dtpm->weight = 1024;
0325 dtpm->ops = ops;
0326 }
0327 }
0328
0329
0330
0331
0332
0333
0334
0335
0336 void dtpm_unregister(struct dtpm *dtpm)
0337 {
0338 powercap_unregister_zone(pct, &dtpm->zone);
0339
0340 pr_debug("Unregistered dtpm node '%s'\n", dtpm->zone.name);
0341 }
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361
0362
0363
0364
0365 int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)
0366 {
0367 struct powercap_zone *pcz;
0368
0369 if (!pct)
0370 return -EAGAIN;
0371
0372 if (root && !parent)
0373 return -EBUSY;
0374
0375 if (!root && parent)
0376 return -EINVAL;
0377
0378 if (parent && parent->ops)
0379 return -EINVAL;
0380
0381 if (!dtpm)
0382 return -EINVAL;
0383
0384 if (dtpm->ops && !(dtpm->ops->set_power_uw &&
0385 dtpm->ops->get_power_uw &&
0386 dtpm->ops->update_power_uw &&
0387 dtpm->ops->release))
0388 return -EINVAL;
0389
0390 pcz = powercap_register_zone(&dtpm->zone, pct, name,
0391 parent ? &parent->zone : NULL,
0392 &zone_ops, MAX_DTPM_CONSTRAINTS,
0393 &constraint_ops);
0394 if (IS_ERR(pcz))
0395 return PTR_ERR(pcz);
0396
0397 if (parent) {
0398 list_add_tail(&dtpm->sibling, &parent->children);
0399 dtpm->parent = parent;
0400 } else {
0401 root = dtpm;
0402 }
0403
0404 if (dtpm->ops && !dtpm->ops->update_power_uw(dtpm)) {
0405 __dtpm_add_power(dtpm);
0406 dtpm->power_limit = dtpm->power_max;
0407 }
0408
0409 pr_debug("Registered dtpm node '%s' / %llu-%llu uW, \n",
0410 dtpm->zone.name, dtpm->power_min, dtpm->power_max);
0411
0412 return 0;
0413 }
0414
0415 static struct dtpm *dtpm_setup_virtual(const struct dtpm_node *hierarchy,
0416 struct dtpm *parent)
0417 {
0418 struct dtpm *dtpm;
0419 int ret;
0420
0421 dtpm = kzalloc(sizeof(*dtpm), GFP_KERNEL);
0422 if (!dtpm)
0423 return ERR_PTR(-ENOMEM);
0424 dtpm_init(dtpm, NULL);
0425
0426 ret = dtpm_register(hierarchy->name, dtpm, parent);
0427 if (ret) {
0428 pr_err("Failed to register dtpm node '%s': %d\n",
0429 hierarchy->name, ret);
0430 kfree(dtpm);
0431 return ERR_PTR(ret);
0432 }
0433
0434 return dtpm;
0435 }
0436
0437 static struct dtpm *dtpm_setup_dt(const struct dtpm_node *hierarchy,
0438 struct dtpm *parent)
0439 {
0440 struct device_node *np;
0441 int i, ret;
0442
0443 np = of_find_node_by_path(hierarchy->name);
0444 if (!np) {
0445 pr_err("Failed to find '%s'\n", hierarchy->name);
0446 return ERR_PTR(-ENXIO);
0447 }
0448
0449 for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) {
0450
0451 if (!dtpm_subsys[i]->setup)
0452 continue;
0453
0454 ret = dtpm_subsys[i]->setup(parent, np);
0455 if (ret) {
0456 pr_err("Failed to setup '%s': %d\n", dtpm_subsys[i]->name, ret);
0457 of_node_put(np);
0458 return ERR_PTR(ret);
0459 }
0460 }
0461
0462 of_node_put(np);
0463
0464
0465
0466
0467
0468 return NULL;
0469 }
0470
0471 typedef struct dtpm * (*dtpm_node_callback_t)(const struct dtpm_node *, struct dtpm *);
0472
0473 static dtpm_node_callback_t dtpm_node_callback[] = {
0474 [DTPM_NODE_VIRTUAL] = dtpm_setup_virtual,
0475 [DTPM_NODE_DT] = dtpm_setup_dt,
0476 };
0477
0478 static int dtpm_for_each_child(const struct dtpm_node *hierarchy,
0479 const struct dtpm_node *it, struct dtpm *parent)
0480 {
0481 struct dtpm *dtpm;
0482 int i, ret;
0483
0484 for (i = 0; hierarchy[i].name; i++) {
0485
0486 if (hierarchy[i].parent != it)
0487 continue;
0488
0489 dtpm = dtpm_node_callback[hierarchy[i].type](&hierarchy[i], parent);
0490
0491
0492
0493
0494
0495 if (!dtpm)
0496 continue;
0497
0498
0499
0500
0501
0502
0503
0504
0505
0506
0507
0508
0509 if (IS_ERR(dtpm)) {
0510 pr_warn("Failed to create '%s' in the hierarchy\n",
0511 hierarchy[i].name);
0512 continue;
0513 }
0514
0515 ret = dtpm_for_each_child(hierarchy, &hierarchy[i], dtpm);
0516 if (ret)
0517 return ret;
0518 }
0519
0520 return 0;
0521 }
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549 int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table)
0550 {
0551 const struct of_device_id *match;
0552 const struct dtpm_node *hierarchy;
0553 struct device_node *np;
0554 int i, ret;
0555
0556 mutex_lock(&dtpm_lock);
0557
0558 if (pct) {
0559 ret = -EBUSY;
0560 goto out_unlock;
0561 }
0562
0563 pct = powercap_register_control_type(NULL, "dtpm", NULL);
0564 if (IS_ERR(pct)) {
0565 pr_err("Failed to register control type\n");
0566 ret = PTR_ERR(pct);
0567 goto out_pct;
0568 }
0569
0570 ret = -ENODEV;
0571 np = of_find_node_by_path("/");
0572 if (!np)
0573 goto out_err;
0574
0575 match = of_match_node(dtpm_match_table, np);
0576
0577 of_node_put(np);
0578
0579 if (!match)
0580 goto out_err;
0581
0582 hierarchy = match->data;
0583 if (!hierarchy) {
0584 ret = -EFAULT;
0585 goto out_err;
0586 }
0587
0588 ret = dtpm_for_each_child(hierarchy, NULL, NULL);
0589 if (ret)
0590 goto out_err;
0591
0592 for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) {
0593
0594 if (!dtpm_subsys[i]->init)
0595 continue;
0596
0597 ret = dtpm_subsys[i]->init();
0598 if (ret)
0599 pr_info("Failed to initialize '%s': %d",
0600 dtpm_subsys[i]->name, ret);
0601 }
0602
0603 mutex_unlock(&dtpm_lock);
0604
0605 return 0;
0606
0607 out_err:
0608 powercap_unregister_control_type(pct);
0609 out_pct:
0610 pct = NULL;
0611 out_unlock:
0612 mutex_unlock(&dtpm_lock);
0613
0614 return ret;
0615 }
0616 EXPORT_SYMBOL_GPL(dtpm_create_hierarchy);
0617
0618 static void __dtpm_destroy_hierarchy(struct dtpm *dtpm)
0619 {
0620 struct dtpm *child, *aux;
0621
0622 list_for_each_entry_safe(child, aux, &dtpm->children, sibling)
0623 __dtpm_destroy_hierarchy(child);
0624
0625
0626
0627
0628
0629 dtpm_unregister(dtpm);
0630 }
0631
0632 void dtpm_destroy_hierarchy(void)
0633 {
0634 int i;
0635
0636 mutex_lock(&dtpm_lock);
0637
0638 if (!pct)
0639 goto out_unlock;
0640
0641 __dtpm_destroy_hierarchy(root);
0642
0643
0644 for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) {
0645
0646 if (!dtpm_subsys[i]->exit)
0647 continue;
0648
0649 dtpm_subsys[i]->exit();
0650 }
0651
0652 powercap_unregister_control_type(pct);
0653
0654 pct = NULL;
0655
0656 root = NULL;
0657
0658 out_unlock:
0659 mutex_unlock(&dtpm_lock);
0660 }
0661 EXPORT_SYMBOL_GPL(dtpm_destroy_hierarchy);