/**************************************************************************** * drivers/devfreq/devfreq_ondemand.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define DEVFREQ_MIN_SAMPLING_INTERVAL (2 * USEC_PER_TICK) #define DEVFREQ_LOAD_THRESHOLD_MAX (100) /**************************************************************************** * Private Types ****************************************************************************/ struct devfreq_ondemand_s { struct work_s work; uint32_t threshold; uint32_t sample_rate; uint32_t target_freq; FAR struct qos_request_s *req; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int devfreq_gov_ondemand_init(FAR struct devfreq_s *dev); static int devfreq_gov_ondemand_exit(FAR struct devfreq_s *dev); static int devfreq_gov_ondemand_start(FAR struct devfreq_s *dev); static void devfreq_gov_ondemand_stop(FAR struct devfreq_s *dev); static uint32_t devfreq_gov_ondemand_limit(FAR struct devfreq_s *dev); /**************************************************************************** * Private Data ****************************************************************************/ static struct devfreq_governor_s g_devfreq_gov_ondemand = { .name = "ondemand", .init = devfreq_gov_ondemand_init, .exit = devfreq_gov_ondemand_exit, .start = devfreq_gov_ondemand_start, .stop = devfreq_gov_ondemand_stop, .limit = devfreq_gov_ondemand_limit, }; /**************************************************************************** * Private Functions ****************************************************************************/ static uint32_t devfreq_gov_ondemand_cpuload(void) { struct cpuload_s loadavg; uint32_t idleload = 0; int cpu; for (cpu = 0; cpu < CONFIG_SMP_NCPUS; cpu++) { clock_cpuload(cpu, &loadavg); idleload += loadavg.active * 100 / loadavg.total; } return 100 - idleload; } static void devfreq_ondemand_worker(FAR void *arg) { FAR struct devfreq_s *dev = arg; FAR struct devfreq_ondemand_s *data; FAR struct qos_request_s *req; uint32_t cpuload; cpuload = devfreq_gov_ondemand_cpuload(); nxmutex_lock(&dev->lock); data = dev->governor_data; if (data == NULL) { nxmutex_unlock(&dev->lock); return; } if (cpuload > CONFIG_DEVFREQ_LOAD_THRESHOLD) { uint32_t cur_freq = devfreq_get_frequency(dev); if (cur_freq < dev->max) { data->target_freq = dev->max; } else { data->target_freq = cur_freq; } } else { data->target_freq = dev->min + cpuload * (dev->max - dev->min) / 100; } /* Re-queue before releasing the lock so that exit/stop can * reliably cancel the pending work after setting governor_data * to NULL. All accesses to 'data' must happen while holding * the lock to avoid use-after-free. */ req = data->req; work_queue(HPWORK, &data->work, devfreq_ondemand_worker, dev, data->sample_rate / USEC_PER_TICK); nxmutex_unlock(&dev->lock); devfreq_qos_update_request(dev, req, dev->min, dev->max); } static int devfreq_gov_ondemand_init(FAR struct devfreq_s *dev) { FAR struct devfreq_ondemand_s *data; data = kmm_zalloc(sizeof(struct devfreq_ondemand_s)); if (!data) { return -ENOMEM; } data->threshold = MIN(DEVFREQ_LOAD_THRESHOLD_MAX, CONFIG_DEVFREQ_LOAD_THRESHOLD); data->sample_rate = MAX(DEVFREQ_MIN_SAMPLING_INTERVAL, CONFIG_DEVFREQ_SAMPLE_RATE); dev->governor_data = data; data->req = devfreq_qos_add_request(dev, dev->min, dev->max); return 0; } static int devfreq_gov_ondemand_exit(FAR struct devfreq_s *dev) { FAR struct devfreq_ondemand_s *data = dev->governor_data; devfreq_qos_remove_request(dev, data->req); /* First, mark governor_data as NULL so that any in-flight worker * will see it and bail out without re-queuing. */ nxmutex_lock(&dev->lock); dev->governor_data = NULL; nxmutex_unlock(&dev->lock); /* Cancel any pending work that was queued before we cleared * governor_data, then it is safe to free. */ work_cancel_sync(HPWORK, &data->work); kmm_free(data); return 0; } static int devfreq_gov_ondemand_start(FAR struct devfreq_s *dev) { FAR struct devfreq_ondemand_s *data = dev->governor_data; work_queue(HPWORK, &data->work, devfreq_ondemand_worker, dev, 0); return 0; } static void devfreq_gov_ondemand_stop(FAR struct devfreq_s *dev) { FAR struct devfreq_ondemand_s *data = dev->governor_data; if (sched_idletask()) { work_cancel(HPWORK, &data->work); } else { work_cancel_sync(HPWORK, &data->work); } } static uint32_t devfreq_gov_ondemand_limit(FAR struct devfreq_s *dev) { FAR struct devfreq_ondemand_s *data = dev->governor_data; uint32_t freq = data->target_freq; if (freq > dev->max) { freq = dev->max; } if (freq < dev->min) { freq = dev->min; } return freq; } /**************************************************************************** * Public Functions ****************************************************************************/ FAR struct devfreq_governor_s *devfreq_ondemand(void) { return &g_devfreq_gov_ondemand; }