OMAP3+: Introduce generic logic for OMAP voltage controller

OMAP Voltage controller is used to generically talk to
PMICs on OMAP3,4,5 over I2C_SR. Instead of replicating code
in multiple SoC code, introduce a common voltage controller
logic which can be re-used from elsewhere.

With this change, we replace setup_sri2c with omap_vc_init which
has the same functionality, and replace the voltage scale
replication in do_scale_vcore and do_scale_tps62361 with
omap_vc_bypass_send_value. omap_vc_bypass_send_value can also
now be used with any configuration of PMIC.

NOTE: Voltage controller controlling I2C_SR is a write-only data
path, so no register read operation can be implemented.

Reported-by: Isabelle Gros <i-gros@ti.com>
Reported-by: Jerome Angeloni <j-angeloni@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
diff --git a/arch/arm/cpu/armv7/omap-common/Makefile b/arch/arm/cpu/armv7/omap-common/Makefile
index 447fcd5..2a6625f 100644
--- a/arch/arm/cpu/armv7/omap-common/Makefile
+++ b/arch/arm/cpu/armv7/omap-common/Makefile
@@ -37,6 +37,7 @@
 COBJS	+= hwinit-common.o
 COBJS	+= clocks-common.o
 COBJS	+= emif-common.o
+COBJS	+= vc.o
 endif
 
 ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX),)
diff --git a/arch/arm/cpu/armv7/omap-common/clocks-common.c b/arch/arm/cpu/armv7/omap-common/clocks-common.c
index 4e74569..90d3156 100644
--- a/arch/arm/cpu/armv7/omap-common/clocks-common.c
+++ b/arch/arm/cpu/armv7/omap-common/clocks-common.c
@@ -362,27 +362,19 @@
 
 void do_scale_tps62361(u32 reg, u32 volt_mv)
 {
-	u32 temp, step;
+	u32 step;
 
 	step = volt_mv - TPS62361_BASE_VOLT_MV;
 	step /= 10;
 
-	temp = TPS62361_I2C_SLAVE_ADDR |
-	    (reg << PRM_VC_VAL_BYPASS_REGADDR_SHIFT) |
-	    (step << PRM_VC_VAL_BYPASS_DATA_SHIFT) |
-	    PRM_VC_VAL_BYPASS_VALID_BIT;
 	debug("do_scale_tps62361: volt - %d step - 0x%x\n", volt_mv, step);
-
-	writel(temp, &prcm->prm_vc_val_bypass);
-	if (!wait_on_value(PRM_VC_VAL_BYPASS_VALID_BIT, 0,
-				&prcm->prm_vc_val_bypass, LDELAY)) {
+	if (omap_vc_bypass_send_value(TPS62361_I2C_SLAVE_ADDR, reg, step))
 		puts("Scaling voltage failed for vdd_mpu from TPS\n");
-	}
 }
 
 void do_scale_vcore(u32 vcore_reg, u32 volt_mv)
 {
-	u32 temp, offset_code;
+	u32 offset_code;
 	u32 step = 12660; /* 12.66 mV represented in uV */
 	u32 offset = volt_mv;
 
@@ -400,16 +392,9 @@
 
 	debug("do_scale_vcore: volt - %d offset_code - 0x%x\n", volt_mv,
 		offset_code);
-
-	temp = SMPS_I2C_SLAVE_ADDR |
-	    (vcore_reg << PRM_VC_VAL_BYPASS_REGADDR_SHIFT) |
-	    (offset_code << PRM_VC_VAL_BYPASS_DATA_SHIFT) |
-	    PRM_VC_VAL_BYPASS_VALID_BIT;
-	writel(temp, &prcm->prm_vc_val_bypass);
-	if (!wait_on_value(PRM_VC_VAL_BYPASS_VALID_BIT, 0,
-				&prcm->prm_vc_val_bypass, LDELAY)) {
+	if (omap_vc_bypass_send_value(SMPS_I2C_SLAVE_ADDR,
+				vcore_reg, offset_code))
 		printf("Scaling voltage failed for 0x%x\n", vcore_reg);
-	}
 }
 
 static inline void enable_clock_domain(u32 *const clkctrl_reg, u32 enable_mode)
@@ -529,29 +514,6 @@
 			CD_CLKCTRL_CLKTRCTRL_SHIFT);
 }
 
-void setup_sri2c(void)
-{
-	u32 sys_clk_khz, cycles_hi, cycles_low, temp;
-
-	sys_clk_khz = get_sys_clk_freq() / 1000;
-
-	/*
-	 * Setup the dedicated I2C controller for Voltage Control
-	 * I2C clk - high period 40% low period 60%
-	 */
-	cycles_hi = sys_clk_khz * 4 / PRM_VC_I2C_CHANNEL_FREQ_KHZ / 10;
-	cycles_low = sys_clk_khz * 6 / PRM_VC_I2C_CHANNEL_FREQ_KHZ / 10;
-	/* values to be set in register - less by 5 & 7 respectively */
-	cycles_hi -= 5;
-	cycles_low -= 7;
-	temp = (cycles_hi << PRM_VC_CFG_I2C_CLK_SCLH_SHIFT) |
-	       (cycles_low << PRM_VC_CFG_I2C_CLK_SCLL_SHIFT);
-	writel(temp, &prcm->prm_vc_cfg_i2c_clk);
-
-	/* Disable high speed mode and all advanced features */
-	writel(0x0, &prcm->prm_vc_cfg_i2c_mode);
-}
-
 void do_enable_clocks(u32 *const *clk_domains,
 			    u32 *const *clk_modules_hw_auto,
 			    u32 *const *clk_modules_explicit_en,
diff --git a/arch/arm/cpu/armv7/omap-common/vc.c b/arch/arm/cpu/armv7/omap-common/vc.c
new file mode 100644
index 0000000..a045b77
--- /dev/null
+++ b/arch/arm/cpu/armv7/omap-common/vc.c
@@ -0,0 +1,138 @@
+/*
+ * Voltage Controller implementation for OMAP
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *	Nishanth Menon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 <common.h>
+#include <asm/omap_common.h>
+#include <asm/arch/sys_proto.h>
+
+/*
+ * Define Master code if there are multiple masters on the I2C_SR bus.
+ * Normally not required
+ */
+#ifndef CONFIG_OMAP_VC_I2C_HS_MCODE
+#define CONFIG_OMAP_VC_I2C_HS_MCODE 0x0
+#endif
+
+/* Register defines and masks for VC IP Block */
+/* PRM_VC_CFG_I2C_MODE */
+#define PRM_VC_CFG_I2C_MODE_DFILTEREN_BIT	(0x1 << 6)
+#define PRM_VC_CFG_I2C_MODE_SRMODEEN_BIT	(0x1 << 4)
+#define PRM_VC_CFG_I2C_MODE_HSMODEEN_BIT	(0x1 << 3)
+#define PRM_VC_CFG_I2C_MODE_HSMCODE_SHIFT	0x0
+#define PRM_VC_CFG_I2C_MODE_HSMCODE_MASK	0x3
+
+/* PRM_VC_CFG_I2C_CLK */
+#define PRM_VC_CFG_I2C_CLK_HSCLL_SHIFT		24
+#define PRM_VC_CFG_I2C_CLK_HSCLL_MASK		0xFF
+#define PRM_VC_CFG_I2C_CLK_HSCLH_SHIFT		16
+#define PRM_VC_CFG_I2C_CLK_HSCLH_MASK		0xFF
+#define PRM_VC_CFG_I2C_CLK_SCLH_SHIFT		0
+#define PRM_VC_CFG_I2C_CLK_SCLH_MASK		0xFF
+#define PRM_VC_CFG_I2C_CLK_SCLL_SHIFT		8
+#define PRM_VC_CFG_I2C_CLK_SCLL_MASK		(0xFF << 8)
+
+/* PRM_VC_VAL_BYPASS */
+#define PRM_VC_VAL_BYPASS_VALID_BIT		(0x1 << 24)
+#define PRM_VC_VAL_BYPASS_SLAVEADDR_SHIFT	0
+#define PRM_VC_VAL_BYPASS_SLAVEADDR_MASK	0x7F
+#define PRM_VC_VAL_BYPASS_REGADDR_SHIFT		8
+#define PRM_VC_VAL_BYPASS_REGADDR_MASK		0xFF
+#define PRM_VC_VAL_BYPASS_DATA_SHIFT		16
+#define PRM_VC_VAL_BYPASS_DATA_MASK		0xFF
+
+/**
+ * omap_vc_init() - Initialization for Voltage controller
+ * @speed_khz: I2C buspeed in KHz
+ */
+void omap_vc_init(u16 speed_khz)
+{
+	u32 val;
+	u32 sys_clk_khz, cycles_hi, cycles_low;
+
+	sys_clk_khz = get_sys_clk_freq() / 1000;
+
+	if (speed_khz > 400) {
+		puts("higher speed requested - throttle to 400Khz\n");
+		speed_khz = 400;
+	}
+
+	/*
+	 * Setup the dedicated I2C controller for Voltage Control
+	 * I2C clk - high period 40% low period 60%
+	 */
+	speed_khz /= 10;
+	cycles_hi = sys_clk_khz * 4 / speed_khz;
+	cycles_low = sys_clk_khz * 6 / speed_khz;
+	/* values to be set in register - less by 5 & 7 respectively */
+	cycles_hi -= 5;
+	cycles_low -= 7;
+	val = (cycles_hi << PRM_VC_CFG_I2C_CLK_SCLH_SHIFT) |
+	       (cycles_low << PRM_VC_CFG_I2C_CLK_SCLL_SHIFT);
+	writel(val, &prcm->prm_vc_cfg_i2c_clk);
+
+	val = CONFIG_OMAP_VC_I2C_HS_MCODE <<
+		PRM_VC_CFG_I2C_MODE_HSMCODE_SHIFT;
+	/* No HS mode for now */
+	val &= ~PRM_VC_CFG_I2C_MODE_HSMODEEN_BIT;
+	writel(val, &prcm->prm_vc_cfg_i2c_mode);
+}
+
+/**
+ * omap_vc_bypass_send_value() - Send a data using VC Bypass command
+ * @sa:		7 bit I2C slave address of the PMIC
+ * @reg_addr:	I2C register address(8 bit) address in PMIC
+ * @reg_data:	what 8 bit data to write
+ */
+int omap_vc_bypass_send_value(u8 sa, u8 reg_addr, u8 reg_data)
+{
+	/*
+	 * Unfortunately we need to loop here instead of a defined time
+	 * use arbitary large value
+	 */
+	u32 timeout = 0xFFFF;
+	u32 reg_val;
+
+	sa &= PRM_VC_VAL_BYPASS_SLAVEADDR_MASK;
+	reg_addr &= PRM_VC_VAL_BYPASS_REGADDR_MASK;
+	reg_data &= PRM_VC_VAL_BYPASS_DATA_MASK;
+
+	/* program VC to send data */
+	reg_val = sa << PRM_VC_VAL_BYPASS_SLAVEADDR_SHIFT |
+	    reg_addr << PRM_VC_VAL_BYPASS_REGADDR_SHIFT |
+	    reg_data << PRM_VC_VAL_BYPASS_DATA_SHIFT;
+	writel(reg_val, &prcm->prm_vc_val_bypass);
+
+	/* Signal VC to send data */
+	writel(reg_val | PRM_VC_VAL_BYPASS_VALID_BIT, &prcm->prm_vc_val_bypass);
+
+	/* Wait on VC to complete transmission */
+	do {
+		reg_val = readl(&prcm->prm_vc_val_bypass) &
+				PRM_VC_VAL_BYPASS_VALID_BIT;
+		if (!reg_val)
+			break;
+
+		sdelay(100);
+	} while (--timeout);
+
+	/* Optional: cleanup PRM_IRQSTATUS_Ax */
+	/* In case we can do something about it in future.. */
+	if (!timeout)
+		return -1;
+
+	/* All good.. */
+	return 0;
+}
diff --git a/arch/arm/cpu/armv7/omap4/clocks.c b/arch/arm/cpu/armv7/omap4/clocks.c
index e2189f7..976cfd5 100644
--- a/arch/arm/cpu/armv7/omap4/clocks.c
+++ b/arch/arm/cpu/armv7/omap4/clocks.c
@@ -275,7 +275,7 @@
 {
 	u32 volt, omap_rev;
 
-	setup_sri2c();
+	omap_vc_init(PRM_VC_I2C_CHANNEL_FREQ_KHZ);
 
 	omap_rev = omap_revision();
 	/* TPS - supplies vdd_mpu on 4460 */
diff --git a/arch/arm/cpu/armv7/omap5/clocks.c b/arch/arm/cpu/armv7/omap5/clocks.c
index dd882a2..0a614f0 100644
--- a/arch/arm/cpu/armv7/omap5/clocks.c
+++ b/arch/arm/cpu/armv7/omap5/clocks.c
@@ -243,7 +243,7 @@
 {
 	u32 volt;
 
-	setup_sri2c();
+	omap_vc_init(PRM_VC_I2C_CHANNEL_FREQ_KHZ);
 
 	/* Enable 1.22V from TPS for vdd_mpu */
 	volt = 1220;
diff --git a/arch/arm/include/asm/arch-omap4/clocks.h b/arch/arm/include/asm/arch-omap4/clocks.h
index cd304e8..4e93950 100644
--- a/arch/arm/include/asm/arch-omap4/clocks.h
+++ b/arch/arm/include/asm/arch-omap4/clocks.h
@@ -652,23 +652,9 @@
 #define OMAP_SYS_CLK_IND_38_4_MHZ	6
 #define OMAP_32K_CLK_FREQ		32768
 
-/* PRM_VC_CFG_I2C_CLK */
-#define PRM_VC_CFG_I2C_CLK_SCLH_SHIFT		0
-#define PRM_VC_CFG_I2C_CLK_SCLH_MASK		0xFF
-#define PRM_VC_CFG_I2C_CLK_SCLL_SHIFT		8
-#define PRM_VC_CFG_I2C_CLK_SCLL_MASK		(0xFF << 8)
-
 /* PRM_VC_VAL_BYPASS */
 #define PRM_VC_I2C_CHANNEL_FREQ_KHZ	400
 
-#define PRM_VC_VAL_BYPASS_VALID_BIT	0x1000000
-#define PRM_VC_VAL_BYPASS_SLAVEADDR_SHIFT	0
-#define PRM_VC_VAL_BYPASS_SLAVEADDR_MASK	0x7F
-#define PRM_VC_VAL_BYPASS_REGADDR_SHIFT		8
-#define PRM_VC_VAL_BYPASS_REGADDR_MASK		0xFF
-#define PRM_VC_VAL_BYPASS_DATA_SHIFT		16
-#define PRM_VC_VAL_BYPASS_DATA_MASK		0xFF
-
 /* SMPS */
 #define SMPS_I2C_SLAVE_ADDR	0x12
 #define SMPS_REG_ADDR_VCORE1	0x55
@@ -757,7 +743,6 @@
 void do_scale_tps62361(u32 reg, u32 volt_mv);
 u32 omap_ddr_clk(void);
 void do_scale_vcore(u32 vcore_reg, u32 volt_mv);
-void setup_sri2c(void);
 void setup_post_dividers(u32 *const base, const struct dpll_params *params);
 u32 get_sys_clk_index(void);
 void enable_basic_clocks(void);
diff --git a/arch/arm/include/asm/arch-omap4/sys_proto.h b/arch/arm/include/asm/arch-omap4/sys_proto.h
index b8dbc2c..101eb46 100644
--- a/arch/arm/include/asm/arch-omap4/sys_proto.h
+++ b/arch/arm/include/asm/arch-omap4/sys_proto.h
@@ -55,6 +55,8 @@
 u32 cortex_rev(void);
 void init_omap_revision(void);
 void do_io_settings(void);
+void omap_vc_init(u16 speed_khz);
+int omap_vc_bypass_send_value(u8 sa, u8 reg_addr, u8 reg_data);
 /*
  * This is used to verify if the configuration header
  * was executed by Romcode prior to control of transfer
diff --git a/arch/arm/include/asm/arch-omap5/clocks.h b/arch/arm/include/asm/arch-omap5/clocks.h
index d0e6dd6..28b9ff7 100644
--- a/arch/arm/include/asm/arch-omap5/clocks.h
+++ b/arch/arm/include/asm/arch-omap5/clocks.h
@@ -615,23 +615,9 @@
 #define OMAP_SYS_CLK_IND_38_4_MHZ	6
 #define OMAP_32K_CLK_FREQ		32768
 
-/* PRM_VC_CFG_I2C_CLK */
-#define PRM_VC_CFG_I2C_CLK_SCLH_SHIFT		0
-#define PRM_VC_CFG_I2C_CLK_SCLH_MASK		0xFF
-#define PRM_VC_CFG_I2C_CLK_SCLL_SHIFT		8
-#define PRM_VC_CFG_I2C_CLK_SCLL_MASK		(0xFF << 8)
-
 /* PRM_VC_VAL_BYPASS */
 #define PRM_VC_I2C_CHANNEL_FREQ_KHZ	400
 
-#define PRM_VC_VAL_BYPASS_VALID_BIT	0x1000000
-#define PRM_VC_VAL_BYPASS_SLAVEADDR_SHIFT	0
-#define PRM_VC_VAL_BYPASS_SLAVEADDR_MASK	0x7F
-#define PRM_VC_VAL_BYPASS_REGADDR_SHIFT		8
-#define PRM_VC_VAL_BYPASS_REGADDR_MASK		0xFF
-#define PRM_VC_VAL_BYPASS_DATA_SHIFT		16
-#define PRM_VC_VAL_BYPASS_DATA_MASK		0xFF
-
 /* SMPS */
 #define SMPS_I2C_SLAVE_ADDR	0x12
 #define SMPS_REG_ADDR_VCORE1	0x55
@@ -703,7 +689,6 @@
 void do_scale_tps62361(u32 reg, u32 volt_mv);
 u32 omap_ddr_clk(void);
 void do_scale_vcore(u32 vcore_reg, u32 volt_mv);
-void setup_sri2c(void);
 void setup_post_dividers(u32 *const base, const struct dpll_params *params);
 u32 get_sys_clk_index(void);
 void enable_basic_clocks(void);
diff --git a/arch/arm/include/asm/arch-omap5/sys_proto.h b/arch/arm/include/asm/arch-omap5/sys_proto.h
index 40a7c57..3b39dbd 100644
--- a/arch/arm/include/asm/arch-omap5/sys_proto.h
+++ b/arch/arm/include/asm/arch-omap5/sys_proto.h
@@ -55,6 +55,8 @@
 u32 cortex_rev(void);
 void init_omap_revision(void);
 void do_io_settings(void);
+void omap_vc_init(u16 speed_khz);
+int omap_vc_bypass_send_value(u8 sa, u8 reg_addr, u8 reg_data);
 
 /*
  * This is used to verify if the configuration header