blob: deb4bc89b9dd61ecfa01dc5cbcd3247b64a668c6 [file] [log] [blame]
/*
* omap-pm-srf.c - OMAP power management interface implemented
* using Shared resource framework
*
* Copyright (C) 2008-2009 Texas Instruments, Inc.
* Copyright (C) 2008-2009 Nokia Corporation
* Rajendra Nayak
*
* 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/module.h>
#include <plat/omap-pm.h>
#include <plat/powerdomain.h>
#include <plat/resource.h>
#include <plat/omap_device.h>
#include <plat/omap34xx.h>
struct omap_opp *dsp_opps;
struct omap_opp *mpu_opps;
struct omap_opp *l3_opps;
#define LAT_RES_POSTAMBLE "_latency"
#define MAX_LATENCY_RES_NAME 30
/**
* get_lat_res_name - gets the latency resource name given a power domain name
* @pwrdm_name: Name of the power domain.
* @lat_name: Buffer in which latency resource name is populated
* @size: Max size of the latency resource name
*
* Returns the latency resource name populated in lat_name.
*/
void get_lat_res_name(const char *pwrdm_name, char **lat_name, int size)
{
strcpy(*lat_name, "");
WARN_ON(strlen(pwrdm_name) + strlen(LAT_RES_POSTAMBLE) > size);
strcpy(*lat_name, pwrdm_name);
strcat(*lat_name, LAT_RES_POSTAMBLE);
return;
}
/*
* Device-driver-originated constraints (via board-*.c files)
*/
void omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
{
if (!dev || t < -1) {
WARN_ON(1);
return;
};
if (t == -1) {
pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
"dev %s\n", dev_name(dev));
resource_release("mpu_latency", dev);
} else {
pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
"dev %s, t = %ld usec\n", dev_name(dev), t);
resource_request("mpu_latency", dev, t);
}
}
void 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_ON(1);
return;
};
if (r == 0) {
pr_debug("OMAP PM: remove min bus tput constraint: "
"dev %s for agent_id %d\n", dev_name(dev), agent_id);
resource_release("vdd2_opp", dev);
} 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);
resource_request("vdd2_opp", dev, r);
}
}
EXPORT_SYMBOL(omap_pm_set_min_bus_tput);
void omap_pm_set_max_dev_wakeup_lat(struct device *dev, long t)
{
struct omap_device *odev;
struct powerdomain *pwrdm_dev;
struct platform_device *pdev;
char *lat_res_name;
if (!dev || t < -1) {
WARN_ON(1);
return;
};
/* Look for the devices Power Domain */
/*
* WARNING! If device is not a platform device, container_of will
* return a pointer to unknown memory!
* TODO: Either change omap-pm interface to support only platform
* devices, or change the underlying omapdev implementation to
* support normal devices.
*/
pdev = container_of(dev, struct platform_device, dev);
/* Try to catch non platform devices. */
if (pdev->name == NULL) {
printk(KERN_ERR "OMAP-PM: Error: platform device not valid\n");
return;
}
odev = to_omap_device(pdev);
if (odev) {
pwrdm_dev = omap_device_get_pwrdm(odev);
} else {
printk(KERN_ERR "OMAP-PM: Error: Could not find omap_device "
"for %s\n", pdev->name);
return;
}
lat_res_name = kmalloc(MAX_LATENCY_RES_NAME, GFP_KERNEL);
if (!lat_res_name) {
printk(KERN_ERR "OMAP-PM: FATAL ERROR: kmalloc failed\n");
return;
}
get_lat_res_name(pwrdm_dev->name, &lat_res_name, MAX_LATENCY_RES_NAME);
if (t == -1) {
pr_debug("OMAP PM: remove max device latency constraint: "
"dev %s\n", dev_name(dev));
resource_release(lat_res_name, dev);
} else {
pr_debug("OMAP PM: add max device latency constraint: "
"dev %s, t = %ld usec\n", dev_name(dev), t);
resource_request(lat_res_name, dev, t);
}
kfree(lat_res_name);
return;
}
void omap_pm_set_max_sdma_lat(struct device *dev, long t)
{
if (!dev || t < -1) {
WARN_ON(1);
return;
};
if (t == -1) {
pr_debug("OMAP PM: remove max DMA latency constraint: "
"dev %s\n", dev_name(dev));
resource_release("core_latency", dev);
} else {
pr_debug("OMAP PM: add max DMA latency constraint: "
"dev %s, t = %ld usec\n", dev_name(dev), t);
resource_request("core_latency", dev, t);
}
}
static struct device dummy_dsp_dev;
/*
* 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 dsp_opps;
}
void omap_pm_dsp_set_min_opp(struct device *dev, unsigned long f)
{
u8 opp_id;
if (!dev) {
WARN_ON(1);
return;
}
pr_debug("OMAP PM: DSP requests minimum VDD1 OPP to be %d\n", opp_id);
opp_id = get_opp_id(dsp_opps + MAX_VDD1_OPP, f);
/*
* For now pass a dummy_dev struct for SRF to identify the caller.
* Maybe its good to have DSP pass this as an argument
*/
resource_request("vdd1_opp", &dummy_dsp_dev, opp_id);
return;
}
u8 omap_pm_dsp_get_opp(void)
{
pr_debug("OMAP PM: DSP requests current DSP OPP ID\n");
return resource_get_level("vdd1_opp");
}
u8 omap_pm_vdd1_get_opp(void)
{
pr_debug("OMAP PM: User requests current VDD1 OPP\n");
return resource_get_level("vdd1_opp");
}
u8 omap_pm_vdd2_get_opp(void)
{
pr_debug("OMAP PM: User requests current VDD2 OPP\n");
return resource_get_level("vdd2_opp");
}
/*
* 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;
}
static struct device dummy_cpufreq_dev;
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);
resource_request("mpu_freq", &dummy_cpufreq_dev, f);
return;
}
void omap_pm_set_min_mpu_freq(struct device *dev, 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);
resource_request("mpu_freq", dev, f);
return;
}
EXPORT_SYMBOL(omap_pm_set_min_mpu_freq);
unsigned long omap_pm_cpu_get_freq(void)
{
pr_debug("OMAP PM: CPUFreq requests current CPU frequency\n");
return resource_get_level("mpu_freq");
}
/*
* Device context loss tracking
*/
int omap_pm_get_dev_context_loss_count(struct device *dev)
{
struct platform_device *pdev;
struct omap_device *odev;
struct powerdomain *pwrdm;
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.
*/
pdev = to_platform_device(dev);
odev = to_omap_device(pdev);
if (odev) {
pwrdm = omap_device_get_pwrdm(odev);
if (pwrdm)
return pwrdm->state_counter[0];
}
return 0;
}
/*
* Must be called before clk framework init
*/
int __init omap_pm_if_early_init(struct omap_opp *mpu_opp_table,
struct omap_opp *dsp_opp_table,
struct omap_opp *l3_opp_table)
{
mpu_opps = mpu_opp_table;
dsp_opps = dsp_opp_table;
l3_opps = l3_opp_table;
return 0;
}
/* Must be called after clock framework is initialized */
int __init omap_pm_if_init(void)
{
resource_init(resources_omap);
return 0;
}
void omap_pm_if_exit(void)
{
/* Deallocate CPUFreq frequency table here */
}
u8 omap_pm_get_max_vdd1_opp()
{
if (cpu_is_omap3630()) {
return VDD1_OPP4;
} else {
return VDD1_OPP5;
}
}
EXPORT_SYMBOL(omap_pm_get_max_vdd1_opp);
u8 omap_pm_get_min_vdd1_opp(void)
{
return VDD1_OPP1;
}
EXPORT_SYMBOL(omap_pm_get_min_vdd1_opp);
u8 omap_pm_get_max_vdd2_opp(void)
{
if (cpu_is_omap3630())
return VDD2_OPP2;
else
return VDD2_OPP3;
}
EXPORT_SYMBOL(omap_pm_get_max_vdd2_opp);
u8 omap_pm_get_min_vdd2_opp(void)
{
if (cpu_is_omap3630())
return VDD2_OPP1;
else
return VDD2_OPP2;
}
EXPORT_SYMBOL(omap_pm_get_min_vdd2_opp);
struct omap_opp *omap_get_mpu_rate_table()
{
return mpu_opps;
}
EXPORT_SYMBOL(omap_get_mpu_rate_table);
struct omap_opp *omap_get_dsp_rate_table()
{
return dsp_opps;
}
EXPORT_SYMBOL(omap_get_dsp_rate_table);