All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH V2 0/8] regulator/OMAP: support VC/VP support in dts
@ 2013-06-21 21:25 ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

Hi,

Texas Instrument's OMAP Processor have a dedicated hardware module
which is customised to operate with Power Management IC(PMIC) over an dedicated
I2C. The communication involves a few SoC internal modules as follows:
PMIC - The power management chip on the dedicated I2C (sometimes called I2C_SR)
Voltage controller - consists of an hardware i2c controller and interface
customized for PMICs. VC consists of multiple VC Channels, each channel
representing a variable voltage rail supply to the SoC.
Voltage Processor(VP) - controls the voltage requests to VC channel
SmartReflex(Adaptive Voltage control) - (SR/AVS)- specialized hardware block
which can dynamically control device voltage without software intervention. This
module communicates with VP.

In the simplest view, a simple voltage set operation is as follows:
Vp->VC Channel -> VC -> PMIC

Note, there may be dedicated PMIC per variable voltage rail OR PMICs which
provide control for multiple SMPS supplying variable rails etc.

In addition, there is an Adaptive Voltage Control (AVS) technique called
SmartReflex which can operate (in a configuration called continous monitoring
hardware loop or class 3 mode of operation), in which the SmartReflex block
communicates with Voltage Processor.

We have an OMAP specific implementation in arch/arm/mach-omap2 which does not
tree VC/VP or PMIC as Linux devices, but as data which is configured as needed.

In this series, we introduce replacement approach which has support for only
Device Tree as OMAP is transitioning completely away from non-DT approach.

As an overview, the following approach is taken.

PMIC is now the regulator driver - generic omap-pmic-regulator (patch #1)
Voltage controller and voltage controller channel is handled by
driver/power/avs/omap_vc.c

Voltage processor is handled by driver/power/avs/omap_vp.c

Benefit of using drivers/power/avs is also to set the foundation to convert
SmartReflex AVS into device tree based solution. (next stage).

S/w dependency is as follows:
Voltage controller <- Voltage Processor
Voltage Processor registers with OMAP_PMIC it's controller operations
OMAP_PMIC uses the controller operations to call vp which in turn calls VC to
setup the communication chain.

This allows us to maintain this as a module if needed as well (something our
existing implementation was not capable  of doing).

The series is also available here:
https://github.com/nmenon/linux-2.6-playground/commits/devel/vc-vp-regulator-rfc
git://github.com/nmenon/linux-2.6-playground.git
branch: devel/vc-vp-regulator-setv-v1-rfc-2

This depends on a few patches for cpufreq/clock node I added in, merged with
3.10-rc6 master + the following for-next branches
benoit/for_3.11/dts
mturquette/clk-next
broonie/for-next
rafael/linux-next
Available as branch: devel/vc-vp-base

Note: 
1. AVS device tree conversion will have to depend on this due to dependency on VP
2. Clock node strategy used here is based on implementation I had posted here:
	http://marc.info/?t=136804008400001&r=1&w=2
3. I chose OMAP4460 based PandaBoard ES platform as my development platform
   and patch #4 in this series is an attempt to showcase how it will look like.
   Rationale: weird PMIC configuration was used in PandaBoard ES. Ability to
   handle that platform makes introduction to other platforms/SoCs trivial.
4. Once this approach is agreed upon, I can do the dts changes for all SoCs
   OMAP3-5 and will post a formal series.

Related defects:
  https://bugzilla.kernel.org/show_bug.cgi?id=58541
  https://bugzilla.kernel.org/show_bug.cgi?id=58611

Related discussion:
v1 of RFC: http://marc.info/?t=136924689600005&r=1&w=2
    related discussion: http://marc.info/?t=137166954900005&r=1&w=2

Nishanth Menon (8):
  regulator: Introduce OMAP regulator to control PMIC over VC/VP
  PM / AVS: Introduce support for OMAP Voltage Controller(VC) with
    device tree nodes
  PM / AVS: Introduce support for OMAP Voltage Processor(VP) with
    device tree nodes
  ARM: dts: OMAP4: add voltage controller nodes
  ARM: dts: OMAP4: add voltage processor nodes
  ARM: dts: TWL6030/OMAP4: Add OMAP voltage path linkage
  ARM: dts: omap4-panda-es: add TPS62361 supply for vdd_mpu
  ARM: dts: OMAP4-panda-es: use tps regulator as cpu0 supply

 .../devicetree/bindings/power/omap-vc.txt          |   99 ++
 .../devicetree/bindings/power/omap-vp.txt          |   39 +
 .../bindings/regulator/omap-pmic-regulator.txt     |   41 +
 arch/arm/boot/dts/omap4-panda-common.dtsi          |    1 +
 arch/arm/boot/dts/omap4-panda-es.dts               |   35 +
 arch/arm/boot/dts/omap4-sdp.dts                    |    1 +
 arch/arm/boot/dts/omap4-var-som.dts                |    1 +
 arch/arm/boot/dts/omap4.dtsi                       |   73 +-
 arch/arm/boot/dts/omap443x.dtsi                    |   32 +-
 arch/arm/boot/dts/omap4460.dtsi                    |   33 +-
 arch/arm/boot/dts/tps62361_omap.dtsi               |   18 +
 arch/arm/boot/dts/twl6030_omap4.dtsi               |   35 +
 drivers/power/avs/Kconfig                          |   15 +
 drivers/power/avs/Makefile                         |   20 +
 drivers/power/avs/omap_vc.c                        | 1513 ++++++++++++++++++++
 drivers/power/avs/omap_vc.h                        |   67 +
 drivers/power/avs/omap_vp.c                        |  892 ++++++++++++
 drivers/regulator/Kconfig                          |   12 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/omap-pmic-regulator.c            |  638 +++++++++
 include/linux/regulator/omap-pmic-regulator.h      |  162 +++
 21 files changed, 3724 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vc.txt
 create mode 100644 Documentation/devicetree/bindings/power/omap-vp.txt
 create mode 100644 Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
 create mode 100644 arch/arm/boot/dts/tps62361_omap.dtsi
 create mode 100644 arch/arm/boot/dts/twl6030_omap4.dtsi
 create mode 100644 drivers/power/avs/omap_vc.c
 create mode 100644 drivers/power/avs/omap_vc.h
 create mode 100644 drivers/power/avs/omap_vp.c
 create mode 100644 drivers/regulator/omap-pmic-regulator.c
 create mode 100644 include/linux/regulator/omap-pmic-regulator.h

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 0/8] regulator/OMAP: support VC/VP support in dts
@ 2013-06-21 21:25 ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

Hi,

Texas Instrument's OMAP Processor have a dedicated hardware module
which is customised to operate with Power Management IC(PMIC) over an dedicated
I2C. The communication involves a few SoC internal modules as follows:
PMIC - The power management chip on the dedicated I2C (sometimes called I2C_SR)
Voltage controller - consists of an hardware i2c controller and interface
customized for PMICs. VC consists of multiple VC Channels, each channel
representing a variable voltage rail supply to the SoC.
Voltage Processor(VP) - controls the voltage requests to VC channel
SmartReflex(Adaptive Voltage control) - (SR/AVS)- specialized hardware block
which can dynamically control device voltage without software intervention. This
module communicates with VP.

In the simplest view, a simple voltage set operation is as follows:
Vp->VC Channel -> VC -> PMIC

Note, there may be dedicated PMIC per variable voltage rail OR PMICs which
provide control for multiple SMPS supplying variable rails etc.

In addition, there is an Adaptive Voltage Control (AVS) technique called
SmartReflex which can operate (in a configuration called continous monitoring
hardware loop or class 3 mode of operation), in which the SmartReflex block
communicates with Voltage Processor.

We have an OMAP specific implementation in arch/arm/mach-omap2 which does not
tree VC/VP or PMIC as Linux devices, but as data which is configured as needed.

In this series, we introduce replacement approach which has support for only
Device Tree as OMAP is transitioning completely away from non-DT approach.

As an overview, the following approach is taken.

PMIC is now the regulator driver - generic omap-pmic-regulator (patch #1)
Voltage controller and voltage controller channel is handled by
driver/power/avs/omap_vc.c

Voltage processor is handled by driver/power/avs/omap_vp.c

Benefit of using drivers/power/avs is also to set the foundation to convert
SmartReflex AVS into device tree based solution. (next stage).

S/w dependency is as follows:
Voltage controller <- Voltage Processor
Voltage Processor registers with OMAP_PMIC it's controller operations
OMAP_PMIC uses the controller operations to call vp which in turn calls VC to
setup the communication chain.

This allows us to maintain this as a module if needed as well (something our
existing implementation was not capable  of doing).

The series is also available here:
https://github.com/nmenon/linux-2.6-playground/commits/devel/vc-vp-regulator-rfc
git://github.com/nmenon/linux-2.6-playground.git
branch: devel/vc-vp-regulator-setv-v1-rfc-2

This depends on a few patches for cpufreq/clock node I added in, merged with
3.10-rc6 master + the following for-next branches
benoit/for_3.11/dts
mturquette/clk-next
broonie/for-next
rafael/linux-next
Available as branch: devel/vc-vp-base

Note: 
1. AVS device tree conversion will have to depend on this due to dependency on VP
2. Clock node strategy used here is based on implementation I had posted here:
	http://marc.info/?t=136804008400001&r=1&w=2
3. I chose OMAP4460 based PandaBoard ES platform as my development platform
   and patch #4 in this series is an attempt to showcase how it will look like.
   Rationale: weird PMIC configuration was used in PandaBoard ES. Ability to
   handle that platform makes introduction to other platforms/SoCs trivial.
4. Once this approach is agreed upon, I can do the dts changes for all SoCs
   OMAP3-5 and will post a formal series.

Related defects:
  https://bugzilla.kernel.org/show_bug.cgi?id=58541
  https://bugzilla.kernel.org/show_bug.cgi?id=58611

Related discussion:
v1 of RFC: http://marc.info/?t=136924689600005&r=1&w=2
    related discussion: http://marc.info/?t=137166954900005&r=1&w=2

Nishanth Menon (8):
  regulator: Introduce OMAP regulator to control PMIC over VC/VP
  PM / AVS: Introduce support for OMAP Voltage Controller(VC) with
    device tree nodes
  PM / AVS: Introduce support for OMAP Voltage Processor(VP) with
    device tree nodes
  ARM: dts: OMAP4: add voltage controller nodes
  ARM: dts: OMAP4: add voltage processor nodes
  ARM: dts: TWL6030/OMAP4: Add OMAP voltage path linkage
  ARM: dts: omap4-panda-es: add TPS62361 supply for vdd_mpu
  ARM: dts: OMAP4-panda-es: use tps regulator as cpu0 supply

 .../devicetree/bindings/power/omap-vc.txt          |   99 ++
 .../devicetree/bindings/power/omap-vp.txt          |   39 +
 .../bindings/regulator/omap-pmic-regulator.txt     |   41 +
 arch/arm/boot/dts/omap4-panda-common.dtsi          |    1 +
 arch/arm/boot/dts/omap4-panda-es.dts               |   35 +
 arch/arm/boot/dts/omap4-sdp.dts                    |    1 +
 arch/arm/boot/dts/omap4-var-som.dts                |    1 +
 arch/arm/boot/dts/omap4.dtsi                       |   73 +-
 arch/arm/boot/dts/omap443x.dtsi                    |   32 +-
 arch/arm/boot/dts/omap4460.dtsi                    |   33 +-
 arch/arm/boot/dts/tps62361_omap.dtsi               |   18 +
 arch/arm/boot/dts/twl6030_omap4.dtsi               |   35 +
 drivers/power/avs/Kconfig                          |   15 +
 drivers/power/avs/Makefile                         |   20 +
 drivers/power/avs/omap_vc.c                        | 1513 ++++++++++++++++++++
 drivers/power/avs/omap_vc.h                        |   67 +
 drivers/power/avs/omap_vp.c                        |  892 ++++++++++++
 drivers/regulator/Kconfig                          |   12 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/omap-pmic-regulator.c            |  638 +++++++++
 include/linux/regulator/omap-pmic-regulator.h      |  162 +++
 21 files changed, 3724 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vc.txt
 create mode 100644 Documentation/devicetree/bindings/power/omap-vp.txt
 create mode 100644 Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
 create mode 100644 arch/arm/boot/dts/tps62361_omap.dtsi
 create mode 100644 arch/arm/boot/dts/twl6030_omap4.dtsi
 create mode 100644 drivers/power/avs/omap_vc.c
 create mode 100644 drivers/power/avs/omap_vc.h
 create mode 100644 drivers/power/avs/omap_vp.c
 create mode 100644 drivers/regulator/omap-pmic-regulator.c
 create mode 100644 include/linux/regulator/omap-pmic-regulator.h

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 0/8] regulator/OMAP: support VC/VP support in dts
@ 2013-06-21 21:25 ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Texas Instrument's OMAP Processor have a dedicated hardware module
which is customised to operate with Power Management IC(PMIC) over an dedicated
I2C. The communication involves a few SoC internal modules as follows:
PMIC - The power management chip on the dedicated I2C (sometimes called I2C_SR)
Voltage controller - consists of an hardware i2c controller and interface
customized for PMICs. VC consists of multiple VC Channels, each channel
representing a variable voltage rail supply to the SoC.
Voltage Processor(VP) - controls the voltage requests to VC channel
SmartReflex(Adaptive Voltage control) - (SR/AVS)- specialized hardware block
which can dynamically control device voltage without software intervention. This
module communicates with VP.

In the simplest view, a simple voltage set operation is as follows:
Vp->VC Channel -> VC -> PMIC

Note, there may be dedicated PMIC per variable voltage rail OR PMICs which
provide control for multiple SMPS supplying variable rails etc.

In addition, there is an Adaptive Voltage Control (AVS) technique called
SmartReflex which can operate (in a configuration called continous monitoring
hardware loop or class 3 mode of operation), in which the SmartReflex block
communicates with Voltage Processor.

We have an OMAP specific implementation in arch/arm/mach-omap2 which does not
tree VC/VP or PMIC as Linux devices, but as data which is configured as needed.

In this series, we introduce replacement approach which has support for only
Device Tree as OMAP is transitioning completely away from non-DT approach.

As an overview, the following approach is taken.

PMIC is now the regulator driver - generic omap-pmic-regulator (patch #1)
Voltage controller and voltage controller channel is handled by
driver/power/avs/omap_vc.c

Voltage processor is handled by driver/power/avs/omap_vp.c

Benefit of using drivers/power/avs is also to set the foundation to convert
SmartReflex AVS into device tree based solution. (next stage).

S/w dependency is as follows:
Voltage controller <- Voltage Processor
Voltage Processor registers with OMAP_PMIC it's controller operations
OMAP_PMIC uses the controller operations to call vp which in turn calls VC to
setup the communication chain.

This allows us to maintain this as a module if needed as well (something our
existing implementation was not capable  of doing).

The series is also available here:
https://github.com/nmenon/linux-2.6-playground/commits/devel/vc-vp-regulator-rfc
git://github.com/nmenon/linux-2.6-playground.git
branch: devel/vc-vp-regulator-setv-v1-rfc-2

This depends on a few patches for cpufreq/clock node I added in, merged with
3.10-rc6 master + the following for-next branches
benoit/for_3.11/dts
mturquette/clk-next
broonie/for-next
rafael/linux-next
Available as branch: devel/vc-vp-base

Note: 
1. AVS device tree conversion will have to depend on this due to dependency on VP
2. Clock node strategy used here is based on implementation I had posted here:
	http://marc.info/?t=136804008400001&r=1&w=2
3. I chose OMAP4460 based PandaBoard ES platform as my development platform
   and patch #4 in this series is an attempt to showcase how it will look like.
   Rationale: weird PMIC configuration was used in PandaBoard ES. Ability to
   handle that platform makes introduction to other platforms/SoCs trivial.
4. Once this approach is agreed upon, I can do the dts changes for all SoCs
   OMAP3-5 and will post a formal series.

Related defects:
  https://bugzilla.kernel.org/show_bug.cgi?id=58541
  https://bugzilla.kernel.org/show_bug.cgi?id=58611

Related discussion:
v1 of RFC: http://marc.info/?t=136924689600005&r=1&w=2
    related discussion: http://marc.info/?t=137166954900005&r=1&w=2

Nishanth Menon (8):
  regulator: Introduce OMAP regulator to control PMIC over VC/VP
  PM / AVS: Introduce support for OMAP Voltage Controller(VC) with
    device tree nodes
  PM / AVS: Introduce support for OMAP Voltage Processor(VP) with
    device tree nodes
  ARM: dts: OMAP4: add voltage controller nodes
  ARM: dts: OMAP4: add voltage processor nodes
  ARM: dts: TWL6030/OMAP4: Add OMAP voltage path linkage
  ARM: dts: omap4-panda-es: add TPS62361 supply for vdd_mpu
  ARM: dts: OMAP4-panda-es: use tps regulator as cpu0 supply

 .../devicetree/bindings/power/omap-vc.txt          |   99 ++
 .../devicetree/bindings/power/omap-vp.txt          |   39 +
 .../bindings/regulator/omap-pmic-regulator.txt     |   41 +
 arch/arm/boot/dts/omap4-panda-common.dtsi          |    1 +
 arch/arm/boot/dts/omap4-panda-es.dts               |   35 +
 arch/arm/boot/dts/omap4-sdp.dts                    |    1 +
 arch/arm/boot/dts/omap4-var-som.dts                |    1 +
 arch/arm/boot/dts/omap4.dtsi                       |   73 +-
 arch/arm/boot/dts/omap443x.dtsi                    |   32 +-
 arch/arm/boot/dts/omap4460.dtsi                    |   33 +-
 arch/arm/boot/dts/tps62361_omap.dtsi               |   18 +
 arch/arm/boot/dts/twl6030_omap4.dtsi               |   35 +
 drivers/power/avs/Kconfig                          |   15 +
 drivers/power/avs/Makefile                         |   20 +
 drivers/power/avs/omap_vc.c                        | 1513 ++++++++++++++++++++
 drivers/power/avs/omap_vc.h                        |   67 +
 drivers/power/avs/omap_vp.c                        |  892 ++++++++++++
 drivers/regulator/Kconfig                          |   12 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/omap-pmic-regulator.c            |  638 +++++++++
 include/linux/regulator/omap-pmic-regulator.h      |  162 +++
 21 files changed, 3724 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vc.txt
 create mode 100644 Documentation/devicetree/bindings/power/omap-vp.txt
 create mode 100644 Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
 create mode 100644 arch/arm/boot/dts/tps62361_omap.dtsi
 create mode 100644 arch/arm/boot/dts/twl6030_omap4.dtsi
 create mode 100644 drivers/power/avs/omap_vc.c
 create mode 100644 drivers/power/avs/omap_vc.h
 create mode 100644 drivers/power/avs/omap_vp.c
 create mode 100644 drivers/regulator/omap-pmic-regulator.c
 create mode 100644 include/linux/regulator/omap-pmic-regulator.h

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon, Grygorii Strashko,
	Taras Kondratiuk

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced
an TI custom hardware block to better facilitate and standardize
integration of Power Management ICs which communicate over I2C.

In fact, many custom PMICs were designed to be usable over this
interface. On the other hand, generic PMICs which are compatible
to the I2C protocol supported by Voltage controller may also be
used.

In general, the following categories of PMICs exist:

a) PMICs which are completely controlled by voltage controller/voltage
processor pair - this implies even configuration needs to be done
over the same interface. Example: TPS62361 used on PandaBoard-ES and
many OMAP4460 based platforms. Few Maxim and Fairchild PMICs used on
certain products would fall in this category.
- Note: in this case, there may not even exist/needed to support an
"traditional Linux regulator driver"

b) PMICs which provide two views over two I2C interfaces - however,
voltage can only be set only on one of them. Example: TWL4030/5030:
allows us to use Voltage controller once we set up a bit over regular
I2C - This is used in OMAP3. TWO6030/TWL6032 - configuration *has*
to be performed over regular i2c (example smps_offset) and voltage
control *has* to be performed by using voltage controller/processor.
- Note: in this case, there may exist "traditional Linux regulator driver"
however, it may not support in any form SMPS modelling for that part of
the device which is exposed to voltage controller/processor.
c) PMICs which allow voltage and configurations over either i2c
interfaces - TWL6035/TWL6037/Palmas family of TI processor
- Note: in this case, there may exist "traditional Linux regulator driver"
and, it may support in some form SMPS modelling for that part of
the device which is exposed to voltage controller/processor.
d) custom PMICs which are setup so that no configuration is needed to be
performed and they operate with preset register offsets and minimal
conferability using voltage controller/processor.
- Note: in this case, there may not even exist/needed to support an
"traditional Linux regulator driver"

However, no matter the type of PMIC used, the OMAP view of a PMIC is
generic when used over voltage controller/processor. We attempt to
model this generic view of the regulator represented by OMAP SoC.

Alternative to this approach would be to "hack" into the get
voltage/set voltage interfaces of regulator drivers which represent
the rest of the PMIC controlled over regular I2C interface and
re-route the requests to voltage controller/processor. But, by doing
that, we needlessly create additional code which may be abstracted out
into device tree node information.

Since the regulator node representing PMIC, voltage controller,
processors are probed at varied points in time, probe deferral is used
to sequence in the right order. It is expected by the driver to register
omap_pmic_register_controller_ops providing mandatory operations at the
earliest possible opportunity.

Despite the current SoCs implementing the solution based on voltage
controller and voltage processor (which are part of the OMAP SoC modules
which enable Adaptive Voltage Scaling class support), the interfaces are
generic enough to handle future equivalent modules.

[grygorii.strashko@ti.com, taras@ti.com: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Taras Kondratiuk <taras@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
---
 .../bindings/regulator/omap-pmic-regulator.txt     |   41 ++
 drivers/regulator/Kconfig                          |   12 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/omap-pmic-regulator.c            |  638 ++++++++++++++++++++
 include/linux/regulator/omap-pmic-regulator.h      |  162 +++++
 5 files changed, 854 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
 create mode 100644 drivers/regulator/omap-pmic-regulator.c
 create mode 100644 include/linux/regulator/omap-pmic-regulator.h

diff --git a/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt b/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
new file mode 100644
index 0000000..6991e58
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
@@ -0,0 +1,41 @@
+Generic Power Management IC(PMIC) Regulator for Texas Instruments OMAP SoCs
+
+Required Properties:
+- compatible: Should be one of:
+  - "ti,omap-twl4030-vdd1" - TWL4030/5030/TPS65950 VDD1
+  - "ti,omap-twl4030-vdd2" - TWL4030/5030/TPS65950 VDD2
+  - "ti,omap-twl6030-vcore1" - TWL6030 VCORE1
+  - "ti,omap-twl6030-vcore2" - TWL6030 VCORE2
+  - "ti,omap-twl6030-vcore3" - TWL6030 VCORE3
+  - "ti,omap-tps62361" - TPS62361 VSEL1
+  - "ti,omap-twl6032-smps1" - TWL6032 SMPS1
+  - "ti,omap-twl6032-smps2" - TWL6032 SMPS2
+  - "ti,omap-twl6032-smps5" - TWL6032 SMPS5
+  - "ti,omap-twl6035-smps1" - TWL6035/6037 in SMPS1, 12, 123 configuration
+  - "ti,omap-twl6035-smps4" - TWL6035/6037 in SMPS4, 45 configuration
+  - "ti,omap-twl6035-smps8" - TWL6035/6037 in SMPS8 configuration
+
+Optional Properties:
+- gpios: OF device-tree gpio specification - can be an array, will be setup
+	in the order of definition and set to the flags.
+- pinctrl: OF device-tree pinctrl definitions as needed (usually for the GPIOs)
+- ti,boot-voltage-micro-volts - voltage in microvolts that bootloader is leaving
+	over the PMIC at. This may be 'PMIC data manual configuration' if
+	bootloader does not set an value, or appropritate value.
+
+
+Example: Sample PMIC description
+omap_tps62361: tps62361 {
+	compatible = "ti,omap-tps62361";
+};
+
+/* Board Specific configuration */
+&omap_tps62361 {
+	pinctrl-names = "default";
+	pinctrl-0 = <
+		&tps62361_wkgpio_pins
+	>;
+	gpios = <&gpio1 7 1>;	/* gpio_wk7 */
+
+	ti,boot-voltage-micro-volts=<1200000>;
+};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 9296425..e14b534 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -482,6 +482,18 @@ config REGULATOR_TI_ABB
 	  on TI SoCs may be unstable without enabling this as it provides
 	  device specific optimized bias to allow/optimize functionality.
 
+config REGULATOR_TI_OMAP_PMIC
+	tristate "TI Generic regulator for Voltage Processor / Controller"
+	depends on ARCH_OMAP
+	help
+	  Select this option to support PMICs over Texas Instruments'
+	  custom Voltage Processor + Voltage Controller data interface
+	  used in OMAP SoCs. This is a specific "write-only" interface
+	  designed to interface with I2C based PMICs.
+
+	  This option enables the regulator driver representing the PMIC
+	  on the OMAP VC/VP hardware.
+
 config REGULATOR_VEXPRESS
 	tristate "Versatile Express regulators"
 	depends on VEXPRESS_CONFIG
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 26e6c4a..8874e74 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
 obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
+obj-$(CONFIG_REGULATOR_TI_OMAP_PMIC) += omap-pmic-regulator.o
 obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
diff --git a/drivers/regulator/omap-pmic-regulator.c b/drivers/regulator/omap-pmic-regulator.c
new file mode 100644
index 0000000..40c725f
--- /dev/null
+++ b/drivers/regulator/omap-pmic-regulator.c
@@ -0,0 +1,638 @@
+/*
+ * OMAP Generic PMIC Regulator
+ *
+ * Idea based on arch/arm/mach-omap2/omap_twl.c
+ * Copyright (C) 2010 Texas Instruments Incorporated.
+ * Thara Gopinath
+ * Copyright (C) 2009 Texas Instruments Incorporated.
+ * Nishanth Menon
+ * Copyright (C) 2009 Nokia Corporation
+ * Paul Walmsley
+ *
+ * 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/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/omap-pmic-regulator.h>
+
+#define DRIVER_NAME	"omap-pmic"
+
+static DEFINE_MUTEX(omap_pmic_cops_mutex);
+static struct omap_pmic_controller_ops *pmic_cops;
+
+/**
+ * omap_pmic_register_controller_ops() - Register voltage operations
+ * @cops:	voltage operations
+ *
+ * It is expected that appropriate controller register it's functions
+ * with this driver using this interface, If this is not done, the probe
+ * for the corresponding device will defer till it fails.
+ *
+ * Return: -EBUSY if already registered, else returns 0
+ */
+int omap_pmic_register_controller_ops(struct omap_pmic_controller_ops *cops)
+{
+	int ret = 0;
+
+	mutex_lock(&omap_pmic_cops_mutex);
+	if (pmic_cops) {
+		pr_err("Controller operations already registered\n");
+		ret = -EBUSY;
+		goto out;
+	}
+	if (!cops->devm_pmic_register || !cops->voltage_set ||
+	    !cops->voltage_get || !cops->voltage_get_range) {
+		pr_err("Missing operations!\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	pmic_cops = cops;
+out:
+	mutex_unlock(&omap_pmic_cops_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(omap_pmic_register_controller_ops);
+
+/**
+ * omap_pmic_vsel_to_uv() - Convert voltage selector(vsel) to microvolts
+ * @pmic:	pointer to pmic struct
+ * @vsel:	voltage selector(vsel)
+ * @uv:		If conversion is successful, returns the voltage in micro volts
+ *
+ * Return: 0 if conversion is successful and *uv has proper value, else
+ * appropriate error value for failure.
+ */
+static int omap_pmic_vsel_to_uv(struct omap_pmic *pmic, u8 vsel, u32 *uv)
+{
+	u32 tmp = vsel;
+	const struct omap_pmic_info *info;
+
+	if (!pmic || !uv) {
+		pr_err("Bad parameters pmic=%p uv=%p!\n", pmic, uv);
+		return -EINVAL;
+	}
+	info = pmic->info;
+
+	if (info->voltage_selector_mask) {
+		tmp &= info->voltage_selector_mask;
+		tmp >>= __ffs(info->voltage_selector_mask);
+	}
+
+	if (!tmp && info->voltage_selector_zero)
+		goto out;
+
+	tmp -= info->voltage_selector_offset;
+	tmp *= info->step_size_uV;
+	tmp += info->min_uV;
+
+	if (tmp < info->min_uV || tmp > info->max_uV) {
+		dev_dbg(pmic->dev, "%s: Out of range 0x%02x[%d] (%d <-> %d)\n",
+			__func__, vsel, tmp, info->min_uV, info->max_uV);
+		return -ERANGE;
+	}
+
+out:
+	*uv = tmp;
+	dev_dbg(pmic->dev, "%s: uv=%d vsel=0x%02x\n", __func__, *uv, vsel);
+
+	return 0;
+}
+
+/**
+ * omap_pmic_uv_to_vsel() - Convert microvolts to voltage selector(vsel)
+ * @pmic:	pointer to pmic struct
+ * @uv:		voltage in micro volts
+ * @vsel:	If conversion is successful, voltage selector(vsel)
+ *
+ * Return: 0 if conversion is successful and *vsel has proper value, else
+ * appropriate error value for failure.
+ */
+static int omap_pmic_uv_to_vsel(struct omap_pmic *pmic, u32 uv, u8 *vsel)
+{
+	u32 tmp = uv;
+	const struct omap_pmic_info *info;
+
+	if (!pmic || !vsel) {
+		pr_err("Bad parameters pmic=%p vsel=%p!\n", pmic, vsel);
+		return -EINVAL;
+	}
+	info = pmic->info;
+
+	if (!tmp && info->voltage_selector_zero)
+		goto skip_convert;
+
+	if (tmp > info->max_uV)
+		goto skip_convert;
+
+	tmp -= info->min_uV;
+	tmp = DIV_ROUND_UP(tmp, info->step_size_uV);
+
+	tmp += info->voltage_selector_offset;
+
+skip_convert:
+	if (tmp > 0xFF) {
+		dev_dbg(pmic->dev, "%s: Out of range 0x%04x[%d] (%d - %d)\n",
+			__func__, tmp, uv, info->min_uV, info->max_uV);
+		return -ERANGE;
+	}
+	if (info->voltage_selector_mask) {
+		tmp <<= __ffs(info->voltage_selector_mask);
+		if (tmp > 0xFF) {
+			dev_warn(pmic->dev, "%s: Out of range 0x%04x[%d]\n",
+				 __func__, tmp, uv);
+			return -ERANGE;
+		}
+		tmp &= info->voltage_selector_mask;
+	}
+
+	tmp |= info->voltage_selector_setbits;
+
+	*vsel = tmp;
+	dev_dbg(pmic->dev, "%s: uv=%d vsel=0x%02x\n", __func__, uv, *vsel);
+
+	return 0;
+}
+
+/**
+ * omap_pmic_set_voltage() - regulator interface to set voltage
+ * @rdev:	regulator device
+ * @min_uV:	min voltage in micro-volts
+ * @max_uV:	max voltage in micro-volts
+ * @unused:	unused.. we dont use sel
+ *
+ * Return: -ERANGE for out of range values, appropriate error code if conversion
+ * fails, else returns 0.
+ */
+static int omap_pmic_set_voltage(struct regulator_dev *rdev, int min_uV,
+				 int max_uV, unsigned *unused)
+{
+	struct omap_pmic *pmic = rdev_get_drvdata(rdev);
+
+	return pmic_cops->voltage_set(pmic->v_dev, min_uV);
+}
+
+/**
+ * omap_pmic_get_voltage() - regulator interface to get voltage
+ * @rdev: regulator device
+ *
+ * Return: current voltage set on PMIC OR appropriate error value
+ */
+static int omap_pmic_get_voltage(struct regulator_dev *rdev)
+{
+	struct omap_pmic *pmic = rdev_get_drvdata(rdev);
+	int ret;
+	u32 uv;
+
+	ret = pmic_cops->voltage_get(pmic->v_dev, &uv);
+	if (ret)
+		return ret;
+
+	return uv;
+}
+
+static struct omap_pmic_ops omap_generic_pmic_ops = {
+	.vsel_to_uv = omap_pmic_vsel_to_uv,
+	.uv_to_vsel = omap_pmic_uv_to_vsel,
+};
+
+static struct regulator_ops omap_pmic_reg_ops = {
+	.list_voltage = regulator_list_voltage_linear,
+
+	.set_voltage = omap_pmic_set_voltage,
+	.get_voltage = omap_pmic_get_voltage,
+};
+
+/**
+ * omap_pmic_of_setup_gpios() - Setup GPIO array if needed.
+ * @dev:	device to pick up the gpios from
+ */
+static int omap_pmic_of_setup_gpios(struct device *dev)
+{
+	struct device_node *node = dev->of_node;
+	int num_gpios, i, ret;
+
+	num_gpios = of_gpio_count(node);
+	if (num_gpios < 0)
+		return 0;
+
+	for (i = 0; i < num_gpios; i++) {
+		int gpio, level;
+		enum of_gpio_flags flags;
+
+		gpio = of_get_gpio_flags(node, i, &flags);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(dev, "Invalid GPIO[%d]: %d\n", i, gpio);
+			return -EINVAL;
+		}
+
+		ret = devm_gpio_request(dev, gpio, dev_name(dev));
+		if (ret) {
+			dev_err(dev, "Unable to get GPIO %d (%d)\n", gpio, ret);
+			return ret;
+		}
+		level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+		ret = gpio_direction_output(gpio, level);
+		if (ret) {
+			dev_err(dev, "Failed to set GPIO %d to %d (%d)\n",
+				gpio, level, ret);
+			return ret;
+		}
+		dev_dbg(dev, "GPIO=%d set_to=%d flags=0x%08x\n", gpio,
+			level, flags);
+	}
+
+	return 0;
+}
+
+/**
+ * omap_pmic_parse_of() - Do DT OF node parsing
+ * @pmic:	pointer to PMIC
+ */
+static int omap_pmic_parse_of(struct omap_pmic *pmic)
+{
+	struct device *dev = pmic->dev;
+	struct device_node *node = dev->of_node;
+	u32 val = 0;
+	char *pname;
+	int ret;
+
+	pname = "ti,boot-voltage-micro-volts";
+	ret = of_property_read_u32(node, pname, &val);
+	if (!ret) {
+		if (!val)
+			goto invalid_of_property;
+		pmic->boot_voltage_uV = val;
+	}
+
+	return ret;
+
+invalid_of_property:
+	if (!ret) {
+		dev_err(dev, "Invalid value 0x%x[%d] in '%s' property.\n",
+			val, val, pname);
+		ret = -EINVAL;
+	} else {
+		dev_err(dev, "Missing/Invalid '%s' property - error(%d)\n",
+			pname, ret);
+	}
+	return ret;
+}
+
+static const struct omap_pmic_info omap_twl4030_vdd1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x00,
+	.cmd_reg_addr = 0x00,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 4000,
+	.step_size_uV = 12500,
+	.min_uV = 600000,
+	.max_uV = 1450000,
+	.voltage_selector_offset = 0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = false,
+};
+
+static const struct omap_pmic_info omap_twl4030_vdd2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x01,
+	.cmd_reg_addr = 0x01,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 4000,
+	.step_size_uV = 12500,
+	.min_uV = 600000,
+	.max_uV = 1450000,
+	.voltage_selector_offset = 0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = false,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x55,
+	.cmd_reg_addr = 0x56,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x5b,
+	.cmd_reg_addr = 0x5c,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore3 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x61,
+	.cmd_reg_addr = 0x62,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_setup_commands omap_tps62361_cmds[] = {
+	{.reg = 0x06, .cmd_val = 0x06},	/* TPS6236X_RAMP_CTRL 32mV/uS */
+	{.reg = 0x04, .cmd_val = 0xc0},	/* TPS6236X_CTRL VSEL0 pull down */
+	{.reg = 0x05, .cmd_val = 0x00}, /* REG_TPS6236X_TEMP enable tshut */
+};
+
+static const struct omap_pmic_info omap_tps62361 = {
+	.slave_addr = 0x60,
+	.voltage_reg_addr = 0x01,
+	.cmd_reg_addr = 0x01,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 32000,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1770000,
+	.voltage_selector_offset = 0x0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x80, /* PFM mode */
+	.voltage_selector_zero = false,
+	.setup_command_list = omap_tps62361_cmds,
+	.setup_num_commands = ARRAY_SIZE(omap_tps62361_cmds),
+};
+
+static const struct omap_pmic_info omap_twl6032_smps1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x55,
+	.cmd_reg_addr = 0x56,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6032_smps2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x5b,
+	.cmd_reg_addr = 0x5c,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6032_smps5 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x49,
+	.cmd_reg_addr = 0x4a,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x23,
+	.cmd_reg_addr = 0x22,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps4 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x2b,
+	.cmd_reg_addr = 0x2a,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps8 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x37,
+	.cmd_reg_addr = 0x36,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct of_device_id omap_pmic_of_match_tbl[] = {
+	{.compatible = "ti,omap-twl4030-vdd1", .data = &omap_twl4030_vdd1,},
+	{.compatible = "ti,omap-twl4030-vdd2", .data = &omap_twl4030_vdd2,},
+	{.compatible = "ti,omap-twl6030-vcore1", .data = &omap_twl6030_vcore1,},
+	{.compatible = "ti,omap-twl6030-vcore2", .data = &omap_twl6030_vcore2,},
+	{.compatible = "ti,omap-twl6030-vcore3", .data = &omap_twl6030_vcore3,},
+	{.compatible = "ti,omap-tps62361", .data = &omap_tps62361,},
+	{.compatible = "ti,omap-twl6032-smps1", .data = &omap_twl6032_smps1,},
+	{.compatible = "ti,omap-twl6032-smps2", .data = &omap_twl6032_smps2,},
+	{.compatible = "ti,omap-twl6032-smps5", .data = &omap_twl6032_smps5,},
+	{.compatible = "ti,omap-twl6035-smps1", .data = &omap_twl6035_smps1,},
+	{.compatible = "ti,omap-twl6035-smps4", .data = &omap_twl6035_smps4,},
+	{.compatible = "ti,omap-twl6035-smps8", .data = &omap_twl6035_smps8,},
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_pmic_of_match_tbl);
+
+static int omap_pmic_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+	struct omap_pmic *pmic;
+	struct regulator_desc *desc;
+	struct regulation_constraints *c;
+	struct regulator_config config = { };
+	struct regulator_init_data *initdata = NULL;
+	struct regulator_dev *rdev = NULL;
+	int ret = 0;
+	bool ops_ready;
+
+	if (!node) {
+		dev_err(dev, "%s: missing device tree nodes?\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&omap_pmic_cops_mutex);
+	ops_ready = pmic_cops ? true : false;
+	mutex_unlock(&omap_pmic_cops_mutex);
+	if (!ops_ready) {
+		dev_dbg(dev, "Voltage Operations not ready yet..\n");
+		return -EPROBE_DEFER;
+	}
+
+	match = of_match_device(omap_pmic_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;
+	}
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc) {
+		dev_err(dev, "%s: unable to allocate desc\n", __func__);
+		return -ENOMEM;
+	}
+
+	pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+	if (!pmic) {
+		dev_err(dev, "%s: unable to allocate pmic\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Read mandatory OF parameters */
+	pmic->dev = dev;
+	pmic->ops = &omap_generic_pmic_ops;
+	pmic->info = match->data;
+
+	initdata = of_get_regulator_init_data(dev, node);
+	if (!initdata) {
+		dev_err(dev, "%s: Unable to alloc regulator init data\n",
+			__func__);
+		return -ENOMEM;
+	}
+	c = &initdata->constraints;
+
+	/* Constraint to PMIC limits */
+	if (pmic->info->min_uV > c->min_uV)
+		c->min_uV = pmic->info->min_uV;
+	if (pmic->info->max_uV < c->max_uV)
+		c->max_uV = pmic->info->max_uV;
+
+	ret = omap_pmic_parse_of(pmic);
+	if (ret)
+		return ret;
+
+	ret = omap_pmic_of_setup_gpios(dev);
+	if (ret)
+		return ret;
+
+	pmic->v_dev = pmic_cops->devm_pmic_register(dev, pmic);
+	if (IS_ERR(pmic->v_dev)) {
+		dev_dbg(dev, "Registration of pmic failed (%d)\n", ret);
+		ret = PTR_ERR(pmic->v_dev);
+		return ret;
+	}
+	desc->name = dev_name(dev);
+	desc->owner = THIS_MODULE;
+	desc->type = REGULATOR_VOLTAGE;
+	desc->ops = &omap_pmic_reg_ops;
+	desc->uV_step = pmic->info->step_size_uV;
+	desc->ramp_delay = pmic->info->slew_rate_uV;
+
+	c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
+	c->always_on = true;
+	ret = pmic_cops->voltage_get_range(pmic->v_dev, &c->min_uV, &c->max_uV);
+	if (ret) {
+		dev_err(dev, "Voltage Range get failed (%d)\n", ret);
+		return ret;
+	}
+
+	config.dev = dev;
+	config.init_data = initdata;
+	config.driver_data = pmic;
+	config.of_node = node;
+
+	rdev = regulator_register(desc, &config);
+	if (IS_ERR(rdev)) {
+		ret = PTR_ERR(rdev);
+		dev_err(dev, "%s: failed to register regulator(%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, rdev);
+
+	return ret;
+}
+
+static struct platform_driver omap_pmic_driver = {
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(omap_pmic_of_match_tbl),
+		   },
+	.probe = omap_pmic_probe,
+};
+module_platform_driver(omap_pmic_driver);
+
+MODULE_DESCRIPTION("OMAP Generic PMIC Regulator");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc.");
diff --git a/include/linux/regulator/omap-pmic-regulator.h b/include/linux/regulator/omap-pmic-regulator.h
new file mode 100644
index 0000000..db0bab4
--- /dev/null
+++ b/include/linux/regulator/omap-pmic-regulator.h
@@ -0,0 +1,162 @@
+/*
+ * OMAP Generic PMIC Regulator interfaces
+ *
+ * Idea based on arch/arm/mach-omap2/omap_twl.c and
+ * arch/arm/mach-omap2/voltage.h
+ * Copyright (C) 2010 Texas Instruments Incorporated.
+ * Thara Gopinath
+ * Copyright (C) 2009 Texas Instruments Incorporated.
+ * Nishanth Menon
+ * Copyright (C) 2009 Nokia Corporation
+ * 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_OMAP_PMIC_H
+#define __POWER_OMAP_PMIC_H
+
+struct omap_pmic;
+
+/**
+ * struct omap_pmic_setup_commands - setup commands over voltage controller
+ * @reg:	device's i2c register address
+ * @cmd_val:	command to send.
+ */
+struct omap_pmic_setup_commands {
+	u8 reg;
+	u8 cmd_val;
+};
+
+/**
+ * struct omap_pmic_ops - Conversion routines for voltage controller/processor
+ * @vsel_to_uv: convert voltage selector to micro-volts
+ * @uv_to_vsel: convert micro-volts to voltage selector
+ *
+ * voltage controller/processor drivers SHOULD NOT do modifications on vsel or
+ * make any assumptions about vsel. Instead, they may operate on micro-volts
+ * and request vsel conversion once they are ready to do hardware operations.
+ *
+ * This is provided over the omap_pmic structure.
+ */
+struct omap_pmic_ops {
+	int (*vsel_to_uv) (struct omap_pmic *pmic, u8 vsel, u32 *uv);
+	int (*uv_to_vsel) (struct omap_pmic *pmic, u32 uv, u8 *vsel);
+};
+
+/**
+ * struct omap_pmic_controller_ops - regulator operations implemented
+ * @devm_pmic_register: managed registration of an PMIC device with a specific
+ *			voltage processor. Voltage processor provides an device
+ *			handle which is remaining operations.
+ *			NOTE:
+ *			- This will be first interface to be invoked by
+ *			omap_pmic regulator.
+ *			- if the underlying layers are not ready, this is
+ *			expected to return -EPROBE_DEFER
+ *			- if failure, appropriate error code is expected.
+ *			- This is expected to be a managed device to avoid
+ *			explicit cleanup operations
+ *			- Once this succeeds, this returns the pointer to
+ *			the controller device and all other operations are
+ *			expected to be ready for functionality.
+ * @voltage_set:	set the voltage - expected to be synchronous.
+ * @voltage_get:	get the current voltage in micro-volts set on PMIC.
+ * @voltage_get_range:	Get minimum and maxium permissible operational voltage
+ *			range for the device - used to set initial regulator
+ *			constraints.
+ *
+ * These voltage processor interfaces are registered by voltage processor driver
+ * using omap_pmic_register_controller_ops. This allows the omap_pmic driver to
+ * operate with a specific voltage processor driver.
+ */
+struct omap_pmic_controller_ops {
+	struct device *(*devm_pmic_register) (struct device *dev,
+					      struct omap_pmic *pmic);
+	int (*voltage_set) (struct device *control_dev, u32 uv);
+	int (*voltage_get) (struct device *control_dev, u32 *uv);
+	int (*voltage_get_range) (struct device *control_dev, u32 *min_uv,
+				  u32 *max_uv);
+};
+
+/**
+ * struct omap_pmic_info - PMIC information
+ *
+ * @slave_addr:		7 bit address representing I2C slave address.
+ * @voltage_reg_addr:	I2C register address for setting voltage
+ * @cmd_reg_addr:	I2C register address for low power transition commands
+ * @i2c_timeout_us:	worst case latency for I2C operations for the device
+ * @slew_rate_uV:	Slew rate in uV/uSeconds for voltage transitions
+ * @step_size_uV:	Step size in uV for one vsel increment.
+ * @min_uV:		represents the minimum step_sized incremental voltage
+ * @max_uV:		represents the maximum step_sized incremental voltage
+ * @voltage_selector_setbits:  what bits to set permenantly for the PMIC
+ *			voltage selector - this may have PMIC specific meaning.
+ * @voltage_selector_mask: what mask to use for the vsel value - this is useful
+ *			for PMICs where the vsel has to be applied at an offset.
+ * @voltage_selector_offset: what offset to apply to conversion routine when
+ *			operating on vsel.
+ * @voltage_selector_zero: Special case handling if 0 value does NOT indicates
+ *			power-off for PMIC.
+ * @setup_command_list:	array of setup commands for PMIC to operate
+ * @setup_num_commands:	number of setup commands for PMIC to operate
+ */
+struct omap_pmic_info {
+	u8 slave_addr;
+	u8 voltage_reg_addr;
+	u8 cmd_reg_addr;
+	u32 i2c_timeout_us;
+
+	u32 slew_rate_uV;
+	u32 step_size_uV;
+	u32 min_uV;
+	u32 max_uV;
+
+	u8 voltage_selector_offset;
+	u8 voltage_selector_mask;
+	u8 voltage_selector_setbits;
+	bool voltage_selector_zero;
+
+	const struct omap_pmic_setup_commands *setup_command_list;
+	u8 setup_num_commands;
+};
+
+/**
+ * struct omap_pmic - represents the OMAP PMIC device.
+ * @ops:		PMIC conversion operations.
+ *
+ * NOTE: the fields marked Private are meant for PMIC driver.
+ */
+struct omap_pmic {
+	const struct omap_pmic_info *info;
+	u32 boot_voltage_uV;
+
+	struct omap_pmic_ops *ops;
+
+	struct device *dev;
+	struct device *v_dev;
+};
+
+#if IS_ENABLED(CONFIG_REGULATOR_TI_OMAP_PMIC)
+int omap_pmic_register_controller_ops(struct omap_pmic_controller_ops *cops);
+#else
+static inline int omap_pmic_register_controller_ops(struct
+						    omap_pmic_controller_ops
+						    *cops)
+{
+	return -EINVAL;
+}
+#endif
+
+#endif				/* __POWER_OMAP_PMIC_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Nishanth Menon, Grygorii Strashko,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, Kevin Hilman,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Taras Kondratiuk,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced
an TI custom hardware block to better facilitate and standardize
integration of Power Management ICs which communicate over I2C.

In fact, many custom PMICs were designed to be usable over this
interface. On the other hand, generic PMICs which are compatible
to the I2C protocol supported by Voltage controller may also be
used.

In general, the following categories of PMICs exist:

a) PMICs which are completely controlled by voltage controller/voltage
processor pair - this implies even configuration needs to be done
over the same interface. Example: TPS62361 used on PandaBoard-ES and
many OMAP4460 based platforms. Few Maxim and Fairchild PMICs used on
certain products would fall in this category.
- Note: in this case, there may not even exist/needed to support an
"traditional Linux regulator driver"

b) PMICs which provide two views over two I2C interfaces - however,
voltage can only be set only on one of them. Example: TWL4030/5030:
allows us to use Voltage controller once we set up a bit over regular
I2C - This is used in OMAP3. TWO6030/TWL6032 - configuration *has*
to be performed over regular i2c (example smps_offset) and voltage
control *has* to be performed by using voltage controller/processor.
- Note: in this case, there may exist "traditional Linux regulator driver"
however, it may not support in any form SMPS modelling for that part of
the device which is exposed to voltage controller/processor.
c) PMICs which allow voltage and configurations over either i2c
interfaces - TWL6035/TWL6037/Palmas family of TI processor
- Note: in this case, there may exist "traditional Linux regulator driver"
and, it may support in some form SMPS modelling for that part of
the device which is exposed to voltage controller/processor.
d) custom PMICs which are setup so that no configuration is needed to be
performed and they operate with preset register offsets and minimal
conferability using voltage controller/processor.
- Note: in this case, there may not even exist/needed to support an
"traditional Linux regulator driver"

However, no matter the type of PMIC used, the OMAP view of a PMIC is
generic when used over voltage controller/processor. We attempt to
model this generic view of the regulator represented by OMAP SoC.

Alternative to this approach would be to "hack" into the get
voltage/set voltage interfaces of regulator drivers which represent
the rest of the PMIC controlled over regular I2C interface and
re-route the requests to voltage controller/processor. But, by doing
that, we needlessly create additional code which may be abstracted out
into device tree node information.

Since the regulator node representing PMIC, voltage controller,
processors are probed at varied points in time, probe deferral is used
to sequence in the right order. It is expected by the driver to register
omap_pmic_register_controller_ops providing mandatory operations at the
earliest possible opportunity.

Despite the current SoCs implementing the solution based on voltage
controller and voltage processor (which are part of the OMAP SoC modules
which enable Adaptive Voltage Scaling class support), the interfaces are
generic enough to handle future equivalent modules.

[grygorii.strashko-l0cyMroinI0@public.gmane.org, taras-l0cyMroinI0@public.gmane.org: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko-l0cyMroinI0@public.gmane.org>
Signed-off-by: Taras Kondratiuk <taras-l0cyMroinI0@public.gmane.org>
Signed-off-by: Nishanth Menon <nm-l0cyMroinI0@public.gmane.org>
---
 .../bindings/regulator/omap-pmic-regulator.txt     |   41 ++
 drivers/regulator/Kconfig                          |   12 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/omap-pmic-regulator.c            |  638 ++++++++++++++++++++
 include/linux/regulator/omap-pmic-regulator.h      |  162 +++++
 5 files changed, 854 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
 create mode 100644 drivers/regulator/omap-pmic-regulator.c
 create mode 100644 include/linux/regulator/omap-pmic-regulator.h

diff --git a/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt b/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
new file mode 100644
index 0000000..6991e58
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
@@ -0,0 +1,41 @@
+Generic Power Management IC(PMIC) Regulator for Texas Instruments OMAP SoCs
+
+Required Properties:
+- compatible: Should be one of:
+  - "ti,omap-twl4030-vdd1" - TWL4030/5030/TPS65950 VDD1
+  - "ti,omap-twl4030-vdd2" - TWL4030/5030/TPS65950 VDD2
+  - "ti,omap-twl6030-vcore1" - TWL6030 VCORE1
+  - "ti,omap-twl6030-vcore2" - TWL6030 VCORE2
+  - "ti,omap-twl6030-vcore3" - TWL6030 VCORE3
+  - "ti,omap-tps62361" - TPS62361 VSEL1
+  - "ti,omap-twl6032-smps1" - TWL6032 SMPS1
+  - "ti,omap-twl6032-smps2" - TWL6032 SMPS2
+  - "ti,omap-twl6032-smps5" - TWL6032 SMPS5
+  - "ti,omap-twl6035-smps1" - TWL6035/6037 in SMPS1, 12, 123 configuration
+  - "ti,omap-twl6035-smps4" - TWL6035/6037 in SMPS4, 45 configuration
+  - "ti,omap-twl6035-smps8" - TWL6035/6037 in SMPS8 configuration
+
+Optional Properties:
+- gpios: OF device-tree gpio specification - can be an array, will be setup
+	in the order of definition and set to the flags.
+- pinctrl: OF device-tree pinctrl definitions as needed (usually for the GPIOs)
+- ti,boot-voltage-micro-volts - voltage in microvolts that bootloader is leaving
+	over the PMIC at. This may be 'PMIC data manual configuration' if
+	bootloader does not set an value, or appropritate value.
+
+
+Example: Sample PMIC description
+omap_tps62361: tps62361 {
+	compatible = "ti,omap-tps62361";
+};
+
+/* Board Specific configuration */
+&omap_tps62361 {
+	pinctrl-names = "default";
+	pinctrl-0 = <
+		&tps62361_wkgpio_pins
+	>;
+	gpios = <&gpio1 7 1>;	/* gpio_wk7 */
+
+	ti,boot-voltage-micro-volts=<1200000>;
+};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 9296425..e14b534 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -482,6 +482,18 @@ config REGULATOR_TI_ABB
 	  on TI SoCs may be unstable without enabling this as it provides
 	  device specific optimized bias to allow/optimize functionality.
 
+config REGULATOR_TI_OMAP_PMIC
+	tristate "TI Generic regulator for Voltage Processor / Controller"
+	depends on ARCH_OMAP
+	help
+	  Select this option to support PMICs over Texas Instruments'
+	  custom Voltage Processor + Voltage Controller data interface
+	  used in OMAP SoCs. This is a specific "write-only" interface
+	  designed to interface with I2C based PMICs.
+
+	  This option enables the regulator driver representing the PMIC
+	  on the OMAP VC/VP hardware.
+
 config REGULATOR_VEXPRESS
 	tristate "Versatile Express regulators"
 	depends on VEXPRESS_CONFIG
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 26e6c4a..8874e74 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
 obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
+obj-$(CONFIG_REGULATOR_TI_OMAP_PMIC) += omap-pmic-regulator.o
 obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
diff --git a/drivers/regulator/omap-pmic-regulator.c b/drivers/regulator/omap-pmic-regulator.c
new file mode 100644
index 0000000..40c725f
--- /dev/null
+++ b/drivers/regulator/omap-pmic-regulator.c
@@ -0,0 +1,638 @@
+/*
+ * OMAP Generic PMIC Regulator
+ *
+ * Idea based on arch/arm/mach-omap2/omap_twl.c
+ * Copyright (C) 2010 Texas Instruments Incorporated.
+ * Thara Gopinath
+ * Copyright (C) 2009 Texas Instruments Incorporated.
+ * Nishanth Menon
+ * Copyright (C) 2009 Nokia Corporation
+ * Paul Walmsley
+ *
+ * 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/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/omap-pmic-regulator.h>
+
+#define DRIVER_NAME	"omap-pmic"
+
+static DEFINE_MUTEX(omap_pmic_cops_mutex);
+static struct omap_pmic_controller_ops *pmic_cops;
+
+/**
+ * omap_pmic_register_controller_ops() - Register voltage operations
+ * @cops:	voltage operations
+ *
+ * It is expected that appropriate controller register it's functions
+ * with this driver using this interface, If this is not done, the probe
+ * for the corresponding device will defer till it fails.
+ *
+ * Return: -EBUSY if already registered, else returns 0
+ */
+int omap_pmic_register_controller_ops(struct omap_pmic_controller_ops *cops)
+{
+	int ret = 0;
+
+	mutex_lock(&omap_pmic_cops_mutex);
+	if (pmic_cops) {
+		pr_err("Controller operations already registered\n");
+		ret = -EBUSY;
+		goto out;
+	}
+	if (!cops->devm_pmic_register || !cops->voltage_set ||
+	    !cops->voltage_get || !cops->voltage_get_range) {
+		pr_err("Missing operations!\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	pmic_cops = cops;
+out:
+	mutex_unlock(&omap_pmic_cops_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(omap_pmic_register_controller_ops);
+
+/**
+ * omap_pmic_vsel_to_uv() - Convert voltage selector(vsel) to microvolts
+ * @pmic:	pointer to pmic struct
+ * @vsel:	voltage selector(vsel)
+ * @uv:		If conversion is successful, returns the voltage in micro volts
+ *
+ * Return: 0 if conversion is successful and *uv has proper value, else
+ * appropriate error value for failure.
+ */
+static int omap_pmic_vsel_to_uv(struct omap_pmic *pmic, u8 vsel, u32 *uv)
+{
+	u32 tmp = vsel;
+	const struct omap_pmic_info *info;
+
+	if (!pmic || !uv) {
+		pr_err("Bad parameters pmic=%p uv=%p!\n", pmic, uv);
+		return -EINVAL;
+	}
+	info = pmic->info;
+
+	if (info->voltage_selector_mask) {
+		tmp &= info->voltage_selector_mask;
+		tmp >>= __ffs(info->voltage_selector_mask);
+	}
+
+	if (!tmp && info->voltage_selector_zero)
+		goto out;
+
+	tmp -= info->voltage_selector_offset;
+	tmp *= info->step_size_uV;
+	tmp += info->min_uV;
+
+	if (tmp < info->min_uV || tmp > info->max_uV) {
+		dev_dbg(pmic->dev, "%s: Out of range 0x%02x[%d] (%d <-> %d)\n",
+			__func__, vsel, tmp, info->min_uV, info->max_uV);
+		return -ERANGE;
+	}
+
+out:
+	*uv = tmp;
+	dev_dbg(pmic->dev, "%s: uv=%d vsel=0x%02x\n", __func__, *uv, vsel);
+
+	return 0;
+}
+
+/**
+ * omap_pmic_uv_to_vsel() - Convert microvolts to voltage selector(vsel)
+ * @pmic:	pointer to pmic struct
+ * @uv:		voltage in micro volts
+ * @vsel:	If conversion is successful, voltage selector(vsel)
+ *
+ * Return: 0 if conversion is successful and *vsel has proper value, else
+ * appropriate error value for failure.
+ */
+static int omap_pmic_uv_to_vsel(struct omap_pmic *pmic, u32 uv, u8 *vsel)
+{
+	u32 tmp = uv;
+	const struct omap_pmic_info *info;
+
+	if (!pmic || !vsel) {
+		pr_err("Bad parameters pmic=%p vsel=%p!\n", pmic, vsel);
+		return -EINVAL;
+	}
+	info = pmic->info;
+
+	if (!tmp && info->voltage_selector_zero)
+		goto skip_convert;
+
+	if (tmp > info->max_uV)
+		goto skip_convert;
+
+	tmp -= info->min_uV;
+	tmp = DIV_ROUND_UP(tmp, info->step_size_uV);
+
+	tmp += info->voltage_selector_offset;
+
+skip_convert:
+	if (tmp > 0xFF) {
+		dev_dbg(pmic->dev, "%s: Out of range 0x%04x[%d] (%d - %d)\n",
+			__func__, tmp, uv, info->min_uV, info->max_uV);
+		return -ERANGE;
+	}
+	if (info->voltage_selector_mask) {
+		tmp <<= __ffs(info->voltage_selector_mask);
+		if (tmp > 0xFF) {
+			dev_warn(pmic->dev, "%s: Out of range 0x%04x[%d]\n",
+				 __func__, tmp, uv);
+			return -ERANGE;
+		}
+		tmp &= info->voltage_selector_mask;
+	}
+
+	tmp |= info->voltage_selector_setbits;
+
+	*vsel = tmp;
+	dev_dbg(pmic->dev, "%s: uv=%d vsel=0x%02x\n", __func__, uv, *vsel);
+
+	return 0;
+}
+
+/**
+ * omap_pmic_set_voltage() - regulator interface to set voltage
+ * @rdev:	regulator device
+ * @min_uV:	min voltage in micro-volts
+ * @max_uV:	max voltage in micro-volts
+ * @unused:	unused.. we dont use sel
+ *
+ * Return: -ERANGE for out of range values, appropriate error code if conversion
+ * fails, else returns 0.
+ */
+static int omap_pmic_set_voltage(struct regulator_dev *rdev, int min_uV,
+				 int max_uV, unsigned *unused)
+{
+	struct omap_pmic *pmic = rdev_get_drvdata(rdev);
+
+	return pmic_cops->voltage_set(pmic->v_dev, min_uV);
+}
+
+/**
+ * omap_pmic_get_voltage() - regulator interface to get voltage
+ * @rdev: regulator device
+ *
+ * Return: current voltage set on PMIC OR appropriate error value
+ */
+static int omap_pmic_get_voltage(struct regulator_dev *rdev)
+{
+	struct omap_pmic *pmic = rdev_get_drvdata(rdev);
+	int ret;
+	u32 uv;
+
+	ret = pmic_cops->voltage_get(pmic->v_dev, &uv);
+	if (ret)
+		return ret;
+
+	return uv;
+}
+
+static struct omap_pmic_ops omap_generic_pmic_ops = {
+	.vsel_to_uv = omap_pmic_vsel_to_uv,
+	.uv_to_vsel = omap_pmic_uv_to_vsel,
+};
+
+static struct regulator_ops omap_pmic_reg_ops = {
+	.list_voltage = regulator_list_voltage_linear,
+
+	.set_voltage = omap_pmic_set_voltage,
+	.get_voltage = omap_pmic_get_voltage,
+};
+
+/**
+ * omap_pmic_of_setup_gpios() - Setup GPIO array if needed.
+ * @dev:	device to pick up the gpios from
+ */
+static int omap_pmic_of_setup_gpios(struct device *dev)
+{
+	struct device_node *node = dev->of_node;
+	int num_gpios, i, ret;
+
+	num_gpios = of_gpio_count(node);
+	if (num_gpios < 0)
+		return 0;
+
+	for (i = 0; i < num_gpios; i++) {
+		int gpio, level;
+		enum of_gpio_flags flags;
+
+		gpio = of_get_gpio_flags(node, i, &flags);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(dev, "Invalid GPIO[%d]: %d\n", i, gpio);
+			return -EINVAL;
+		}
+
+		ret = devm_gpio_request(dev, gpio, dev_name(dev));
+		if (ret) {
+			dev_err(dev, "Unable to get GPIO %d (%d)\n", gpio, ret);
+			return ret;
+		}
+		level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+		ret = gpio_direction_output(gpio, level);
+		if (ret) {
+			dev_err(dev, "Failed to set GPIO %d to %d (%d)\n",
+				gpio, level, ret);
+			return ret;
+		}
+		dev_dbg(dev, "GPIO=%d set_to=%d flags=0x%08x\n", gpio,
+			level, flags);
+	}
+
+	return 0;
+}
+
+/**
+ * omap_pmic_parse_of() - Do DT OF node parsing
+ * @pmic:	pointer to PMIC
+ */
+static int omap_pmic_parse_of(struct omap_pmic *pmic)
+{
+	struct device *dev = pmic->dev;
+	struct device_node *node = dev->of_node;
+	u32 val = 0;
+	char *pname;
+	int ret;
+
+	pname = "ti,boot-voltage-micro-volts";
+	ret = of_property_read_u32(node, pname, &val);
+	if (!ret) {
+		if (!val)
+			goto invalid_of_property;
+		pmic->boot_voltage_uV = val;
+	}
+
+	return ret;
+
+invalid_of_property:
+	if (!ret) {
+		dev_err(dev, "Invalid value 0x%x[%d] in '%s' property.\n",
+			val, val, pname);
+		ret = -EINVAL;
+	} else {
+		dev_err(dev, "Missing/Invalid '%s' property - error(%d)\n",
+			pname, ret);
+	}
+	return ret;
+}
+
+static const struct omap_pmic_info omap_twl4030_vdd1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x00,
+	.cmd_reg_addr = 0x00,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 4000,
+	.step_size_uV = 12500,
+	.min_uV = 600000,
+	.max_uV = 1450000,
+	.voltage_selector_offset = 0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = false,
+};
+
+static const struct omap_pmic_info omap_twl4030_vdd2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x01,
+	.cmd_reg_addr = 0x01,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 4000,
+	.step_size_uV = 12500,
+	.min_uV = 600000,
+	.max_uV = 1450000,
+	.voltage_selector_offset = 0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = false,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x55,
+	.cmd_reg_addr = 0x56,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x5b,
+	.cmd_reg_addr = 0x5c,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore3 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x61,
+	.cmd_reg_addr = 0x62,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_setup_commands omap_tps62361_cmds[] = {
+	{.reg = 0x06, .cmd_val = 0x06},	/* TPS6236X_RAMP_CTRL 32mV/uS */
+	{.reg = 0x04, .cmd_val = 0xc0},	/* TPS6236X_CTRL VSEL0 pull down */
+	{.reg = 0x05, .cmd_val = 0x00}, /* REG_TPS6236X_TEMP enable tshut */
+};
+
+static const struct omap_pmic_info omap_tps62361 = {
+	.slave_addr = 0x60,
+	.voltage_reg_addr = 0x01,
+	.cmd_reg_addr = 0x01,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 32000,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1770000,
+	.voltage_selector_offset = 0x0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x80, /* PFM mode */
+	.voltage_selector_zero = false,
+	.setup_command_list = omap_tps62361_cmds,
+	.setup_num_commands = ARRAY_SIZE(omap_tps62361_cmds),
+};
+
+static const struct omap_pmic_info omap_twl6032_smps1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x55,
+	.cmd_reg_addr = 0x56,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6032_smps2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x5b,
+	.cmd_reg_addr = 0x5c,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6032_smps5 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x49,
+	.cmd_reg_addr = 0x4a,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x23,
+	.cmd_reg_addr = 0x22,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps4 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x2b,
+	.cmd_reg_addr = 0x2a,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps8 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x37,
+	.cmd_reg_addr = 0x36,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct of_device_id omap_pmic_of_match_tbl[] = {
+	{.compatible = "ti,omap-twl4030-vdd1", .data = &omap_twl4030_vdd1,},
+	{.compatible = "ti,omap-twl4030-vdd2", .data = &omap_twl4030_vdd2,},
+	{.compatible = "ti,omap-twl6030-vcore1", .data = &omap_twl6030_vcore1,},
+	{.compatible = "ti,omap-twl6030-vcore2", .data = &omap_twl6030_vcore2,},
+	{.compatible = "ti,omap-twl6030-vcore3", .data = &omap_twl6030_vcore3,},
+	{.compatible = "ti,omap-tps62361", .data = &omap_tps62361,},
+	{.compatible = "ti,omap-twl6032-smps1", .data = &omap_twl6032_smps1,},
+	{.compatible = "ti,omap-twl6032-smps2", .data = &omap_twl6032_smps2,},
+	{.compatible = "ti,omap-twl6032-smps5", .data = &omap_twl6032_smps5,},
+	{.compatible = "ti,omap-twl6035-smps1", .data = &omap_twl6035_smps1,},
+	{.compatible = "ti,omap-twl6035-smps4", .data = &omap_twl6035_smps4,},
+	{.compatible = "ti,omap-twl6035-smps8", .data = &omap_twl6035_smps8,},
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_pmic_of_match_tbl);
+
+static int omap_pmic_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+	struct omap_pmic *pmic;
+	struct regulator_desc *desc;
+	struct regulation_constraints *c;
+	struct regulator_config config = { };
+	struct regulator_init_data *initdata = NULL;
+	struct regulator_dev *rdev = NULL;
+	int ret = 0;
+	bool ops_ready;
+
+	if (!node) {
+		dev_err(dev, "%s: missing device tree nodes?\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&omap_pmic_cops_mutex);
+	ops_ready = pmic_cops ? true : false;
+	mutex_unlock(&omap_pmic_cops_mutex);
+	if (!ops_ready) {
+		dev_dbg(dev, "Voltage Operations not ready yet..\n");
+		return -EPROBE_DEFER;
+	}
+
+	match = of_match_device(omap_pmic_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;
+	}
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc) {
+		dev_err(dev, "%s: unable to allocate desc\n", __func__);
+		return -ENOMEM;
+	}
+
+	pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+	if (!pmic) {
+		dev_err(dev, "%s: unable to allocate pmic\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Read mandatory OF parameters */
+	pmic->dev = dev;
+	pmic->ops = &omap_generic_pmic_ops;
+	pmic->info = match->data;
+
+	initdata = of_get_regulator_init_data(dev, node);
+	if (!initdata) {
+		dev_err(dev, "%s: Unable to alloc regulator init data\n",
+			__func__);
+		return -ENOMEM;
+	}
+	c = &initdata->constraints;
+
+	/* Constraint to PMIC limits */
+	if (pmic->info->min_uV > c->min_uV)
+		c->min_uV = pmic->info->min_uV;
+	if (pmic->info->max_uV < c->max_uV)
+		c->max_uV = pmic->info->max_uV;
+
+	ret = omap_pmic_parse_of(pmic);
+	if (ret)
+		return ret;
+
+	ret = omap_pmic_of_setup_gpios(dev);
+	if (ret)
+		return ret;
+
+	pmic->v_dev = pmic_cops->devm_pmic_register(dev, pmic);
+	if (IS_ERR(pmic->v_dev)) {
+		dev_dbg(dev, "Registration of pmic failed (%d)\n", ret);
+		ret = PTR_ERR(pmic->v_dev);
+		return ret;
+	}
+	desc->name = dev_name(dev);
+	desc->owner = THIS_MODULE;
+	desc->type = REGULATOR_VOLTAGE;
+	desc->ops = &omap_pmic_reg_ops;
+	desc->uV_step = pmic->info->step_size_uV;
+	desc->ramp_delay = pmic->info->slew_rate_uV;
+
+	c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
+	c->always_on = true;
+	ret = pmic_cops->voltage_get_range(pmic->v_dev, &c->min_uV, &c->max_uV);
+	if (ret) {
+		dev_err(dev, "Voltage Range get failed (%d)\n", ret);
+		return ret;
+	}
+
+	config.dev = dev;
+	config.init_data = initdata;
+	config.driver_data = pmic;
+	config.of_node = node;
+
+	rdev = regulator_register(desc, &config);
+	if (IS_ERR(rdev)) {
+		ret = PTR_ERR(rdev);
+		dev_err(dev, "%s: failed to register regulator(%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, rdev);
+
+	return ret;
+}
+
+static struct platform_driver omap_pmic_driver = {
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(omap_pmic_of_match_tbl),
+		   },
+	.probe = omap_pmic_probe,
+};
+module_platform_driver(omap_pmic_driver);
+
+MODULE_DESCRIPTION("OMAP Generic PMIC Regulator");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc.");
diff --git a/include/linux/regulator/omap-pmic-regulator.h b/include/linux/regulator/omap-pmic-regulator.h
new file mode 100644
index 0000000..db0bab4
--- /dev/null
+++ b/include/linux/regulator/omap-pmic-regulator.h
@@ -0,0 +1,162 @@
+/*
+ * OMAP Generic PMIC Regulator interfaces
+ *
+ * Idea based on arch/arm/mach-omap2/omap_twl.c and
+ * arch/arm/mach-omap2/voltage.h
+ * Copyright (C) 2010 Texas Instruments Incorporated.
+ * Thara Gopinath
+ * Copyright (C) 2009 Texas Instruments Incorporated.
+ * Nishanth Menon
+ * Copyright (C) 2009 Nokia Corporation
+ * 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_OMAP_PMIC_H
+#define __POWER_OMAP_PMIC_H
+
+struct omap_pmic;
+
+/**
+ * struct omap_pmic_setup_commands - setup commands over voltage controller
+ * @reg:	device's i2c register address
+ * @cmd_val:	command to send.
+ */
+struct omap_pmic_setup_commands {
+	u8 reg;
+	u8 cmd_val;
+};
+
+/**
+ * struct omap_pmic_ops - Conversion routines for voltage controller/processor
+ * @vsel_to_uv: convert voltage selector to micro-volts
+ * @uv_to_vsel: convert micro-volts to voltage selector
+ *
+ * voltage controller/processor drivers SHOULD NOT do modifications on vsel or
+ * make any assumptions about vsel. Instead, they may operate on micro-volts
+ * and request vsel conversion once they are ready to do hardware operations.
+ *
+ * This is provided over the omap_pmic structure.
+ */
+struct omap_pmic_ops {
+	int (*vsel_to_uv) (struct omap_pmic *pmic, u8 vsel, u32 *uv);
+	int (*uv_to_vsel) (struct omap_pmic *pmic, u32 uv, u8 *vsel);
+};
+
+/**
+ * struct omap_pmic_controller_ops - regulator operations implemented
+ * @devm_pmic_register: managed registration of an PMIC device with a specific
+ *			voltage processor. Voltage processor provides an device
+ *			handle which is remaining operations.
+ *			NOTE:
+ *			- This will be first interface to be invoked by
+ *			omap_pmic regulator.
+ *			- if the underlying layers are not ready, this is
+ *			expected to return -EPROBE_DEFER
+ *			- if failure, appropriate error code is expected.
+ *			- This is expected to be a managed device to avoid
+ *			explicit cleanup operations
+ *			- Once this succeeds, this returns the pointer to
+ *			the controller device and all other operations are
+ *			expected to be ready for functionality.
+ * @voltage_set:	set the voltage - expected to be synchronous.
+ * @voltage_get:	get the current voltage in micro-volts set on PMIC.
+ * @voltage_get_range:	Get minimum and maxium permissible operational voltage
+ *			range for the device - used to set initial regulator
+ *			constraints.
+ *
+ * These voltage processor interfaces are registered by voltage processor driver
+ * using omap_pmic_register_controller_ops. This allows the omap_pmic driver to
+ * operate with a specific voltage processor driver.
+ */
+struct omap_pmic_controller_ops {
+	struct device *(*devm_pmic_register) (struct device *dev,
+					      struct omap_pmic *pmic);
+	int (*voltage_set) (struct device *control_dev, u32 uv);
+	int (*voltage_get) (struct device *control_dev, u32 *uv);
+	int (*voltage_get_range) (struct device *control_dev, u32 *min_uv,
+				  u32 *max_uv);
+};
+
+/**
+ * struct omap_pmic_info - PMIC information
+ *
+ * @slave_addr:		7 bit address representing I2C slave address.
+ * @voltage_reg_addr:	I2C register address for setting voltage
+ * @cmd_reg_addr:	I2C register address for low power transition commands
+ * @i2c_timeout_us:	worst case latency for I2C operations for the device
+ * @slew_rate_uV:	Slew rate in uV/uSeconds for voltage transitions
+ * @step_size_uV:	Step size in uV for one vsel increment.
+ * @min_uV:		represents the minimum step_sized incremental voltage
+ * @max_uV:		represents the maximum step_sized incremental voltage
+ * @voltage_selector_setbits:  what bits to set permenantly for the PMIC
+ *			voltage selector - this may have PMIC specific meaning.
+ * @voltage_selector_mask: what mask to use for the vsel value - this is useful
+ *			for PMICs where the vsel has to be applied at an offset.
+ * @voltage_selector_offset: what offset to apply to conversion routine when
+ *			operating on vsel.
+ * @voltage_selector_zero: Special case handling if 0 value does NOT indicates
+ *			power-off for PMIC.
+ * @setup_command_list:	array of setup commands for PMIC to operate
+ * @setup_num_commands:	number of setup commands for PMIC to operate
+ */
+struct omap_pmic_info {
+	u8 slave_addr;
+	u8 voltage_reg_addr;
+	u8 cmd_reg_addr;
+	u32 i2c_timeout_us;
+
+	u32 slew_rate_uV;
+	u32 step_size_uV;
+	u32 min_uV;
+	u32 max_uV;
+
+	u8 voltage_selector_offset;
+	u8 voltage_selector_mask;
+	u8 voltage_selector_setbits;
+	bool voltage_selector_zero;
+
+	const struct omap_pmic_setup_commands *setup_command_list;
+	u8 setup_num_commands;
+};
+
+/**
+ * struct omap_pmic - represents the OMAP PMIC device.
+ * @ops:		PMIC conversion operations.
+ *
+ * NOTE: the fields marked Private are meant for PMIC driver.
+ */
+struct omap_pmic {
+	const struct omap_pmic_info *info;
+	u32 boot_voltage_uV;
+
+	struct omap_pmic_ops *ops;
+
+	struct device *dev;
+	struct device *v_dev;
+};
+
+#if IS_ENABLED(CONFIG_REGULATOR_TI_OMAP_PMIC)
+int omap_pmic_register_controller_ops(struct omap_pmic_controller_ops *cops);
+#else
+static inline int omap_pmic_register_controller_ops(struct
+						    omap_pmic_controller_ops
+						    *cops)
+{
+	return -EINVAL;
+}
+#endif
+
+#endif				/* __POWER_OMAP_PMIC_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced
an TI custom hardware block to better facilitate and standardize
integration of Power Management ICs which communicate over I2C.

In fact, many custom PMICs were designed to be usable over this
interface. On the other hand, generic PMICs which are compatible
to the I2C protocol supported by Voltage controller may also be
used.

In general, the following categories of PMICs exist:

a) PMICs which are completely controlled by voltage controller/voltage
processor pair - this implies even configuration needs to be done
over the same interface. Example: TPS62361 used on PandaBoard-ES and
many OMAP4460 based platforms. Few Maxim and Fairchild PMICs used on
certain products would fall in this category.
- Note: in this case, there may not even exist/needed to support an
"traditional Linux regulator driver"

b) PMICs which provide two views over two I2C interfaces - however,
voltage can only be set only on one of them. Example: TWL4030/5030:
allows us to use Voltage controller once we set up a bit over regular
I2C - This is used in OMAP3. TWO6030/TWL6032 - configuration *has*
to be performed over regular i2c (example smps_offset) and voltage
control *has* to be performed by using voltage controller/processor.
- Note: in this case, there may exist "traditional Linux regulator driver"
however, it may not support in any form SMPS modelling for that part of
the device which is exposed to voltage controller/processor.
c) PMICs which allow voltage and configurations over either i2c
interfaces - TWL6035/TWL6037/Palmas family of TI processor
- Note: in this case, there may exist "traditional Linux regulator driver"
and, it may support in some form SMPS modelling for that part of
the device which is exposed to voltage controller/processor.
d) custom PMICs which are setup so that no configuration is needed to be
performed and they operate with preset register offsets and minimal
conferability using voltage controller/processor.
- Note: in this case, there may not even exist/needed to support an
"traditional Linux regulator driver"

However, no matter the type of PMIC used, the OMAP view of a PMIC is
generic when used over voltage controller/processor. We attempt to
model this generic view of the regulator represented by OMAP SoC.

Alternative to this approach would be to "hack" into the get
voltage/set voltage interfaces of regulator drivers which represent
the rest of the PMIC controlled over regular I2C interface and
re-route the requests to voltage controller/processor. But, by doing
that, we needlessly create additional code which may be abstracted out
into device tree node information.

Since the regulator node representing PMIC, voltage controller,
processors are probed at varied points in time, probe deferral is used
to sequence in the right order. It is expected by the driver to register
omap_pmic_register_controller_ops providing mandatory operations at the
earliest possible opportunity.

Despite the current SoCs implementing the solution based on voltage
controller and voltage processor (which are part of the OMAP SoC modules
which enable Adaptive Voltage Scaling class support), the interfaces are
generic enough to handle future equivalent modules.

[grygorii.strashko at ti.com, taras at ti.com: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Taras Kondratiuk <taras@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
---
 .../bindings/regulator/omap-pmic-regulator.txt     |   41 ++
 drivers/regulator/Kconfig                          |   12 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/omap-pmic-regulator.c            |  638 ++++++++++++++++++++
 include/linux/regulator/omap-pmic-regulator.h      |  162 +++++
 5 files changed, 854 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
 create mode 100644 drivers/regulator/omap-pmic-regulator.c
 create mode 100644 include/linux/regulator/omap-pmic-regulator.h

diff --git a/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt b/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
new file mode 100644
index 0000000..6991e58
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/omap-pmic-regulator.txt
@@ -0,0 +1,41 @@
+Generic Power Management IC(PMIC) Regulator for Texas Instruments OMAP SoCs
+
+Required Properties:
+- compatible: Should be one of:
+  - "ti,omap-twl4030-vdd1" - TWL4030/5030/TPS65950 VDD1
+  - "ti,omap-twl4030-vdd2" - TWL4030/5030/TPS65950 VDD2
+  - "ti,omap-twl6030-vcore1" - TWL6030 VCORE1
+  - "ti,omap-twl6030-vcore2" - TWL6030 VCORE2
+  - "ti,omap-twl6030-vcore3" - TWL6030 VCORE3
+  - "ti,omap-tps62361" - TPS62361 VSEL1
+  - "ti,omap-twl6032-smps1" - TWL6032 SMPS1
+  - "ti,omap-twl6032-smps2" - TWL6032 SMPS2
+  - "ti,omap-twl6032-smps5" - TWL6032 SMPS5
+  - "ti,omap-twl6035-smps1" - TWL6035/6037 in SMPS1, 12, 123 configuration
+  - "ti,omap-twl6035-smps4" - TWL6035/6037 in SMPS4, 45 configuration
+  - "ti,omap-twl6035-smps8" - TWL6035/6037 in SMPS8 configuration
+
+Optional Properties:
+- gpios: OF device-tree gpio specification - can be an array, will be setup
+	in the order of definition and set to the flags.
+- pinctrl: OF device-tree pinctrl definitions as needed (usually for the GPIOs)
+- ti,boot-voltage-micro-volts - voltage in microvolts that bootloader is leaving
+	over the PMIC at. This may be 'PMIC data manual configuration' if
+	bootloader does not set an value, or appropritate value.
+
+
+Example: Sample PMIC description
+omap_tps62361: tps62361 {
+	compatible = "ti,omap-tps62361";
+};
+
+/* Board Specific configuration */
+&omap_tps62361 {
+	pinctrl-names = "default";
+	pinctrl-0 = <
+		&tps62361_wkgpio_pins
+	>;
+	gpios = <&gpio1 7 1>;	/* gpio_wk7 */
+
+	ti,boot-voltage-micro-volts=<1200000>;
+};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 9296425..e14b534 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -482,6 +482,18 @@ config REGULATOR_TI_ABB
 	  on TI SoCs may be unstable without enabling this as it provides
 	  device specific optimized bias to allow/optimize functionality.
 
+config REGULATOR_TI_OMAP_PMIC
+	tristate "TI Generic regulator for Voltage Processor / Controller"
+	depends on ARCH_OMAP
+	help
+	  Select this option to support PMICs over Texas Instruments'
+	  custom Voltage Processor + Voltage Controller data interface
+	  used in OMAP SoCs. This is a specific "write-only" interface
+	  designed to interface with I2C based PMICs.
+
+	  This option enables the regulator driver representing the PMIC
+	  on the OMAP VC/VP hardware.
+
 config REGULATOR_VEXPRESS
 	tristate "Versatile Express regulators"
 	depends on VEXPRESS_CONFIG
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 26e6c4a..8874e74 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
 obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
+obj-$(CONFIG_REGULATOR_TI_OMAP_PMIC) += omap-pmic-regulator.o
 obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
diff --git a/drivers/regulator/omap-pmic-regulator.c b/drivers/regulator/omap-pmic-regulator.c
new file mode 100644
index 0000000..40c725f
--- /dev/null
+++ b/drivers/regulator/omap-pmic-regulator.c
@@ -0,0 +1,638 @@
+/*
+ * OMAP Generic PMIC Regulator
+ *
+ * Idea based on arch/arm/mach-omap2/omap_twl.c
+ * Copyright (C) 2010 Texas Instruments Incorporated.
+ * Thara Gopinath
+ * Copyright (C) 2009 Texas Instruments Incorporated.
+ * Nishanth Menon
+ * Copyright (C) 2009 Nokia Corporation
+ * Paul Walmsley
+ *
+ * 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/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/omap-pmic-regulator.h>
+
+#define DRIVER_NAME	"omap-pmic"
+
+static DEFINE_MUTEX(omap_pmic_cops_mutex);
+static struct omap_pmic_controller_ops *pmic_cops;
+
+/**
+ * omap_pmic_register_controller_ops() - Register voltage operations
+ * @cops:	voltage operations
+ *
+ * It is expected that appropriate controller register it's functions
+ * with this driver using this interface, If this is not done, the probe
+ * for the corresponding device will defer till it fails.
+ *
+ * Return: -EBUSY if already registered, else returns 0
+ */
+int omap_pmic_register_controller_ops(struct omap_pmic_controller_ops *cops)
+{
+	int ret = 0;
+
+	mutex_lock(&omap_pmic_cops_mutex);
+	if (pmic_cops) {
+		pr_err("Controller operations already registered\n");
+		ret = -EBUSY;
+		goto out;
+	}
+	if (!cops->devm_pmic_register || !cops->voltage_set ||
+	    !cops->voltage_get || !cops->voltage_get_range) {
+		pr_err("Missing operations!\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	pmic_cops = cops;
+out:
+	mutex_unlock(&omap_pmic_cops_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(omap_pmic_register_controller_ops);
+
+/**
+ * omap_pmic_vsel_to_uv() - Convert voltage selector(vsel) to microvolts
+ * @pmic:	pointer to pmic struct
+ * @vsel:	voltage selector(vsel)
+ * @uv:		If conversion is successful, returns the voltage in micro volts
+ *
+ * Return: 0 if conversion is successful and *uv has proper value, else
+ * appropriate error value for failure.
+ */
+static int omap_pmic_vsel_to_uv(struct omap_pmic *pmic, u8 vsel, u32 *uv)
+{
+	u32 tmp = vsel;
+	const struct omap_pmic_info *info;
+
+	if (!pmic || !uv) {
+		pr_err("Bad parameters pmic=%p uv=%p!\n", pmic, uv);
+		return -EINVAL;
+	}
+	info = pmic->info;
+
+	if (info->voltage_selector_mask) {
+		tmp &= info->voltage_selector_mask;
+		tmp >>= __ffs(info->voltage_selector_mask);
+	}
+
+	if (!tmp && info->voltage_selector_zero)
+		goto out;
+
+	tmp -= info->voltage_selector_offset;
+	tmp *= info->step_size_uV;
+	tmp += info->min_uV;
+
+	if (tmp < info->min_uV || tmp > info->max_uV) {
+		dev_dbg(pmic->dev, "%s: Out of range 0x%02x[%d] (%d <-> %d)\n",
+			__func__, vsel, tmp, info->min_uV, info->max_uV);
+		return -ERANGE;
+	}
+
+out:
+	*uv = tmp;
+	dev_dbg(pmic->dev, "%s: uv=%d vsel=0x%02x\n", __func__, *uv, vsel);
+
+	return 0;
+}
+
+/**
+ * omap_pmic_uv_to_vsel() - Convert microvolts to voltage selector(vsel)
+ * @pmic:	pointer to pmic struct
+ * @uv:		voltage in micro volts
+ * @vsel:	If conversion is successful, voltage selector(vsel)
+ *
+ * Return: 0 if conversion is successful and *vsel has proper value, else
+ * appropriate error value for failure.
+ */
+static int omap_pmic_uv_to_vsel(struct omap_pmic *pmic, u32 uv, u8 *vsel)
+{
+	u32 tmp = uv;
+	const struct omap_pmic_info *info;
+
+	if (!pmic || !vsel) {
+		pr_err("Bad parameters pmic=%p vsel=%p!\n", pmic, vsel);
+		return -EINVAL;
+	}
+	info = pmic->info;
+
+	if (!tmp && info->voltage_selector_zero)
+		goto skip_convert;
+
+	if (tmp > info->max_uV)
+		goto skip_convert;
+
+	tmp -= info->min_uV;
+	tmp = DIV_ROUND_UP(tmp, info->step_size_uV);
+
+	tmp += info->voltage_selector_offset;
+
+skip_convert:
+	if (tmp > 0xFF) {
+		dev_dbg(pmic->dev, "%s: Out of range 0x%04x[%d] (%d - %d)\n",
+			__func__, tmp, uv, info->min_uV, info->max_uV);
+		return -ERANGE;
+	}
+	if (info->voltage_selector_mask) {
+		tmp <<= __ffs(info->voltage_selector_mask);
+		if (tmp > 0xFF) {
+			dev_warn(pmic->dev, "%s: Out of range 0x%04x[%d]\n",
+				 __func__, tmp, uv);
+			return -ERANGE;
+		}
+		tmp &= info->voltage_selector_mask;
+	}
+
+	tmp |= info->voltage_selector_setbits;
+
+	*vsel = tmp;
+	dev_dbg(pmic->dev, "%s: uv=%d vsel=0x%02x\n", __func__, uv, *vsel);
+
+	return 0;
+}
+
+/**
+ * omap_pmic_set_voltage() - regulator interface to set voltage
+ * @rdev:	regulator device
+ * @min_uV:	min voltage in micro-volts
+ * @max_uV:	max voltage in micro-volts
+ * @unused:	unused.. we dont use sel
+ *
+ * Return: -ERANGE for out of range values, appropriate error code if conversion
+ * fails, else returns 0.
+ */
+static int omap_pmic_set_voltage(struct regulator_dev *rdev, int min_uV,
+				 int max_uV, unsigned *unused)
+{
+	struct omap_pmic *pmic = rdev_get_drvdata(rdev);
+
+	return pmic_cops->voltage_set(pmic->v_dev, min_uV);
+}
+
+/**
+ * omap_pmic_get_voltage() - regulator interface to get voltage
+ * @rdev: regulator device
+ *
+ * Return: current voltage set on PMIC OR appropriate error value
+ */
+static int omap_pmic_get_voltage(struct regulator_dev *rdev)
+{
+	struct omap_pmic *pmic = rdev_get_drvdata(rdev);
+	int ret;
+	u32 uv;
+
+	ret = pmic_cops->voltage_get(pmic->v_dev, &uv);
+	if (ret)
+		return ret;
+
+	return uv;
+}
+
+static struct omap_pmic_ops omap_generic_pmic_ops = {
+	.vsel_to_uv = omap_pmic_vsel_to_uv,
+	.uv_to_vsel = omap_pmic_uv_to_vsel,
+};
+
+static struct regulator_ops omap_pmic_reg_ops = {
+	.list_voltage = regulator_list_voltage_linear,
+
+	.set_voltage = omap_pmic_set_voltage,
+	.get_voltage = omap_pmic_get_voltage,
+};
+
+/**
+ * omap_pmic_of_setup_gpios() - Setup GPIO array if needed.
+ * @dev:	device to pick up the gpios from
+ */
+static int omap_pmic_of_setup_gpios(struct device *dev)
+{
+	struct device_node *node = dev->of_node;
+	int num_gpios, i, ret;
+
+	num_gpios = of_gpio_count(node);
+	if (num_gpios < 0)
+		return 0;
+
+	for (i = 0; i < num_gpios; i++) {
+		int gpio, level;
+		enum of_gpio_flags flags;
+
+		gpio = of_get_gpio_flags(node, i, &flags);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(dev, "Invalid GPIO[%d]: %d\n", i, gpio);
+			return -EINVAL;
+		}
+
+		ret = devm_gpio_request(dev, gpio, dev_name(dev));
+		if (ret) {
+			dev_err(dev, "Unable to get GPIO %d (%d)\n", gpio, ret);
+			return ret;
+		}
+		level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+		ret = gpio_direction_output(gpio, level);
+		if (ret) {
+			dev_err(dev, "Failed to set GPIO %d to %d (%d)\n",
+				gpio, level, ret);
+			return ret;
+		}
+		dev_dbg(dev, "GPIO=%d set_to=%d flags=0x%08x\n", gpio,
+			level, flags);
+	}
+
+	return 0;
+}
+
+/**
+ * omap_pmic_parse_of() - Do DT OF node parsing
+ * @pmic:	pointer to PMIC
+ */
+static int omap_pmic_parse_of(struct omap_pmic *pmic)
+{
+	struct device *dev = pmic->dev;
+	struct device_node *node = dev->of_node;
+	u32 val = 0;
+	char *pname;
+	int ret;
+
+	pname = "ti,boot-voltage-micro-volts";
+	ret = of_property_read_u32(node, pname, &val);
+	if (!ret) {
+		if (!val)
+			goto invalid_of_property;
+		pmic->boot_voltage_uV = val;
+	}
+
+	return ret;
+
+invalid_of_property:
+	if (!ret) {
+		dev_err(dev, "Invalid value 0x%x[%d] in '%s' property.\n",
+			val, val, pname);
+		ret = -EINVAL;
+	} else {
+		dev_err(dev, "Missing/Invalid '%s' property - error(%d)\n",
+			pname, ret);
+	}
+	return ret;
+}
+
+static const struct omap_pmic_info omap_twl4030_vdd1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x00,
+	.cmd_reg_addr = 0x00,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 4000,
+	.step_size_uV = 12500,
+	.min_uV = 600000,
+	.max_uV = 1450000,
+	.voltage_selector_offset = 0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = false,
+};
+
+static const struct omap_pmic_info omap_twl4030_vdd2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x01,
+	.cmd_reg_addr = 0x01,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 4000,
+	.step_size_uV = 12500,
+	.min_uV = 600000,
+	.max_uV = 1450000,
+	.voltage_selector_offset = 0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = false,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x55,
+	.cmd_reg_addr = 0x56,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x5b,
+	.cmd_reg_addr = 0x5c,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6030_vcore3 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x61,
+	.cmd_reg_addr = 0x62,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_setup_commands omap_tps62361_cmds[] = {
+	{.reg = 0x06, .cmd_val = 0x06},	/* TPS6236X_RAMP_CTRL 32mV/uS */
+	{.reg = 0x04, .cmd_val = 0xc0},	/* TPS6236X_CTRL VSEL0 pull down */
+	{.reg = 0x05, .cmd_val = 0x00}, /* REG_TPS6236X_TEMP enable tshut */
+};
+
+static const struct omap_pmic_info omap_tps62361 = {
+	.slave_addr = 0x60,
+	.voltage_reg_addr = 0x01,
+	.cmd_reg_addr = 0x01,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 32000,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1770000,
+	.voltage_selector_offset = 0x0,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x80, /* PFM mode */
+	.voltage_selector_zero = false,
+	.setup_command_list = omap_tps62361_cmds,
+	.setup_num_commands = ARRAY_SIZE(omap_tps62361_cmds),
+};
+
+static const struct omap_pmic_info omap_twl6032_smps1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x55,
+	.cmd_reg_addr = 0x56,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6032_smps2 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x5b,
+	.cmd_reg_addr = 0x5c,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6032_smps5 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x49,
+	.cmd_reg_addr = 0x4a,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 9000,
+	.step_size_uV = 12660,
+	.min_uV = 709000,
+	.max_uV = 1418000,
+	.voltage_selector_offset = 0x1,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps1 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x23,
+	.cmd_reg_addr = 0x22,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps4 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x2b,
+	.cmd_reg_addr = 0x2a,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct omap_pmic_info omap_twl6035_smps8 = {
+	.slave_addr = 0x12,
+	.voltage_reg_addr = 0x37,
+	.cmd_reg_addr = 0x36,
+	.i2c_timeout_us = 200,
+	.slew_rate_uV = 220,
+	.step_size_uV = 10000,
+	.min_uV = 500000,
+	.max_uV = 1650000,
+	.voltage_selector_offset = 0x6,
+	.voltage_selector_mask = 0x7F,
+	.voltage_selector_setbits = 0x0,
+	.voltage_selector_zero = true,
+};
+
+static const struct of_device_id omap_pmic_of_match_tbl[] = {
+	{.compatible = "ti,omap-twl4030-vdd1", .data = &omap_twl4030_vdd1,},
+	{.compatible = "ti,omap-twl4030-vdd2", .data = &omap_twl4030_vdd2,},
+	{.compatible = "ti,omap-twl6030-vcore1", .data = &omap_twl6030_vcore1,},
+	{.compatible = "ti,omap-twl6030-vcore2", .data = &omap_twl6030_vcore2,},
+	{.compatible = "ti,omap-twl6030-vcore3", .data = &omap_twl6030_vcore3,},
+	{.compatible = "ti,omap-tps62361", .data = &omap_tps62361,},
+	{.compatible = "ti,omap-twl6032-smps1", .data = &omap_twl6032_smps1,},
+	{.compatible = "ti,omap-twl6032-smps2", .data = &omap_twl6032_smps2,},
+	{.compatible = "ti,omap-twl6032-smps5", .data = &omap_twl6032_smps5,},
+	{.compatible = "ti,omap-twl6035-smps1", .data = &omap_twl6035_smps1,},
+	{.compatible = "ti,omap-twl6035-smps4", .data = &omap_twl6035_smps4,},
+	{.compatible = "ti,omap-twl6035-smps8", .data = &omap_twl6035_smps8,},
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_pmic_of_match_tbl);
+
+static int omap_pmic_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+	struct omap_pmic *pmic;
+	struct regulator_desc *desc;
+	struct regulation_constraints *c;
+	struct regulator_config config = { };
+	struct regulator_init_data *initdata = NULL;
+	struct regulator_dev *rdev = NULL;
+	int ret = 0;
+	bool ops_ready;
+
+	if (!node) {
+		dev_err(dev, "%s: missing device tree nodes?\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&omap_pmic_cops_mutex);
+	ops_ready = pmic_cops ? true : false;
+	mutex_unlock(&omap_pmic_cops_mutex);
+	if (!ops_ready) {
+		dev_dbg(dev, "Voltage Operations not ready yet..\n");
+		return -EPROBE_DEFER;
+	}
+
+	match = of_match_device(omap_pmic_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;
+	}
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc) {
+		dev_err(dev, "%s: unable to allocate desc\n", __func__);
+		return -ENOMEM;
+	}
+
+	pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+	if (!pmic) {
+		dev_err(dev, "%s: unable to allocate pmic\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Read mandatory OF parameters */
+	pmic->dev = dev;
+	pmic->ops = &omap_generic_pmic_ops;
+	pmic->info = match->data;
+
+	initdata = of_get_regulator_init_data(dev, node);
+	if (!initdata) {
+		dev_err(dev, "%s: Unable to alloc regulator init data\n",
+			__func__);
+		return -ENOMEM;
+	}
+	c = &initdata->constraints;
+
+	/* Constraint to PMIC limits */
+	if (pmic->info->min_uV > c->min_uV)
+		c->min_uV = pmic->info->min_uV;
+	if (pmic->info->max_uV < c->max_uV)
+		c->max_uV = pmic->info->max_uV;
+
+	ret = omap_pmic_parse_of(pmic);
+	if (ret)
+		return ret;
+
+	ret = omap_pmic_of_setup_gpios(dev);
+	if (ret)
+		return ret;
+
+	pmic->v_dev = pmic_cops->devm_pmic_register(dev, pmic);
+	if (IS_ERR(pmic->v_dev)) {
+		dev_dbg(dev, "Registration of pmic failed (%d)\n", ret);
+		ret = PTR_ERR(pmic->v_dev);
+		return ret;
+	}
+	desc->name = dev_name(dev);
+	desc->owner = THIS_MODULE;
+	desc->type = REGULATOR_VOLTAGE;
+	desc->ops = &omap_pmic_reg_ops;
+	desc->uV_step = pmic->info->step_size_uV;
+	desc->ramp_delay = pmic->info->slew_rate_uV;
+
+	c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
+	c->always_on = true;
+	ret = pmic_cops->voltage_get_range(pmic->v_dev, &c->min_uV, &c->max_uV);
+	if (ret) {
+		dev_err(dev, "Voltage Range get failed (%d)\n", ret);
+		return ret;
+	}
+
+	config.dev = dev;
+	config.init_data = initdata;
+	config.driver_data = pmic;
+	config.of_node = node;
+
+	rdev = regulator_register(desc, &config);
+	if (IS_ERR(rdev)) {
+		ret = PTR_ERR(rdev);
+		dev_err(dev, "%s: failed to register regulator(%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, rdev);
+
+	return ret;
+}
+
+static struct platform_driver omap_pmic_driver = {
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(omap_pmic_of_match_tbl),
+		   },
+	.probe = omap_pmic_probe,
+};
+module_platform_driver(omap_pmic_driver);
+
+MODULE_DESCRIPTION("OMAP Generic PMIC Regulator");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc.");
diff --git a/include/linux/regulator/omap-pmic-regulator.h b/include/linux/regulator/omap-pmic-regulator.h
new file mode 100644
index 0000000..db0bab4
--- /dev/null
+++ b/include/linux/regulator/omap-pmic-regulator.h
@@ -0,0 +1,162 @@
+/*
+ * OMAP Generic PMIC Regulator interfaces
+ *
+ * Idea based on arch/arm/mach-omap2/omap_twl.c and
+ * arch/arm/mach-omap2/voltage.h
+ * Copyright (C) 2010 Texas Instruments Incorporated.
+ * Thara Gopinath
+ * Copyright (C) 2009 Texas Instruments Incorporated.
+ * Nishanth Menon
+ * Copyright (C) 2009 Nokia Corporation
+ * 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_OMAP_PMIC_H
+#define __POWER_OMAP_PMIC_H
+
+struct omap_pmic;
+
+/**
+ * struct omap_pmic_setup_commands - setup commands over voltage controller
+ * @reg:	device's i2c register address
+ * @cmd_val:	command to send.
+ */
+struct omap_pmic_setup_commands {
+	u8 reg;
+	u8 cmd_val;
+};
+
+/**
+ * struct omap_pmic_ops - Conversion routines for voltage controller/processor
+ * @vsel_to_uv: convert voltage selector to micro-volts
+ * @uv_to_vsel: convert micro-volts to voltage selector
+ *
+ * voltage controller/processor drivers SHOULD NOT do modifications on vsel or
+ * make any assumptions about vsel. Instead, they may operate on micro-volts
+ * and request vsel conversion once they are ready to do hardware operations.
+ *
+ * This is provided over the omap_pmic structure.
+ */
+struct omap_pmic_ops {
+	int (*vsel_to_uv) (struct omap_pmic *pmic, u8 vsel, u32 *uv);
+	int (*uv_to_vsel) (struct omap_pmic *pmic, u32 uv, u8 *vsel);
+};
+
+/**
+ * struct omap_pmic_controller_ops - regulator operations implemented
+ * @devm_pmic_register: managed registration of an PMIC device with a specific
+ *			voltage processor. Voltage processor provides an device
+ *			handle which is remaining operations.
+ *			NOTE:
+ *			- This will be first interface to be invoked by
+ *			omap_pmic regulator.
+ *			- if the underlying layers are not ready, this is
+ *			expected to return -EPROBE_DEFER
+ *			- if failure, appropriate error code is expected.
+ *			- This is expected to be a managed device to avoid
+ *			explicit cleanup operations
+ *			- Once this succeeds, this returns the pointer to
+ *			the controller device and all other operations are
+ *			expected to be ready for functionality.
+ * @voltage_set:	set the voltage - expected to be synchronous.
+ * @voltage_get:	get the current voltage in micro-volts set on PMIC.
+ * @voltage_get_range:	Get minimum and maxium permissible operational voltage
+ *			range for the device - used to set initial regulator
+ *			constraints.
+ *
+ * These voltage processor interfaces are registered by voltage processor driver
+ * using omap_pmic_register_controller_ops. This allows the omap_pmic driver to
+ * operate with a specific voltage processor driver.
+ */
+struct omap_pmic_controller_ops {
+	struct device *(*devm_pmic_register) (struct device *dev,
+					      struct omap_pmic *pmic);
+	int (*voltage_set) (struct device *control_dev, u32 uv);
+	int (*voltage_get) (struct device *control_dev, u32 *uv);
+	int (*voltage_get_range) (struct device *control_dev, u32 *min_uv,
+				  u32 *max_uv);
+};
+
+/**
+ * struct omap_pmic_info - PMIC information
+ *
+ * @slave_addr:		7 bit address representing I2C slave address.
+ * @voltage_reg_addr:	I2C register address for setting voltage
+ * @cmd_reg_addr:	I2C register address for low power transition commands
+ * @i2c_timeout_us:	worst case latency for I2C operations for the device
+ * @slew_rate_uV:	Slew rate in uV/uSeconds for voltage transitions
+ * @step_size_uV:	Step size in uV for one vsel increment.
+ * @min_uV:		represents the minimum step_sized incremental voltage
+ * @max_uV:		represents the maximum step_sized incremental voltage
+ * @voltage_selector_setbits:  what bits to set permenantly for the PMIC
+ *			voltage selector - this may have PMIC specific meaning.
+ * @voltage_selector_mask: what mask to use for the vsel value - this is useful
+ *			for PMICs where the vsel has to be applied at an offset.
+ * @voltage_selector_offset: what offset to apply to conversion routine when
+ *			operating on vsel.
+ * @voltage_selector_zero: Special case handling if 0 value does NOT indicates
+ *			power-off for PMIC.
+ * @setup_command_list:	array of setup commands for PMIC to operate
+ * @setup_num_commands:	number of setup commands for PMIC to operate
+ */
+struct omap_pmic_info {
+	u8 slave_addr;
+	u8 voltage_reg_addr;
+	u8 cmd_reg_addr;
+	u32 i2c_timeout_us;
+
+	u32 slew_rate_uV;
+	u32 step_size_uV;
+	u32 min_uV;
+	u32 max_uV;
+
+	u8 voltage_selector_offset;
+	u8 voltage_selector_mask;
+	u8 voltage_selector_setbits;
+	bool voltage_selector_zero;
+
+	const struct omap_pmic_setup_commands *setup_command_list;
+	u8 setup_num_commands;
+};
+
+/**
+ * struct omap_pmic - represents the OMAP PMIC device.
+ * @ops:		PMIC conversion operations.
+ *
+ * NOTE: the fields marked Private are meant for PMIC driver.
+ */
+struct omap_pmic {
+	const struct omap_pmic_info *info;
+	u32 boot_voltage_uV;
+
+	struct omap_pmic_ops *ops;
+
+	struct device *dev;
+	struct device *v_dev;
+};
+
+#if IS_ENABLED(CONFIG_REGULATOR_TI_OMAP_PMIC)
+int omap_pmic_register_controller_ops(struct omap_pmic_controller_ops *cops);
+#else
+static inline int omap_pmic_register_controller_ops(struct
+						    omap_pmic_controller_ops
+						    *cops)
+{
+	return -EINVAL;
+}
+#endif
+
+#endif				/* __POWER_OMAP_PMIC_H */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 2/8] PM / AVS: Introduce support for OMAP Voltage Controller(VC) with device tree nodes
  2013-06-21 21:25 ` Nishanth Menon
  (?)
@ 2013-06-21 21:25   ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon, Grygorii Strashko,
	Taras Kondratiuk

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced an TI
custom hardware block to better facilitate and standardize integration
of Power Management ICs which communicate over I2C called Voltage
Controller(VC).

This is an specialized hardware block meant to support PMIC chips and
is customized to that requirement. Even though it functions as an I2C
controller, it is a write-only interface whose configurations are custom
to PMICs.

We have an existing implementation in mach-omap2 which has been
re factored and moved out as a separate driver. This new driver is DT
only and the separation was meant to get a maintainable driver which
does not have to deal with legacy platform_data dependencies. The legacy
driver is retained to support non-DT boot and functionality will be
migrated to the DT-only version as we enable features.

Currently, this implementation considers only the basic steps needed for
voltage scaling and exposing voltage controller as a device whose
child devices are voltage controller channel devices.

We may need to do additional timing configurations to enable Low power
mode voltage transitions, but this will be completed in a following
series. We may need a few tweaks to hook the Adaptive Voltage
Scaling(AVS) implementation for OMAP which also uses the same voltage
controller to talk to PMICs.

This driver is meant to interface with voltage processor when the
voltage processor attempts devm_omap_vc_channel_get for the
VC channel corresponding to the voltage processor.

[grygorii.strashko@ti.com, taras@ti.com: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Taras Kondratiuk <taras@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
---
 .../devicetree/bindings/power/omap-vc.txt          |   99 ++
 drivers/power/avs/Kconfig                          |   15 +
 drivers/power/avs/Makefile                         |   20 +
 drivers/power/avs/omap_vc.c                        | 1513 ++++++++++++++++++++
 drivers/power/avs/omap_vc.h                        |   67 +
 5 files changed, 1714 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vc.txt
 create mode 100644 drivers/power/avs/omap_vc.c
 create mode 100644 drivers/power/avs/omap_vc.h

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 */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 2/8] PM / AVS: Introduce support for OMAP Voltage Controller(VC) with device tree nodes
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon, Grygorii Strashko,
	Taras Kondratiuk

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced an TI
custom hardware block to better facilitate and standardize integration
of Power Management ICs which communicate over I2C called Voltage
Controller(VC).

This is an specialized hardware block meant to support PMIC chips and
is customized to that requirement. Even though it functions as an I2C
controller, it is a write-only interface whose configurations are custom
to PMICs.

We have an existing implementation in mach-omap2 which has been
re factored and moved out as a separate driver. This new driver is DT
only and the separation was meant to get a maintainable driver which
does not have to deal with legacy platform_data dependencies. The legacy
driver is retained to support non-DT boot and functionality will be
migrated to the DT-only version as we enable features.

Currently, this implementation considers only the basic steps needed for
voltage scaling and exposing voltage controller as a device whose
child devices are voltage controller channel devices.

We may need to do additional timing configurations to enable Low power
mode voltage transitions, but this will be completed in a following
series. We may need a few tweaks to hook the Adaptive Voltage
Scaling(AVS) implementation for OMAP which also uses the same voltage
controller to talk to PMICs.

This driver is meant to interface with voltage processor when the
voltage processor attempts devm_omap_vc_channel_get for the
VC channel corresponding to the voltage processor.

[grygorii.strashko@ti.com, taras@ti.com: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Taras Kondratiuk <taras@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
---
 .../devicetree/bindings/power/omap-vc.txt          |   99 ++
 drivers/power/avs/Kconfig                          |   15 +
 drivers/power/avs/Makefile                         |   20 +
 drivers/power/avs/omap_vc.c                        | 1513 ++++++++++++++++++++
 drivers/power/avs/omap_vc.h                        |   67 +
 5 files changed, 1714 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vc.txt
 create mode 100644 drivers/power/avs/omap_vc.c
 create mode 100644 drivers/power/avs/omap_vc.h

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 */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 2/8] PM / AVS: Introduce support for OMAP Voltage Controller(VC) with device tree nodes
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced an TI
custom hardware block to better facilitate and standardize integration
of Power Management ICs which communicate over I2C called Voltage
Controller(VC).

This is an specialized hardware block meant to support PMIC chips and
is customized to that requirement. Even though it functions as an I2C
controller, it is a write-only interface whose configurations are custom
to PMICs.

We have an existing implementation in mach-omap2 which has been
re factored and moved out as a separate driver. This new driver is DT
only and the separation was meant to get a maintainable driver which
does not have to deal with legacy platform_data dependencies. The legacy
driver is retained to support non-DT boot and functionality will be
migrated to the DT-only version as we enable features.

Currently, this implementation considers only the basic steps needed for
voltage scaling and exposing voltage controller as a device whose
child devices are voltage controller channel devices.

We may need to do additional timing configurations to enable Low power
mode voltage transitions, but this will be completed in a following
series. We may need a few tweaks to hook the Adaptive Voltage
Scaling(AVS) implementation for OMAP which also uses the same voltage
controller to talk to PMICs.

This driver is meant to interface with voltage processor when the
voltage processor attempts devm_omap_vc_channel_get for the
VC channel corresponding to the voltage processor.

[grygorii.strashko at ti.com, taras at ti.com: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Taras Kondratiuk <taras@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
---
 .../devicetree/bindings/power/omap-vc.txt          |   99 ++
 drivers/power/avs/Kconfig                          |   15 +
 drivers/power/avs/Makefile                         |   20 +
 drivers/power/avs/omap_vc.c                        | 1513 ++++++++++++++++++++
 drivers/power/avs/omap_vc.h                        |   67 +
 5 files changed, 1714 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vc.txt
 create mode 100644 drivers/power/avs/omap_vc.c
 create mode 100644 drivers/power/avs/omap_vc.h

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 at 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@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 */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 3/8] PM / AVS: Introduce support for OMAP Voltage Processor(VP) with device tree nodes
  2013-06-21 21:25 ` Nishanth Menon
  (?)
@ 2013-06-21 21:25   ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon, Grygorii Strashko,
	Taras Kondratiuk

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced an TI
custom hardware block to better facilitate and standardize integration
of Power Management ICs which communicate over I2C called Voltage
Processor(VP).

This is an specialized hardware block meant to support PMIC chips only
over Voltage Controller(VC) interface. This provides an interface for
SmartReflex AVS module to send adjustment steps which is converted into
voltage values and send onwards by VP to VC. VP is also used to set the
voltage of the PMIC by commanding using "forceupdate".

We have an existing implementation in mach-omap2 which has been
re factored and moved out as a separate driver. This new driver is DT
only and the separation was meant to get a maintainable driver which
does not have to deal with legacy platform_data dependencies. The legacy
driver is retained to support non-DT boot and functionality will be
migrated to the DT-only version as we enable features.

Currently, this implementation considers only the basic steps needed for
voltage scaling and exposing voltage processor which hooks on to Voltage
Controller driver and OMAP PMIC driver to provide an regulator interface
over OMAP PMIC driver.

We may need to do additional timing configurations to enable Low power
mode voltage transitions and to hook the Adaptive Voltage
Scaling(AVS) implementation for OMAP which also uses the same voltage
controller to talk to PMICs. This needs to be addressed in a later
series.

This driver is meant to interface with OMAP PMIC driver when the
controller driver registers it's operations with OMAP PMIC driver and
associates with an voltage controller channel. This enables the full
communication path between the OMAP PMIC regulator to the external PMIC
hardware over OMAP Voltage Processor and OMAP Voltage Controller.

[grygorii.strashko@ti.com, taras@ti.com: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Taras Kondratiuk <taras@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
---
 .../devicetree/bindings/power/omap-vp.txt          |   39 +
 drivers/power/avs/Makefile                         |    2 +-
 drivers/power/avs/omap_vp.c                        |  892 ++++++++++++++++++++
 3 files changed, 932 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vp.txt
 create mode 100644 drivers/power/avs/omap_vp.c

diff --git a/Documentation/devicetree/bindings/power/omap-vp.txt b/Documentation/devicetree/bindings/power/omap-vp.txt
new file mode 100644
index 0000000..b690e35
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/omap-vp.txt
@@ -0,0 +1,39 @@
+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-vp" - for OMAP3 family of devices
+  - "ti,omap4-vp" - for OMAP4 family of devices
+  - "ti,omap5-vp" - 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 VP module
+  - "int-address"	- contains base address of interrupt register for VP module
+- clocks: should point to the clock node used by VC module, usually sysclk
+- ti,min-micro-volts - SoC supported min operational voltage in micro-volts
+- ti,max-micro-volts - SoC supported max operational voltage in micro-volts
+- ti,min-step-micro-volts - SoC supported min operational voltage steps in micro-volts
+- ti,max-step-micro-volts - SoC supported max operational voltage steps in micro-volts
+- ti,tranxdone-status-mask: Mask to the int-register to write-to-clear mask
+	indicating VP has completed operation in sending command to Voltage Controller.
+- ti,vc-channel - phandle to the Voltage Controller Channel device used by this Voltage
+	Processor.
+Example:
+vp_mpu: vp@0x4a307b58 {
+	compatible = "ti,omap4-vp";
+
+	reg = <0x4a307b58 0x18>, <0x4A306014 0x4>;
+	reg-names = "base-address", "int-address";
+	ti,tranxdone-status-mask=<0x20>;
+
+	clocks = <&sysclk_in>;
+
+	ti,vc-channel = <&vc_mpu>;
+	ti,min-step-micro-volts = <10000>;
+	ti,max-step-micro-volts = <50000>;
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1410000>;
+};
diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile
index 95d5f59..535cab5 100644
--- a/drivers/power/avs/Makefile
+++ b/drivers/power/avs/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_POWER_AVS_OMAP)		+= smartreflex.o
 ifneq ($(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL),)
 
 # OMAP Common
-omap-volt-common			=  omap_vc.o
+omap-volt-common			=  omap_vc.o omap_vp.o
 
 # OMAP SoC specific
 ifneq ($(CONFIG_ARCH_OMAP3),)
diff --git a/drivers/power/avs/omap_vp.c b/drivers/power/avs/omap_vp.c
new file mode 100644
index 0000000..8ce42e1
--- /dev/null
+++ b/drivers/power/avs/omap_vp.c
@@ -0,0 +1,892 @@
+/*
+ * OMAP Voltage Processor (VP) interface
+ *
+ * Idea based on arch/arm/mach-omap2/vp.c
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/omap-pmic-regulator.h>
+#include <linux/regmap.h>
+#include "omap_vc.h"
+
+#define DRIVER_NAME	"omap-vp"
+
+/**
+ * omap_vp_test_timeout - busy-loop, testing a condition
+ * @cond: condition to test until it evaluates to true
+ * @timeout: maximum number of microseconds in the timeout
+ * @index: loop index (integer)
+ *
+ * Loop waiting for @cond to become true or until at least @timeout
+ * microseconds have passed.  To use, define some integer @index in the
+ * calling code.  After running, if @index == @timeout, then the loop has
+ * timed out.
+ */
+#define omap_vp_test_timeout(cond, timeout, index)		\
+({								\
+	for (index = 0; index < timeout; index++) {		\
+		if (cond)					\
+			break;					\
+		udelay(1);					\
+	}							\
+})
+
+/**
+ * struct omap_vp_reg_data - Voltage processor register offsets
+ * @config:	CONFIG register
+ * @status:	STATUS register
+ * @vlimitto:	VLIMITTO register
+ * @voltage:	VOLTAGE register
+ * @step_max:	STEP_MAX register
+ * @step_min:	STEP_MAX register
+ */
+struct omap_vp_reg_data {
+	u8 config;
+	u8 status;
+	u8 vlimitto;
+	u8 voltage;
+	u8 step_max;
+	u8 step_min;
+};
+
+static const struct omap_vp_reg_data omap_vp_reg_type1 = {
+	.config = 0x00,
+	.status = 0x14,
+	.vlimitto = 0x0C,
+	.voltage = 0x10,
+	.step_max = 0x08,
+	.step_min = 0x04,
+};
+
+static const struct omap_vp_reg_data omap_vp_reg_type2 = {
+	.config = 0x00,
+	.status = 0x04,
+	.vlimitto = 0x08,
+	.voltage = 0x0C,
+	.step_max = 0x10,
+	.step_min = 0x14,
+};
+
+/* Config register masks - All revisions */
+#define CONFIG_ERROR_OFFSET_MASK	(0xff << 24)
+#define CONFIG_ERROR_GAIN_MASK		(0xff << 16)
+#define CONFIG_INIT_VOLTAGE_MASK	(0xff << 8)
+#define CONFIG_TIMEOUT_ENABLE_MASK	(0x01 << 3)
+#define CONFIG_INITVDD_MASK		(0x01 << 2)
+#define CONFIG_FORCEUPDATE_MASK		(0x01 << 1)
+#define CONFIG_VP_ENABLE_MASK		(0x01 << 0)
+
+/* Status register masks - All revisions */
+#define STATUS_VP_IN_IDLE_MASK		(0x01 << 0)
+
+/* Vlmitto register masks - All revisions */
+#define VLIMITTO_VDDMAX_MASK		(0xff << 24)
+#define VLIMITTO_VDDMIN_MASK		(0xff << 16)
+#define VLIMITTO_TIMEOUT_MASK		(0xffff << 0)
+
+/* Voltage register masks - All revisions */
+#define VOLTAGE_MASK			(0xff << 0)
+
+/* Step max/min register masks - All revisions */
+#define STEP_SMPSTIMEOUT_MASK		(0xffff << 8)
+#define STEP_VSTEP_MASK			(0xff << 0)
+
+/* 32 bit voltage processor registers */
+static struct regmap_config omap_vp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+/**
+ * struct omap_vp - Structure representing Voltage Processor info
+ * @dev:		device pointer for Voltage Processor
+ * @list:		list head for VP list.
+ * @usage_count:	Usage count - only 1 user at a time.(not always module)
+ * @clk_rate:		Sysclk rate for VP computation.
+ * @vc:			Voltage controller channel corresponding to VP
+ * @pmic:		PMIC used for this path
+ * @regmap:		regmap for VP instance
+ * @regs:		register map
+ * @int_base:		interrupt register base address
+ * @txdone_mask:	TRANXDONE interrupt mask for this VP instance in intreg
+ * @min_uV:		minimum voltage allowed by VP in micro-volts
+ * @max_uV:		maximum voltage allowed by VP in micro-volts
+ * @min_step_uV:	minimum continous voltage step in micro-volts for VP
+ * @max_step_uV:	maximum continous voltage step in micro-volts for VP
+ */
+struct omap_vp {
+	struct device *dev;
+	struct list_head list;
+	int usage_count;
+
+	unsigned long clk_rate;
+	struct omap_vc_channel_info *vc;
+	struct omap_pmic *pmic;
+
+	struct regmap *regmap;
+	const struct omap_vp_reg_data *regs;
+
+	void __iomem *int_base;
+	u32 txdone_mask;
+
+	u32 min_uV;
+	u32 max_uV;
+	u32 min_step_uV;
+	u32 max_step_uV;
+};
+
+static const struct of_device_id omap_vp_of_match[] = {
+	{.compatible = "ti,omap3-vp", .data = &omap_vp_reg_type1},
+	{.compatible = "ti,omap4-vp", .data = &omap_vp_reg_type2},
+	{.compatible = "ti,omap5-vp", .data = &omap_vp_reg_type2},
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_vp_of_match);
+
+static LIST_HEAD(omap_vp_list);
+static DEFINE_MUTEX(omap_vp_list_mutex);
+
+/**
+ * omap_vp_check_txdone() - inline helper to see if TRANXDONE is set
+ * @vp:	pointer to voltage processor
+ */
+static inline bool omap_vp_check_txdone(const struct omap_vp *vp)
+{
+	return !!(readl(vp->int_base) & vp->txdone_mask);
+}
+
+/**
+ * omap_vp_clear_txdone() - inline helper to clear TRANXDONE
+ * @vp:	pointer to voltage processor
+ *
+ * write of 1 bit clears that interrupt bit only.
+ */
+static inline void omap_vp_clear_txdone(const struct omap_vp *vp)
+{
+	writel(vp->txdone_mask, vp->int_base);
+};
+
+/**
+ * omap_vp_read_idle() - inline helper to read idle register
+ * @regmap:	regmap for voltage processor
+ * @regs:	registers for voltage processor
+ */
+static inline u32 omap_vp_read_idle(struct regmap *regmap,
+				    const struct omap_vp_reg_data *regs)
+{
+	u32 val = 0;
+	regmap_read(regmap, regs->status, &val);
+	return val;
+}
+
+/**
+ * omap_vp_wait_for_idle() - Wait for Voltage processor to idle
+ * @vp:	pointer to voltage processor
+ *
+ * helper to ensure that VP is idle (no pending AVS / previous VP operations)
+ */
+static inline int omap_vp_wait_for_idle(struct omap_vp *vp)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int timeout;
+
+	omap_vp_test_timeout((omap_vp_read_idle(regmap, regs) &
+			      STATUS_VP_IN_IDLE_MASK), pinfo->i2c_timeout_us,
+			     timeout);
+
+	if (timeout >= pinfo->i2c_timeout_us) {
+		dev_warn_ratelimited(dev, "%s: idle timedout(%d)\n",
+				     __func__, pinfo->i2c_timeout_us);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+/**
+ * omap_vp_set_init_voltage() - Setup voltage for transmission.
+ * @vp:	pointer to voltage processor
+ * @volt: voltage to setup the voltage processor with
+ */
+static int omap_vp_set_init_voltage(struct omap_vp *vp, u32 volt)
+{
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	struct omap_pmic_ops *ops = pmic->ops;
+	char vsel;
+	int ret;
+
+	ret = ops->uv_to_vsel(pmic, volt, &vsel);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(regmap, regs->config,
+				 (CONFIG_INIT_VOLTAGE_MASK |
+				  CONFIG_FORCEUPDATE_MASK |
+				  CONFIG_INITVDD_MASK),
+				 vsel << __ffs(CONFIG_INIT_VOLTAGE_MASK));
+	if (ret)
+		return ret;
+
+	/* Trigger initVDD value copy to voltage processor */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_INITVDD_MASK, CONFIG_INITVDD_MASK);
+	if (ret)
+		return ret;
+
+	/* Clear initVDD copy trigger bit */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_INITVDD_MASK, 0x0);
+
+	return ret;
+}
+
+/**
+ * omap_vp_get_current_voltage() - get the current voltage processor voltage
+ * @vp:	pointer to voltage processor
+ * @uv:	returns with voltage in micro-volts if read was successful.
+ */
+static int omap_vp_get_current_voltage(struct omap_vp *vp, u32 *uv)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	struct omap_pmic_ops *ops = pmic->ops;
+	u32 val;
+	u8 vsel;
+	int ret;
+
+	ret = regmap_read(regmap, regs->config, &val);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: unable to read config reg (%d)\n",
+				     __func__, ret);
+		return ret;
+	}
+
+	val &= CONFIG_INIT_VOLTAGE_MASK;
+	vsel = val >> __ffs(CONFIG_INIT_VOLTAGE_MASK);
+	ret = ops->vsel_to_uv(pmic, vsel, &val);
+
+	if (!ret)
+		*uv = val;
+	return ret;
+}
+
+/**
+ * omap_vp_forceupdate_scale() - Update voltage on PMIC using VP "Forceupdate"
+ * @vp:			pointer to voltage processor
+ * @target_volt:	voltage to set the PMIC to
+ *
+ * This will wait for the slew duration to ensure that the voltage is sync-ed
+ * on the PMIC.
+ */
+static int omap_vp_forceupdate_scale(struct omap_vp *vp, u32 target_volt)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int ret, timeout = 0, max_timeout;
+	u32 old_volt = 0;
+	u32 smps_transition_uv, smps_delay;
+
+	ret = omap_vp_wait_for_idle(vp);
+	if (ret)
+		return ret;
+
+	ret = omap_vp_get_current_voltage(vp, &old_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Unable to convert old voltage(%d)\n",
+				     __func__, ret);
+		/* We will use worst case start voltage - 0V for delay */
+	}
+
+	/*
+	 * Clear all pending TransactionDone interrupt/status. Typical latency
+	 * is <3us - use an conservative value from pmic info.
+	 */
+	max_timeout = 2 * pinfo->i2c_timeout_us;
+	while (timeout++ < max_timeout) {
+		omap_vp_clear_txdone(vp);
+		if (!omap_vp_check_txdone(vp))
+			break;
+		udelay(1);
+	}
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not clear(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	ret = omap_vp_set_init_voltage(vp, target_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Fail set init voltage at v=%d(%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Force update of voltage */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_FORCEUPDATE_MASK,
+				 CONFIG_FORCEUPDATE_MASK);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Forceupdate not set v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/*
+	 * Wait for TransactionDone. Typical latency is <200us.
+	 * Depends on SMPSWAITTIMEMIN/MAX and voltage change
+	 */
+	timeout = 0;
+	omap_vp_test_timeout(omap_vp_check_txdone(vp), max_timeout, timeout);
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not set(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * Due to the inability of OMAP Voltage controller OR voltage processor
+	 * to precisely know when the voltage has achieved the requested value,
+	 * we need a delay loop to ensure that the voltage has transitioned to
+	 * the required level.
+	 */
+	smps_transition_uv = abs(target_volt - old_volt);
+
+	/* delta_voltage / slew_rate, 2uS added as buffer */
+	smps_delay = DIV_ROUND_UP(smps_transition_uv, pinfo->slew_rate_uV) + 2;
+
+	/* We dont want to sleep for too long either */
+	usleep_range(smps_delay, smps_delay + 2);
+
+	/*
+	 * Disable TransactionDone interrupt , clear all status, clear
+	 * control registers
+	 */
+	timeout = 0;
+	while (timeout++ < max_timeout) {
+		omap_vp_clear_txdone(vp);
+		if (!omap_vp_check_txdone(vp))
+			break;
+		udelay(1);
+	}
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not recleared(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	/* Clear force bit */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_FORCEUPDATE_MASK, 0x0);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Forceupdate not cleared v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Do the required updates */
+	ret = omap_vc_channel_set_on_voltage(vp->vc, target_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Fail update VC onV at v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Now, Wait for VP to idle down */
+	ret = omap_vp_wait_for_idle(vp);
+
+	return ret;
+}
+
+/**
+ * omap_vp_setup() - Setup voltage processor
+ * @vp:	pointer to voltage processor
+ */
+static int omap_vp_setup(struct omap_vp *vp)
+{
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	struct omap_pmic_ops *ops = pmic->ops;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	u32 val, clk_rate, timeout, waittime;
+	u8 vstepmin, vstepmax;
+	u8 vddmin, vddmax;
+	int ret;
+
+	/* Div 1000 to avoid overflow */
+	clk_rate = vp->clk_rate / 1000;
+
+	ret = ops->uv_to_vsel(pmic, vp->min_uV, &vddmin);
+	if (ret)
+		return ret;
+	ret = ops->uv_to_vsel(pmic, vp->max_uV, &vddmax);
+	if (ret)
+		return ret;
+
+	timeout = DIV_ROUND_UP_ULL(clk_rate * pinfo->i2c_timeout_us, 1000);
+	waittime = DIV_ROUND_UP_ULL(pinfo->step_size_uV * clk_rate,
+				    1000 * pinfo->slew_rate_uV);
+
+	vstepmin = DIV_ROUND_UP(vp->min_step_uV, pinfo->step_size_uV);
+	vstepmax = DIV_ROUND_UP(vp->max_step_uV, pinfo->step_size_uV);
+
+	/* VSTEPMIN */
+	val =
+	    (waittime << __ffs(STEP_SMPSTIMEOUT_MASK)) & STEP_SMPSTIMEOUT_MASK;
+	val |= (vstepmin << __ffs(STEP_VSTEP_MASK)) & STEP_VSTEP_MASK;
+	ret = regmap_write(regmap, regs->step_min, val);
+	if (ret)
+		return ret;
+
+	/* VSTEPMIN */
+	val =
+	    (waittime << __ffs(STEP_SMPSTIMEOUT_MASK)) & STEP_SMPSTIMEOUT_MASK;
+	val |= (vstepmax << __ffs(STEP_VSTEP_MASK)) & STEP_VSTEP_MASK;
+	ret = regmap_write(regmap, regs->step_max, val);
+	if (ret)
+		return ret;
+
+	/* VLIMITTO */
+	val = (vddmax << __ffs(VLIMITTO_VDDMAX_MASK)) & VLIMITTO_VDDMAX_MASK;
+	val |= (vddmin << __ffs(VLIMITTO_VDDMIN_MASK)) & VLIMITTO_VDDMIN_MASK;
+	val |=
+	    (timeout << __ffs(VLIMITTO_TIMEOUT_MASK)) & VLIMITTO_TIMEOUT_MASK;
+	ret = regmap_write(regmap, regs->vlimitto, val);
+	if (ret)
+		return ret;
+
+	/* CONFIG */
+	ret =
+	    regmap_update_bits(regmap, regs->config, CONFIG_TIMEOUT_ENABLE_MASK,
+			       CONFIG_TIMEOUT_ENABLE_MASK);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+/**
+ * devm_omap_vp_release() -  helper to keep track of free usage.
+ * @dev:	device
+ * @res:	resource
+ */
+static void devm_omap_vp_release(struct device *dev, void *res)
+{
+	struct omap_vp *vp = *((struct omap_vp **)res);
+
+	mutex_lock(&omap_vp_list_mutex);
+
+	if (!vp->usage_count) {
+		vp->pmic = NULL;
+		vp->vc = NULL;
+	}
+	module_put(vp->dev->driver->owner);
+
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return;
+}
+
+/**
+ * of_get_omap_vp() - get the pmic node
+ * @dev:	device to pull information from
+ */
+static struct device_node *of_get_omap_vp(struct device *dev)
+{
+	struct device_node *pmic_node = NULL;
+	char *prop_name = "ti,vp";
+
+	dev_dbg(dev, "%s: Looking up %s from device tree\n", __func__,
+		prop_name);
+
+	pmic_node = of_parse_phandle(dev->of_node, prop_name, 0);
+
+	if (!pmic_node) {
+		dev_err(dev, "%s: Looking up %s property in node %s failed",
+			__func__, prop_name, dev->of_node->full_name);
+		return ERR_PTR(-ENODEV);
+	}
+	return pmic_node;
+}
+
+/**
+ * devm_omap_vp_get() - managed request to get a VP device
+ * @dev:	Generic device to handle the request for
+ * @pmic:	PMIC resource this will be assigned to
+ *
+ * Ensures that vp 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
+ */
+static struct device *devm_omap_vp_get(struct device *dev,
+				       struct omap_pmic *pmic)
+{
+	const struct omap_pmic_info *pinfo;
+	struct omap_vp *vp, **ptr;
+	struct device_node *node;
+	struct device *vp_dev;
+	struct omap_vc_channel_info *vc;
+	u32 min_uV, max_uV;
+	int ret = 0;
+
+	if (!dev || !dev->of_node) {
+		pr_err("%s: invalid parameters\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	node = of_get_omap_vp(dev);
+	if (IS_ERR(node))
+		return (void *)node;
+
+	mutex_lock(&omap_vp_list_mutex);
+	list_for_each_entry(vp, &omap_vp_list, list)
+	    if (vp->dev->of_node == node)
+		goto found;
+
+	/* Node definition is present, but not probed yet.. request defer */
+	vp_dev = ERR_PTR(-EPROBE_DEFER);
+	goto out_unlock;
+
+found:
+	vp_dev = vp->dev;
+	if (!try_module_get(vp_dev->driver->owner)) {
+		dev_err(dev, "%s: Cant get device owner\n", __func__);
+		vp_dev = ERR_PTR(-EINVAL);
+		goto out_unlock;
+	}
+
+	/* Allow ONLY 1 user at a time */
+	if (vp->usage_count) {
+		dev_err(dev, "%s: device %s is busy..\n", __func__,
+			dev_name(vp_dev));
+		ret = -EBUSY;
+		goto out;
+	}
+	vc = devm_omap_vc_channel_get(vp_dev, pmic);
+	if (IS_ERR(vc)) {
+		ret = PTR_ERR(vc);
+		dev_err(dev, "%s: vc channel not ready(%d) in %s?\n",
+			__func__, ret, dev_name(vp_dev));
+		goto out;
+	}
+	vp->vc = vc;
+	vp->pmic = pmic;
+	pinfo = pmic->info;
+
+	/* Adjust our voltages */
+	/* Cant go below PMIC min voltage */
+	min_uV = max(vp->min_uV, pinfo->min_uV);
+	/* Cant go below SoC retention voltage for operational case */
+	min_uV = max(min_uV, vc->retention_uV);
+	vp->min_uV = min_uV;
+
+	/* Cant go above PMIC max voltage */
+	max_uV = min(vp->max_uV, pinfo->max_uV);
+	vp->max_uV = max_uV;
+
+	ret = omap_vp_setup(vp);
+	if (ret) {
+		dev_err(dev, "%s: Failed to setup vp (%d) dev %s\n",
+			__func__, ret, dev_name(vp_dev));
+		goto out;
+	}
+
+	if (pmic->boot_voltage_uV) {
+		ret = omap_vp_forceupdate_scale(vp, pmic->boot_voltage_uV);
+		if (ret) {
+			dev_err(dev, "%s: Failed to set boot voltage %d(%d)\n",
+				__func__, pmic->boot_voltage_uV, ret);
+			goto out;
+		}
+	}
+
+	ptr = devres_alloc(devm_omap_vp_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	*ptr = vp;
+	vp->usage_count++;
+	devres_add(dev, ptr);
+
+out:
+	if (ret) {
+		module_put(vp_dev->driver->owner);
+		vp_dev = ERR_PTR(ret);
+	}
+out_unlock:
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return vp_dev;
+}
+
+/**
+ * omap_vp_voltage_set() - controller operation to set voltage
+ * @dev:	VP device to set voltage
+ * @uv:		voltage in micro-volts to set
+ */
+static int omap_vp_voltage_set(struct device *dev, u32 uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	return omap_vp_forceupdate_scale(vp, uv);
+}
+
+/**
+ * omap_vp_voltage_get() - controller operation to get voltage
+ * @dev:	VP device to get voltage from
+ * @uv:		returns voltage in micro-volts if successful
+ */
+static int omap_vp_voltage_get(struct device *dev, u32 *uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp || !uv)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	return omap_vp_get_current_voltage(vp, uv);
+}
+
+/**
+ * omap_vp_voltage_get_range() - controller function to return VP voltage range
+ * @dev:	VP device to query
+ * @min_uv:	if successful, returns min voltage supported by VP
+ * @max_uv:	if successful, returns max voltage supported by VP
+ */
+static int omap_vp_voltage_get_range(struct device *dev, u32 *min_uv,
+				     u32 *max_uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp || !min_uv || !max_uv)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	*min_uv = vp->min_uV;
+	*max_uv = vp->max_uV;
+	return 0;
+}
+
+static struct omap_pmic_controller_ops voltage_processor_ops = {
+	.devm_pmic_register = devm_omap_vp_get,
+	.voltage_set = omap_vp_voltage_set,
+	.voltage_get = omap_vp_voltage_get,
+	.voltage_get_range = omap_vp_voltage_get_range,
+};
+static bool voltage_processor_ops_registered;
+
+static int omap_vp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+	struct omap_vp *vp;
+	struct resource *res;
+	struct regmap *regmap;
+	char *pname;
+	struct clk *clk;
+	int ret = 0;
+	void __iomem *base, *int_base;
+
+	if (!node) {
+		dev_err(dev, "%s: missing device tree nodes?\n", __func__);
+		return -EINVAL;
+	}
+
+	match = of_match_device(omap_vp_of_match, 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;
+	}
+
+	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;
+	}
+	pname = "int-address";
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+	if (!res) {
+		dev_err(dev, "Missing '%s' IO resource\n", pname);
+		return -ENODEV;
+	}
+
+	/*
+	 * We may have shared interrupt register offsets which are
+	 * write-1-to-clear between domains ensuring exclusivity.
+	 */
+	int_base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (!int_base) {
+		dev_err(dev, "Unable to map '%s'\n", pname);
+		return -ENOMEM;
+	}
+
+	regmap = devm_regmap_init_mmio(dev, base, &omap_vp_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(dev, "regmap init failed(%d)\n", ret);
+		return ret;
+	}
+
+	vp = devm_kzalloc(dev, sizeof(*vp), GFP_KERNEL);
+	if (!vp) {
+		dev_err(dev, "%s: Unable to allocate VP\n", __func__);
+		return -ENOMEM;
+	}
+	vp->dev = dev;
+	vp->regs = match->data;
+	vp->regmap = regmap;
+	vp->int_base = int_base;
+
+	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;
+	}
+	vp->clk_rate = clk_get_rate(clk);
+	/* We dont need the clk any more */
+	clk_put(clk);
+
+	pname = "ti,min-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->min_uV);
+	if (ret)
+		goto invalid_of_property;
+
+	pname = "ti,max-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->max_uV);
+	if (ret || !vp->max_uV)
+		goto invalid_of_property;
+
+	pname = "ti,min-step-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->min_step_uV);
+	if (ret || !vp->min_step_uV)
+		goto invalid_of_property;
+
+	pname = "ti,max-step-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->max_step_uV);
+	if (ret || !vp->max_step_uV)
+		goto invalid_of_property;
+
+	pname = "ti,tranxdone-status-mask";
+	ret = of_property_read_u32(node, pname, &vp->txdone_mask);
+	if (ret || !vp->txdone_mask)
+		goto invalid_of_property;
+
+	platform_set_drvdata(pdev, vp);
+
+	mutex_lock(&omap_vp_list_mutex);
+	if (!voltage_processor_ops_registered) {
+		ret = omap_pmic_register_controller_ops(&voltage_processor_ops);
+		if (ret)
+			dev_err(dev, "Failed register pmic cops (%d)\n", ret);
+		else
+			voltage_processor_ops_registered = true;
+	}
+	if (!ret)
+		list_add(&vp->list, &omap_vp_list);
+
+	mutex_unlock(&omap_vp_list_mutex);
+	return ret;
+
+invalid_of_property:
+	if (!ret) {
+		dev_err(dev, "%s: Invalid value 0x0 in '%s' property.\n",
+			__func__, pname);
+		ret = -EINVAL;
+	} else {
+		dev_err(dev, "%s: Missing/Invalid '%s' property - error(%d)\n",
+			__func__, pname, ret);
+	}
+	return ret;
+}
+
+static int omap_vp_remove(struct platform_device *pdev)
+{
+	struct omap_vp *vp = platform_get_drvdata(pdev);
+
+	mutex_lock(&omap_vp_list_mutex);
+	list_del(&vp->list);
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return 0;
+}
+
+static struct platform_driver omap_vp_driver = {
+	.probe = omap_vp_probe,
+	.remove = omap_vp_remove,
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(omap_vp_of_match),
+		   },
+};
+module_platform_driver(omap_vp_driver);
+
+MODULE_DESCRIPTION("OMAP Voltage Processor Regulator Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc.");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 3/8] PM / AVS: Introduce support for OMAP Voltage Processor(VP) with device tree nodes
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon, Grygorii Strashko,
	Taras Kondratiuk

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced an TI
custom hardware block to better facilitate and standardize integration
of Power Management ICs which communicate over I2C called Voltage
Processor(VP).

This is an specialized hardware block meant to support PMIC chips only
over Voltage Controller(VC) interface. This provides an interface for
SmartReflex AVS module to send adjustment steps which is converted into
voltage values and send onwards by VP to VC. VP is also used to set the
voltage of the PMIC by commanding using "forceupdate".

We have an existing implementation in mach-omap2 which has been
re factored and moved out as a separate driver. This new driver is DT
only and the separation was meant to get a maintainable driver which
does not have to deal with legacy platform_data dependencies. The legacy
driver is retained to support non-DT boot and functionality will be
migrated to the DT-only version as we enable features.

Currently, this implementation considers only the basic steps needed for
voltage scaling and exposing voltage processor which hooks on to Voltage
Controller driver and OMAP PMIC driver to provide an regulator interface
over OMAP PMIC driver.

We may need to do additional timing configurations to enable Low power
mode voltage transitions and to hook the Adaptive Voltage
Scaling(AVS) implementation for OMAP which also uses the same voltage
controller to talk to PMICs. This needs to be addressed in a later
series.

This driver is meant to interface with OMAP PMIC driver when the
controller driver registers it's operations with OMAP PMIC driver and
associates with an voltage controller channel. This enables the full
communication path between the OMAP PMIC regulator to the external PMIC
hardware over OMAP Voltage Processor and OMAP Voltage Controller.

[grygorii.strashko@ti.com, taras@ti.com: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Taras Kondratiuk <taras@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
---
 .../devicetree/bindings/power/omap-vp.txt          |   39 +
 drivers/power/avs/Makefile                         |    2 +-
 drivers/power/avs/omap_vp.c                        |  892 ++++++++++++++++++++
 3 files changed, 932 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vp.txt
 create mode 100644 drivers/power/avs/omap_vp.c

diff --git a/Documentation/devicetree/bindings/power/omap-vp.txt b/Documentation/devicetree/bindings/power/omap-vp.txt
new file mode 100644
index 0000000..b690e35
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/omap-vp.txt
@@ -0,0 +1,39 @@
+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-vp" - for OMAP3 family of devices
+  - "ti,omap4-vp" - for OMAP4 family of devices
+  - "ti,omap5-vp" - 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 VP module
+  - "int-address"	- contains base address of interrupt register for VP module
+- clocks: should point to the clock node used by VC module, usually sysclk
+- ti,min-micro-volts - SoC supported min operational voltage in micro-volts
+- ti,max-micro-volts - SoC supported max operational voltage in micro-volts
+- ti,min-step-micro-volts - SoC supported min operational voltage steps in micro-volts
+- ti,max-step-micro-volts - SoC supported max operational voltage steps in micro-volts
+- ti,tranxdone-status-mask: Mask to the int-register to write-to-clear mask
+	indicating VP has completed operation in sending command to Voltage Controller.
+- ti,vc-channel - phandle to the Voltage Controller Channel device used by this Voltage
+	Processor.
+Example:
+vp_mpu: vp@0x4a307b58 {
+	compatible = "ti,omap4-vp";
+
+	reg = <0x4a307b58 0x18>, <0x4A306014 0x4>;
+	reg-names = "base-address", "int-address";
+	ti,tranxdone-status-mask=<0x20>;
+
+	clocks = <&sysclk_in>;
+
+	ti,vc-channel = <&vc_mpu>;
+	ti,min-step-micro-volts = <10000>;
+	ti,max-step-micro-volts = <50000>;
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1410000>;
+};
diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile
index 95d5f59..535cab5 100644
--- a/drivers/power/avs/Makefile
+++ b/drivers/power/avs/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_POWER_AVS_OMAP)		+= smartreflex.o
 ifneq ($(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL),)
 
 # OMAP Common
-omap-volt-common			=  omap_vc.o
+omap-volt-common			=  omap_vc.o omap_vp.o
 
 # OMAP SoC specific
 ifneq ($(CONFIG_ARCH_OMAP3),)
diff --git a/drivers/power/avs/omap_vp.c b/drivers/power/avs/omap_vp.c
new file mode 100644
index 0000000..8ce42e1
--- /dev/null
+++ b/drivers/power/avs/omap_vp.c
@@ -0,0 +1,892 @@
+/*
+ * OMAP Voltage Processor (VP) interface
+ *
+ * Idea based on arch/arm/mach-omap2/vp.c
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/omap-pmic-regulator.h>
+#include <linux/regmap.h>
+#include "omap_vc.h"
+
+#define DRIVER_NAME	"omap-vp"
+
+/**
+ * omap_vp_test_timeout - busy-loop, testing a condition
+ * @cond: condition to test until it evaluates to true
+ * @timeout: maximum number of microseconds in the timeout
+ * @index: loop index (integer)
+ *
+ * Loop waiting for @cond to become true or until at least @timeout
+ * microseconds have passed.  To use, define some integer @index in the
+ * calling code.  After running, if @index == @timeout, then the loop has
+ * timed out.
+ */
+#define omap_vp_test_timeout(cond, timeout, index)		\
+({								\
+	for (index = 0; index < timeout; index++) {		\
+		if (cond)					\
+			break;					\
+		udelay(1);					\
+	}							\
+})
+
+/**
+ * struct omap_vp_reg_data - Voltage processor register offsets
+ * @config:	CONFIG register
+ * @status:	STATUS register
+ * @vlimitto:	VLIMITTO register
+ * @voltage:	VOLTAGE register
+ * @step_max:	STEP_MAX register
+ * @step_min:	STEP_MAX register
+ */
+struct omap_vp_reg_data {
+	u8 config;
+	u8 status;
+	u8 vlimitto;
+	u8 voltage;
+	u8 step_max;
+	u8 step_min;
+};
+
+static const struct omap_vp_reg_data omap_vp_reg_type1 = {
+	.config = 0x00,
+	.status = 0x14,
+	.vlimitto = 0x0C,
+	.voltage = 0x10,
+	.step_max = 0x08,
+	.step_min = 0x04,
+};
+
+static const struct omap_vp_reg_data omap_vp_reg_type2 = {
+	.config = 0x00,
+	.status = 0x04,
+	.vlimitto = 0x08,
+	.voltage = 0x0C,
+	.step_max = 0x10,
+	.step_min = 0x14,
+};
+
+/* Config register masks - All revisions */
+#define CONFIG_ERROR_OFFSET_MASK	(0xff << 24)
+#define CONFIG_ERROR_GAIN_MASK		(0xff << 16)
+#define CONFIG_INIT_VOLTAGE_MASK	(0xff << 8)
+#define CONFIG_TIMEOUT_ENABLE_MASK	(0x01 << 3)
+#define CONFIG_INITVDD_MASK		(0x01 << 2)
+#define CONFIG_FORCEUPDATE_MASK		(0x01 << 1)
+#define CONFIG_VP_ENABLE_MASK		(0x01 << 0)
+
+/* Status register masks - All revisions */
+#define STATUS_VP_IN_IDLE_MASK		(0x01 << 0)
+
+/* Vlmitto register masks - All revisions */
+#define VLIMITTO_VDDMAX_MASK		(0xff << 24)
+#define VLIMITTO_VDDMIN_MASK		(0xff << 16)
+#define VLIMITTO_TIMEOUT_MASK		(0xffff << 0)
+
+/* Voltage register masks - All revisions */
+#define VOLTAGE_MASK			(0xff << 0)
+
+/* Step max/min register masks - All revisions */
+#define STEP_SMPSTIMEOUT_MASK		(0xffff << 8)
+#define STEP_VSTEP_MASK			(0xff << 0)
+
+/* 32 bit voltage processor registers */
+static struct regmap_config omap_vp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+/**
+ * struct omap_vp - Structure representing Voltage Processor info
+ * @dev:		device pointer for Voltage Processor
+ * @list:		list head for VP list.
+ * @usage_count:	Usage count - only 1 user at a time.(not always module)
+ * @clk_rate:		Sysclk rate for VP computation.
+ * @vc:			Voltage controller channel corresponding to VP
+ * @pmic:		PMIC used for this path
+ * @regmap:		regmap for VP instance
+ * @regs:		register map
+ * @int_base:		interrupt register base address
+ * @txdone_mask:	TRANXDONE interrupt mask for this VP instance in intreg
+ * @min_uV:		minimum voltage allowed by VP in micro-volts
+ * @max_uV:		maximum voltage allowed by VP in micro-volts
+ * @min_step_uV:	minimum continous voltage step in micro-volts for VP
+ * @max_step_uV:	maximum continous voltage step in micro-volts for VP
+ */
+struct omap_vp {
+	struct device *dev;
+	struct list_head list;
+	int usage_count;
+
+	unsigned long clk_rate;
+	struct omap_vc_channel_info *vc;
+	struct omap_pmic *pmic;
+
+	struct regmap *regmap;
+	const struct omap_vp_reg_data *regs;
+
+	void __iomem *int_base;
+	u32 txdone_mask;
+
+	u32 min_uV;
+	u32 max_uV;
+	u32 min_step_uV;
+	u32 max_step_uV;
+};
+
+static const struct of_device_id omap_vp_of_match[] = {
+	{.compatible = "ti,omap3-vp", .data = &omap_vp_reg_type1},
+	{.compatible = "ti,omap4-vp", .data = &omap_vp_reg_type2},
+	{.compatible = "ti,omap5-vp", .data = &omap_vp_reg_type2},
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_vp_of_match);
+
+static LIST_HEAD(omap_vp_list);
+static DEFINE_MUTEX(omap_vp_list_mutex);
+
+/**
+ * omap_vp_check_txdone() - inline helper to see if TRANXDONE is set
+ * @vp:	pointer to voltage processor
+ */
+static inline bool omap_vp_check_txdone(const struct omap_vp *vp)
+{
+	return !!(readl(vp->int_base) & vp->txdone_mask);
+}
+
+/**
+ * omap_vp_clear_txdone() - inline helper to clear TRANXDONE
+ * @vp:	pointer to voltage processor
+ *
+ * write of 1 bit clears that interrupt bit only.
+ */
+static inline void omap_vp_clear_txdone(const struct omap_vp *vp)
+{
+	writel(vp->txdone_mask, vp->int_base);
+};
+
+/**
+ * omap_vp_read_idle() - inline helper to read idle register
+ * @regmap:	regmap for voltage processor
+ * @regs:	registers for voltage processor
+ */
+static inline u32 omap_vp_read_idle(struct regmap *regmap,
+				    const struct omap_vp_reg_data *regs)
+{
+	u32 val = 0;
+	regmap_read(regmap, regs->status, &val);
+	return val;
+}
+
+/**
+ * omap_vp_wait_for_idle() - Wait for Voltage processor to idle
+ * @vp:	pointer to voltage processor
+ *
+ * helper to ensure that VP is idle (no pending AVS / previous VP operations)
+ */
+static inline int omap_vp_wait_for_idle(struct omap_vp *vp)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int timeout;
+
+	omap_vp_test_timeout((omap_vp_read_idle(regmap, regs) &
+			      STATUS_VP_IN_IDLE_MASK), pinfo->i2c_timeout_us,
+			     timeout);
+
+	if (timeout >= pinfo->i2c_timeout_us) {
+		dev_warn_ratelimited(dev, "%s: idle timedout(%d)\n",
+				     __func__, pinfo->i2c_timeout_us);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+/**
+ * omap_vp_set_init_voltage() - Setup voltage for transmission.
+ * @vp:	pointer to voltage processor
+ * @volt: voltage to setup the voltage processor with
+ */
+static int omap_vp_set_init_voltage(struct omap_vp *vp, u32 volt)
+{
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	struct omap_pmic_ops *ops = pmic->ops;
+	char vsel;
+	int ret;
+
+	ret = ops->uv_to_vsel(pmic, volt, &vsel);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(regmap, regs->config,
+				 (CONFIG_INIT_VOLTAGE_MASK |
+				  CONFIG_FORCEUPDATE_MASK |
+				  CONFIG_INITVDD_MASK),
+				 vsel << __ffs(CONFIG_INIT_VOLTAGE_MASK));
+	if (ret)
+		return ret;
+
+	/* Trigger initVDD value copy to voltage processor */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_INITVDD_MASK, CONFIG_INITVDD_MASK);
+	if (ret)
+		return ret;
+
+	/* Clear initVDD copy trigger bit */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_INITVDD_MASK, 0x0);
+
+	return ret;
+}
+
+/**
+ * omap_vp_get_current_voltage() - get the current voltage processor voltage
+ * @vp:	pointer to voltage processor
+ * @uv:	returns with voltage in micro-volts if read was successful.
+ */
+static int omap_vp_get_current_voltage(struct omap_vp *vp, u32 *uv)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	struct omap_pmic_ops *ops = pmic->ops;
+	u32 val;
+	u8 vsel;
+	int ret;
+
+	ret = regmap_read(regmap, regs->config, &val);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: unable to read config reg (%d)\n",
+				     __func__, ret);
+		return ret;
+	}
+
+	val &= CONFIG_INIT_VOLTAGE_MASK;
+	vsel = val >> __ffs(CONFIG_INIT_VOLTAGE_MASK);
+	ret = ops->vsel_to_uv(pmic, vsel, &val);
+
+	if (!ret)
+		*uv = val;
+	return ret;
+}
+
+/**
+ * omap_vp_forceupdate_scale() - Update voltage on PMIC using VP "Forceupdate"
+ * @vp:			pointer to voltage processor
+ * @target_volt:	voltage to set the PMIC to
+ *
+ * This will wait for the slew duration to ensure that the voltage is sync-ed
+ * on the PMIC.
+ */
+static int omap_vp_forceupdate_scale(struct omap_vp *vp, u32 target_volt)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int ret, timeout = 0, max_timeout;
+	u32 old_volt = 0;
+	u32 smps_transition_uv, smps_delay;
+
+	ret = omap_vp_wait_for_idle(vp);
+	if (ret)
+		return ret;
+
+	ret = omap_vp_get_current_voltage(vp, &old_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Unable to convert old voltage(%d)\n",
+				     __func__, ret);
+		/* We will use worst case start voltage - 0V for delay */
+	}
+
+	/*
+	 * Clear all pending TransactionDone interrupt/status. Typical latency
+	 * is <3us - use an conservative value from pmic info.
+	 */
+	max_timeout = 2 * pinfo->i2c_timeout_us;
+	while (timeout++ < max_timeout) {
+		omap_vp_clear_txdone(vp);
+		if (!omap_vp_check_txdone(vp))
+			break;
+		udelay(1);
+	}
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not clear(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	ret = omap_vp_set_init_voltage(vp, target_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Fail set init voltage at v=%d(%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Force update of voltage */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_FORCEUPDATE_MASK,
+				 CONFIG_FORCEUPDATE_MASK);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Forceupdate not set v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/*
+	 * Wait for TransactionDone. Typical latency is <200us.
+	 * Depends on SMPSWAITTIMEMIN/MAX and voltage change
+	 */
+	timeout = 0;
+	omap_vp_test_timeout(omap_vp_check_txdone(vp), max_timeout, timeout);
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not set(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * Due to the inability of OMAP Voltage controller OR voltage processor
+	 * to precisely know when the voltage has achieved the requested value,
+	 * we need a delay loop to ensure that the voltage has transitioned to
+	 * the required level.
+	 */
+	smps_transition_uv = abs(target_volt - old_volt);
+
+	/* delta_voltage / slew_rate, 2uS added as buffer */
+	smps_delay = DIV_ROUND_UP(smps_transition_uv, pinfo->slew_rate_uV) + 2;
+
+	/* We dont want to sleep for too long either */
+	usleep_range(smps_delay, smps_delay + 2);
+
+	/*
+	 * Disable TransactionDone interrupt , clear all status, clear
+	 * control registers
+	 */
+	timeout = 0;
+	while (timeout++ < max_timeout) {
+		omap_vp_clear_txdone(vp);
+		if (!omap_vp_check_txdone(vp))
+			break;
+		udelay(1);
+	}
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not recleared(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	/* Clear force bit */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_FORCEUPDATE_MASK, 0x0);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Forceupdate not cleared v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Do the required updates */
+	ret = omap_vc_channel_set_on_voltage(vp->vc, target_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Fail update VC onV at v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Now, Wait for VP to idle down */
+	ret = omap_vp_wait_for_idle(vp);
+
+	return ret;
+}
+
+/**
+ * omap_vp_setup() - Setup voltage processor
+ * @vp:	pointer to voltage processor
+ */
+static int omap_vp_setup(struct omap_vp *vp)
+{
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	struct omap_pmic_ops *ops = pmic->ops;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	u32 val, clk_rate, timeout, waittime;
+	u8 vstepmin, vstepmax;
+	u8 vddmin, vddmax;
+	int ret;
+
+	/* Div 1000 to avoid overflow */
+	clk_rate = vp->clk_rate / 1000;
+
+	ret = ops->uv_to_vsel(pmic, vp->min_uV, &vddmin);
+	if (ret)
+		return ret;
+	ret = ops->uv_to_vsel(pmic, vp->max_uV, &vddmax);
+	if (ret)
+		return ret;
+
+	timeout = DIV_ROUND_UP_ULL(clk_rate * pinfo->i2c_timeout_us, 1000);
+	waittime = DIV_ROUND_UP_ULL(pinfo->step_size_uV * clk_rate,
+				    1000 * pinfo->slew_rate_uV);
+
+	vstepmin = DIV_ROUND_UP(vp->min_step_uV, pinfo->step_size_uV);
+	vstepmax = DIV_ROUND_UP(vp->max_step_uV, pinfo->step_size_uV);
+
+	/* VSTEPMIN */
+	val =
+	    (waittime << __ffs(STEP_SMPSTIMEOUT_MASK)) & STEP_SMPSTIMEOUT_MASK;
+	val |= (vstepmin << __ffs(STEP_VSTEP_MASK)) & STEP_VSTEP_MASK;
+	ret = regmap_write(regmap, regs->step_min, val);
+	if (ret)
+		return ret;
+
+	/* VSTEPMIN */
+	val =
+	    (waittime << __ffs(STEP_SMPSTIMEOUT_MASK)) & STEP_SMPSTIMEOUT_MASK;
+	val |= (vstepmax << __ffs(STEP_VSTEP_MASK)) & STEP_VSTEP_MASK;
+	ret = regmap_write(regmap, regs->step_max, val);
+	if (ret)
+		return ret;
+
+	/* VLIMITTO */
+	val = (vddmax << __ffs(VLIMITTO_VDDMAX_MASK)) & VLIMITTO_VDDMAX_MASK;
+	val |= (vddmin << __ffs(VLIMITTO_VDDMIN_MASK)) & VLIMITTO_VDDMIN_MASK;
+	val |=
+	    (timeout << __ffs(VLIMITTO_TIMEOUT_MASK)) & VLIMITTO_TIMEOUT_MASK;
+	ret = regmap_write(regmap, regs->vlimitto, val);
+	if (ret)
+		return ret;
+
+	/* CONFIG */
+	ret =
+	    regmap_update_bits(regmap, regs->config, CONFIG_TIMEOUT_ENABLE_MASK,
+			       CONFIG_TIMEOUT_ENABLE_MASK);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+/**
+ * devm_omap_vp_release() -  helper to keep track of free usage.
+ * @dev:	device
+ * @res:	resource
+ */
+static void devm_omap_vp_release(struct device *dev, void *res)
+{
+	struct omap_vp *vp = *((struct omap_vp **)res);
+
+	mutex_lock(&omap_vp_list_mutex);
+
+	if (!vp->usage_count) {
+		vp->pmic = NULL;
+		vp->vc = NULL;
+	}
+	module_put(vp->dev->driver->owner);
+
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return;
+}
+
+/**
+ * of_get_omap_vp() - get the pmic node
+ * @dev:	device to pull information from
+ */
+static struct device_node *of_get_omap_vp(struct device *dev)
+{
+	struct device_node *pmic_node = NULL;
+	char *prop_name = "ti,vp";
+
+	dev_dbg(dev, "%s: Looking up %s from device tree\n", __func__,
+		prop_name);
+
+	pmic_node = of_parse_phandle(dev->of_node, prop_name, 0);
+
+	if (!pmic_node) {
+		dev_err(dev, "%s: Looking up %s property in node %s failed",
+			__func__, prop_name, dev->of_node->full_name);
+		return ERR_PTR(-ENODEV);
+	}
+	return pmic_node;
+}
+
+/**
+ * devm_omap_vp_get() - managed request to get a VP device
+ * @dev:	Generic device to handle the request for
+ * @pmic:	PMIC resource this will be assigned to
+ *
+ * Ensures that vp 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
+ */
+static struct device *devm_omap_vp_get(struct device *dev,
+				       struct omap_pmic *pmic)
+{
+	const struct omap_pmic_info *pinfo;
+	struct omap_vp *vp, **ptr;
+	struct device_node *node;
+	struct device *vp_dev;
+	struct omap_vc_channel_info *vc;
+	u32 min_uV, max_uV;
+	int ret = 0;
+
+	if (!dev || !dev->of_node) {
+		pr_err("%s: invalid parameters\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	node = of_get_omap_vp(dev);
+	if (IS_ERR(node))
+		return (void *)node;
+
+	mutex_lock(&omap_vp_list_mutex);
+	list_for_each_entry(vp, &omap_vp_list, list)
+	    if (vp->dev->of_node == node)
+		goto found;
+
+	/* Node definition is present, but not probed yet.. request defer */
+	vp_dev = ERR_PTR(-EPROBE_DEFER);
+	goto out_unlock;
+
+found:
+	vp_dev = vp->dev;
+	if (!try_module_get(vp_dev->driver->owner)) {
+		dev_err(dev, "%s: Cant get device owner\n", __func__);
+		vp_dev = ERR_PTR(-EINVAL);
+		goto out_unlock;
+	}
+
+	/* Allow ONLY 1 user at a time */
+	if (vp->usage_count) {
+		dev_err(dev, "%s: device %s is busy..\n", __func__,
+			dev_name(vp_dev));
+		ret = -EBUSY;
+		goto out;
+	}
+	vc = devm_omap_vc_channel_get(vp_dev, pmic);
+	if (IS_ERR(vc)) {
+		ret = PTR_ERR(vc);
+		dev_err(dev, "%s: vc channel not ready(%d) in %s?\n",
+			__func__, ret, dev_name(vp_dev));
+		goto out;
+	}
+	vp->vc = vc;
+	vp->pmic = pmic;
+	pinfo = pmic->info;
+
+	/* Adjust our voltages */
+	/* Cant go below PMIC min voltage */
+	min_uV = max(vp->min_uV, pinfo->min_uV);
+	/* Cant go below SoC retention voltage for operational case */
+	min_uV = max(min_uV, vc->retention_uV);
+	vp->min_uV = min_uV;
+
+	/* Cant go above PMIC max voltage */
+	max_uV = min(vp->max_uV, pinfo->max_uV);
+	vp->max_uV = max_uV;
+
+	ret = omap_vp_setup(vp);
+	if (ret) {
+		dev_err(dev, "%s: Failed to setup vp (%d) dev %s\n",
+			__func__, ret, dev_name(vp_dev));
+		goto out;
+	}
+
+	if (pmic->boot_voltage_uV) {
+		ret = omap_vp_forceupdate_scale(vp, pmic->boot_voltage_uV);
+		if (ret) {
+			dev_err(dev, "%s: Failed to set boot voltage %d(%d)\n",
+				__func__, pmic->boot_voltage_uV, ret);
+			goto out;
+		}
+	}
+
+	ptr = devres_alloc(devm_omap_vp_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	*ptr = vp;
+	vp->usage_count++;
+	devres_add(dev, ptr);
+
+out:
+	if (ret) {
+		module_put(vp_dev->driver->owner);
+		vp_dev = ERR_PTR(ret);
+	}
+out_unlock:
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return vp_dev;
+}
+
+/**
+ * omap_vp_voltage_set() - controller operation to set voltage
+ * @dev:	VP device to set voltage
+ * @uv:		voltage in micro-volts to set
+ */
+static int omap_vp_voltage_set(struct device *dev, u32 uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	return omap_vp_forceupdate_scale(vp, uv);
+}
+
+/**
+ * omap_vp_voltage_get() - controller operation to get voltage
+ * @dev:	VP device to get voltage from
+ * @uv:		returns voltage in micro-volts if successful
+ */
+static int omap_vp_voltage_get(struct device *dev, u32 *uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp || !uv)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	return omap_vp_get_current_voltage(vp, uv);
+}
+
+/**
+ * omap_vp_voltage_get_range() - controller function to return VP voltage range
+ * @dev:	VP device to query
+ * @min_uv:	if successful, returns min voltage supported by VP
+ * @max_uv:	if successful, returns max voltage supported by VP
+ */
+static int omap_vp_voltage_get_range(struct device *dev, u32 *min_uv,
+				     u32 *max_uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp || !min_uv || !max_uv)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	*min_uv = vp->min_uV;
+	*max_uv = vp->max_uV;
+	return 0;
+}
+
+static struct omap_pmic_controller_ops voltage_processor_ops = {
+	.devm_pmic_register = devm_omap_vp_get,
+	.voltage_set = omap_vp_voltage_set,
+	.voltage_get = omap_vp_voltage_get,
+	.voltage_get_range = omap_vp_voltage_get_range,
+};
+static bool voltage_processor_ops_registered;
+
+static int omap_vp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+	struct omap_vp *vp;
+	struct resource *res;
+	struct regmap *regmap;
+	char *pname;
+	struct clk *clk;
+	int ret = 0;
+	void __iomem *base, *int_base;
+
+	if (!node) {
+		dev_err(dev, "%s: missing device tree nodes?\n", __func__);
+		return -EINVAL;
+	}
+
+	match = of_match_device(omap_vp_of_match, 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;
+	}
+
+	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;
+	}
+	pname = "int-address";
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+	if (!res) {
+		dev_err(dev, "Missing '%s' IO resource\n", pname);
+		return -ENODEV;
+	}
+
+	/*
+	 * We may have shared interrupt register offsets which are
+	 * write-1-to-clear between domains ensuring exclusivity.
+	 */
+	int_base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (!int_base) {
+		dev_err(dev, "Unable to map '%s'\n", pname);
+		return -ENOMEM;
+	}
+
+	regmap = devm_regmap_init_mmio(dev, base, &omap_vp_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(dev, "regmap init failed(%d)\n", ret);
+		return ret;
+	}
+
+	vp = devm_kzalloc(dev, sizeof(*vp), GFP_KERNEL);
+	if (!vp) {
+		dev_err(dev, "%s: Unable to allocate VP\n", __func__);
+		return -ENOMEM;
+	}
+	vp->dev = dev;
+	vp->regs = match->data;
+	vp->regmap = regmap;
+	vp->int_base = int_base;
+
+	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;
+	}
+	vp->clk_rate = clk_get_rate(clk);
+	/* We dont need the clk any more */
+	clk_put(clk);
+
+	pname = "ti,min-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->min_uV);
+	if (ret)
+		goto invalid_of_property;
+
+	pname = "ti,max-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->max_uV);
+	if (ret || !vp->max_uV)
+		goto invalid_of_property;
+
+	pname = "ti,min-step-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->min_step_uV);
+	if (ret || !vp->min_step_uV)
+		goto invalid_of_property;
+
+	pname = "ti,max-step-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->max_step_uV);
+	if (ret || !vp->max_step_uV)
+		goto invalid_of_property;
+
+	pname = "ti,tranxdone-status-mask";
+	ret = of_property_read_u32(node, pname, &vp->txdone_mask);
+	if (ret || !vp->txdone_mask)
+		goto invalid_of_property;
+
+	platform_set_drvdata(pdev, vp);
+
+	mutex_lock(&omap_vp_list_mutex);
+	if (!voltage_processor_ops_registered) {
+		ret = omap_pmic_register_controller_ops(&voltage_processor_ops);
+		if (ret)
+			dev_err(dev, "Failed register pmic cops (%d)\n", ret);
+		else
+			voltage_processor_ops_registered = true;
+	}
+	if (!ret)
+		list_add(&vp->list, &omap_vp_list);
+
+	mutex_unlock(&omap_vp_list_mutex);
+	return ret;
+
+invalid_of_property:
+	if (!ret) {
+		dev_err(dev, "%s: Invalid value 0x0 in '%s' property.\n",
+			__func__, pname);
+		ret = -EINVAL;
+	} else {
+		dev_err(dev, "%s: Missing/Invalid '%s' property - error(%d)\n",
+			__func__, pname, ret);
+	}
+	return ret;
+}
+
+static int omap_vp_remove(struct platform_device *pdev)
+{
+	struct omap_vp *vp = platform_get_drvdata(pdev);
+
+	mutex_lock(&omap_vp_list_mutex);
+	list_del(&vp->list);
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return 0;
+}
+
+static struct platform_driver omap_vp_driver = {
+	.probe = omap_vp_probe,
+	.remove = omap_vp_remove,
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(omap_vp_of_match),
+		   },
+};
+module_platform_driver(omap_vp_driver);
+
+MODULE_DESCRIPTION("OMAP Voltage Processor Regulator Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc.");
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 3/8] PM / AVS: Introduce support for OMAP Voltage Processor(VP) with device tree nodes
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

Texas Instrument's OMAP SoC generations since OMAP3-5 introduced an TI
custom hardware block to better facilitate and standardize integration
of Power Management ICs which communicate over I2C called Voltage
Processor(VP).

This is an specialized hardware block meant to support PMIC chips only
over Voltage Controller(VC) interface. This provides an interface for
SmartReflex AVS module to send adjustment steps which is converted into
voltage values and send onwards by VP to VC. VP is also used to set the
voltage of the PMIC by commanding using "forceupdate".

We have an existing implementation in mach-omap2 which has been
re factored and moved out as a separate driver. This new driver is DT
only and the separation was meant to get a maintainable driver which
does not have to deal with legacy platform_data dependencies. The legacy
driver is retained to support non-DT boot and functionality will be
migrated to the DT-only version as we enable features.

Currently, this implementation considers only the basic steps needed for
voltage scaling and exposing voltage processor which hooks on to Voltage
Controller driver and OMAP PMIC driver to provide an regulator interface
over OMAP PMIC driver.

We may need to do additional timing configurations to enable Low power
mode voltage transitions and to hook the Adaptive Voltage
Scaling(AVS) implementation for OMAP which also uses the same voltage
controller to talk to PMICs. This needs to be addressed in a later
series.

This driver is meant to interface with OMAP PMIC driver when the
controller driver registers it's operations with OMAP PMIC driver and
associates with an voltage controller channel. This enables the full
communication path between the OMAP PMIC regulator to the external PMIC
hardware over OMAP Voltage Processor and OMAP Voltage Controller.

[grygorii.strashko at ti.com, taras at ti.com: co-developer]
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Taras Kondratiuk <taras@ti.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
---
 .../devicetree/bindings/power/omap-vp.txt          |   39 +
 drivers/power/avs/Makefile                         |    2 +-
 drivers/power/avs/omap_vp.c                        |  892 ++++++++++++++++++++
 3 files changed, 932 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/power/omap-vp.txt
 create mode 100644 drivers/power/avs/omap_vp.c

diff --git a/Documentation/devicetree/bindings/power/omap-vp.txt b/Documentation/devicetree/bindings/power/omap-vp.txt
new file mode 100644
index 0000000..b690e35
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/omap-vp.txt
@@ -0,0 +1,39 @@
+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-vp" - for OMAP3 family of devices
+  - "ti,omap4-vp" - for OMAP4 family of devices
+  - "ti,omap5-vp" - 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 VP module
+  - "int-address"	- contains base address of interrupt register for VP module
+- clocks: should point to the clock node used by VC module, usually sysclk
+- ti,min-micro-volts - SoC supported min operational voltage in micro-volts
+- ti,max-micro-volts - SoC supported max operational voltage in micro-volts
+- ti,min-step-micro-volts - SoC supported min operational voltage steps in micro-volts
+- ti,max-step-micro-volts - SoC supported max operational voltage steps in micro-volts
+- ti,tranxdone-status-mask: Mask to the int-register to write-to-clear mask
+	indicating VP has completed operation in sending command to Voltage Controller.
+- ti,vc-channel - phandle to the Voltage Controller Channel device used by this Voltage
+	Processor.
+Example:
+vp_mpu: vp at 0x4a307b58 {
+	compatible = "ti,omap4-vp";
+
+	reg = <0x4a307b58 0x18>, <0x4A306014 0x4>;
+	reg-names = "base-address", "int-address";
+	ti,tranxdone-status-mask=<0x20>;
+
+	clocks = <&sysclk_in>;
+
+	ti,vc-channel = <&vc_mpu>;
+	ti,min-step-micro-volts = <10000>;
+	ti,max-step-micro-volts = <50000>;
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1410000>;
+};
diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile
index 95d5f59..535cab5 100644
--- a/drivers/power/avs/Makefile
+++ b/drivers/power/avs/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_POWER_AVS_OMAP)		+= smartreflex.o
 ifneq ($(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL),)
 
 # OMAP Common
-omap-volt-common			=  omap_vc.o
+omap-volt-common			=  omap_vc.o omap_vp.o
 
 # OMAP SoC specific
 ifneq ($(CONFIG_ARCH_OMAP3),)
diff --git a/drivers/power/avs/omap_vp.c b/drivers/power/avs/omap_vp.c
new file mode 100644
index 0000000..8ce42e1
--- /dev/null
+++ b/drivers/power/avs/omap_vp.c
@@ -0,0 +1,892 @@
+/*
+ * OMAP Voltage Processor (VP) interface
+ *
+ * Idea based on arch/arm/mach-omap2/vp.c
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/omap-pmic-regulator.h>
+#include <linux/regmap.h>
+#include "omap_vc.h"
+
+#define DRIVER_NAME	"omap-vp"
+
+/**
+ * omap_vp_test_timeout - busy-loop, testing a condition
+ * @cond: condition to test until it evaluates to true
+ * @timeout: maximum number of microseconds in the timeout
+ * @index: loop index (integer)
+ *
+ * Loop waiting for @cond to become true or until at least @timeout
+ * microseconds have passed.  To use, define some integer @index in the
+ * calling code.  After running, if @index == @timeout, then the loop has
+ * timed out.
+ */
+#define omap_vp_test_timeout(cond, timeout, index)		\
+({								\
+	for (index = 0; index < timeout; index++) {		\
+		if (cond)					\
+			break;					\
+		udelay(1);					\
+	}							\
+})
+
+/**
+ * struct omap_vp_reg_data - Voltage processor register offsets
+ * @config:	CONFIG register
+ * @status:	STATUS register
+ * @vlimitto:	VLIMITTO register
+ * @voltage:	VOLTAGE register
+ * @step_max:	STEP_MAX register
+ * @step_min:	STEP_MAX register
+ */
+struct omap_vp_reg_data {
+	u8 config;
+	u8 status;
+	u8 vlimitto;
+	u8 voltage;
+	u8 step_max;
+	u8 step_min;
+};
+
+static const struct omap_vp_reg_data omap_vp_reg_type1 = {
+	.config = 0x00,
+	.status = 0x14,
+	.vlimitto = 0x0C,
+	.voltage = 0x10,
+	.step_max = 0x08,
+	.step_min = 0x04,
+};
+
+static const struct omap_vp_reg_data omap_vp_reg_type2 = {
+	.config = 0x00,
+	.status = 0x04,
+	.vlimitto = 0x08,
+	.voltage = 0x0C,
+	.step_max = 0x10,
+	.step_min = 0x14,
+};
+
+/* Config register masks - All revisions */
+#define CONFIG_ERROR_OFFSET_MASK	(0xff << 24)
+#define CONFIG_ERROR_GAIN_MASK		(0xff << 16)
+#define CONFIG_INIT_VOLTAGE_MASK	(0xff << 8)
+#define CONFIG_TIMEOUT_ENABLE_MASK	(0x01 << 3)
+#define CONFIG_INITVDD_MASK		(0x01 << 2)
+#define CONFIG_FORCEUPDATE_MASK		(0x01 << 1)
+#define CONFIG_VP_ENABLE_MASK		(0x01 << 0)
+
+/* Status register masks - All revisions */
+#define STATUS_VP_IN_IDLE_MASK		(0x01 << 0)
+
+/* Vlmitto register masks - All revisions */
+#define VLIMITTO_VDDMAX_MASK		(0xff << 24)
+#define VLIMITTO_VDDMIN_MASK		(0xff << 16)
+#define VLIMITTO_TIMEOUT_MASK		(0xffff << 0)
+
+/* Voltage register masks - All revisions */
+#define VOLTAGE_MASK			(0xff << 0)
+
+/* Step max/min register masks - All revisions */
+#define STEP_SMPSTIMEOUT_MASK		(0xffff << 8)
+#define STEP_VSTEP_MASK			(0xff << 0)
+
+/* 32 bit voltage processor registers */
+static struct regmap_config omap_vp_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+/**
+ * struct omap_vp - Structure representing Voltage Processor info
+ * @dev:		device pointer for Voltage Processor
+ * @list:		list head for VP list.
+ * @usage_count:	Usage count - only 1 user@a time.(not always module)
+ * @clk_rate:		Sysclk rate for VP computation.
+ * @vc:			Voltage controller channel corresponding to VP
+ * @pmic:		PMIC used for this path
+ * @regmap:		regmap for VP instance
+ * @regs:		register map
+ * @int_base:		interrupt register base address
+ * @txdone_mask:	TRANXDONE interrupt mask for this VP instance in intreg
+ * @min_uV:		minimum voltage allowed by VP in micro-volts
+ * @max_uV:		maximum voltage allowed by VP in micro-volts
+ * @min_step_uV:	minimum continous voltage step in micro-volts for VP
+ * @max_step_uV:	maximum continous voltage step in micro-volts for VP
+ */
+struct omap_vp {
+	struct device *dev;
+	struct list_head list;
+	int usage_count;
+
+	unsigned long clk_rate;
+	struct omap_vc_channel_info *vc;
+	struct omap_pmic *pmic;
+
+	struct regmap *regmap;
+	const struct omap_vp_reg_data *regs;
+
+	void __iomem *int_base;
+	u32 txdone_mask;
+
+	u32 min_uV;
+	u32 max_uV;
+	u32 min_step_uV;
+	u32 max_step_uV;
+};
+
+static const struct of_device_id omap_vp_of_match[] = {
+	{.compatible = "ti,omap3-vp", .data = &omap_vp_reg_type1},
+	{.compatible = "ti,omap4-vp", .data = &omap_vp_reg_type2},
+	{.compatible = "ti,omap5-vp", .data = &omap_vp_reg_type2},
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_vp_of_match);
+
+static LIST_HEAD(omap_vp_list);
+static DEFINE_MUTEX(omap_vp_list_mutex);
+
+/**
+ * omap_vp_check_txdone() - inline helper to see if TRANXDONE is set
+ * @vp:	pointer to voltage processor
+ */
+static inline bool omap_vp_check_txdone(const struct omap_vp *vp)
+{
+	return !!(readl(vp->int_base) & vp->txdone_mask);
+}
+
+/**
+ * omap_vp_clear_txdone() - inline helper to clear TRANXDONE
+ * @vp:	pointer to voltage processor
+ *
+ * write of 1 bit clears that interrupt bit only.
+ */
+static inline void omap_vp_clear_txdone(const struct omap_vp *vp)
+{
+	writel(vp->txdone_mask, vp->int_base);
+};
+
+/**
+ * omap_vp_read_idle() - inline helper to read idle register
+ * @regmap:	regmap for voltage processor
+ * @regs:	registers for voltage processor
+ */
+static inline u32 omap_vp_read_idle(struct regmap *regmap,
+				    const struct omap_vp_reg_data *regs)
+{
+	u32 val = 0;
+	regmap_read(regmap, regs->status, &val);
+	return val;
+}
+
+/**
+ * omap_vp_wait_for_idle() - Wait for Voltage processor to idle
+ * @vp:	pointer to voltage processor
+ *
+ * helper to ensure that VP is idle (no pending AVS / previous VP operations)
+ */
+static inline int omap_vp_wait_for_idle(struct omap_vp *vp)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int timeout;
+
+	omap_vp_test_timeout((omap_vp_read_idle(regmap, regs) &
+			      STATUS_VP_IN_IDLE_MASK), pinfo->i2c_timeout_us,
+			     timeout);
+
+	if (timeout >= pinfo->i2c_timeout_us) {
+		dev_warn_ratelimited(dev, "%s: idle timedout(%d)\n",
+				     __func__, pinfo->i2c_timeout_us);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+/**
+ * omap_vp_set_init_voltage() - Setup voltage for transmission.
+ * @vp:	pointer to voltage processor
+ * @volt: voltage to setup the voltage processor with
+ */
+static int omap_vp_set_init_voltage(struct omap_vp *vp, u32 volt)
+{
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	struct omap_pmic_ops *ops = pmic->ops;
+	char vsel;
+	int ret;
+
+	ret = ops->uv_to_vsel(pmic, volt, &vsel);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(regmap, regs->config,
+				 (CONFIG_INIT_VOLTAGE_MASK |
+				  CONFIG_FORCEUPDATE_MASK |
+				  CONFIG_INITVDD_MASK),
+				 vsel << __ffs(CONFIG_INIT_VOLTAGE_MASK));
+	if (ret)
+		return ret;
+
+	/* Trigger initVDD value copy to voltage processor */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_INITVDD_MASK, CONFIG_INITVDD_MASK);
+	if (ret)
+		return ret;
+
+	/* Clear initVDD copy trigger bit */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_INITVDD_MASK, 0x0);
+
+	return ret;
+}
+
+/**
+ * omap_vp_get_current_voltage() - get the current voltage processor voltage
+ * @vp:	pointer to voltage processor
+ * @uv:	returns with voltage in micro-volts if read was successful.
+ */
+static int omap_vp_get_current_voltage(struct omap_vp *vp, u32 *uv)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	struct omap_pmic_ops *ops = pmic->ops;
+	u32 val;
+	u8 vsel;
+	int ret;
+
+	ret = regmap_read(regmap, regs->config, &val);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: unable to read config reg (%d)\n",
+				     __func__, ret);
+		return ret;
+	}
+
+	val &= CONFIG_INIT_VOLTAGE_MASK;
+	vsel = val >> __ffs(CONFIG_INIT_VOLTAGE_MASK);
+	ret = ops->vsel_to_uv(pmic, vsel, &val);
+
+	if (!ret)
+		*uv = val;
+	return ret;
+}
+
+/**
+ * omap_vp_forceupdate_scale() - Update voltage on PMIC using VP "Forceupdate"
+ * @vp:			pointer to voltage processor
+ * @target_volt:	voltage to set the PMIC to
+ *
+ * This will wait for the slew duration to ensure that the voltage is sync-ed
+ * on the PMIC.
+ */
+static int omap_vp_forceupdate_scale(struct omap_vp *vp, u32 target_volt)
+{
+	struct device *dev = vp->dev;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	int ret, timeout = 0, max_timeout;
+	u32 old_volt = 0;
+	u32 smps_transition_uv, smps_delay;
+
+	ret = omap_vp_wait_for_idle(vp);
+	if (ret)
+		return ret;
+
+	ret = omap_vp_get_current_voltage(vp, &old_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Unable to convert old voltage(%d)\n",
+				     __func__, ret);
+		/* We will use worst case start voltage - 0V for delay */
+	}
+
+	/*
+	 * Clear all pending TransactionDone interrupt/status. Typical latency
+	 * is <3us - use an conservative value from pmic info.
+	 */
+	max_timeout = 2 * pinfo->i2c_timeout_us;
+	while (timeout++ < max_timeout) {
+		omap_vp_clear_txdone(vp);
+		if (!omap_vp_check_txdone(vp))
+			break;
+		udelay(1);
+	}
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not clear(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	ret = omap_vp_set_init_voltage(vp, target_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Fail set init voltage at v=%d(%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Force update of voltage */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_FORCEUPDATE_MASK,
+				 CONFIG_FORCEUPDATE_MASK);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Forceupdate not set v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/*
+	 * Wait for TransactionDone. Typical latency is <200us.
+	 * Depends on SMPSWAITTIMEMIN/MAX and voltage change
+	 */
+	timeout = 0;
+	omap_vp_test_timeout(omap_vp_check_txdone(vp), max_timeout, timeout);
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not set(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * Due to the inability of OMAP Voltage controller OR voltage processor
+	 * to precisely know when the voltage has achieved the requested value,
+	 * we need a delay loop to ensure that the voltage has transitioned to
+	 * the required level.
+	 */
+	smps_transition_uv = abs(target_volt - old_volt);
+
+	/* delta_voltage / slew_rate, 2uS added as buffer */
+	smps_delay = DIV_ROUND_UP(smps_transition_uv, pinfo->slew_rate_uV) + 2;
+
+	/* We dont want to sleep for too long either */
+	usleep_range(smps_delay, smps_delay + 2);
+
+	/*
+	 * Disable TransactionDone interrupt , clear all status, clear
+	 * control registers
+	 */
+	timeout = 0;
+	while (timeout++ < max_timeout) {
+		omap_vp_clear_txdone(vp);
+		if (!omap_vp_check_txdone(vp))
+			break;
+		udelay(1);
+	}
+	if (timeout >= max_timeout) {
+		dev_warn_ratelimited(dev,
+				     "%s: TRANXDONE not recleared(t=%d v=%d)\n",
+				     __func__, max_timeout, target_volt);
+		return -ETIMEDOUT;
+	}
+
+	/* Clear force bit */
+	ret = regmap_update_bits(regmap, regs->config,
+				 CONFIG_FORCEUPDATE_MASK, 0x0);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Forceupdate not cleared v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Do the required updates */
+	ret = omap_vc_channel_set_on_voltage(vp->vc, target_volt);
+	if (ret) {
+		dev_warn_ratelimited(dev,
+				     "%s: Fail update VC onV at v=%d (%d)\n",
+				     __func__, target_volt, ret);
+		return ret;
+	}
+
+	/* Now, Wait for VP to idle down */
+	ret = omap_vp_wait_for_idle(vp);
+
+	return ret;
+}
+
+/**
+ * omap_vp_setup() - Setup voltage processor
+ * @vp:	pointer to voltage processor
+ */
+static int omap_vp_setup(struct omap_vp *vp)
+{
+	struct omap_pmic *pmic = vp->pmic;
+	const struct omap_pmic_info *pinfo = pmic->info;
+	struct omap_pmic_ops *ops = pmic->ops;
+	struct regmap *regmap = vp->regmap;
+	const struct omap_vp_reg_data *regs = vp->regs;
+	u32 val, clk_rate, timeout, waittime;
+	u8 vstepmin, vstepmax;
+	u8 vddmin, vddmax;
+	int ret;
+
+	/* Div 1000 to avoid overflow */
+	clk_rate = vp->clk_rate / 1000;
+
+	ret = ops->uv_to_vsel(pmic, vp->min_uV, &vddmin);
+	if (ret)
+		return ret;
+	ret = ops->uv_to_vsel(pmic, vp->max_uV, &vddmax);
+	if (ret)
+		return ret;
+
+	timeout = DIV_ROUND_UP_ULL(clk_rate * pinfo->i2c_timeout_us, 1000);
+	waittime = DIV_ROUND_UP_ULL(pinfo->step_size_uV * clk_rate,
+				    1000 * pinfo->slew_rate_uV);
+
+	vstepmin = DIV_ROUND_UP(vp->min_step_uV, pinfo->step_size_uV);
+	vstepmax = DIV_ROUND_UP(vp->max_step_uV, pinfo->step_size_uV);
+
+	/* VSTEPMIN */
+	val =
+	    (waittime << __ffs(STEP_SMPSTIMEOUT_MASK)) & STEP_SMPSTIMEOUT_MASK;
+	val |= (vstepmin << __ffs(STEP_VSTEP_MASK)) & STEP_VSTEP_MASK;
+	ret = regmap_write(regmap, regs->step_min, val);
+	if (ret)
+		return ret;
+
+	/* VSTEPMIN */
+	val =
+	    (waittime << __ffs(STEP_SMPSTIMEOUT_MASK)) & STEP_SMPSTIMEOUT_MASK;
+	val |= (vstepmax << __ffs(STEP_VSTEP_MASK)) & STEP_VSTEP_MASK;
+	ret = regmap_write(regmap, regs->step_max, val);
+	if (ret)
+		return ret;
+
+	/* VLIMITTO */
+	val = (vddmax << __ffs(VLIMITTO_VDDMAX_MASK)) & VLIMITTO_VDDMAX_MASK;
+	val |= (vddmin << __ffs(VLIMITTO_VDDMIN_MASK)) & VLIMITTO_VDDMIN_MASK;
+	val |=
+	    (timeout << __ffs(VLIMITTO_TIMEOUT_MASK)) & VLIMITTO_TIMEOUT_MASK;
+	ret = regmap_write(regmap, regs->vlimitto, val);
+	if (ret)
+		return ret;
+
+	/* CONFIG */
+	ret =
+	    regmap_update_bits(regmap, regs->config, CONFIG_TIMEOUT_ENABLE_MASK,
+			       CONFIG_TIMEOUT_ENABLE_MASK);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+/**
+ * devm_omap_vp_release() -  helper to keep track of free usage.
+ * @dev:	device
+ * @res:	resource
+ */
+static void devm_omap_vp_release(struct device *dev, void *res)
+{
+	struct omap_vp *vp = *((struct omap_vp **)res);
+
+	mutex_lock(&omap_vp_list_mutex);
+
+	if (!vp->usage_count) {
+		vp->pmic = NULL;
+		vp->vc = NULL;
+	}
+	module_put(vp->dev->driver->owner);
+
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return;
+}
+
+/**
+ * of_get_omap_vp() - get the pmic node
+ * @dev:	device to pull information from
+ */
+static struct device_node *of_get_omap_vp(struct device *dev)
+{
+	struct device_node *pmic_node = NULL;
+	char *prop_name = "ti,vp";
+
+	dev_dbg(dev, "%s: Looking up %s from device tree\n", __func__,
+		prop_name);
+
+	pmic_node = of_parse_phandle(dev->of_node, prop_name, 0);
+
+	if (!pmic_node) {
+		dev_err(dev, "%s: Looking up %s property in node %s failed",
+			__func__, prop_name, dev->of_node->full_name);
+		return ERR_PTR(-ENODEV);
+	}
+	return pmic_node;
+}
+
+/**
+ * devm_omap_vp_get() - managed request to get a VP device
+ * @dev:	Generic device to handle the request for
+ * @pmic:	PMIC resource this will be assigned to
+ *
+ * Ensures that vp 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
+ */
+static struct device *devm_omap_vp_get(struct device *dev,
+				       struct omap_pmic *pmic)
+{
+	const struct omap_pmic_info *pinfo;
+	struct omap_vp *vp, **ptr;
+	struct device_node *node;
+	struct device *vp_dev;
+	struct omap_vc_channel_info *vc;
+	u32 min_uV, max_uV;
+	int ret = 0;
+
+	if (!dev || !dev->of_node) {
+		pr_err("%s: invalid parameters\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	node = of_get_omap_vp(dev);
+	if (IS_ERR(node))
+		return (void *)node;
+
+	mutex_lock(&omap_vp_list_mutex);
+	list_for_each_entry(vp, &omap_vp_list, list)
+	    if (vp->dev->of_node == node)
+		goto found;
+
+	/* Node definition is present, but not probed yet.. request defer */
+	vp_dev = ERR_PTR(-EPROBE_DEFER);
+	goto out_unlock;
+
+found:
+	vp_dev = vp->dev;
+	if (!try_module_get(vp_dev->driver->owner)) {
+		dev_err(dev, "%s: Cant get device owner\n", __func__);
+		vp_dev = ERR_PTR(-EINVAL);
+		goto out_unlock;
+	}
+
+	/* Allow ONLY 1 user at a time */
+	if (vp->usage_count) {
+		dev_err(dev, "%s: device %s is busy..\n", __func__,
+			dev_name(vp_dev));
+		ret = -EBUSY;
+		goto out;
+	}
+	vc = devm_omap_vc_channel_get(vp_dev, pmic);
+	if (IS_ERR(vc)) {
+		ret = PTR_ERR(vc);
+		dev_err(dev, "%s: vc channel not ready(%d) in %s?\n",
+			__func__, ret, dev_name(vp_dev));
+		goto out;
+	}
+	vp->vc = vc;
+	vp->pmic = pmic;
+	pinfo = pmic->info;
+
+	/* Adjust our voltages */
+	/* Cant go below PMIC min voltage */
+	min_uV = max(vp->min_uV, pinfo->min_uV);
+	/* Cant go below SoC retention voltage for operational case */
+	min_uV = max(min_uV, vc->retention_uV);
+	vp->min_uV = min_uV;
+
+	/* Cant go above PMIC max voltage */
+	max_uV = min(vp->max_uV, pinfo->max_uV);
+	vp->max_uV = max_uV;
+
+	ret = omap_vp_setup(vp);
+	if (ret) {
+		dev_err(dev, "%s: Failed to setup vp (%d) dev %s\n",
+			__func__, ret, dev_name(vp_dev));
+		goto out;
+	}
+
+	if (pmic->boot_voltage_uV) {
+		ret = omap_vp_forceupdate_scale(vp, pmic->boot_voltage_uV);
+		if (ret) {
+			dev_err(dev, "%s: Failed to set boot voltage %d(%d)\n",
+				__func__, pmic->boot_voltage_uV, ret);
+			goto out;
+		}
+	}
+
+	ptr = devres_alloc(devm_omap_vp_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	*ptr = vp;
+	vp->usage_count++;
+	devres_add(dev, ptr);
+
+out:
+	if (ret) {
+		module_put(vp_dev->driver->owner);
+		vp_dev = ERR_PTR(ret);
+	}
+out_unlock:
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return vp_dev;
+}
+
+/**
+ * omap_vp_voltage_set() - controller operation to set voltage
+ * @dev:	VP device to set voltage
+ * @uv:		voltage in micro-volts to set
+ */
+static int omap_vp_voltage_set(struct device *dev, u32 uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	return omap_vp_forceupdate_scale(vp, uv);
+}
+
+/**
+ * omap_vp_voltage_get() - controller operation to get voltage
+ * @dev:	VP device to get voltage from
+ * @uv:		returns voltage in micro-volts if successful
+ */
+static int omap_vp_voltage_get(struct device *dev, u32 *uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp || !uv)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	return omap_vp_get_current_voltage(vp, uv);
+}
+
+/**
+ * omap_vp_voltage_get_range() - controller function to return VP voltage range
+ * @dev:	VP device to query
+ * @min_uv:	if successful, returns min voltage supported by VP
+ * @max_uv:	if successful, returns max voltage supported by VP
+ */
+static int omap_vp_voltage_get_range(struct device *dev, u32 *min_uv,
+				     u32 *max_uv)
+{
+	struct omap_vp *vp = dev_get_drvdata(dev);
+
+	if (!vp || !min_uv || !max_uv)
+		return -EINVAL;
+	if (!vp->pmic || !vp->vc)
+		return -EINVAL;
+
+	*min_uv = vp->min_uV;
+	*max_uv = vp->max_uV;
+	return 0;
+}
+
+static struct omap_pmic_controller_ops voltage_processor_ops = {
+	.devm_pmic_register = devm_omap_vp_get,
+	.voltage_set = omap_vp_voltage_set,
+	.voltage_get = omap_vp_voltage_get,
+	.voltage_get_range = omap_vp_voltage_get_range,
+};
+static bool voltage_processor_ops_registered;
+
+static int omap_vp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+	struct omap_vp *vp;
+	struct resource *res;
+	struct regmap *regmap;
+	char *pname;
+	struct clk *clk;
+	int ret = 0;
+	void __iomem *base, *int_base;
+
+	if (!node) {
+		dev_err(dev, "%s: missing device tree nodes?\n", __func__);
+		return -EINVAL;
+	}
+
+	match = of_match_device(omap_vp_of_match, 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;
+	}
+
+	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;
+	}
+	pname = "int-address";
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+	if (!res) {
+		dev_err(dev, "Missing '%s' IO resource\n", pname);
+		return -ENODEV;
+	}
+
+	/*
+	 * We may have shared interrupt register offsets which are
+	 * write-1-to-clear between domains ensuring exclusivity.
+	 */
+	int_base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (!int_base) {
+		dev_err(dev, "Unable to map '%s'\n", pname);
+		return -ENOMEM;
+	}
+
+	regmap = devm_regmap_init_mmio(dev, base, &omap_vp_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(dev, "regmap init failed(%d)\n", ret);
+		return ret;
+	}
+
+	vp = devm_kzalloc(dev, sizeof(*vp), GFP_KERNEL);
+	if (!vp) {
+		dev_err(dev, "%s: Unable to allocate VP\n", __func__);
+		return -ENOMEM;
+	}
+	vp->dev = dev;
+	vp->regs = match->data;
+	vp->regmap = regmap;
+	vp->int_base = int_base;
+
+	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;
+	}
+	vp->clk_rate = clk_get_rate(clk);
+	/* We dont need the clk any more */
+	clk_put(clk);
+
+	pname = "ti,min-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->min_uV);
+	if (ret)
+		goto invalid_of_property;
+
+	pname = "ti,max-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->max_uV);
+	if (ret || !vp->max_uV)
+		goto invalid_of_property;
+
+	pname = "ti,min-step-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->min_step_uV);
+	if (ret || !vp->min_step_uV)
+		goto invalid_of_property;
+
+	pname = "ti,max-step-micro-volts";
+	ret = of_property_read_u32(node, pname, &vp->max_step_uV);
+	if (ret || !vp->max_step_uV)
+		goto invalid_of_property;
+
+	pname = "ti,tranxdone-status-mask";
+	ret = of_property_read_u32(node, pname, &vp->txdone_mask);
+	if (ret || !vp->txdone_mask)
+		goto invalid_of_property;
+
+	platform_set_drvdata(pdev, vp);
+
+	mutex_lock(&omap_vp_list_mutex);
+	if (!voltage_processor_ops_registered) {
+		ret = omap_pmic_register_controller_ops(&voltage_processor_ops);
+		if (ret)
+			dev_err(dev, "Failed register pmic cops (%d)\n", ret);
+		else
+			voltage_processor_ops_registered = true;
+	}
+	if (!ret)
+		list_add(&vp->list, &omap_vp_list);
+
+	mutex_unlock(&omap_vp_list_mutex);
+	return ret;
+
+invalid_of_property:
+	if (!ret) {
+		dev_err(dev, "%s: Invalid value 0x0 in '%s' property.\n",
+			__func__, pname);
+		ret = -EINVAL;
+	} else {
+		dev_err(dev, "%s: Missing/Invalid '%s' property - error(%d)\n",
+			__func__, pname, ret);
+	}
+	return ret;
+}
+
+static int omap_vp_remove(struct platform_device *pdev)
+{
+	struct omap_vp *vp = platform_get_drvdata(pdev);
+
+	mutex_lock(&omap_vp_list_mutex);
+	list_del(&vp->list);
+	mutex_unlock(&omap_vp_list_mutex);
+
+	return 0;
+}
+
+static struct platform_driver omap_vp_driver = {
+	.probe = omap_vp_probe,
+	.remove = omap_vp_remove,
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(omap_vp_of_match),
+		   },
+};
+module_platform_driver(omap_vp_driver);
+
+MODULE_DESCRIPTION("OMAP Voltage Processor Regulator Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc.");
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 4/8] ARM: dts: OMAP4: add voltage controller nodes
  2013-06-21 21:25 ` Nishanth Menon
  (?)
@ 2013-06-21 21:25   ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

OMAP443x, OMAP446x SoC use same offsets for voltage controller,
however their voltage characteristics differ a little.

Introduce the voltage controller nodes for the same.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4.dtsi    |   26 ++++++++++++++++++++++++++
 arch/arm/boot/dts/omap443x.dtsi |   15 +++++++++++++++
 arch/arm/boot/dts/omap4460.dtsi |   16 ++++++++++++++++
 3 files changed, 57 insertions(+)

diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 3b44546..a153e8d 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -104,6 +104,11 @@
 			ti,hwmods = "counter_32k";
 		};
 
+		sysclk_in: sys_clkin {
+			#clock-cells = <0>;
+			compatible = "ti,omap-clock";
+		};
+
 		dpll_mpu: dpll_mpu {
 			#clock-cells = <0>;
 			compatible = "ti,omap-clock";
@@ -663,5 +668,26 @@
 			ram-bits = <12>;
 			ti,has-mailbox;
 		};
+
+		vc: vc@0x4A307B88 {
+			compatible = "ti,omap4-vc";
+			clocks = <&sysclk_in>;
+			reg = <0x4A307B88 0x40>;
+			reg-names = "base-address";
+
+			ti,i2c-high-speed; /* belongs to board file */
+			vc_mpu: vc_mpu {
+				compatible = "ti,omap4-vc-channel-mpu";
+				ti,master-channel;
+			};
+
+			vc_iva: vc_iva {
+				compatible = "ti,omap4-vc-channel-iva";
+			};
+
+			vc_core: vc_core {
+				compatible = "ti,omap4-vc-channel-core";
+			};
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index bcf455e..2b0deb5 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -31,3 +31,18 @@
 		compatible = "ti,omap4430-bandgap";
 	};
 };
+
+&vc_mpu {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_iva {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_core {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index c2f0f39..16210a1 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -39,3 +39,19 @@
 		gpios = <&gpio3 22 0>; /* tshut */
 	};
 };
+
+&vc_mpu {
+	ti,retention-micro-volts = <750000>;
+	/* Erratum i738: Reliability impact work around */
+	ti,off-micro-volts = <750000>;
+};
+
+&vc_iva {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_core {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 4/8] ARM: dts: OMAP4: add voltage controller nodes
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

OMAP443x, OMAP446x SoC use same offsets for voltage controller,
however their voltage characteristics differ a little.

Introduce the voltage controller nodes for the same.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4.dtsi    |   26 ++++++++++++++++++++++++++
 arch/arm/boot/dts/omap443x.dtsi |   15 +++++++++++++++
 arch/arm/boot/dts/omap4460.dtsi |   16 ++++++++++++++++
 3 files changed, 57 insertions(+)

diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 3b44546..a153e8d 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -104,6 +104,11 @@
 			ti,hwmods = "counter_32k";
 		};
 
+		sysclk_in: sys_clkin {
+			#clock-cells = <0>;
+			compatible = "ti,omap-clock";
+		};
+
 		dpll_mpu: dpll_mpu {
 			#clock-cells = <0>;
 			compatible = "ti,omap-clock";
@@ -663,5 +668,26 @@
 			ram-bits = <12>;
 			ti,has-mailbox;
 		};
+
+		vc: vc@0x4A307B88 {
+			compatible = "ti,omap4-vc";
+			clocks = <&sysclk_in>;
+			reg = <0x4A307B88 0x40>;
+			reg-names = "base-address";
+
+			ti,i2c-high-speed; /* belongs to board file */
+			vc_mpu: vc_mpu {
+				compatible = "ti,omap4-vc-channel-mpu";
+				ti,master-channel;
+			};
+
+			vc_iva: vc_iva {
+				compatible = "ti,omap4-vc-channel-iva";
+			};
+
+			vc_core: vc_core {
+				compatible = "ti,omap4-vc-channel-core";
+			};
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index bcf455e..2b0deb5 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -31,3 +31,18 @@
 		compatible = "ti,omap4430-bandgap";
 	};
 };
+
+&vc_mpu {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_iva {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_core {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index c2f0f39..16210a1 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -39,3 +39,19 @@
 		gpios = <&gpio3 22 0>; /* tshut */
 	};
 };
+
+&vc_mpu {
+	ti,retention-micro-volts = <750000>;
+	/* Erratum i738: Reliability impact work around */
+	ti,off-micro-volts = <750000>;
+};
+
+&vc_iva {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_core {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 4/8] ARM: dts: OMAP4: add voltage controller nodes
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

OMAP443x, OMAP446x SoC use same offsets for voltage controller,
however their voltage characteristics differ a little.

Introduce the voltage controller nodes for the same.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4.dtsi    |   26 ++++++++++++++++++++++++++
 arch/arm/boot/dts/omap443x.dtsi |   15 +++++++++++++++
 arch/arm/boot/dts/omap4460.dtsi |   16 ++++++++++++++++
 3 files changed, 57 insertions(+)

diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 3b44546..a153e8d 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -104,6 +104,11 @@
 			ti,hwmods = "counter_32k";
 		};
 
+		sysclk_in: sys_clkin {
+			#clock-cells = <0>;
+			compatible = "ti,omap-clock";
+		};
+
 		dpll_mpu: dpll_mpu {
 			#clock-cells = <0>;
 			compatible = "ti,omap-clock";
@@ -663,5 +668,26 @@
 			ram-bits = <12>;
 			ti,has-mailbox;
 		};
+
+		vc: vc at 0x4A307B88 {
+			compatible = "ti,omap4-vc";
+			clocks = <&sysclk_in>;
+			reg = <0x4A307B88 0x40>;
+			reg-names = "base-address";
+
+			ti,i2c-high-speed; /* belongs to board file */
+			vc_mpu: vc_mpu {
+				compatible = "ti,omap4-vc-channel-mpu";
+				ti,master-channel;
+			};
+
+			vc_iva: vc_iva {
+				compatible = "ti,omap4-vc-channel-iva";
+			};
+
+			vc_core: vc_core {
+				compatible = "ti,omap4-vc-channel-core";
+			};
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index bcf455e..2b0deb5 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -31,3 +31,18 @@
 		compatible = "ti,omap4430-bandgap";
 	};
 };
+
+&vc_mpu {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_iva {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_core {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index c2f0f39..16210a1 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -39,3 +39,19 @@
 		gpios = <&gpio3 22 0>; /* tshut */
 	};
 };
+
+&vc_mpu {
+	ti,retention-micro-volts = <750000>;
+	/* Erratum i738: Reliability impact work around */
+	ti,off-micro-volts = <750000>;
+};
+
+&vc_iva {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
+
+&vc_core {
+	ti,retention-micro-volts = <750000>;
+	ti,off-micro-volts = <0>;
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 5/8] ARM: dts: OMAP4: add voltage processor nodes
  2013-06-21 21:25 ` Nishanth Menon
  (?)
@ 2013-06-21 21:25   ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

OMAP443x, OMAP446x SoC use same offsets for voltage processor,
however their voltage characteristics differ a little.

Introduce the voltage processor nodes for the same.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4.dtsi    |   43 +++++++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/omap443x.dtsi |   15 ++++++++++++++
 arch/arm/boot/dts/omap4460.dtsi |   15 ++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index a153e8d..3c75b23 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -689,5 +689,48 @@
 				compatible = "ti,omap4-vc-channel-core";
 			};
 		};
+
+		vp_mpu: vp@0x4A307B58 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307b58 0x18>, <0x4A306014 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x20>;
+
+			clocks = <&sysclk_in>;
+
+			ti,vc-channel = <&vc_mpu>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
+
+		vp_iva: vp@0x4A307B70 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307B70 0x18>, <0x4A306010 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x20000000>;
+
+			clocks = <&sysclk_in>;
+
+			ti,vc-channel = <&vc_iva>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
+
+		vp_core: vp@0x4A307B40 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307b40 0x18>, <0x4A306010 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x200000>;
+
+			clocks = <&sysclk_in>;
+
+			regulator_name = "vdd_core";
+			ti,vc-channel = <&vc_core>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index 2b0deb5..e759937 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -46,3 +46,18 @@
 	ti,retention-micro-volts = <750000>;
 	ti,off-micro-volts = <0>;
 };
+
+&vp_mpu {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1388000>;
+};
+
+&vp_iva {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1291000>;
+};
+
+&vp_core {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1127000>;
+};
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index 16210a1..8320865 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -55,3 +55,18 @@
 	ti,retention-micro-volts = <750000>;
 	ti,off-micro-volts = <0>;
 };
+
+&vp_mpu {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1380000>;
+};
+
+&vp_iva {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1375000>;
+};
+
+&vp_core {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1250000>;
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 5/8] ARM: dts: OMAP4: add voltage processor nodes
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

OMAP443x, OMAP446x SoC use same offsets for voltage processor,
however their voltage characteristics differ a little.

Introduce the voltage processor nodes for the same.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4.dtsi    |   43 +++++++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/omap443x.dtsi |   15 ++++++++++++++
 arch/arm/boot/dts/omap4460.dtsi |   15 ++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index a153e8d..3c75b23 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -689,5 +689,48 @@
 				compatible = "ti,omap4-vc-channel-core";
 			};
 		};
+
+		vp_mpu: vp@0x4A307B58 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307b58 0x18>, <0x4A306014 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x20>;
+
+			clocks = <&sysclk_in>;
+
+			ti,vc-channel = <&vc_mpu>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
+
+		vp_iva: vp@0x4A307B70 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307B70 0x18>, <0x4A306010 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x20000000>;
+
+			clocks = <&sysclk_in>;
+
+			ti,vc-channel = <&vc_iva>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
+
+		vp_core: vp@0x4A307B40 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307b40 0x18>, <0x4A306010 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x200000>;
+
+			clocks = <&sysclk_in>;
+
+			regulator_name = "vdd_core";
+			ti,vc-channel = <&vc_core>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index 2b0deb5..e759937 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -46,3 +46,18 @@
 	ti,retention-micro-volts = <750000>;
 	ti,off-micro-volts = <0>;
 };
+
+&vp_mpu {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1388000>;
+};
+
+&vp_iva {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1291000>;
+};
+
+&vp_core {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1127000>;
+};
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index 16210a1..8320865 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -55,3 +55,18 @@
 	ti,retention-micro-volts = <750000>;
 	ti,off-micro-volts = <0>;
 };
+
+&vp_mpu {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1380000>;
+};
+
+&vp_iva {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1375000>;
+};
+
+&vp_core {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1250000>;
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 5/8] ARM: dts: OMAP4: add voltage processor nodes
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

OMAP443x, OMAP446x SoC use same offsets for voltage processor,
however their voltage characteristics differ a little.

Introduce the voltage processor nodes for the same.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4.dtsi    |   43 +++++++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/omap443x.dtsi |   15 ++++++++++++++
 arch/arm/boot/dts/omap4460.dtsi |   15 ++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index a153e8d..3c75b23 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -689,5 +689,48 @@
 				compatible = "ti,omap4-vc-channel-core";
 			};
 		};
+
+		vp_mpu: vp at 0x4A307B58 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307b58 0x18>, <0x4A306014 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x20>;
+
+			clocks = <&sysclk_in>;
+
+			ti,vc-channel = <&vc_mpu>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
+
+		vp_iva: vp at 0x4A307B70 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307B70 0x18>, <0x4A306010 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x20000000>;
+
+			clocks = <&sysclk_in>;
+
+			ti,vc-channel = <&vc_iva>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
+
+		vp_core: vp at 0x4A307B40 {
+			compatible = "ti,omap4-vp";
+
+			reg = <0x4A307b40 0x18>, <0x4A306010 0x4>;
+			reg-names = "base-address", "int-address";
+			ti,tranxdone-status-mask=<0x200000>;
+
+			clocks = <&sysclk_in>;
+
+			regulator_name = "vdd_core";
+			ti,vc-channel = <&vc_core>;
+			ti,min-step-micro-volts = <10000>;
+			ti,max-step-micro-volts = <50000>;
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index 2b0deb5..e759937 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -46,3 +46,18 @@
 	ti,retention-micro-volts = <750000>;
 	ti,off-micro-volts = <0>;
 };
+
+&vp_mpu {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1388000>;
+};
+
+&vp_iva {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1291000>;
+};
+
+&vp_core {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1127000>;
+};
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index 16210a1..8320865 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -55,3 +55,18 @@
 	ti,retention-micro-volts = <750000>;
 	ti,off-micro-volts = <0>;
 };
+
+&vp_mpu {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1380000>;
+};
+
+&vp_iva {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1375000>;
+};
+
+&vp_core {
+	ti,min-micro-volts = <750000>;
+	ti,max-micro-volts = <1250000>;
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 6/8] ARM: dts: TWL6030/OMAP4: Add OMAP voltage path linkage
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

OMAP4430 and 4460 platforms use TWL6030 to power voltage rails.
However, on 4460, we use twl6030 only for iva and core voltage rails.

Introduce an twl6030_omap4.dtsi to be able to reuse the base definition
for all OMAP4 platforms and the delta change only for panda-es platform
which is based on 4460.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4-panda-common.dtsi |    1 +
 arch/arm/boot/dts/omap4-panda-es.dts      |   13 +++++++++++
 arch/arm/boot/dts/omap4-sdp.dts           |    1 +
 arch/arm/boot/dts/omap4-var-som.dts       |    1 +
 arch/arm/boot/dts/twl6030_omap4.dtsi      |   35 +++++++++++++++++++++++++++++
 5 files changed, 51 insertions(+)
 create mode 100644 arch/arm/boot/dts/twl6030_omap4.dtsi

diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
index 00cbaa5..2b15c2b 100644
--- a/arch/arm/boot/dts/omap4-panda-common.dtsi
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -199,6 +199,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 56c4354..49017c5 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -62,3 +62,16 @@
 		gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
 	};
 };
+
+&omap_twl6030_vcore1 {
+	ti,vp = <&vp_core>;
+};
+
+&omap_twl6030_vcore2 {
+	ti,vp = <&vp_iva>;
+};
+
+&omap_twl6030_vcore3 {
+	/* We use TPS62361 on this platform instead */
+	status = "disabled";
+};
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 7951b4e..2f81b4d5 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -337,6 +337,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/omap4-var-som.dts b/arch/arm/boot/dts/omap4-var-som.dts
index b41269e..6f0b763 100644
--- a/arch/arm/boot/dts/omap4-var-som.dts
+++ b/arch/arm/boot/dts/omap4-var-som.dts
@@ -40,6 +40,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	clock-frequency = <400000>;
diff --git a/arch/arm/boot/dts/twl6030_omap4.dtsi b/arch/arm/boot/dts/twl6030_omap4.dtsi
new file mode 100644
index 0000000..7724039
--- /dev/null
+++ b/arch/arm/boot/dts/twl6030_omap4.dtsi
@@ -0,0 +1,35 @@
+/*
+ * TWL6030 entries specific for OMAP
+ *
+ * Copyright (C) 2013 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.
+ */
+
+&twl {
+	omap_twl6030_vcore1: vcore1 {
+		compatible = "ti,omap-twl6030-vcore1";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_mpu>;
+	};
+
+	omap_twl6030_vcore2: vcore2 {
+		compatible = "ti,omap-twl6030-vcore2";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_iva>;
+	};
+
+	omap_twl6030_vcore3: vcore3 {
+		compatible = "ti,omap-twl6030-vcore3";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_core>;
+	};
+};
+
+&vc {
+	ti,i2c-high-speed;
+	ti,i2c-pad-load = <3>;
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 6/8] ARM: dts: TWL6030/OMAP4: Add OMAP voltage path linkage
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Nishanth Menon, linux-doc-u79uwXL29TY76Z2rM5mHXA, Kevin Hilman,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

OMAP4430 and 4460 platforms use TWL6030 to power voltage rails.
However, on 4460, we use twl6030 only for iva and core voltage rails.

Introduce an twl6030_omap4.dtsi to be able to reuse the base definition
for all OMAP4 platforms and the delta change only for panda-es platform
which is based on 4460.

Signed-off-by: Nishanth Menon <nm-l0cyMroinI0@public.gmane.org>
---
 arch/arm/boot/dts/omap4-panda-common.dtsi |    1 +
 arch/arm/boot/dts/omap4-panda-es.dts      |   13 +++++++++++
 arch/arm/boot/dts/omap4-sdp.dts           |    1 +
 arch/arm/boot/dts/omap4-var-som.dts       |    1 +
 arch/arm/boot/dts/twl6030_omap4.dtsi      |   35 +++++++++++++++++++++++++++++
 5 files changed, 51 insertions(+)
 create mode 100644 arch/arm/boot/dts/twl6030_omap4.dtsi

diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
index 00cbaa5..2b15c2b 100644
--- a/arch/arm/boot/dts/omap4-panda-common.dtsi
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -199,6 +199,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 56c4354..49017c5 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -62,3 +62,16 @@
 		gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
 	};
 };
+
+&omap_twl6030_vcore1 {
+	ti,vp = <&vp_core>;
+};
+
+&omap_twl6030_vcore2 {
+	ti,vp = <&vp_iva>;
+};
+
+&omap_twl6030_vcore3 {
+	/* We use TPS62361 on this platform instead */
+	status = "disabled";
+};
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 7951b4e..2f81b4d5 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -337,6 +337,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/omap4-var-som.dts b/arch/arm/boot/dts/omap4-var-som.dts
index b41269e..6f0b763 100644
--- a/arch/arm/boot/dts/omap4-var-som.dts
+++ b/arch/arm/boot/dts/omap4-var-som.dts
@@ -40,6 +40,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	clock-frequency = <400000>;
diff --git a/arch/arm/boot/dts/twl6030_omap4.dtsi b/arch/arm/boot/dts/twl6030_omap4.dtsi
new file mode 100644
index 0000000..7724039
--- /dev/null
+++ b/arch/arm/boot/dts/twl6030_omap4.dtsi
@@ -0,0 +1,35 @@
+/*
+ * TWL6030 entries specific for OMAP
+ *
+ * Copyright (C) 2013 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.
+ */
+
+&twl {
+	omap_twl6030_vcore1: vcore1 {
+		compatible = "ti,omap-twl6030-vcore1";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_mpu>;
+	};
+
+	omap_twl6030_vcore2: vcore2 {
+		compatible = "ti,omap-twl6030-vcore2";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_iva>;
+	};
+
+	omap_twl6030_vcore3: vcore3 {
+		compatible = "ti,omap-twl6030-vcore3";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_core>;
+	};
+};
+
+&vc {
+	ti,i2c-high-speed;
+	ti,i2c-pad-load = <3>;
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 6/8] ARM: dts: TWL6030/OMAP4: Add OMAP voltage path linkage
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

OMAP4430 and 4460 platforms use TWL6030 to power voltage rails.
However, on 4460, we use twl6030 only for iva and core voltage rails.

Introduce an twl6030_omap4.dtsi to be able to reuse the base definition
for all OMAP4 platforms and the delta change only for panda-es platform
which is based on 4460.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4-panda-common.dtsi |    1 +
 arch/arm/boot/dts/omap4-panda-es.dts      |   13 +++++++++++
 arch/arm/boot/dts/omap4-sdp.dts           |    1 +
 arch/arm/boot/dts/omap4-var-som.dts       |    1 +
 arch/arm/boot/dts/twl6030_omap4.dtsi      |   35 +++++++++++++++++++++++++++++
 5 files changed, 51 insertions(+)
 create mode 100644 arch/arm/boot/dts/twl6030_omap4.dtsi

diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
index 00cbaa5..2b15c2b 100644
--- a/arch/arm/boot/dts/omap4-panda-common.dtsi
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -199,6 +199,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 56c4354..49017c5 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -62,3 +62,16 @@
 		gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
 	};
 };
+
+&omap_twl6030_vcore1 {
+	ti,vp = <&vp_core>;
+};
+
+&omap_twl6030_vcore2 {
+	ti,vp = <&vp_iva>;
+};
+
+&omap_twl6030_vcore3 {
+	/* We use TPS62361 on this platform instead */
+	status = "disabled";
+};
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 7951b4e..2f81b4d5 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -337,6 +337,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/omap4-var-som.dts b/arch/arm/boot/dts/omap4-var-som.dts
index b41269e..6f0b763 100644
--- a/arch/arm/boot/dts/omap4-var-som.dts
+++ b/arch/arm/boot/dts/omap4-var-som.dts
@@ -40,6 +40,7 @@
 };
 
 #include "twl6030.dtsi"
+#include "twl6030_omap4.dtsi"
 
 &i2c2 {
 	clock-frequency = <400000>;
diff --git a/arch/arm/boot/dts/twl6030_omap4.dtsi b/arch/arm/boot/dts/twl6030_omap4.dtsi
new file mode 100644
index 0000000..7724039
--- /dev/null
+++ b/arch/arm/boot/dts/twl6030_omap4.dtsi
@@ -0,0 +1,35 @@
+/*
+ * TWL6030 entries specific for OMAP
+ *
+ * Copyright (C) 2013 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.
+ */
+
+&twl {
+	omap_twl6030_vcore1: vcore1 {
+		compatible = "ti,omap-twl6030-vcore1";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_mpu>;
+	};
+
+	omap_twl6030_vcore2: vcore2 {
+		compatible = "ti,omap-twl6030-vcore2";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_iva>;
+	};
+
+	omap_twl6030_vcore3: vcore3 {
+		compatible = "ti,omap-twl6030-vcore3";
+		ti,boot-voltage-micro-volts = <1200000>;
+		ti,vp = <&vp_core>;
+	};
+};
+
+&vc {
+	ti,i2c-high-speed;
+	ti,i2c-pad-load = <3>;
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 7/8] ARM: dts: omap4-panda-es: add TPS62361 supply for vdd_mpu
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

Unlike other OMAP4 platforms including the the "vanilla" PandaBoard,
PandaBoard-ES uses TPS62361 to supply vdd_mpu.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4-panda-es.dts |   18 ++++++++++++++++++
 arch/arm/boot/dts/tps62361_omap.dtsi |   18 ++++++++++++++++++
 2 files changed, 36 insertions(+)
 create mode 100644 arch/arm/boot/dts/tps62361_omap.dtsi

diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 49017c5..325816c 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -75,3 +75,21 @@
 	/* We use TPS62361 on this platform instead */
 	status = "disabled";
 };
+
+#include "tps62361_omap.dtsi"
+
+&omap4_pmx_wkup {
+	tps62361_wkgpio_pins: pinmux_tps62361_wkpins {
+		pinctrl-single,pins = <
+			0x1a (PIN_OUTPUT | MUX_MODE3 | OFF_EN | OFFOUT_VAL) /* gpio_wk7 */
+		>;
+	};
+};
+
+&omap_tps62361 {
+	pinctrl-names = "default";
+	pinctrl-0 = <
+		&tps62361_wkgpio_pins
+	>;
+	gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+};
diff --git a/arch/arm/boot/dts/tps62361_omap.dtsi b/arch/arm/boot/dts/tps62361_omap.dtsi
new file mode 100644
index 0000000..ee261ec
--- /dev/null
+++ b/arch/arm/boot/dts/tps62361_omap.dtsi
@@ -0,0 +1,18 @@
+/*
+ * TPS62361 entries specific for OMAP
+ *
+ * Copyright (C) 2013 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.
+ */
+
+/ {
+	omap_tps62361: tps62361 {
+		compatible = "ti,omap-tps62361";
+		ti,boot-voltage-micro-volts = <1203000>;
+		ti,vp = <&vp_mpu>;
+	};
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 7/8] ARM: dts: omap4-panda-es: add TPS62361 supply for vdd_mpu
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Nishanth Menon, linux-doc-u79uwXL29TY76Z2rM5mHXA, Kevin Hilman,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Unlike other OMAP4 platforms including the the "vanilla" PandaBoard,
PandaBoard-ES uses TPS62361 to supply vdd_mpu.

Signed-off-by: Nishanth Menon <nm-l0cyMroinI0@public.gmane.org>
---
 arch/arm/boot/dts/omap4-panda-es.dts |   18 ++++++++++++++++++
 arch/arm/boot/dts/tps62361_omap.dtsi |   18 ++++++++++++++++++
 2 files changed, 36 insertions(+)
 create mode 100644 arch/arm/boot/dts/tps62361_omap.dtsi

diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 49017c5..325816c 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -75,3 +75,21 @@
 	/* We use TPS62361 on this platform instead */
 	status = "disabled";
 };
+
+#include "tps62361_omap.dtsi"
+
+&omap4_pmx_wkup {
+	tps62361_wkgpio_pins: pinmux_tps62361_wkpins {
+		pinctrl-single,pins = <
+			0x1a (PIN_OUTPUT | MUX_MODE3 | OFF_EN | OFFOUT_VAL) /* gpio_wk7 */
+		>;
+	};
+};
+
+&omap_tps62361 {
+	pinctrl-names = "default";
+	pinctrl-0 = <
+		&tps62361_wkgpio_pins
+	>;
+	gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+};
diff --git a/arch/arm/boot/dts/tps62361_omap.dtsi b/arch/arm/boot/dts/tps62361_omap.dtsi
new file mode 100644
index 0000000..ee261ec
--- /dev/null
+++ b/arch/arm/boot/dts/tps62361_omap.dtsi
@@ -0,0 +1,18 @@
+/*
+ * TPS62361 entries specific for OMAP
+ *
+ * Copyright (C) 2013 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.
+ */
+
+/ {
+	omap_tps62361: tps62361 {
+		compatible = "ti,omap-tps62361";
+		ti,boot-voltage-micro-volts = <1203000>;
+		ti,vp = <&vp_mpu>;
+	};
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 7/8] ARM: dts: omap4-panda-es: add TPS62361 supply for vdd_mpu
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

Unlike other OMAP4 platforms including the the "vanilla" PandaBoard,
PandaBoard-ES uses TPS62361 to supply vdd_mpu.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4-panda-es.dts |   18 ++++++++++++++++++
 arch/arm/boot/dts/tps62361_omap.dtsi |   18 ++++++++++++++++++
 2 files changed, 36 insertions(+)
 create mode 100644 arch/arm/boot/dts/tps62361_omap.dtsi

diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 49017c5..325816c 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -75,3 +75,21 @@
 	/* We use TPS62361 on this platform instead */
 	status = "disabled";
 };
+
+#include "tps62361_omap.dtsi"
+
+&omap4_pmx_wkup {
+	tps62361_wkgpio_pins: pinmux_tps62361_wkpins {
+		pinctrl-single,pins = <
+			0x1a (PIN_OUTPUT | MUX_MODE3 | OFF_EN | OFFOUT_VAL) /* gpio_wk7 */
+		>;
+	};
+};
+
+&omap_tps62361 {
+	pinctrl-names = "default";
+	pinctrl-0 = <
+		&tps62361_wkgpio_pins
+	>;
+	gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+};
diff --git a/arch/arm/boot/dts/tps62361_omap.dtsi b/arch/arm/boot/dts/tps62361_omap.dtsi
new file mode 100644
index 0000000..ee261ec
--- /dev/null
+++ b/arch/arm/boot/dts/tps62361_omap.dtsi
@@ -0,0 +1,18 @@
+/*
+ * TPS62361 entries specific for OMAP
+ *
+ * Copyright (C) 2013 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.
+ */
+
+/ {
+	omap_tps62361: tps62361 {
+		compatible = "ti,omap-tps62361";
+		ti,boot-voltage-micro-volts = <1203000>;
+		ti,vp = <&vp_mpu>;
+	};
+};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 8/8] ARM: dts: OMAP4-panda-es: use tps regulator as cpu0 supply
  2013-06-21 21:25 ` Nishanth Menon
  (?)
@ 2013-06-21 21:25   ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

TPS62361 is used for cpufreq regulation. As part of this change
use labels that can be used to reference cpu0 to setup it's supply.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4-panda-es.dts |    4 ++++
 arch/arm/boot/dts/omap4.dtsi         |    4 ++--
 arch/arm/boot/dts/omap443x.dtsi      |    2 +-
 arch/arm/boot/dts/omap4460.dtsi      |    2 +-
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 325816c..d2ce518 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -93,3 +93,7 @@
 	>;
 	gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
 };
+
+&cpu0 {
+	cpu0-supply = <&omap_tps62361>;
+};
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 3c75b23..127b974 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -24,13 +24,13 @@
 	};
 
 	cpus {
-		cpu@0 {
+		cpu0: cpu@0 {
 			compatible = "arm,cortex-a9";
 			next-level-cache = <&L2>;
 			clocks = <&dpll_mpu>;
 			clock-names = "cpu";
 		};
-		cpu@1 {
+		cpu1: cpu@1 {
 			compatible = "arm,cortex-a9";
 			next-level-cache = <&L2>;
 		};
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index e759937..91a5f70 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -12,7 +12,7 @@
 
 / {
 	cpus {
-		cpu@0 {
+		cpu0: cpu@0 {
 			/* OMAP443x variants OPP50-OPPNT */
 			operating-points = <
 				/* kHz    uV */
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index 8320865..9827e35 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -12,7 +12,7 @@
 / {
 	cpus {
 		/* OMAP446x 'standard device' variants OPP50 to OPPTurbo */
-		cpu@0 {
+		cpu0: cpu@0 {
 			operating-points = <
 				/* kHz    uV */
 				350000  1025000
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 8/8] ARM: dts: OMAP4-panda-es: use tps regulator as cpu0 supply
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: Benoît Cousson, Mark Brown, Tony Lindgren
  Cc: Kevin Hilman, devicetree-discuss, linux-doc, linux-kernel,
	linux-omap, linux-arm-kernel, Nishanth Menon

TPS62361 is used for cpufreq regulation. As part of this change
use labels that can be used to reference cpu0 to setup it's supply.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4-panda-es.dts |    4 ++++
 arch/arm/boot/dts/omap4.dtsi         |    4 ++--
 arch/arm/boot/dts/omap443x.dtsi      |    2 +-
 arch/arm/boot/dts/omap4460.dtsi      |    2 +-
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 325816c..d2ce518 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -93,3 +93,7 @@
 	>;
 	gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
 };
+
+&cpu0 {
+	cpu0-supply = <&omap_tps62361>;
+};
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 3c75b23..127b974 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -24,13 +24,13 @@
 	};
 
 	cpus {
-		cpu@0 {
+		cpu0: cpu@0 {
 			compatible = "arm,cortex-a9";
 			next-level-cache = <&L2>;
 			clocks = <&dpll_mpu>;
 			clock-names = "cpu";
 		};
-		cpu@1 {
+		cpu1: cpu@1 {
 			compatible = "arm,cortex-a9";
 			next-level-cache = <&L2>;
 		};
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index e759937..91a5f70 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -12,7 +12,7 @@
 
 / {
 	cpus {
-		cpu@0 {
+		cpu0: cpu@0 {
 			/* OMAP443x variants OPP50-OPPNT */
 			operating-points = <
 				/* kHz    uV */
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index 8320865..9827e35 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -12,7 +12,7 @@
 / {
 	cpus {
 		/* OMAP446x 'standard device' variants OPP50 to OPPTurbo */
-		cpu@0 {
+		cpu0: cpu@0 {
 			operating-points = <
 				/* kHz    uV */
 				350000  1025000
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 8/8] ARM: dts: OMAP4-panda-es: use tps regulator as cpu0 supply
@ 2013-06-21 21:25   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-06-21 21:25 UTC (permalink / raw)
  To: linux-arm-kernel

TPS62361 is used for cpufreq regulation. As part of this change
use labels that can be used to reference cpu0 to setup it's supply.

Signed-off-by: Nishanth Menon <nm@ti.com>
---
 arch/arm/boot/dts/omap4-panda-es.dts |    4 ++++
 arch/arm/boot/dts/omap4.dtsi         |    4 ++--
 arch/arm/boot/dts/omap443x.dtsi      |    2 +-
 arch/arm/boot/dts/omap4460.dtsi      |    2 +-
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
index 325816c..d2ce518 100644
--- a/arch/arm/boot/dts/omap4-panda-es.dts
+++ b/arch/arm/boot/dts/omap4-panda-es.dts
@@ -93,3 +93,7 @@
 	>;
 	gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
 };
+
+&cpu0 {
+	cpu0-supply = <&omap_tps62361>;
+};
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 3c75b23..127b974 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -24,13 +24,13 @@
 	};
 
 	cpus {
-		cpu at 0 {
+		cpu0: cpu at 0 {
 			compatible = "arm,cortex-a9";
 			next-level-cache = <&L2>;
 			clocks = <&dpll_mpu>;
 			clock-names = "cpu";
 		};
-		cpu at 1 {
+		cpu1: cpu at 1 {
 			compatible = "arm,cortex-a9";
 			next-level-cache = <&L2>;
 		};
diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi
index e759937..91a5f70 100644
--- a/arch/arm/boot/dts/omap443x.dtsi
+++ b/arch/arm/boot/dts/omap443x.dtsi
@@ -12,7 +12,7 @@
 
 / {
 	cpus {
-		cpu at 0 {
+		cpu0: cpu at 0 {
 			/* OMAP443x variants OPP50-OPPNT */
 			operating-points = <
 				/* kHz    uV */
diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
index 8320865..9827e35 100644
--- a/arch/arm/boot/dts/omap4460.dtsi
+++ b/arch/arm/boot/dts/omap4460.dtsi
@@ -12,7 +12,7 @@
 / {
 	cpus {
 		/* OMAP446x 'standard device' variants OPP50 to OPPTurbo */
-		cpu at 0 {
+		cpu0: cpu@0 {
 			operating-points = <
 				/* kHz    uV */
 				350000  1025000
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-04 15:41     ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-04 15:41 UTC (permalink / raw)
  To: Nishanth Menon
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

[-- Attachment #1: Type: text/plain, Size: 1002 bytes --]

On Fri, Jun 21, 2013 at 04:25:42PM -0500, Nishanth Menon wrote:

> +static const struct omap_pmic_info omap_twl4030_vdd1 = {
> +	.slave_addr = 0x12,
> +	.voltage_reg_addr = 0x00,
> +	.cmd_reg_addr = 0x00,
> +	.i2c_timeout_us = 200,
> +	.slew_rate_uV = 4000,
> +	.step_size_uV = 12500,
> +	.min_uV = 600000,
> +	.max_uV = 1450000,
> +	.voltage_selector_offset = 0,
> +	.voltage_selector_mask = 0x7F,
> +	.voltage_selector_setbits = 0x0,
> +	.voltage_selector_zero = false,
> +};

So, this still has the thing where all the data about the PMIC is
replicated (but now in this driver).  It should be possible to pull all
the above information except possibly the I2C timeout and perhaps the
I2C address (if there's a separate control interface) from the standard
regulator core data structures for the PMIC.  This would allow the
driver to Just Work with any PMIC without needing to add it in two
places.

Other than that this looks good, the only issue I see is where the
driver is getting the data from.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-04 15:41     ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-04 15:41 UTC (permalink / raw)
  To: Nishanth Menon
  Cc: Grygorii Strashko, linux-doc-u79uwXL29TY76Z2rM5mHXA,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Taras Kondratiuk,
	Kevin Hilman, linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r


[-- Attachment #1.1: Type: text/plain, Size: 1002 bytes --]

On Fri, Jun 21, 2013 at 04:25:42PM -0500, Nishanth Menon wrote:

> +static const struct omap_pmic_info omap_twl4030_vdd1 = {
> +	.slave_addr = 0x12,
> +	.voltage_reg_addr = 0x00,
> +	.cmd_reg_addr = 0x00,
> +	.i2c_timeout_us = 200,
> +	.slew_rate_uV = 4000,
> +	.step_size_uV = 12500,
> +	.min_uV = 600000,
> +	.max_uV = 1450000,
> +	.voltage_selector_offset = 0,
> +	.voltage_selector_mask = 0x7F,
> +	.voltage_selector_setbits = 0x0,
> +	.voltage_selector_zero = false,
> +};

So, this still has the thing where all the data about the PMIC is
replicated (but now in this driver).  It should be possible to pull all
the above information except possibly the I2C timeout and perhaps the
I2C address (if there's a separate control interface) from the standard
regulator core data structures for the PMIC.  This would allow the
driver to Just Work with any PMIC without needing to add it in two
places.

Other than that this looks good, the only issue I see is where the
driver is getting the data from.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 192 bytes --]

_______________________________________________
devicetree-discuss mailing list
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
https://lists.ozlabs.org/listinfo/devicetree-discuss

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-04 15:41     ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-04 15:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jun 21, 2013 at 04:25:42PM -0500, Nishanth Menon wrote:

> +static const struct omap_pmic_info omap_twl4030_vdd1 = {
> +	.slave_addr = 0x12,
> +	.voltage_reg_addr = 0x00,
> +	.cmd_reg_addr = 0x00,
> +	.i2c_timeout_us = 200,
> +	.slew_rate_uV = 4000,
> +	.step_size_uV = 12500,
> +	.min_uV = 600000,
> +	.max_uV = 1450000,
> +	.voltage_selector_offset = 0,
> +	.voltage_selector_mask = 0x7F,
> +	.voltage_selector_setbits = 0x0,
> +	.voltage_selector_zero = false,
> +};

So, this still has the thing where all the data about the PMIC is
replicated (but now in this driver).  It should be possible to pull all
the above information except possibly the I2C timeout and perhaps the
I2C address (if there's a separate control interface) from the standard
regulator core data structures for the PMIC.  This would allow the
driver to Just Work with any PMIC without needing to add it in two
places.

Other than that this looks good, the only issue I see is where the
driver is getting the data from.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130704/f4329a32/attachment.sig>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-04 15:41     ` Mark Brown
@ 2013-07-05 13:55       ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-05 13:55 UTC (permalink / raw)
  To: Mark Brown
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

On 16:41-20130704, Mark Brown wrote:
> On Fri, Jun 21, 2013 at 04:25:42PM -0500, Nishanth Menon wrote:
> 
> > +static const struct omap_pmic_info omap_twl4030_vdd1 = {
> > +	.slave_addr = 0x12,
> > +	.voltage_reg_addr = 0x00,
> > +	.cmd_reg_addr = 0x00,
> > +	.i2c_timeout_us = 200,
> > +	.slew_rate_uV = 4000,
> > +	.step_size_uV = 12500,
> > +	.min_uV = 600000,
> > +	.max_uV = 1450000,
> > +	.voltage_selector_offset = 0,
> > +	.voltage_selector_mask = 0x7F,
> > +	.voltage_selector_setbits = 0x0,
> > +	.voltage_selector_zero = false,
> > +};
> 
> So, this still has the thing where all the data about the PMIC is
> replicated (but now in this driver).  It should be possible to pull all
> the above information except possibly the I2C timeout and perhaps the
> I2C address (if there's a separate control interface) from the standard
> regulator core data structures for the PMIC.  This would allow the
> driver to Just Work with any PMIC without needing to add it in two
> places.
> 
> Other than that this looks good, the only issue I see is where the
> driver is getting the data from.
I like the idea and had tried to implement it as well..lets get down to
the details: If I understand what you are stating,
regulators such as twl-regulator has the same/similar data that is
represented here. twl-regulator uses standard i2c path, it cannot
send voltage over so called "SR path". Alrite, so, lets say we reuse
definition of VDD1,
option 1) we just bypass get_voltage/set_voltage to point through to
this function. result will be something similar to what we got here[1]
Again, based on this discussion, it is wrong - and we already did implement
the *wrong* way in OMAP and the new code is supposed to throw away the
old code once all relevant platforms are converted to DT.
- now, we could improve this by passing rdev and slurp out required
 data from regulator structures, but obviously, as you pointed out, it wont
 be sufficient.
- In this solution, we will have existing regulator drivers supporting
additional data path. *but* that also means that existing regulator
drivers will need to be modified to handle multiple data path and "Just
Work" wont happen - so not really good other than screw up existing
regulator drivers with handling OMAP specific APIs in them.

option 2) make omap_pmic regulator use twl_regulator - regulator
chaining.
 - we already agreed that this is conceptually wrong[2](regulator
   talking to another regulator). So wont debate more here.
 - but here, you'd have omap_pmic regulator "using twl/existing
 regulators" to pick up data about slew, conversion information etc..

This series attempts to keep omap pmic delta simplified to just the
additional PMIC data, no replication of code or "OMAP specific
infection" of existing generic regulators.

Is there any other suggestions how to pick up the data from an existing
regulator and avoid few bytes of data replication?

[1]
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/arm/mach-omap2/omap_twl.c#n142
[2] http://marc.info/?t=136513865200005&r=1&w=2
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-05 13:55       ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-05 13:55 UTC (permalink / raw)
  To: linux-arm-kernel

On 16:41-20130704, Mark Brown wrote:
> On Fri, Jun 21, 2013 at 04:25:42PM -0500, Nishanth Menon wrote:
> 
> > +static const struct omap_pmic_info omap_twl4030_vdd1 = {
> > +	.slave_addr = 0x12,
> > +	.voltage_reg_addr = 0x00,
> > +	.cmd_reg_addr = 0x00,
> > +	.i2c_timeout_us = 200,
> > +	.slew_rate_uV = 4000,
> > +	.step_size_uV = 12500,
> > +	.min_uV = 600000,
> > +	.max_uV = 1450000,
> > +	.voltage_selector_offset = 0,
> > +	.voltage_selector_mask = 0x7F,
> > +	.voltage_selector_setbits = 0x0,
> > +	.voltage_selector_zero = false,
> > +};
> 
> So, this still has the thing where all the data about the PMIC is
> replicated (but now in this driver).  It should be possible to pull all
> the above information except possibly the I2C timeout and perhaps the
> I2C address (if there's a separate control interface) from the standard
> regulator core data structures for the PMIC.  This would allow the
> driver to Just Work with any PMIC without needing to add it in two
> places.
> 
> Other than that this looks good, the only issue I see is where the
> driver is getting the data from.
I like the idea and had tried to implement it as well..lets get down to
the details: If I understand what you are stating,
regulators such as twl-regulator has the same/similar data that is
represented here. twl-regulator uses standard i2c path, it cannot
send voltage over so called "SR path". Alrite, so, lets say we reuse
definition of VDD1,
option 1) we just bypass get_voltage/set_voltage to point through to
this function. result will be something similar to what we got here[1]
Again, based on this discussion, it is wrong - and we already did implement
the *wrong* way in OMAP and the new code is supposed to throw away the
old code once all relevant platforms are converted to DT.
- now, we could improve this by passing rdev and slurp out required
 data from regulator structures, but obviously, as you pointed out, it wont
 be sufficient.
- In this solution, we will have existing regulator drivers supporting
additional data path. *but* that also means that existing regulator
drivers will need to be modified to handle multiple data path and "Just
Work" wont happen - so not really good other than screw up existing
regulator drivers with handling OMAP specific APIs in them.

option 2) make omap_pmic regulator use twl_regulator - regulator
chaining.
 - we already agreed that this is conceptually wrong[2](regulator
   talking to another regulator). So wont debate more here.
 - but here, you'd have omap_pmic regulator "using twl/existing
 regulators" to pick up data about slew, conversion information etc..

This series attempts to keep omap pmic delta simplified to just the
additional PMIC data, no replication of code or "OMAP specific
infection" of existing generic regulators.

Is there any other suggestions how to pick up the data from an existing
regulator and avoid few bytes of data replication?

[1]
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/arm/mach-omap2/omap_twl.c#n142
[2] http://marc.info/?t=136513865200005&r=1&w=2
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-05 13:55       ` Nishanth Menon
@ 2013-07-05 14:08         ` Mark Brown
  -1 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-05 14:08 UTC (permalink / raw)
  To: Nishanth Menon
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

[-- Attachment #1: Type: text/plain, Size: 2116 bytes --]

On Fri, Jul 05, 2013 at 08:55:07AM -0500, Nishanth Menon wrote:

Please write in paragraphs, an enormous wall of unbroken text isn't
helpful for legibility.

> On 16:41-20130704, Mark Brown wrote:

> > So, this still has the thing where all the data about the PMIC is
> > replicated (but now in this driver).  It should be possible to pull all
> > the above information except possibly the I2C timeout and perhaps the
> > I2C address (if there's a separate control interface) from the standard
> > regulator core data structures for the PMIC.  This would allow the
> > driver to Just Work with any PMIC without needing to add it in two
> > places.

> option 1) we just bypass get_voltage/set_voltage to point through to
> this function. result will be something similar to what we got here[1]

I don't really know what you mean when you say "bypass get_voltage/set_voltage
so it's kind of hard to comment...  the link you posted appears to be a
link to the code I'm reviewing so it's not terribly enlightening.

> Again, based on this discussion, it is wrong - and we already did implement
> the *wrong* way in OMAP and the new code is supposed to throw away the
> old code once all relevant platforms are converted to DT.
> - now, we could improve this by passing rdev and slurp out required
>  data from regulator structures, but obviously, as you pointed out, it wont
>  be sufficient.
> - In this solution, we will have existing regulator drivers supporting
> additional data path. *but* that also means that existing regulator
> drivers will need to be modified to handle multiple data path and "Just
> Work" wont happen - so not really good other than screw up existing
> regulator drivers with handling OMAP specific APIs in them.

What makes you think that the existing regulator drivers need to be
modified?  They already have all the data exported to the core (or
should do)...  the only thing that's missing is the timeouts and it's
not at all obvious why those need to be tuned per device.

> option 2) make omap_pmic regulator use twl_regulator - regulator
> chaining.

Again I don't know what this is.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-05 14:08         ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-05 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 05, 2013 at 08:55:07AM -0500, Nishanth Menon wrote:

Please write in paragraphs, an enormous wall of unbroken text isn't
helpful for legibility.

> On 16:41-20130704, Mark Brown wrote:

> > So, this still has the thing where all the data about the PMIC is
> > replicated (but now in this driver).  It should be possible to pull all
> > the above information except possibly the I2C timeout and perhaps the
> > I2C address (if there's a separate control interface) from the standard
> > regulator core data structures for the PMIC.  This would allow the
> > driver to Just Work with any PMIC without needing to add it in two
> > places.

> option 1) we just bypass get_voltage/set_voltage to point through to
> this function. result will be something similar to what we got here[1]

I don't really know what you mean when you say "bypass get_voltage/set_voltage
so it's kind of hard to comment...  the link you posted appears to be a
link to the code I'm reviewing so it's not terribly enlightening.

> Again, based on this discussion, it is wrong - and we already did implement
> the *wrong* way in OMAP and the new code is supposed to throw away the
> old code once all relevant platforms are converted to DT.
> - now, we could improve this by passing rdev and slurp out required
>  data from regulator structures, but obviously, as you pointed out, it wont
>  be sufficient.
> - In this solution, we will have existing regulator drivers supporting
> additional data path. *but* that also means that existing regulator
> drivers will need to be modified to handle multiple data path and "Just
> Work" wont happen - so not really good other than screw up existing
> regulator drivers with handling OMAP specific APIs in them.

What makes you think that the existing regulator drivers need to be
modified?  They already have all the data exported to the core (or
should do)...  the only thing that's missing is the timeouts and it's
not at all obvious why those need to be tuned per device.

> option 2) make omap_pmic regulator use twl_regulator - regulator
> chaining.

Again I don't know what this is.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130705/37daebf2/attachment.sig>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-05 14:08         ` Mark Brown
  (?)
@ 2013-07-05 14:50           ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-05 14:50 UTC (permalink / raw)
  To: Mark Brown
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

On 07/05/2013 09:08 AM, Mark Brown wrote:
> On Fri, Jul 05, 2013 at 08:55:07AM -0500, Nishanth Menon wrote:
>
> Please write in paragraphs, an enormous wall of unbroken text isn't
> helpful for legibility.

Apologies on the same. Will try to do better.

>
>> On 16:41-20130704, Mark Brown wrote:
>
>>> So, this still has the thing where all the data about the PMIC is
>>> replicated (but now in this driver).  It should be possible to pull all
>>> the above information except possibly the I2C timeout and perhaps the
>>> I2C address (if there's a separate control interface) from the standard
>>> regulator core data structures for the PMIC.  This would allow the
>>> driver to Just Work with any PMIC without needing to add it in two
>>> places.
>
>> option 1) we just bypass get_voltage/set_voltage to point through to
>> this function. result will be something similar to what we got here[1]
>
> I don't really know what you mean when you say "bypass get_voltage/set_voltage
> so it's kind of hard to comment...  the link you posted appears to be a
> link to the code I'm reviewing so it's not terribly enlightening.

:) it is similar, yes. by bypass get/set_voltage, I mean something like [1]

>
>> Again, based on this discussion, it is wrong - and we already did implement
>> the *wrong* way in OMAP and the new code is supposed to throw away the
>> old code once all relevant platforms are converted to DT.
>> - now, we could improve this by passing rdev and slurp out required
>>   data from regulator structures, but obviously, as you pointed out, it wont
>>   be sufficient.
>> - In this solution, we will have existing regulator drivers supporting
>> additional data path. *but* that also means that existing regulator
>> drivers will need to be modified to handle multiple data path and "Just
>> Work" wont happen - so not really good other than screw up existing
>> regulator drivers with handling OMAP specific APIs in them.
>
> What makes you think that the existing regulator drivers need to be
> modified?


data path difference - Almost all standard regulators use i2c (standard 
i2c APIs) for every other SMPS/LDO except for the ones controlled by 
OMAP custom i2c path(vc/vp), the set_voltage/get_voltage needs a 
different implementation when it comes to using the vc/vp path.

 > They already have all the data exported to the core (or
 > should do)...

I see that twl-regulator does not indeed do it, but, assuming the 
regulators have all the data exported to the core, the data is hidden in 
struct regulator_desc which is private to the device and driver. lets go 
through these:

 > +static const struct omap_pmic_info omap_twl4030_vdd1 = {
 > +	.slave_addr = 0x12,
Not readily exposed, but we can solve that using reg=<0x12>; in dts - 
this might not be duplication as the register address will be unique for 
the bus.

 > +	.voltage_reg_addr = 0x00,
desc->vsel_reg maps fine

 > +	.cmd_reg_addr = 0x00,
command register is used for sending low power state commands - which is 
not the same as voltage register or vsel_reg as used in depicted in 
regulator_desc.	

 > +	.i2c_timeout_us = 200,
yep, does not match up.

 > +	.slew_rate_uV = 4000,
desc->ramp_delay maps fine

 > +	.step_size_uV = 12500,
desc->uV_step maps fine.

 > +	.min_uV = 600000,
desc->min_uV maps fine.

 > +	.max_uV = 1450000,
can be used with constraints, but most regulator drivers seem to store 
this internally.

 > +	.voltage_selector_offset = 0,
 > +	.voltage_selector_mask = 0x7F,
 > +	.voltage_selector_setbits = 0x0,
 > +	.voltage_selector_zero = false,
to an extent we can map these to vsel_mask, linear_min_sel etc. (again 
assuming the regulator driver has populated it - most that implement 
custom set_voltage, get_voltage do not do that.

Now, How do you suggest I pick up this data in omap_pmic regulator?
linux/regulator/consumer.h does not seem appropriate omap_pmic is not 
really a consumer of the regulator and further all the detailed data is 
not exposed either.

Other option is to make rdev available to omap_pmic regulator - which 
again implies change in existing regulator driver?


> the only thing that's missing is the timeouts and it's
> not at all obvious why those need to be tuned per device.

OMAP VC hardware has no idea about how long to wait before giving up on 
an ongoing i2c transaction. This may depend on PMIC and what it does 
before acking on i2c.

>
>> option 2) make omap_pmic regulator use twl_regulator - regulator
>> chaining.
>
> Again I don't know what this is.
Lets ignore this for the moment.


[1] 
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=63bfff4e20211b464cbea6e79e5fd36df227c154
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-05 14:50           ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-05 14:50 UTC (permalink / raw)
  To: Mark Brown
  Cc: Grygorii Strashko, Benoît Cousson, linux-doc, Tony Lindgren,
	devicetree-discuss, linux-kernel, Taras Kondratiuk, Kevin Hilman,
	linux-omap, linux-arm-kernel

On 07/05/2013 09:08 AM, Mark Brown wrote:
> On Fri, Jul 05, 2013 at 08:55:07AM -0500, Nishanth Menon wrote:
>
> Please write in paragraphs, an enormous wall of unbroken text isn't
> helpful for legibility.

Apologies on the same. Will try to do better.

>
>> On 16:41-20130704, Mark Brown wrote:
>
>>> So, this still has the thing where all the data about the PMIC is
>>> replicated (but now in this driver).  It should be possible to pull all
>>> the above information except possibly the I2C timeout and perhaps the
>>> I2C address (if there's a separate control interface) from the standard
>>> regulator core data structures for the PMIC.  This would allow the
>>> driver to Just Work with any PMIC without needing to add it in two
>>> places.
>
>> option 1) we just bypass get_voltage/set_voltage to point through to
>> this function. result will be something similar to what we got here[1]
>
> I don't really know what you mean when you say "bypass get_voltage/set_voltage
> so it's kind of hard to comment...  the link you posted appears to be a
> link to the code I'm reviewing so it's not terribly enlightening.

:) it is similar, yes. by bypass get/set_voltage, I mean something like [1]

>
>> Again, based on this discussion, it is wrong - and we already did implement
>> the *wrong* way in OMAP and the new code is supposed to throw away the
>> old code once all relevant platforms are converted to DT.
>> - now, we could improve this by passing rdev and slurp out required
>>   data from regulator structures, but obviously, as you pointed out, it wont
>>   be sufficient.
>> - In this solution, we will have existing regulator drivers supporting
>> additional data path. *but* that also means that existing regulator
>> drivers will need to be modified to handle multiple data path and "Just
>> Work" wont happen - so not really good other than screw up existing
>> regulator drivers with handling OMAP specific APIs in them.
>
> What makes you think that the existing regulator drivers need to be
> modified?


data path difference - Almost all standard regulators use i2c (standard 
i2c APIs) for every other SMPS/LDO except for the ones controlled by 
OMAP custom i2c path(vc/vp), the set_voltage/get_voltage needs a 
different implementation when it comes to using the vc/vp path.

 > They already have all the data exported to the core (or
 > should do)...

I see that twl-regulator does not indeed do it, but, assuming the 
regulators have all the data exported to the core, the data is hidden in 
struct regulator_desc which is private to the device and driver. lets go 
through these:

 > +static const struct omap_pmic_info omap_twl4030_vdd1 = {
 > +	.slave_addr = 0x12,
Not readily exposed, but we can solve that using reg=<0x12>; in dts - 
this might not be duplication as the register address will be unique for 
the bus.

 > +	.voltage_reg_addr = 0x00,
desc->vsel_reg maps fine

 > +	.cmd_reg_addr = 0x00,
command register is used for sending low power state commands - which is 
not the same as voltage register or vsel_reg as used in depicted in 
regulator_desc.	

 > +	.i2c_timeout_us = 200,
yep, does not match up.

 > +	.slew_rate_uV = 4000,
desc->ramp_delay maps fine

 > +	.step_size_uV = 12500,
desc->uV_step maps fine.

 > +	.min_uV = 600000,
desc->min_uV maps fine.

 > +	.max_uV = 1450000,
can be used with constraints, but most regulator drivers seem to store 
this internally.

 > +	.voltage_selector_offset = 0,
 > +	.voltage_selector_mask = 0x7F,
 > +	.voltage_selector_setbits = 0x0,
 > +	.voltage_selector_zero = false,
to an extent we can map these to vsel_mask, linear_min_sel etc. (again 
assuming the regulator driver has populated it - most that implement 
custom set_voltage, get_voltage do not do that.

Now, How do you suggest I pick up this data in omap_pmic regulator?
linux/regulator/consumer.h does not seem appropriate omap_pmic is not 
really a consumer of the regulator and further all the detailed data is 
not exposed either.

Other option is to make rdev available to omap_pmic regulator - which 
again implies change in existing regulator driver?


> the only thing that's missing is the timeouts and it's
> not at all obvious why those need to be tuned per device.

OMAP VC hardware has no idea about how long to wait before giving up on 
an ongoing i2c transaction. This may depend on PMIC and what it does 
before acking on i2c.

>
>> option 2) make omap_pmic regulator use twl_regulator - regulator
>> chaining.
>
> Again I don't know what this is.
Lets ignore this for the moment.


[1] 
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=63bfff4e20211b464cbea6e79e5fd36df227c154
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-05 14:50           ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-05 14:50 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/05/2013 09:08 AM, Mark Brown wrote:
> On Fri, Jul 05, 2013 at 08:55:07AM -0500, Nishanth Menon wrote:
>
> Please write in paragraphs, an enormous wall of unbroken text isn't
> helpful for legibility.

Apologies on the same. Will try to do better.

>
>> On 16:41-20130704, Mark Brown wrote:
>
>>> So, this still has the thing where all the data about the PMIC is
>>> replicated (but now in this driver).  It should be possible to pull all
>>> the above information except possibly the I2C timeout and perhaps the
>>> I2C address (if there's a separate control interface) from the standard
>>> regulator core data structures for the PMIC.  This would allow the
>>> driver to Just Work with any PMIC without needing to add it in two
>>> places.
>
>> option 1) we just bypass get_voltage/set_voltage to point through to
>> this function. result will be something similar to what we got here[1]
>
> I don't really know what you mean when you say "bypass get_voltage/set_voltage
> so it's kind of hard to comment...  the link you posted appears to be a
> link to the code I'm reviewing so it's not terribly enlightening.

:) it is similar, yes. by bypass get/set_voltage, I mean something like [1]

>
>> Again, based on this discussion, it is wrong - and we already did implement
>> the *wrong* way in OMAP and the new code is supposed to throw away the
>> old code once all relevant platforms are converted to DT.
>> - now, we could improve this by passing rdev and slurp out required
>>   data from regulator structures, but obviously, as you pointed out, it wont
>>   be sufficient.
>> - In this solution, we will have existing regulator drivers supporting
>> additional data path. *but* that also means that existing regulator
>> drivers will need to be modified to handle multiple data path and "Just
>> Work" wont happen - so not really good other than screw up existing
>> regulator drivers with handling OMAP specific APIs in them.
>
> What makes you think that the existing regulator drivers need to be
> modified?


data path difference - Almost all standard regulators use i2c (standard 
i2c APIs) for every other SMPS/LDO except for the ones controlled by 
OMAP custom i2c path(vc/vp), the set_voltage/get_voltage needs a 
different implementation when it comes to using the vc/vp path.

 > They already have all the data exported to the core (or
 > should do)...

I see that twl-regulator does not indeed do it, but, assuming the 
regulators have all the data exported to the core, the data is hidden in 
struct regulator_desc which is private to the device and driver. lets go 
through these:

 > +static const struct omap_pmic_info omap_twl4030_vdd1 = {
 > +	.slave_addr = 0x12,
Not readily exposed, but we can solve that using reg=<0x12>; in dts - 
this might not be duplication as the register address will be unique for 
the bus.

 > +	.voltage_reg_addr = 0x00,
desc->vsel_reg maps fine

 > +	.cmd_reg_addr = 0x00,
command register is used for sending low power state commands - which is 
not the same as voltage register or vsel_reg as used in depicted in 
regulator_desc.	

 > +	.i2c_timeout_us = 200,
yep, does not match up.

 > +	.slew_rate_uV = 4000,
desc->ramp_delay maps fine

 > +	.step_size_uV = 12500,
desc->uV_step maps fine.

 > +	.min_uV = 600000,
desc->min_uV maps fine.

 > +	.max_uV = 1450000,
can be used with constraints, but most regulator drivers seem to store 
this internally.

 > +	.voltage_selector_offset = 0,
 > +	.voltage_selector_mask = 0x7F,
 > +	.voltage_selector_setbits = 0x0,
 > +	.voltage_selector_zero = false,
to an extent we can map these to vsel_mask, linear_min_sel etc. (again 
assuming the regulator driver has populated it - most that implement 
custom set_voltage, get_voltage do not do that.

Now, How do you suggest I pick up this data in omap_pmic regulator?
linux/regulator/consumer.h does not seem appropriate omap_pmic is not 
really a consumer of the regulator and further all the detailed data is 
not exposed either.

Other option is to make rdev available to omap_pmic regulator - which 
again implies change in existing regulator driver?


> the only thing that's missing is the timeouts and it's
> not at all obvious why those need to be tuned per device.

OMAP VC hardware has no idea about how long to wait before giving up on 
an ongoing i2c transaction. This may depend on PMIC and what it does 
before acking on i2c.

>
>> option 2) make omap_pmic regulator use twl_regulator - regulator
>> chaining.
>
> Again I don't know what this is.
Lets ignore this for the moment.


[1] 
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=63bfff4e20211b464cbea6e79e5fd36df227c154
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-05 14:50           ` Nishanth Menon
@ 2013-07-05 16:52             ` Mark Brown
  -1 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-05 16:52 UTC (permalink / raw)
  To: Nishanth Menon
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

[-- Attachment #1: Type: text/plain, Size: 3281 bytes --]

On Fri, Jul 05, 2013 at 09:50:34AM -0500, Nishanth Menon wrote:
> On 07/05/2013 09:08 AM, Mark Brown wrote:

> >>option 1) we just bypass get_voltage/set_voltage to point through to
> >>this function. result will be something similar to what we got here[1]

> >I don't really know what you mean when you say "bypass get_voltage/set_voltage
> >so it's kind of hard to comment...  the link you posted appears to be a
> >link to the code I'm reviewing so it's not terribly enlightening.

> :) it is similar, yes. by bypass get/set_voltage, I mean something like [1]

No, that's not a good idea.

> >What makes you think that the existing regulator drivers need to be
> >modified?

> data path difference - Almost all standard regulators use i2c
> (standard i2c APIs) for every other SMPS/LDO except for the ones
> controlled by OMAP custom i2c path(vc/vp), the
> set_voltage/get_voltage needs a different implementation when it
> comes to using the vc/vp path.

> > They already have all the data exported to the core (or
> > should do)...

> I see that twl-regulator does not indeed do it, but, assuming the
> regulators have all the data exported to the core, the data is
> hidden in struct regulator_desc which is private to the device and
> driver. lets go through these:

That's just a simple matter of programming to fix, and any regulator
which can work with this sort of table based stuff you're looking at
here must also be able to be converted to work with the equivalent code
in the regulator core so open coding is a deficiency in the driver.

> > +	.cmd_reg_addr = 0x00,

> command register is used for sending low power state commands -
> which is not the same as voltage register or vsel_reg as used in
> depicted in regulator_desc.	

There's no information about how to use this register in your
bindings...  but anyway, can't be too hard to add this if it's actually
used.

> > +	.i2c_timeout_us = 200,

> yep, does not match up.

Like I say I just don't see why this is even specified per device.

> > +	.max_uV = 1450000,

> can be used with constraints, but most regulator drivers seem to
> store this internally.

Or just trivially calculate it as we do currently.

> > +	.voltage_selector_offset = 0,
> > +	.voltage_selector_mask = 0x7F,
> > +	.voltage_selector_setbits = 0x0,
> > +	.voltage_selector_zero = false,

> to an extent we can map these to vsel_mask, linear_min_sel etc.
> (again assuming the regulator driver has populated it - most that
> implement custom set_voltage, get_voltage do not do that.

Anything that implements a custom set_voltage() won't work with your
data structure either...

> Other option is to make rdev available to omap_pmic regulator -
> which again implies change in existing regulator driver?

Why would any of the drivers need to change to do this?  They're already
exporting the information.

> >the only thing that's missing is the timeouts and it's
> >not at all obvious why those need to be tuned per device.

> OMAP VC hardware has no idea about how long to wait before giving up
> on an ongoing i2c transaction. This may depend on PMIC and what it
> does before acking on i2c.

So pick a high number (it's only for error cases...)?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-05 16:52             ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-05 16:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 05, 2013 at 09:50:34AM -0500, Nishanth Menon wrote:
> On 07/05/2013 09:08 AM, Mark Brown wrote:

> >>option 1) we just bypass get_voltage/set_voltage to point through to
> >>this function. result will be something similar to what we got here[1]

> >I don't really know what you mean when you say "bypass get_voltage/set_voltage
> >so it's kind of hard to comment...  the link you posted appears to be a
> >link to the code I'm reviewing so it's not terribly enlightening.

> :) it is similar, yes. by bypass get/set_voltage, I mean something like [1]

No, that's not a good idea.

> >What makes you think that the existing regulator drivers need to be
> >modified?

> data path difference - Almost all standard regulators use i2c
> (standard i2c APIs) for every other SMPS/LDO except for the ones
> controlled by OMAP custom i2c path(vc/vp), the
> set_voltage/get_voltage needs a different implementation when it
> comes to using the vc/vp path.

> > They already have all the data exported to the core (or
> > should do)...

> I see that twl-regulator does not indeed do it, but, assuming the
> regulators have all the data exported to the core, the data is
> hidden in struct regulator_desc which is private to the device and
> driver. lets go through these:

That's just a simple matter of programming to fix, and any regulator
which can work with this sort of table based stuff you're looking at
here must also be able to be converted to work with the equivalent code
in the regulator core so open coding is a deficiency in the driver.

> > +	.cmd_reg_addr = 0x00,

> command register is used for sending low power state commands -
> which is not the same as voltage register or vsel_reg as used in
> depicted in regulator_desc.	

There's no information about how to use this register in your
bindings...  but anyway, can't be too hard to add this if it's actually
used.

> > +	.i2c_timeout_us = 200,

> yep, does not match up.

Like I say I just don't see why this is even specified per device.

> > +	.max_uV = 1450000,

> can be used with constraints, but most regulator drivers seem to
> store this internally.

Or just trivially calculate it as we do currently.

> > +	.voltage_selector_offset = 0,
> > +	.voltage_selector_mask = 0x7F,
> > +	.voltage_selector_setbits = 0x0,
> > +	.voltage_selector_zero = false,

> to an extent we can map these to vsel_mask, linear_min_sel etc.
> (again assuming the regulator driver has populated it - most that
> implement custom set_voltage, get_voltage do not do that.

Anything that implements a custom set_voltage() won't work with your
data structure either...

> Other option is to make rdev available to omap_pmic regulator -
> which again implies change in existing regulator driver?

Why would any of the drivers need to change to do this?  They're already
exporting the information.

> >the only thing that's missing is the timeouts and it's
> >not at all obvious why those need to be tuned per device.

> OMAP VC hardware has no idea about how long to wait before giving up
> on an ongoing i2c transaction. This may depend on PMIC and what it
> does before acking on i2c.

So pick a high number (it's only for error cases...)?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130705/b856fb33/attachment.sig>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-05 16:52             ` Mark Brown
@ 2013-07-05 17:33               ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-05 17:33 UTC (permalink / raw)
  To: Mark Brown
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

On 07/05/2013 11:52 AM, Mark Brown wrote:
> On Fri, Jul 05, 2013 at 09:50:34AM -0500, Nishanth Menon wrote:
>> On 07/05/2013 09:08 AM, Mark Brown wrote:
>
>>>> option 1) we just bypass get_voltage/set_voltage to point through to
>>>> this function. result will be something similar to what we got here[1]
>
>>> I don't really know what you mean when you say "bypass get_voltage/set_voltage
>>> so it's kind of hard to comment...  the link you posted appears to be a
>>> link to the code I'm reviewing so it's not terribly enlightening.
>
>> :) it is similar, yes. by bypass get/set_voltage, I mean something like [1]
>
> No, that's not a good idea.

I agree.

>
>>> What makes you think that the existing regulator drivers need to be
>>> modified?
>
>> data path difference - Almost all standard regulators use i2c
>> (standard i2c APIs) for every other SMPS/LDO except for the ones
>> controlled by OMAP custom i2c path(vc/vp), the
>> set_voltage/get_voltage needs a different implementation when it
>> comes to using the vc/vp path.
>
>>> They already have all the data exported to the core (or
>>> should do)...
>
>> I see that twl-regulator does not indeed do it, but, assuming the
>> regulators have all the data exported to the core, the data is
>> hidden in struct regulator_desc which is private to the device and
>> driver. lets go through these:
>
> That's just a simple matter of programming to fix, and any regulator
> which can work with this sort of table based stuff you're looking at
> here must also be able to be converted to work with the equivalent code
> in the regulator core so open coding is a deficiency in the driver.

OK, conceptually, I am a bit lost here (may be I thinking about "how the 
heck am I supposed to implement this?") - Hoping for your patience in 
trying to get through to my thick skull :)

Taking an example of twl-regulator and omap_pmic, are you suggesting 
omap_pmic to be a user twl-regulator using 
include/linux/regulator/consumer.h? or are you suggesting that omap_pmic 
should not be a regulator at all?

Could you suggest what you have in your mind here, I can see how we can 
make that happen. I am not averse to writing new code ofcourse.

>
>>> +	.cmd_reg_addr = 0x00,
>
>> command register is used for sending low power state commands -
>> which is not the same as voltage register or vsel_reg as used in
>> depicted in regulator_desc.	
>
> There's no information about how to use this register in your
> bindings...  but anyway, can't be too hard to add this if it's actually
> used.

Yes it is, and also happens to be how OMAPs achieve maximum power 
savings - when low power modes are achieved in OMAP(automatic hardware 
assisted commands are send to the specific command registers in PMIC and 
viceversa on wakeup) - but this also happens to be very specific to OMAP 
way of handling things. I can refer to the Reference Manual as to how it 
actually works, but that'd be an overkill, I will try to expand on the 
bindings a little more, I guess.

>
>>> +	.i2c_timeout_us = 200,
>
>> yep, does not match up.
>
> Like I say I just don't see why this is even specified per device.
>
>>> +	.max_uV = 1450000,
>
>> can be used with constraints, but most regulator drivers seem to
>> store this internally.
>
> Or just trivially calculate it as we do currently.
>
>>> +	.voltage_selector_offset = 0,
>>> +	.voltage_selector_mask = 0x7F,
>>> +	.voltage_selector_setbits = 0x0,
>>> +	.voltage_selector_zero = false,
>
>> to an extent we can map these to vsel_mask, linear_min_sel etc.
>> (again assuming the regulator driver has populated it - most that
>> implement custom set_voltage, get_voltage do not do that.
>
> Anything that implements a custom set_voltage() won't work with your
> data structure either...

It would not work with OMAP either ;). But that said, drivers do freely 
implement custom set_voltage/get_voltage primarily because there are 
ranges in supported voltages that are non-linear and try to be generic 
to work on non-OMAP platforms as well. However, within the supported 
range, only the linear ranges are used with OMAP.

>
>> Other option is to make rdev available to omap_pmic regulator -
>> which again implies change in existing regulator driver?
>
> Why would any of the drivers need to change to do this?  They're already
> exporting the information.
I am thinking here: two regulator drivers, using same rdev/desc for 
getting the data. probably makes no sense, I agree.

>
>>> the only thing that's missing is the timeouts and it's
>>> not at all obvious why those need to be tuned per device.
>
>> OMAP VC hardware has no idea about how long to wait before giving up
>> on an ongoing i2c transaction. This may depend on PMIC and what it
>> does before acking on i2c.
>
> So pick a high number (it's only for error cases...)?
>
from hardware perspective yeah, if it does not lockup (based on erratas 
on specific devices ;) ). it also controls in part, the latency of 
response to Voltage processor from Voltage controller also needed for 
computing SmartReflex latencies (as it should consider worst case 
conditions)

-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-05 17:33               ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-05 17:33 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/05/2013 11:52 AM, Mark Brown wrote:
> On Fri, Jul 05, 2013 at 09:50:34AM -0500, Nishanth Menon wrote:
>> On 07/05/2013 09:08 AM, Mark Brown wrote:
>
>>>> option 1) we just bypass get_voltage/set_voltage to point through to
>>>> this function. result will be something similar to what we got here[1]
>
>>> I don't really know what you mean when you say "bypass get_voltage/set_voltage
>>> so it's kind of hard to comment...  the link you posted appears to be a
>>> link to the code I'm reviewing so it's not terribly enlightening.
>
>> :) it is similar, yes. by bypass get/set_voltage, I mean something like [1]
>
> No, that's not a good idea.

I agree.

>
>>> What makes you think that the existing regulator drivers need to be
>>> modified?
>
>> data path difference - Almost all standard regulators use i2c
>> (standard i2c APIs) for every other SMPS/LDO except for the ones
>> controlled by OMAP custom i2c path(vc/vp), the
>> set_voltage/get_voltage needs a different implementation when it
>> comes to using the vc/vp path.
>
>>> They already have all the data exported to the core (or
>>> should do)...
>
>> I see that twl-regulator does not indeed do it, but, assuming the
>> regulators have all the data exported to the core, the data is
>> hidden in struct regulator_desc which is private to the device and
>> driver. lets go through these:
>
> That's just a simple matter of programming to fix, and any regulator
> which can work with this sort of table based stuff you're looking at
> here must also be able to be converted to work with the equivalent code
> in the regulator core so open coding is a deficiency in the driver.

OK, conceptually, I am a bit lost here (may be I thinking about "how the 
heck am I supposed to implement this?") - Hoping for your patience in 
trying to get through to my thick skull :)

Taking an example of twl-regulator and omap_pmic, are you suggesting 
omap_pmic to be a user twl-regulator using 
include/linux/regulator/consumer.h? or are you suggesting that omap_pmic 
should not be a regulator at all?

Could you suggest what you have in your mind here, I can see how we can 
make that happen. I am not averse to writing new code ofcourse.

>
>>> +	.cmd_reg_addr = 0x00,
>
>> command register is used for sending low power state commands -
>> which is not the same as voltage register or vsel_reg as used in
>> depicted in regulator_desc.	
>
> There's no information about how to use this register in your
> bindings...  but anyway, can't be too hard to add this if it's actually
> used.

Yes it is, and also happens to be how OMAPs achieve maximum power 
savings - when low power modes are achieved in OMAP(automatic hardware 
assisted commands are send to the specific command registers in PMIC and 
viceversa on wakeup) - but this also happens to be very specific to OMAP 
way of handling things. I can refer to the Reference Manual as to how it 
actually works, but that'd be an overkill, I will try to expand on the 
bindings a little more, I guess.

>
>>> +	.i2c_timeout_us = 200,
>
>> yep, does not match up.
>
> Like I say I just don't see why this is even specified per device.
>
>>> +	.max_uV = 1450000,
>
>> can be used with constraints, but most regulator drivers seem to
>> store this internally.
>
> Or just trivially calculate it as we do currently.
>
>>> +	.voltage_selector_offset = 0,
>>> +	.voltage_selector_mask = 0x7F,
>>> +	.voltage_selector_setbits = 0x0,
>>> +	.voltage_selector_zero = false,
>
>> to an extent we can map these to vsel_mask, linear_min_sel etc.
>> (again assuming the regulator driver has populated it - most that
>> implement custom set_voltage, get_voltage do not do that.
>
> Anything that implements a custom set_voltage() won't work with your
> data structure either...

It would not work with OMAP either ;). But that said, drivers do freely 
implement custom set_voltage/get_voltage primarily because there are 
ranges in supported voltages that are non-linear and try to be generic 
to work on non-OMAP platforms as well. However, within the supported 
range, only the linear ranges are used with OMAP.

>
>> Other option is to make rdev available to omap_pmic regulator -
>> which again implies change in existing regulator driver?
>
> Why would any of the drivers need to change to do this?  They're already
> exporting the information.
I am thinking here: two regulator drivers, using same rdev/desc for 
getting the data. probably makes no sense, I agree.

>
>>> the only thing that's missing is the timeouts and it's
>>> not at all obvious why those need to be tuned per device.
>
>> OMAP VC hardware has no idea about how long to wait before giving up
>> on an ongoing i2c transaction. This may depend on PMIC and what it
>> does before acking on i2c.
>
> So pick a high number (it's only for error cases...)?
>
from hardware perspective yeah, if it does not lockup (based on erratas 
on specific devices ;) ). it also controls in part, the latency of 
response to Voltage processor from Voltage controller also needed for 
computing SmartReflex latencies (as it should consider worst case 
conditions)

-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-05 17:33               ` Nishanth Menon
@ 2013-07-05 17:47                 ` Mark Brown
  -1 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-05 17:47 UTC (permalink / raw)
  To: Nishanth Menon
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

[-- Attachment #1: Type: text/plain, Size: 3851 bytes --]

On Fri, Jul 05, 2013 at 12:33:10PM -0500, Nishanth Menon wrote:

> Taking an example of twl-regulator and omap_pmic, are you suggesting
> omap_pmic to be a user twl-regulator using
> include/linux/regulator/consumer.h? or are you suggesting that
> omap_pmic should not be a regulator at all?

No, I'm suggesting that omap_pmic find the TWL driver data at runtime
(eg, using the device tree to locate the relevant regulator) and get the
information out of the regulator driver that way.  It can then tell the
hardware about the data that way without having to explictly add every
single regulator both standalone and to the OMAP driver.

> >There's no information about how to use this register in your
> >bindings...  but anyway, can't be too hard to add this if it's actually
> >used.

> Yes it is, and also happens to be how OMAPs achieve maximum power
> savings - when low power modes are achieved in OMAP(automatic
> hardware assisted commands are send to the specific command
> registers in PMIC and viceversa on wakeup) - but this also happens
> to be very specific to OMAP way of handling things. I can refer to
> the Reference Manual as to how it actually works, but that'd be an
> overkill, I will try to expand on the bindings a little more, I
> guess.

OK, so this is a register defined by the OMAP architecture?  I think
it's reasonable to add something to allow this to be obtained to the
core, using a DT property seems yucky since every board usingt this is
going to have to cut'n'paste the value.  Some sort of custom parameter
readback thing perhaps, it doesn't have to be too generic.

> >Anything that implements a custom set_voltage() won't work with your
> >data structure either...

> It would not work with OMAP either ;). But that said, drivers do

Yes, that's kind of my point - as with the code Paul was implementing it
doesn't matter if you can't support every single regualtor since the
hardware design constrains what the regulator can do.  The regualtor
framework already has helpers which factor out the code for anything
which has the limiations the OMAP hardware has (or where it doesn't we
could add them) so there shouldn't be any need for a driver to provide
custom callbacks.

> freely implement custom set_voltage/get_voltage primarily because
> there are ranges in supported voltages that are non-linear and try
> to be generic to work on non-OMAP platforms as well. However, within
> the supported range, only the linear ranges are used with OMAP.

OK, that's a bit more interesting but I expect such regulators will
actually work with the linear ranges helpers I added the other day
(Marvell had a PMIC using them and I realised that the same pattern can
be applied to a bunch of other devices).  Do you think that'd cover the
cases you're aware of?

Another option is for the drivers to provide the data and use the
helpers for their linear ranges as part of a more complex
implementation.

> >>OMAP VC hardware has no idea about how long to wait before giving up
> >>on an ongoing i2c transaction. This may depend on PMIC and what it
> >>does before acking on i2c.

> >So pick a high number (it's only for error cases...)?

> from hardware perspective yeah, if it does not lockup (based on
> erratas on specific devices ;) ). it also controls in part, the
> latency of response to Voltage processor from Voltage controller
> also needed for computing SmartReflex latencies (as it should
> consider worst case conditions)

OK, that's a bit more fun but I think the kernel wants that information
in general anyway since a software cpufreq driver or something might
want to make the same latency decisions.  This is what set_voltage_time() 
is for in part.  But to a first approximation is there really much
variation in the numbers?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-05 17:47                 ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-05 17:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 05, 2013 at 12:33:10PM -0500, Nishanth Menon wrote:

> Taking an example of twl-regulator and omap_pmic, are you suggesting
> omap_pmic to be a user twl-regulator using
> include/linux/regulator/consumer.h? or are you suggesting that
> omap_pmic should not be a regulator at all?

No, I'm suggesting that omap_pmic find the TWL driver data at runtime
(eg, using the device tree to locate the relevant regulator) and get the
information out of the regulator driver that way.  It can then tell the
hardware about the data that way without having to explictly add every
single regulator both standalone and to the OMAP driver.

> >There's no information about how to use this register in your
> >bindings...  but anyway, can't be too hard to add this if it's actually
> >used.

> Yes it is, and also happens to be how OMAPs achieve maximum power
> savings - when low power modes are achieved in OMAP(automatic
> hardware assisted commands are send to the specific command
> registers in PMIC and viceversa on wakeup) - but this also happens
> to be very specific to OMAP way of handling things. I can refer to
> the Reference Manual as to how it actually works, but that'd be an
> overkill, I will try to expand on the bindings a little more, I
> guess.

OK, so this is a register defined by the OMAP architecture?  I think
it's reasonable to add something to allow this to be obtained to the
core, using a DT property seems yucky since every board usingt this is
going to have to cut'n'paste the value.  Some sort of custom parameter
readback thing perhaps, it doesn't have to be too generic.

> >Anything that implements a custom set_voltage() won't work with your
> >data structure either...

> It would not work with OMAP either ;). But that said, drivers do

Yes, that's kind of my point - as with the code Paul was implementing it
doesn't matter if you can't support every single regualtor since the
hardware design constrains what the regulator can do.  The regualtor
framework already has helpers which factor out the code for anything
which has the limiations the OMAP hardware has (or where it doesn't we
could add them) so there shouldn't be any need for a driver to provide
custom callbacks.

> freely implement custom set_voltage/get_voltage primarily because
> there are ranges in supported voltages that are non-linear and try
> to be generic to work on non-OMAP platforms as well. However, within
> the supported range, only the linear ranges are used with OMAP.

OK, that's a bit more interesting but I expect such regulators will
actually work with the linear ranges helpers I added the other day
(Marvell had a PMIC using them and I realised that the same pattern can
be applied to a bunch of other devices).  Do you think that'd cover the
cases you're aware of?

Another option is for the drivers to provide the data and use the
helpers for their linear ranges as part of a more complex
implementation.

> >>OMAP VC hardware has no idea about how long to wait before giving up
> >>on an ongoing i2c transaction. This may depend on PMIC and what it
> >>does before acking on i2c.

> >So pick a high number (it's only for error cases...)?

> from hardware perspective yeah, if it does not lockup (based on
> erratas on specific devices ;) ). it also controls in part, the
> latency of response to Voltage processor from Voltage controller
> also needed for computing SmartReflex latencies (as it should
> consider worst case conditions)

OK, that's a bit more fun but I think the kernel wants that information
in general anyway since a software cpufreq driver or something might
want to make the same latency decisions.  This is what set_voltage_time() 
is for in part.  But to a first approximation is there really much
variation in the numbers?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130705/15e22df1/attachment.sig>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-05 17:47                 ` Mark Brown
@ 2013-07-08 17:22                   ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-08 17:22 UTC (permalink / raw)
  To: Mark Brown
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

On 07/05/2013 12:47 PM, Mark Brown wrote:
> On Fri, Jul 05, 2013 at 12:33:10PM -0500, Nishanth Menon wrote:
>
>> Taking an example of twl-regulator and omap_pmic, are you suggesting
>> omap_pmic to be a user twl-regulator using
>> include/linux/regulator/consumer.h? or are you suggesting that
>> omap_pmic should not be a regulator at all?
>
> No, I'm suggesting that omap_pmic find the TWL driver data at runtime
> (eg, using the device tree to locate the relevant regulator) and get the
> information out of the regulator driver that way.  It can then tell the
> hardware about the data that way without having to explictly add every
> single regulator both standalone and to the OMAP driver.

Apologies on delayed response, I had spend sometime thinking about 
this(and try some prototype code), here it goes:

case #1 - TPS62361 has a single SMPS and a single generic i2c bus, one 
can talk on. In this case, you'd associate the regulator device in one 
place - i2cX or on custom SoC hardware interface.

When used with custom SoC hardware interface, generic tps62361 regulator 
which talks regular i2c wont even probe for omap_pmic to get the 
regulator data from tps62361 regulator driver. Even if we were to define 
the generic TPS62361 in dts nodes, it will fail probe as it cant access 
any of it's registers using generic i2c.

case #2 - TWL6030/2 has a multiple SMPS and LDOs and a two i2c bus one 
can talk on. however, SMPS meant for SoC can only be controlled by 
custom SoC hardware. In this case, we'd not even have a 
regulator_register taking place for SMPS controlled by custom hardware.

I am lost as to how we can even do this?

All the dts node tells for an SMPS today is:
vaux3: regulator-vaux3 {
         compatible = "ti,twl6030-vaux3";
} as an example.
the slew data is embedded inside the twl-regulator data structures - if 
it never gets probed, it wont make that data available in regulator core 
information.

so doing something like:
omap_pmic_vdd_mpu {
	pmic-supply = <&vcore1>;
};
wont work, unless we define a phandle vcore1.
vcore1: regulator-vcore1 {
	compatible="ti,twl6030-vcore1";
};
wont help us either, because this wont be an SMPS that twl-regulator can 
even try to control.

So, how would omap_pmic find vcore1 twl driver data when it does not 
even twl6030 does not even probe vcore1? and making twl-regulator.c 
probe vcore1 (aka making it probe for a device that it cannot manage) is 
conceptually wrong, no (not to mention wrong definition of two device 
nodes in dt blob for the same device)?


>
>>> There's no information about how to use this register in your
>>> bindings...  but anyway, can't be too hard to add this if it's actually
>>> used.
>
>> Yes it is, and also happens to be how OMAPs achieve maximum power
>> savings - when low power modes are achieved in OMAP(automatic
>> hardware assisted commands are send to the specific command
>> registers in PMIC and viceversa on wakeup) - but this also happens
>> to be very specific to OMAP way of handling things. I can refer to
>> the Reference Manual as to how it actually works, but that'd be an
>> overkill, I will try to expand on the bindings a little more, I
>> guess.
>
> OK, so this is a register defined by the OMAP architecture?  I think

I might say yes, but that will only be because I just know OMAP 
architecture alone, Have'nt had enough indepth knowledge of other 
architectures to make a broader statement :(

> it's reasonable to add something to allow this to be obtained to the
> core, using a DT property seems yucky since every board usingt this is
> going to have to cut'n'paste the value.  Some sort of custom parameter
> readback thing perhaps, it doesn't have to be too generic.

custom of->match data parameter is how it is done today in this patch

>
>>> Anything that implements a custom set_voltage() won't work with your
>>> data structure either...
>
>> It would not work with OMAP either ;). But that said, drivers do
>
> Yes, that's kind of my point - as with the code Paul was implementing it
> doesn't matter if you can't support every single regualtor since the
> hardware design constrains what the regulator can do.  The regualtor
> framework already has helpers which factor out the code for anything
> which has the limiations the OMAP hardware has (or where it doesn't we
> could add them) so there shouldn't be any need for a driver to provide
> custom callbacks.

custom callback context was as follows: if we force the SoC generic 
regulators to use OMAP custom interfaces, then we have a need for custom 
callbacks, else w.r.t PMIC data, covered above, we do not need any 
custom callbacks, agreed.

>> freely implement custom set_voltage/get_voltage primarily because
>> there are ranges in supported voltages that are non-linear and try
>> to be generic to work on non-OMAP platforms as well. However, within
>> the supported range, only the linear ranges are used with OMAP.
>
> OK, that's a bit more interesting but I expect such regulators will
> actually work with the linear ranges helpers I added the other day
> (Marvell had a PMIC using them and I realised that the same pattern can
> be applied to a bunch of other devices).  Do you think that'd cover the
> cases you're aware of?
>
> Another option is for the drivers to provide the data and use the
> helpers for their linear ranges as part of a more complex
> implementation.

Having looked at a few regulator driver implementations, there seems to 
be the following combinations:
a) drivers which have n ranges of linear voltages for incremental selector
b) drivers with 1 range of linear voltages only for all ranges of selectors
c) drivers with 1 range of linear voltages and nonlinear voltage values 
for other vsels.


>
>>>> OMAP VC hardware has no idea about how long to wait before giving up
>>>> on an ongoing i2c transaction. This may depend on PMIC and what it
>>>> does before acking on i2c.
>
>>> So pick a high number (it's only for error cases...)?
>
>> from hardware perspective yeah, if it does not lockup (based on
>> erratas on specific devices ;) ). it also controls in part, the
>> latency of response to Voltage processor from Voltage controller
>> also needed for computing SmartReflex latencies (as it should
>> consider worst case conditions)
>
> OK, that's a bit more fun but I think the kernel wants that information
> in general anyway since a software cpufreq driver or something might
> want to make the same latency decisions.  This is what set_voltage_time()
> is for in part.  But to a first approximation is there really much
> variation in the numbers?

between PMICs? yep, twl4030 does 4mV/uSec, 6030 can do 6mV/uSec, 
TPS62361 can do 32mV/uSec, TWL6035/37 does 0.220mV/uSec

For a given PMIC, voltage X to Y time will vary ofcourse. SmartReflex 
AVS is something cpufreq wont know about -> completely different topic 
of its own :)

What we are representing is data for PMIC supported on specific SoC 
hardware controller interface meant just for PMIC - IMHO, it is similar 
to data that twl-regulator stores for the SMPS/LDOs that it can control 
- given these are starting to become legacy platforms (OMAP3,4,5) and 
the fact that it is a one-off(at this point in time) knwon custom 
hardware doing stuff like this, we would'nt expect too much churn either 
there(new PMICs added etc). On the flip side, I do understand your 
concern with scalability and ease of re-use here. but given that we 
have'nt seen much of a churn in this data for years on the go now, I'd 
expect lesser in the future. This dedicated interface has been dropped 
in our future chips precisely due to the lack of scalability to PMIC 
choices such as those using SPI.

just my 2 cents.
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-08 17:22                   ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-08 17:22 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/05/2013 12:47 PM, Mark Brown wrote:
> On Fri, Jul 05, 2013 at 12:33:10PM -0500, Nishanth Menon wrote:
>
>> Taking an example of twl-regulator and omap_pmic, are you suggesting
>> omap_pmic to be a user twl-regulator using
>> include/linux/regulator/consumer.h? or are you suggesting that
>> omap_pmic should not be a regulator at all?
>
> No, I'm suggesting that omap_pmic find the TWL driver data at runtime
> (eg, using the device tree to locate the relevant regulator) and get the
> information out of the regulator driver that way.  It can then tell the
> hardware about the data that way without having to explictly add every
> single regulator both standalone and to the OMAP driver.

Apologies on delayed response, I had spend sometime thinking about 
this(and try some prototype code), here it goes:

case #1 - TPS62361 has a single SMPS and a single generic i2c bus, one 
can talk on. In this case, you'd associate the regulator device in one 
place - i2cX or on custom SoC hardware interface.

When used with custom SoC hardware interface, generic tps62361 regulator 
which talks regular i2c wont even probe for omap_pmic to get the 
regulator data from tps62361 regulator driver. Even if we were to define 
the generic TPS62361 in dts nodes, it will fail probe as it cant access 
any of it's registers using generic i2c.

case #2 - TWL6030/2 has a multiple SMPS and LDOs and a two i2c bus one 
can talk on. however, SMPS meant for SoC can only be controlled by 
custom SoC hardware. In this case, we'd not even have a 
regulator_register taking place for SMPS controlled by custom hardware.

I am lost as to how we can even do this?

All the dts node tells for an SMPS today is:
vaux3: regulator-vaux3 {
         compatible = "ti,twl6030-vaux3";
} as an example.
the slew data is embedded inside the twl-regulator data structures - if 
it never gets probed, it wont make that data available in regulator core 
information.

so doing something like:
omap_pmic_vdd_mpu {
	pmic-supply = <&vcore1>;
};
wont work, unless we define a phandle vcore1.
vcore1: regulator-vcore1 {
	compatible="ti,twl6030-vcore1";
};
wont help us either, because this wont be an SMPS that twl-regulator can 
even try to control.

So, how would omap_pmic find vcore1 twl driver data when it does not 
even twl6030 does not even probe vcore1? and making twl-regulator.c 
probe vcore1 (aka making it probe for a device that it cannot manage) is 
conceptually wrong, no (not to mention wrong definition of two device 
nodes in dt blob for the same device)?


>
>>> There's no information about how to use this register in your
>>> bindings...  but anyway, can't be too hard to add this if it's actually
>>> used.
>
>> Yes it is, and also happens to be how OMAPs achieve maximum power
>> savings - when low power modes are achieved in OMAP(automatic
>> hardware assisted commands are send to the specific command
>> registers in PMIC and viceversa on wakeup) - but this also happens
>> to be very specific to OMAP way of handling things. I can refer to
>> the Reference Manual as to how it actually works, but that'd be an
>> overkill, I will try to expand on the bindings a little more, I
>> guess.
>
> OK, so this is a register defined by the OMAP architecture?  I think

I might say yes, but that will only be because I just know OMAP 
architecture alone, Have'nt had enough indepth knowledge of other 
architectures to make a broader statement :(

> it's reasonable to add something to allow this to be obtained to the
> core, using a DT property seems yucky since every board usingt this is
> going to have to cut'n'paste the value.  Some sort of custom parameter
> readback thing perhaps, it doesn't have to be too generic.

custom of->match data parameter is how it is done today in this patch

>
>>> Anything that implements a custom set_voltage() won't work with your
>>> data structure either...
>
>> It would not work with OMAP either ;). But that said, drivers do
>
> Yes, that's kind of my point - as with the code Paul was implementing it
> doesn't matter if you can't support every single regualtor since the
> hardware design constrains what the regulator can do.  The regualtor
> framework already has helpers which factor out the code for anything
> which has the limiations the OMAP hardware has (or where it doesn't we
> could add them) so there shouldn't be any need for a driver to provide
> custom callbacks.

custom callback context was as follows: if we force the SoC generic 
regulators to use OMAP custom interfaces, then we have a need for custom 
callbacks, else w.r.t PMIC data, covered above, we do not need any 
custom callbacks, agreed.

>> freely implement custom set_voltage/get_voltage primarily because
>> there are ranges in supported voltages that are non-linear and try
>> to be generic to work on non-OMAP platforms as well. However, within
>> the supported range, only the linear ranges are used with OMAP.
>
> OK, that's a bit more interesting but I expect such regulators will
> actually work with the linear ranges helpers I added the other day
> (Marvell had a PMIC using them and I realised that the same pattern can
> be applied to a bunch of other devices).  Do you think that'd cover the
> cases you're aware of?
>
> Another option is for the drivers to provide the data and use the
> helpers for their linear ranges as part of a more complex
> implementation.

Having looked at a few regulator driver implementations, there seems to 
be the following combinations:
a) drivers which have n ranges of linear voltages for incremental selector
b) drivers with 1 range of linear voltages only for all ranges of selectors
c) drivers with 1 range of linear voltages and nonlinear voltage values 
for other vsels.


>
>>>> OMAP VC hardware has no idea about how long to wait before giving up
>>>> on an ongoing i2c transaction. This may depend on PMIC and what it
>>>> does before acking on i2c.
>
>>> So pick a high number (it's only for error cases...)?
>
>> from hardware perspective yeah, if it does not lockup (based on
>> erratas on specific devices ;) ). it also controls in part, the
>> latency of response to Voltage processor from Voltage controller
>> also needed for computing SmartReflex latencies (as it should
>> consider worst case conditions)
>
> OK, that's a bit more fun but I think the kernel wants that information
> in general anyway since a software cpufreq driver or something might
> want to make the same latency decisions.  This is what set_voltage_time()
> is for in part.  But to a first approximation is there really much
> variation in the numbers?

between PMICs? yep, twl4030 does 4mV/uSec, 6030 can do 6mV/uSec, 
TPS62361 can do 32mV/uSec, TWL6035/37 does 0.220mV/uSec

For a given PMIC, voltage X to Y time will vary ofcourse. SmartReflex 
AVS is something cpufreq wont know about -> completely different topic 
of its own :)

What we are representing is data for PMIC supported on specific SoC 
hardware controller interface meant just for PMIC - IMHO, it is similar 
to data that twl-regulator stores for the SMPS/LDOs that it can control 
- given these are starting to become legacy platforms (OMAP3,4,5) and 
the fact that it is a one-off(at this point in time) knwon custom 
hardware doing stuff like this, we would'nt expect too much churn either 
there(new PMICs added etc). On the flip side, I do understand your 
concern with scalability and ease of re-use here. but given that we 
have'nt seen much of a churn in this data for years on the go now, I'd 
expect lesser in the future. This dedicated interface has been dropped 
in our future chips precisely due to the lack of scalability to PMIC 
choices such as those using SPI.

just my 2 cents.
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-08 17:22                   ` Nishanth Menon
@ 2013-07-09 15:29                     ` Mark Brown
  -1 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-09 15:29 UTC (permalink / raw)
  To: Nishanth Menon
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

[-- Attachment #1: Type: text/plain, Size: 2344 bytes --]

On Mon, Jul 08, 2013 at 12:22:36PM -0500, Nishanth Menon wrote:

> case #1 - TPS62361 has a single SMPS and a single generic i2c bus,
> one can talk on. In this case, you'd associate the regulator device
> in one place - i2cX or on custom SoC hardware interface.

> When used with custom SoC hardware interface, generic tps62361
> regulator which talks regular i2c wont even probe for omap_pmic to
> get the regulator data from tps62361 regulator driver. Even if we
> were to define the generic TPS62361 in dts nodes, it will fail probe
> as it cant access any of it's registers using generic i2c.

This seems like something we should be able to cope with by for example
adding a bus for the custom PMIC interface or otherwise finding a way to
get to the data at runtime based on the compatible string.  This would
need some custom code in the regulators but would have the advantage of
keeping the data the same at least.  Hrm.

> >Another option is for the drivers to provide the data and use the
> >helpers for their linear ranges as part of a more complex
> >implementation.

> Having looked at a few regulator driver implementations, there seems
> to be the following combinations:
> a) drivers which have n ranges of linear voltages for incremental selector
> b) drivers with 1 range of linear voltages only for all ranges of selectors
> c) drivers with 1 range of linear voltages and nonlinear voltage
> values for other vsels.

Everything else is just a special case of option a - either there's just
a single range or there's a bunch of ranges each with a single value.  I
suspect that ranges will be the most useful thing for any hardware which
can practically be used by these regulators anyway.

> >OK, that's a bit more fun but I think the kernel wants that information
> >in general anyway since a software cpufreq driver or something might
> >want to make the same latency decisions.  This is what set_voltage_time()
> >is for in part.  But to a first approximation is there really much
> >variation in the numbers?

> between PMICs? yep, twl4030 does 4mV/uSec, 6030 can do 6mV/uSec,
> TPS62361 can do 32mV/uSec, TWL6035/37 does 0.220mV/uSec

Those are ramp rates, they're not I2C I/O limits.  Ramp rates we already
know about.  I think what you're saying here is that this latency value
is actually about worst case ramp times?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-09 15:29                     ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-09 15:29 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jul 08, 2013 at 12:22:36PM -0500, Nishanth Menon wrote:

> case #1 - TPS62361 has a single SMPS and a single generic i2c bus,
> one can talk on. In this case, you'd associate the regulator device
> in one place - i2cX or on custom SoC hardware interface.

> When used with custom SoC hardware interface, generic tps62361
> regulator which talks regular i2c wont even probe for omap_pmic to
> get the regulator data from tps62361 regulator driver. Even if we
> were to define the generic TPS62361 in dts nodes, it will fail probe
> as it cant access any of it's registers using generic i2c.

This seems like something we should be able to cope with by for example
adding a bus for the custom PMIC interface or otherwise finding a way to
get to the data at runtime based on the compatible string.  This would
need some custom code in the regulators but would have the advantage of
keeping the data the same at least.  Hrm.

> >Another option is for the drivers to provide the data and use the
> >helpers for their linear ranges as part of a more complex
> >implementation.

> Having looked at a few regulator driver implementations, there seems
> to be the following combinations:
> a) drivers which have n ranges of linear voltages for incremental selector
> b) drivers with 1 range of linear voltages only for all ranges of selectors
> c) drivers with 1 range of linear voltages and nonlinear voltage
> values for other vsels.

Everything else is just a special case of option a - either there's just
a single range or there's a bunch of ranges each with a single value.  I
suspect that ranges will be the most useful thing for any hardware which
can practically be used by these regulators anyway.

> >OK, that's a bit more fun but I think the kernel wants that information
> >in general anyway since a software cpufreq driver or something might
> >want to make the same latency decisions.  This is what set_voltage_time()
> >is for in part.  But to a first approximation is there really much
> >variation in the numbers?

> between PMICs? yep, twl4030 does 4mV/uSec, 6030 can do 6mV/uSec,
> TPS62361 can do 32mV/uSec, TWL6035/37 does 0.220mV/uSec

Those are ramp rates, they're not I2C I/O limits.  Ramp rates we already
know about.  I think what you're saying here is that this latency value
is actually about worst case ramp times?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130709/3dbf5b82/attachment.sig>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-09 15:29                     ` Mark Brown
@ 2013-07-09 16:04                       ` Nishanth Menon
  -1 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-09 16:04 UTC (permalink / raw)
  To: Mark Brown
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

On 07/09/2013 10:29 AM, Mark Brown wrote:
> On Mon, Jul 08, 2013 at 12:22:36PM -0500, Nishanth Menon wrote:
>
>> case #1 - TPS62361 has a single SMPS and a single generic i2c bus,
>> one can talk on. In this case, you'd associate the regulator device
>> in one place - i2cX or on custom SoC hardware interface.
>
>> When used with custom SoC hardware interface, generic tps62361
>> regulator which talks regular i2c wont even probe for omap_pmic to
>> get the regulator data from tps62361 regulator driver. Even if we
>> were to define the generic TPS62361 in dts nodes, it will fail probe
>> as it cant access any of it's registers using generic i2c.
>
> This seems like something we should be able to cope with by for example
> adding a bus for the custom PMIC interface or otherwise finding a way to

I had considered introducing a custom bus for custom PMIC interface, but 
as you stated below, standard regulators will probably need some custom 
monkeying around.

> get to the data at runtime based on the compatible string.  This would
> need some custom code in the regulators but would have the advantage of
> keeping the data the same at least.  Hrm.

Ofcourse,this will be to add custom set/get_voltage implementation using 
this "custom API" which we discussed was'nt that good an idea.

I am at a stalemate as to where we should go from here.

>
>>> Another option is for the drivers to provide the data and use the
>>> helpers for their linear ranges as part of a more complex
>>> implementation.
>
>> Having looked at a few regulator driver implementations, there seems
>> to be the following combinations:
>> a) drivers which have n ranges of linear voltages for incremental selector
>> b) drivers with 1 range of linear voltages only for all ranges of selectors
>> c) drivers with 1 range of linear voltages and nonlinear voltage
>> values for other vsels.
>
> Everything else is just a special case of option a - either there's just
> a single range or there's a bunch of ranges each with a single value.  I
> suspect that ranges will be the most useful thing for any hardware which
> can practically be used by these regulators anyway.

True, but slightly different topic though.

>
>>> OK, that's a bit more fun but I think the kernel wants that information
>>> in general anyway since a software cpufreq driver or something might
>>> want to make the same latency decisions.  This is what set_voltage_time()
>>> is for in part.  But to a first approximation is there really much
>>> variation in the numbers?
>
>> between PMICs? yep, twl4030 does 4mV/uSec, 6030 can do 6mV/uSec,
>> TPS62361 can do 32mV/uSec, TWL6035/37 does 0.220mV/uSec
>
> Those are ramp rates, they're not I2C I/O limits.  Ramp rates we already
> know about.  I think what you're saying here is that this latency value
> is actually about worst case ramp times?
Arrgh.. my bad. I confused ramp time with I2C transfer timeout 
parameter. I know that I2C bus can be held[1] by PMIC as long as it is 
busy. Some custom ASIC can do some weird stuff I suppose. I dont seem to 
have clear data points in the sketchy TRMs for 6030/2 , 6035, 5030, for 
these other than to state it is i2c specification compliant (/me 
grumbles). So, I just have emperical value which is a bit conservative 
and seem to work on the devices.


[1] http://www.i2c-bus.org/clock-generation-stretching-arbitration/
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-09 16:04                       ` Nishanth Menon
  0 siblings, 0 replies; 51+ messages in thread
From: Nishanth Menon @ 2013-07-09 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/09/2013 10:29 AM, Mark Brown wrote:
> On Mon, Jul 08, 2013 at 12:22:36PM -0500, Nishanth Menon wrote:
>
>> case #1 - TPS62361 has a single SMPS and a single generic i2c bus,
>> one can talk on. In this case, you'd associate the regulator device
>> in one place - i2cX or on custom SoC hardware interface.
>
>> When used with custom SoC hardware interface, generic tps62361
>> regulator which talks regular i2c wont even probe for omap_pmic to
>> get the regulator data from tps62361 regulator driver. Even if we
>> were to define the generic TPS62361 in dts nodes, it will fail probe
>> as it cant access any of it's registers using generic i2c.
>
> This seems like something we should be able to cope with by for example
> adding a bus for the custom PMIC interface or otherwise finding a way to

I had considered introducing a custom bus for custom PMIC interface, but 
as you stated below, standard regulators will probably need some custom 
monkeying around.

> get to the data at runtime based on the compatible string.  This would
> need some custom code in the regulators but would have the advantage of
> keeping the data the same at least.  Hrm.

Ofcourse,this will be to add custom set/get_voltage implementation using 
this "custom API" which we discussed was'nt that good an idea.

I am at a stalemate as to where we should go from here.

>
>>> Another option is for the drivers to provide the data and use the
>>> helpers for their linear ranges as part of a more complex
>>> implementation.
>
>> Having looked at a few regulator driver implementations, there seems
>> to be the following combinations:
>> a) drivers which have n ranges of linear voltages for incremental selector
>> b) drivers with 1 range of linear voltages only for all ranges of selectors
>> c) drivers with 1 range of linear voltages and nonlinear voltage
>> values for other vsels.
>
> Everything else is just a special case of option a - either there's just
> a single range or there's a bunch of ranges each with a single value.  I
> suspect that ranges will be the most useful thing for any hardware which
> can practically be used by these regulators anyway.

True, but slightly different topic though.

>
>>> OK, that's a bit more fun but I think the kernel wants that information
>>> in general anyway since a software cpufreq driver or something might
>>> want to make the same latency decisions.  This is what set_voltage_time()
>>> is for in part.  But to a first approximation is there really much
>>> variation in the numbers?
>
>> between PMICs? yep, twl4030 does 4mV/uSec, 6030 can do 6mV/uSec,
>> TPS62361 can do 32mV/uSec, TWL6035/37 does 0.220mV/uSec
>
> Those are ramp rates, they're not I2C I/O limits.  Ramp rates we already
> know about.  I think what you're saying here is that this latency value
> is actually about worst case ramp times?
Arrgh.. my bad. I confused ramp time with I2C transfer timeout 
parameter. I know that I2C bus can be held[1] by PMIC as long as it is 
busy. Some custom ASIC can do some weird stuff I suppose. I dont seem to 
have clear data points in the sketchy TRMs for 6030/2 , 6035, 5030, for 
these other than to state it is i2c specification compliant (/me 
grumbles). So, I just have emperical value which is a bit conservative 
and seem to work on the devices.


[1] http://www.i2c-bus.org/clock-generation-stretching-arbitration/
-- 
Regards,
Nishanth Menon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
  2013-07-09 16:04                       ` Nishanth Menon
@ 2013-07-10  9:19                         ` Mark Brown
  -1 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-10  9:19 UTC (permalink / raw)
  To: Nishanth Menon
  Cc: Benoît Cousson, Tony Lindgren, Kevin Hilman,
	devicetree-discuss, linux-doc, linux-kernel, linux-omap,
	linux-arm-kernel, Grygorii Strashko, Taras Kondratiuk

[-- Attachment #1: Type: text/plain, Size: 1996 bytes --]

On Tue, Jul 09, 2013 at 11:04:23AM -0500, Nishanth Menon wrote:
> On 07/09/2013 10:29 AM, Mark Brown wrote:

> >This seems like something we should be able to cope with by for example
> >adding a bus for the custom PMIC interface or otherwise finding a way to

> I had considered introducing a custom bus for custom PMIC interface,
> but as you stated below, standard regulators will probably need some
> custom monkeying around.

We should just be able to use the platform bus here.

> >get to the data at runtime based on the compatible string.  This would
> >need some custom code in the regulators but would have the advantage of
> >keeping the data the same at least.  Hrm.

> Ofcourse,this will be to add custom set/get_voltage implementation
> using this "custom API" which we discussed was'nt that good an idea.

No, if the regulator isn't being interacted with directly then it
doesn't need to export any operations - just data.  The operations would
come from the magic SoC hardware that controls the regulator.  The code
in the drivers should be very small, if it isn't there's no point in
doing this.

> >>between PMICs? yep, twl4030 does 4mV/uSec, 6030 can do 6mV/uSec,
> >>TPS62361 can do 32mV/uSec, TWL6035/37 does 0.220mV/uSec

> >Those are ramp rates, they're not I2C I/O limits.  Ramp rates we already
> >know about.  I think what you're saying here is that this latency value
> >is actually about worst case ramp times?

> Arrgh.. my bad. I confused ramp time with I2C transfer timeout
> parameter. I know that I2C bus can be held[1] by PMIC as long as it
> is busy. Some custom ASIC can do some weird stuff I suppose. I dont
> seem to have clear data points in the sketchy TRMs for 6030/2 ,
> 6035, 5030, for these other than to state it is i2c specification
> compliant (/me grumbles). So, I just have emperical value which is a
> bit conservative and seem to work on the devices.

OK, no problem - like we said further up the thread I think adding
something to get the data

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP
@ 2013-07-10  9:19                         ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2013-07-10  9:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 09, 2013 at 11:04:23AM -0500, Nishanth Menon wrote:
> On 07/09/2013 10:29 AM, Mark Brown wrote:

> >This seems like something we should be able to cope with by for example
> >adding a bus for the custom PMIC interface or otherwise finding a way to

> I had considered introducing a custom bus for custom PMIC interface,
> but as you stated below, standard regulators will probably need some
> custom monkeying around.

We should just be able to use the platform bus here.

> >get to the data at runtime based on the compatible string.  This would
> >need some custom code in the regulators but would have the advantage of
> >keeping the data the same at least.  Hrm.

> Ofcourse,this will be to add custom set/get_voltage implementation
> using this "custom API" which we discussed was'nt that good an idea.

No, if the regulator isn't being interacted with directly then it
doesn't need to export any operations - just data.  The operations would
come from the magic SoC hardware that controls the regulator.  The code
in the drivers should be very small, if it isn't there's no point in
doing this.

> >>between PMICs? yep, twl4030 does 4mV/uSec, 6030 can do 6mV/uSec,
> >>TPS62361 can do 32mV/uSec, TWL6035/37 does 0.220mV/uSec

> >Those are ramp rates, they're not I2C I/O limits.  Ramp rates we already
> >know about.  I think what you're saying here is that this latency value
> >is actually about worst case ramp times?

> Arrgh.. my bad. I confused ramp time with I2C transfer timeout
> parameter. I know that I2C bus can be held[1] by PMIC as long as it
> is busy. Some custom ASIC can do some weird stuff I suppose. I dont
> seem to have clear data points in the sketchy TRMs for 6030/2 ,
> 6035, 5030, for these other than to state it is i2c specification
> compliant (/me grumbles). So, I just have emperical value which is a
> bit conservative and seem to work on the devices.

OK, no problem - like we said further up the thread I think adding
something to get the data
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130710/c6ebfb65/attachment-0001.sig>

^ permalink raw reply	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2013-07-10  9:51 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-21 21:25 [RFC PATCH V2 0/8] regulator/OMAP: support VC/VP support in dts Nishanth Menon
2013-06-21 21:25 ` Nishanth Menon
2013-06-21 21:25 ` Nishanth Menon
2013-06-21 21:25 ` [RFC PATCH V2 1/8] regulator: Introduce OMAP regulator to control PMIC over VC/VP Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-07-04 15:41   ` Mark Brown
2013-07-04 15:41     ` Mark Brown
2013-07-04 15:41     ` Mark Brown
2013-07-05 13:55     ` Nishanth Menon
2013-07-05 13:55       ` Nishanth Menon
2013-07-05 14:08       ` Mark Brown
2013-07-05 14:08         ` Mark Brown
2013-07-05 14:50         ` Nishanth Menon
2013-07-05 14:50           ` Nishanth Menon
2013-07-05 14:50           ` Nishanth Menon
2013-07-05 16:52           ` Mark Brown
2013-07-05 16:52             ` Mark Brown
2013-07-05 17:33             ` Nishanth Menon
2013-07-05 17:33               ` Nishanth Menon
2013-07-05 17:47               ` Mark Brown
2013-07-05 17:47                 ` Mark Brown
2013-07-08 17:22                 ` Nishanth Menon
2013-07-08 17:22                   ` Nishanth Menon
2013-07-09 15:29                   ` Mark Brown
2013-07-09 15:29                     ` Mark Brown
2013-07-09 16:04                     ` Nishanth Menon
2013-07-09 16:04                       ` Nishanth Menon
2013-07-10  9:19                       ` Mark Brown
2013-07-10  9:19                         ` Mark Brown
2013-06-21 21:25 ` [RFC PATCH V2 2/8] PM / AVS: Introduce support for OMAP Voltage Controller(VC) with device tree nodes Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25 ` [RFC PATCH V2 3/8] PM / AVS: Introduce support for OMAP Voltage Processor(VP) " Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25 ` [RFC PATCH V2 4/8] ARM: dts: OMAP4: add voltage controller nodes Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25 ` [RFC PATCH V2 5/8] ARM: dts: OMAP4: add voltage processor nodes Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25 ` [RFC PATCH V2 6/8] ARM: dts: TWL6030/OMAP4: Add OMAP voltage path linkage Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25 ` [RFC PATCH V2 7/8] ARM: dts: omap4-panda-es: add TPS62361 supply for vdd_mpu Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25 ` [RFC PATCH V2 8/8] ARM: dts: OMAP4-panda-es: use tps regulator as cpu0 supply Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon
2013-06-21 21:25   ` Nishanth Menon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.