From f5522a07aa12c9d4cb9e55c80fac16dd37b04ae7 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sat, 2 Nov 2013 00:51:00 +0200 Subject: [PATCH] omap_overclocking: add support for IVA (DSP) overclocking --- drivers/misc/omap_overclocking.c | 314 +++++++++++++++++++++++++------ 1 file changed, 260 insertions(+), 54 deletions(-) diff --git a/drivers/misc/omap_overclocking.c b/drivers/misc/omap_overclocking.c index 7551abd357d1..96e08cdc25cf 100644 --- a/drivers/misc/omap_overclocking.c +++ b/drivers/misc/omap_overclocking.c @@ -9,57 +9,74 @@ #include #include #include +#include #include #define PROC_DIR "pandora" #define PROC_CPUMHZ "pandora/cpu_mhz_max" +#define PROC_DSPMHZ "pandora/dsp_mhz_max" #define PROC_CPUOPP "pandora/cpu_opp_max" #define PROC_SYSMHZ "pandora/sys_mhz_max" -/* FIXME: could use opp3xxx_data.c, but that's initdata.. */ -static const unsigned long nominal_freqs_35xx[] = { +static struct device *mpu_dev; + +static struct device *iva_dev; +static struct regulator *iva_reg; +static struct clk *iva_clk; +static DEFINE_MUTEX(iva_lock); +static struct delayed_work iva_work; +static int iva_mhz_max; +static int iva_opp_min; +static int iva_active; + +/* XXX: could use opp3xxx_data.c, but that's initdata.. */ +static const unsigned long nominal_f_mpu_35xx[] = { 125000000, 250000000, 500000000, 550000000, 600000000, }; -static const unsigned long nominal_freqs_36xx[] = { +static const unsigned long nominal_f_mpu_36xx[] = { 300000000, 600000000, 800000000, 1000000000, }; -static const unsigned long *nominal_freqs; +static const unsigned long nominal_f_iva_35xx[] = { + 90000000, 180000000, 360000000, 400000000, 430000000, +}; + +static const unsigned long nominal_f_iva_36xx[] = { + 260000000, 520000000, 660000000, 800000000, +}; + +static const unsigned long *nominal_freqs_mpu; +static const unsigned long *nominal_freqs_iva; + +/* IVA voltages (MPU ones are managed by cpufreq) */ +static unsigned long iva_voltages[5]; static int opp_max_avail, opp_max_now, opp_max_ceil; -static int set_opp_max(int new_opp_max) +static int set_mpu_opp_max(int new_opp_max) { - struct device *mpu_dev; int i, ret; if (new_opp_max == opp_max_now) return 0; - mpu_dev = omap_device_get_by_hwmod_name("mpu"); - if (IS_ERR(mpu_dev)) { - pr_err("%s: mpu device not available (%ld)\n", - __func__, PTR_ERR(mpu_dev)); - return -ENODEV; - } - for (i = 1; i < new_opp_max; i++) { ret = opp_enable_i(mpu_dev, i); if (ret != 0) - dev_err(mpu_dev, "%s: opp_enable returned %d\n", + dev_err(mpu_dev, "%s: mpu opp_enable returned %d\n", __func__, ret); } for (i = new_opp_max; i < opp_max_avail; i++) { ret = opp_disable_i(mpu_dev, i); if (ret != 0) - dev_err(mpu_dev, "%s: opp_disable returned %d\n", + dev_err(mpu_dev, "%s: mpu opp_disable returned %d\n", __func__, ret); } - dev_info(mpu_dev, "max OPP set to %d\n", new_opp_max); + dev_info(mpu_dev, "max MPU OPP set to %d\n", new_opp_max); opp_max_now = new_opp_max; return 0; @@ -68,13 +85,12 @@ static int set_opp_max(int new_opp_max) static int set_opp_max_ceil(int new_opp_max) { opp_max_ceil = new_opp_max; - return set_opp_max(new_opp_max); + return set_mpu_opp_max(new_opp_max); } -static int set_cpu_mhz_max(unsigned long new_mhz_max) +static int set_mpu_mhz_max(unsigned long new_mhz_max) { unsigned long cur_mhz_max = 0; - struct device *mpu_dev; int index, ret; new_mhz_max *= 1000000; @@ -84,20 +100,15 @@ static int set_cpu_mhz_max(unsigned long new_mhz_max) __func__, opp_max_ceil); return -EINVAL; } - index = opp_max_ceil - 1; - /* find the minimum opp needed for this clock - * and make it max allowed for cpufreq */ - while (index > 0 && new_mhz_max <= nominal_freqs[index - 1]) + /* determine minimum OPP needed for given MPU clock limit, + * and limit that opp as maximum OPP. + * This is for cpufreq governors only. */ + index = opp_max_ceil - 1; + while (index > 0 && new_mhz_max <= nominal_freqs_mpu[index - 1]) index--; - set_opp_max(index + 1); - mpu_dev = omap_device_get_by_hwmod_name("mpu"); - if (IS_ERR(mpu_dev)) { - pr_err("%s: mpu device not available (%ld)\n", - __func__, PTR_ERR(mpu_dev)); - return -ENODEV; - } + set_mpu_opp_max(index + 1); opp_hack_get_freq(mpu_dev, index, &cur_mhz_max); if (cur_mhz_max == new_mhz_max) @@ -113,46 +124,133 @@ static int set_cpu_mhz_max(unsigned long new_mhz_max) return 0; } -static int get_cpu_mhz_max(void) +static int get_mpu_mhz_max(void) { unsigned long cur_mhz_max = 0; - struct device *mpu_dev; if (opp_max_now < 1 || opp_max_now > opp_max_avail) { pr_err("%s: corrupt opp_max: %d\n", __func__, opp_max_now); return -EINVAL; } - mpu_dev = omap_device_get_by_hwmod_name("mpu"); - if (IS_ERR(mpu_dev)) { - pr_err("%s: mpu device not available (%ld)\n", - __func__, PTR_ERR(mpu_dev)); - return -ENODEV; - } - opp_hack_get_freq(mpu_dev, opp_max_now - 1, &cur_mhz_max); return cur_mhz_max / 1000000; } -static int init_opp_hacks(void) +static void update_iva_opp_limit(int target_mhz) { - struct device *mpu_dev; + int volt_max; + int i, ret; - mpu_dev = omap_device_get_by_hwmod_name("mpu"); - if (IS_ERR(mpu_dev)) { - pr_err("%s: mpu device not available (%ld)\n", - __func__, PTR_ERR(mpu_dev)); - return -ENODEV; + for (i = 0; i < opp_max_ceil - 1; i++) { + if (target_mhz * 1000000 <= nominal_freqs_iva[i]) + break; + } + + if (iva_opp_min == i + 1) + return; + + //dev_info(iva_dev, "new IVA OPP %d for clock %d\n", + // i + 1, target_mhz); + + volt_max = iva_voltages[opp_max_avail - 1]; + volt_max += volt_max * 4 / 100; + + ret = regulator_set_voltage(iva_reg, iva_voltages[i], volt_max); + if (ret < 0) + dev_warn(iva_dev, "unable to set IVA OPP limits: %d\n", ret); + else + iva_opp_min = i + 1; +} + +static int set_dsp_mhz_max(unsigned long new_mhz_max) +{ + int ret; + + mutex_lock(&iva_lock); + + if (iva_active && new_mhz_max > iva_mhz_max) + /* going up.. */ + update_iva_opp_limit(new_mhz_max); + + ret = clk_set_rate(iva_clk, new_mhz_max * 1000000); + if (ret != 0) { + dev_warn(iva_dev, "unable to change IVA clock to %lu: %d\n", + new_mhz_max, ret); + goto out; + } + + if (iva_active && new_mhz_max < iva_mhz_max) + /* going down.. */ + update_iva_opp_limit(new_mhz_max); + + iva_mhz_max = new_mhz_max; +out: + mutex_unlock(&iva_lock); + + return ret; +} + +static int get_dsp_mhz_max(void) +{ + return iva_mhz_max; +} + +static void iva_unneeded_work(struct work_struct *work) +{ + mutex_lock(&iva_lock); + + update_iva_opp_limit(0); + iva_active = 0; + + mutex_unlock(&iva_lock); +} + +/* called from c64_tools */ +void dsp_power_notify(int enable) +{ + if (enable) { + cancel_delayed_work_sync(&iva_work); + + mutex_lock(&iva_lock); + + if (iva_active) { + mutex_unlock(&iva_lock); + return; + } + + /* apply the OPP limit */ + update_iva_opp_limit(iva_mhz_max); + iva_active = 1; + + mutex_unlock(&iva_lock); } + else { + if (!iva_active) + return; + + cancel_delayed_work_sync(&iva_work); + schedule_delayed_work(&iva_work, HZ * 2); + } +} +EXPORT_SYMBOL(dsp_power_notify); + +static int init_opp_hacks(void) +{ + int iva_init_freq; + struct opp *opp; + int i, ret; if (cpu_is_omap3630()) { - nominal_freqs = nominal_freqs_36xx; - opp_max_avail = sizeof(nominal_freqs_36xx) / sizeof(nominal_freqs_36xx[0]); + nominal_freqs_mpu = nominal_f_mpu_36xx; + nominal_freqs_iva = nominal_f_iva_36xx; + opp_max_avail = sizeof(nominal_f_mpu_36xx) / sizeof(nominal_f_mpu_36xx[0]); opp_max_ceil = 2; } else if (cpu_is_omap34xx()) { - nominal_freqs = nominal_freqs_35xx; - opp_max_avail = sizeof(nominal_freqs_35xx) / sizeof(nominal_freqs_35xx[0]); + nominal_freqs_mpu = nominal_f_mpu_35xx; + nominal_freqs_iva = nominal_f_iva_35xx; + opp_max_avail = sizeof(nominal_f_mpu_35xx) / sizeof(nominal_f_mpu_35xx[0]); opp_max_ceil = opp_max_avail; } else { dev_err(mpu_dev, "%s: unsupported CPU\n", __func__); @@ -160,6 +258,39 @@ static int init_opp_hacks(void) } opp_max_now = opp_max_ceil; + for (i = 0; i < opp_max_avail; i++) { + /* enable all OPPs for MPU so that cpufreq can find out + * maximum voltage to supply to regulator as max */ + ret = opp_enable_i(mpu_dev, i); + if (ret != 0) { + dev_err(mpu_dev, "opp_enable returned %d\n", ret); + return ret; + } + + ret = opp_enable_i(iva_dev, i); + if (ret != 0) { + dev_err(iva_dev, "opp_enable returned %d\n", ret); + return ret; + } + + opp = opp_find_freq_exact(iva_dev, nominal_freqs_iva[i], true); + if (IS_ERR(opp)) { + dev_err(iva_dev, "mising opp %d, %lu\n", + i, nominal_freqs_iva[i]); + return PTR_ERR(opp); + } + iva_voltages[i] = opp_get_voltage(opp); + } + + iva_init_freq = nominal_freqs_iva[(i + 1) / 2]; + ret = clk_set_rate(iva_clk, iva_init_freq); + if (ret == 0) { + iva_mhz_max = iva_init_freq / 1000000; + dev_info(iva_dev, "IVA freq set to %dMHz\n", iva_mhz_max); + } + else + dev_err(iva_dev, "IVA freq set failed: %d\n", ret); + return 0; } @@ -245,7 +376,7 @@ static int proc_write_val(struct file *file, const char __user *buffer, static int cpu_clk_read(char *page, char **start, off_t off, int count, int *eof, void *data) { - return proc_read_val(page, start, off, count, eof, get_cpu_mhz_max()); + return proc_read_val(page, start, off, count, eof, get_mpu_mhz_max()); } static int cpu_clk_write(struct file *file, const char __user *buffer, @@ -258,7 +389,30 @@ static int cpu_clk_write(struct file *file, const char __user *buffer, if (retval < 0) return retval; - ret = set_cpu_mhz_max(val); + ret = set_mpu_mhz_max(val); + if (ret < 0) + return ret; + + return retval; +} + +static int dsp_clk_read(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return proc_read_val(page, start, off, count, eof, get_dsp_mhz_max()); +} + +static int dsp_clk_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + unsigned long val; + int ret, retval; + + retval = proc_write_val(file, buffer, count, &val); + if (retval < 0) + return retval; + + ret = set_dsp_mhz_max(val); if (ret < 0) return ret; @@ -342,26 +496,78 @@ static int pndctrl_init(void) { int ret; + INIT_DELAYED_WORK(&iva_work, iva_unneeded_work); + + mpu_dev = omap_device_get_by_hwmod_name("mpu"); + if (IS_ERR(mpu_dev)) { + pr_err("%s: mpu device not available (%ld)\n", + __func__, PTR_ERR(mpu_dev)); + return -ENODEV; + } + + iva_dev = omap_device_get_by_hwmod_name("iva"); + if (IS_ERR(iva_dev)) { + pr_err("%s: iva device not available (%ld)\n", + __func__, PTR_ERR(iva_dev)); + return -ENODEV; + } + + /* regulator to constrain OPPs while DSP is running */ + iva_reg = regulator_get(iva_dev, "vcc"); + if (IS_ERR(iva_reg)) { + dev_err(iva_dev, "unable to get MPU regulator\n"); + return -ENODEV; + } + + /* + * Ensure physical regulator is present. + * (e.g. could be dummy regulator.) + */ + if (regulator_get_voltage(iva_reg) < 0) { + dev_err(iva_dev, "IVA regulator is not physical?\n"); + ret = -ENODEV; + goto fail_reg; + } + + iva_clk = clk_get(NULL, "dpll2_ck"); + if (IS_ERR(iva_clk)) { + dev_err(iva_dev, "IVA clock not available.\n"); + ret = PTR_ERR(iva_clk); + goto fail_reg; + } + ret = init_opp_hacks(); if (ret != 0) { pr_err("init_opp_hacks failed: %d\n", ret); - return -EFAULT; + goto fail_opp; } proc_create_rw(PROC_CPUMHZ, NULL, cpu_clk_read, cpu_clk_write); + proc_create_rw(PROC_DSPMHZ, NULL, dsp_clk_read, dsp_clk_write); proc_create_rw(PROC_CPUOPP, NULL, cpu_maxopp_read, cpu_maxopp_write); proc_create_rw(PROC_SYSMHZ, NULL, sys_clk_read, sys_clk_write); pr_info("OMAP overclocker loaded.\n"); return 0; + +fail_opp: + clk_put(iva_clk); +fail_reg: + regulator_put(iva_reg); + return ret; } static void pndctrl_cleanup(void) { + remove_proc_entry(PROC_SYSMHZ, NULL); remove_proc_entry(PROC_CPUOPP, NULL); + remove_proc_entry(PROC_DSPMHZ, NULL); remove_proc_entry(PROC_CPUMHZ, NULL); - remove_proc_entry(PROC_SYSMHZ, NULL); + + cancel_delayed_work_sync(&iva_work); + regulator_put(iva_reg); + clk_put(iva_clk); } module_init(pndctrl_init); -- 2.39.2