The previous implementation of OMAP interrupt/DMA latency constraints were implemented using SRF which is obsolete now. This patch has implemented the same using PM QOS infeastructure. As part of this, API signature has been changed to take pm qos handle instead of dev pointer since PM QOS infrastructure takes pm_qos handle to distingush different latency requests.So each driver is expected to have it's own pm_qos handle. However it's an opaque handle from driver's point of view as drivers just need to define a handle and this handle gets initialized automatically when they make the first mpu/dma latency request. Cc: Kevin Hilman <khilman@xxxxxxxxxxxxxxxxxxx> Cc: Paul Walmsley <paul@xxxxxxxxx> Signed-off-by: Vishwanath BS <vishwanath.bs@xxxxxx> The changes are rebased to latest kevin's origin/pm branch and tested on OMAP ZOOM3. --- V2: aligned the implementation with latest PM QOS APIs addressed comments from Kevin Hilman (https://patchwork.kernel.org/patch/113855/) V1: Initial Patch arch/arm/plat-omap/Kconfig | 3 + arch/arm/plat-omap/Makefile | 1 + arch/arm/plat-omap/i2c.c | 3 +- arch/arm/plat-omap/include/plat/omap-pm.h | 13 +- arch/arm/plat-omap/omap-pm-noop.c | 20 +- arch/arm/plat-omap/omap-pm.c | 316 +++++++++++++++++++++++++++++ 6 files changed, 341 insertions(+), 15 deletions(-) create mode 100755 arch/arm/plat-omap/omap-pm.c diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 92c5bb7..4ff485e 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -184,6 +184,9 @@ config OMAP_PM_NONE config OMAP_PM_NOOP bool "No-op/debug PM layer" +config OMAP_PM + depends on PM + bool "OMAP PM layer implementation" endchoice endmenu diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 3f0a2bb..545a6a1 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -38,3 +38,4 @@ obj-y += $(i2c-omap-m) $(i2c-omap-y) obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o +obj-$(CONFIG_OMAP_PM) += omap-pm.o diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c index a5bff9c..eb9a7cb 100644 --- a/arch/arm/plat-omap/i2c.c +++ b/arch/arm/plat-omap/i2c.c @@ -114,7 +114,8 @@ static inline int omap1_i2c_add_bus(int bus_id) */ static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t) { - omap_pm_set_max_mpu_wakeup_lat(dev, t); + static struct pm_qos_request_list *qos_handle; + omap_pm_set_max_mpu_wakeup_lat(&qos_handle, t); } static struct omap_device_pm_latency omap_i2c_latency[] = { diff --git a/arch/arm/plat-omap/include/plat/omap-pm.h b/arch/arm/plat-omap/include/plat/omap-pm.h index c5b533d..6425e82 --- a/arch/arm/plat-omap/include/plat/omap-pm.h +++ b/arch/arm/plat-omap/include/plat/omap-pm.h @@ -20,6 +20,7 @@ #include "powerdomain.h" #include <plat/opp.h> +#include <linux/pm_qos_params.h> /* * agent_id values for use with omap_pm_set_min_bus_tput(): @@ -75,7 +76,8 @@ void omap_pm_if_exit(void); /** * omap_pm_set_max_mpu_wakeup_lat - set the maximum MPU wakeup latency - * @dev: struct device * requesting the constraint + * @qos_request: handle for the constraint. The pointer should be + * initialized to NULL * @t: maximum MPU wakeup latency in microseconds * * Request that the maximum interrupt latency for the MPU to be no @@ -107,7 +109,8 @@ void omap_pm_if_exit(void); * Returns -EINVAL for an invalid argument, -ERANGE if the constraint * is not satisfiable, or 0 upon success. */ -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t); +int omap_pm_set_max_mpu_wakeup_lat(struct pm_qos_request_list **qos_request, + long t); /** @@ -174,7 +177,8 @@ int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev, /** * omap_pm_set_max_sdma_lat - set the maximum system DMA transfer start latency - * @dev: struct device * + * @qos_request: handle for the constraint. The pointer should be + * initialized to NULL * @t: maximum DMA transfer start latency in microseconds * * Request that the maximum system DMA transfer start latency for this @@ -199,7 +203,8 @@ int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev, * Returns -EINVAL for an invalid argument, -ERANGE if the constraint * is not satisfiable, or 0 upon success. */ -int omap_pm_set_max_sdma_lat(struct device *dev, long t); +int omap_pm_set_max_sdma_lat(struct pm_qos_request_list **qos_request, + long t); /** diff --git a/arch/arm/plat-omap/omap-pm-noop.c b/arch/arm/plat-omap/omap-pm-noop.c index ca75abb..abcf9ae --- a/arch/arm/plat-omap/omap-pm-noop.c +++ b/arch/arm/plat-omap/omap-pm-noop.c @@ -30,19 +30,19 @@ * Device-driver-originated constraints (via board-*.c files) */ -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t) +int omap_pm_set_max_mpu_wakeup_lat(struct pm_qos_request_list **pmqos_req, + long t) { - if (!dev || t < -1) { + 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: " - "dev %s\n", dev_name(dev)); + pr_debug("OMAP PM: remove max MPU wakeup latency constraint\n"); else pr_debug("OMAP PM: add max MPU wakeup latency constraint: " - "dev %s, t = %ld usec\n", dev_name(dev), t); + "t = %ld usec\n", t); /* * For current Linux, this needs to map the MPU to a @@ -116,19 +116,19 @@ int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev, return 0; } -int omap_pm_set_max_sdma_lat(struct device *dev, long t) +int omap_pm_set_max_sdma_lat(struct pm_qos_request_list **qos_request, + long t) { - if (!dev || t < -1) { + 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: " - "dev %s\n", dev_name(dev)); + pr_debug("OMAP PM: remove max DMA latency constraint:\n"); else pr_debug("OMAP PM: add max DMA latency constraint: " - "dev %s, t = %ld usec\n", dev_name(dev), t); + "t = %ld usec\n", t); /* * For current Linux PM QOS params, this code should scan the diff --git a/arch/arm/plat-omap/omap-pm.c b/arch/arm/plat-omap/omap-pm.c new file mode 100755 index 0000000..9002334 --- /dev/null +++ b/arch/arm/plat-omap/omap-pm.c @@ -0,0 +1,316 @@ +/* + * 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/slab.h> + +/* Interface documentation is in mach/omap-pm.h */ +#include <plat/omap-pm.h> + +#include <plat/powerdomain.h> + +/* + * 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) { + pr_warning("OMAP PM: %s: invalid parameter(s)\n", __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); + if (!*qos_request) { + pr_warning("OMAP PM: %s: kzalloc failed\n", __func__); + return -ENOMEM; + } + 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_bus_tput(struct device *dev, u8 agent_id, unsigned 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) { + pr_warning("OMAP PM: %s: invalid parameter(s)\n", __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); + if (!*qos_request) { + pr_warning("OMAP PM: %s: kzalloc failed\n", __func__); + return -ENOMEM; + } + 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; +} + +/* + * Device context loss tracking + */ + +int omap_pm_get_dev_context_loss_count(struct device *dev) +{ + 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. + */ + + return 0; +} + + +/* 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 */ +} + + -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html