| 1 | |
| 2 | |
| 3 | * Generic Exynos Bus frequency driver with DEVFREQ Framework
|
| 4 | *
|
| 5 | * Copyright (c) 2016 Samsung Electronics Co., Ltd.
|
| 6 | * Author : Chanwoo Choi <[email protected]>
|
| 7 | *
|
| 8 | * This driver support Exynos Bus frequency feature by using
|
| 9 | * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c.
|
| 10 |
|
| 11 |
|
| 12 | #include <linux/clk.h>
|
| 13 | #include <linux/devfreq.h>
|
| 14 | #include <linux/devfreq-event.h>
|
| 15 | #include <linux/device.h>
|
| 16 | #include <linux/export.h>
|
| 17 | #include <linux/module.h>
|
| 18 | #include <linux/of.h>
|
| 19 | #include <linux/pm_opp.h>
|
| 20 | #include <linux/platform_device.h>
|
| 21 | #include <linux/regulator/consumer.h>
|
| 22 |
|
| 23 | #define DEFAULT_SATURATION_RATIO 40
|
| 24 |
|
| 25 | struct exynos_bus {
|
| 26 | struct device *dev;
|
| 27 | struct platform_device *icc_pdev;
|
| 28 |
|
| 29 | struct devfreq *devfreq;
|
| 30 | struct devfreq_event_dev **edev;
|
| 31 | unsigned int edev_count;
|
| 32 | struct mutex lock;
|
| 33 |
|
| 34 | unsigned long curr_freq;
|
| 35 |
|
| 36 | int opp_token;
|
| 37 | struct clk *clk;
|
| 38 | unsigned int ratio;
|
| 39 | };
|
| 40 |
|
| 41 | |
| 42 | * Control the devfreq-event device to get the current state of bus
|
| 43 |
|
| 44 | #define exynos_bus_ops_edev(ops) \
|
| 45 | static int exynos_bus_##ops(struct exynos_bus *bus) \
|
| 46 | { \
|
| 47 | int i, ret; \
|
| 48 | \
|
| 49 | for (i = 0; i < bus->edev_count; i++) { \
|
| 50 | if (!bus->edev[i]) \
|
| 51 | continue; \
|
| 52 | ret = devfreq_event_##ops(bus->edev[i]); \
|
| 53 | if (ret < 0) \
|
| 54 | return ret; \
|
| 55 | } \
|
| 56 | \
|
| 57 | return 0; \
|
| 58 | }
|
| 59 | exynos_bus_ops_edev(enable_edev);
|
| 60 | exynos_bus_ops_edev(disable_edev);
|
| 61 | exynos_bus_ops_edev(set_event);
|
| 62 |
|
| 63 | static int exynos_bus_get_event(struct exynos_bus *bus,
|
| 64 | struct devfreq_event_data *edata)
|
| 65 | {
|
| 66 | struct devfreq_event_data event_data;
|
| 67 | unsigned long load_count = 0, total_count = 0;
|
| 68 | int i, ret = 0;
|
| 69 |
|
| 70 | for (i = 0; i < bus->edev_count; i++) {
|
| 71 | if (!bus->edev[i])
|
| 72 | continue;
|
| 73 |
|
| 74 | ret = devfreq_event_get_event(bus->edev[i], &event_data);
|
| 75 | if (ret < 0)
|
| 76 | return ret;
|
| 77 |
|
| 78 | if (i == 0 || event_data.load_count > load_count) {
|
| 79 | load_count = event_data.load_count;
|
| 80 | total_count = event_data.total_count;
|
| 81 | }
|
| 82 | }
|
| 83 |
|
| 84 | edata->load_count = load_count;
|
| 85 | edata->total_count = total_count;
|
| 86 |
|
| 87 | return ret;
|
| 88 | }
|
| 89 |
|
| 90 | |
| 91 | * devfreq function for both simple-ondemand and passive governor
|
| 92 |
|
| 93 | static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
|
| 94 | {
|
| 95 | struct exynos_bus *bus = dev_get_drvdata(dev);
|
| 96 | struct dev_pm_opp *new_opp;
|
| 97 | int ret = 0;
|
| 98 |
|
| 99 |
|
| 100 | new_opp = devfreq_recommended_opp(dev, freq, flags);
|
| 101 | if (IS_ERR(new_opp)) {
|
| 102 | dev_err(dev, "failed to get recommended opp instance\n");
|
| 103 | return PTR_ERR(new_opp);
|
| 104 | }
|
| 105 |
|
| 106 | dev_pm_opp_put(new_opp);
|
| 107 |
|
| 108 |
|
| 109 | mutex_lock(&bus->lock);
|
| 110 | ret = dev_pm_opp_set_rate(dev, *freq);
|
| 111 | if (!ret)
|
| 112 | bus->curr_freq = *freq;
|
| 113 |
|
| 114 | mutex_unlock(&bus->lock);
|
| 115 |
|
| 116 | return ret;
|
| 117 | }
|
| 118 |
|
| 119 | static int exynos_bus_get_dev_status(struct device *dev,
|
| 120 | struct devfreq_dev_status *stat)
|
| 121 | {
|
| 122 | struct exynos_bus *bus = dev_get_drvdata(dev);
|
| 123 | struct devfreq_event_data edata;
|
| 124 | int ret;
|
| 125 |
|
| 126 | stat->current_frequency = bus->curr_freq;
|
| 127 |
|
| 128 | ret = exynos_bus_get_event(bus, &edata);
|
| 129 | if (ret < 0) {
|
| 130 | dev_err(dev, "failed to get event from devfreq-event devices\n");
|
| 131 | stat->total_time = stat->busy_time = 0;
|
| 132 | goto err;
|
| 133 | }
|
| 134 |
|
| 135 | stat->busy_time = (edata.load_count * 100) / bus->ratio;
|
| 136 | stat->total_time = edata.total_count;
|
| 137 |
|
| 138 | dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n", stat->busy_time,
|
| 139 | stat->total_time);
|
| 140 |
|
| 141 | err:
|
| 142 | ret = exynos_bus_set_event(bus);
|
| 143 | if (ret < 0) {
|
| 144 | dev_err(dev, "failed to set event to devfreq-event devices\n");
|
| 145 | return ret;
|
| 146 | }
|
| 147 |
|
| 148 | return ret;
|
| 149 | }
|
| 150 |
|
| 151 | static void exynos_bus_exit(struct device *dev)
|
| 152 | {
|
| 153 | struct exynos_bus *bus = dev_get_drvdata(dev);
|
| 154 | int ret;
|
| 155 |
|
| 156 | ret = exynos_bus_disable_edev(bus);
|
| 157 | if (ret < 0)
|
| 158 | dev_warn(dev, "failed to disable the devfreq-event devices\n");
|
| 159 |
|
| 160 | platform_device_unregister(bus->icc_pdev);
|
| 161 |
|
| 162 | dev_pm_opp_of_remove_table(dev);
|
| 163 | dev_pm_opp_put_regulators(bus->opp_token);
|
| 164 | }
|
| 165 |
|
| 166 | static void exynos_bus_passive_exit(struct device *dev)
|
| 167 | {
|
| 168 | struct exynos_bus *bus = dev_get_drvdata(dev);
|
| 169 |
|
| 170 | platform_device_unregister(bus->icc_pdev);
|
| 171 |
|
| 172 | dev_pm_opp_of_remove_table(dev);
|
| 173 | }
|
| 174 |
|
| 175 | static int exynos_bus_parent_parse_of(struct device_node *np,
|
| 176 | struct exynos_bus *bus)
|
| 177 | {
|
| 178 | struct device *dev = bus->dev;
|
| 179 | const char *supplies[] = { "vdd", NULL };
|
| 180 | int i, ret, count, size;
|
| 181 |
|
| 182 | ret = dev_pm_opp_set_regulators(dev, supplies);
|
| 183 | if (ret < 0) {
|
| 184 | dev_err(dev, "failed to set regulators %d\n", ret);
|
| 185 | return ret;
|
| 186 | }
|
| 187 |
|
| 188 | bus->opp_token = ret;
|
| 189 |
|
| 190 | |
| 191 | * Get the devfreq-event devices to get the current utilization of
|
| 192 | * buses. This raw data will be used in devfreq ondemand governor.
|
| 193 |
|
| 194 | count = devfreq_event_get_edev_count(dev, "devfreq-events");
|
| 195 | if (count < 0) {
|
| 196 | dev_err(dev, "failed to get the count of devfreq-event dev\n");
|
| 197 | ret = count;
|
| 198 | goto err_regulator;
|
| 199 | }
|
| 200 | bus->edev_count = count;
|
| 201 |
|
| 202 | size = sizeof(*bus->edev) * count;
|
| 203 | bus->edev = devm_kzalloc(dev, size, GFP_KERNEL);
|
| 204 | if (!bus->edev) {
|
| 205 | ret = -ENOMEM;
|
| 206 | goto err_regulator;
|
| 207 | }
|
| 208 |
|
| 209 | for (i = 0; i < count; i++) {
|
| 210 | bus->edev[i] = devfreq_event_get_edev_by_phandle(dev,
|
| 211 | "devfreq-events", i);
|
| 212 | if (IS_ERR(bus->edev[i])) {
|
| 213 | ret = -EPROBE_DEFER;
|
| 214 | goto err_regulator;
|
| 215 | }
|
| 216 | }
|
| 217 |
|
| 218 | |
| 219 | * Optionally, Get the saturation ratio according to Exynos SoC
|
| 220 | * When measuring the utilization of each AXI bus with devfreq-event
|
| 221 | * devices, the measured real cycle might be much lower than the
|
| 222 | * total cycle of bus during sampling rate. In result, the devfreq
|
| 223 | * simple-ondemand governor might not decide to change the current
|
| 224 | * frequency due to too utilization (= real cycle/total cycle).
|
| 225 | * So, this property is used to adjust the utilization when calculating
|
| 226 | * the busy_time in exynos_bus_get_dev_status().
|
| 227 |
|
| 228 | if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
|
| 229 | bus->ratio = DEFAULT_SATURATION_RATIO;
|
| 230 |
|
| 231 | return 0;
|
| 232 |
|
| 233 | err_regulator:
|
| 234 | dev_pm_opp_put_regulators(bus->opp_token);
|
| 235 |
|
| 236 | return ret;
|
| 237 | }
|
| 238 |
|
| 239 | static int exynos_bus_parse_of(struct exynos_bus *bus)
|
| 240 | {
|
| 241 | struct device *dev = bus->dev;
|
| 242 | struct dev_pm_opp *opp;
|
| 243 | unsigned long rate;
|
| 244 | int ret;
|
| 245 |
|
| 246 |
|
| 247 | bus->clk = devm_clk_get_enabled(dev, "bus");
|
| 248 | if (IS_ERR(bus->clk))
|
| 249 | return dev_err_probe(dev, PTR_ERR(bus->clk),
|
| 250 | "failed to get bus clock\n");
|
| 251 |
|
| 252 |
|
| 253 | ret = dev_pm_opp_of_add_table(dev);
|
| 254 | if (ret < 0) {
|
| 255 | dev_err(dev, "failed to get OPP table\n");
|
| 256 | return ret;
|
| 257 | }
|
| 258 |
|
| 259 | rate = clk_get_rate(bus->clk);
|
| 260 |
|
| 261 | opp = devfreq_recommended_opp(dev, &rate, 0);
|
| 262 | if (IS_ERR(opp)) {
|
| 263 | dev_err(dev, "failed to find dev_pm_opp\n");
|
| 264 | ret = PTR_ERR(opp);
|
| 265 | goto err_opp;
|
| 266 | }
|
| 267 | bus->curr_freq = dev_pm_opp_get_freq(opp);
|
| 268 | dev_pm_opp_put(opp);
|
| 269 |
|
| 270 | return 0;
|
| 271 |
|
| 272 | err_opp:
|
| 273 | dev_pm_opp_of_remove_table(dev);
|
| 274 |
|
| 275 | return ret;
|
| 276 | }
|
| 277 |
|
| 278 | static int exynos_bus_profile_init(struct exynos_bus *bus,
|
| 279 | struct devfreq_dev_profile *profile)
|
| 280 | {
|
| 281 | struct device *dev = bus->dev;
|
| 282 | struct devfreq_simple_ondemand_data *ondemand_data;
|
| 283 | int ret;
|
| 284 |
|
| 285 |
|
| 286 | profile->polling_ms = 50;
|
| 287 | profile->target = exynos_bus_target;
|
| 288 | profile->get_dev_status = exynos_bus_get_dev_status;
|
| 289 | profile->exit = exynos_bus_exit;
|
| 290 |
|
| 291 | ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL);
|
| 292 | if (!ondemand_data)
|
| 293 | return -ENOMEM;
|
| 294 |
|
| 295 | ondemand_data->upthreshold = 40;
|
| 296 | ondemand_data->downdifferential = 5;
|
| 297 |
|
| 298 |
|
| 299 | bus->devfreq = devm_devfreq_add_device(dev, profile,
|
| 300 | DEVFREQ_GOV_SIMPLE_ONDEMAND,
|
| 301 | ondemand_data);
|
| 302 | if (IS_ERR(bus->devfreq)) {
|
| 303 | dev_err(dev, "failed to add devfreq device\n");
|
| 304 | return PTR_ERR(bus->devfreq);
|
| 305 | }
|
| 306 |
|
| 307 |
|
| 308 | ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq);
|
| 309 | if (ret < 0) {
|
| 310 | dev_err(dev, "failed to register opp notifier\n");
|
| 311 | return ret;
|
| 312 | }
|
| 313 |
|
| 314 | |
| 315 | * Enable devfreq-event to get raw data which is used to determine
|
| 316 | * current bus load.
|
| 317 |
|
| 318 | ret = exynos_bus_enable_edev(bus);
|
| 319 | if (ret < 0) {
|
| 320 | dev_err(dev, "failed to enable devfreq-event devices\n");
|
| 321 | return ret;
|
| 322 | }
|
| 323 |
|
| 324 | ret = exynos_bus_set_event(bus);
|
| 325 | if (ret < 0) {
|
| 326 | dev_err(dev, "failed to set event to devfreq-event devices\n");
|
| 327 | goto err_edev;
|
| 328 | }
|
| 329 |
|
| 330 | return 0;
|
| 331 |
|
| 332 | err_edev:
|
| 333 | if (exynos_bus_disable_edev(bus))
|
| 334 | dev_warn(dev, "failed to disable the devfreq-event devices\n");
|
| 335 |
|
| 336 | return ret;
|
| 337 | }
|
| 338 |
|
| 339 | static int exynos_bus_profile_init_passive(struct exynos_bus *bus,
|
| 340 | struct devfreq_dev_profile *profile)
|
| 341 | {
|
| 342 | struct device *dev = bus->dev;
|
| 343 | struct devfreq_passive_data *passive_data;
|
| 344 | struct devfreq *parent_devfreq;
|
| 345 |
|
| 346 |
|
| 347 | profile->target = exynos_bus_target;
|
| 348 | profile->exit = exynos_bus_passive_exit;
|
| 349 |
|
| 350 |
|
| 351 | parent_devfreq = devfreq_get_devfreq_by_phandle(dev, "devfreq", 0);
|
| 352 | if (IS_ERR(parent_devfreq))
|
| 353 | return -EPROBE_DEFER;
|
| 354 |
|
| 355 | passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);
|
| 356 | if (!passive_data)
|
| 357 | return -ENOMEM;
|
| 358 |
|
| 359 | passive_data->parent = parent_devfreq;
|
| 360 |
|
| 361 |
|
| 362 | bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_PASSIVE,
|
| 363 | passive_data);
|
| 364 | if (IS_ERR(bus->devfreq)) {
|
| 365 | dev_err(dev,
|
| 366 | "failed to add devfreq dev with passive governor\n");
|
| 367 | return PTR_ERR(bus->devfreq);
|
| 368 | }
|
| 369 |
|
| 370 | return 0;
|
| 371 | }
|
| 372 |
|
| 373 | static int exynos_bus_probe(struct platform_device *pdev)
|
| 374 | {
|
| 375 | struct device *dev = &pdev->dev;
|
| 376 | struct device_node *np = dev->of_node, *node;
|
| 377 | struct devfreq_dev_profile *profile;
|
| 378 | struct exynos_bus *bus;
|
| 379 | int ret, max_state;
|
| 380 | unsigned long min_freq, max_freq;
|
| 381 | bool passive = false;
|
| 382 |
|
| 383 | if (!np) {
|
| 384 | dev_err(dev, "failed to find devicetree node\n");
|
| 385 | return -EINVAL;
|
| 386 | }
|
| 387 |
|
| 388 | bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
|
| 389 | if (!bus)
|
| 390 | return -ENOMEM;
|
| 391 | mutex_init(&bus->lock);
|
| 392 | bus->dev = &pdev->dev;
|
| 393 | platform_set_drvdata(pdev, bus);
|
| 394 |
|
| 395 | profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL);
|
| 396 | if (!profile)
|
| 397 | return -ENOMEM;
|
| 398 |
|
| 399 | node = of_parse_phandle(dev->of_node, "devfreq", 0);
|
| 400 | if (node) {
|
| 401 | of_node_put(node);
|
| 402 | passive = true;
|
| 403 | } else {
|
| 404 | ret = exynos_bus_parent_parse_of(np, bus);
|
| 405 | if (ret < 0)
|
| 406 | return ret;
|
| 407 | }
|
| 408 |
|
| 409 |
|
| 410 | ret = exynos_bus_parse_of(bus);
|
| 411 | if (ret < 0)
|
| 412 | goto err_reg;
|
| 413 |
|
| 414 | if (passive)
|
| 415 | ret = exynos_bus_profile_init_passive(bus, profile);
|
| 416 | else
|
| 417 | ret = exynos_bus_profile_init(bus, profile);
|
| 418 |
|
| 419 | if (ret < 0)
|
| 420 | goto err;
|
| 421 |
|
| 422 |
|
| 423 | if (of_property_present(dev->of_node, "#interconnect-cells")) {
|
| 424 | bus->icc_pdev = platform_device_register_data(
|
| 425 | dev, "exynos-generic-icc",
|
| 426 | PLATFORM_DEVID_AUTO, NULL, 0);
|
| 427 |
|
| 428 | if (IS_ERR(bus->icc_pdev)) {
|
| 429 | ret = PTR_ERR(bus->icc_pdev);
|
| 430 | goto err;
|
| 431 | }
|
| 432 | }
|
| 433 |
|
| 434 | max_state = bus->devfreq->max_state;
|
| 435 | min_freq = (bus->devfreq->freq_table[0] / 1000);
|
| 436 | max_freq = (bus->devfreq->freq_table[max_state - 1] / 1000);
|
| 437 | pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n",
|
| 438 | dev_name(dev), min_freq, max_freq);
|
| 439 |
|
| 440 | return 0;
|
| 441 |
|
| 442 | err:
|
| 443 | dev_pm_opp_of_remove_table(dev);
|
| 444 | err_reg:
|
| 445 | dev_pm_opp_put_regulators(bus->opp_token);
|
| 446 |
|
| 447 | return ret;
|
| 448 | }
|
| 449 |
|
| 450 | static void exynos_bus_shutdown(struct platform_device *pdev)
|
| 451 | {
|
| 452 | struct exynos_bus *bus = dev_get_drvdata(&pdev->dev);
|
| 453 |
|
| 454 | devfreq_suspend_device(bus->devfreq);
|
| 455 | }
|
| 456 |
|
| 457 | static int exynos_bus_resume(struct device *dev)
|
| 458 | {
|
| 459 | struct exynos_bus *bus = dev_get_drvdata(dev);
|
| 460 | int ret;
|
| 461 |
|
| 462 | ret = exynos_bus_enable_edev(bus);
|
| 463 | if (ret < 0) {
|
| 464 | dev_err(dev, "failed to enable the devfreq-event devices\n");
|
| 465 | return ret;
|
| 466 | }
|
| 467 |
|
| 468 | return 0;
|
| 469 | }
|
| 470 |
|
| 471 | static int exynos_bus_suspend(struct device *dev)
|
| 472 | {
|
| 473 | struct exynos_bus *bus = dev_get_drvdata(dev);
|
| 474 | int ret;
|
| 475 |
|
| 476 | ret = exynos_bus_disable_edev(bus);
|
| 477 | if (ret < 0) {
|
| 478 | dev_err(dev, "failed to disable the devfreq-event devices\n");
|
| 479 | return ret;
|
| 480 | }
|
| 481 |
|
| 482 | return 0;
|
| 483 | }
|
| 484 |
|
| 485 | static DEFINE_SIMPLE_DEV_PM_OPS(exynos_bus_pm,
|
| 486 | exynos_bus_suspend, exynos_bus_resume);
|
| 487 |
|
| 488 | static const struct of_device_id exynos_bus_of_match[] = {
|
| 489 | { .compatible = "samsung,exynos-bus", },
|
| 490 | { },
|
| 491 | };
|
| 492 | MODULE_DEVICE_TABLE(of, exynos_bus_of_match);
|
| 493 |
|
| 494 | static struct platform_driver exynos_bus_platdrv = {
|
| 495 | .probe = exynos_bus_probe,
|
| 496 | .shutdown = exynos_bus_shutdown,
|
| 497 | .driver = {
|
| 498 | .name = "exynos-bus",
|
| 499 | .pm = pm_sleep_ptr(&exynos_bus_pm),
|
| 500 | .of_match_table = exynos_bus_of_match,
|
| 501 | },
|
| 502 | };
|
| 503 | module_platform_driver(exynos_bus_platdrv);
|
| 504 |
|
| 505 | MODULE_SOFTDEP("pre: exynos_ppmu");
|
| 506 | MODULE_DESCRIPTION("Generic Exynos Bus frequency driver");
|
| 507 | MODULE_AUTHOR("Chanwoo Choi <[email protected]>");
|
| 508 | MODULE_LICENSE("GPL v2");
|