linux/drivers/devfreq/governor_passive.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * linux/drivers/devfreq/governor_passive.c
   4 *
   5 * Copyright (C) 2016 Samsung Electronics
   6 * Author: Chanwoo Choi <cw00.choi@samsung.com>
   7 * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/device.h>
  12#include <linux/devfreq.h>
  13#include "governor.h"
  14
  15static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
  16                                        unsigned long *freq)
  17{
  18        struct devfreq_passive_data *p_data
  19                        = (struct devfreq_passive_data *)devfreq->data;
  20        struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent;
  21        unsigned long child_freq = ULONG_MAX;
  22        struct dev_pm_opp *opp;
  23        int i, count, ret = 0;
  24
  25        /*
  26         * If the devfreq device with passive governor has the specific method
  27         * to determine the next frequency, should use the get_target_freq()
  28         * of struct devfreq_passive_data.
  29         */
  30        if (p_data->get_target_freq) {
  31                ret = p_data->get_target_freq(devfreq, freq);
  32                goto out;
  33        }
  34
  35        /*
  36         * If the parent and passive devfreq device uses the OPP table,
  37         * get the next frequency by using the OPP table.
  38         */
  39
  40        /*
  41         * - parent devfreq device uses the governors except for passive.
  42         * - passive devfreq device uses the passive governor.
  43         *
  44         * Each devfreq has the OPP table. After deciding the new frequency
  45         * from the governor of parent devfreq device, the passive governor
  46         * need to get the index of new frequency on OPP table of parent
  47         * device. And then the index is used for getting the suitable
  48         * new frequency for passive devfreq device.
  49         */
  50        if (!devfreq->profile || !devfreq->profile->freq_table
  51                || devfreq->profile->max_state <= 0)
  52                return -EINVAL;
  53
  54        /*
  55         * The passive governor have to get the correct frequency from OPP
  56         * list of parent device. Because in this case, *freq is temporary
  57         * value which is decided by ondemand governor.
  58         */
  59        opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0);
  60        if (IS_ERR(opp)) {
  61                ret = PTR_ERR(opp);
  62                goto out;
  63        }
  64
  65        dev_pm_opp_put(opp);
  66
  67        /*
  68         * Get the OPP table's index of decided freqeuncy by governor
  69         * of parent device.
  70         */
  71        for (i = 0; i < parent_devfreq->profile->max_state; i++)
  72                if (parent_devfreq->profile->freq_table[i] == *freq)
  73                        break;
  74
  75        if (i == parent_devfreq->profile->max_state) {
  76                ret = -EINVAL;
  77                goto out;
  78        }
  79
  80        /* Get the suitable frequency by using index of parent device. */
  81        if (i < devfreq->profile->max_state) {
  82                child_freq = devfreq->profile->freq_table[i];
  83        } else {
  84                count = devfreq->profile->max_state;
  85                child_freq = devfreq->profile->freq_table[count - 1];
  86        }
  87
  88        /* Return the suitable frequency for passive device. */
  89        *freq = child_freq;
  90
  91out:
  92        return ret;
  93}
  94
  95static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
  96{
  97        int ret;
  98
  99        if (!devfreq->governor)
 100                return -EINVAL;
 101
 102        mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
 103
 104        ret = devfreq->governor->get_target_freq(devfreq, &freq);
 105        if (ret < 0)
 106                goto out;
 107
 108        ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0);
 109        if (ret < 0)
 110                goto out;
 111
 112        if (devfreq->profile->freq_table
 113                && (devfreq_update_status(devfreq, freq)))
 114                dev_err(&devfreq->dev,
 115                        "Couldn't update frequency transition information.\n");
 116
 117        devfreq->previous_freq = freq;
 118
 119out:
 120        mutex_unlock(&devfreq->lock);
 121
 122        return 0;
 123}
 124
 125static int devfreq_passive_notifier_call(struct notifier_block *nb,
 126                                unsigned long event, void *ptr)
 127{
 128        struct devfreq_passive_data *data
 129                        = container_of(nb, struct devfreq_passive_data, nb);
 130        struct devfreq *devfreq = (struct devfreq *)data->this;
 131        struct devfreq *parent = (struct devfreq *)data->parent;
 132        struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr;
 133        unsigned long freq = freqs->new;
 134
 135        switch (event) {
 136        case DEVFREQ_PRECHANGE:
 137                if (parent->previous_freq > freq)
 138                        update_devfreq_passive(devfreq, freq);
 139                break;
 140        case DEVFREQ_POSTCHANGE:
 141                if (parent->previous_freq < freq)
 142                        update_devfreq_passive(devfreq, freq);
 143                break;
 144        }
 145
 146        return NOTIFY_DONE;
 147}
 148
 149static int devfreq_passive_event_handler(struct devfreq *devfreq,
 150                                unsigned int event, void *data)
 151{
 152        struct device *dev = devfreq->dev.parent;
 153        struct devfreq_passive_data *p_data
 154                        = (struct devfreq_passive_data *)devfreq->data;
 155        struct devfreq *parent = (struct devfreq *)p_data->parent;
 156        struct notifier_block *nb = &p_data->nb;
 157        int ret = 0;
 158
 159        if (!parent)
 160                return -EPROBE_DEFER;
 161
 162        switch (event) {
 163        case DEVFREQ_GOV_START:
 164                if (!p_data->this)
 165                        p_data->this = devfreq;
 166
 167                nb->notifier_call = devfreq_passive_notifier_call;
 168                ret = devm_devfreq_register_notifier(dev, parent, nb,
 169                                        DEVFREQ_TRANSITION_NOTIFIER);
 170                break;
 171        case DEVFREQ_GOV_STOP:
 172                devm_devfreq_unregister_notifier(dev, parent, nb,
 173                                        DEVFREQ_TRANSITION_NOTIFIER);
 174                break;
 175        default:
 176                break;
 177        }
 178
 179        return ret;
 180}
 181
 182static struct devfreq_governor devfreq_passive = {
 183        .name = DEVFREQ_GOV_PASSIVE,
 184        .immutable = 1,
 185        .get_target_freq = devfreq_passive_get_target_freq,
 186        .event_handler = devfreq_passive_event_handler,
 187};
 188
 189static int __init devfreq_passive_init(void)
 190{
 191        return devfreq_add_governor(&devfreq_passive);
 192}
 193subsys_initcall(devfreq_passive_init);
 194
 195static void __exit devfreq_passive_exit(void)
 196{
 197        int ret;
 198
 199        ret = devfreq_remove_governor(&devfreq_passive);
 200        if (ret)
 201                pr_err("%s: failed remove governor %d\n", __func__, ret);
 202}
 203module_exit(devfreq_passive_exit);
 204
 205MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
 206MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 207MODULE_DESCRIPTION("DEVFREQ Passive governor");
 208MODULE_LICENSE("GPL v2");
 209