static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq, u32 flags) { int err = 0; struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct busfreq_data_int *data = platform_get_drvdata(pdev); struct opp *opp; unsigned long old_freq, freq; unsigned long volt; rcu_read_lock(); opp = devfreq_recommended_opp(dev, _freq, flags); if (IS_ERR(opp)) { rcu_read_unlock(); dev_err(dev, "%s: Invalid OPP.\n", __func__); return PTR_ERR(opp); } freq = opp_get_freq(opp); volt = opp_get_voltage(opp); rcu_read_unlock(); old_freq = data->curr_freq; if (old_freq == freq) return 0; dev_dbg(dev, "targeting %lukHz %luuV\n", freq, volt); mutex_lock(&data->lock); if (data->disabled) goto out; if (freq > exynos5_int_opp_table[0].clk) pm_qos_update_request(&data->int_req, freq * 16 / 1000); else pm_qos_update_request(&data->int_req, -1); if (old_freq < freq) err = exynos5_int_setvolt(data, volt); if (err) goto out; err = clk_set_rate(data->int_clk, freq * 1000); if (err) goto out; if (old_freq > freq) err = exynos5_int_setvolt(data, volt); if (err) goto out; data->curr_freq = freq; out: mutex_unlock(&data->lock); return err; }
static void exynos_busfreq_timer(struct work_struct *work) { struct delayed_work *delayed_work = to_delayed_work(work); struct busfreq_data *data = container_of(delayed_work, struct busfreq_data, worker); struct opp *opp; unsigned int voltage; unsigned long currfreq; unsigned long newfreq; unsigned int index = 0; opp = data->monitor(data); if (bus_ctrl.opp_lock) opp = bus_ctrl.opp_lock; ppmu_start(data->dev); newfreq = opp_get_freq(opp); index = data->get_table_index(opp); mutex_lock(&busfreq_lock); if (opp == data->curr_opp || newfreq == 0 || data->use == false) goto out; currfreq = opp_get_freq(data->curr_opp); voltage = opp_get_voltage(opp); if (newfreq > currfreq) { regulator_set_voltage(data->vdd_mif, voltage, voltage + 25000); voltage = data->get_int_volt(index); regulator_set_voltage(data->vdd_int, voltage, voltage + 25000); /*if (data->busfreq_prepare) data->busfreq_prepare(index);*/ } if (data->set_qos) data->set_qos(index); data->target(index); if (newfreq < currfreq) { /*if (data->busfreq_post) data->busfreq_post(index);*/ regulator_set_voltage(data->vdd_mif, voltage, voltage + 25000); voltage = data->get_int_volt(index); regulator_set_voltage(data->vdd_int, voltage, voltage + 25000); } data->curr_opp = opp; out: update_busfreq_stat(data, index); mutex_unlock(&busfreq_lock); queue_delayed_work(system_freezable_wq, &data->worker, data->sampling_rate); }
/* * This API is to be called during init to set the various voltage * domains to the voltage as per the opp table. Typically we boot up * at the nominal voltage. So this function finds out the rate of * the clock associated with the voltage domain, finds out the correct * opp entry and sets the voltage domain to the voltage specified * in the opp entry */ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, const char *oh_name) { struct voltagedomain *voltdm; struct clk *clk; struct opp *opp; unsigned long freq, bootup_volt; struct device *dev; if (!vdd_name || !clk_name || !oh_name) { pr_err("%s: invalid parameters\n", __func__); goto exit; } dev = omap_device_get_by_hwmod_name(oh_name); if (IS_ERR(dev)) { pr_err("%s: Unable to get dev pointer for hwmod %s\n", __func__, oh_name); goto exit; } voltdm = voltdm_lookup(vdd_name); if (IS_ERR(voltdm)) { pr_err("%s: unable to get vdd pointer for vdd_%s\n", __func__, vdd_name); goto exit; } clk = clk_get(NULL, clk_name); if (IS_ERR(clk)) { pr_err("%s: unable to get clk %s\n", __func__, clk_name); goto exit; } freq = clk->rate; clk_put(clk); opp = opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) { pr_err("%s: unable to find boot up OPP for vdd_%s\n", __func__, vdd_name); goto exit; } bootup_volt = opp_get_voltage(opp); if (!bootup_volt) { pr_err("%s: unable to find voltage corresponding " "to the bootup OPP for vdd_%s\n", __func__, vdd_name); goto exit; } voltdm_scale(voltdm, bootup_volt); return 0; exit: pr_err("%s: unable to set vdd_%s\n", __func__, vdd_name); return -EINVAL; }
/* * This API is to be called during init to put the various voltage * domains to the voltage as per the opp table. Typically we boot up * at the nominal voltage. So this function finds out the rate of * the clock associated with the voltage domain, finds out the correct * opp entry and puts the voltage domain to the voltage specifies * in the opp entry */ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, struct device *dev) { struct voltagedomain *voltdm; struct clk *clk; struct opp *opp; unsigned long freq, bootup_volt; if (!vdd_name || !clk_name || !dev) { printk(KERN_ERR "%s: Invalid parameters!\n", __func__); goto exit; } voltdm = omap_voltage_domain_lookup(vdd_name); if (IS_ERR(voltdm)) { printk(KERN_ERR "%s: Unable to get vdd pointer for vdd_%s\n", __func__, vdd_name); goto exit; } clk = clk_get(NULL, clk_name); if (IS_ERR(clk)) { printk(KERN_ERR "%s: unable to get clk %s\n", __func__, clk_name); goto exit; } freq = clk->rate; clk_put(clk); opp = opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) { printk(KERN_ERR "%s: unable to find boot up OPP for vdd_%s\n", __func__, vdd_name); goto exit; } bootup_volt = opp_get_voltage(opp); if (!bootup_volt) { printk(KERN_ERR "%s: unable to find voltage corresponding" "to the bootup OPP for vdd_%s\n", __func__, vdd_name); goto exit; } omap_voltage_scale_vdd(voltdm, bootup_volt); return 0; exit: printk(KERN_ERR "%s: Unable to put vdd_%s to its init voltage\n\n", __func__, vdd_name); return -EINVAL; }
static int exynos5_devfreq_isp_target(struct device *dev, unsigned long *target_freq, u32 flags) { int ret = 0; struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct devfreq_data_isp *data = platform_get_drvdata(pdev); struct devfreq *devfreq_isp = data->devfreq; struct opp *target_opp; int target_idx, old_idx; unsigned long target_volt; unsigned long old_freq; mutex_lock(&data->lock); rcu_read_lock(); target_opp = devfreq_recommended_opp(dev, target_freq, flags); if (IS_ERR(target_opp)) { rcu_read_unlock(); mutex_unlock(&data->lock); dev_err(dev, "DEVFREQ(ISP) : Invalid OPP to find\n"); ret = PTR_ERR(target_opp); goto out; } *target_freq = opp_get_freq(target_opp); target_volt = opp_get_voltage(target_opp); #ifdef CONFIG_EXYNOS_THERMAL target_volt = get_limit_voltage(target_volt, data->volt_offset); #endif rcu_read_unlock(); target_idx = exynos5_devfreq_get_idx(devfreq_isp_opp_list, data->max_state, *target_freq); old_idx = exynos5_devfreq_get_idx(devfreq_isp_opp_list, data->max_state, devfreq_isp->previous_freq); old_freq = devfreq_isp->previous_freq; if (target_idx < 0 || old_idx < 0) { ret = -EINVAL; goto out; } if (old_freq == *target_freq) goto out; pr_debug("ISP %lu ================> %lu\n", old_freq, *target_freq); if (old_freq < *target_freq) { if (data->isp_set_volt) data->isp_set_volt(data, target_volt, target_volt + VOLT_STEP, false); if (data->isp_set_freq) data->isp_set_freq(data, target_idx, old_idx); } else { if (data->isp_set_freq) data->isp_set_freq(data, target_idx, old_idx); if (data->isp_set_volt) data->isp_set_volt(data, target_volt, target_volt + VOLT_STEP, true); } out: mutex_unlock(&data->lock); return ret; }
static int exynos5_devfreq_isp_probe(struct platform_device *pdev) { int ret = 0; struct devfreq_data_isp *data; struct exynos_devfreq_platdata *plat_data; struct opp *target_opp; unsigned long freq; unsigned long volt; data = kzalloc(sizeof(struct devfreq_data_isp), GFP_KERNEL); if (data == NULL) { pr_err("DEVFREQ(ISP) : Failed to allocate private data\n"); ret = -ENOMEM; goto err_data; } exynos5433_devfreq_isp_init(data); exynos5_devfreq_isp_profile.max_state = data->max_state; exynos5_devfreq_isp_profile.freq_table = kzalloc(sizeof(int) * data->max_state, GFP_KERNEL); if (exynos5_devfreq_isp_profile.freq_table == NULL) { pr_err("DEVFREQ(ISP) : Failed to allocate freq table\n"); ret = -ENOMEM; goto err_freqtable; } ret = exynos5_init_isp_table(&pdev->dev, data); if (ret) goto err_inittable; platform_set_drvdata(pdev, data); mutex_init(&data->lock); data->initial_freq = exynos5_devfreq_isp_profile.initial_freq; data->volt_offset = 0; isp_dev = data->dev = &pdev->dev; data->vdd_isp = regulator_get(NULL, "vdd_disp_cam0"); if (IS_ERR_OR_NULL(data->vdd_isp)) { pr_err("DEVFREQ(ISP) : Failed to get regulator\n"); goto err_inittable; } freq = DEVFREQ_INITIAL_FREQ; rcu_read_lock(); target_opp = devfreq_recommended_opp(data->dev, &freq, 0); if (IS_ERR(target_opp)) { rcu_read_unlock(); dev_err(data->dev, "DEVFREQ(ISP) : Invalid OPP to set voltage"); ret = PTR_ERR(target_opp); goto err_opp; } volt = opp_get_voltage(target_opp); #ifdef CONFIG_EXYNOS_THERMAL volt = get_limit_voltage(volt, data->volt_offset); #endif rcu_read_unlock(); if (data->isp_set_volt) data->isp_set_volt(data, volt, volt + VOLT_STEP, false); data->devfreq = devfreq_add_device(data->dev, &exynos5_devfreq_isp_profile, "simple_ondemand", &exynos5_devfreq_isp_governor_data); plat_data = data->dev->platform_data; data->devfreq->min_freq = plat_data->default_qos; data->devfreq->max_freq = exynos5_devfreq_isp_governor_data.cal_qos_max; register_reboot_notifier(&exynos5_isp_reboot_notifier); #ifdef CONFIG_EXYNOS_THERMAL data->tmu_notifier.notifier_call = exynos5_devfreq_isp_tmu_notifier; exynos_tmu_add_notifier(&data->tmu_notifier); #endif data->use_dvfs = true; return ret; err_opp: regulator_put(data->vdd_isp); err_inittable: devfreq_remove_device(data->devfreq); kfree(exynos5_devfreq_isp_profile.freq_table); err_freqtable: kfree(data); err_data: return ret; }
static int exynos7_devfreq_disp_target(struct device *dev, unsigned long *target_freq, u32 flags) { int ret = 0; struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct devfreq_data_disp *data = platform_get_drvdata(pdev); struct devfreq *devfreq_disp = data->devfreq; struct opp *target_opp; int target_idx, old_idx; unsigned long target_volt; unsigned long old_freq; mutex_lock(&data->lock); rcu_read_lock(); target_opp = devfreq_recommended_opp(dev, target_freq, flags); if (IS_ERR(target_opp)) { rcu_read_unlock(); mutex_unlock(&data->lock); dev_err(dev, "DEVFREQ(DISP) : Invalid OPP to find\n"); return PTR_ERR(target_opp); } *target_freq = opp_get_freq(target_opp); target_volt = opp_get_voltage(target_opp); rcu_read_unlock(); target_idx = devfreq_get_opp_idx(devfreq_disp_opp_list, data->max_state, *target_freq); old_idx = devfreq_get_opp_idx(devfreq_disp_opp_list, data->max_state, devfreq_disp->previous_freq); old_freq = devfreq_disp->previous_freq; if (target_idx < 0 || old_idx < 0) { ret = -EINVAL; goto out; } if (old_freq == *target_freq) goto out; #ifdef CONFIG_EXYNOS_THERMAL target_volt = get_limit_voltage(target_volt, data->volt_offset, 0); #endif pr_debug("DISP LV_%d(%lu) ================> LV_%d(%lu, volt: %lu)\n", old_idx, old_freq, target_idx, *target_freq, target_volt); exynos_ss_freq(ESS_FLAG_DISP, old_freq, ESS_FLAG_IN); if (old_freq < *target_freq) { if (data->disp_set_volt) data->disp_set_volt(data, target_volt, REGULATOR_MAX_MICROVOLT); if (data->disp_set_freq) data->disp_set_freq(data, target_idx, old_idx); } else { if (data->disp_set_freq) data->disp_set_freq(data, target_idx, old_idx); if (data->disp_set_volt) data->disp_set_volt(data, target_volt, REGULATOR_MAX_MICROVOLT); } exynos_ss_freq(ESS_FLAG_DISP, *target_freq, ESS_FLAG_OUT); data->cur_freq = *target_freq; out: mutex_unlock(&data->lock); return ret; }
} data->curr_opp = opp; out: update_busfreq_stat(data, index); mutex_unlock(&busfreq_lock); queue_delayed_work(system_freezable_wq, &data->worker, data->sampling_rate); } static int exynos_buspm_notifier_event(struct notifier_block *this, unsigned long event, void *ptr) { struct busfreq_data *data = container_of(this, struct busfreq_data, exynos_buspm_notifier); unsigned long voltage = opp_get_voltage(data->max_opp); unsigned long freq = opp_get_freq(data->max_opp); unsigned int index = 0; switch (event) { case PM_SUSPEND_PREPARE: mutex_lock(&busfreq_lock); data->use = false; regulator_set_voltage(data->vdd_mif, voltage, voltage + 25000); voltage = data->get_int_volt(freq); regulator_set_voltage(data->vdd_int, voltage, voltage + 25000); index = data->get_table_index(data->max_opp); if (data->busfreq_prepare) data->busfreq_prepare(index); data->target(index); data->curr_opp = data->max_opp;
static int exynos5_devfreq_int_target(struct device *dev, unsigned long *target_freq, u32 flags) { int ret = 0; struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct devfreq_data_int *data = platform_get_drvdata(pdev); struct devfreq *devfreq_int = data->devfreq; struct opp *target_opp; int target_idx, old_idx; unsigned long target_volt; unsigned long old_freq; mutex_lock(&data->lock); rcu_read_lock(); target_opp = devfreq_recommended_opp(dev, target_freq, flags); if (IS_ERR(target_opp)) { rcu_read_unlock(); mutex_unlock(&data->lock); dev_err(dev, "DEVFREQ(INT) : Invalid OPP to find\n"); ret = PTR_ERR(target_opp); goto out; } *target_freq = opp_get_freq(target_opp); target_volt = opp_get_voltage(target_opp); #ifdef CONFIG_EXYNOS_THERMAL target_volt = get_limit_voltage(target_volt, data->volt_offset); #endif /* just want to save voltage before apply constraint with isp */ data->target_volt = target_volt; if (target_volt < data->volt_constraint_isp) target_volt = data->volt_constraint_isp; rcu_read_unlock(); target_idx = exynos5_devfreq_get_idx(devfreq_int_opp_list, data->max_state, *target_freq); old_idx = exynos5_devfreq_get_idx(devfreq_int_opp_list, data->max_state, devfreq_int->previous_freq); old_freq = devfreq_int->previous_freq; if (target_idx < 0 || old_idx < 0) { ret = -EINVAL; goto out; } if (old_freq == *target_freq) goto out; pr_debug("INT %lu ===================> %lu\n", old_freq, *target_freq); if (old_freq < *target_freq) { if (data->int_set_volt) data->int_set_volt(data, target_volt, target_volt + VOLT_STEP); set_match_abb(ID_INT, data->int_asv_abb_table[target_idx]); if (data->int_set_freq) data->int_set_freq(data, target_idx, old_idx); } else { if (data->int_set_freq) data->int_set_freq(data, target_idx, old_idx); set_match_abb(ID_INT, data->int_asv_abb_table[target_idx]); if (data->int_set_volt) data->int_set_volt(data, target_volt, target_volt + VOLT_STEP); } out: mutex_unlock(&data->lock); return ret; }
/* * This API is to be called during init to set the various voltage * domains to the voltage as per the opp table. Typically we boot up * at the nominal voltage. So this function finds out the rate of * the clock associated with the voltage domain, finds out the correct * opp entry and sets the voltage domain to the voltage specified * in the opp entry */ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, const char *oh_name) { struct voltagedomain *voltdm; struct clk *clk; struct opp *opp; struct device *dev; unsigned long freq_cur, freq_valid, bootup_volt; int ret = -EINVAL; dev = omap_device_get_by_hwmod_name(oh_name); if (IS_ERR(dev)) { pr_err("%s: Unable to get dev pointer for hwmod %s\n", __func__, oh_name); goto exit; } voltdm = voltdm_lookup(vdd_name); if (IS_ERR(voltdm)) { pr_err("%s: unable to get vdd pointer for vdd_%s\n", __func__, vdd_name); goto exit; } clk = clk_get(NULL, clk_name); if (IS_ERR(clk)) { pr_err("%s: unable to get clk %s\n", __func__, clk_name); goto exit; } freq_cur = clk->rate; freq_valid = freq_cur; rcu_read_lock(); opp = opp_find_freq_ceil(dev, &freq_valid); if (IS_ERR(opp)) { opp = opp_find_freq_floor(dev, &freq_valid); if (IS_ERR(opp)) { rcu_read_unlock(); pr_err("%s: no boot OPP match for %ld on vdd_%s\n", __func__, freq_cur, vdd_name); ret = -ENOENT; goto exit_ck; } } bootup_volt = opp_get_voltage(opp); rcu_read_unlock(); if (!bootup_volt) { pr_err("%s: unable to find voltage corresponding " "to the bootup OPP for vdd_%s\n", __func__, vdd_name); ret = -ENOENT; goto exit_ck; } /* * Frequency and Voltage have to be sequenced: if we move from * a lower frequency to higher frequency, raise voltage, followed by * frequency, and vice versa. we assume that the voltage at boot * is the required voltage for the frequency it was set for. * NOTE: * we can check the frequency, but there is numerous ways to set * voltage. We play the safe path and just set the voltage. */ if (freq_cur < freq_valid) { ret = voltdm_scale(voltdm, bootup_volt); if (ret) { pr_err("%s: Fail set voltage-%s(f=%ld v=%ld)on vdd%s\n", __func__, vdd_name, freq_valid, bootup_volt, vdd_name); goto exit_ck; } } /* Set freq only if there is a difference in freq */ if (freq_valid != freq_cur) { ret = clk_set_rate(clk, freq_valid); if (ret) { pr_err("%s: Fail set clk-%s(f=%ld v=%ld)on vdd%s\n", __func__, clk_name, freq_valid, bootup_volt, vdd_name); goto exit_ck; } } if (freq_cur >= freq_valid) { ret = voltdm_scale(voltdm, bootup_volt); if (ret) { pr_err("%s: Fail set voltage-%s(f=%ld v=%ld)on vdd%s\n", __func__, clk_name, freq_valid, bootup_volt, vdd_name); goto exit_ck; } } ret = 0; exit_ck: clk_put(clk); if (!ret) return 0; exit: pr_err("%s: unable to set vdd_%s\n", __func__, vdd_name); return -EINVAL; }
/** * omap_device_set_rate - Set a new rate at which the device is to operate * @req_dev : pointer to the device requesting the scaling. * @dev : pointer to the device that is to be scaled * @rate : the rnew rate for the device. * * This API gets the device opp table associated with this device and * tries putting the device to the requested rate and the voltage domain * associated with the device to the voltage corresponding to the * requested rate. Since multiple devices can be assocciated with a * voltage domain this API finds out the possible voltage the * voltage domain can enter and then decides on the final device * rate. Return 0 on success else the error value */ int omap_device_set_rate(struct device *req_dev, struct device *dev, unsigned long rate) { struct omap_opp *opp; unsigned long volt, freq, min_freq, max_freq, flags; struct voltagedomain *voltdm; struct platform_device *pdev; struct omap_device *od; int ret; pdev = container_of(dev, struct platform_device, dev); od = _find_by_pdev(pdev); /* if in low power DPLL cascading mode, bail out early */ if (cpu_is_omap44xx()) { read_lock_irqsave(&dpll_cascading_lock, flags); if (in_dpll_cascading) { ret = -EINVAL; goto out; } } /* * Figure out if the desired frquency lies between the * maximum and minimum possible for the particular device */ min_freq = 0; if (IS_ERR(opp_find_freq_ceil(dev, &min_freq))) { dev_err(dev, "%s: Unable to find lowest opp\n", __func__); ret = -ENODEV; goto out; } max_freq = ULONG_MAX; if (IS_ERR(opp_find_freq_floor(dev, &max_freq))) { dev_err(dev, "%s: Unable to find highest opp\n", __func__); ret = -ENODEV; goto out; } if (rate < min_freq) freq = min_freq; else if (rate > max_freq) freq = max_freq; else freq = rate; /* Get the possible rate from the opp layer */ opp = opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) { dev_dbg(dev, "%s: Unable to find OPP for freq%ld\n", __func__, rate); ret = -ENODEV; goto out; } if (unlikely(freq != rate)) dev_dbg(dev, "%s: Available freq %ld != dpll freq %ld.\n", __func__, freq, rate); /* Get the voltage corresponding to the requested frequency */ volt = opp_get_voltage(opp); /* * Call into the voltage layer to get the final voltage possible * for the voltage domain associated with the device. */ voltdm = od->hwmods[0]->voltdm; ret = omap_voltage_add_userreq(voltdm, req_dev, &volt); if (ret) { dev_err(dev, "%s: Unable to get the final volt for scaling\n", __func__); goto out; } /* Do the actual scaling */ ret = omap_voltage_scale(voltdm); out: if (cpu_is_omap44xx()) read_unlock_irqrestore(&dpll_cascading_lock, flags); return ret; }
/** * omap_device_scale() - Set a new rate at which the device is to operate * @req_dev: pointer to the device requesting the scaling. * @dev: pointer to the device that is to be scaled * @rate: the rnew rate for the device. * * This API gets the device opp table associated with this device and * tries putting the device to the requested rate and the voltage domain * associated with the device to the voltage corresponding to the * requested rate. Since multiple devices can be assocciated with a * voltage domain this API finds out the possible voltage the * voltage domain can enter and then decides on the final device * rate. Return 0 on success else the error value */ int omap_device_scale(struct device *req_dev, struct device *dev, unsigned long rate) { struct opp *opp; unsigned long volt, freq, min_freq, max_freq; struct voltagedomain *voltdm; struct platform_device *pdev; struct omap_device *od; int ret; pdev = container_of(dev, struct platform_device, dev); od = _find_by_pdev(pdev); /* * Figure out if the desired frquency lies between the * maximum and minimum possible for the particular device */ min_freq = 0; if (IS_ERR(opp_find_freq_ceil(dev, &min_freq))) { dev_err(dev, "%s: Unable to find lowest opp\n", __func__); return -ENODEV; } max_freq = ULONG_MAX; if (IS_ERR(opp_find_freq_floor(dev, &max_freq))) { dev_err(dev, "%s: Unable to find highest opp\n", __func__); return -ENODEV; } if (rate < min_freq) freq = min_freq; else if (rate > max_freq) freq = max_freq; else freq = rate; opp = opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) { dev_err(dev, "%s: Unable to find OPP for freq%ld\n", __func__, rate); return -ENODEV; } /* Get the voltage corresponding to the requested frequency */ volt = opp_get_voltage(opp); /* * Call into the voltage layer to get the final voltage possible * for the voltage domain associated with the device. */ voltdm = od->hwmods[0]->voltdm; ret = omap_voltage_add_request(voltdm, req_dev, &volt); if (ret) { dev_err(dev, "%s: Unable to get the final volt for scaling\n", __func__); return ret; } /* Do the actual scaling */ return omap_voltage_scale(voltdm, volt); }