mirror of
https://git.yoctoproject.org/meta-ti
synced 2026-07-16 14:27:48 +00:00
6efc27aedc
* Update to the latest PSP release (version 04.06.00.10) of the 3.2 kernel. * Rename recipe to match meta-ti kernel recipe naming convention. * Use in tree defconfig instead of providing a separate defconfig. These defconfigs were the same anyway. * Add additional crypto patches that were needed to fix suspend and resume issues. Signed-off-by: Franklin S. Cooper Jr <fcooper@ti.com> Signed-off-by: Denys Dmytriyenko <denys@ti.com>
2015 lines
69 KiB
Diff
2015 lines
69 KiB
Diff
From 35ae6b61d349e5b4efd1c6337a0d1e23b6e86899 Mon Sep 17 00:00:00 2001
|
|
From: Greg Guyotte <gguyotte@ti.com>
|
|
Date: Thu, 7 Jun 2012 18:05:31 -0500
|
|
Subject: [PATCH] am33xx: Add SmartReflex support.
|
|
|
|
This patch introduces SmartReflex support to AM33XX devices. The
|
|
purpose of SmartReflex is to optimize (lower) voltage based upon
|
|
silicon process and temperature.
|
|
|
|
The SmartReflex driver requires the silicon to be programmed with
|
|
"nTarget" EFUSE values. If the values are not present (as with
|
|
pre-RTP samples), the driver will simply fail to load and kernel
|
|
boot will continue normally.
|
|
|
|
The SR driver logs several items in the debugfs at /debug/smartreflex.
|
|
To disable SmartReflex, use the command 'echo 0 > /debug/smartreflex/autocomp'.
|
|
The node /debug/smartreflex/smartreflex0 gives information about
|
|
the CORE voltage domain, and /smartreflex1 is related to the MPU voltage
|
|
domain.
|
|
|
|
To determine the effectiveness of SmartReflex, you can compare the
|
|
initial voltage with the current voltage for a given OPP. For example,
|
|
'cat /debug/smartreflex/smartreflex1/current_voltage' gives the current
|
|
MPU voltage. Comparing that with 'cat /debug/smartreflex/smartreflex1/
|
|
initial_voltage' will show you the voltage drop associated with SR
|
|
operation.
|
|
|
|
Signed-off-by: Greg Guyotte <gguyotte@ti.com>
|
|
---
|
|
arch/arm/mach-omap2/Makefile | 1 +
|
|
arch/arm/mach-omap2/am33xx-smartreflex-class2.c | 1055 ++++++++++++++++++++
|
|
arch/arm/mach-omap2/board-am335xevm.c | 7 +
|
|
arch/arm/mach-omap2/devices.c | 269 +++++
|
|
arch/arm/mach-omap2/include/mach/board-am335xevm.h | 1 +
|
|
arch/arm/plat-omap/Kconfig | 21 +
|
|
arch/arm/plat-omap/include/plat/am33xx.h | 3 +
|
|
arch/arm/plat-omap/include/plat/smartreflex.h | 431 ++++++++
|
|
drivers/regulator/core.c | 4 +
|
|
include/linux/regulator/driver.h | 2 +-
|
|
include/linux/regulator/machine.h | 3 +-
|
|
11 files changed, 1795 insertions(+), 2 deletions(-)
|
|
create mode 100644 arch/arm/mach-omap2/am33xx-smartreflex-class2.c
|
|
create mode 100644 arch/arm/plat-omap/include/plat/smartreflex.h
|
|
|
|
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
|
|
index f275e74..c01b62d 100644
|
|
--- a/arch/arm/mach-omap2/Makefile
|
|
+++ b/arch/arm/mach-omap2/Makefile
|
|
@@ -73,6 +73,7 @@ obj-$(CONFIG_SOC_OMAPAM33XX) += cpuidle33xx.o pm33xx.o \
|
|
obj-$(CONFIG_PM_DEBUG) += pm-debug.o
|
|
obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o
|
|
obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
|
|
+obj-$(CONFIG_AM33XX_SMARTREFLEX) += am33xx-smartreflex-class2.o
|
|
|
|
AFLAGS_sleep24xx.o :=-Wa,-march=armv6
|
|
AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec)
|
|
diff --git a/arch/arm/mach-omap2/am33xx-smartreflex-class2.c b/arch/arm/mach-omap2/am33xx-smartreflex-class2.c
|
|
new file mode 100644
|
|
index 0000000..66f98b7
|
|
--- /dev/null
|
|
+++ b/arch/arm/mach-omap2/am33xx-smartreflex-class2.c
|
|
@@ -0,0 +1,1055 @@
|
|
+/*
|
|
+ * SmartReflex Voltage Control driver
|
|
+ *
|
|
+ * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/
|
|
+ * Author: Greg Guyotte <gguyotte@ti.com> (modified for AM33xx)
|
|
+ *
|
|
+ * Copyright (C) 2011 Texas Instruments, Inc. - http://www.ti.com/
|
|
+ * Author: AnilKumar Ch <anilkumar@ti.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation version 2.
|
|
+ *
|
|
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
|
+ * kind, whether express or implied; without even the implied warranty
|
|
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
+#include <linux/cpufreq.h>
|
|
+#include <linux/opp.h>
|
|
+
|
|
+#include <plat/common.h>
|
|
+#include <plat/smartreflex.h>
|
|
+
|
|
+#include "control.h"
|
|
+#include "voltage.h"
|
|
+
|
|
+#define CLK_NAME_LEN 40
|
|
+
|
|
+static inline void sr_write_reg(struct am33xx_sr *sr, int offset, u32 value,
|
|
+ u32 srid)
|
|
+{
|
|
+ writel(value, sr->sen[srid].base + offset);
|
|
+}
|
|
+
|
|
+static inline void sr_modify_reg(struct am33xx_sr *sr, int offset, u32 mask,
|
|
+ u32 value, u32 srid)
|
|
+{
|
|
+ u32 reg_val;
|
|
+
|
|
+ reg_val = readl(sr->sen[srid].base + offset);
|
|
+ reg_val &= ~mask;
|
|
+ reg_val |= (value&mask);
|
|
+
|
|
+ writel(reg_val, sr->sen[srid].base + offset);
|
|
+}
|
|
+
|
|
+static inline u32 sr_read_reg(struct am33xx_sr *sr, int offset, u32 srid)
|
|
+{
|
|
+ return readl(sr->sen[srid].base + offset);
|
|
+}
|
|
+
|
|
+static void cal_reciprocal(u32 sensor, u32 *sengain, u32 *rnsen) {
|
|
+ u32 gn, rn, mul;
|
|
+
|
|
+ for (gn = 0; gn < GAIN_MAXLIMIT; gn++) {
|
|
+ mul = 1 << (gn + 8);
|
|
+ rn = mul / sensor;
|
|
+ if (rn < R_MAXLIMIT) {
|
|
+ *sengain = gn;
|
|
+ *rnsen = rn;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static u32 cal_test_nvalue(u32 sennval, u32 senpval) {
|
|
+ u32 senpgain=0, senngain=0;
|
|
+ u32 rnsenp=0, rnsenn=0;
|
|
+
|
|
+ /* Calculating the gain and reciprocal of the SenN and SenP values */
|
|
+ cal_reciprocal(senpval, &senpgain, &rnsenp);
|
|
+ cal_reciprocal(sennval, &senngain, &rnsenn);
|
|
+
|
|
+ return (senpgain << NVALUERECIPROCAL_SENPGAIN_SHIFT) |
|
|
+ (senngain << NVALUERECIPROCAL_SENNGAIN_SHIFT) |
|
|
+ (rnsenp << NVALUERECIPROCAL_RNSENP_SHIFT) |
|
|
+ (rnsenn << NVALUERECIPROCAL_RNSENN_SHIFT);
|
|
+}
|
|
+
|
|
+static unsigned int sr_adjust_efuse_nvalue(unsigned int opp_no,
|
|
+ unsigned int orig_opp_nvalue,
|
|
+ unsigned int mv_delta) {
|
|
+ unsigned int new_opp_nvalue;
|
|
+ unsigned int senp_gain, senn_gain, rnsenp, rnsenn, pnt_delta, nnt_delta;
|
|
+ unsigned int new_senn, new_senp, senn, senp;
|
|
+
|
|
+ /* calculate SenN and SenP from the efuse value */
|
|
+ senp_gain = ((orig_opp_nvalue >> 20) & 0xf);
|
|
+ senn_gain = ((orig_opp_nvalue >> 16) & 0xf);
|
|
+ rnsenp = ((orig_opp_nvalue >> 8) & 0xff);
|
|
+ rnsenn = (orig_opp_nvalue & 0xff);
|
|
+
|
|
+ senp = ((1<<(senp_gain+8))/(rnsenp));
|
|
+ senn = ((1<<(senn_gain+8))/(rnsenn));
|
|
+
|
|
+ /* calculate the voltage delta */
|
|
+ pnt_delta = (26 * mv_delta)/10;
|
|
+ nnt_delta = (3 * mv_delta);
|
|
+
|
|
+ /* now lets add the voltage delta to the sensor values */
|
|
+ new_senn = senn + nnt_delta;
|
|
+ new_senp = senp + pnt_delta;
|
|
+
|
|
+ new_opp_nvalue = cal_test_nvalue(new_senn, new_senp);
|
|
+
|
|
+ printk("Compensating OPP%d for %dmV Orig nvalue:0x%x New nvalue:0x%x \n",
|
|
+ opp_no, mv_delta, orig_opp_nvalue, new_opp_nvalue);
|
|
+
|
|
+ return new_opp_nvalue;
|
|
+}
|
|
+
|
|
+/* irq_sr_reenable - Re-enable SR interrupts (triggered by delayed work queue)
|
|
+ * @work: pointer to work_struct embedded in am33xx_sr_sensor struct
|
|
+ *
|
|
+ * While servicing the IRQ, this function is added to the delayed work queue.
|
|
+ * This gives time for the voltage change to settle before we re-enable
|
|
+ * the interrupt.
|
|
+ */
|
|
+static void irq_sr_reenable(struct work_struct *work)
|
|
+{
|
|
+ u32 srid;
|
|
+ struct am33xx_sr_sensor *sens;
|
|
+ struct am33xx_sr *sr;
|
|
+
|
|
+ sens = container_of((void *)work, struct am33xx_sr_sensor,
|
|
+ work_reenable);
|
|
+
|
|
+ srid = sens->sr_id;
|
|
+
|
|
+ sr = container_of((void *)sens, struct am33xx_sr, sen[srid]);
|
|
+
|
|
+ dev_dbg(&sr->pdev->dev, "%s: SR %d\n", __func__, srid);
|
|
+
|
|
+ /* Must clear IRQ status */
|
|
+ sens->irq_status = 0;
|
|
+
|
|
+ /* Re-enable the interrupt */
|
|
+ sr_modify_reg(sr, IRQENABLE_SET, IRQENABLE_MCUBOUNDSINT,
|
|
+ IRQENABLE_MCUBOUNDSINT, srid);
|
|
+
|
|
+ /* Restart the module after voltage set */
|
|
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE,
|
|
+ SRCONFIG_SRENABLE, srid);
|
|
+}
|
|
+
|
|
+/* get_errvolt - get error voltage from SR error register
|
|
+ * @sr: contains SR driver data
|
|
+ * @srid: contains the srid, indicates which SR moduel lswe are using
|
|
+ *
|
|
+ * Read the error from SENSOR error register and then convert
|
|
+ * to voltage delta, return value is the voltage delta in micro
|
|
+ * volt.
|
|
+ */
|
|
+static int get_errvolt(struct am33xx_sr *sr, s32 srid)
|
|
+{
|
|
+ struct am33xx_sr_sensor *sens;
|
|
+ int senerror_reg;
|
|
+ s32 uvoltage;
|
|
+ s8 terror;
|
|
+
|
|
+ sens = &sr->sen[srid];
|
|
+
|
|
+ senerror_reg = sr_read_reg(sr, SENERROR_V2, srid);
|
|
+ senerror_reg = (senerror_reg & 0x0000FF00);
|
|
+ terror = (s8)(senerror_reg >> 8);
|
|
+
|
|
+ /* math defined in SR functional spec */
|
|
+ uvoltage = ((terror) * sr->uvoltage_step_size) >> 7;
|
|
+ uvoltage = uvoltage * sens->opp_data[sens->curr_opp].e2v_gain;
|
|
+
|
|
+ return uvoltage;
|
|
+}
|
|
+
|
|
+/* set_voltage - Schedule task for setting the voltage
|
|
+ * @work: pointer to the work structure
|
|
+ *
|
|
+ * Voltage is set based on previous voltage and calculated
|
|
+ * voltage error.
|
|
+ *
|
|
+ * Generic voltage regulator set voltage is used for changing
|
|
+ * the voltage to new value. Could potentially use voltdm_scale
|
|
+ * but at time of testing voltdm was not populated with volt_data.
|
|
+ *
|
|
+ * Disabling the module before changing the voltage, this is
|
|
+ * needed for not generating interrupt during voltage change,
|
|
+ * enabling after voltage change. This will also take care of
|
|
+ * resetting the SR registers.
|
|
+ */
|
|
+static void set_voltage(struct work_struct *work)
|
|
+{
|
|
+ struct am33xx_sr *sr;
|
|
+ int prev_volt, new_volt, i, ret;
|
|
+ s32 delta_v;
|
|
+
|
|
+ sr = container_of((void *)work, struct am33xx_sr, work);
|
|
+
|
|
+ for (i = 0; i < sr->no_of_sens; i++) {
|
|
+ if (sr->sen[i].irq_status != 1)
|
|
+ continue;
|
|
+
|
|
+ /* Get the current voltage from PMIC */
|
|
+ prev_volt = regulator_get_voltage(sr->sen[i].reg);
|
|
+
|
|
+ if (prev_volt < 0) {
|
|
+ dev_err(&sr->pdev->dev,
|
|
+ "%s: SR %d: regulator_get_voltage error %d\n",
|
|
+ __func__, i, prev_volt);
|
|
+
|
|
+ goto reenable;
|
|
+ }
|
|
+
|
|
+ delta_v = get_errvolt(sr, i);
|
|
+ new_volt = prev_volt + delta_v;
|
|
+
|
|
+ /* this is the primary output for debugging SR activity */
|
|
+ dev_dbg(&sr->pdev->dev,
|
|
+ "%s: SR %d: prev volt=%d, delta_v=%d, req_volt=%d\n",
|
|
+ __func__, i, prev_volt, delta_v, new_volt);
|
|
+
|
|
+ /* Clear the counter, SR module disable */
|
|
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE,
|
|
+ ~SRCONFIG_SRENABLE, i);
|
|
+
|
|
+ if (delta_v != 0) {
|
|
+ ret = regulator_set_voltage(sr->sen[i].reg, new_volt,
|
|
+ new_volt + sr->uvoltage_step_size);
|
|
+
|
|
+ if (ret < 0)
|
|
+ dev_err(&sr->pdev->dev,
|
|
+ "%s: regulator_set_voltage failed! (err %d)\n",
|
|
+ __func__, ret);
|
|
+ }
|
|
+reenable:
|
|
+ /* allow time for voltage to settle before re-enabling SR
|
|
+ module and interrupt */
|
|
+ schedule_delayed_work(&sr->sen[i].work_reenable,
|
|
+ msecs_to_jiffies(sr->irq_delay));
|
|
+ }
|
|
+}
|
|
+
|
|
+/* sr_class2_irq - sr irq handling
|
|
+ * @irq: Number of the irq serviced
|
|
+ * @data: data contains the SR driver structure
|
|
+ *
|
|
+ * Smartreflex IRQ handling for class2 IP, once the IRQ handler
|
|
+ * is here then disable the interrupt and re-enable after some
|
|
+ * time. This is the work around for handling both interrupts,
|
|
+ * while one got satisfied with the voltage change but not the
|
|
+ * other. The same logic helps the case where PMIC cannot set
|
|
+ * the exact voltage requested by SR IP
|
|
+ *
|
|
+ * Schedule work only if both interrupts are serviced
|
|
+ *
|
|
+ * Note that same irq handler is used for both the interrupts,
|
|
+ * needed for decision making for voltage change
|
|
+ */
|
|
+static irqreturn_t sr_class2_irq(int irq, void *data)
|
|
+{
|
|
+ u32 srid;
|
|
+ struct am33xx_sr *sr;
|
|
+ struct am33xx_sr_sensor *sr_sensor = (struct am33xx_sr_sensor *)data;
|
|
+
|
|
+ srid = sr_sensor->sr_id;
|
|
+
|
|
+ sr = container_of(data, struct am33xx_sr, sen[srid]);
|
|
+
|
|
+ sr->sen[srid].irq_status = 1;
|
|
+
|
|
+ /* Clear MCUBounds Interrupt */
|
|
+ sr_modify_reg(sr, IRQSTATUS, IRQSTATUS_MCBOUNDSINT,
|
|
+ IRQSTATUS_MCBOUNDSINT, srid);
|
|
+
|
|
+ /* Disable the interrupt and re-enable in set_voltage() */
|
|
+ sr_modify_reg(sr, IRQENABLE_CLR, IRQENABLE_MCUBOUNDSINT,
|
|
+ IRQENABLE_MCUBOUNDSINT, srid);
|
|
+
|
|
+ /* Causes set_voltage() to get called at a later time. Set_voltage()
|
|
+ will check the irq_status flags to determine which SR needs to
|
|
+ be serviced. This was previously done with schedule_work, but
|
|
+ I observed a crash in set_voltage() when changing OPPs on weak
|
|
+ silicon, which may have been related to insufficient voltage
|
|
+ settling time for OPP change. This additional delay avoids the
|
|
+ crash. */
|
|
+ schedule_delayed_work(&sr->work,
|
|
+ msecs_to_jiffies(250));
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int sr_clk_enable(struct am33xx_sr *sr, u32 srid)
|
|
+{
|
|
+ if (clk_enable(sr->sen[srid].fck) != 0) {
|
|
+ dev_err(&sr->pdev->dev, "%s: Could not enable sr_fck\n",
|
|
+ __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sr_clk_disable(struct am33xx_sr *sr, u32 srid)
|
|
+{
|
|
+ clk_disable(sr->sen[srid].fck);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int sr_set_nvalues(struct am33xx_sr *sr, u32 srid)
|
|
+{
|
|
+ int i;
|
|
+ struct am33xx_sr_sensor *sens = &sr->sen[srid];
|
|
+
|
|
+ for (i = 0; i < sens->no_of_opps; i++) {
|
|
+ /* Read nTarget value form EFUSE register*/
|
|
+ sens->opp_data[i].nvalue = readl(AM33XX_CTRL_REGADDR
|
|
+ (sens->opp_data[i].efuse_offs)) & 0xFFFFFF;
|
|
+
|
|
+ /* validate nTarget value */
|
|
+ if (sens->opp_data[i].nvalue == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* adjust nTarget based on margin in mv */
|
|
+ sens->opp_data[i].adj_nvalue = sr_adjust_efuse_nvalue(i,
|
|
+ sens->opp_data[i].nvalue,
|
|
+ sens->opp_data[i].margin);
|
|
+
|
|
+ dev_dbg(&sr->pdev->dev,
|
|
+ "NValueReciprocal value (from efuse) = %08x\n",
|
|
+ sens->opp_data[i].nvalue);
|
|
+
|
|
+ dev_dbg(&sr->pdev->dev,
|
|
+ "Adjusted NValueReciprocal value = %08x\n",
|
|
+ sens->opp_data[i].adj_nvalue);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* sr_configure - Configure SR module to work in Error generator mode
|
|
+ * @sr: contains SR driver data
|
|
+ * @srid: contains the srid, specify whether it is CORE or MPU
|
|
+ *
|
|
+ * Configure the corresponding values to SR module registers for
|
|
+ * operating SR module in Error Generator mode.
|
|
+ */
|
|
+static void sr_configure(struct am33xx_sr *sr, u32 srid)
|
|
+{
|
|
+ struct am33xx_sr_sensor *sens = &sr->sen[srid];
|
|
+
|
|
+ /* Configuring the SR module with clock length, enabling the
|
|
+ * error generator, enable SR module, enable individual N and P
|
|
+ * sensors
|
|
+ */
|
|
+ sr_write_reg(sr, SRCONFIG, (SRCLKLENGTH_125MHZ_SYSCLK |
|
|
+ SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN |
|
|
+ (sens->senn_en << SRCONFIG_SENNENABLE_V2_SHIFT) |
|
|
+ (sens->senp_en << SRCONFIG_SENPENABLE_V2_SHIFT)),
|
|
+ srid);
|
|
+
|
|
+ /* Configuring the Error Generator */
|
|
+ sr_modify_reg(sr, ERRCONFIG_V2, (SR_ERRWEIGHT_MASK |
|
|
+ SR_ERRMAXLIMIT_MASK | SR_ERRMINLIMIT_MASK),
|
|
+ ((sens->opp_data[sens->curr_opp].err_weight <<
|
|
+ ERRCONFIG_ERRWEIGHT_SHIFT) |
|
|
+ (sens->opp_data[sens->curr_opp].err_maxlimit <<
|
|
+ ERRCONFIG_ERRMAXLIMIT_SHIFT) |
|
|
+ (sens->opp_data[sens->curr_opp].err_minlimit <<
|
|
+ ERRCONFIG_ERRMINLIMIT_SHIFT)),
|
|
+ srid);
|
|
+}
|
|
+
|
|
+/* sr_enable - Enable SR module
|
|
+ * @sr: contains SR driver data
|
|
+ * @srid: contains the srid, specify whether it is CORE or MPU
|
|
+ *
|
|
+ * Enable SR module by writing nTarget values to corresponding SR
|
|
+ * NVALUERECIPROCAL register, enable the interrupt and enable SR
|
|
+ */
|
|
+static void sr_enable(struct am33xx_sr *sr, u32 srid)
|
|
+{
|
|
+ struct am33xx_sr_sensor *sens;
|
|
+
|
|
+ sens = &sr->sen[srid];
|
|
+
|
|
+ /* Check if SR is already enabled. If yes do nothing */
|
|
+ if (sr_read_reg(sr, SRCONFIG, srid) & SRCONFIG_SRENABLE)
|
|
+ return;
|
|
+
|
|
+ if (sens->opp_data[sens->curr_opp].nvalue == 0)
|
|
+ dev_err(&sr->pdev->dev,
|
|
+ "%s: OPP doesn't support SmartReflex\n", __func__);
|
|
+
|
|
+ /* Writing the nReciprocal value to the register */
|
|
+ sr_write_reg(sr, NVALUERECIPROCAL,
|
|
+ sens->opp_data[sens->curr_opp].adj_nvalue, srid);
|
|
+
|
|
+ /* Enable the interrupt */
|
|
+ sr_modify_reg(sr, IRQENABLE_SET, IRQENABLE_MCUBOUNDSINT,
|
|
+ IRQENABLE_MCUBOUNDSINT, srid);
|
|
+
|
|
+ /* SRCONFIG - enable SR */
|
|
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE,
|
|
+ SRCONFIG_SRENABLE, srid);
|
|
+}
|
|
+
|
|
+/* sr_disable - Disable SR module
|
|
+ * @sr: contains SR driver data
|
|
+ * @srid: contains the srid, specify whether it is CORE or MPU
|
|
+ *
|
|
+ * Disable SR module by disabling the interrupt and Smartreflex module
|
|
+ */
|
|
+static void sr_disable(struct am33xx_sr *sr, u32 srid)
|
|
+{
|
|
+ /* Disable the interrupt */
|
|
+ sr_modify_reg(sr, IRQENABLE_CLR, IRQENABLE_MCUBOUNDSINT,
|
|
+ IRQENABLE_MCUBOUNDSINT, srid);
|
|
+
|
|
+ /* SRCONFIG - disable SR */
|
|
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE,
|
|
+ ~SRCONFIG_SRENABLE, srid);
|
|
+}
|
|
+
|
|
+/* sr_start_vddautocomp - Start VDD auto compensation
|
|
+ * @sr: contains SR driver data
|
|
+ *
|
|
+ * This is the starting point for AVS enable from user space.
|
|
+ * Also used to re-enable SR after OPP change.
|
|
+ */
|
|
+static void sr_start_vddautocomp(struct am33xx_sr *sr)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if ((sr->sen[SR_CORE].opp_data[0].nvalue == 0) ||
|
|
+ (sr->sen[SR_MPU].opp_data[0].nvalue == 0)) {
|
|
+ dev_err(&sr->pdev->dev, "SR module not enabled, nTarget"
|
|
+ " values are not found\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (sr->autocomp_active == 1) {
|
|
+ dev_warn(&sr->pdev->dev, "SR VDD autocomp already active\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < sr->no_of_sens; i++) {
|
|
+ /* Read current regulator value and voltage */
|
|
+ sr->sen[i].init_volt_mv = regulator_get_voltage(sr->sen[i].reg);
|
|
+
|
|
+ dev_dbg(&sr->pdev->dev, "%s: regulator %d, init_volt = %d\n",
|
|
+ __func__, i, sr->sen[i].init_volt_mv);
|
|
+
|
|
+ if (sr_clk_enable(sr, i))
|
|
+ return;
|
|
+ sr_configure(sr, i);
|
|
+ sr_enable(sr, i);
|
|
+ }
|
|
+
|
|
+ sr->autocomp_active = 1;
|
|
+}
|
|
+
|
|
+/* sr_stop_vddautocomp - Stop VDD auto compensation
|
|
+ * @sr: contains SR driver data
|
|
+ *
|
|
+ * This is the ending point during SR disable from user space.
|
|
+ * Also used to disable SR after OPP change.
|
|
+ */
|
|
+static void sr_stop_vddautocomp(struct am33xx_sr *sr)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (sr->autocomp_active == 0) {
|
|
+ dev_warn(&sr->pdev->dev, "SR VDD autocomp is not active\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* cancel bottom half interrupt handlers that haven't run yet */
|
|
+ cancel_delayed_work_sync(&sr->work);
|
|
+
|
|
+ for (i = 0; i < sr->no_of_sens; i++) {
|
|
+ /* cancel any outstanding SR IRQ re-enables on work queue */
|
|
+ cancel_delayed_work_sync(&sr->sen[i].work_reenable);
|
|
+ sr_disable(sr, i);
|
|
+ sr_clk_disable(sr, i);
|
|
+ }
|
|
+
|
|
+ sr->autocomp_active = 0;
|
|
+}
|
|
+
|
|
+/* am33xx_sr_autocomp_show - Store user input value and stop SR
|
|
+ * @data: contains SR driver data
|
|
+ * @val: pointer to store autocomp_active status
|
|
+ *
|
|
+ * This is the Debug Fs enteries to show whether SR is enabled
|
|
+ * or disabled
|
|
+ */
|
|
+static int am33xx_sr_autocomp_show(void *data, u64 *val)
|
|
+{
|
|
+ struct am33xx_sr *sr_info = (struct am33xx_sr *) data;
|
|
+
|
|
+ *val = (u64) sr_info->autocomp_active;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int am33xx_sr_margin_show(void *data, u64 *val)
|
|
+{
|
|
+ struct am33xx_sr_opp_data *sr_opp_data = (struct am33xx_sr_opp_data *)data;
|
|
+
|
|
+ *val = (u64) sr_opp_data->margin;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int am33xx_sr_margin_update(void *data, u64 val)
|
|
+{
|
|
+ struct am33xx_sr_opp_data *sr_opp_data =
|
|
+ (struct am33xx_sr_opp_data *)data;
|
|
+ struct am33xx_sr_sensor *sr_sensor;
|
|
+ struct am33xx_sr *sr_info;
|
|
+
|
|
+ /* work back to the sr_info pointer */
|
|
+ sr_sensor = container_of((void *)sr_opp_data, struct am33xx_sr_sensor,
|
|
+ opp_data[sr_opp_data->opp_id]);
|
|
+
|
|
+ sr_info = container_of((void *)sr_sensor, struct am33xx_sr,
|
|
+ sen[sr_sensor->sr_id]);
|
|
+
|
|
+ /* store the value of margin */
|
|
+ sr_opp_data->margin = (s32)val;
|
|
+
|
|
+ dev_warn(&sr_info->pdev->dev, "%s: new margin=%d, srid=%d, opp=%d\n",
|
|
+ __func__, sr_opp_data->margin, sr_sensor->sr_id,
|
|
+ sr_opp_data->opp_id);
|
|
+
|
|
+ /* updata ntarget values based upon new margin */
|
|
+ if (sr_set_nvalues(sr_info, sr_sensor->sr_id) == -EINVAL)
|
|
+ dev_err(&sr_info->pdev->dev,
|
|
+ "%s: Zero NValue read from EFUSE\n", __func__);
|
|
+
|
|
+ /* restart SmartReflex to adapt to new values */
|
|
+ sr_stop_vddautocomp(sr_info);
|
|
+ sr_start_vddautocomp(sr_info);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* am33xx_sr_autocomp_store - Store user input and start SR
|
|
+ * @data: contains SR driver data
|
|
+ * @val: contains the value pased by user
|
|
+ *
|
|
+ * This is the Debug Fs enteries to store user input and
|
|
+ * enable smartreflex.
|
|
+ */
|
|
+static int am33xx_sr_autocomp_store(void *data, u64 val)
|
|
+{
|
|
+ struct am33xx_sr *sr_info = (struct am33xx_sr *) data;
|
|
+
|
|
+ /* Sanity check */
|
|
+ if (val && (val != 1)) {
|
|
+ dev_warn(&sr_info->pdev->dev, "%s: Invalid argument %llu\n",
|
|
+ __func__, val);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (!val) {
|
|
+ sr_info->disabled_by_user = 1;
|
|
+ sr_stop_vddautocomp(sr_info);
|
|
+ }
|
|
+ else {
|
|
+ sr_info->disabled_by_user = 0;
|
|
+ sr_start_vddautocomp(sr_info);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+DEFINE_SIMPLE_ATTRIBUTE(sr_fops, am33xx_sr_autocomp_show,
|
|
+ am33xx_sr_autocomp_store, "%llu\n");
|
|
+
|
|
+/* sr_curr_volt_show - Show current voltage value
|
|
+ * @data: contains SR driver data
|
|
+ * @val: pointer to store current voltage value
|
|
+ *
|
|
+ * Read the current voltage value and display the same on console
|
|
+ * This is used in debugfs entries
|
|
+ */
|
|
+static int am33xx_sr_curr_volt_show(void *data, u64 *val)
|
|
+{
|
|
+ struct am33xx_sr_sensor *sr_sensor = (struct am33xx_sr_sensor *) data;
|
|
+
|
|
+ *val = (u64) regulator_get_voltage(sr_sensor->reg);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+DEFINE_SIMPLE_ATTRIBUTE(curr_volt_fops, am33xx_sr_curr_volt_show,
|
|
+ NULL, "%llu\n");
|
|
+
|
|
+DEFINE_SIMPLE_ATTRIBUTE(margin_fops, am33xx_sr_margin_show,
|
|
+ am33xx_sr_margin_update, "%llu\n");
|
|
+
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
+/* sr_debugfs_entries - Create debugfs entries
|
|
+ * @sr_info: contains SR driver data
|
|
+ *
|
|
+ * Create debugfs entries, which is exposed to user for knowing
|
|
+ * the current status. Some of the parameters can change during
|
|
+ * run time
|
|
+ */
|
|
+static int sr_debugfs_entries(struct am33xx_sr *sr_info)
|
|
+{
|
|
+ struct am33xx_sr_sensor *sens;
|
|
+ struct dentry *dbg_dir, *sen_dir, *opp_dir;
|
|
+ int i, j;
|
|
+
|
|
+ dbg_dir = debugfs_create_dir("smartreflex", NULL);
|
|
+ if (IS_ERR(dbg_dir)) {
|
|
+ dev_err(&sr_info->pdev->dev, "%s: Unable to create debugfs"
|
|
+ " directory\n", __func__);
|
|
+ return PTR_ERR(dbg_dir);
|
|
+ }
|
|
+
|
|
+ (void) debugfs_create_file("autocomp", S_IRUGO | S_IWUGO, dbg_dir,
|
|
+ (void *)sr_info, &sr_fops);
|
|
+ (void) debugfs_create_u32("interrupt_delay", S_IRUGO | S_IWUGO,
|
|
+ dbg_dir, &sr_info->irq_delay);
|
|
+
|
|
+ for (i = 0; i < sr_info->no_of_sens; i++) {
|
|
+ sens = &sr_info->sen[i];
|
|
+ sen_dir = debugfs_create_dir(sens->name, dbg_dir);
|
|
+ if (IS_ERR(sen_dir)) {
|
|
+ dev_err(&sr_info->pdev->dev, "%s: Unable to create"
|
|
+ " debugfs directory\n", __func__);
|
|
+ return PTR_ERR(sen_dir);
|
|
+ }
|
|
+
|
|
+ (void)debugfs_create_u32("initial_voltage", S_IRUGO, sen_dir,
|
|
+ &sens->init_volt_mv);
|
|
+ (void)debugfs_create_file("current_voltage", S_IRUGO, sen_dir,
|
|
+ (void *)sens, &curr_volt_fops);
|
|
+
|
|
+ for (j = 0; j < sr_info->sen[i].no_of_opps; j++) {
|
|
+ char tmp[20];
|
|
+
|
|
+ sprintf(&tmp[0], "opp%d", j);
|
|
+ opp_dir = debugfs_create_dir(tmp, sen_dir);
|
|
+ if (IS_ERR(opp_dir)) {
|
|
+ dev_err(&sr_info->pdev->dev,
|
|
+ "%s: Unable to create debugfs directory\n",
|
|
+ __func__);
|
|
+ return PTR_ERR(opp_dir);
|
|
+ }
|
|
+
|
|
+ (void)debugfs_create_file("margin", S_IRUGO | S_IWUGO,
|
|
+ opp_dir, (void *)&sens->opp_data[j],
|
|
+ &margin_fops);
|
|
+ (void)debugfs_create_x32("err2voltgain",
|
|
+ S_IRUGO | S_IWUGO,
|
|
+ opp_dir,
|
|
+ &sens->opp_data[j].e2v_gain);
|
|
+ (void)debugfs_create_x32("nvalue", S_IRUGO,
|
|
+ opp_dir,
|
|
+ &sens->opp_data[j].nvalue);
|
|
+ (void)debugfs_create_x32("adj_nvalue", S_IRUGO,
|
|
+ opp_dir,
|
|
+ &sens->opp_data[j].adj_nvalue);
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+#else
|
|
+static int sr_debugfs_entries(struct am33xx_sr *sr_info)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_CPU_FREQ
|
|
+
|
|
+/* Find and return current OPP. This should change to use system APIs,
|
|
+ but voltdm is not currently populated, and opp APIs are also not working. */
|
|
+static int get_current_opp(struct am33xx_sr *sr, u32 srid, u32 freq) {
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < sr->sen[srid].no_of_opps; i++) {
|
|
+ if (sr->sen[srid].opp_data[i].frequency == freq)
|
|
+ return i;
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int am33xx_sr_cpufreq_transition(struct notifier_block *nb,
|
|
+ unsigned long val, void *data)
|
|
+{
|
|
+ struct am33xx_sr *sr;
|
|
+ struct cpufreq_freqs *cpu;
|
|
+
|
|
+ sr = container_of(nb, struct am33xx_sr, freq_transition);
|
|
+
|
|
+ /* We are required to disable SR while OPP change is occurring */
|
|
+ if (val == CPUFREQ_PRECHANGE) {
|
|
+ dev_dbg(&sr->pdev->dev, "%s: prechange\n", __func__);
|
|
+ sr_stop_vddautocomp(sr);
|
|
+ } else if (val == CPUFREQ_POSTCHANGE) {
|
|
+ cpu = (struct cpufreq_freqs *)data;
|
|
+ dev_dbg(&sr->pdev->dev,
|
|
+ "%s: postchange, cpu=%d, old=%d, new=%d\n",
|
|
+ __func__, cpu->cpu, cpu->old, cpu->new);
|
|
+
|
|
+ /* update current OPP */
|
|
+ sr->sen[SR_MPU].curr_opp = get_current_opp(sr, SR_MPU,
|
|
+ cpu->new*1000);
|
|
+ if (sr->sen[SR_MPU].curr_opp == -EINVAL) {
|
|
+ dev_err(&sr->pdev->dev, "%s: cannot determine opp\n",
|
|
+ __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&sr->pdev->dev, "%s: postchange, new opp=%d\n",
|
|
+ __func__, sr->sen[SR_MPU].curr_opp);
|
|
+
|
|
+ /* this handles the case when the user has disabled SR via
|
|
+ debugfs, therefore we do not want to enable SR */
|
|
+ if (sr->disabled_by_user == 0)
|
|
+ sr_start_vddautocomp(sr);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int am33xx_sr_cpufreq_register(struct am33xx_sr *sr)
|
|
+{
|
|
+ sr->freq_transition.notifier_call = am33xx_sr_cpufreq_transition;
|
|
+
|
|
+ return cpufreq_register_notifier(&sr->freq_transition,
|
|
+ CPUFREQ_TRANSITION_NOTIFIER);
|
|
+}
|
|
+
|
|
+static inline void am33xx_sr_cpufreq_deregister(struct am33xx_sr *sr)
|
|
+{
|
|
+ cpufreq_unregister_notifier(&sr->freq_transition,
|
|
+ CPUFREQ_TRANSITION_NOTIFIER);
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+static int __init am33xx_sr_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct am33xx_sr *sr_info;
|
|
+ struct am33xx_sr_platform_data *pdata;
|
|
+ struct resource *res[MAX_SENSORS];
|
|
+ int irq;
|
|
+ int ret;
|
|
+ int i,j;
|
|
+
|
|
+ sr_info = kzalloc(sizeof(struct am33xx_sr), GFP_KERNEL);
|
|
+ if (!sr_info) {
|
|
+ dev_err(&pdev->dev, "%s: unable to allocate sr_info\n",
|
|
+ __func__);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ pdata = pdev->dev.platform_data;
|
|
+ if (!pdata) {
|
|
+ dev_err(&pdev->dev, "%s: platform data missing\n", __func__);
|
|
+ ret = -EINVAL;
|
|
+ goto err_free_sr_info;
|
|
+ }
|
|
+
|
|
+ sr_info->pdev = pdev;
|
|
+ sr_info->sen[SR_CORE].name = "smartreflex0";
|
|
+ sr_info->sen[SR_MPU].name = "smartreflex1";
|
|
+ sr_info->ip_type = pdata->ip_type;
|
|
+ sr_info->irq_delay = pdata->irq_delay;
|
|
+ sr_info->no_of_sens = pdata->no_of_sens;
|
|
+ sr_info->no_of_vds = pdata->no_of_vds;
|
|
+ sr_info->uvoltage_step_size = pdata->vstep_size_uv;
|
|
+ sr_info->autocomp_active = false;
|
|
+ sr_info->disabled_by_user = false;
|
|
+
|
|
+ for (i = 0; i < sr_info->no_of_sens; i++) {
|
|
+ u32 curr_freq=0;
|
|
+
|
|
+ sr_info->sen[i].reg_name = pdata->vd_name[i];
|
|
+
|
|
+ /* this should be determined from voltdm or opp layer, but
|
|
+ those approaches are not working */
|
|
+ sr_info->sen[i].no_of_opps = pdata->sr_sdata[i].no_of_opps;
|
|
+ sr_info->sen[i].sr_id = i;
|
|
+
|
|
+ /* Reading per OPP Values */
|
|
+ for (j = 0; j < sr_info->sen[i].no_of_opps; j++) {
|
|
+ sr_info->sen[i].opp_data[j].efuse_offs =
|
|
+ pdata->sr_sdata[i].sr_opp_data[j].efuse_offs;
|
|
+ sr_info->sen[i].opp_data[j].e2v_gain =
|
|
+ pdata->sr_sdata[i].sr_opp_data[j].e2v_gain;
|
|
+ sr_info->sen[i].opp_data[j].err_weight =
|
|
+ pdata->sr_sdata[i].sr_opp_data[j].err_weight;
|
|
+ sr_info->sen[i].opp_data[j].err_minlimit =
|
|
+ pdata->sr_sdata[i].sr_opp_data[j].err_minlimit;
|
|
+ sr_info->sen[i].opp_data[j].err_maxlimit =
|
|
+ pdata->sr_sdata[i].sr_opp_data[j].err_maxlimit;
|
|
+ sr_info->sen[i].opp_data[j].margin =
|
|
+ pdata->sr_sdata[i].sr_opp_data[j].margin;
|
|
+ sr_info->sen[i].opp_data[j].nominal_volt =
|
|
+ pdata->sr_sdata[i].sr_opp_data[j].nominal_volt;
|
|
+ sr_info->sen[i].opp_data[j].frequency =
|
|
+ pdata->sr_sdata[i].sr_opp_data[j].frequency;
|
|
+ sr_info->sen[i].opp_data[j].opp_id = j;
|
|
+ }
|
|
+
|
|
+ if (i == SR_MPU) {
|
|
+ /* hardcoded CPU NR */
|
|
+ curr_freq = cpufreq_get(0);
|
|
+
|
|
+ /* update current OPP */
|
|
+ sr_info->sen[i].curr_opp = get_current_opp(sr_info, i,
|
|
+ curr_freq*1000);
|
|
+ if (sr_info->sen[i].curr_opp == -EINVAL) {
|
|
+ dev_err(&sr_info->pdev->dev,
|
|
+ "%s: cannot determine opp\n",__func__);
|
|
+ ret = -EINVAL;
|
|
+ goto err_free_sr_info;
|
|
+ }
|
|
+ } else {
|
|
+ sr_info->sen[i].curr_opp =
|
|
+ pdata->sr_sdata[i].default_opp;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&pdev->dev,
|
|
+ "%s: SR%d, curr_opp=%d, no_of_opps=%d, step_size=%d\n",
|
|
+ __func__, i, sr_info->sen[i].curr_opp,
|
|
+ sr_info->sen[i].no_of_opps,
|
|
+ sr_info->uvoltage_step_size);
|
|
+
|
|
+ ret = sr_set_nvalues(sr_info, i);
|
|
+ if (ret == -EINVAL) {
|
|
+ dev_err(&sr_info->pdev->dev,
|
|
+ "%s: Zero NValue read from EFUSE\n", __func__);
|
|
+ goto err_free_sr_info;
|
|
+ }
|
|
+
|
|
+ INIT_DELAYED_WORK(&sr_info->sen[i].work_reenable,
|
|
+ irq_sr_reenable);
|
|
+
|
|
+ sr_info->res_name[i] = kzalloc(CLK_NAME_LEN + 1, GFP_KERNEL);
|
|
+
|
|
+ /* resources */
|
|
+ res[i] = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
|
+ sr_info->sen[i].name);
|
|
+ if (!res[i]) {
|
|
+ dev_err(&pdev->dev, "%s: no mem resource\n", __func__);
|
|
+ ret = -ENOENT;
|
|
+ goto err_free_mem;
|
|
+ }
|
|
+
|
|
+ irq = platform_get_irq_byname(pdev, sr_info->sen[i].name);
|
|
+ if (irq < 0) {
|
|
+ dev_err(&pdev->dev, "Can't get interrupt resource\n");
|
|
+ ret = irq;
|
|
+ goto err_free_mem;
|
|
+ }
|
|
+ sr_info->sen[i].irq = irq;
|
|
+
|
|
+ res[i] = request_mem_region(res[i]->start,
|
|
+ resource_size(res[i]), pdev->name);
|
|
+ if (!res[i]) {
|
|
+ dev_err(&pdev->dev, "can't request mem region\n");
|
|
+ ret = -EBUSY;
|
|
+ goto err_free_mem;
|
|
+ }
|
|
+
|
|
+ sr_info->sen[i].base = ioremap(res[i]->start,
|
|
+ resource_size(res[i]));
|
|
+ if (!sr_info->sen[i].base) {
|
|
+ dev_err(&pdev->dev, "%s: ioremap fail\n", __func__);
|
|
+ ret = -ENOMEM;
|
|
+ goto err_release_mem;
|
|
+ }
|
|
+
|
|
+ strcat(sr_info->res_name[i], sr_info->sen[i].name);
|
|
+ strcat(sr_info->res_name[i], "_fck");
|
|
+
|
|
+ sr_info->sen[i].fck = clk_get(NULL, sr_info->res_name[i]);
|
|
+ if (IS_ERR(sr_info->sen[i].fck)) {
|
|
+ dev_err(&pdev->dev, "%s: Could not get sr fck\n",
|
|
+ __func__);
|
|
+ ret = PTR_ERR(sr_info->sen[i].fck);
|
|
+ goto err_unmap;
|
|
+ }
|
|
+
|
|
+ ret = request_irq(sr_info->sen[i].irq, sr_class2_irq,
|
|
+ IRQF_DISABLED, sr_info->sen[i].name,
|
|
+ (void *)&sr_info->sen[i]);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "%s: Could not install SR ISR\n",
|
|
+ __func__);
|
|
+ goto err_put_clock;
|
|
+ }
|
|
+
|
|
+ sr_info->sen[i].senn_en = pdata->sr_sdata[i].senn_mod;
|
|
+ sr_info->sen[i].senp_en = pdata->sr_sdata[i].senp_mod;
|
|
+
|
|
+ sr_info->sen[i].reg =
|
|
+ regulator_get(NULL, sr_info->sen[i].reg_name);
|
|
+ if (IS_ERR(sr_info->sen[i].reg)) {
|
|
+ ret = -EINVAL;
|
|
+ goto err_free_irq;
|
|
+ }
|
|
+
|
|
+ /* Read current regulator value and voltage */
|
|
+ sr_info->sen[i].init_volt_mv =
|
|
+ regulator_get_voltage(sr_info->sen[i].reg);
|
|
+
|
|
+ dev_dbg(&pdev->dev, "%s: regulator %d, init_volt = %d\n",
|
|
+ __func__, i, sr_info->sen[i].init_volt_mv);
|
|
+ } /* for() */
|
|
+
|
|
+ /* set_voltage() will be used as the bottom half IRQ handler */
|
|
+ INIT_DELAYED_WORK(&sr_info->work, set_voltage);
|
|
+
|
|
+#ifdef CONFIG_CPU_FREQ
|
|
+ ret = am33xx_sr_cpufreq_register(sr_info);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "failed to register cpufreq\n");
|
|
+ goto err_reg_put;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* debugfs entries */
|
|
+ ret = sr_debugfs_entries(sr_info);
|
|
+ if (ret)
|
|
+ dev_warn(&pdev->dev, "%s: Debugfs entries are not created\n",
|
|
+ __func__);
|
|
+
|
|
+ platform_set_drvdata(pdev, sr_info);
|
|
+
|
|
+ dev_info(&pdev->dev, "%s: Driver initialized\n", __func__);
|
|
+
|
|
+ /* disabled_by_user used to ensure SR doesn't come on via CPUFREQ
|
|
+ scaling if user has disabled SR via debugfs on enable_on_init */
|
|
+ if (pdata->enable_on_init)
|
|
+ sr_start_vddautocomp(sr_info);
|
|
+ else
|
|
+ sr_info->disabled_by_user = 1;
|
|
+
|
|
+ return ret;
|
|
+
|
|
+#ifdef CONFIG_CPU_FREQ
|
|
+ am33xx_sr_cpufreq_deregister(sr_info);
|
|
+#endif
|
|
+
|
|
+err_reg_put:
|
|
+ i--; /* back up i by one to walk back through the for loop */
|
|
+ regulator_put(sr_info->sen[i].reg);
|
|
+err_free_irq:
|
|
+ free_irq(sr_info->sen[i].irq, (void *)sr_info);
|
|
+err_put_clock:
|
|
+ clk_put(sr_info->sen[i].fck);
|
|
+err_unmap:
|
|
+ iounmap(sr_info->sen[i].base);
|
|
+err_release_mem:
|
|
+ release_mem_region(res[i]->start, resource_size(res[i]));
|
|
+err_free_mem:
|
|
+ kfree(sr_info->res_name[i]);
|
|
+ /* unwind back through the for loop */
|
|
+ if (i != 0) {
|
|
+ goto err_reg_put;
|
|
+ }
|
|
+
|
|
+err_free_sr_info:
|
|
+ kfree(sr_info);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int __devexit am33xx_sr_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct am33xx_sr *sr_info;
|
|
+ struct resource *res[MAX_SENSORS];
|
|
+ int irq;
|
|
+ int i;
|
|
+
|
|
+ sr_info = dev_get_drvdata(&pdev->dev);
|
|
+ if (!sr_info) {
|
|
+ dev_err(&pdev->dev, "%s: sr_info missing\n", __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (sr_info->autocomp_active)
|
|
+ sr_stop_vddautocomp(sr_info);
|
|
+
|
|
+#ifdef CONFIG_CPU_FREQ
|
|
+ am33xx_sr_cpufreq_deregister(sr_info);
|
|
+#endif
|
|
+
|
|
+ for (i = 0; i < sr_info->no_of_sens; i++) {
|
|
+ regulator_put(sr_info->sen[i].reg);
|
|
+ irq = platform_get_irq_byname(pdev, sr_info->sen[i].name);
|
|
+ free_irq(irq, (void *)sr_info);
|
|
+ clk_put(sr_info->sen[i].fck);
|
|
+ iounmap(sr_info->sen[i].base);
|
|
+ res[i] = platform_get_resource_byname(pdev,
|
|
+ IORESOURCE_MEM, sr_info->sen[i].name);
|
|
+ release_mem_region(res[i]->start, resource_size(res[i]));
|
|
+ kfree(sr_info->res_name[i]);
|
|
+ }
|
|
+
|
|
+ kfree(sr_info);
|
|
+
|
|
+ dev_info(&pdev->dev, "%s: SR has been removed\n", __func__);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct platform_driver smartreflex_driver = {
|
|
+ .driver = {
|
|
+ .name = "smartreflex",
|
|
+ .owner = THIS_MODULE,
|
|
+ },
|
|
+ .remove = am33xx_sr_remove,
|
|
+};
|
|
+
|
|
+static int __init sr_init(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = platform_driver_probe(&smartreflex_driver, am33xx_sr_probe);
|
|
+ if (ret) {
|
|
+ pr_err("%s: platform driver register failed\n", __func__);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit sr_exit(void)
|
|
+{
|
|
+ platform_driver_unregister(&smartreflex_driver);
|
|
+}
|
|
+late_initcall(sr_init);
|
|
+module_exit(sr_exit);
|
|
+
|
|
+MODULE_DESCRIPTION("AM33XX Smartreflex Class2 Driver");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
+MODULE_AUTHOR("Texas Instruments Inc");
|
|
diff --git a/arch/arm/mach-omap2/board-am335xevm.c b/arch/arm/mach-omap2/board-am335xevm.c
|
|
index 0bcadd7..6e1c026 100644
|
|
--- a/arch/arm/mach-omap2/board-am335xevm.c
|
|
+++ b/arch/arm/mach-omap2/board-am335xevm.c
|
|
@@ -1410,6 +1410,7 @@ static struct regulator_init_data tps65217_regulator_data[] = {
|
|
.num_consumer_supplies = ARRAY_SIZE(tps65217_dcdc2_consumers),
|
|
.consumer_supplies = tps65217_dcdc2_consumers,
|
|
.driver_data = &dcdc2_ramp_delay,
|
|
+ .ignore_check_consumers = 1,
|
|
},
|
|
|
|
/* dcdc3 */
|
|
@@ -1424,6 +1425,7 @@ static struct regulator_init_data tps65217_regulator_data[] = {
|
|
},
|
|
.num_consumer_supplies = ARRAY_SIZE(tps65217_dcdc3_consumers),
|
|
.consumer_supplies = tps65217_dcdc3_consumers,
|
|
+ .ignore_check_consumers = 1,
|
|
},
|
|
|
|
/* ldo1 */
|
|
@@ -2214,6 +2216,9 @@ static void am335x_evm_setup(struct memory_accessor *mem_acc, void *context)
|
|
goto out;
|
|
}
|
|
|
|
+ /* SmartReflex also requires board information. */
|
|
+ am33xx_sr_init();
|
|
+
|
|
return;
|
|
|
|
out:
|
|
@@ -2265,6 +2270,7 @@ static struct regulator_init_data am335x_vdd1 = {
|
|
},
|
|
.num_consumer_supplies = ARRAY_SIZE(am335x_vdd1_supply),
|
|
.consumer_supplies = am335x_vdd1_supply,
|
|
+ .ignore_check_consumers = 1,
|
|
};
|
|
|
|
static struct regulator_consumer_supply am335x_vdd2_supply[] = {
|
|
@@ -2281,6 +2287,7 @@ static struct regulator_init_data am335x_vdd2 = {
|
|
},
|
|
.num_consumer_supplies = ARRAY_SIZE(am335x_vdd2_supply),
|
|
.consumer_supplies = am335x_vdd2_supply,
|
|
+ .ignore_check_consumers = 1,
|
|
};
|
|
|
|
static struct tps65910_board am335x_tps65910_info = {
|
|
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
|
|
index 6113654..ebf0d9e 100644
|
|
--- a/arch/arm/mach-omap2/devices.c
|
|
+++ b/arch/arm/mach-omap2/devices.c
|
|
@@ -52,6 +52,7 @@
|
|
#include <plat/config_pwm.h>
|
|
#include <plat/cpu.h>
|
|
#include <plat/gpmc.h>
|
|
+#include <plat/smartreflex.h>
|
|
#include <plat/am33xx.h>
|
|
|
|
/* LCD controller similar DA8xx */
|
|
@@ -60,10 +61,28 @@
|
|
#include "mux.h"
|
|
#include "control.h"
|
|
#include "devices.h"
|
|
+#include "omap_opp_data.h"
|
|
|
|
#define L3_MODULES_MAX_LEN 12
|
|
#define L3_MODULES 3
|
|
|
|
+static unsigned int am33xx_evmid;
|
|
+
|
|
+/*
|
|
+ * am33xx_evmid_fillup - set up board evmid
|
|
+ * @evmid - evm id which needs to be configured
|
|
+ *
|
|
+ * This function is called to configure board evm id.
|
|
+ * IA Motor Control EVM needs special setting of MAC PHY Id.
|
|
+ * This function is called when IA Motor Control EVM is detected
|
|
+ * during boot-up.
|
|
+ */
|
|
+void am33xx_evmid_fillup(unsigned int evmid)
|
|
+{
|
|
+ am33xx_evmid = evmid;
|
|
+ return;
|
|
+}
|
|
+
|
|
static int __init omap3_l3_init(void)
|
|
{
|
|
int l;
|
|
@@ -1226,6 +1245,256 @@ static struct platform_device am335x_sgx = {
|
|
|
|
#endif
|
|
|
|
+#ifdef CONFIG_AM33XX_SMARTREFLEX
|
|
+
|
|
+/* smartreflex platform data */
|
|
+
|
|
+/* The values below are based upon silicon characterization data.
|
|
+ * Each OPP and sensor combination potentially has different values.
|
|
+ * The values of ERR2VOLT_GAIN and ERR_MIN_LIMIT also change based on
|
|
+ * the PMIC step size. Values have been given to cover the AM335 EVM
|
|
+ * (12.5mV step) and the Beaglebone (25mV step). If the step
|
|
+ * size changes, you should update these values, and don't forget to
|
|
+ * change the step size in the platform data structure, am33xx_sr_pdata.
|
|
+ */
|
|
+
|
|
+#define AM33XX_SR0_OPP50_CNTRL_OFFSET 0x07B8
|
|
+#define AM33XX_SR0_OPP50_EVM_ERR2VOLT_GAIN 0xC
|
|
+#define AM33XX_SR0_OPP50_EVM_ERR_MIN_LIMIT 0xF5
|
|
+#define AM33XX_SR0_OPP50_BB_ERR2VOLT_GAIN 0x6
|
|
+#define AM33XX_SR0_OPP50_BB_ERR_MIN_LIMIT 0xEA
|
|
+#define AM33XX_SR0_OPP50_ERR_MAX_LIMIT 0x2
|
|
+#define AM33XX_SR0_OPP50_ERR_WEIGHT 0x4
|
|
+#define AM33XX_SR0_OPP50_MARGIN 0
|
|
+
|
|
+#define AM33XX_SR0_OPP100_CNTRL_OFFSET 0x07BC
|
|
+#define AM33XX_SR0_OPP100_EVM_ERR2VOLT_GAIN 0x12
|
|
+#define AM33XX_SR0_OPP100_EVM_ERR_MIN_LIMIT 0xF8
|
|
+#define AM33XX_SR0_OPP100_BB_ERR2VOLT_GAIN 0x9
|
|
+#define AM33XX_SR0_OPP100_BB_ERR_MIN_LIMIT 0xF1
|
|
+#define AM33XX_SR0_OPP100_ERR_MAX_LIMIT 0x2
|
|
+#define AM33XX_SR0_OPP100_ERR_WEIGHT 0x4
|
|
+#define AM33XX_SR0_OPP100_MARGIN 0
|
|
+
|
|
+#define AM33XX_SR1_OPP50_CNTRL_OFFSET 0x0770
|
|
+#define AM33XX_SR1_OPP50_EVM_ERR2VOLT_GAIN 0x5
|
|
+#define AM33XX_SR1_OPP50_EVM_ERR_MIN_LIMIT 0xE6
|
|
+#define AM33XX_SR1_OPP50_BB_ERR2VOLT_GAIN 0x2
|
|
+#define AM33XX_SR1_OPP50_BB_ERR_MIN_LIMIT 0xC0
|
|
+#define AM33XX_SR1_OPP50_ERR_MAX_LIMIT 0x2
|
|
+#define AM33XX_SR1_OPP50_ERR_WEIGHT 0x4
|
|
+#define AM33XX_SR1_OPP50_MARGIN 0
|
|
+
|
|
+#define AM33XX_SR1_OPP100_CNTRL_OFFSET 0x0774
|
|
+#define AM33XX_SR1_OPP100_EVM_ERR2VOLT_GAIN 0x8
|
|
+#define AM33XX_SR1_OPP100_EVM_ERR_MIN_LIMIT 0xF0
|
|
+#define AM33XX_SR1_OPP100_BB_ERR2VOLT_GAIN 0x4
|
|
+#define AM33XX_SR1_OPP100_BB_ERR_MIN_LIMIT 0xDF
|
|
+#define AM33XX_SR1_OPP100_ERR_MAX_LIMIT 0x2
|
|
+#define AM33XX_SR1_OPP100_ERR_WEIGHT 0x4
|
|
+#define AM33XX_SR1_OPP100_MARGIN 0
|
|
+
|
|
+#define AM33XX_SR1_OPP120_CNTRL_OFFSET 0x0778
|
|
+#define AM33XX_SR1_OPP120_EVM_ERR2VOLT_GAIN 0xB
|
|
+#define AM33XX_SR1_OPP120_EVM_ERR_MIN_LIMIT 0xF4
|
|
+#define AM33XX_SR1_OPP120_BB_ERR2VOLT_GAIN 0x5
|
|
+#define AM33XX_SR1_OPP120_BB_ERR_MIN_LIMIT 0xE6
|
|
+#define AM33XX_SR1_OPP120_ERR_MAX_LIMIT 0x2
|
|
+#define AM33XX_SR1_OPP120_ERR_WEIGHT 0x4
|
|
+#define AM33XX_SR1_OPP120_MARGIN 0
|
|
+
|
|
+#define AM33XX_SR1_OPPTURBO_CNTRL_OFFSET 0x077C
|
|
+#define AM33XX_SR1_OPPTURBO_EVM_ERR2VOLT_GAIN 0xC
|
|
+#define AM33XX_SR1_OPPTURBO_EVM_ERR_MIN_LIMIT 0xF5
|
|
+#define AM33XX_SR1_OPPTURBO_BB_ERR2VOLT_GAIN 0x6
|
|
+#define AM33XX_SR1_OPPTURBO_BB_ERR_MIN_LIMIT 0xEA
|
|
+#define AM33XX_SR1_OPPTURBO_ERR_MAX_LIMIT 0x2
|
|
+#define AM33XX_SR1_OPPTURBO_ERR_WEIGHT 0x4
|
|
+#define AM33XX_SR1_OPPTURBO_MARGIN 0
|
|
+
|
|
+/* the voltages and frequencies should probably be defined in opp3xxx_data.c.
|
|
+ Once SR is integrated to the mainline driver, and voltdm is working
|
|
+ correctly in AM335x, these can be removed. */
|
|
+#define AM33XX_VDD_MPU_OPP50_UV 950000
|
|
+#define AM33XX_VDD_MPU_OPP100_UV 1100000
|
|
+#define AM33XX_VDD_MPU_OPP120_UV 1200000
|
|
+#define AM33XX_VDD_MPU_OPPTURBO_UV 1260000
|
|
+#define AM33XX_VDD_CORE_OPP50_UV 950000
|
|
+#define AM33XX_VDD_CORE_OPP100_UV 1100000
|
|
+
|
|
+#define AM33XX_VDD_MPU_OPP50_FREQ 275000000
|
|
+#define AM33XX_VDD_MPU_OPP100_FREQ 500000000
|
|
+#define AM33XX_VDD_MPU_OPP120_FREQ 600000000
|
|
+#define AM33XX_VDD_MPU_OPPTURBO_FREQ 720000000
|
|
+
|
|
+static struct am33xx_sr_opp_data sr1_opp_data[] = {
|
|
+ {
|
|
+ .efuse_offs = AM33XX_SR1_OPP50_CNTRL_OFFSET,
|
|
+ .e2v_gain = AM33XX_SR1_OPP50_EVM_ERR2VOLT_GAIN,
|
|
+ .err_minlimit = AM33XX_SR1_OPP50_EVM_ERR_MIN_LIMIT,
|
|
+ .err_maxlimit = AM33XX_SR1_OPP50_ERR_MAX_LIMIT,
|
|
+ .err_weight = AM33XX_SR1_OPP50_ERR_WEIGHT,
|
|
+ .margin = AM33XX_SR1_OPP50_MARGIN,
|
|
+ .nominal_volt = AM33XX_VDD_MPU_OPP50_UV,
|
|
+ .frequency = AM33XX_VDD_MPU_OPP50_FREQ,
|
|
+ },
|
|
+ {
|
|
+ .efuse_offs = AM33XX_SR1_OPP100_CNTRL_OFFSET,
|
|
+ .e2v_gain = AM33XX_SR1_OPP100_EVM_ERR2VOLT_GAIN,
|
|
+ .err_minlimit = AM33XX_SR1_OPP100_EVM_ERR_MIN_LIMIT,
|
|
+ .err_maxlimit = AM33XX_SR1_OPP100_ERR_MAX_LIMIT,
|
|
+ .err_weight = AM33XX_SR1_OPP100_ERR_WEIGHT,
|
|
+ .margin = AM33XX_SR1_OPP100_MARGIN,
|
|
+ .nominal_volt = AM33XX_VDD_MPU_OPP100_UV,
|
|
+ .frequency = AM33XX_VDD_MPU_OPP100_FREQ,
|
|
+ },
|
|
+ {
|
|
+ .efuse_offs = AM33XX_SR1_OPP120_CNTRL_OFFSET,
|
|
+ .e2v_gain = AM33XX_SR1_OPP120_EVM_ERR2VOLT_GAIN,
|
|
+ .err_minlimit = AM33XX_SR1_OPP120_EVM_ERR_MIN_LIMIT,
|
|
+ .err_maxlimit = AM33XX_SR1_OPP120_ERR_MAX_LIMIT,
|
|
+ .err_weight = AM33XX_SR1_OPP120_ERR_WEIGHT,
|
|
+ .margin = AM33XX_SR1_OPP120_MARGIN,
|
|
+ .nominal_volt = AM33XX_VDD_MPU_OPP120_UV,
|
|
+ .frequency = AM33XX_VDD_MPU_OPP120_FREQ,
|
|
+ },
|
|
+ {
|
|
+ .efuse_offs = AM33XX_SR1_OPPTURBO_CNTRL_OFFSET,
|
|
+ .e2v_gain = AM33XX_SR1_OPPTURBO_EVM_ERR2VOLT_GAIN,
|
|
+ .err_minlimit = AM33XX_SR1_OPPTURBO_EVM_ERR_MIN_LIMIT,
|
|
+ .err_maxlimit = AM33XX_SR1_OPPTURBO_ERR_MAX_LIMIT,
|
|
+ .err_weight = AM33XX_SR1_OPPTURBO_ERR_WEIGHT,
|
|
+ .margin = AM33XX_SR1_OPPTURBO_MARGIN,
|
|
+ .nominal_volt = AM33XX_VDD_MPU_OPPTURBO_UV,
|
|
+ .frequency = AM33XX_VDD_MPU_OPPTURBO_FREQ,
|
|
+ },
|
|
+};
|
|
+
|
|
+static struct am33xx_sr_opp_data sr0_opp_data[] = {
|
|
+ {
|
|
+ .efuse_offs = AM33XX_SR0_OPP50_CNTRL_OFFSET,
|
|
+ .e2v_gain = AM33XX_SR0_OPP50_EVM_ERR2VOLT_GAIN,
|
|
+ .err_minlimit = AM33XX_SR0_OPP50_EVM_ERR_MIN_LIMIT,
|
|
+ .err_maxlimit = AM33XX_SR0_OPP50_ERR_MAX_LIMIT,
|
|
+ .err_weight = AM33XX_SR0_OPP50_ERR_WEIGHT,
|
|
+ .margin = AM33XX_SR0_OPP50_MARGIN,
|
|
+ .nominal_volt = AM33XX_VDD_CORE_OPP50_UV,
|
|
+ },
|
|
+ {
|
|
+ .efuse_offs = AM33XX_SR0_OPP100_CNTRL_OFFSET,
|
|
+ .e2v_gain = AM33XX_SR0_OPP100_EVM_ERR2VOLT_GAIN,
|
|
+ .err_minlimit = AM33XX_SR0_OPP100_EVM_ERR_MIN_LIMIT,
|
|
+ .err_maxlimit = AM33XX_SR0_OPP100_ERR_MAX_LIMIT,
|
|
+ .err_weight = AM33XX_SR0_OPP100_ERR_WEIGHT,
|
|
+ .margin = AM33XX_SR0_OPP100_MARGIN,
|
|
+ .nominal_volt = AM33XX_VDD_CORE_OPP100_UV,
|
|
+ },
|
|
+};
|
|
+
|
|
+static struct am33xx_sr_sdata sr_sensor_data[] = {
|
|
+ {
|
|
+ .sr_opp_data = sr0_opp_data,
|
|
+ /* note that OPP50 is NOT used in Linux kernel for AM335x */
|
|
+ .no_of_opps = 0x2,
|
|
+ .default_opp = 0x1,
|
|
+ .senn_mod = 0x1,
|
|
+ .senp_mod = 0x1,
|
|
+ },
|
|
+ {
|
|
+ .sr_opp_data = sr1_opp_data,
|
|
+ /* the opp data below should be determined
|
|
+ dynamically during SR probe */
|
|
+ .no_of_opps = 0x4,
|
|
+ .default_opp = 0x3,
|
|
+ .senn_mod = 0x1,
|
|
+ .senp_mod = 0x1,
|
|
+ },
|
|
+};
|
|
+
|
|
+static struct am33xx_sr_platform_data am33xx_sr_pdata = {
|
|
+ .vd_name[0] = "vdd_core",
|
|
+ .vd_name[1] = "vdd_mpu",
|
|
+ .ip_type = 2,
|
|
+ .irq_delay = 1000,
|
|
+ .no_of_vds = 2,
|
|
+ .no_of_sens = ARRAY_SIZE(sr_sensor_data),
|
|
+ .vstep_size_uv = 12500,
|
|
+ .enable_on_init = true,
|
|
+ .sr_sdata = sr_sensor_data,
|
|
+};
|
|
+
|
|
+static struct resource am33xx_sr_resources[] = {
|
|
+ {
|
|
+ .name = "smartreflex0",
|
|
+ .start = AM33XX_SR0_BASE,
|
|
+ .end = AM33XX_SR0_BASE + SZ_4K - 1,
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
+ {
|
|
+ .name = "smartreflex0",
|
|
+ .start = AM33XX_IRQ_SMARTREFLEX0,
|
|
+ .end = AM33XX_IRQ_SMARTREFLEX0,
|
|
+ .flags = IORESOURCE_IRQ,
|
|
+ },
|
|
+ {
|
|
+ .name = "smartreflex1",
|
|
+ .start = AM33XX_SR1_BASE,
|
|
+ .end = AM33XX_SR1_BASE + SZ_4K - 1,
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
+ {
|
|
+ .name = "smartreflex1",
|
|
+ .start = AM33XX_IRQ_SMARTREFLEX1,
|
|
+ .end = AM33XX_IRQ_SMARTREFLEX1,
|
|
+ .flags = IORESOURCE_IRQ,
|
|
+ },
|
|
+};
|
|
+
|
|
+/* VCORE for SR regulator init */
|
|
+static struct platform_device am33xx_sr_device = {
|
|
+ .name = "smartreflex",
|
|
+ .id = -1,
|
|
+ .num_resources = ARRAY_SIZE(am33xx_sr_resources),
|
|
+ .resource = am33xx_sr_resources,
|
|
+ .dev = {
|
|
+ .platform_data = &am33xx_sr_pdata,
|
|
+ },
|
|
+};
|
|
+
|
|
+void __init am33xx_sr_init(void)
|
|
+{
|
|
+ /* For beaglebone, update voltage step size and related parameters
|
|
+ appropriately. All other AM33XX platforms are good with the
|
|
+ structure defaults as initialized above. */
|
|
+ if ((am33xx_evmid == BEAGLE_BONE_OLD) ||
|
|
+ (am33xx_evmid == BEAGLE_BONE_A3)) {
|
|
+ printk(KERN_ERR "address of pdata = %08x\n", (u32)&am33xx_sr_pdata);
|
|
+ am33xx_sr_pdata.vstep_size_uv = 25000;
|
|
+ /* CORE */
|
|
+ sr0_opp_data[0].e2v_gain = AM33XX_SR0_OPP50_BB_ERR2VOLT_GAIN;
|
|
+ sr0_opp_data[0].err_minlimit = AM33XX_SR0_OPP50_BB_ERR_MIN_LIMIT;
|
|
+ sr0_opp_data[1].e2v_gain = AM33XX_SR0_OPP100_BB_ERR2VOLT_GAIN;
|
|
+ sr0_opp_data[1].err_minlimit = AM33XX_SR0_OPP100_BB_ERR_MIN_LIMIT;
|
|
+ /* MPU */
|
|
+ sr1_opp_data[0].e2v_gain = AM33XX_SR1_OPP50_BB_ERR2VOLT_GAIN;
|
|
+ sr1_opp_data[0].err_minlimit = AM33XX_SR1_OPP50_BB_ERR_MIN_LIMIT;
|
|
+ sr1_opp_data[1].e2v_gain = AM33XX_SR1_OPP100_BB_ERR2VOLT_GAIN;
|
|
+ sr1_opp_data[1].err_minlimit = AM33XX_SR1_OPP100_BB_ERR_MIN_LIMIT;
|
|
+ sr1_opp_data[2].e2v_gain = AM33XX_SR1_OPP120_BB_ERR2VOLT_GAIN;
|
|
+ sr1_opp_data[2].err_minlimit = AM33XX_SR1_OPP120_BB_ERR_MIN_LIMIT;
|
|
+ sr1_opp_data[3].e2v_gain = AM33XX_SR1_OPPTURBO_BB_ERR2VOLT_GAIN;
|
|
+ sr1_opp_data[3].err_minlimit = AM33XX_SR1_OPPTURBO_BB_ERR_MIN_LIMIT;
|
|
+ }
|
|
+
|
|
+ if (platform_device_register(&am33xx_sr_device))
|
|
+ printk(KERN_ERR "failed to register am33xx_sr device\n");
|
|
+ else
|
|
+ printk(KERN_INFO "registered am33xx_sr device\n");
|
|
+}
|
|
+#else
|
|
+inline void am33xx_sr_init(void) {}
|
|
+#endif
|
|
+
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
static int __init omap2_init_devices(void)
|
|
diff --git a/arch/arm/mach-omap2/include/mach/board-am335xevm.h b/arch/arm/mach-omap2/include/mach/board-am335xevm.h
|
|
index 1d24495..85a8df0 100644
|
|
--- a/arch/arm/mach-omap2/include/mach/board-am335xevm.h
|
|
+++ b/arch/arm/mach-omap2/include/mach/board-am335xevm.h
|
|
@@ -41,6 +41,7 @@
|
|
void am335x_evm_set_id(unsigned int evmid);
|
|
int am335x_evm_get_id(void);
|
|
void am33xx_cpsw_macidfillup(char *eeprommacid0, char *eeprommacid1);
|
|
+void am33xx_sr_init(void);
|
|
void am33xx_d_can_init(unsigned int instance);
|
|
|
|
#endif
|
|
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
|
|
index 734009a..33f17f2 100644
|
|
--- a/arch/arm/plat-omap/Kconfig
|
|
+++ b/arch/arm/plat-omap/Kconfig
|
|
@@ -43,6 +43,27 @@ config OMAP_DEBUG_LEDS
|
|
depends on OMAP_DEBUG_DEVICES
|
|
default y if LEDS_CLASS
|
|
|
|
+config AM33XX_SMARTREFLEX
|
|
+ bool "AM33XX SmartReflex support"
|
|
+ depends on (SOC_OMAPAM33XX) && PM
|
|
+ help
|
|
+ Say Y if you want to enable SmartReflex.
|
|
+
|
|
+ SmartReflex can perform continuous dynamic voltage
|
|
+ scaling around the nominal operating point voltage
|
|
+ according to silicon characteristics and operating
|
|
+ conditions. Enabling SmartReflex reduces active power
|
|
+ consumption.
|
|
+
|
|
+ Please note, that by default SmartReflex is enabled.
|
|
+ To disable the automatic voltage compensation for
|
|
+ vdd mpu and vdd core from user space, user must
|
|
+ write 1 to /debug/smartreflex/autocomp.
|
|
+
|
|
+ Optionally autocompensation can be disabled in the kernel
|
|
+ by default during system init via the enable_on_init flag
|
|
+ which an be passed as platform data to the smartreflex driver.
|
|
+
|
|
config OMAP_SMARTREFLEX
|
|
bool "SmartReflex support"
|
|
depends on (ARCH_OMAP3 || ARCH_OMAP4) && PM
|
|
diff --git a/arch/arm/plat-omap/include/plat/am33xx.h b/arch/arm/plat-omap/include/plat/am33xx.h
|
|
index 32522df..a628b1f 100644
|
|
--- a/arch/arm/plat-omap/include/plat/am33xx.h
|
|
+++ b/arch/arm/plat-omap/include/plat/am33xx.h
|
|
@@ -43,6 +43,9 @@
|
|
#define AM33XX_TSC_BASE 0x44E0D000
|
|
#define AM33XX_RTC_BASE 0x44E3E000
|
|
|
|
+#define AM33XX_SR0_BASE 0x44E37000
|
|
+#define AM33XX_SR1_BASE 0x44E39000
|
|
+
|
|
#define AM33XX_ASP0_BASE 0x48038000
|
|
#define AM33XX_ASP1_BASE 0x4803C000
|
|
|
|
diff --git a/arch/arm/plat-omap/include/plat/smartreflex.h b/arch/arm/plat-omap/include/plat/smartreflex.h
|
|
new file mode 100644
|
|
index 0000000..36338f7
|
|
--- /dev/null
|
|
+++ b/arch/arm/plat-omap/include/plat/smartreflex.h
|
|
@@ -0,0 +1,431 @@
|
|
+/*
|
|
+ * OMAP Smartreflex Defines and Routines
|
|
+ *
|
|
+ * Author: Thara Gopinath <thara@ti.com>
|
|
+ *
|
|
+ * Copyright (C) 2010 Texas Instruments, Inc.
|
|
+ * Thara Gopinath <thara@ti.com>
|
|
+ *
|
|
+ * Copyright (C) 2008 Nokia Corporation
|
|
+ * Kalle Jokiniemi
|
|
+ *
|
|
+ * Copyright (C) 2007 Texas Instruments, Inc.
|
|
+ * Lesly A M <x0080970@ti.com>
|
|
+ *
|
|
+ * 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 __ASM_ARM_OMAP_SMARTREFLEX_H
|
|
+#define __ASM_ARM_OMAP_SMARTREFLEX_H
|
|
+
|
|
+#include <linux/platform_device.h>
|
|
+#include <plat/voltage.h>
|
|
+
|
|
+/*
|
|
+ * Different Smartreflex IPs version. The v1 is the 65nm version used in
|
|
+ * OMAP3430. The v2 is the update for the 45nm version of the IP
|
|
+ * used in OMAP3630 and OMAP4430
|
|
+ */
|
|
+#define SR_TYPE_V1 1
|
|
+#define SR_TYPE_V2 2
|
|
+
|
|
+/* SMART REFLEX REG ADDRESS OFFSET */
|
|
+#define SRCONFIG 0x00
|
|
+#define SRSTATUS 0x04
|
|
+#define SENVAL 0x08
|
|
+#define SENMIN 0x0C
|
|
+#define SENMAX 0x10
|
|
+#define SENAVG 0x14
|
|
+#define AVGWEIGHT 0x18
|
|
+#define NVALUERECIPROCAL 0x1c
|
|
+#define SENERROR_V1 0x20
|
|
+#define ERRCONFIG_V1 0x24
|
|
+#define IRQ_EOI 0x20
|
|
+#define IRQSTATUS_RAW 0x24
|
|
+#define IRQSTATUS 0x28
|
|
+#define IRQENABLE_SET 0x2C
|
|
+#define IRQENABLE_CLR 0x30
|
|
+#define SENERROR_V2 0x34
|
|
+#define ERRCONFIG_V2 0x38
|
|
+
|
|
+/* Bit/Shift Positions */
|
|
+
|
|
+/* SRCONFIG */
|
|
+#define SRCONFIG_ACCUMDATA_SHIFT 22
|
|
+#define SRCONFIG_SRCLKLENGTH_SHIFT 12
|
|
+#define SRCONFIG_SENNENABLE_V1_SHIFT 5
|
|
+#define SRCONFIG_SENPENABLE_V1_SHIFT 3
|
|
+#define SRCONFIG_SENNENABLE_V2_SHIFT 1
|
|
+#define SRCONFIG_SENPENABLE_V2_SHIFT 0
|
|
+#define SRCONFIG_CLKCTRL_SHIFT 0
|
|
+
|
|
+#define SRCONFIG_ACCUMDATA_MASK (0x3ff << 22)
|
|
+
|
|
+#define SRCONFIG_SRENABLE BIT(11)
|
|
+#define SRCONFIG_SENENABLE BIT(10)
|
|
+#define SRCONFIG_ERRGEN_EN BIT(9)
|
|
+#define SRCONFIG_MINMAXAVG_EN BIT(8)
|
|
+#define SRCONFIG_DELAYCTRL BIT(2)
|
|
+
|
|
+/* AVGWEIGHT */
|
|
+#define AVGWEIGHT_SENPAVGWEIGHT_SHIFT 2
|
|
+#define AVGWEIGHT_SENNAVGWEIGHT_SHIFT 0
|
|
+
|
|
+/* NVALUERECIPROCAL */
|
|
+#define NVALUERECIPROCAL_SENPGAIN_SHIFT 20
|
|
+#define NVALUERECIPROCAL_SENNGAIN_SHIFT 16
|
|
+#define NVALUERECIPROCAL_RNSENP_SHIFT 8
|
|
+#define NVALUERECIPROCAL_RNSENN_SHIFT 0
|
|
+
|
|
+/* ERRCONFIG */
|
|
+#define ERRCONFIG_ERRWEIGHT_SHIFT 16
|
|
+#define ERRCONFIG_ERRMAXLIMIT_SHIFT 8
|
|
+#define ERRCONFIG_ERRMINLIMIT_SHIFT 0
|
|
+
|
|
+#define SR_ERRWEIGHT_MASK (0x07 << 16)
|
|
+#define SR_ERRMAXLIMIT_MASK (0xff << 8)
|
|
+#define SR_ERRMINLIMIT_MASK (0xff << 0)
|
|
+
|
|
+#define ERRCONFIG_VPBOUNDINTEN_V1 BIT(31)
|
|
+#define ERRCONFIG_VPBOUNDINTST_V1 BIT(30)
|
|
+#define ERRCONFIG_MCUACCUMINTEN BIT(29)
|
|
+#define ERRCONFIG_MCUACCUMINTST BIT(28)
|
|
+#define ERRCONFIG_MCUVALIDINTEN BIT(27)
|
|
+#define ERRCONFIG_MCUVALIDINTST BIT(26)
|
|
+#define ERRCONFIG_MCUBOUNDINTEN BIT(25)
|
|
+#define ERRCONFIG_MCUBOUNDINTST BIT(24)
|
|
+#define ERRCONFIG_MCUDISACKINTEN BIT(23)
|
|
+#define ERRCONFIG_VPBOUNDINTST_V2 BIT(23)
|
|
+#define ERRCONFIG_MCUDISACKINTST BIT(22)
|
|
+#define ERRCONFIG_VPBOUNDINTEN_V2 BIT(22)
|
|
+
|
|
+#define ERRCONFIG_STATUS_V1_MASK (ERRCONFIG_VPBOUNDINTST_V1 | \
|
|
+ ERRCONFIG_MCUACCUMINTST | \
|
|
+ ERRCONFIG_MCUVALIDINTST | \
|
|
+ ERRCONFIG_MCUBOUNDINTST | \
|
|
+ ERRCONFIG_MCUDISACKINTST)
|
|
+/* IRQSTATUS */
|
|
+#define IRQSTATUS_MCUACCUMINT BIT(3)
|
|
+#define IRQSTATUS_MCVALIDINT BIT(2)
|
|
+#define IRQSTATUS_MCBOUNDSINT BIT(1)
|
|
+#define IRQSTATUS_MCUDISABLEACKINT BIT(0)
|
|
+
|
|
+/* IRQENABLE_SET and IRQENABLE_CLEAR */
|
|
+#define IRQENABLE_MCUACCUMINT BIT(3)
|
|
+#define IRQENABLE_MCUVALIDINT BIT(2)
|
|
+#define IRQENABLE_MCUBOUNDSINT BIT(1)
|
|
+#define IRQENABLE_MCUDISABLEACKINT BIT(0)
|
|
+
|
|
+/* Common Bit values */
|
|
+
|
|
+#define SRCLKLENGTH_12MHZ_SYSCLK 0x3c
|
|
+#define SRCLKLENGTH_13MHZ_SYSCLK 0x41
|
|
+#define SRCLKLENGTH_19MHZ_SYSCLK 0x60
|
|
+#define SRCLKLENGTH_26MHZ_SYSCLK 0x82
|
|
+#define SRCLKLENGTH_38MHZ_SYSCLK 0xC0
|
|
+
|
|
+/*
|
|
+ * 3430 specific values. Maybe these should be passed from board file or
|
|
+ * pmic structures.
|
|
+ */
|
|
+#define OMAP3430_SR_ACCUMDATA 0x1f4
|
|
+
|
|
+#define OMAP3430_SR1_SENPAVGWEIGHT 0x03
|
|
+#define OMAP3430_SR1_SENNAVGWEIGHT 0x03
|
|
+
|
|
+#define OMAP3430_SR2_SENPAVGWEIGHT 0x01
|
|
+#define OMAP3430_SR2_SENNAVGWEIGHT 0x01
|
|
+
|
|
+#define OMAP3430_SR_ERRWEIGHT 0x04
|
|
+#define OMAP3430_SR_ERRMAXLIMIT 0x02
|
|
+
|
|
+/**
|
|
+ * struct omap_sr_pmic_data - Strucutre to be populated by pmic code to pass
|
|
+ * pmic specific info to smartreflex driver
|
|
+ *
|
|
+ * @sr_pmic_init: API to initialize smartreflex on the PMIC side.
|
|
+ */
|
|
+struct omap_sr_pmic_data {
|
|
+ void (*sr_pmic_init) (void);
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_OMAP_SMARTREFLEX
|
|
+/*
|
|
+ * The smart reflex driver supports CLASS1 CLASS2 and CLASS3 SR.
|
|
+ * The smartreflex class driver should pass the class type.
|
|
+ * Should be used to populate the class_type field of the
|
|
+ * omap_smartreflex_class_data structure.
|
|
+ */
|
|
+#define SR_CLASS1 0x1
|
|
+#define SR_CLASS2 0x2
|
|
+#define SR_CLASS3 0x3
|
|
+
|
|
+/**
|
|
+ * struct omap_sr_class_data - Smartreflex class driver info
|
|
+ *
|
|
+ * @enable: API to enable a particular class smaartreflex.
|
|
+ * @disable: API to disable a particular class smartreflex.
|
|
+ * @configure: API to configure a particular class smartreflex.
|
|
+ * @notify: API to notify the class driver about an event in SR.
|
|
+ * Not needed for class3.
|
|
+ * @notify_flags: specify the events to be notified to the class driver
|
|
+ * @class_type: specify which smartreflex class.
|
|
+ * Can be used by the SR driver to take any class
|
|
+ * based decisions.
|
|
+ */
|
|
+struct omap_sr_class_data {
|
|
+ int (*enable)(struct voltagedomain *voltdm);
|
|
+ int (*disable)(struct voltagedomain *voltdm, int is_volt_reset);
|
|
+ int (*configure)(struct voltagedomain *voltdm);
|
|
+ int (*notify)(struct voltagedomain *voltdm, u32 status);
|
|
+ u8 notify_flags;
|
|
+ u8 class_type;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct omap_sr_nvalue_table - Smartreflex n-target value info
|
|
+ *
|
|
+ * @efuse_offs: The offset of the efuse where n-target values are stored.
|
|
+ * @nvalue: The n-target value.
|
|
+ */
|
|
+struct omap_sr_nvalue_table {
|
|
+ u32 efuse_offs;
|
|
+ u32 nvalue;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct omap_sr_data - Smartreflex platform data.
|
|
+ *
|
|
+ * @ip_type: Smartreflex IP type.
|
|
+ * @senp_mod: SENPENABLE value for the sr
|
|
+ * @senn_mod: SENNENABLE value for sr
|
|
+ * @nvalue_count: Number of distinct nvalues in the nvalue table
|
|
+ * @enable_on_init: whether this sr module needs to enabled at
|
|
+ * boot up or not.
|
|
+ * @nvalue_table: table containing the efuse offsets and nvalues
|
|
+ * corresponding to them.
|
|
+ * @voltdm: Pointer to the voltage domain associated with the SR
|
|
+ */
|
|
+struct omap_sr_data {
|
|
+ int ip_type;
|
|
+ u32 senp_mod;
|
|
+ u32 senn_mod;
|
|
+ int nvalue_count;
|
|
+ bool enable_on_init;
|
|
+ struct omap_sr_nvalue_table *nvalue_table;
|
|
+ struct voltagedomain *voltdm;
|
|
+};
|
|
+
|
|
+/* Smartreflex module enable/disable interface */
|
|
+void omap_sr_enable(struct voltagedomain *voltdm);
|
|
+void omap_sr_disable(struct voltagedomain *voltdm);
|
|
+void omap_sr_disable_reset_volt(struct voltagedomain *voltdm);
|
|
+
|
|
+/* API to register the pmic specific data with the smartreflex driver. */
|
|
+void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data);
|
|
+
|
|
+/* Smartreflex driver hooks to be called from Smartreflex class driver */
|
|
+int sr_enable(struct voltagedomain *voltdm, unsigned long volt);
|
|
+void sr_disable(struct voltagedomain *voltdm);
|
|
+int sr_configure_errgen(struct voltagedomain *voltdm);
|
|
+int sr_configure_minmax(struct voltagedomain *voltdm);
|
|
+
|
|
+/* API to register the smartreflex class driver with the smartreflex driver */
|
|
+int sr_register_class(struct omap_sr_class_data *class_data);
|
|
+#else
|
|
+
|
|
+#ifdef CONFIG_AM33XX_SMARTREFLEX
|
|
+
|
|
+#define SR_CORE (0)
|
|
+#define SR_MPU (1)
|
|
+#define SRCLKLENGTH_125MHZ_SYSCLK (0x78 << 12)
|
|
+#define GAIN_MAXLIMIT (16)
|
|
+#define R_MAXLIMIT (256)
|
|
+#define MAX_SENSORS 2
|
|
+/* GG: eventually this should be determined at runtime */
|
|
+#define AM33XX_OPP_COUNT 4
|
|
+
|
|
+/**
|
|
+ * struct am33xx_sr_opp_data - Smartreflex data per OPP
|
|
+ * @efuse_offs: The offset of the efuse where n-target values are
|
|
+ * stored.
|
|
+ * @nvalue: NTarget as stored in EFUSE.
|
|
+ * @adj_nvalue: Adjusted NTarget (adjusted by margin)
|
|
+ * @e2v_gain: Error to voltage gain for changing the percentage
|
|
+ * error into voltage delta
|
|
+ * @err_weight: Average sensor error weight
|
|
+ * @err_minlimit: Minimum error limit of the sensor
|
|
+ * @err_maxlimit: Maximum error limit of the sensor
|
|
+ * @margin: Voltage margin to apply
|
|
+ * @nominal_volt: Nominal voltage for this OPP
|
|
+ * @frequency: Defined frequency for this OPP (in KHz)
|
|
+ */
|
|
+struct am33xx_sr_opp_data {
|
|
+ u32 efuse_offs;
|
|
+ u32 nvalue;
|
|
+ u32 adj_nvalue;
|
|
+ s32 e2v_gain;
|
|
+ u32 err_weight;
|
|
+ u32 err_minlimit;
|
|
+ u32 err_maxlimit;
|
|
+ s32 margin;
|
|
+ u32 nominal_volt; /* nominal_volt and frequency may be removed
|
|
+ once am33xx voltdm layer works */
|
|
+ u32 frequency;
|
|
+ u32 opp_id;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct am33xx_sr_sdata - Smartreflex sensors data
|
|
+ * @sr_opp_data: Pointer to data structure containing per OPP data
|
|
+ * for this SR module.
|
|
+ * @no_of_opps: Number of OPP's supported for this sensor -
|
|
+ * determined dynamically when possible.
|
|
+ * @default_opp: Defines the opp to use on startup if OPP is fixed
|
|
+ * or cannot be determined dynamically.
|
|
+ * @senn_mod: Enable bit for N sensor
|
|
+ * @senp_mod: Enable bit for P sensor
|
|
+ */
|
|
+struct am33xx_sr_sdata {
|
|
+ struct am33xx_sr_opp_data *sr_opp_data;
|
|
+ u32 no_of_opps;
|
|
+ u32 default_opp;
|
|
+ u32 senn_mod;
|
|
+ u32 senp_mod;
|
|
+};
|
|
+
|
|
+struct am33xx_sr_sensor {
|
|
+ u32 sr_id;
|
|
+ u32 irq;
|
|
+ u32 irq_status;
|
|
+ u32 senn_en;
|
|
+ u32 senp_en;
|
|
+ char *name;
|
|
+ char *reg_name;
|
|
+ void __iomem *base;
|
|
+ int init_volt_mv;
|
|
+ int curr_opp;
|
|
+ u32 no_of_opps;
|
|
+ struct delayed_work work_reenable;
|
|
+ struct regulator *reg;
|
|
+ struct am33xx_sr_opp_data opp_data[AM33XX_OPP_COUNT];
|
|
+ struct clk *fck;
|
|
+ struct voltagedomain *voltdm;
|
|
+ struct omap_volt_data *volt_data;
|
|
+};
|
|
+
|
|
+struct am33xx_sr {
|
|
+ u32 autocomp_active;
|
|
+ u32 sens_per_vd;
|
|
+ u32 no_of_sens;
|
|
+ u32 no_of_vds;
|
|
+ u32 ip_type;
|
|
+ u32 irq_delay;
|
|
+ u32 disabled_by_user;
|
|
+ int uvoltage_step_size;
|
|
+ char *res_name[MAX_SENSORS];
|
|
+#ifdef CONFIG_CPU_FREQ
|
|
+ struct notifier_block freq_transition;
|
|
+#endif
|
|
+ /*struct work_struct work;*/
|
|
+ struct delayed_work work;
|
|
+ struct sr_platform_data *sr_data;
|
|
+ struct am33xx_sr_sensor sen[MAX_SENSORS];
|
|
+ struct platform_device *pdev;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct am33xx_sr_platform_data - Smartreflex platform data.
|
|
+ * @sr_sdata: SR per sensor details, contains the efuse off-sets,
|
|
+ * error to voltage gain factor, minimum error limits
|
|
+ * @vd_name: Name of the voltage domain.
|
|
+ * @ip_type: Smartreflex IP type, class1 or class2 or class3.
|
|
+ * @irq_delay: Amount of time required for changed voltage to settle.
|
|
+ * @no_of_vds: Number of voltage domains to which SR applicable
|
|
+ * @no_of_sens: Number of SR sensors used to monitor the device
|
|
+ * performance, temp etc...
|
|
+ * @vstep_size_uv: PMIC voltage step size in micro volts
|
|
+ * @enable_on_init: whether this sr module needs to enabled at
|
|
+ * boot up or not.
|
|
+ */
|
|
+struct am33xx_sr_platform_data {
|
|
+ struct am33xx_sr_sdata *sr_sdata;
|
|
+ char *vd_name[2];
|
|
+ u32 ip_type;
|
|
+ u32 irq_delay;
|
|
+ u32 no_of_vds;
|
|
+ u32 no_of_sens;
|
|
+ u32 vstep_size_uv;
|
|
+ bool enable_on_init;
|
|
+};
|
|
+
|
|
+#endif /*CONFIG_AM33XX_SMARTREFLEX*/
|
|
+
|
|
+#ifdef CONFIG_TI816X_SMARTREFLEX
|
|
+
|
|
+#define SRHVT 0
|
|
+#define SRSVT 1
|
|
+
|
|
+/* SRClk = 100KHz */
|
|
+#define SRCLKLENGTH_125MHZ_SYSCLK (0x271 << 12)
|
|
+
|
|
+/**
|
|
+ * struct ti816x_sr_sdata - Smartreflex sensors data
|
|
+ * @efuse_offs: The offset of the efuse where n-target values are
|
|
+ * stored.
|
|
+ * @e2v_gain: Error to voltage gain for changing the percentage
|
|
+ * error into voltage delta
|
|
+ * @err_weight: Average sensor error weight
|
|
+ * @err_minlimit: Minimum error limit of the sensor
|
|
+ * @err_maxlimit: Maximum error limit of the sensor
|
|
+ * @senn_mod: Enable bit for N sensor
|
|
+ * @senp_mod: Enable bit for P sensor
|
|
+ */
|
|
+struct ti816x_sr_sdata {
|
|
+ u32 efuse_offs;
|
|
+ u32 e2v_gain;
|
|
+ u32 err_weight;
|
|
+ u32 err_minlimit;
|
|
+ u32 err_maxlimit;
|
|
+ u32 senn_mod;
|
|
+ u32 senp_mod;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct ti816x_sr_platform_data - Smartreflex platform data.
|
|
+ * @sr_sdata: SR per sensor details, contains the efuse off-sets,
|
|
+ * error to voltage gain factor, minimum error limits
|
|
+ * @vd_name: Name of the voltage domain.
|
|
+ * @ip_type: Smartreflex IP type, class1 or class2 or class3.
|
|
+ * @irq_delay: Time delay between disable and re-enable the
|
|
+ * interrupts, in msec
|
|
+ * @no_of_vds: Number of voltage domains to which SR applicable
|
|
+ * @no_of_sens: Number of SR sensors used to monitor the device
|
|
+ * performance, temp etc...
|
|
+ * @vstep_size_uv: PMIC voltage step size in micro volts
|
|
+ * @enable_on_init: whether this sr module needs to enabled at
|
|
+ * boot up or not.
|
|
+ */
|
|
+struct ti816x_sr_platform_data {
|
|
+ struct ti816x_sr_sdata *sr_sdata;
|
|
+ char *vd_name;
|
|
+ u32 ip_type;
|
|
+ u32 irq_delay;
|
|
+ u32 no_of_vds;
|
|
+ u32 no_of_sens;
|
|
+ u32 vstep_size_uv;
|
|
+ bool enable_on_init;
|
|
+};
|
|
+
|
|
+#endif /* CONFIG_TI816X_SMARTREFLEX */
|
|
+
|
|
+static inline void omap_sr_enable(struct voltagedomain *voltdm) {}
|
|
+static inline void omap_sr_disable(struct voltagedomain *voltdm) {}
|
|
+static inline void omap_sr_disable_reset_volt(
|
|
+ struct voltagedomain *voltdm) {}
|
|
+static inline void omap_sr_register_pmic(
|
|
+ struct omap_sr_pmic_data *pmic_data) {}
|
|
+#endif
|
|
+#endif
|
|
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
|
|
index 00706c6..382ce2d 100644
|
|
--- a/drivers/regulator/core.c
|
|
+++ b/drivers/regulator/core.c
|
|
@@ -169,6 +169,9 @@ static int regulator_check_consumers(struct regulator_dev *rdev,
|
|
{
|
|
struct regulator *regulator;
|
|
|
|
+ if (rdev->ignore_check_consumers)
|
|
+ return 0;
|
|
+
|
|
list_for_each_entry(regulator, &rdev->consumer_list, list) {
|
|
/*
|
|
* Assume consumers that didn't say anything are OK
|
|
@@ -2688,6 +2691,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
|
|
rdev->reg_data = driver_data;
|
|
rdev->owner = regulator_desc->owner;
|
|
rdev->desc = regulator_desc;
|
|
+ rdev->ignore_check_consumers = init_data->ignore_check_consumers;
|
|
INIT_LIST_HEAD(&rdev->consumer_list);
|
|
INIT_LIST_HEAD(&rdev->list);
|
|
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
|
|
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
|
|
index 52c89ae..6176167 100644
|
|
--- a/include/linux/regulator/driver.h
|
|
+++ b/include/linux/regulator/driver.h
|
|
@@ -204,7 +204,7 @@ struct regulator_dev {
|
|
int deferred_disables;
|
|
|
|
void *reg_data; /* regulator_dev data */
|
|
-
|
|
+ int ignore_check_consumers;
|
|
#ifdef CONFIG_DEBUG_FS
|
|
struct dentry *debugfs;
|
|
#endif
|
|
diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
|
|
index f3f13fd..0de52a3 100644
|
|
--- a/include/linux/regulator/machine.h
|
|
+++ b/include/linux/regulator/machine.h
|
|
@@ -169,7 +169,7 @@ struct regulator_consumer_supply {
|
|
* be usable.
|
|
* @num_consumer_supplies: Number of consumer device supplies.
|
|
* @consumer_supplies: Consumer device supply configuration.
|
|
- *
|
|
+ * @ignore_check_consumers: If != 0, regulator_check_consumers() is disabled.
|
|
* @regulator_init: Callback invoked when the regulator has been registered.
|
|
* @driver_data: Data passed to regulator_init.
|
|
*/
|
|
@@ -181,6 +181,7 @@ struct regulator_init_data {
|
|
int num_consumer_supplies;
|
|
struct regulator_consumer_supply *consumer_supplies;
|
|
|
|
+ int ignore_check_consumers;
|
|
/* optional regulator machine specific init */
|
|
int (*regulator_init)(void *driver_data);
|
|
void *driver_data; /* core does not touch this */
|
|
--
|
|
1.7.0.4
|