diff options
author | Colin Cross <ccross@android.com> | 2011-06-22 15:34:56 -0700 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2011-06-22 15:34:56 -0700 |
commit | 59993b9bdd4c00fa56456eb43de5403b9c69dac8 (patch) | |
tree | b9df8accdcd3374a5a7b6011e2bc19102c5f5706 | |
parent | 60c6f44ba237be3c0920d75c3d694ccadfd59fab (diff) | |
parent | 059da53243176a255574e347fd71f9bc1b39140d (diff) | |
download | kernel_samsung_espresso10-59993b9bdd4c00fa56456eb43de5403b9c69dac8.zip kernel_samsung_espresso10-59993b9bdd4c00fa56456eb43de5403b9c69dac8.tar.gz kernel_samsung_espresso10-59993b9bdd4c00fa56456eb43de5403b9c69dac8.tar.bz2 |
Merge branch 'linux-omap-pm-3.0' into linux-omap-3.0
-rw-r--r-- | arch/arm/mach-omap2/clock44xx_data.c | 114 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clockdomains44xx_data.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/dvfs.c | 19 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 89 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_opp_data.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/opp.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-omap2/opp4xxx_data.c | 56 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm.c | 8 | ||||
-rw-r--r-- | arch/arm/mach-omap2/voltagedomains44xx_data.c | 8 | ||||
-rw-r--r-- | arch/arm/plat-omap/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/plat-omap/i2c.c | 13 | ||||
-rw-r--r-- | arch/arm/plat-omap/omap-pm-helper.c | 321 | ||||
-rw-r--r-- | arch/arm/plat-omap/omap-pm-helper.h | 40 | ||||
-rw-r--r-- | arch/arm/plat-omap/omap-pm-interface.c | 239 | ||||
-rw-r--r-- | arch/arm/plat-omap/omap-pm-noop.c | 372 | ||||
-rw-r--r-- | arch/arm/plat-omap/omap-pm.c | 630 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq_hotplug.c | 69 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 47 | ||||
-rw-r--r-- | include/linux/i2c-omap.h | 2 |
20 files changed, 977 insertions, 1080 deletions
diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c index 85c551c..d332e7b 100644 --- a/arch/arm/mach-omap2/clock44xx_data.c +++ b/arch/arm/mach-omap2/clock44xx_data.c @@ -42,6 +42,10 @@ #define OMAP4430_MODULEMODE_HWCTRL 0 #define OMAP4430_MODULEMODE_SWCTRL 1 +static int omap4_virt_l3_set_rate(struct clk *clk, unsigned long rate); +static long omap4_virt_l3_round_rate(struct clk *clk, unsigned long rate); +static unsigned long omap4_virt_l3_recalc(struct clk *clk); + /* Root clocks */ static struct clk extalt_clkin_ck = { @@ -524,6 +528,15 @@ static struct clk dpll_core_m5x2_ck = { .set_rate = &omap2_clksel_set_rate, }; +static struct clk virt_l3_ck = { + .name = "virt_l3_ck", + .parent = &dpll_core_m5x2_ck, + .ops = &clkops_null, + .set_rate = &omap4_virt_l3_set_rate, + .recalc = &omap4_virt_l3_recalc, + .round_rate = &omap4_virt_l3_round_rate, +}; + static const struct clksel div_core_div[] = { { .parent = &dpll_core_m5x2_ck, .rates = div2_1to2_rates }, { .parent = NULL }, @@ -3208,6 +3221,7 @@ static struct omap_clk omap44xx_clks[] = { CLK(NULL, "dpll_core_m2_ck", &dpll_core_m2_ck, CK_44XX), CLK(NULL, "ddrphy_ck", &ddrphy_ck, CK_44XX), CLK(NULL, "dpll_core_m5x2_ck", &dpll_core_m5x2_ck, CK_44XX), + CLK(NULL, "virt_l3_ck", &virt_l3_ck, CK_44XX), CLK(NULL, "div_core_ck", &div_core_ck, CK_44XX), CLK(NULL, "div_iva_hs_clk", &div_iva_hs_clk, CK_44XX), CLK(NULL, "div_mpu_hs_clk", &div_mpu_hs_clk, CK_44XX), @@ -3452,6 +3466,106 @@ static struct omap_clk omap44xx_clks[] = { CLK(NULL, "smp_twd", &smp_twd, CK_44XX), }; +#define L3_OPP50_RATE 100000000 +#define DPLL_CORE_M3_OPP50_RATE 200000000 +#define DPLL_CORE_M3_OPP100_RATE 320000000 +#define DPLL_CORE_M6_OPP50_RATE 200000000 +#define DPLL_CORE_M6_OPP100_RATE 266600000 +#define DPLL_CORE_M7_OPP50_RATE 133333333 +#define DPLL_CORE_M7_OPP100_RATE 266666666 +#define DPLL_PER_M3_OPP50_RATE 192000000 +#define DPLL_PER_M3_OPP100_RATE 256000000 +#define DPLL_PER_M6_OPP50_RATE 192000000 +#define DPLL_PER_M6_OPP100_RATE 384000000 + +static long omap4_virt_l3_round_rate(struct clk *clk, unsigned long rate) +{ + long parent_rate; + + if (!clk || !clk->parent) + return 0; + + if (clk->parent->round_rate) { + parent_rate = clk->parent->round_rate(clk, rate * 2); + if (parent_rate) + return parent_rate / 2; + } + return 0; +} + +static unsigned long omap4_virt_l3_recalc(struct clk *clk) +{ + if (!clk || !clk->parent) + return 0; + + return clk->parent->rate / 2; +} + +static int omap4_clksel_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EINVAL; + + if (!clk->set_rate || !clk->round_rate) + return ret; + + rate = clk->round_rate(clk, rate); + if (rate) { + ret = clk->set_rate(clk, rate); + if (!ret) + propagate_rate(clk); + } + return ret; +} + +struct virt_l3_ck_deps { + unsigned long core_m3_rate; + unsigned long core_m6_rate; + unsigned long core_m7_rate; + unsigned long per_m3_rate; + unsigned long per_m6_rate; +}; + +#define NO_OF_L3_OPPS 2 +#define L3_OPP_50_INDEX 0 +#define L3_OPP_100_INDEX 1 + +static struct virt_l3_ck_deps omap4_virt_l3_clk_deps[NO_OF_L3_OPPS] = { + { /* OPP 50 */ + .core_m3_rate = DPLL_CORE_M3_OPP50_RATE, + .core_m6_rate = DPLL_CORE_M6_OPP50_RATE, + .core_m7_rate = DPLL_CORE_M7_OPP50_RATE, + .per_m3_rate = DPLL_PER_M3_OPP50_RATE, + .per_m6_rate = DPLL_PER_M6_OPP50_RATE, + }, + { /* OPP 100 */ + .core_m3_rate = DPLL_CORE_M3_OPP100_RATE, + .core_m6_rate = DPLL_CORE_M3_OPP100_RATE, + .core_m7_rate = DPLL_CORE_M7_OPP100_RATE, + .per_m3_rate = DPLL_PER_M3_OPP100_RATE, + .per_m6_rate = DPLL_PER_M6_OPP100_RATE, + }, +}; + +static int omap4_virt_l3_set_rate(struct clk *clk, unsigned long rate) +{ + struct virt_l3_ck_deps *l3_deps; + + if (rate <= L3_OPP50_RATE) + l3_deps = &omap4_virt_l3_clk_deps[L3_OPP_50_INDEX]; + else + l3_deps = &omap4_virt_l3_clk_deps[L3_OPP_100_INDEX]; + + omap4_clksel_set_rate(&dpll_core_m3x2_ck, l3_deps->core_m3_rate); + omap4_clksel_set_rate(&dpll_core_m6x2_ck, l3_deps->core_m6_rate); + omap4_clksel_set_rate(&dpll_core_m7x2_ck, l3_deps->core_m7_rate); + omap4_clksel_set_rate(&dpll_per_m3x2_ck, l3_deps->per_m3_rate); + omap4_clksel_set_rate(&dpll_per_m6x2_ck, l3_deps->per_m6_rate); + omap4_clksel_set_rate(&dpll_core_m5x2_ck, rate * 2); + + clk->rate = rate; + return 0; +} + int __init omap4xxx_clk_init(void) { struct omap_clk *c; diff --git a/arch/arm/mach-omap2/clockdomains44xx_data.c b/arch/arm/mach-omap2/clockdomains44xx_data.c index 6ac8fe2..8d1a061 100644 --- a/arch/arm/mach-omap2/clockdomains44xx_data.c +++ b/arch/arm/mach-omap2/clockdomains44xx_data.c @@ -605,7 +605,7 @@ static struct clockdomain iss_44xx_clkdm = { .clkdm_offs = OMAP4430_CM2_CAM_CAM_CDOFFS, .wkdep_srcs = iss_wkup_sleep_deps, .sleepdep_srcs = iss_wkup_sleep_deps, - .flags = CLKDM_CAN_HWSUP_SWSUP, + .flags = CLKDM_CAN_SWSUP, .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP44XX), }; diff --git a/arch/arm/mach-omap2/dvfs.c b/arch/arm/mach-omap2/dvfs.c index 5420eb9..8c82f2c 100644 --- a/arch/arm/mach-omap2/dvfs.c +++ b/arch/arm/mach-omap2/dvfs.c @@ -907,6 +907,7 @@ int __init omap_dvfs_register_device(struct device *dev, char *voltdm_name, struct omap_vdd_dev_list *temp_dev; struct omap_vdd_dvfs_info *dvfs_info; struct clk *clk = NULL; + struct voltagedomain *voltdm; int ret = 0; if (!voltdm_name) { @@ -921,7 +922,14 @@ int __init omap_dvfs_register_device(struct device *dev, char *voltdm_name, /* Lock me to secure structure changes */ mutex_lock(&omap_dvfs_lock); - dvfs_info = _dev_to_dvfs_info(dev); + voltdm = voltdm_lookup(voltdm_name); + if (!voltdm) { + dev_warn(dev, "%s: unable to find voltdm %s!\n", + __func__, voltdm_name); + ret = -EINVAL; + goto out; + } + dvfs_info = _voltdm_to_dvfs_info(voltdm); if (!dvfs_info) { dvfs_info = kzalloc(sizeof(struct omap_vdd_dvfs_info), GFP_KERNEL); @@ -931,14 +939,7 @@ int __init omap_dvfs_register_device(struct device *dev, char *voltdm_name, ret = -ENOMEM; goto out; } - dvfs_info->voltdm = voltdm_lookup(voltdm_name); - if (!dvfs_info->voltdm) { - dev_warn(dev, "%s: unable to find voltdm %s!\n", - __func__, voltdm_name); - kfree(dvfs_info); - ret = -EINVAL; - goto out; - } + dvfs_info->voltdm = voltdm; /* Init the plist */ spin_lock_init(&dvfs_info->user_lock); diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 085731c..94573ef 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -401,7 +401,8 @@ static int _enable_wakeup(struct omap_hwmod *oh, u32 *v) wakeup_mask = (0x1 << oh->class->sysc->sysc_fields->enwkup_shift); - *v |= wakeup_mask; + if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) + *v |= wakeup_mask; if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); @@ -436,7 +437,8 @@ static int _disable_wakeup(struct omap_hwmod *oh, u32 *v) wakeup_mask = (0x1 << oh->class->sysc->sysc_fields->enwkup_shift); - *v &= ~wakeup_mask; + if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) + *v &= ~wakeup_mask; if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART, v); @@ -1381,8 +1383,11 @@ static int _shutdown(struct omap_hwmod *oh) } } - if (oh->class->sysc) + if (oh->class->sysc) { + if (oh->_state == _HWMOD_STATE_IDLE) + _enable(oh); _shutdown_sysc(oh); + } /* * If an IP contains only one HW reset line, then assert it diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index e523072..dfdcc4c 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -50,6 +50,7 @@ static struct omap_hwmod omap44xx_dmm_hwmod; static struct omap_hwmod omap44xx_dsp_hwmod; static struct omap_hwmod omap44xx_dss_hwmod; static struct omap_hwmod omap44xx_emif_fw_hwmod; +static struct omap_hwmod omap44xx_fdif_hwmod; static struct omap_hwmod omap44xx_gpu_hwmod; static struct omap_hwmod omap44xx_hsi_hwmod; static struct omap_hwmod omap44xx_ipu_hwmod; @@ -353,6 +354,14 @@ static struct omap_hwmod_ocp_if omap44xx_iss__l3_main_2 = { .user = OCP_USER_MPU | OCP_USER_SDMA, }; +/* fdif -> l3_main_2 */ +static struct omap_hwmod_ocp_if omap44xx_fdif__l3_main_2 = { + .master = &omap44xx_fdif_hwmod, + .slave = &omap44xx_l3_main_2_hwmod, + .clk = "l3_div_ck", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + /* iva -> l3_main_2 */ static struct omap_hwmod_ocp_if omap44xx_iva__l3_main_2 = { .master = &omap44xx_iva_hwmod, @@ -401,6 +410,7 @@ static struct omap_hwmod_ocp_if *omap44xx_l3_main_2_slaves[] = { &omap44xx_hsi__l3_main_2, &omap44xx_ipu__l3_main_2, &omap44xx_iss__l3_main_2, + &omap44xx_fdif__l3_main_2, &omap44xx_iva__l3_main_2, &omap44xx_l3_main_1__l3_main_2, &omap44xx_gpu__l3_main_2, @@ -641,7 +651,6 @@ static struct omap_hwmod omap44xx_mpu_private_hwmod = { * elm * emif1 * emif2 - * fdif * gpmc * gpu * hdq1w @@ -885,7 +894,7 @@ static struct omap_hwmod omap44xx_smartreflex_mpu_hwmod = { static struct omap_hwmod_class_sysconfig omap44xx_aess_sysc = { .rev_offs = 0x0000, .sysc_offs = 0x0010, - .sysc_flags = (SYSC_HAS_MIDLEMODE | SYSC_HAS_SIDLEMODE), + .sysc_flags = 0, .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), .sysc_fields = &omap_hwmod_sysc_type2, @@ -2033,6 +2042,77 @@ static struct omap_hwmod omap44xx_dss_venc_hwmod = { }; /* + * 'fdif' class + * face detection hw accelerator module + */ + +static struct omap_hwmod_class_sysconfig omap44xx_fdif_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .sysc_flags = (SYSC_HAS_MIDLEMODE | SYSC_HAS_RESET_STATUS | + SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type2, +}; + +static struct omap_hwmod_class omap44xx_fdif_hwmod_class = { + .name = "fdif", + .sysc = &omap44xx_fdif_sysc, +}; + +/* fdif */ +static struct omap_hwmod_irq_info omap44xx_fdif_irqs[] = { + { .irq = 69 + OMAP44XX_IRQ_GIC_START }, +}; + +/* fdif master ports */ +static struct omap_hwmod_ocp_if *omap44xx_fdif_masters[] = { + &omap44xx_fdif__l3_main_2, +}; + +static struct omap_hwmod_addr_space omap44xx_fdif_addrs[] = { + { + .pa_start = 0x4a10a000, + .pa_end = 0x4a10a1ff, + .flags = ADDR_TYPE_RT + }, +}; + +/* l4_cfg -> fdif */ +static struct omap_hwmod_ocp_if omap44xx_l3_main_2__fdif = { + .master = &omap44xx_l3_main_2_hwmod, + .slave = &omap44xx_fdif_hwmod, + .clk = "l3_div_ck", + .addr = omap44xx_fdif_addrs, + .addr_cnt = ARRAY_SIZE(omap44xx_fdif_addrs), + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* fdif slave ports */ +static struct omap_hwmod_ocp_if *omap44xx_fdif_slaves[] = { + &omap44xx_l3_main_2__fdif, +}; + +static struct omap_hwmod omap44xx_fdif_hwmod = { + .name = "fdif", + .class = &omap44xx_fdif_hwmod_class, + .mpu_irqs = omap44xx_fdif_irqs, + .mpu_irqs_cnt = ARRAY_SIZE(omap44xx_fdif_irqs), + .main_clk = "fdif_fck", + .prcm = { + .omap4 = { + .clkctrl_reg = OMAP4430_CM_CAM_FDIF_CLKCTRL, + }, + }, + .slaves = omap44xx_fdif_slaves, + .slaves_cnt = ARRAY_SIZE(omap44xx_fdif_slaves), + .masters = omap44xx_fdif_masters, + .masters_cnt = ARRAY_SIZE(omap44xx_fdif_masters), + .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP44XX), +}; + +/* * 'gpio' class * general purpose io module */ @@ -5401,7 +5481,10 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = { &omap44xx_ipu_c1_hwmod, /* iss class */ -/* &omap44xx_iss_hwmod, */ + &omap44xx_iss_hwmod, + + /* fdif class */ + &omap44xx_fdif_hwmod, /* iva class */ &omap44xx_iva_hwmod, diff --git a/arch/arm/mach-omap2/omap_opp_data.h b/arch/arm/mach-omap2/omap_opp_data.h index 076bcd4..903310f 100644 --- a/arch/arm/mach-omap2/omap_opp_data.h +++ b/arch/arm/mach-omap2/omap_opp_data.h @@ -104,5 +104,7 @@ extern struct omap_volt_data omap446x_vdd_core_volt_data[]; extern struct omap_vdd_dep_info omap443x_vddmpu_dep_info[]; extern struct omap_vdd_dep_info omap443x_vddiva_dep_info[]; +extern struct omap_vdd_dep_info omap446x_vddmpu_dep_info[]; +extern struct omap_vdd_dep_info omap446x_vddiva_dep_info[]; #endif /* __ARCH_ARM_MACH_OMAP2_OMAP_OPP_DATA_H */ diff --git a/arch/arm/mach-omap2/opp.c b/arch/arm/mach-omap2/opp.c index 3f423bf..1952854 100644 --- a/arch/arm/mach-omap2/opp.c +++ b/arch/arm/mach-omap2/opp.c @@ -59,16 +59,16 @@ int __init omap_init_opp_table(struct omap_opp_def *opp_def, struct device *dev; if (!opp_def->hwmod_name) { - pr_err("%s: NULL name of omap_hwmod, failing [%d].\n", - __func__, i); - return -EINVAL; + WARN(1, "%s: NULL name of omap_hwmod, failing" + " [%d].\n", __func__, i); + goto next; } oh = omap_hwmod_lookup(opp_def->hwmod_name); if (!oh || !oh->od) { - pr_warn("%s: no hwmod or odev for %s, [%d] " + WARN(1, "%s: no hwmod or odev for %s, [%d] " "cannot add OPPs.\n", __func__, opp_def->hwmod_name, i); - return -EINVAL; + goto next; } dev = &oh->od->pdev.dev; @@ -93,6 +93,7 @@ int __init omap_init_opp_table(struct omap_opp_def *opp_def, dev_err(dev, "%s:%s:err dvfs register %d %d\n", __func__, opp_def->hwmod_name, r, i); } +next: opp_def++; } diff --git a/arch/arm/mach-omap2/opp4xxx_data.c b/arch/arm/mach-omap2/opp4xxx_data.c index 9083c7f..8d14e2f 100644 --- a/arch/arm/mach-omap2/opp4xxx_data.c +++ b/arch/arm/mach-omap2/opp4xxx_data.c @@ -116,16 +116,20 @@ static struct omap_opp_def __initdata omap443x_opp_def_list[] = { /* MPU OPP4 - OPP-SB */ OPP_INITIALIZER("mpu", "dpll_mpu_ck", "mpu", true, 1008000000, OMAP4430_VDD_MPU_OPPNITRO_UV), /* L3 OPP1 - OPP50 */ - OPP_INITIALIZER("l3_main_1", "dpll_core_m5x2_ck", "core", true, 100000000, OMAP4430_VDD_CORE_OPP50_UV), + OPP_INITIALIZER("l3_main_1", "virt_l3_ck", "core", true, 100000000, OMAP4430_VDD_CORE_OPP50_UV), /* L3 OPP2 - OPP100, OPP-Turbo, OPP-SB */ - OPP_INITIALIZER("l3_main_1", "dpll_core_m5x2_ck", "core", true, 200000000, OMAP4430_VDD_CORE_OPP100_UV), + OPP_INITIALIZER("l3_main_1", "virt_l3_ck", "core", true, 200000000, OMAP4430_VDD_CORE_OPP100_UV), /* IVA OPP1 - OPP50 */ OPP_INITIALIZER("iva", "dpll_iva_m5x2_ck", "iva", true, 133000000, OMAP4430_VDD_IVA_OPP50_UV), /* IVA OPP2 - OPP100 */ OPP_INITIALIZER("iva", "dpll_iva_m5x2_ck", "iva", true, 266100000, OMAP4430_VDD_IVA_OPP100_UV), /* IVA OPP3 - OPP-Turbo */ OPP_INITIALIZER("iva", "dpll_iva_m5x2_ck", "iva", false, 332000000, OMAP4430_VDD_IVA_OPPTURBO_UV), - /* TODO: add DSP, aess, fdif, gpu */ + /* SGX OPP1 - OPP50 */ + OPP_INITIALIZER("gpu", "dpll_per_m7x2_ck", "core", true, 153600000, OMAP4430_VDD_CORE_OPP50_UV), + /* SGX OPP2 - OPP100 */ + OPP_INITIALIZER("gpu", "dpll_per_m7x2_ck", "core", true, 307200000, OMAP4430_VDD_CORE_OPP50_UV), + /* TODO: add DSP, aess, fdif */ }; #define OMAP4460_VDD_MPU_OPP50_UV 1025000 @@ -165,6 +169,39 @@ struct omap_volt_data omap446x_vdd_core_volt_data[] = { VOLT_DATA_DEFINE(0, 0, 0, 0), }; +/* OMAP 4460 MPU Core VDD dependency table */ +static struct omap_vdd_dep_volt omap446x_vdd_mpu_core_dep_data[] = { + {.main_vdd_volt = OMAP4460_VDD_MPU_OPP50_UV, .dep_vdd_volt = OMAP4460_VDD_CORE_OPP50_UV}, + {.main_vdd_volt = OMAP4460_VDD_MPU_OPP100_UV, .dep_vdd_volt = OMAP4460_VDD_CORE_OPP100_UV}, + {.main_vdd_volt = OMAP4460_VDD_MPU_OPPTURBO_UV, .dep_vdd_volt = OMAP4460_VDD_CORE_OPP100_UV}, + {.main_vdd_volt = OMAP4460_VDD_MPU_OPPNITRO_UV, .dep_vdd_volt = OMAP4460_VDD_CORE_OPP100_UV}, +}; + +struct omap_vdd_dep_info omap446x_vddmpu_dep_info[] = { + { + .name = "core", + .dep_table = omap446x_vdd_mpu_core_dep_data, + .nr_dep_entries = ARRAY_SIZE(omap446x_vdd_mpu_core_dep_data), + }, + {.name = NULL, .dep_table = NULL, .nr_dep_entries = 0}, +}; + +/* OMAP 4460 MPU IVA VDD dependency table */ +static struct omap_vdd_dep_volt omap446x_vdd_iva_core_dep_data[] = { + {.main_vdd_volt = OMAP4460_VDD_IVA_OPP50_UV, .dep_vdd_volt = OMAP4460_VDD_CORE_OPP50_UV}, + {.main_vdd_volt = OMAP4460_VDD_IVA_OPP100_UV, .dep_vdd_volt = OMAP4460_VDD_CORE_OPP100_UV}, + {.main_vdd_volt = OMAP4460_VDD_IVA_OPPTURBO_UV, .dep_vdd_volt = OMAP4460_VDD_CORE_OPP100_UV}, +}; + +struct omap_vdd_dep_info omap446x_vddiva_dep_info[] = { + { + .name = "core", + .dep_table = omap446x_vdd_iva_core_dep_data, + .nr_dep_entries = ARRAY_SIZE(omap446x_vdd_iva_core_dep_data), + }, + {.name = NULL, .dep_table = NULL, .nr_dep_entries = 0}, +}; + static struct omap_opp_def __initdata omap446x_opp_def_list[] = { /* MPU OPP1 - OPP50 */ OPP_INITIALIZER("mpu", "virt_dpll_mpu_ck", "mpu", true, 350000000, OMAP4460_VDD_MPU_OPP50_UV), @@ -182,9 +219,9 @@ static struct omap_opp_def __initdata omap446x_opp_def_list[] = { /* MPU OPP4 - OPP-Nitro SpeedBin */ OPP_INITIALIZER("mpu", "virt_dpll_mpu_ck", "mpu", false, 1500000000, OMAP4460_VDD_MPU_OPPNITRO_UV), /* L3 OPP1 - OPP50 */ - OPP_INITIALIZER("l3_main_1", "dpll_core_m5x2_ck", "core", true, 100000000, OMAP4460_VDD_CORE_OPP50_UV), + OPP_INITIALIZER("l3_main_1", "virt_l3_ck", "core", true, 100000000, OMAP4460_VDD_CORE_OPP50_UV), /* L3 OPP2 - OPP100 */ - OPP_INITIALIZER("l3_main_1", "dpll_core_m5x2_ck", "core", true, 200000000, OMAP4460_VDD_CORE_OPP100_UV), + OPP_INITIALIZER("l3_main_1", "virt_l3_ck", "core", true, 200000000, OMAP4460_VDD_CORE_OPP100_UV), /* IVA OPP1 - OPP50 */ OPP_INITIALIZER("iva", "dpll_iva_m5x2_ck", "iva", true, 133000000, OMAP4460_VDD_IVA_OPP50_UV), /* IVA OPP2 - OPP100 */ @@ -201,7 +238,14 @@ static struct omap_opp_def __initdata omap446x_opp_def_list[] = { /* IVA OPP5 - OPP-Nitro SpeedBin*/ OPP_INITIALIZER("iva", "dpll_iva_m5x2_ck", "iva", false, 500000000, OMAP4460_VDD_IVA_OPPNITRO_UV), - /* TODO: add DSP, aess, fdif, gpu */ + /* SGX OPP1 - OPP50 */ + OPP_INITIALIZER("gpu", "dpll_per_m7x2_ck", "core", true, 153600000, OMAP4460_VDD_CORE_OPP50_UV), + /* SGX OPP2 - OPP100 */ + OPP_INITIALIZER("gpu", "dpll_per_m7x2_ck", "core", true, 307200000, OMAP4460_VDD_CORE_OPP100_UV), + /* SGX OPP3 - OPPOV */ + OPP_INITIALIZER("gpu", "dpll_per_m7x2_ck", "core", true, 384000000, OMAP4460_VDD_CORE_OPP100_OV_UV), + + /* TODO: add DSP, aess, fdif */ }; /** diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 4dde946..744431c 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -294,11 +294,13 @@ static void __init omap4_init_voltages(void) if (!cpu_is_omap44xx()) return; - if (cpu_is_omap446x()) + if (cpu_is_omap446x()) { omap2_set_init_voltage("mpu", "virt_dpll_mpu_ck", mpu_dev); - else + omap2_set_init_voltage("core", "virt_l3_ck", l3_dev); + } else { omap2_set_init_voltage("mpu", "dpll_mpu_ck", mpu_dev); - omap2_set_init_voltage("core", "l3_div_ck", l3_dev); + omap2_set_init_voltage("core", "l3_div_ck", l3_dev); + } omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", iva_dev); } diff --git a/arch/arm/mach-omap2/voltagedomains44xx_data.c b/arch/arm/mach-omap2/voltagedomains44xx_data.c index b3500e0..5fce5ad 100644 --- a/arch/arm/mach-omap2/voltagedomains44xx_data.c +++ b/arch/arm/mach-omap2/voltagedomains44xx_data.c @@ -114,18 +114,14 @@ void __init omap44xx_voltagedomains_init(void) omap4_vdd_mpu_info.volt_data = omap443x_vdd_mpu_volt_data; omap4_vdd_iva_info.volt_data = omap443x_vdd_iva_volt_data; omap4_vdd_core_info.volt_data = omap443x_vdd_core_volt_data; -#if 0 - /* - * TODO: XXX: we *should* fix O4 core domain clks before doing - * this - already tested dvfs logic.. - */ omap4_vdd_mpu_info.dep_vdd_info = omap443x_vddmpu_dep_info; omap4_vdd_iva_info.dep_vdd_info = omap443x_vddiva_dep_info; -#endif } else if (cpu_is_omap446x()) { omap4_vdd_mpu_info.volt_data = omap446x_vdd_mpu_volt_data; omap4_vdd_iva_info.volt_data = omap446x_vdd_iva_volt_data; omap4_vdd_core_info.volt_data = omap446x_vdd_core_volt_data; + omap4_vdd_mpu_info.dep_vdd_info = omap446x_vddmpu_dep_info; + omap4_vdd_iva_info.dep_vdd_info = omap446x_vddiva_dep_info; } else { return; } diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index e2d8843..f3ab483 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -31,5 +31,5 @@ obj-y += $(i2c-omap-m) $(i2c-omap-y) # OMAP mailbox framework obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o -obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o -obj-$(CONFIG_OMAP_PM) += omap-pm.o +obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-interface.o +obj-$(CONFIG_OMAP_PM) += omap-pm-interface.o omap-pm-helper.o diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c index 5e76d49..f8dc82f 100644 --- a/arch/arm/plat-omap/i2c.c +++ b/arch/arm/plat-omap/i2c.c @@ -113,17 +113,6 @@ static inline int omap1_i2c_add_bus(int bus_id) #ifdef CONFIG_ARCH_OMAP2PLUS -/* - * XXX This function is a temporary compatibility wrapper - only - * needed until the I2C driver can be converted to call - * omap_pm_set_max_dev_wakeup_lat() and handle a return code. - */ -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t) -{ - static struct pm_qos_request_list *qos_request; - omap_pm_set_max_mpu_wakeup_lat(&qos_request, t); -} - static struct omap_device_pm_latency omap_i2c_latency[] = { [0] = { .deactivate_func = omap_device_idle_hwmods, @@ -160,7 +149,7 @@ static inline int omap2_i2c_add_bus(int bus_id) * Only omap3 has support for constraints */ if (cpu_is_omap34xx() || cpu_is_omap44xx()) - pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat; + pdata->needs_wakeup_latency = true; od = omap_device_build(name, bus_id, oh, pdata, sizeof(struct omap_i2c_bus_platform_data), omap_i2c_latency, ARRAY_SIZE(omap_i2c_latency), 0); diff --git a/arch/arm/plat-omap/omap-pm-helper.c b/arch/arm/plat-omap/omap-pm-helper.c new file mode 100644 index 0000000..0f71c45 --- /dev/null +++ b/arch/arm/plat-omap/omap-pm-helper.c @@ -0,0 +1,321 @@ +/* + * omap-pm.c - OMAP power management interface + * + * Copyright (C) 2008-2011 Texas Instruments, Inc. + * Copyright (C) 2008-2009 Nokia Corporation + * Vishwanath BS + * + * This code is based on plat-omap/omap-pm-noop.c. + * + * Interface developed by (in alphabetical order): + * Karthik Dasu, Tony Lindgren, Rajendra Nayak, Sakari Poussa, Veeramanikandan + * Raju, Anand Sawant, Igor Stoppa, Paul Walmsley, Richard Woodruff + */ + +#undef DEBUG + +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/list.h> + +/* Interface documentation is in mach/omap-pm.h */ +#include <plat/omap-pm.h> +#include <plat/omap_device.h> +#include <plat/common.h> +#include "../mach-omap2/powerdomain.h" +#include "omap-pm-helper.h" + +struct omap_opp *dsp_opps; +struct omap_opp *mpu_opps; +struct omap_opp *l3_opps; + +static DEFINE_MUTEX(bus_tput_mutex); +static DEFINE_MUTEX(mpu_tput_mutex); +static DEFINE_MUTEX(mpu_lat_mutex); + +/* Used to model a Interconnect Throughput */ +static struct interconnect_tput { + /* Total no of users at any point of interconnect */ + u8 no_of_users; + /* List of all the current users for interconnect */ + struct list_head users_list; + struct list_head node; + /* Protect interconnect throughput */ + struct mutex throughput_mutex; + /* Target level for interconnect throughput */ + unsigned long target_level; + +} *bus_tput; + +/* Used to represent a user of a interconnect throughput */ +struct users { + /* Device pointer used to uniquely identify the user */ + struct device *dev; + struct list_head node; + /* Current level as requested for interconnect throughput by the user */ + u32 level; +}; + +/* Private/Internal Functions */ + +/** + * user_lookup - look up a user by its device pointer, return a pointer + * @dev: The device to be looked up + * + * Looks for a interconnect user by its device pointer. Returns a + * pointer to + * the struct users if found, else returns NULL. + */ +static struct users *user_lookup(struct device *dev) +{ + struct users *usr, *tmp_usr; + + usr = NULL; + list_for_each_entry(tmp_usr, &bus_tput->users_list, node) { + if (tmp_usr->dev == dev) { + usr = tmp_usr; + break; + } + } + + return usr; +} + +/** + * get_user - gets a new users_list struct dynamically + * + * This function allocates dynamcially the user node + * Returns a pointer to users struct on success. On dynamic allocation + * failure + * returns a ERR_PTR(-ENOMEM). + */ +static struct users *get_user(void) +{ + struct users *user; + + user = kmalloc(sizeof(struct users), GFP_KERNEL); + if (!user) { + pr_err("%s FATAL ERROR: kmalloc failed\n", __func__); + return ERR_PTR(-ENOMEM); + } + return user; +} + +/** + * omap_bus_tput_init - Initializes the interconnect throughput + * userlist + * Allocates memory for global throughput variable dynamically. + * Intializes Userlist, no. of users and throughput target level. + * Returns 0 on sucess, else returns EINVAL if memory + * allocation fails. + */ +static int __init omap_bus_tput_init(void) +{ + bus_tput = kmalloc(sizeof(struct interconnect_tput), GFP_KERNEL); + if (!bus_tput) { + pr_err("%s FATAL ERROR: kmalloc failed\n", __func__); + return -EINVAL; + } + INIT_LIST_HEAD(&bus_tput->users_list); + mutex_init(&bus_tput->throughput_mutex); + bus_tput->no_of_users = 0; + bus_tput->target_level = 0; + return 0; +} + +/** + * add_req_tput - Request for a required level by a device + * @dev: Uniquely identifes the caller + * @level: The requested level for the interconnect bandwidth in KiB/s + * + * This function recomputes the target level of the interconnect + * bandwidth + * based on the level requested by all the users. + * Multiple calls to this function by the same device will + * replace the previous level requested + * Returns the updated level of interconnect throughput. + * In case of Invalid dev or user pointer, it returns 0. + */ +static unsigned long add_req_tput(struct device *dev, unsigned long level) +{ + int ret; + struct users *user; + + if (!dev) { + pr_err("Invalid dev pointer\n"); + ret = 0; + } + mutex_lock(&bus_tput->throughput_mutex); + user = user_lookup(dev); + if (user == NULL) { + user = get_user(); + if (IS_ERR(user)) { + pr_err("Couldn't get user from the list to" + "add new throughput constraint"); + ret = 0; + goto unlock; + } + bus_tput->target_level += level; + bus_tput->no_of_users++; + user->dev = dev; + list_add(&user->node, &bus_tput->users_list); + user->level = level; + } else { + bus_tput->target_level -= user->level; + bus_tput->target_level += level; + user->level = level; + } + ret = bus_tput->target_level; +unlock: + mutex_unlock(&bus_tput->throughput_mutex); + return ret; +} + +/** + * remove_req_tput - Release a previously requested level of + * a throughput level for interconnect + * @dev: Device pointer to dev + * + * This function recomputes the target level of the interconnect + * throughput after removing + * the level requested by the user. + * Returns 0, if the dev structure is invalid + * else returns modified interconnect throughput rate. + */ +static unsigned long remove_req_tput(struct device *dev) +{ + struct users *user; + int found = 0; + int ret; + + mutex_lock(&bus_tput->throughput_mutex); + list_for_each_entry(user, &bus_tput->users_list, node) { + if (user->dev == dev) { + found = 1; + break; + } + } + if (!found) { + /* No such user exists */ + pr_err("Invalid Device Structure\n"); + ret = 0; + goto unlock; + } + bus_tput->target_level -= user->level; + bus_tput->no_of_users--; + list_del(&user->node); + kfree(user); + ret = bus_tput->target_level; +unlock: + mutex_unlock(&bus_tput->throughput_mutex); + return ret; +} + +int omap_pm_set_min_bus_tput_helper(struct device *dev, u8 agent_id, long r) +{ + + int ret = 0; + struct device *l3_dev; + + /* + * TODO: This will go away when complete device scaling support for + * L3 is added. + */ +#if 0 + static struct device dummy_l3_dev; +#endif + unsigned long target_level = 0; + + mutex_lock(&bus_tput_mutex); + + l3_dev = omap2_get_l3_device(); + if (!l3_dev) { + pr_err("Unable to get l3 device pointer"); + ret = -EINVAL; + goto unlock; + } + + if (r == -1) + target_level = remove_req_tput(dev); + else + target_level = add_req_tput(dev, r); + + /* Convert the throughput(in KiB/s) into Hz. */ + target_level = (target_level * 1000) / 4; + + /* + * TODO: This will go away when complete device scaling support for + * L3 is added. + */ +#if 0 + ret = omap_device_scale(&dummy_l3_dev, l3_dev, target_level); + if (ret) + pr_err("Failed: change interconnect bandwidth to %ld\n", + target_level); +#else + WARN(1, "OMAP PM: %s: constraint not called, needs DVFS", __func__); +#endif +unlock: + mutex_unlock(&bus_tput_mutex); + return ret; +} + +int omap_pm_set_max_dev_wakeup_lat_helper(struct device *req_dev, + struct device *dev, long t) +{ + struct omap_device *odev; + struct powerdomain *pwrdm_dev; + struct platform_device *pdev; + int ret = 0; + + if (!req_dev || !dev || t < -1) { + WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); + return -EINVAL; + }; + + /* Look for the devices Power Domain */ + pdev = container_of(dev, struct platform_device, dev); + + /* Try to catch non platform devices. */ + if (pdev->name == NULL) { + pr_err("OMAP-PM: Error: platform device not valid\n"); + return -EINVAL; + } + + odev = to_omap_device(pdev); + if (odev) { + pwrdm_dev = omap_device_get_pwrdm(odev); + } else { + pr_err("OMAP-PM: Error: Could not find omap_device for %s\n", + pdev->name); + return -EINVAL; + } + + /* Catch devices with undefined powerdomains. */ + if (!pwrdm_dev) { + pr_err("OMAP-PM: Error: could not find parent pwrdm for %s\n", + pdev->name); + return -EINVAL; + } + + if (t == -1) + ret = pwrdm_wakeuplat_release_constraint(pwrdm_dev, req_dev); + else + ret = pwrdm_wakeuplat_set_constraint(pwrdm_dev, req_dev, t); + + return ret; +} + +/* Must be called after clock framework is initialized */ +int __init omap_pm_if_init_helper(void) +{ + int ret; + ret = omap_bus_tput_init(); + if (ret) + pr_err("Failed: init of interconnect bandwidth users list\n"); + return ret; +} diff --git a/arch/arm/plat-omap/omap-pm-helper.h b/arch/arm/plat-omap/omap-pm-helper.h new file mode 100644 index 0000000..9c4b5d7 --- /dev/null +++ b/arch/arm/plat-omap/omap-pm-helper.h @@ -0,0 +1,40 @@ +/* + * OMAP PM interface helpers + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Nishanth Menon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __OMAP_PM_HELPER_INTERFACE_H__ +#define __OMAP_PM_HELPER_INTERFACE_H__ + +#ifdef CONFIG_OMAP_PM +int omap_pm_set_min_bus_tput_helper(struct device *dev, u8 agent_id, long r); +int omap_pm_set_max_dev_wakeup_lat_helper(struct device *req_dev, + struct device *dev, long t); +int __init omap_pm_if_init_helper(void); + +#else +static inline int omap_pm_set_min_bus_tput_helper(struct device *dev, + u8 agent_id, long r) +{ + return 0; +} + +static inline int omap_pm_set_max_dev_wakeup_lat_helper(struct device *req_dev, + struct device *dev, long t) +{ + return 0; +} + +static inline int omap_pm_if_init_helper(void) +{ + return 0; +} +#endif /* CONFIG_OMAP_PM */ + +#endif /* __OMAP_PM_HELPER_INTERFACE_H__ */ diff --git a/arch/arm/plat-omap/omap-pm-interface.c b/arch/arm/plat-omap/omap-pm-interface.c new file mode 100644 index 0000000..67319a7 --- /dev/null +++ b/arch/arm/plat-omap/omap-pm-interface.c @@ -0,0 +1,239 @@ +/* + * omap-pm-interface.c - OMAP power management interface + * + * This code implements the OMAP power management interface to + * drivers, CPUIdle, CPUFreq, and DSP Bridge. + * + * Copyright (C) 2008-2011 Texas Instruments, Inc. + * Copyright (C) 2008-2009 Nokia Corporation + * Paul Walmsley + * + * Interface developed by (in alphabetical order): + * Karthik Dasu, Tony Lindgren, Rajendra Nayak, Sakari Poussa, Veeramanikandan + * Raju, Anand Sawant, Igor Stoppa, Paul Walmsley, Richard Woodruff + */ + +#undef DEBUG + +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/device.h> +#include <linux/platform_device.h> + +/* Interface documentation is in mach/omap-pm.h */ +#include <plat/omap-pm.h> +#include <plat/omap_device.h> + +#include "omap-pm-helper.h" + +static bool off_mode_enabled; + +/* + * Device-driver-originated constraints (via board-*.c files) + * WARNING: Device drivers need to now use pm_qos directly. + */ +int omap_pm_set_max_mpu_wakeup_lat(struct pm_qos_request_list **pmqos_req, + long t) +{ + WARN(1, "Deprecated %s: Driver should use pm_qos to add request\n", + __func__); + + return -EINVAL; +} + +int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, long r) +{ + int ret; + if (!dev || (agent_id != OCP_INITIATOR_AGENT && + agent_id != OCP_TARGET_AGENT)) { + WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); + return -EINVAL; + }; + + if (r == -1) + pr_debug("OMAP PM: remove min bus tput constraint: " + "dev %s for agent_id %d\n", dev_name(dev), agent_id); + else + pr_debug("OMAP PM: add min bus tput constraint: " + "dev %s for agent_id %d: rate %ld KiB\n", + dev_name(dev), agent_id, r); + + ret = omap_pm_set_min_bus_tput_helper(dev, agent_id, r); + + return ret; +} + +int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev, + long t) +{ + int ret; + if (!req_dev || !dev || t < -1) { + WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); + return -EINVAL; + }; + + if (t == -1) + pr_debug("OMAP PM: remove max device latency constraint: " + "dev %s\n", dev_name(dev)); + else + pr_debug("OMAP PM: add max device latency constraint: " + "dev %s, t = %ld usec\n", dev_name(dev), t); + + ret = omap_pm_set_max_dev_wakeup_lat_helper(req_dev, dev, t); + + return ret; +} + +/* WARNING: Device drivers need to now use pm_qos directly. */ +int omap_pm_set_max_sdma_lat(struct pm_qos_request_list **qos_request, long t) +{ + WARN(1, "Deprecated %s: Driver should use pm_qos to add request\n", + __func__); + + return -EINVAL; +} + +int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r) +{ + WARN(1, "Deprecated %s: Driver should use omap_device_scale/opp\n", + __func__); + + return -EINVAL; +} + +/* + * DSP Bridge-specific constraints + * WARNING: Device drivers need to now use opp layer/omap_device_scale directly. + */ +const struct omap_opp *omap_pm_dsp_get_opp_table(void) +{ + WARN(1, "Deprecated %s: Driver should use omap_device_scale/opp\n", + __func__); + + return ERR_PTR(-EINVAL); +} + +void omap_pm_dsp_set_min_opp(u8 opp_id) +{ + WARN(1, "Deprecated %s: Driver should use omap_device_scale/opp\n", + __func__); + + return; +} + +int omap_pm_set_min_mpu_freq(struct device *dev, unsigned long f) +{ + WARN(1, "Deprecated %s: Driver should NOT use this function\n", + __func__); + + return -EINVAL; + +} + +EXPORT_SYMBOL(omap_pm_set_min_mpu_freq); + +u8 omap_pm_dsp_get_opp(void) +{ + WARN(1, "Deprecated %s: Driver should use omap_device_scale/opp\n", + __func__); + + return 0; +} + +/* + * CPUFreq-originated constraint + * + * In the future, this should be handled by custom OPP clocktype + * functions. + */ + +struct cpufreq_frequency_table **omap_pm_cpu_get_freq_table(void) +{ + WARN(1, "Deprecated %s: Driver should use omap_device_scale/opp\n", + __func__); + + return ERR_PTR(-EINVAL); +} + +void omap_pm_cpu_set_freq(unsigned long f) +{ + WARN(1, "Deprecated %s: Driver should use omap_device_scale/opp\n", + __func__); + + return; +} + +unsigned long omap_pm_cpu_get_freq(void) +{ + WARN(1, "Deprecated %s: Driver should use omap_device_scale/opp\n", + __func__); + + return 0; +} + +/** + * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled + * + * Intended for use only by OMAP PM core code to notify this layer + * that off mode has been enabled. + */ +void omap_pm_enable_off_mode(void) +{ + off_mode_enabled = true; +} + +/** + * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled + * + * Intended for use only by OMAP PM core code to notify this layer + * that off mode has been disabled. + */ +void omap_pm_disable_off_mode(void) +{ + off_mode_enabled = false; +} + +/* + * Device context loss tracking + * WARNING: at this point we dont have a reliable context loss reporting + * mechanism. Instead, we ensure that we report context loss always. + */ +int omap_pm_get_dev_context_loss_count(struct device *dev) +{ + static u32 count = 1; + + if (!dev) { + WARN_ON(1); + return -EINVAL; + }; + + count++; + + /* + * Context loss count has to be a non-negative value. + * Clear the sign bit to get a value range from 0 to + * INT_MAX. Roll over to 1 + */ + count = (count & ~INT_MAX) ? 1 : count; + + pr_debug("OMAP PM: returning context loss count for dev %s count %ul\n", + dev_name(dev), count); + return count; +} + +/* Should be called before clk framework init */ +int __init omap_pm_if_early_init(void) +{ + return 0; +} + +/* Must be called after clock framework is initialized */ +int __init omap_pm_if_init(void) +{ + return omap_pm_if_init_helper(); +} + +void omap_pm_if_exit(void) +{ + /* Deallocate CPUFreq frequency table here */ +} diff --git a/arch/arm/plat-omap/omap-pm-noop.c b/arch/arm/plat-omap/omap-pm-noop.c deleted file mode 100644 index 348f0cc..0000000 --- a/arch/arm/plat-omap/omap-pm-noop.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * omap-pm-noop.c - OMAP power management interface - dummy version - * - * This code implements the OMAP power management interface to - * drivers, CPUIdle, CPUFreq, and DSP Bridge. It is strictly for - * debug/demonstration use, as it does nothing but printk() whenever a - * function is called (when DEBUG is defined, below) - * - * Copyright (C) 2008-2009 Texas Instruments, Inc. - * Copyright (C) 2008-2009 Nokia Corporation - * Paul Walmsley - * - * Interface developed by (in alphabetical order): - * Karthik Dasu, Tony Lindgren, Rajendra Nayak, Sakari Poussa, Veeramanikandan - * Raju, Anand Sawant, Igor Stoppa, Paul Walmsley, Richard Woodruff - */ - -#undef DEBUG - -#include <linux/init.h> -#include <linux/cpufreq.h> -#include <linux/device.h> -#include <linux/platform_device.h> - -/* Interface documentation is in mach/omap-pm.h */ -#include <plat/omap-pm.h> -#include <plat/omap_device.h> - -static bool off_mode_enabled; -static int dummy_context_loss_counter; - -/* - * Device-driver-originated constraints (via board-*.c files) - */ - -int omap_pm_set_max_mpu_wakeup_lat(struct pm_qos_request_list **pmqos_req, - long t) -{ - if (!pmqos_req || t < -1) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - }; - - if (t == -1) - pr_debug("OMAP PM: remove max MPU wakeup latency constraint\n"); - else - pr_debug("OMAP PM: add max MPU wakeup latency constraint:" - "t = %ld usec\n", t); - - /* - * For current Linux, this needs to map the MPU to a - * powerdomain, then go through the list of current max lat - * constraints on the MPU and find the smallest. If - * the latency constraint has changed, the code should - * recompute the state to enter for the next powerdomain - * state. - * - * TI CDP code can call constraint_set here. - */ - - return 0; -} - -int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, long r) -{ - if (!dev || (agent_id != OCP_INITIATOR_AGENT && - agent_id != OCP_TARGET_AGENT)) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - }; - - if (r == 0) - pr_debug("OMAP PM: remove min bus tput constraint: " - "dev %s for agent_id %d\n", dev_name(dev), agent_id); - else - pr_debug("OMAP PM: add min bus tput constraint: " - "dev %s for agent_id %d: rate %ld KiB\n", - dev_name(dev), agent_id, r); - - /* - * This code should model the interconnect and compute the - * required clock frequency, convert that to a VDD2 OPP ID, then - * set the VDD2 OPP appropriately. - * - * TI CDP code can call constraint_set here on the VDD2 OPP. - */ - - return 0; -} - -int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev, - long t) -{ - if (!req_dev || !dev || t < -1) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - }; - - if (t == -1) - pr_debug("OMAP PM: remove max device latency constraint: " - "dev %s\n", dev_name(dev)); - else - pr_debug("OMAP PM: add max device latency constraint: " - "dev %s, t = %ld usec\n", dev_name(dev), t); - - /* - * For current Linux, this needs to map the device to a - * powerdomain, then go through the list of current max lat - * constraints on that powerdomain and find the smallest. If - * the latency constraint has changed, the code should - * recompute the state to enter for the next powerdomain - * state. Conceivably, this code should also determine - * whether to actually disable the device clocks or not, - * depending on how long it takes to re-enable the clocks. - * - * TI CDP code can call constraint_set here. - */ - - return 0; -} - -int omap_pm_set_max_sdma_lat(struct pm_qos_request_list **qos_request, long t) -{ - if (!qos_request || t < -1) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - }; - - if (t == -1) - pr_debug("OMAP PM: remove max DMA latency constraint:\n"); - else - pr_debug("OMAP PM: add max DMA latency constraint:" - "t = %ld usec\n", t); - - /* - * For current Linux PM QOS params, this code should scan the - * list of maximum CPU and DMA latencies and select the - * smallest, then set cpu_dma_latency pm_qos_param - * accordingly. - * - * For future Linux PM QOS params, with separate CPU and DMA - * latency params, this code should just set the dma_latency param. - * - * TI CDP code can call constraint_set here. - */ - - return 0; -} - -int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r) -{ - if (!dev || !c || r < 0) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - } - - if (r == 0) - pr_debug("OMAP PM: remove min clk rate constraint: " - "dev %s\n", dev_name(dev)); - else - pr_debug("OMAP PM: add min clk rate constraint: " - "dev %s, rate = %ld Hz\n", dev_name(dev), r); - - /* - * Code in a real implementation should keep track of these - * constraints on the clock, and determine the highest minimum - * clock rate. It should iterate over each OPP and determine - * whether the OPP will result in a clock rate that would - * satisfy this constraint (and any other PM constraint in effect - * at that time). Once it finds the lowest-voltage OPP that - * meets those conditions, it should switch to it, or return - * an error if the code is not capable of doing so. - */ - - return 0; -} - -/* - * DSP Bridge-specific constraints - */ - -const struct omap_opp *omap_pm_dsp_get_opp_table(void) -{ - pr_debug("OMAP PM: DSP request for OPP table\n"); - - /* - * Return DSP frequency table here: The final item in the - * array should have .rate = .opp_id = 0. - */ - - return NULL; -} - -void omap_pm_dsp_set_min_opp(u8 opp_id) -{ - if (opp_id == 0) { - WARN_ON(1); - return; - } - - pr_debug("OMAP PM: DSP requests minimum VDD1 OPP to be %d\n", opp_id); - - /* - * - * For l-o dev tree, our VDD1 clk is keyed on OPP ID, so we - * can just test to see which is higher, the CPU's desired OPP - * ID or the DSP's desired OPP ID, and use whichever is - * highest. - * - * In CDP12.14+, the VDD1 OPP custom clock that controls the DSP - * rate is keyed on MPU speed, not the OPP ID. So we need to - * map the OPP ID to the MPU speed for use with clk_set_rate() - * if it is higher than the current OPP clock rate. - * - */ -} - - -u8 omap_pm_dsp_get_opp(void) -{ - pr_debug("OMAP PM: DSP requests current DSP OPP ID\n"); - - /* - * For l-o dev tree, call clk_get_rate() on VDD1 OPP clock - * - * CDP12.14+: - * Call clk_get_rate() on the OPP custom clock, map that to an - * OPP ID using the tables defined in board-*.c/chip-*.c files. - */ - - return 0; -} - -/* - * CPUFreq-originated constraint - * - * In the future, this should be handled by custom OPP clocktype - * functions. - */ - -struct cpufreq_frequency_table **omap_pm_cpu_get_freq_table(void) -{ - pr_debug("OMAP PM: CPUFreq request for frequency table\n"); - - /* - * Return CPUFreq frequency table here: loop over - * all VDD1 clkrates, pull out the mpu_ck frequencies, build - * table - */ - - return NULL; -} - -void omap_pm_cpu_set_freq(unsigned long f) -{ - if (f == 0) { - WARN_ON(1); - return; - } - - pr_debug("OMAP PM: CPUFreq requests CPU frequency to be set to %lu\n", - f); - - /* - * For l-o dev tree, determine whether MPU freq or DSP OPP id - * freq is higher. Find the OPP ID corresponding to the - * higher frequency. Call clk_round_rate() and clk_set_rate() - * on the OPP custom clock. - * - * CDP should just be able to set the VDD1 OPP clock rate here. - */ -} - -unsigned long omap_pm_cpu_get_freq(void) -{ - pr_debug("OMAP PM: CPUFreq requests current CPU frequency\n"); - - /* - * Call clk_get_rate() on the mpu_ck. - */ - - return 0; -} - -/** - * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled - * - * Intended for use only by OMAP PM core code to notify this layer - * that off mode has been enabled. - */ -void omap_pm_enable_off_mode(void) -{ - off_mode_enabled = true; -} - -/** - * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled - * - * Intended for use only by OMAP PM core code to notify this layer - * that off mode has been disabled. - */ -void omap_pm_disable_off_mode(void) -{ - off_mode_enabled = false; -} - -/* - * Device context loss tracking - */ - -#ifdef CONFIG_ARCH_OMAP2PLUS - -int omap_pm_get_dev_context_loss_count(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - int count; - - if (WARN_ON(!dev)) - return -ENODEV; - - if (dev->parent == &omap_device_parent) { - count = omap_device_get_context_loss_count(pdev); - } else { - WARN_ONCE(off_mode_enabled, "omap_pm: using dummy context loss counter; device %s should be converted to omap_device", - dev_name(dev)); - - count = dummy_context_loss_counter; - - if (off_mode_enabled) { - count++; - /* - * Context loss count has to be a non-negative value. - * Clear the sign bit to get a value range from 0 to - * INT_MAX. - */ - count &= INT_MAX; - dummy_context_loss_counter = count; - } - } - - pr_debug("OMAP PM: context loss count for dev %s = %d\n", - dev_name(dev), count); - - return count; -} - -#else - -int omap_pm_get_dev_context_loss_count(struct device *dev) -{ - return dummy_context_loss_counter; -} - -#endif - -/* Should be called before clk framework init */ -int __init omap_pm_if_early_init(void) -{ - return 0; -} - -/* Must be called after clock framework is initialized */ -int __init omap_pm_if_init(void) -{ - return 0; -} - -void omap_pm_if_exit(void) -{ - /* Deallocate CPUFreq frequency table here */ -} - diff --git a/arch/arm/plat-omap/omap-pm.c b/arch/arm/plat-omap/omap-pm.c deleted file mode 100644 index a2ee551..0000000 --- a/arch/arm/plat-omap/omap-pm.c +++ /dev/null @@ -1,630 +0,0 @@ -/* - * omap-pm.c - OMAP power management interface - * - * Copyright (C) 2008-2010 Texas Instruments, Inc. - * Copyright (C) 2008-2009 Nokia Corporation - * Vishwanath BS - * - * This code is based on plat-omap/omap-pm-noop.c. - * - * Interface developed by (in alphabetical order): - * Karthik Dasu, Tony Lindgren, Rajendra Nayak, Sakari Poussa, Veeramanikandan - * Raju, Anand Sawant, Igor Stoppa, Paul Walmsley, Richard Woodruff - */ - -#undef DEBUG - -#include <linux/init.h> -#include <linux/cpufreq.h> -#include <linux/device.h> -#include <linux/errno.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/list.h> - -/* Interface documentation is in mach/omap-pm.h */ -#include <plat/omap-pm.h> -#include <plat/omap_device.h> -#include <plat/common.h> -#include "../mach-omap2/powerdomain.h" - -struct omap_opp *dsp_opps; -struct omap_opp *mpu_opps; -struct omap_opp *l3_opps; - -static DEFINE_MUTEX(bus_tput_mutex); -static DEFINE_MUTEX(mpu_tput_mutex); -static DEFINE_MUTEX(mpu_lat_mutex); - -static bool off_mode_enabled; - -/* Used to model a Interconnect Throughput */ -static struct interconnect_tput { - /* Total no of users at any point of interconnect */ - u8 no_of_users; - /* List of all the current users for interconnect */ - struct list_head users_list; - struct list_head node; - /* Protect interconnect throughput */ - struct mutex throughput_mutex; - /* Target level for interconnect throughput */ - unsigned long target_level; - -} *bus_tput; - -/* Used to represent a user of a interconnect throughput */ -struct users { - /* Device pointer used to uniquely identify the user */ - struct device *dev; - struct list_head node; - /* Current level as requested for interconnect throughput by the user */ - u32 level; -}; - -/* Private/Internal Functions */ - -/** - * user_lookup - look up a user by its device pointer, return a pointer - * @dev: The device to be looked up - * - * Looks for a interconnect user by its device pointer. Returns a - * pointer to - * the struct users if found, else returns NULL. - **/ - -static struct users *user_lookup(struct device *dev) -{ - struct users *usr, *tmp_usr; - - usr = NULL; - list_for_each_entry(tmp_usr, &bus_tput->users_list, node) { - if (tmp_usr->dev == dev) { - usr = tmp_usr; - break; - } - } - - return usr; -} - -/** - * get_user - gets a new users_list struct dynamically - * - * This function allocates dynamcially the user node - * Returns a pointer to users struct on success. On dynamic allocation - * failure - * returns a ERR_PTR(-ENOMEM). - **/ - -static struct users *get_user(void) -{ - struct users *user; - - user = kmalloc(sizeof(struct users), GFP_KERNEL); - if (!user) { - pr_err("%s FATAL ERROR: kmalloc " - "failed\n", __func__); - return ERR_PTR(-ENOMEM); - } - return user; -} - - -/** - * omap_bus_tput_init - Initializes the interconnect throughput - * userlist - * Allocates memory for global throughput variable dynamically. - * Intializes Userlist, no. of users and throughput target level. - * Returns 0 on sucess, else returns EINVAL if memory - * allocation fails. - */ -int omap_bus_tput_init(void) -{ - bus_tput = kmalloc(sizeof(struct interconnect_tput), GFP_KERNEL); - if (!bus_tput) { - pr_err("%s FATAL ERROR: kmalloc failed\n", __func__); - return -EINVAL; - } - INIT_LIST_HEAD(&bus_tput->users_list); - mutex_init(&bus_tput->throughput_mutex); - bus_tput->no_of_users = 0; - bus_tput->target_level = 0; - return 0; -} - - -/** - * add_req_tput - Request for a required level by a device - * @dev: Uniquely identifes the caller - * @level: The requested level for the interconnect bandwidth in KiB/s - * - * This function recomputes the target level of the interconnect - * bandwidth - * based on the level requested by all the users. - * Multiple calls to this function by the same device will - * replace the previous level requested - * Returns the updated level of interconnect throughput. - * In case of Invalid dev or user pointer, it returns 0. - */ -static unsigned long add_req_tput(struct device *dev, unsigned long level) -{ - int ret; - struct users *user; - - if (!dev) { - pr_err("Invalid dev pointer\n"); - ret = 0; - } - mutex_lock(&bus_tput->throughput_mutex); - user = user_lookup(dev); - if (user == NULL) { - user = get_user(); - if (IS_ERR(user)) { - pr_err("Couldn't get user from the list to" - "add new throughput constraint"); - ret = 0; - goto unlock; - } - bus_tput->target_level += level; - bus_tput->no_of_users++; - user->dev = dev; - list_add(&user->node, &bus_tput->users_list); - user->level = level; - } else { - bus_tput->target_level -= user->level; - bus_tput->target_level += level; - user->level = level; - } - ret = bus_tput->target_level; -unlock: - mutex_unlock(&bus_tput->throughput_mutex); - return ret; -} - - -/** - * remove_req_tput - Release a previously requested level of - * a throughput level for interconnect - * @dev: Device pointer to dev - * - * This function recomputes the target level of the interconnect - * throughput after removing - * the level requested by the user. - * Returns 0, if the dev structure is invalid - * else returns modified interconnect throughput rate. - */ -static unsigned long remove_req_tput(struct device *dev) -{ - struct users *user; - int found = 0; - int ret; - - mutex_lock(&bus_tput->throughput_mutex); - list_for_each_entry(user, &bus_tput->users_list, node) { - if (user->dev == dev) { - found = 1; - break; - } - } - if (!found) { - /* No such user exists */ - pr_err("Invalid Device Structure\n"); - ret = 0; - goto unlock; - } - bus_tput->target_level -= user->level; - bus_tput->no_of_users--; - list_del(&user->node); - kfree(user); - ret = bus_tput->target_level; -unlock: - mutex_unlock(&bus_tput->throughput_mutex); - return ret; -} - -/* - * Device-driver-originated constraints (via board-*.c files) - */ - -int omap_pm_set_max_mpu_wakeup_lat(struct pm_qos_request_list **qos_request, - long t) -{ - if (!qos_request || t < -1) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - }; - - mutex_lock(&mpu_lat_mutex); - - if (t == -1) { - pm_qos_remove_request(*qos_request); - kfree(*qos_request); - *qos_request = NULL; - } else if (*qos_request == NULL) { - *qos_request = kzalloc(sizeof(struct pm_qos_request_list), GFP_KERNEL); - pm_qos_add_request(*qos_request, PM_QOS_CPU_DMA_LATENCY, t); - } else - pm_qos_update_request(*qos_request, t); - - mutex_unlock(&mpu_lat_mutex); - return 0; -} - - -int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, long r) -{ - - int ret; - struct device *l3_dev; - static struct device dummy_l3_dev; - unsigned long target_level = 0; - - if (!dev || (agent_id != OCP_INITIATOR_AGENT && - agent_id != OCP_TARGET_AGENT)) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - }; - - mutex_lock(&bus_tput_mutex); - - l3_dev = omap2_get_l3_device(); - if (!l3_dev) { - pr_err("Unable to get l3 device pointer"); - ret = -EINVAL; - goto unlock; - } - if (r == -1) { - pr_debug("OMAP PM: remove min bus tput constraint for: " - "interconnect dev %s for agent_id %d\n", dev_name(dev), - agent_id); - target_level = remove_req_tput(dev); - } else { - pr_debug("OMAP PM: add min bus tput constraint for: " - "interconnect dev %s for agent_id %d: rate %ld KiB\n", - dev_name(dev), agent_id, r); - target_level = add_req_tput(dev, r); - } - - /* Convert the throughput(in KiB/s) into Hz. */ - target_level = (target_level * 1000)/4; - - WARN(1, "OMAP PM: %s: constraint not called, needs DVFS", __func__); -#if 0 - ret = omap_device_scale(&dummy_l3_dev, l3_dev, target_level); -#endif - if (ret) - pr_err("Unable to change level for interconnect bandwidth to %ld\n", - target_level); -unlock: - mutex_unlock(&bus_tput_mutex); - return ret; -} - -int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev, - long t) -{ - struct omap_device *odev; - struct powerdomain *pwrdm_dev; - struct platform_device *pdev; - int ret = 0; - - if (!req_dev || !dev || t < -1) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - }; - - /* Look for the devices Power Domain */ - pdev = container_of(dev, struct platform_device, dev); - - /* Try to catch non platform devices. */ - if (pdev->name == NULL) { - pr_err("OMAP-PM: Error: platform device not valid\n"); - return -EINVAL; - } - - odev = to_omap_device(pdev); - if (odev) { - pwrdm_dev = omap_device_get_pwrdm(odev); - } else { - pr_err("OMAP-PM: Error: Could not find omap_device " - "for %s\n", pdev->name); - return -EINVAL; - } - - /* Catch devices with undefined powerdomains. */ - if (!pwrdm_dev) { - pr_err("OMAP-PM: Error: could not find parent " - "powerdomain for %s\n", pdev->name); - return -EINVAL; - } - - if (t == -1) { - pr_debug("OMAP PM: remove max device latency constraint: " - "dev %s, pwrdm %s, req by %s\n", dev_name(dev), - pwrdm_dev->name, dev_name(req_dev)); - ret = pwrdm_wakeuplat_release_constraint(pwrdm_dev, req_dev); - } else { - pr_debug("OMAP PM: add max device latency constraint: " - "dev %s, t = %ld usec, pwrdm %s, req by %s\n", - dev_name(dev), t, pwrdm_dev->name, dev_name(req_dev)); - ret = pwrdm_wakeuplat_set_constraint(pwrdm_dev, req_dev, t); - } - - /* - * For current Linux, this needs to map the device to a - * powerdomain, then go through the list of current max lat - * constraints on that powerdomain and find the smallest. If - * the latency constraint has changed, the code should - * recompute the state to enter for the next powerdomain - * state. Conceivably, this code should also determine - * whether to actually disable the device clocks or not, - * depending on how long it takes to re-enable the clocks. - * - * TI CDP code can call constraint_set here. - */ - - return ret; -} - -int omap_pm_set_max_sdma_lat(struct pm_qos_request_list **qos_request, - long t) -{ - if (!qos_request || t < -1) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - }; - - if (t == -1) { - pm_qos_remove_request(*qos_request); - kfree(*qos_request); - *qos_request = NULL; - } else if (*qos_request == NULL) { - *qos_request = kzalloc(sizeof(struct pm_qos_request_list), GFP_KERNEL); - pm_qos_add_request(*qos_request, PM_QOS_CPU_DMA_LATENCY, t); - } else - pm_qos_update_request(*qos_request, t); - - return 0; -} - -int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r) -{ - if (!dev || !c || r < 0) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - } - - if (r == 0) - pr_debug("OMAP PM: remove min clk rate constraint: " - "dev %s\n", dev_name(dev)); - else - pr_debug("OMAP PM: add min clk rate constraint: " - "dev %s, rate = %ld Hz\n", dev_name(dev), r); - - /* - * Code in a real implementation should keep track of these - * constraints on the clock, and determine the highest minimum - * clock rate. It should iterate over each OPP and determine - * whether the OPP will result in a clock rate that would - * satisfy this constraint (and any other PM constraint in effect - * at that time). Once it finds the lowest-voltage OPP that - * meets those conditions, it should switch to it, or return - * an error if the code is not capable of doing so. - */ - - return 0; -} - -/* - * DSP Bridge-specific constraints - */ - -const struct omap_opp *omap_pm_dsp_get_opp_table(void) -{ - pr_debug("OMAP PM: DSP request for OPP table\n"); - - /* - * Return DSP frequency table here: The final item in the - * array should have .rate = .opp_id = 0. - */ - - return NULL; -} - -void omap_pm_dsp_set_min_opp(u8 opp_id) -{ - if (opp_id == 0) { - WARN_ON(1); - return; - } - - pr_debug("OMAP PM: DSP requests minimum VDD1 OPP to be %d\n", opp_id); - - /* - * - * For l-o dev tree, our VDD1 clk is keyed on OPP ID, so we - * can just test to see which is higher, the CPU's desired OPP - * ID or the DSP's desired OPP ID, and use whichever is - * highest. - * - * In CDP12.14+, the VDD1 OPP custom clock that controls the DSP - * rate is keyed on MPU speed, not the OPP ID. So we need to - * map the OPP ID to the MPU speed for use with clk_set_rate() - * if it is higher than the current OPP clock rate. - * - */ -} - - -u8 omap_pm_dsp_get_opp(void) -{ - pr_debug("OMAP PM: DSP requests current DSP OPP ID\n"); - - /* - * For l-o dev tree, call clk_get_rate() on VDD1 OPP clock - * - * CDP12.14+: - * Call clk_get_rate() on the OPP custom clock, map that to an - * OPP ID using the tables defined in board-*.c/chip-*.c files. - */ - - return 0; -} - -/* - * CPUFreq-originated constraint - * - * In the future, this should be handled by custom OPP clocktype - * functions. - */ - -struct cpufreq_frequency_table **omap_pm_cpu_get_freq_table(void) -{ - pr_debug("OMAP PM: CPUFreq request for frequency table\n"); - - /* - * Return CPUFreq frequency table here: loop over - * all VDD1 clkrates, pull out the mpu_ck frequencies, build - * table - */ - - return NULL; -} - -void omap_pm_cpu_set_freq(unsigned long f) -{ - if (f == 0) { - WARN_ON(1); - return; - } - - pr_debug("OMAP PM: CPUFreq requests CPU frequency to be set to %lu\n", - f); - - /* - * For l-o dev tree, determine whether MPU freq or DSP OPP id - * freq is higher. Find the OPP ID corresponding to the - * higher frequency. Call clk_round_rate() and clk_set_rate() - * on the OPP custom clock. - * - * CDP should just be able to set the VDD1 OPP clock rate here. - */ -} - -unsigned long omap_pm_cpu_get_freq(void) -{ - pr_debug("OMAP PM: CPUFreq requests current CPU frequency\n"); - - /* - * Call clk_get_rate() on the mpu_ck. - */ - - return 0; -} - -/** - * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled - * - * Intended for use only by OMAP PM core code to notify this layer - * that off mode has been enabled. - */ -void omap_pm_enable_off_mode(void) -{ - off_mode_enabled = true; -} - -/** - * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled - * - * Intended for use only by OMAP PM core code to notify this layer - * that off mode has been disabled. - */ -void omap_pm_disable_off_mode(void) -{ - off_mode_enabled = false; -} - -/* - * Device context loss tracking - */ - -int omap_pm_get_dev_context_loss_count(struct device *dev) -{ - static u32 counter = 1; - - if (!dev) { - WARN_ON(1); - return -EINVAL; - }; - - pr_debug("OMAP PM: returning context loss count for dev %s\n", - dev_name(dev)); - - /* - * Map the device to the powerdomain. Return the powerdomain - * off counter. - */ - - /* Let the counter roll-over: its for test only */ - return counter++; -} - - -/* Should be called before clk framework init */ -int __init omap_pm_if_early_init() -{ - return 0; -} - -/* Must be called after clock framework is initialized */ -int __init omap_pm_if_init(void) -{ - int ret; - ret = omap_bus_tput_init(); - if (ret) - pr_err("Failed to initialize interconnect" - " bandwidth users list\n"); - return ret; -} - -void omap_pm_if_exit(void) -{ - /* Deallocate CPUFreq frequency table here */ -} - -int omap_pm_set_min_mpu_freq(struct device *dev, unsigned long f) -{ - - int ret = 0; - struct device *mpu_dev; - - if (!dev) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - } - - mutex_lock(&mpu_tput_mutex); - - mpu_dev = omap2_get_mpuss_device(); - if (!mpu_dev) { - pr_err("Unable to get MPU device pointer"); - ret = -EINVAL; - goto unlock; - } - - - /* Rescale the frequency if a change is detected with - * the new constraint. - */ - WARN(1, "OMAP PM: %s: constraint not called, needs DVFS", __func__); -#if 0 - ret = omap_device_set_rate(dev, mpu_dev, f); -#endif - if (ret) - pr_err("Unable to set MPU frequency to %ld\n", f); - -unlock: - mutex_unlock(&mpu_tput_mutex); - return ret; -} -EXPORT_SYMBOL(omap_pm_set_min_mpu_freq); diff --git a/drivers/cpufreq/cpufreq_hotplug.c b/drivers/cpufreq/cpufreq_hotplug.c index 85aa6d2..853947e 100644 --- a/drivers/cpufreq/cpufreq_hotplug.c +++ b/drivers/cpufreq/cpufreq_hotplug.c @@ -33,8 +33,11 @@ /* greater than 80% avg load across online CPUs increases frequency */ #define DEFAULT_UP_FREQ_MIN_LOAD (80) +/* Keep 10% of idle under the up threshold when decreasing the frequency */ +#define DEFAULT_FREQ_DOWN_DIFFERENTIAL (10) + /* less than 20% avg load across online CPUs decreases frequency */ -#define DEFAULT_DOWN_FREQ_MAX_LOAD (20) +#define DEFAULT_DOWN_FREQ_MAX_LOAD (30) /* default sampling period (uSec) is bogus; 10x ondemand's default for x86 */ #define DEFAULT_SAMPLING_PERIOD (100000) @@ -88,6 +91,7 @@ static struct workqueue_struct *khotplug_wq; static struct dbs_tuners { unsigned int sampling_rate; unsigned int up_threshold; + unsigned int down_differential; unsigned int down_threshold; unsigned int hotplug_in_sampling_periods; unsigned int hotplug_out_sampling_periods; @@ -98,6 +102,7 @@ static struct dbs_tuners { } dbs_tuners_ins = { .sampling_rate = DEFAULT_SAMPLING_PERIOD, .up_threshold = DEFAULT_UP_FREQ_MIN_LOAD, + .down_differential = DEFAULT_FREQ_DOWN_DIFFERENTIAL, .down_threshold = DEFAULT_DOWN_FREQ_MAX_LOAD, .hotplug_in_sampling_periods = DEFAULT_HOTPLUG_IN_SAMPLING_PERIODS, .hotplug_out_sampling_periods = DEFAULT_HOTPLUG_OUT_SAMPLING_PERIODS, @@ -145,6 +150,7 @@ static ssize_t show_##file_name \ } show_one(sampling_rate, sampling_rate); show_one(up_threshold, up_threshold); +show_one(down_differential, down_differential); show_one(down_threshold, down_threshold); show_one(hotplug_in_sampling_periods, hotplug_in_sampling_periods); show_one(hotplug_out_sampling_periods, hotplug_out_sampling_periods); @@ -185,6 +191,23 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, return count; } +static ssize_t store_down_differential(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input >= dbs_tuners_ins.up_threshold) + return -EINVAL; + + mutex_lock(&dbs_mutex); + dbs_tuners_ins.down_differential = input; + mutex_unlock(&dbs_mutex); + + return count; +} + static ssize_t store_down_threshold(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -357,6 +380,7 @@ static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b, define_one_global_rw(sampling_rate); define_one_global_rw(up_threshold); +define_one_global_rw(down_differential); define_one_global_rw(down_threshold); define_one_global_rw(hotplug_in_sampling_periods); define_one_global_rw(hotplug_out_sampling_periods); @@ -366,6 +390,7 @@ define_one_global_rw(io_is_busy); static struct attribute *dbs_attributes[] = { &sampling_rate.attr, &up_threshold.attr, + &down_differential.attr, &down_threshold.attr, &hotplug_in_sampling_periods.attr, &hotplug_out_sampling_periods.attr, @@ -385,8 +410,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) { /* combined load of all enabled CPUs */ unsigned int total_load = 0; - /* single largest CPU load */ + /* single largest CPU load percentage*/ unsigned int max_load = 0; + /* largest CPU load in terms of frequency */ + unsigned int max_load_freq = 0; /* average load across all enabled CPUs */ unsigned int avg_load = 0; /* average load across multiple sampling periods for hotplug events */ @@ -396,7 +423,6 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) unsigned int periods; struct cpufreq_policy *policy; - unsigned int index = 0; unsigned int i, j; policy = this_dbs_info->cur_policy; @@ -440,6 +466,9 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) max_load = load; } + /* use the max load in the OPP freq change policy */ + max_load_freq = max_load * policy->cur; + /* calculate the average load across all related CPUs */ avg_load = total_load / num_online_cpus(); @@ -481,7 +510,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) if (++dbs_tuners_ins.hotplug_load_index == periods) dbs_tuners_ins.hotplug_load_index = 0; - /* check for frequency increase */ + /* check if auxiliary CPU is needed based on avg_load */ if (avg_load > dbs_tuners_ins.up_threshold) { /* should we enable auxillary CPUs? */ if (num_online_cpus() < 2 && hotplug_in_avg_load > @@ -495,7 +524,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) mutex_lock(&this_dbs_info->timer_mutex); goto out; } + } + /* check for frequency increase based on max_load */ + if (max_load > dbs_tuners_ins.up_threshold) { /* increase to highest frequency supported */ if (policy->cur < policy->max) __cpufreq_driver_target(policy, policy->max, @@ -517,18 +549,25 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) } goto out; } + } - /* bump down to the next lowest frequency in the table */ - if (cpufreq_frequency_table_next_lowest(policy, - this_dbs_info->freq_table, &index)) { - pr_err("%s: failed to get next lowest frequency\n", - __func__); - goto out; - } - - __cpufreq_driver_target(policy, - this_dbs_info->freq_table[index].frequency, - CPUFREQ_RELATION_L); + /* + * go down to the lowest frequency which can sustain the load by + * keeping 30% of idle in order to not cross the up_threshold + */ + if ((max_load_freq < + (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential) * + policy->cur) && (policy->cur > policy->min)) { + unsigned int freq_next; + freq_next = max_load_freq / + (dbs_tuners_ins.up_threshold - + dbs_tuners_ins.down_differential); + + if (freq_next < policy->min) + freq_next = policy->min; + + __cpufreq_driver_target(policy, freq_next, + CPUFREQ_RELATION_L); } out: return; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index ab9833b..da986f2 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -40,6 +40,7 @@ #include <linux/slab.h> #include <linux/i2c-omap.h> #include <linux/pm_runtime.h> +#include <linux/pm_qos_params.h> /* I2C controller revisions */ #define OMAP_I2C_REV_2 0x20 @@ -179,8 +180,7 @@ struct omap_i2c_dev { struct completion cmd_complete; struct resource *ioarea; u32 latency; /* maximum mpu wkup latency */ - void (*set_mpu_wkup_lat)(struct device *dev, - long latency); + struct pm_qos_request_list *pm_qos; u32 speed; /* Speed of bus in Khz */ u16 cmd_err; u8 *buf; @@ -648,8 +648,14 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) if (r < 0) goto out; - if (dev->set_mpu_wkup_lat != NULL) - dev->set_mpu_wkup_lat(dev->dev, dev->latency); + /* + * When waiting for completion of a i2c transfer, we need to + * set a wake up latency constraint for the MPU. This is to + * ensure quick enough wakeup from idle, when transfer + * completes. + */ + if (dev->pm_qos) + pm_qos_update_request(dev->pm_qos, dev->latency); for (i = 0; i < num; i++) { r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1))); @@ -657,8 +663,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) break; } - if (dev->set_mpu_wkup_lat != NULL) - dev->set_mpu_wkup_lat(dev->dev, -1); + if (dev->pm_qos) + pm_qos_update_request(dev->pm_qos, PM_QOS_DEFAULT_VALUE); if (r == 0) r = num; @@ -1007,13 +1013,10 @@ omap_i2c_probe(struct platform_device *pdev) goto err_release_region; } - if (pdata != NULL) { + if (pdata) speed = pdata->clkrate; - dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat; - } else { + else speed = 100; /* Default speed */ - dev->set_mpu_wkup_lat = NULL; - } dev->speed = speed; dev->idle = 1; @@ -1025,6 +1028,17 @@ omap_i2c_probe(struct platform_device *pdev) goto err_free_mem; } + if (pdata && pdata->needs_wakeup_latency) { + dev->pm_qos = kzalloc(sizeof(struct pm_qos_request_list), + GFP_KERNEL); + if (!dev->pm_qos) { + r = -ENOMEM; + goto err_unmap; + } + pm_qos_add_request(dev->pm_qos, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + } + platform_set_drvdata(pdev, dev); if (cpu_is_omap7xx()) @@ -1067,7 +1081,7 @@ omap_i2c_probe(struct platform_device *pdev) dev->b_hw = 1; /* Enable hardware fixes */ } /* calculate wakeup latency constraint for MPU */ - if (dev->set_mpu_wkup_lat != NULL) + if (dev->pm_qos) dev->latency = (1000000 * dev->fifo_size) / (1000 * speed / 8); } @@ -1111,6 +1125,11 @@ err_free_irq: err_unuse_clocks: omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); omap_i2c_idle(dev); + if (dev->pm_qos) { + pm_qos_remove_request(dev->pm_qos); + kfree(dev->pm_qos); + } +err_unmap: iounmap(dev->base); err_free_mem: platform_set_drvdata(pdev, NULL); @@ -1133,6 +1152,10 @@ omap_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&dev->adapter); omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); iounmap(dev->base); + if (dev->pm_qos) { + pm_qos_remove_request(dev->pm_qos); + kfree(dev->pm_qos); + } kfree(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(mem->start, resource_size(mem)); diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h index 7472449..3beb390 100644 --- a/include/linux/i2c-omap.h +++ b/include/linux/i2c-omap.h @@ -5,7 +5,7 @@ struct omap_i2c_bus_platform_data { u32 clkrate; - void (*set_mpu_wkup_lat)(struct device *dev, long set); + bool needs_wakeup_latency; int (*device_enable) (struct platform_device *pdev); int (*device_shutdown) (struct platform_device *pdev); int (*device_idle) (struct platform_device *pdev); |