diff --git a/Documentation/devicetree/bindings/power/omap-vc.txt b/Documentation/devicetree/bindings/power/omap-vc.txt
new file mode 100644
index 0000000..f97737c
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/omap-vc.txt
@@ -0,0 +1,99 @@
+Voltage Controller driver for Texas Instruments OMAP SoCs
+
+Voltage Controller Properties:
+The following are the properties of the voltage controller node
+Required Properties:
+- compatible: Should be one of:
+  - "ti,omap3-vc" - for OMAP3 family of devices
+  - "ti,omap4-vc" - for OMAP4 family of devices
+  - "ti,omap5-vc" - for OMAP5 family of devices
+- reg: Address and length of the register set for the device. It contains
+  the information of registers in the same order as described by reg-names
+- reg-names: Should contain the reg names
+  - "base-address"	- contains base address of VC module
+- clocks: should point to the clock node used by VC module, usually sysclk
+- ti,i2c-clk-scl-low: is mandatory if ti,i2c-pad-load is not used. contains
+  hex to represent timing for slow I2C phase low clock time.
+- ti,i2c-clk-scl-high: is mandatory if ti,i2c-pad-load is not used. contains
+  hex to represent timing for slow I2C phase high clock time.
+- ti,i2c-clk-hsscl-low: is mandatory if ti,i2c-pad-load is not used and
+  ti,i2c-high-speed is used, contains hex to represent timing for high speed I2C
+  phase low clock time.
+- ti,i2c-clk-hsscl-high: is mandatory if ti,i2c-pad-load is not used and
+  ti,i2c-high-speed is used, contains hex to represent timing for high speed I2C
+  phase high clock time.
+- Must contain VC channel nodes which belong to the Voltage controller.
+
+Optional Properties:
+- ti,i2c-high-speed: bool to indicate if VC should operate in high speed I2C
+  mode.
+- ti,i2c-pad-load: if ti,i2c-high-speed, then this is optional to auto load
+  pre-calculated I2C clock timing configuration. This is denoted in pico-farads.
+- ti,i2c-pcb-length: if ti,i2c-pad-load, then this is optional to select the
+  pre-calculated I2C clock timing configuration. default of '63' is used.
+  This is denoted in millimeters.
+- pinctrl: Most OMAP SoCs do not allow pinctrl option to select VC's I2C path.
+  it is usually hardcoded by default. Define "default" pinctrl-0 as needed.
+
+Voltage Controller Channel Properties:
+The following are the properties of the voltage controller channel nodes
+Required Properties:
+- compatible: Should be one of:
+  - ti,omap3-vc-channel-0 - Channel 0 on OMAP3 family of devices
+  - ti,omap3-vc-channel-1 - Channel 1 on OMAP3 family of devices
+  - ti,omap4-vc-channel-mpu - Channel MPU on OMAP4 family of devices
+  - ti,omap4-vc-channel-iva - Channel IVA on OMAP4 family of devices
+  - ti,omap4-vc-channel-core - Channel CORE on OMAP4 family of devices
+  - ti,omap5-vc-channel-mpu - Channel MPU on OMAP5 family of devices
+  - ti,omap5-vc-channel-mm - Channel MM on OMAP5 family of devices
+  - ti,omap5-vc-channel-core - Channel CORE on OMAP5 family of devices
+- ti,master-channel: bool to mark the "master channel". Only ONE channel is
+  to be marked as master channel depending on SoC specification.
+
+Optional Properties:
+- ti,retention-micro-volts: retention voltage for the voltage controller in
+  micro-volts
+- ti,off-micro-volts: OFF mode voltage for the voltage controller in micro-volts
+- ti,use-master-slave-addr: available only for a "non-master" channel. This
+  indicates the the channel will use master channel's slave address. Usually
+  used when the PMIC has the same slave address for all SMPSs.
+- ti,use-master-voltage-reg-addr: available only for a "non-master" channel.
+  channel will use voltage register of the master channel to send voltage
+  commands - usually used in "ganged" voltage rail configuration - NOTE: this
+  is useful only when doing voltage transitions alone and not using AVS.
+- ti,use-master-command-reg-addr: available only for a "non-master" channel.
+  channel will use command register of the master channel to send command
+  values - usually used in "ganged" voltage rail configuration - NOTE: this
+  is useful only when doing voltage transitions alone and not using AVS.
+- ti,use-master-command-reg-val: available only for a "non-master" channel.
+  channel will use command value of the master channel to send commands
+  - usually used in "ganged" voltage rail configuration - NOTE: this
+  is useful only when doing voltage transitions alone and not using AVS.
+
+Example:
+SoC file: (example from OMAP4)
+vc: vc@0x4A307B88 {
+		compatible = "ti,omap4-vc";
+		clocks = <&sysclk_in>;
+		reg = <0x4A307B88 0x40>;
+		reg-names = "base-address";
+
+		vc_mpu: vc_mpu {
+			compatible = "ti,omap4-vc-channel-mpu";
+			ti,master-channel;
+			ti,retention-micro-volts = <750000>;
+			ti,off-micro-volts = <0>;
+		};
+
+		vc_iva: vc_iva {
+			compatible = "ti,omap4-vc-channel-iva";
+			ti,retention-micro-volts = <750000>;
+			ti,off-micro-volts = <0>;
+		};
+
+		vc_core: vc_core {
+			compatible = "ti,omap4-vc-channel-core";
+			ti,retention-micro-volts = <750000>;
+			ti,off-micro-volts = <0>;
+		};
+	};
diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig
index 2a1008b..68dff06 100644
--- a/drivers/power/avs/Kconfig
+++ b/drivers/power/avs/Kconfig
@@ -10,3 +10,18 @@ menuconfig POWER_AVS
 	  AVS is also called SmartReflex on OMAP devices.
 
 	  Say Y here to enable Adaptive Voltage Scaling class support.
+
+config POWER_TI_HARDWARE_VOLTAGE_CONTROL
+	tristate "TI OMAP SoC Voltage Control support"
+	depends on OF && ARCH_OMAP && REGULATOR_TI_OMAP_PMIC
+	select REGMAP_MMIO
+	help
+	  Select this option to support Texas Instruments' custom Voltage
+	  Processor + Voltage Controller data interface used in OMAP SoCs
+	  to enable Adaptive Voltage Scaling(AVS) and Device Frequency and
+	  Voltage Scaling(DVFS). This is a specific "write-only" interface
+	  designed to interface with I2C based PMICs. These two subsystems
+	  in OMAP do not exist independent of each other as they are
+	  practically useless without each other.
+
+	  Say Y here to enable TI OMAP SoC Voltage Control support.
diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile
index 0843386..95d5f59 100644
--- a/drivers/power/avs/Makefile
+++ b/drivers/power/avs/Makefile
@@ -1 +1,21 @@
 obj-$(CONFIG_POWER_AVS_OMAP)		+= smartreflex.o
+
+ifneq ($(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL),)
+
+# OMAP Common
+omap-volt-common			=  omap_vc.o
+
+# OMAP SoC specific
+ifneq ($(CONFIG_ARCH_OMAP3),)
+obj-$(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL) += $(omap-volt-common)
+endif
+
+ifneq ($(CONFIG_ARCH_OMAP4),)
+obj-$(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL) += $(omap-volt-common)
+endif
+
+ifneq ($(CONFIG_SOC_OMAP5),)
+obj-$(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL) += $(omap-volt-common)
+endif
+
+endif
diff --git a/drivers/power/avs/omap_vc.c b/drivers/power/avs/omap_vc.c
new file mode 100644
index 0000000..53f1c63
--- /dev/null
+++ b/drivers/power/avs/omap_vc.c
@@ -0,0 +1,1513 @@
+/*
+ * OMAP Voltage Controller (VC) interface
+ *
+ * Idea based on arch/arm/mach-omap2/vc.c
+ * Copyright (C) 2011 Texas Instruments Incorporated.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ * Taras Kondratiuk
+ * Grygorii Strashko
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/omap-pmic-regulator.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include "omap_vc.h"
+
+#define DRIVER_NAME	"omap-vc"
+
+/**
+ * struct vc_channel_regs - Register description for VC channel
+ * @cfg_reg_offset:  Channel configuration register offset
+ * @sa_reg_offset: Slave Address configuration register offset
+ * @voltage_reg_offset: Voltage address register offset
+ * @command_addr_reg_offset: Command address register offset
+ * @command_val_reg_offset: Command value register offset
+ *
+ * NOTE: All register offsets are from VC base, as certain flavor
+ * of SoCs use shared registers and other have unique register offsets
+ */
+struct vc_channel_regs {
+	u16 cfg_reg_offset;
+	u16 sa_reg_offset;
+	u16 voltage_reg_offset;
+	u16 command_addr_reg_offset;
+	u16 command_val_reg_offset;
+
+	/* private: */
+	/* cfg_reg_offset details */
+	u32 racen_mask;
+	u32 rac_enable_mask;
+	u32 rav_enable_mask;
+	u32 cmd_sel_mask;
+	u32 sa_enable_mask;
+
+	/* sa_reg_offset details */
+	u32 sa_addr_mask;
+
+	/* voltage_reg_offset details */
+	u32 voltage_addr_mask;
+
+	/* command_addr_reg_offset details */
+	u32 command_addr_mask;
+
+	/* command_val_reg_offset details */
+	u32 command_val_on_mask;
+	u32 command_val_onlp_mask;
+	u32 command_val_ret_mask;
+	u32 command_val_off_mask;
+};
+
+/**
+ * struct omap_vc_channel - internal representation of VC channel
+ * @dev:		device pointer for VC channel device
+ * @list:		channel list
+ * @usage_count:	Usage count - only 1 user at a time.(not always module)
+ * @is_master_channel:	Is this channel the master channel?
+ * @use_master_channel_sa: if this channel uses master channel's slave address
+ * @use_master_channel_voltage_reg: if this channel uses master channel's
+ *			voltage register address
+ * @use_master_channel_cmd_reg: if this channel uses master channel's command
+ *			register address
+ * @use_master_channel_cmd_val: if this channel uses master channel's command
+ *			value address
+ * @ch_regs:		Channel register description
+ * @info:		exported information containing PMIC hooked to this
+ *			channel, low power state voltage information etc.
+ */
+struct omap_vc_channel {
+	struct device *dev;
+	struct list_head list;
+	int usage_count;
+	bool is_master_channel;
+	bool use_master_channel_sa;
+	bool use_master_channel_voltage_reg;
+	bool use_master_channel_cmd_reg;
+	bool use_master_channel_cmd_val;
+	const struct vc_channel_regs *ch_regs;
+	struct omap_vc_channel_info info;
+};
+
+/**
+ * struct omap_omap_vc_i2c_config - Voltage controller channel's I2C config
+ * @clk_rate:		Frequency of sys_clock for the voltage controller's I2C
+ * @highspeed:		used in I2C highspeed mode?
+ * @mcode:		Master Code used in High speed mode
+ * @i2c_clk_pad_load:	What is the pad load at i2c
+ * @i2c_clk_pcb_length:	What is the pcb length to the PMIC (all inclusive)
+ * @i2c_clk_scll:	Clock low timing config for slow speed
+ * @i2c_clk_sclh:	Clock high timing config for slow speed
+ * @i2c_clk_hsscll:	Clock low timing config for high speed
+ * @i2c_clk_hssclh:	Clock high timing config for high speed
+ *
+ * Used one time at boot.
+ */
+struct omap_omap_vc_i2c_config {
+	unsigned long clk_rate;
+	bool highspeed;
+	u8 mcode;
+	u32 i2c_clk_pad_load;
+	u32 i2c_clk_pcb_length;
+	u8 i2c_clk_scll;
+	u8 i2c_clk_sclh;
+	u8 i2c_clk_hsscll;
+	u8 i2c_clk_hssclh;
+};
+
+/**
+ * struct omap_vc_common_reg - Voltage Controller Common registers
+ * @i2c_clk_cfg_reg:	I2C clock configuration register offset
+ * @i2c_clk_mode_reg:	I2C clock operation mode register offset
+ * @bypass_cmd_reg:	VC_BYPASS (very low level control) register offset
+ *
+ * These may be -1 to indicate register not being present.
+ */
+struct omap_vc_common_reg {
+	s16 i2c_clk_cfg_reg;
+	u16 i2c_clk_mode_reg;
+	u16 bypass_cmd_reg;
+};
+
+/**
+ * struct omap_vc - represents the voltage controller(vc) device
+ * @regmap:	regmap to the entire voltage controller region(includes channel)
+ * @regs:	Voltage Controller common registers
+ * @master_channel_configured:	Is Master channel has been configured.
+ */
+struct omap_vc {
+	struct regmap *regmap;
+	const struct omap_vc_common_reg *regs;
+	bool master_channel_configured;
+};
+
+/* VC Channel register configurations */
+static const struct vc_channel_regs omap3_ch_1_regs = {
+	.cfg_reg_offset = 0x14,
+	.racen_mask = 0x80000,
+	.rac_enable_mask = 0x40000,
+	.rav_enable_mask = 0x20000,
+	.cmd_sel_mask = 0x100000,
+	.sa_enable_mask = 0x10000,
+
+	.sa_reg_offset = 0x0,
+	.sa_addr_mask = 0x7f0000,
+
+	.voltage_reg_offset = 0x4,
+	.voltage_addr_mask = 0xFF0000,
+
+	.command_addr_reg_offset = 0x8,
+	.command_addr_mask = 0xFF0000,
+
+	.command_val_reg_offset = 0x10,
+	.command_val_on_mask = 0xFF000000,
+	.command_val_onlp_mask = 0xFF0000,
+	.command_val_ret_mask = 0xFF00,
+	.command_val_off_mask = 0xFF,
+};
+
+static const struct vc_channel_regs omap3_ch_0_regs = {
+	.cfg_reg_offset = 0x14,
+	.racen_mask = 0x8,
+	.rac_enable_mask = 0x4,
+	.rav_enable_mask = 0x2,
+	.cmd_sel_mask = 0x10,
+	.sa_enable_mask = 0x1,
+
+	.sa_reg_offset = 0x0,
+	.sa_addr_mask = 0x7f,
+
+	.voltage_reg_offset = 0x4,
+	.voltage_addr_mask = 0xFF,
+
+	.command_addr_reg_offset = 0x8,
+	.command_addr_mask = 0xFF,
+
+	.command_val_reg_offset = 0xC,
+	.command_val_on_mask = 0xFF000000,
+	.command_val_onlp_mask = 0xFF0000,
+	.command_val_ret_mask = 0xFF00,
+	.command_val_off_mask = 0xFF,
+};
+
+static const struct vc_channel_regs omap4_ch_mpu_regs = {
+	.cfg_reg_offset = 0x1c,
+	.racen_mask = 0x100000,
+	.rac_enable_mask = 0x80000,
+	.rav_enable_mask = 0x40000,
+	.cmd_sel_mask = 0x20000,
+	.sa_enable_mask = 0x10000,
+
+	.sa_reg_offset = 0x0,
+	.sa_addr_mask = 0x7f0000,
+
+	.voltage_reg_offset = 0x4,
+	.voltage_addr_mask = 0xFF0000,
+
+	.command_addr_reg_offset = 0x8,
+	.command_addr_mask = 0xFF0000,
+
+	.command_val_reg_offset = 0x10,
+	.command_val_on_mask = 0xFF000000,
+	.command_val_onlp_mask = 0xFF0000,
+	.command_val_ret_mask = 0xFF00,
+	.command_val_off_mask = 0xFF,
+};
+
+static const struct vc_channel_regs omap4_ch_iva_regs = {
+	.cfg_reg_offset = 0x1c,
+	.racen_mask = 0x800,
+	.rac_enable_mask = 0x400,
+	.rav_enable_mask = 0x200,
+	.cmd_sel_mask = 0x1000,
+	.sa_enable_mask = 0x100,
+
+	.sa_reg_offset = 0x0,
+	.sa_addr_mask = 0x7f00,
+
+	.voltage_reg_offset = 0x4,
+	.voltage_addr_mask = 0xFF00,
+
+	.command_addr_reg_offset = 0x8,
+	.command_addr_mask = 0xFF00,
+
+	.command_val_reg_offset = 0x14,
+	.command_val_on_mask = 0xFF000000,
+	.command_val_onlp_mask = 0xFF0000,
+	.command_val_ret_mask = 0xFF00,
+	.command_val_off_mask = 0xFF,
+};
+
+static const struct vc_channel_regs omap4_ch_core_regs = {
+	.cfg_reg_offset = 0x1c,
+	.racen_mask = 0x8,
+	.rac_enable_mask = 0x4,
+	.rav_enable_mask = 0x2,
+	.cmd_sel_mask = 0x10,
+	.sa_enable_mask = 0x1,
+
+	.sa_reg_offset = 0x0,
+	.sa_addr_mask = 0x7f,
+
+	.voltage_reg_offset = 0x4,
+	.voltage_addr_mask = 0xFF,
+
+	.command_addr_reg_offset = 0x8,
+	.command_addr_mask = 0xFF,
+
+	.command_val_reg_offset = 0xC,
+	.command_val_on_mask = 0xFF000000,
+	.command_val_onlp_mask = 0xFF0000,
+	.command_val_ret_mask = 0xFF00,
+	.command_val_off_mask = 0xFF,
+};
+
+static const struct vc_channel_regs omap5_ch_core_regs = {
+	.cfg_reg_offset = 0x0,
+	.racen_mask = 0x8000000,
+	.rac_enable_mask = 0x4000000,
+	.rav_enable_mask = 0x2000000,
+	.cmd_sel_mask = 0x10000000,
+	.sa_enable_mask = 0x1000000,
+
+	.sa_reg_offset = 0x0,
+	.sa_addr_mask = 0x7f,
+
+	.voltage_reg_offset = 0x0,
+	.voltage_addr_mask = 0xFF00,
+
+	.command_addr_reg_offset = 0x0,
+	.command_addr_mask = 0xFF0000,
+
+	.command_val_reg_offset = 0xC,
+	.command_val_on_mask = 0xFF000000,
+	.command_val_onlp_mask = 0xFF0000,
+	.command_val_ret_mask = 0xFF00,
+	.command_val_off_mask = 0x00,	/* Reserved */
+};
+
+static const struct vc_channel_regs omap5_ch_mm_regs = {
+	.cfg_reg_offset = 0x4,
+	.racen_mask = 0x8000000,
+	.rac_enable_mask = 0x4000000,
+	.rav_enable_mask = 0x2000000,
+	.cmd_sel_mask = 0x10000000,
+	.sa_enable_mask = 0x1000000,
+
+	.sa_reg_offset = 0x4,
+	.sa_addr_mask = 0x7f,
+
+	.voltage_reg_offset = 0x4,
+	.voltage_addr_mask = 0xFF00,
+
+	.command_addr_reg_offset = 0x4,
+	.command_addr_mask = 0xFF0000,
+
+	.command_val_reg_offset = 0x10,
+	.command_val_on_mask = 0xFF000000,
+	.command_val_onlp_mask = 0xFF0000,
+	.command_val_ret_mask = 0xFF00,
+	.command_val_off_mask = 0x00,	/* Reserved */
+};
+
+static const struct vc_channel_regs omap5_ch_mpu_regs = {
+	.cfg_reg_offset = 0x8,
+	.racen_mask = 0x8000000,
+	.rac_enable_mask = 0x4000000,
+	.rav_enable_mask = 0x2000000,
+	.cmd_sel_mask = 0x10000000,
+	.sa_enable_mask = 0x1000000,
+
+	.sa_reg_offset = 0x8,
+	.sa_addr_mask = 0x7f,
+
+	.voltage_reg_offset = 0x8,
+	.voltage_addr_mask = 0xFF00,
+
+	.command_addr_reg_offset = 0x8,
+	.command_addr_mask = 0xFF0000,
+
+	.command_val_reg_offset = 0x14,
+	.command_val_on_mask = 0xFF000000,
+	.command_val_onlp_mask = 0xFF0000,
+	.command_val_ret_mask = 0xFF00,
+	.command_val_off_mask = 0x00,	/* Reserved */
+};
+
+/* VC Generic register configurations */
+static const struct omap_vc_common_reg omap3_vc_regs = {
+	.i2c_clk_cfg_reg = -1,
+	/* NOTE: register is called I2C_CFG in TRM, but compatible with MODE */
+	.i2c_clk_mode_reg = 0x18,
+	.bypass_cmd_reg = 0x1C,
+};
+
+static const struct omap_vc_common_reg omap4_vc_regs = {
+	.i2c_clk_cfg_reg = 0x24,
+	.i2c_clk_mode_reg = 0x20,
+	.bypass_cmd_reg = 0x18,
+};
+
+static const struct omap_vc_common_reg omap5_vc_regs = {
+	.i2c_clk_cfg_reg = 0x30,
+	.i2c_clk_mode_reg = 0x2c,
+	.bypass_cmd_reg = 0x18,
+};
+
+/* VCBYPASS register bit definitions - No variance at all */
+#define VC_BYPASS_VALID_MASK	(0x01 << 24)
+#define VC_BYPASS_DATA_MASK	(0xff << 16)
+#define VC_BYPASS_REGADDR_MASK	(0xff << 8)
+#define VC_BYPASS_SA_MASK	(0x7f << 0)
+/* I2C_CLK_MODE register bit definitions - No variance at all */
+#define VC_MODE_REPEAT_START_MASK	BIT(4)
+#define VC_MODE_HSENABLE_MASK		BIT(3)
+#define VC_MODE_MCODE_MASK		(0x3 << 0)
+/* I2C_CLK_CONFIG register bit definitions - No variance at all */
+#define VC_CLK_CFG_HSSCLL_MASK		(0xff << 24)
+#define VC_CLK_CFG_HSSCLH_MASK		(0xff << 16)
+#define VC_CLK_CFG_SCLL_MASK		(0xff << 8)
+#define VC_CLK_CFG_SCLH_MASK		(0xff << 0)
+
+/* Stores the list containing all voltage controller channels */
+static LIST_HEAD(omap_vc_channel_list);
+static DEFINE_MUTEX(omap_vc_channel_list_mutex);
+
+/**
+ * omap_vc_channel_set_on_voltage() - Update the ON transition voltage
+ * @info:	channel info (must be valid pointer from
+ *		devm_omap_vc_channel_get)
+ * @uv:		ON voltage in micro volts.
+ *
+ * Updates the voltage that OMAP comes back to when resuming from a low
+ * power state. It is recommended to invoke this as part of voltage transition
+ * as the low power transitions are more performance sensitive that voltage
+ * transition path (part of DVFS).
+ *
+ * This needs to be invoked either as part of DVFS sequence or prior to
+ * attempting to enter low power state.
+ *
+ * NOTE: no extra error handling is performed to reduce overhead as much as
+ * sanely possible. No explicit locks are needed as regmap takes care of
+ * the same.
+ *
+ * Return: 0 if all operations are successful, else returns appropriate
+ * error value.
+ */
+int omap_vc_channel_set_on_voltage(struct omap_vc_channel_info *info, u32 uv)
+{
+	struct omap_vc *vc;
+	struct omap_vc_channel *vc_channel;
+	struct device *dev;
+	const struct vc_channel_regs *ch_regs;
+	struct omap_pmic *pmic;
+	struct omap_pmic_ops *ops;
+	struct regmap *regmap;
+	int ret = 0;
+	u32 val = 0;
+	u8 vsel;
+
+	if (!info) {
+		pr_err("Bad parameters\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	vc_channel = info->ch;
+	dev = vc_channel->dev;
+	ch_regs = vc_channel->ch_regs;
+	pmic = info->pmic;
+	ops = pmic->ops;
+
+	vc = dev_get_drvdata(dev->parent);
+	if (!vc) {
+		dev_err(dev, "Unable to find parent VC data\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = ops->uv_to_vsel(pmic, uv, &vsel);
+	if (ret) {
+		dev_err(dev, "%s: Conversion onV %d to vsel fail(%d)\n",
+			__func__, uv, ret);
+		goto out;
+	}
+	regmap = vc->regmap;
+
+	if (ch_regs->command_val_onlp_mask) {
+		val = vsel << __ffs(ch_regs->command_val_onlp_mask);
+		ret = regmap_update_bits(regmap,
+					 ch_regs->command_val_reg_offset,
+					 ch_regs->command_val_onlp_mask,
+					 val);
+	}
+	if (ret)
+		goto fail_reg;
+
+	if (ch_regs->command_val_on_mask) {
+		val = vsel << __ffs(ch_regs->command_val_on_mask);
+		ret = regmap_update_bits(regmap,
+					 ch_regs->command_val_reg_offset,
+					 ch_regs->command_val_on_mask, val);
+	}
+
+fail_reg:
+	if (ret)
+		dev_err(dev, "%s: Register operation failed with %d\n",
+			__func__, ret);
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(omap_vc_channel_set_on_voltage);
+
+/**
+ * omap_vc_channel_setup_lp() - Low power voltage configuration
+ * @vc_channel:	VC channel to configure for
+ * @vc:		VC to which this channel belongs to.
+ *
+ * The Low power states such as OFF, RETENTION are pre-determined SoC specific
+ * voltage values. These can be configured at boot time and the voltages are
+ * achieved as needed. This also configures the boot voltage as ON voltage if
+ * it is available.
+ */
+static int omap_vc_channel_setup_lp(struct omap_vc_channel *vc_channel,
+				    struct omap_vc *vc)
+{
+	struct device *dev = vc_channel->dev;
+	const struct vc_channel_regs *ch_regs = vc_channel->ch_regs;
+	struct regmap *regmap = vc->regmap;
+	struct omap_pmic *pmic = vc_channel->info.pmic;
+	struct omap_pmic_ops *ops = pmic->ops;
+	struct omap_vc_channel_info *info = &vc_channel->info;
+	int ret = 0;
+	u32 val;
+	u8 vsel;
+
+	if (ch_regs->command_val_off_mask) {
+		ret = ops->uv_to_vsel(pmic, info->off_uV, &vsel);
+		if (ret) {
+			dev_err(dev, "%s: Conversion OFF %d to vsel fail(%d)\n",
+				__func__, info->off_uV, ret);
+			goto out;
+		}
+		val = vsel << __ffs(ch_regs->command_val_off_mask);
+		ret = regmap_update_bits(regmap,
+					 ch_regs->command_val_reg_offset,
+					 ch_regs->command_val_off_mask, val);
+		if (ret)
+			goto fail_reg;
+	}
+
+	if (ch_regs->command_val_ret_mask) {
+		ret = ops->uv_to_vsel(pmic, info->retention_uV, &vsel);
+		if (ret) {
+			dev_err(dev, "%s: Conversion RET %d to vsel fail(%d)\n",
+				__func__, info->retention_uV, ret);
+			goto out;
+		}
+		val = vsel << __ffs(ch_regs->command_val_ret_mask);
+		ret = regmap_update_bits(regmap,
+					 ch_regs->command_val_reg_offset,
+					 ch_regs->command_val_ret_mask, val);
+		if (ret)
+			goto fail_reg;
+	}
+
+	if (pmic->boot_voltage_uV && (ch_regs->command_val_on_mask |
+				      ch_regs->command_val_onlp_mask)) {
+		ret = ops->uv_to_vsel(pmic, pmic->boot_voltage_uV, &vsel);
+		if (ret) {
+			dev_err(dev, "%s: Conversion ON %d to vsel fail(%d)\n",
+				__func__,  pmic->boot_voltage_uV, ret);
+			goto out;
+		}
+	}
+	if (pmic->boot_voltage_uV && ch_regs->command_val_on_mask) {
+		val = vsel << __ffs(ch_regs->command_val_on_mask);
+		ret = regmap_update_bits(regmap,
+					 ch_regs->command_val_reg_offset,
+					 ch_regs->command_val_on_mask, val);
+		if (ret)
+			goto fail_reg;
+	}
+
+	if (pmic->boot_voltage_uV && ch_regs->command_val_onlp_mask) {
+		val = vsel << __ffs(ch_regs->command_val_onlp_mask);
+		ret = regmap_update_bits(regmap,
+					 ch_regs->command_val_reg_offset,
+					 ch_regs->command_val_onlp_mask,
+					 val);
+	}
+
+fail_reg:
+	if (ret)
+		dev_err(dev, "%s: Register operation failed with %d\n",
+			__func__, ret);
+
+out:
+	return ret;
+}
+
+/**
+ * omap_vc_send_msg() - Send a VC bypass command
+ * @vc:		Voltage controller
+ * @vc_channel:	Voltage controller channel
+ * @reg_addr:	Register address to write to
+ * @data:	data to write.
+ *
+ * This bypasses all channel scheduling mechanisms inside Voltage controller
+ * and must be used sparingly in controlled environments. Using this to scale
+ * voltage is NOT a recommended procedure.
+ *
+ * The only safe usage is when Voltage Processors, SmartReflex, VFSM(PRCM)
+ * is in idle or known state - example at boot, this may be used for
+ * configuration.
+ */
+static int omap_vc_send_msg(struct omap_vc *vc,
+			    struct omap_vc_channel *vc_channel, u8 reg_addr,
+			    u8 data)
+{
+	struct device *dev = vc_channel->dev;
+	struct regmap *regmap = vc->regmap;
+	struct omap_pmic *pmic = vc_channel->info.pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	u32 loop_cnt = 0, retries_cnt = 0;
+	u32 vc_bypass_value;
+	int ret = 0;
+
+	ret = regmap_update_bits(regmap, vc->regs->bypass_cmd_reg,
+				 VC_BYPASS_DATA_MASK,
+				 data << __ffs(VC_BYPASS_DATA_MASK));
+	if (ret)
+		goto reg_fail;
+	ret = regmap_update_bits(regmap, vc->regs->bypass_cmd_reg,
+				 VC_BYPASS_REGADDR_MASK,
+				 reg_addr << __ffs(VC_BYPASS_REGADDR_MASK));
+	if (ret)
+		goto reg_fail;
+	ret = regmap_update_bits(regmap, vc->regs->bypass_cmd_reg,
+				 VC_BYPASS_SA_MASK,
+				 pinfo->slave_addr << __ffs(VC_BYPASS_SA_MASK));
+	if (ret)
+		goto reg_fail;
+	/* Activate the transfer */
+	ret = regmap_update_bits(regmap, vc->regs->bypass_cmd_reg,
+				 VC_BYPASS_VALID_MASK, VC_BYPASS_VALID_MASK);
+	if (ret)
+		goto reg_fail;
+
+	/* See if transfer complete */
+	ret = regmap_read(regmap, vc->regs->bypass_cmd_reg, &vc_bypass_value);
+	if (ret)
+		goto reg_fail;
+
+	dev_dbg(dev,
+		"bypass_val = 0x%08x, sa=0x%02x, reg=0x%02x, data=0x%02x\n",
+		vc_bypass_value, pinfo->slave_addr, reg_addr, data);
+
+	/*
+	 * Loop which polls continously 50 times before sleeping and retry
+	 * around for atleast pinfo->i2c_timeout_us * 5. Rationale as follows:
+	 * 1) continuous poll loops for 50 times, if it cant get it, then (2)
+	 * 2) sleeps between 5-15 uSec. then try (1)
+	 * if it cant get in timeout_us *5 ish (in step 1) then give up
+	 *
+	 * The continous loop(1) is used because the first VALID setting
+	 * is expected to appear quiet fast. However, the delay occurs only
+	 * when VC internally does round robin scheduling between PRCM's VFSM,
+	 * VC channels (1-n) followed by vcbypass. Only if there is pending
+	 * transaction will the bypass valid bit get delayed, 99.99% of the
+	 * cases at this point in time(configuration and setup time), we do
+	 * not expect this conflict to take place.
+	 */
+	while (vc_bypass_value & VC_BYPASS_VALID_MASK) {
+		loop_cnt++;
+
+		/* Get at least 5 times pinfo->i2c_timeout_us for completion */
+		if (retries_cnt > pinfo->i2c_timeout_us) {
+			dev_err(dev, "%s: Retry count exceeded\n", __func__);
+			ret = -ETIMEDOUT;
+			goto out;
+		}
+
+		if (loop_cnt > 50) {
+			retries_cnt++;
+			loop_cnt = 0;
+			usleep_range(5, 15);
+		}
+		ret =
+		    regmap_read(regmap, vc->regs->bypass_cmd_reg,
+				&vc_bypass_value);
+		if (ret)
+			goto reg_fail;
+	}
+
+reg_fail:
+	if (ret)
+		dev_err(dev, "%s: register operation failed(%d)\n",
+			__func__, ret);
+
+out:
+	return ret;
+}
+
+/**
+ * omap_vc_channel_setup_pmic() - Setup PMIC configuration commands if any
+ * @vc_channel:	vc channel
+ * @vc:		vc
+ *
+ * Uses vc_bypass to send commands to the PMIC for mandatory configurations
+ * for the device to function.
+ */
+static int omap_vc_channel_setup_pmic(struct omap_vc_channel *vc_channel,
+				      struct omap_vc *vc)
+{
+	struct device *dev = vc_channel->dev;
+	struct omap_pmic *pmic = vc_channel->info.pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	const struct omap_pmic_setup_commands *cmd;
+	int i, ret = 0;
+
+	if (!pinfo->setup_num_commands)
+		return 0;
+	if (!pinfo->setup_command_list) {
+		dev_err(dev, "Bad setup command list\n");
+		return -EINVAL;
+	}
+	cmd = pinfo->setup_command_list;
+
+	for (i = 0; i < pinfo->setup_num_commands; i++, cmd++) {
+		ret = omap_vc_send_msg(vc, vc_channel, cmd->reg, cmd->cmd_val);
+		if (ret) {
+			dev_err(dev, "Failed cmd [r=0x%02x v=0x%02x] [%d]\n",
+				cmd->reg, cmd->cmd_val, ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+/* Quick helper to avoid having to define /bits/ 8 <0x20> for 8 bit params */
+static inline int vc_property_read_u8(const struct device_node *np,
+				      const char *propname, u8 *out_value)
+{
+	u32 val;
+	int r;
+
+	r = of_property_read_u32(np, propname, &val);
+	if (r)
+		return r;
+
+	if (val > 0xFF)
+		return -ERANGE;
+
+	*out_value = (u8) val;
+	return 0;
+}
+
+/**
+ * omap_vc_channel_setup_sa() - setup Slave address
+ * @vc_channel:	vc channel
+ * @vc:		vc
+ *
+ * setups the slave address configuration as needed.
+ */
+static int omap_vc_channel_setup_sa(struct omap_vc_channel *vc_channel,
+				    struct omap_vc *vc)
+{
+	struct device *dev = vc_channel->dev;
+	const struct vc_channel_regs *ch_regs = vc_channel->ch_regs;
+	struct regmap *regmap = vc->regmap;
+	struct omap_pmic *pmic = vc_channel->info.pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int ret;
+	u32 enable_mask = 0;
+
+	if (!vc_channel->use_master_channel_sa) {
+		u32 val;
+
+		val = pinfo->slave_addr << __ffs(ch_regs->sa_addr_mask);
+		ret =
+		    regmap_update_bits(regmap, ch_regs->sa_reg_offset,
+				       ch_regs->sa_addr_mask, val);
+		if (ret)
+			goto out;
+
+		/* SA bit is never set for master */
+		if (!vc_channel->is_master_channel)
+			enable_mask = ch_regs->sa_enable_mask;
+	}
+
+	ret = regmap_update_bits(regmap, ch_regs->cfg_reg_offset,
+				 ch_regs->sa_enable_mask, enable_mask);
+
+out:
+	if (ret)
+		dev_err(dev, "%s: update reg failed(%d)\n", __func__, ret);
+
+	return ret;
+}
+
+/**
+ * omap_vc_channel_setup_voltage() - configure channel's voltage register addr
+ * @vc_channel:	vc channel
+ * @vc:		voltage controller
+ */
+static int omap_vc_channel_setup_voltage(struct omap_vc_channel *vc_channel,
+					 struct omap_vc *vc)
+{
+	struct device *dev = vc_channel->dev;
+	struct regmap *regmap = vc->regmap;
+	const struct vc_channel_regs *ch_regs = vc_channel->ch_regs;
+	struct omap_pmic *pmic = vc_channel->info.pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int ret;
+	u32 enable_mask = 0;
+
+	if (!vc_channel->use_master_channel_voltage_reg) {
+		u32 val;
+
+		val = pinfo->voltage_reg_addr <<
+			__ffs(ch_regs->voltage_addr_mask);
+		ret = regmap_update_bits(regmap, ch_regs->voltage_reg_offset,
+				       ch_regs->voltage_addr_mask, val);
+		if (ret)
+			goto out;
+
+		/* RAV bit is never set for master */
+		if (!vc_channel->is_master_channel)
+			enable_mask = ch_regs->rav_enable_mask;
+	}
+	ret = regmap_update_bits(regmap, ch_regs->cfg_reg_offset,
+				 ch_regs->rav_enable_mask, enable_mask);
+out:
+	if (ret)
+		dev_err(dev, "%s: update reg failed(%d)\n", __func__, ret);
+
+	return ret;
+}
+
+/**
+ * omap_vc_channel_setup_command() - configure channel's command register addr
+ * @vc_channel:	vc channel
+ * @vc:		voltage controller
+ */
+static int omap_vc_channel_setup_command(struct omap_vc_channel *vc_channel,
+					 struct omap_vc *vc)
+{
+	struct device *dev = vc_channel->dev;
+	struct regmap *regmap = vc->regmap;
+	const struct vc_channel_regs *ch_regs = vc_channel->ch_regs;
+	struct omap_pmic *pmic = vc_channel->info.pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int ret;
+	u32 cmd_sel_mask = ch_regs->cmd_sel_mask;
+	u32 rac_enable_mask = 0;
+	u32 racen_mask = 0;
+
+	if (!vc_channel->use_master_channel_cmd_reg) {
+		u32 val;
+
+		val = pinfo->cmd_reg_addr << __ffs(ch_regs->command_addr_mask);
+		ret =
+		    regmap_update_bits(regmap, ch_regs->command_addr_reg_offset,
+				       ch_regs->command_addr_mask, val);
+		if (ret)
+			goto out;
+
+		/* RAC bit is never set for master */
+		if (!vc_channel->is_master_channel)
+			rac_enable_mask = ch_regs->rac_enable_mask;
+	}
+
+	ret = regmap_update_bits(regmap, ch_regs->cfg_reg_offset,
+				 ch_regs->rac_enable_mask, rac_enable_mask);
+	if (ret)
+		goto out;
+
+	/* Retaining legacy logic - if needed for special case, switch to DT? */
+	if (pinfo->cmd_reg_addr == pinfo->voltage_reg_addr)
+		racen_mask = ch_regs->racen_mask;
+
+	ret = regmap_update_bits(regmap, ch_regs->cfg_reg_offset,
+				 ch_regs->racen_mask, racen_mask);
+	if (ret)
+		goto out;
+
+	/* CMD bit is never set for master */
+	if (vc_channel->is_master_channel ||
+	    vc_channel->use_master_channel_cmd_val)
+		cmd_sel_mask = 0;
+
+	ret = regmap_update_bits(regmap, ch_regs->cfg_reg_offset,
+				 ch_regs->cmd_sel_mask, cmd_sel_mask);
+out:
+	if (ret)
+		dev_err(dev, "%s: update reg failed(%d)\n", __func__, ret);
+
+	return ret;
+}
+
+/* Quick helper to find the OF node for a device matching to channel */
+static struct device_node *of_get_omap_vc_channel(struct device *dev)
+{
+	struct device_node *vc_channel_node = NULL;
+	char *prop_name = "ti,vc-channel";
+
+	dev_dbg(dev, "Looking up %s from device tree\n", prop_name);
+	vc_channel_node = of_parse_phandle(dev->of_node, prop_name, 0);
+
+	if (!vc_channel_node) {
+		dev_err(dev, "Looking up %s property in node %s failed",
+			prop_name, dev->of_node->full_name);
+		return ERR_PTR(-ENODEV);
+	}
+	return vc_channel_node;
+}
+
+/* Helper to cleanup when the managed device is released */
+static void devm_omap_vc_channel_release(struct device *dev, void *res)
+{
+	struct omap_vc_channel *vc_channel = *((struct omap_vc_channel **)res);
+
+	mutex_lock(&omap_vc_channel_list_mutex);
+
+	vc_channel->usage_count--;
+	if (!vc_channel->usage_count)
+		vc_channel->info.pmic = NULL;
+	module_put(vc_channel->dev->driver->owner);
+
+	mutex_unlock(&omap_vc_channel_list_mutex);
+
+	return;
+}
+
+/**
+ * devm_omap_vc_channel_get() - managed request to get a VC channel
+ * @dev:	Generic device to handle the request for
+ * @pmic:	PMIC resource this will be assigned to
+ *
+ * Ensures that usage count is maintained. Uses managed device,
+ * so everything is undone on driver detach.
+ *
+ * Return: -EPROBE_DEFER if the node is present, however device is
+ * not yet probed.
+ * -EINVAL if bad pointers or node description is not found.
+ * -ENODEV if the property cannot be found
+ * -ENOMEM if allocation could not be done.
+ *  device pointer to vp dev if all successful. error handling should be
+ *  performed with IS_ERR
+ */
+struct omap_vc_channel_info *devm_omap_vc_channel_get(struct device *dev,
+						      struct omap_pmic *pmic)
+{
+	struct omap_vc_channel *vc_channel, **ptr;
+	struct device_node *node;
+	struct device *vc_dev;
+	struct omap_vc_channel_info *info;
+	struct omap_vc *vc;
+	int ret = 0;
+
+	if (!dev || !dev->of_node || !pmic) {
+		pr_err("Invalid parameters\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	node = of_get_omap_vc_channel(dev);
+	if (IS_ERR(node))
+		return (void *)node;
+
+	mutex_lock(&omap_vc_channel_list_mutex);
+	list_for_each_entry(vc_channel, &omap_vc_channel_list, list)
+	    if (vc_channel->dev->of_node == node)
+		goto found;
+
+	/* Node definition is present, but not probed yet.. request defer */
+	info = ERR_PTR(-EPROBE_DEFER);
+	goto out_unlock;
+
+found:
+	vc_dev = vc_channel->dev;
+	if (!try_module_get(vc_dev->driver->owner)) {
+		dev_err(dev, "%s: Cant get device owner\n", __func__);
+		info = ERR_PTR(-EINVAL);
+		goto out_unlock;
+	}
+
+	/* Allow ONLY 1 user at a time */
+	if (vc_channel->usage_count) {
+		dev_err(dev, "%s: device %s is busy..\n", __func__,
+			dev_name(vc_dev));
+		ret = -EBUSY;
+		goto out;
+	}
+	ptr = devres_alloc(devm_omap_vc_channel_release, sizeof(*ptr),
+			   GFP_KERNEL);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	info = &vc_channel->info;
+
+	vc = dev_get_drvdata(vc_dev->parent);
+	if (!vc) {
+		dev_err(dev, "Unable to find parent VC data\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	info->pmic = pmic;
+	ret = omap_vc_channel_setup_sa(vc_channel, vc);
+	if (ret)
+		goto out;
+
+	ret = omap_vc_channel_setup_voltage(vc_channel, vc);
+	if (ret)
+		goto out;
+
+	ret = omap_vc_channel_setup_command(vc_channel, vc);
+	if (ret)
+		goto out;
+
+	ret = omap_vc_channel_setup_pmic(vc_channel, vc);
+	if (ret)
+		goto out;
+
+	ret = omap_vc_channel_setup_lp(vc_channel, vc);
+	if (ret)
+		goto out;
+
+	if (vc_channel->is_master_channel)
+		vc->master_channel_configured = true;
+
+	*ptr = vc_channel;
+	vc_channel->usage_count++;
+	devres_add(dev, ptr);
+
+out:
+	if (ret) {
+		module_put(vc_dev->driver->owner);
+		info = ERR_PTR(ret);
+		dev_err(dev, "Failed setup vc with (%d)\n", ret);
+	}
+
+out_unlock:
+	mutex_unlock(&omap_vc_channel_list_mutex);
+
+	return info;
+}
+EXPORT_SYMBOL_GPL(devm_omap_vc_channel_get);
+
+static const struct of_device_id omap_vc_channel_of_match_tbl[] = {
+	{.compatible = "ti,omap3-vc-channel-0", .data = &omap3_ch_0_regs},
+	{.compatible = "ti,omap3-vc-channel-1", .data = &omap3_ch_1_regs},
+	{.compatible = "ti,omap4-vc-channel-mpu", .data = &omap4_ch_mpu_regs},
+	{.compatible = "ti,omap4-vc-channel-iva", .data = &omap4_ch_iva_regs},
+	{.compatible = "ti,omap4-vc-channel-core", .data = &omap4_ch_core_regs},
+	{.compatible = "ti,omap5-vc-channel-mpu", .data = &omap5_ch_mpu_regs},
+	{.compatible = "ti,omap5-vc-channel-mm", .data = &omap5_ch_mm_regs},
+	{.compatible = "ti,omap5-vc-channel-core", .data = &omap5_ch_core_regs},
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_vc_channel_of_match_tbl);
+
+static int omap_vc_channel_probe(struct platform_device *pdev)
+{
+	struct device *vc_dev, *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct omap_vc *vc;
+	struct omap_vc_channel *vc_channel;
+	struct omap_vc_channel_info *info;
+	const struct of_device_id *match;
+
+	if (!node) {
+		dev_err(dev, "%s: no OF information?\n", __func__);
+		return -EINVAL;
+	}
+
+	match = of_match_device(omap_vc_channel_of_match_tbl, dev);
+	if (!match) {
+		/* We do not expect this to happen */
+		dev_err(dev, "%s: Unable to match device\n", __func__);
+		return -ENODEV;
+	}
+	if (!match->data) {
+		dev_err(dev, "%s: Bad data in match\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!dev->parent) {
+		dev_err(dev, "%s: Unable to find parent VC device\n", __func__);
+		return -EINVAL;
+	}
+	vc_dev = dev->parent;
+
+	vc = dev_get_drvdata(vc_dev);
+	if (!vc) {
+		dev_err(dev, "%s: Unable to find parent VC data\n", __func__);
+		return -EINVAL;
+	}
+
+	vc_channel = devm_kzalloc(dev, sizeof(*vc_channel), GFP_KERNEL);
+	if (!vc_channel) {
+		dev_err(dev, "%s: Unable to allocate vc channel\n", __func__);
+		return -ENOMEM;
+	}
+	vc_channel->dev = dev;
+	vc_channel->ch_regs = match->data;
+	info = &vc_channel->info;
+	info->ch = vc_channel;
+
+	/* Pick up optional parameters */
+	of_property_read_u32(node, "ti,retention-micro-volts",
+			     &info->retention_uV);
+	of_property_read_u32(node, "ti,off-micro-volts", &info->off_uV);
+
+	vc_channel->is_master_channel =
+	    of_property_read_bool(node, "ti,master-channel");
+	if (vc_channel->is_master_channel && vc->master_channel_configured) {
+		dev_err(dev, "There can only be a single master channel!\n");
+		return -EINVAL;
+	}
+	vc_channel->use_master_channel_sa =
+	    of_property_read_bool(node, "ti,use-master-slave-addr");
+	vc_channel->use_master_channel_voltage_reg =
+	    of_property_read_bool(node, "ti,use-master-voltage-reg-addr");
+	vc_channel->use_master_channel_cmd_reg =
+	    of_property_read_bool(node, "ti,use-master-command-reg-addr");
+	vc_channel->use_master_channel_cmd_val =
+	    of_property_read_bool(node, "ti,use-master-command-reg-val");
+
+	if (vc_channel->is_master_channel &&
+	    (vc_channel->use_master_channel_sa ||
+	     vc_channel->use_master_channel_voltage_reg ||
+	     vc_channel->use_master_channel_cmd_reg ||
+	     vc_channel->use_master_channel_cmd_val)) {
+		dev_err(dev, "Master channel cannot route to slave channel!\n");
+		return -EINVAL;
+	}
+	if (!vc->master_channel_configured &&
+	    (vc_channel->use_master_channel_sa ||
+	     vc_channel->use_master_channel_voltage_reg ||
+	     vc_channel->use_master_channel_cmd_reg ||
+	     vc_channel->use_master_channel_cmd_val)) {
+		dev_dbg(dev, "Deferring - Master channel not yet ready!\n");
+		return -EPROBE_DEFER;
+	}
+
+	platform_set_drvdata(pdev, vc_channel);
+
+	/* Add it to VC channel list */
+	mutex_lock(&omap_vc_channel_list_mutex);
+	list_add(&vc_channel->list, &omap_vc_channel_list);
+	mutex_unlock(&omap_vc_channel_list_mutex);
+
+	return 0;
+}
+
+/**
+ * omap_vc_channel_remove() - Cleanup operations for channel
+ * @pdev:	platform device
+ *
+ * Return: -EBUSY if all users have not transitioned out, else return 0
+ */
+static int omap_vc_channel_remove(struct platform_device *pdev)
+{
+	struct omap_vc_channel *vc_channel = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	mutex_lock(&omap_vc_channel_list_mutex);
+	list_del(&vc_channel->list);
+	mutex_unlock(&omap_vc_channel_list_mutex);
+
+	return ret;
+}
+
+static struct platform_driver omap_vc_channel_driver = {
+	.driver = {
+		   .name = DRIVER_NAME "-channel",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(omap_vc_channel_of_match_tbl),
+		   },
+	.probe = omap_vc_channel_probe,
+	.remove = omap_vc_channel_remove,
+};
+
+/* Regular 32 bit registers for Voltage controller */
+static struct regmap_config omap_vc_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+/**
+ * omap_vc_i2c_config() - configure the I2C clock and Mode registers
+ * @vc:		voltage controller
+ * @i2c_config:	i2c configuration to configure
+ */
+static int omap_vc_i2c_config(struct omap_vc *vc,
+			 struct omap_omap_vc_i2c_config *i2c_config)
+{
+	u32 mask;
+
+	mask = VC_MODE_HSENABLE_MASK;
+	regmap_update_bits(vc->regmap, vc->regs->i2c_clk_mode_reg, mask,
+			   i2c_config->highspeed << __ffs(mask));
+	mask = VC_MODE_MCODE_MASK;
+	regmap_update_bits(vc->regmap, vc->regs->i2c_clk_mode_reg, mask,
+			   i2c_config->mcode << __ffs(mask));
+	/* Disable repeated start */
+	mask = VC_MODE_REPEAT_START_MASK;
+	regmap_update_bits(vc->regmap, vc->regs->i2c_clk_mode_reg, mask, 0);
+
+	/* if there is clock config register on the SoC, skip.. */
+	if (vc->regs->i2c_clk_cfg_reg < 0)
+		return 0;
+
+	if (!i2c_config->i2c_clk_hsscll &&
+	    !i2c_config->i2c_clk_hssclh &&
+	    !i2c_config->i2c_clk_scll && !i2c_config->i2c_clk_sclh)
+		return -EINVAL;
+
+	/* Configure up clk timings */
+	mask = VC_CLK_CFG_HSSCLL_MASK;
+	regmap_update_bits(vc->regmap, vc->regs->i2c_clk_cfg_reg, mask,
+			   i2c_config->i2c_clk_hsscll << __ffs(mask));
+	mask = VC_CLK_CFG_HSSCLH_MASK;
+	regmap_update_bits(vc->regmap, vc->regs->i2c_clk_cfg_reg, mask,
+			   i2c_config->i2c_clk_hssclh << __ffs(mask));
+	mask = VC_CLK_CFG_SCLL_MASK;
+	regmap_update_bits(vc->regmap, vc->regs->i2c_clk_cfg_reg, mask,
+			   i2c_config->i2c_clk_scll << __ffs(mask));
+	mask = VC_CLK_CFG_SCLH_MASK;
+	regmap_update_bits(vc->regmap, vc->regs->i2c_clk_cfg_reg, mask,
+			   i2c_config->i2c_clk_sclh << __ffs(mask));
+	return 0;
+}
+
+/**
+ * struct i2c_load_data - table mapping load to clock configs
+ * @load:	load of the I2C bus
+ * @hsscll_38_4:	38.4MHz Sysclk HSSCLL configuration
+ * @hsscll_26:		26MHz Sysclk HSSCLL configuration
+ * @hsscll_19_2:	19.2MHz Sysclk HSSCLL configuration
+ * @hsscll_16_8:	16.8MHz Sysclk HSSCLL configuration
+ * @hsscll_12:		12MHz Sysclk HSSCLL configuration
+ *
+ * Instead of doing an hard multi-parameter computation, if load is provided by
+ * OF data then we pick the values from the table, There is always an option
+ * of defining the clocks from OF data itself.
+ */
+struct i2c_load_data {
+	u8 load;
+	u8 hsscll_38_4;
+	u8 hsscll_26;
+	u8 hsscll_19_2;
+	u8 hsscll_16_8;
+	u8 hsscll_12;
+};
+
+/**
+ * omap_vc_i2c_timing_init() - sets up board I2C timing parameters
+ * @dev:	vc channel device
+ * @cfg:	I2C configuration
+ *
+ * Use PMIC + board supplied settings for calculating the total I2C channel
+ * capacitance and set the timing parameters based on this. Pre-calculated
+ * values are provided in data tables, as it is not too straightforward to
+ * calculate these runtime.
+ */
+static int omap_vc_i2c_timing_init(struct device *dev,
+				   struct omap_omap_vc_i2c_config *cfg)
+{
+	u32 capacitance;
+	u16 hsscll;
+	const struct i2c_load_data i2c_timing_data[] = {
+		{
+		 .load = 50,
+		 .hsscll_38_4 = 13,
+		 .hsscll_26 = 11,
+		 .hsscll_19_2 = 9,
+		 .hsscll_16_8 = 9,
+		 .hsscll_12 = 8,
+		 },
+		{
+		 .load = 25,
+		 .hsscll_38_4 = 13,
+		 .hsscll_26 = 11,
+		 .hsscll_19_2 = 9,
+		 .hsscll_16_8 = 9,
+		 .hsscll_12 = 8,
+		 },
+		{
+		 .load = 12,
+		 .hsscll_38_4 = 11,
+		 .hsscll_26 = 10,
+		 .hsscll_19_2 = 9,
+		 .hsscll_16_8 = 9,
+		 .hsscll_12 = 8,
+		 },
+		{
+		 .load = 0,
+		 .hsscll_38_4 = 12,
+		 .hsscll_26 = 10,
+		 .hsscll_19_2 = 9,
+		 .hsscll_16_8 = 8,
+		 .hsscll_12 = 8,
+		 },
+	};
+	const struct i2c_load_data *i2c_data;
+
+	if (!cfg->i2c_clk_pad_load && !cfg->i2c_clk_pcb_length)
+		return 0;
+
+	/* PCB trace capacitance, 0.125pF / mm => mm / 8 */
+	capacitance = DIV_ROUND_UP(cfg->i2c_clk_pcb_length, 8);
+
+	/* OMAP pad capacitance */
+	capacitance += 4;
+
+	/* PMIC pad capacitance */
+	capacitance += cfg->i2c_clk_pad_load;
+
+	/* Search for capacitance match in the table */
+	i2c_data = i2c_timing_data;
+
+	while (i2c_data->load > capacitance)
+		i2c_data++;
+
+	/* Select proper values based on sysclk frequency */
+	switch (cfg->clk_rate) {
+	case 38400000:
+		hsscll = i2c_data->hsscll_38_4;
+		break;
+	case 26000000:
+		hsscll = i2c_data->hsscll_26;
+		break;
+	case 19200000:
+		hsscll = i2c_data->hsscll_19_2;
+		break;
+	case 16800000:
+		hsscll = i2c_data->hsscll_16_8;
+		break;
+	case 12000000:
+		hsscll = i2c_data->hsscll_12;
+		break;
+	default:
+		dev_err(dev, "%s: Unsupported sysclk rate: %ld!\n", __func__,
+			cfg->clk_rate);
+		return -ERANGE;
+	}
+
+	/* HSSCLH can always be zero */
+	cfg->i2c_clk_hssclh = 0x0;
+	cfg->i2c_clk_hsscll = hsscll;
+	/* FS timing - standard */
+	cfg->i2c_clk_scll = 0x28;
+	cfg->i2c_clk_sclh = 0x2C;
+
+	return 0;
+}
+
+static const struct of_device_id omap_vc_of_match_tbl[] = {
+	{.compatible = "ti,omap3-vc", .data = &omap3_vc_regs,},
+	{.compatible = "ti,omap4-vc", .data = &omap4_vc_regs,},
+	{.compatible = "ti,omap5-vc", .data = &omap5_vc_regs,},
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_vc_of_match_tbl);
+
+static int omap_vc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct omap_vc *vc;
+	struct omap_omap_vc_i2c_config i2c_config = { 0 };
+	int ret;
+	struct resource *res;
+	char *pname;
+	struct clk *clk;
+	void __iomem *base;
+	struct regmap *regmap;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+
+	if (!node) {
+		dev_err(dev, "no OF information?\n");
+		return -EINVAL;
+	}
+
+	match = of_match_device(omap_vc_of_match_tbl, dev);
+	if (!match) {
+		/* We do not expect this to happen */
+		dev_err(dev, "%s: Unable to match device\n", __func__);
+		return -ENODEV;
+	}
+	if (!match->data) {
+		dev_err(dev, "%s: Bad data in match\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Read mandatory parameters */
+	clk = clk_get(dev, NULL);
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		dev_err(dev, "%s: Unable to get clk(%d)\n", __func__, ret);
+		return ret;
+	}
+	i2c_config.clk_rate = clk_get_rate(clk);
+	/* We do not need the clock any more */
+	clk_put(clk);
+
+	pname = "base-address";
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+	base = devm_request_and_ioremap(dev, res);
+	if (!base) {
+		dev_err(dev, "Unable to map '%s'\n", pname);
+		return -EADDRNOTAVAIL;
+	}
+
+	regmap = devm_regmap_init_mmio(dev, base, &omap_vc_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(dev, "regmap init failed(%d)\n", ret);
+		return ret;
+	}
+
+	/* Read optional highspeed mode param */
+	pname = "ti,i2c-high-speed";
+	i2c_config.highspeed = of_property_read_bool(node, pname);
+
+	/* Mcode if the platform likes to set it explicitly */
+	if (i2c_config.highspeed)
+		vc_property_read_u8(node, "ti,i2c-high-speed-mcode",
+				    &i2c_config.mcode);
+
+	if (i2c_config.highspeed) {
+		pname = "ti,i2c-pad-load";
+		of_property_read_u32(node, pname, &i2c_config.i2c_clk_pad_load);
+		pname = "ti,i2c-pcb-length";
+		of_property_read_u32(node, pname,
+				     &i2c_config.i2c_clk_pcb_length);
+
+		if (i2c_config.i2c_clk_pad_load) {
+			if (!i2c_config.i2c_clk_pcb_length)
+				i2c_config.i2c_clk_pcb_length = 63;
+
+			ret = omap_vc_i2c_timing_init(dev, &i2c_config);
+			if (ret)
+				goto out;
+
+			goto skip_i2c_clk_config;
+		}
+	}
+
+	/* So, we do not have pad load defined, expect proper timing config */
+	pname = "ti,i2c-clk-scl-low";
+	ret = vc_property_read_u8(node, pname, &i2c_config.i2c_clk_scll);
+	if (ret)
+		goto invalid_of_property;
+
+	pname = "ti,i2c-clk-scl-high";
+	ret = vc_property_read_u8(node, pname, &i2c_config.i2c_clk_sclh);
+	if (ret)
+		goto invalid_of_property;
+
+	pname = "ti,i2c-clk-hsscl-low";
+	ret = vc_property_read_u8(node, pname, &i2c_config.i2c_clk_hsscll);
+	if (ret && i2c_config.highspeed)
+		goto invalid_of_property;
+
+	pname = "ti,i2c-clk-hsscl-high";
+	ret = vc_property_read_u8(node, pname, &i2c_config.i2c_clk_hssclh);
+	if (ret && i2c_config.highspeed)
+		goto invalid_of_property;
+
+skip_i2c_clk_config:
+	vc = devm_kzalloc(dev, sizeof(*vc), GFP_KERNEL);
+	if (!vc) {
+		dev_err(dev, "unable to allocate vc\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+	vc->regmap = regmap;
+	vc->regs = match->data;
+
+	ret = omap_vc_i2c_config(vc, &i2c_config);
+	if (ret) {
+		dev_err(dev, "Bad I2C configuration: %d\n", ret);
+		goto out;
+	}
+
+	platform_set_drvdata(pdev, vc);
+	ret = of_platform_populate(dev->of_node,
+				   omap_vc_channel_of_match_tbl, NULL, dev);
+
+	if (ret)
+		dev_err(dev, "Failed to create DT children: %d\n", ret);
+
+out:
+	return ret;
+
+invalid_of_property:
+	dev_err(dev, "Missing/Invalid '%s' property - error(%d)\n", pname, ret);
+	return ret;
+}
+
+static struct platform_driver omap_vc_driver = {
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(omap_vc_of_match_tbl),
+		   },
+	.probe = omap_vc_probe,
+};
+
+static int __init omap_vc_init(void)
+{
+	int ret = 0;
+
+	ret = platform_driver_register(&omap_vc_driver);
+	if (ret) {
+		pr_err("platform driver register failed for VC(%d)\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&omap_vc_channel_driver);
+	if (ret) {
+		pr_err("platform driver register failed for VC Channel(%d)\n",
+		       ret);
+		return ret;
+	}
+
+	return 0;
+}
+module_init(omap_vc_init);
+
+static void __exit omap_vc_exit(void)
+{
+	platform_driver_unregister(&omap_vc_channel_driver);
+	platform_driver_unregister(&omap_vc_driver);
+}
+module_exit(omap_vc_exit);
+
+MODULE_DESCRIPTION("OMAP Voltage Controller Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc.");
diff --git a/drivers/power/avs/omap_vc.h b/drivers/power/avs/omap_vc.h
new file mode 100644
index 0000000..11503b0
--- /dev/null
+++ b/drivers/power/avs/omap_vc.h
@@ -0,0 +1,67 @@
+/*
+ * OMAP Voltage Controller (VC) interface exported functions
+ *
+ * Idea based on arch/arm/mach-omap2/vc.h
+ * Copyright (C) 2007, 2010 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ * Lesly A M <x0080970@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * Copyright (C) 2008, 2011 Nokia Corporation
+ * Kalle Jokiniemi
+ * Paul Walmsley
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ * Grygorii Strashko
+ * 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.
+ */
+#ifndef _POWER_AVS_OMAP_VC_H
+#define _POWER_AVS_OMAP_VC_H
+
+struct omap_pmic;
+/* Internal to VC */
+struct omap_vc_channel;
+
+/**
+ * struct omap_vc_channel_info - Channel information visible to users
+ * @pmic:		PMIC pointer
+ * @retention_uV:	retention voltage in micro volts
+ * @off_uV:		OFF voltage in micro volts
+ */
+struct omap_vc_channel_info {
+	struct omap_pmic *pmic;
+	u32 retention_uV;
+	u32 off_uV;
+	/* private: */
+	/* Used internally by Voltage Controller driver */
+	struct omap_vc_channel *ch;
+};
+
+#if IS_ENABLED(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL)
+struct omap_vc_channel_info *devm_omap_vc_channel_get(struct device *dev,
+						      struct omap_pmic *pmic);
+int omap_vc_channel_set_on_voltage(struct omap_vc_channel_info *info, u32 uv);
+#else
+struct inline omap_vc_channel_info *
+	devm_omap_vc_channel_get(struct device *dev, struct omap_pmic *pmic)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+static inline int omap_vc_channel_set_on_voltage(struct omap_vc_channel_info
+						 *info, u32 uv)
+{
+	return -ENODEV;
+}
+#endif
+
+#endif				/* _POWER_AVS_OMAP_VC_H */
