summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra_cl_dvfs.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2013-11-02 18:57:38 -0700
committerYu-Huan Hsu <yhsu@nvidia.com>2013-11-07 12:29:07 -0800
commit80186cd7852ea5ba2289a7028c5508a9157495c1 (patch)
tree2d336f810166acd635671b39e8f289fdd6e908db /arch/arm/mach-tegra/tegra_cl_dvfs.c
parentf3f293eabaf27fb6b063bf1c573d4493914303ca (diff)
ARM: tegra: dvfs: Split CL-DVFS registers accessors
In some h/w configurations CL-DVFS module registers have two different address bases: one for I2C control/status registers, and one for all other registers. Separated registers accessors accordingly. Change-Id: I4e72e0a643b1870a77564511763dad9d8bd52804 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/325933 Reviewed-by: Krishna Sitaraman <ksitaraman@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra_cl_dvfs.c')
-rw-r--r--arch/arm/mach-tegra/tegra_cl_dvfs.c83
1 files changed, 63 insertions, 20 deletions
diff --git a/arch/arm/mach-tegra/tegra_cl_dvfs.c b/arch/arm/mach-tegra/tegra_cl_dvfs.c
index 3918f8634bf4..d2d0248967d3 100644
--- a/arch/arm/mach-tegra/tegra_cl_dvfs.c
+++ b/arch/arm/mach-tegra/tegra_cl_dvfs.c
@@ -176,6 +176,7 @@ struct dfll_rate_req {
struct tegra_cl_dvfs {
void *cl_base;
+ void *cl_i2c_base;
struct tegra_cl_dvfs_platform_data *p_data;
struct dvfs *safe_dvfs;
@@ -246,12 +247,42 @@ static const char *mode_name[] = {
[TEGRA_CL_DVFS_CLOSED_LOOP] = "closed_loop",
};
+/*
+ * In some h/w configurations CL-DVFS module registers have two different
+ * address bases: one for I2C control/status registers, and one for all other
+ * registers. Registers accessors are separated below accordingly just by
+ * comparing register offset with start of I2C section - CL_DVFS_I2C_CFG. One
+ * special case is CL_DVFS_OUTPUT_CFG register: when I2C controls are separated
+ * I2C_ENABLE bit of this register is accessed from I2C base, and all other bits
+ * are accessed from the main base.
+ */
+static inline u32 cl_dvfs_i2c_readl(struct tegra_cl_dvfs *cld, u32 offs)
+{
+ return __raw_readl(cld->cl_i2c_base + offs);
+}
+static inline void cl_dvfs_i2c_writel(struct tegra_cl_dvfs *cld,
+ u32 val, u32 offs)
+{
+ __raw_writel(val, cld->cl_i2c_base + offs);
+}
+static inline void cl_dvfs_i2c_wmb(struct tegra_cl_dvfs *cld)
+{
+ wmb();
+ cl_dvfs_i2c_readl(cld, CL_DVFS_I2C_CFG);
+}
+
static inline u32 cl_dvfs_readl(struct tegra_cl_dvfs *cld, u32 offs)
{
+ if (offs >= CL_DVFS_I2C_CFG)
+ return cl_dvfs_i2c_readl(cld, offs);
return __raw_readl((void *)cld->cl_base + offs);
}
static inline void cl_dvfs_writel(struct tegra_cl_dvfs *cld, u32 val, u32 offs)
{
+ if (offs >= CL_DVFS_I2C_CFG) {
+ cl_dvfs_i2c_writel(cld, val, offs);
+ return;
+ }
__raw_writel(val, (void *)cld->cl_base + offs);
}
static inline void cl_dvfs_wmb(struct tegra_cl_dvfs *cld)
@@ -324,11 +355,13 @@ static inline int get_mv(struct tegra_cl_dvfs *cld, u32 out_val)
static int output_enable(struct tegra_cl_dvfs *cld)
{
- u32 val = cl_dvfs_readl(cld, CL_DVFS_OUTPUT_CFG);
-
if (is_i2c(cld)) {
+ u32 val = cl_dvfs_i2c_readl(cld, CL_DVFS_OUTPUT_CFG);
val |= CL_DVFS_OUTPUT_CFG_I2C_ENABLE;
+ cl_dvfs_i2c_writel(cld, val, CL_DVFS_OUTPUT_CFG);
+ cl_dvfs_i2c_wmb(cld);
} else {
+ u32 val = cl_dvfs_readl(cld, CL_DVFS_OUTPUT_CFG);
struct tegra_cl_dvfs_platform_data *d = cld->p_data;
if (d->u.pmu_pwm.pwm_bus == TEGRA_CL_DVFS_PWM_1WIRE_BUFFER) {
int gpio = d->u.pmu_pwm.out_gpio;
@@ -344,10 +377,10 @@ static int output_enable(struct tegra_cl_dvfs *cld)
}
val |= CL_DVFS_OUTPUT_CFG_PWM_ENABLE;
+ cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_CFG);
+ cl_dvfs_wmb(cld);
}
- cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_CFG);
- cl_dvfs_wmb(cld);
return 0;
}
@@ -380,7 +413,7 @@ static noinline int output_flush_disable(struct tegra_cl_dvfs *cld)
{
int i;
u32 sts;
- u32 val = cl_dvfs_readl(cld, CL_DVFS_OUTPUT_CFG);
+ u32 val = cl_dvfs_i2c_readl(cld, CL_DVFS_OUTPUT_CFG);
/* Flush transactions in flight, and then disable */
for (i = 0; i < CL_DVFS_OUTPUT_PENDING_TIMEOUT / 2; i++) {
@@ -390,7 +423,7 @@ static noinline int output_flush_disable(struct tegra_cl_dvfs *cld)
sts = cl_dvfs_readl(cld, CL_DVFS_I2C_STS);
if (!(sts & CL_DVFS_I2C_STS_I2C_REQ_PENDING)) {
val &= ~CL_DVFS_OUTPUT_CFG_I2C_ENABLE;
- cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_CFG);
+ cl_dvfs_i2c_writel(cld, val, CL_DVFS_OUTPUT_CFG);
wmb();
sts = cl_dvfs_readl(cld, CL_DVFS_I2C_STS);
if (!(sts & CL_DVFS_I2C_STS_I2C_REQ_PENDING))
@@ -398,7 +431,7 @@ static noinline int output_flush_disable(struct tegra_cl_dvfs *cld)
/* Re-enable, continue wait */
val |= CL_DVFS_OUTPUT_CFG_I2C_ENABLE;
- cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_CFG);
+ cl_dvfs_i2c_writel(cld, val, CL_DVFS_OUTPUT_CFG);
wmb();
}
}
@@ -406,8 +439,8 @@ static noinline int output_flush_disable(struct tegra_cl_dvfs *cld)
/* I2C request is still pending - disable, anyway, but report error */
val &= ~CL_DVFS_OUTPUT_CFG_I2C_ENABLE;
- cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_CFG);
- cl_dvfs_wmb(cld);
+ cl_dvfs_i2c_writel(cld, val, CL_DVFS_OUTPUT_CFG);
+ cl_dvfs_i2c_wmb(cld);
return -ETIMEDOUT;
}
@@ -415,12 +448,12 @@ static noinline int output_disable_flush(struct tegra_cl_dvfs *cld)
{
int i;
u32 sts;
- u32 val = cl_dvfs_readl(cld, CL_DVFS_OUTPUT_CFG);
+ u32 val = cl_dvfs_i2c_readl(cld, CL_DVFS_OUTPUT_CFG);
/* Disable output interface right away */
val &= ~CL_DVFS_OUTPUT_CFG_I2C_ENABLE;
- cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_CFG);
- cl_dvfs_wmb(cld);
+ cl_dvfs_i2c_writel(cld, val, CL_DVFS_OUTPUT_CFG);
+ cl_dvfs_i2c_wmb(cld);
/* Flush possible transaction in flight */
for (i = 0; i < CL_DVFS_OUTPUT_PENDING_TIMEOUT / 2; i++) {
@@ -531,20 +564,20 @@ static inline void _load_lut(struct tegra_cl_dvfs *cld)
for (; i < cld->num_voltages; i++)
cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_LUT + i * 4);
- cl_dvfs_wmb(cld);
+ cl_dvfs_i2c_wmb(cld);
}
static void cl_dvfs_load_lut(struct tegra_cl_dvfs *cld)
{
- u32 val = cl_dvfs_readl(cld, CL_DVFS_OUTPUT_CFG);
+ u32 val = cl_dvfs_i2c_readl(cld, CL_DVFS_OUTPUT_CFG);
bool disable_out_for_load =
!(cld->p_data->flags & TEGRA_CL_DVFS_FLAGS_I2C_WAIT_QUIET) &&
(val & CL_DVFS_OUTPUT_CFG_I2C_ENABLE);
if (disable_out_for_load) {
val &= ~CL_DVFS_OUTPUT_CFG_I2C_ENABLE;
- cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_CFG);
- cl_dvfs_wmb(cld);
+ cl_dvfs_i2c_writel(cld, val, CL_DVFS_OUTPUT_CFG);
+ cl_dvfs_i2c_wmb(cld);
udelay(2); /* 2us (big margin) window for disable propafation */
}
@@ -552,8 +585,8 @@ static void cl_dvfs_load_lut(struct tegra_cl_dvfs *cld)
if (disable_out_for_load) {
val |= CL_DVFS_OUTPUT_CFG_I2C_ENABLE;
- cl_dvfs_writel(cld, val, CL_DVFS_OUTPUT_CFG);
- cl_dvfs_wmb(cld);
+ cl_dvfs_i2c_writel(cld, val, CL_DVFS_OUTPUT_CFG);
+ cl_dvfs_i2c_wmb(cld);
}
}
@@ -1162,7 +1195,7 @@ static void cl_dvfs_init_i2c_if(struct tegra_cl_dvfs *cld)
}
val |= (div - 1) << CL_DVFS_I2C_CLK_DIVISOR_HS_SHIFT;
cl_dvfs_writel(cld, val, CL_DVFS_I2C_CLK_DIVISOR);
- cl_dvfs_wmb(cld);
+ cl_dvfs_i2c_wmb(cld);
}
static void cl_dvfs_init_out_if(struct tegra_cl_dvfs *cld)
@@ -1205,6 +1238,7 @@ static void cl_dvfs_init_out_if(struct tegra_cl_dvfs *cld)
cld->lut_max = get_output_cap(cld, NULL);
}
+ cl_dvfs_i2c_writel(cld, 0, CL_DVFS_OUTPUT_CFG);
val = (cld->safe_output << CL_DVFS_OUTPUT_CFG_SAFE_SHIFT) |
(out_max << CL_DVFS_OUTPUT_CFG_MAX_SHIFT) |
(out_min << CL_DVFS_OUTPUT_CFG_MIN_SHIFT);
@@ -1633,7 +1667,7 @@ static int __init tegra_cl_dvfs_probe(struct platform_device *pdev)
{
int ret;
struct tegra_cl_dvfs_platform_data *p_data;
- struct resource *res;
+ struct resource *res, *res_i2c = NULL;
struct tegra_cl_dvfs *cld;
struct clk *ref_clk, *soc_clk, *i2c_clk, *safe_dvfs_clk, *dfll_clk;
@@ -1644,6 +1678,14 @@ static int __init tegra_cl_dvfs_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ if (pdev->num_resources > 1) {
+ res_i2c = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res_i2c) {
+ dev_err(&pdev->dev, "missing i2c register base\n");
+ return -ENOMEM;
+ }
+ }
+
p_data = pdev->dev.platform_data;
if (!p_data || !p_data->cfg_param || !p_data->vdd_map) {
dev_err(&pdev->dev, "missing platform data\n");
@@ -1680,6 +1722,7 @@ static int __init tegra_cl_dvfs_probe(struct platform_device *pdev)
}
cld->cl_base = IO_ADDRESS(res->start);
+ cld->cl_i2c_base = res_i2c ? IO_ADDRESS(res_i2c->start) : cld->cl_base;
cld->p_data = p_data;
cld->ref_clk = ref_clk;
cld->soc_clk = soc_clk;