aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-08-14 17:46:04 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-08-14 17:46:04 +0800
commit3f57fe6b186a1f38b03f37197216293adeebed50 (patch)
treec70ebcf401e14356d6a7523366bb109c397ac6a7
parentd3e8e2c29991a30a1c0fe1a2a3505b01a5316034 (diff)
parentffe0fb71c9031b7b982cf9d2d650996fea594230 (diff)
Merge branch 'tracking-hilt-pmic-regulator' into integration-lsk-pubintegration-lsk-pub
-rw-r--r--arch/arm/boot/dts/hi4511.dts516
-rw-r--r--arch/arm/configs/hs_defconfig28
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/hi6421-pmic-core.c298
-rw-r--r--drivers/regulator/Kconfig7
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/hi6421-regulator.c580
-rw-r--r--include/linux/mfd/hi6421-pmic.h84
9 files changed, 1522 insertions, 2 deletions
diff --git a/arch/arm/boot/dts/hi4511.dts b/arch/arm/boot/dts/hi4511.dts
index 915f380fe787..006e9e339786 100644
--- a/arch/arm/boot/dts/hi4511.dts
+++ b/arch/arm/boot/dts/hi4511.dts
@@ -777,6 +777,522 @@
pinctrl-single,bias-pulldown = <2 2 0 2>;
pinctrl-single,bias-pullup = <0 1 0 1>;
};
+ pmic_int_cfg_func: pincfg_pmic_func {
+ pinctrl-single,pins = <
+ 0x018 0 /* GPIO159 (IOCFG003) */
+ >;
+ };
};
+
+ pmic: pmic@fcc00000 {
+ compatible = "hisilicon,hi6421-pmic";
+ reg = <0xfcc00000 0x0180>; /* 0x60 << 2 */
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ gpios = <&gpio19 7 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_int_cfg_func>;
+
+ ldo0: ldo@20 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO0";
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ hisilicon,hi6421-ctrl = <0x20 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x20 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <10000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo1: ldo@21 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO1";
+ regulator-min-microvolt = <1700000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x21 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x21 0x03>;
+ hisilicon,hi6421-n-voltages = <4>;
+ hisilicon,hi6421-vset-table = <1700000>, <1800000>,
+ <1900000>, <2000000>;
+ hisilicon,hi6421-off-on-delay-us = <10000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <5000>;
+ };
+
+ ldo2: ldo@22 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO2";
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x22 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x22 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1050000>, <1100000>,
+ <1150000>, <1200000>,
+ <1250000>, <1300000>,
+ <1350000>, <1400000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo3: ldo@23 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO3";
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x23 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x23 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1050000>, <1100000>,
+ <1150000>, <1200000>,
+ <1250000>, <1300000>,
+ <1350000>, <1400000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo4: ldo@24 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO4";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x24 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x24 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo5: ldo@25 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO5";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x25 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x25 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo6: ldo@26 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO6";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x26 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x26 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo7: ldo@27 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO7";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x27 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x27 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <5000>;
+ };
+
+ ldo8: ldo@28 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO8";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x28 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x28 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2600000>,
+ <2700000>, <2850000>,
+ <3000000>, <3300000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo9: ldo@29 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO9";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x29 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x29 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo10: ldo@2a {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO10";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2a 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2a 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo11: ldo@2b {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO11";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2b 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2b 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo12: ldo@2c {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO12";
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ hisilicon,hi6421-ctrl = <0x2c 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2c 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo13: ldo@2d {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO13";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2d 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2d 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo14: ldo@2e {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO14";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2e 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2e 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo15: ldo@2f {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO15";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3300000>;
+ hisilicon,hi6421-ctrl = <0x2f 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2f 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2600000>,
+ <2700000>, <2850000>,
+ <3000000>, <3300000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo16: ldo@30 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO16";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x30 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x30 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo17: ldo@31 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO17";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x31 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x31 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo18: ldo@32 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO18";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x32 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x32 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo19: ldo@33 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO19";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x2a 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x2a 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldo20: ldo@34 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDO20";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <3000000>;
+ hisilicon,hi6421-ctrl = <0x34 0x10 0x20>;
+ hisilicon,hi6421-vset = <0x34 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+ <2400000>, <2500000>,
+ <2600000>, <2700000>,
+ <2850000>, <3000000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <8000>;
+ };
+
+ ldoaudio: ldo@36 {
+ compatible = "hisilicon,hi6421-ldo";
+ regulator-name = "LDOAUDIO";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <3300000>;
+ hisilicon,hi6421-ctrl = <0x36 0x01 0x02>;
+ hisilicon,hi6421-vset = <0x36 0x70>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <2800000>, <2850000>,
+ <2900000>, <2950000>,
+ <3000000>, <3100000>,
+ <3200000>, <3300000>;
+ hisilicon,hi6421-off-on-delay-us = <40000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ hisilicon,hi6421-eco-microamp = <5000>;
+ };
+
+ buck0: buck@0c {
+ compatible = "hisilicon,hi6421-buck012";
+ regulator-name = "BUCK0";
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1600000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x0c 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x0d 0x7f>;
+ hisilicon,hi6421-n-voltages = <128>;
+ hisilicon,hi6421-uv-step = <7086>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <300>;
+ };
+
+ buck1: buck@0e {
+ compatible = "hisilicon,hi6421-buck012";
+ regulator-name = "BUCK1";
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1600000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x0e 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x0f 0x7f>;
+ hisilicon,hi6421-n-voltages = <128>;
+ hisilicon,hi6421-uv-step = <7086>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <300>;
+ };
+
+ buck2: buck@10 {
+ compatible = "hisilicon,hi6421-buck012";
+ regulator-name = "BUCK2";
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1600000>;
+ regulator-boot-on;
+ hisilicon,hi6421-ctrl = <0x10 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x11 0x7f>;
+ hisilicon,hi6421-n-voltages = <128>;
+ hisilicon,hi6421-uv-step = <7086>;
+ hisilicon,hi6421-off-on-delay-us = <100>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ };
+
+ buck3: buck@12 {
+ compatible = "hisilicon,hi6421-buck345";
+ regulator-name = "BUCK3";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x12 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x13 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <950000>, <1050000>,
+ <1100000>, <1170000>,
+ <1134000>, <1150000>,
+ <1167000>, <1200000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ };
+
+ buck4: buck@14 {
+ compatible = "hisilicon,hi6421-buck345";
+ regulator-name = "BUCK4";
+ regulator-min-microvolt = <1150000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x14 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x15 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1150000>, <1200000>,
+ <1250000>, <1350000>,
+ <1700000>, <1800000>,
+ <1900000>, <2000000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ };
+
+ buck5: buck@16 {
+ compatible = "hisilicon,hi6421-buck345";
+ regulator-name = "BUCK5";
+ regulator-min-microvolt = <1150000>;
+ regulator-max-microvolt = <1900000>;
+ regulator-boot-on;
+ regulator-always-on;
+ hisilicon,hi6421-ctrl = <0x16 0x01 0x10>;
+ hisilicon,hi6421-vset = <0x17 0x07>;
+ hisilicon,hi6421-n-voltages = <8>;
+ hisilicon,hi6421-vset-table = <1150000>, <1200000>,
+ <1250000>, <1350000>,
+ <1600000>, <1700000>,
+ <1800000>, <1900000>;
+ hisilicon,hi6421-off-on-delay-us = <20000>;
+ hisilicon,hi6421-enable-time-us = <250>;
+ };
+
+ onkey {
+ compatible = "hisilicon,hi6421-onkey";
+ interrupt-parent = <&pmic>;
+ interrupts = <7 0>, <6 0>, <5 0>, <4 0>;
+ interrupt-names = "down", "up", "hold 1s", "hold 10s";
+ };
+
+ rtc {
+ compatible = "hisilicon,hi6421-rtc";
+ interrupt-parent = <&pmic>;
+ interrupts = <0 0>;
+ };
+ }; /* end of pmic */
};
};
diff --git a/arch/arm/configs/hs_defconfig b/arch/arm/configs/hs_defconfig
index d4314c9572ce..af9bec4963f1 100644
--- a/arch/arm/configs/hs_defconfig
+++ b/arch/arm/configs/hs_defconfig
@@ -1287,8 +1287,34 @@ CONFIG_BCMA_POSSIBLE=y
# CONFIG_MFD_VIPERBOARD is not set
# CONFIG_MFD_RETU is not set
# CONFIG_MFD_AS3711 is not set
+CONFIG_MFD_HI6421_PMIC=y
CONFIG_VEXPRESS_CONFIG=y
-# CONFIG_REGULATOR is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_DUMMY is not set
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_GPIO is not set
+# CONFIG_REGULATOR_AD5398 is not set
+# CONFIG_REGULATOR_FAN53555 is not set
+# CONFIG_REGULATOR_ISL6271A is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_MAX8649 is not set
+# CONFIG_REGULATOR_MAX8660 is not set
+# CONFIG_REGULATOR_MAX8952 is not set
+# CONFIG_REGULATOR_MAX8973 is not set
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_LP3972 is not set
+# CONFIG_REGULATOR_LP872X is not set
+# CONFIG_REGULATOR_LP8755 is not set
+# CONFIG_REGULATOR_TPS51632 is not set
+# CONFIG_REGULATOR_TPS62360 is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+# CONFIG_REGULATOR_TPS6507X is not set
+# CONFIG_REGULATOR_TPS6524X is not set
+# CONFIG_REGULATOR_VEXPRESS is not set
+CONFIG_REGULATOR_HI6421=y
# CONFIG_MEDIA_SUPPORT is not set
#
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d54e985748b7..b74ed0fc4e6f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1116,6 +1116,14 @@ config MFD_WM8994
core support for the WM8994, in order to use the actual
functionaltiy of the device other drivers must be enabled.
+config MFD_HI6421_PMIC
+ tristate "HiSilicon Hi6421 PMU/Codec IC"
+ depends on OF
+ help
+ This driver supports HiSilicon Hi6421 power management and codec IC,
+ including regulators, codec, ADCs, Coulomb counter, etc. Memory
+ mapped I/O ports are the way of communication with it.
+
endmenu
endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 718e94a2a9a7..58cda2579185 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -155,3 +155,4 @@ obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o
obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o
+obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o
diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c
new file mode 100644
index 000000000000..8d3053aced49
--- /dev/null
+++ b/drivers/mfd/hi6421-pmic-core.c
@@ -0,0 +1,298 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/mfd/hi6421-pmic.h>
+
+#include <asm/mach/irq.h>
+
+/* 8-bit register offset in PMIC */
+#define HI6421_REG_IRQ1 1
+#define HI6421_REG_IRQ2 2
+#define HI6421_REG_IRQ3 3
+#define HI6421_REG_IRQM1 4
+#define HI6421_REG_IRQM2 5
+#define HI6421_REG_IRQM3 6
+
+static struct of_device_id of_hi6421_pmic_child_match_tbl[] = {
+ /* regulators */
+ {
+ .compatible = "hisilicon,hi6421-ldo",
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck012",
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck345",
+ },
+ { /* end */ }
+};
+
+static struct of_device_id of_hi6421_pmic_match_tbl[] = {
+ {
+ .compatible = "hisilicon,hi6421-pmic",
+ },
+ { /* end */ }
+};
+
+/*
+ * The PMIC register is only 8-bit.
+ * Hisilicon SoC use hardware to map PMIC register into SoC mapping.
+ * At here, we are accessing SoC register with 32-bit.
+ */
+u32 hi6421_pmic_read(struct hi6421_pmic *pmic, int reg)
+{
+ unsigned long flags;
+ u32 ret;
+ spin_lock_irqsave(&pmic->lock, flags);
+ ret = readl_relaxed(pmic->regs + (reg << 2));
+ spin_unlock_irqrestore(&pmic->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(hi6421_pmic_read);
+
+void hi6421_pmic_write(struct hi6421_pmic *pmic, int reg, u32 val)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&pmic->lock, flags);
+ writel_relaxed(val, pmic->regs + (reg << 2));
+ spin_unlock_irqrestore(&pmic->lock, flags);
+}
+EXPORT_SYMBOL(hi6421_pmic_write);
+
+void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg,
+ u32 mask, u32 bits)
+{
+ u32 data;
+
+ spin_lock(&pmic->lock);
+ data = readl_relaxed(pmic->regs + (reg << 2)) & ~mask;
+ data |= mask & bits;
+ writel_relaxed(data, pmic->regs + (reg << 2));
+ spin_unlock(&pmic->lock);
+}
+EXPORT_SYMBOL(hi6421_pmic_rmw);
+
+static int hi6421_to_irq(struct hi6421_pmic *pmic, unsigned offset)
+{
+ return irq_create_mapping(pmic->domain, offset);
+}
+
+static irqreturn_t hi6421_irq_handler(int irq, void *data)
+{
+ struct hi6421_pmic *pmic = (struct hi6421_pmic *)data;
+ unsigned long pending;
+ int i, offset;
+
+
+ for (i = HI6421_REG_IRQ1; i <= HI6421_REG_IRQ3; i++) {
+ spin_lock(&pmic->lock);
+ pending = readl_relaxed(pmic->regs + (i << 2));
+ pending &= HI6421_MASK_FIELD;
+ writel_relaxed(pending, pmic->regs + ((i + 3) << 2));
+ spin_unlock(&pmic->lock);
+
+ if (pending) {
+ for_each_set_bit(offset, &pending, HI6421_BITS)
+ generic_handle_irq(hi6421_to_irq(pmic, offset));
+ }
+
+ spin_lock(&pmic->lock);
+ writel_relaxed(0, pmic->regs + ((i + 3) << 2));
+ writel_relaxed(pending, pmic->regs + (i << 2));
+ spin_unlock(&pmic->lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hi6421_irq_mask(struct irq_data *d)
+{
+ struct hi6421_pmic *pmic = irq_data_get_irq_chip_data(d);
+ u32 data, offset;
+
+ offset = ((irqd_to_hwirq(d) >> 3) + HI6421_REG_IRQM1) << 2;
+ spin_lock(&pmic->lock);
+ data = readl_relaxed(pmic->regs + offset);
+ data |= irqd_to_hwirq(d) % 8;
+ writel_relaxed(data, pmic->regs + offset);
+ spin_unlock(&pmic->lock);
+}
+
+static void hi6421_irq_unmask(struct irq_data *d)
+{
+ struct hi6421_pmic *pmic = irq_data_get_irq_chip_data(d);
+ u32 data, offset;
+
+ offset = ((irqd_to_hwirq(d) >> 3) + HI6421_REG_IRQM1) << 2;
+ spin_lock(&pmic->lock);
+ data = readl_relaxed(pmic->regs + offset);
+ data &= ~(irqd_to_hwirq(d) % 8);
+ writel_relaxed(data, pmic->regs + offset);
+ spin_unlock(&pmic->lock);
+}
+
+static struct irq_chip hi6421_irqchip = {
+ .name = "pmic",
+ .irq_mask = hi6421_irq_mask,
+ .irq_unmask = hi6421_irq_unmask,
+};
+
+static int hi6421_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct hi6421_pmic *pmic = d->host_data;
+
+ irq_set_chip_and_handler_name(virq, &hi6421_irqchip,
+ handle_simple_irq, "hi6421");
+ irq_set_chip_data(virq, pmic);
+ irq_set_irq_type(virq, IRQ_TYPE_NONE);
+
+ return 0;
+}
+
+static struct irq_domain_ops hi6421_domain_ops = {
+ .map = hi6421_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int hi6421_pmic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct hi6421_pmic *pmic = NULL;
+ enum of_gpio_flags flags;
+ int ret;
+
+ pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic) {
+ dev_err(dev, "cannot allocate hi6421_pmic device info\n");
+ return -ENOMEM;
+ }
+
+ /* get resources */
+ pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pmic->res) {
+ dev_err(dev, "platform_get_resource err\n");
+ return -ENOENT;
+ }
+
+ if (!devm_request_mem_region(dev, pmic->res->start,
+ resource_size(pmic->res),
+ pdev->name)) {
+ dev_err(dev, "cannot claim register memory\n");
+ return -ENOMEM;
+ }
+
+ pmic->regs = devm_ioremap(dev, pmic->res->start,
+ resource_size(pmic->res));
+ if (!pmic->regs) {
+ dev_err(dev, "cannot map register memory\n");
+ return -ENOMEM;
+ }
+
+ /* TODO: get and enable clk request */
+
+ spin_lock_init(&pmic->lock);
+
+ pmic->gpio = of_get_gpio_flags(np, 0, &flags);
+ if (pmic->gpio < 0)
+ return pmic->gpio;
+ if (!gpio_is_valid(pmic->gpio))
+ return -EINVAL;
+ ret = gpio_request_one(pmic->gpio, GPIOF_IN, "pmic");
+ if (ret < 0) {
+ dev_err(dev, "failed to request gpio%d\n", pmic->gpio);
+ return ret;
+ }
+ pmic->irq = gpio_to_irq(pmic->gpio);
+ /* clear IRQ status */
+ spin_lock(&pmic->lock);
+ writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ1 << 2));
+ writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ2 << 2));
+ writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ3 << 2));
+ spin_unlock(&pmic->lock);
+
+ pmic->domain = irq_domain_add_simple(np, HI6421_NR_IRQ, 0,
+ &hi6421_domain_ops, pmic);
+ if (!pmic->domain)
+ return -ENODEV;
+
+ ret = request_threaded_irq(pmic->irq, hi6421_irq_handler, NULL,
+ IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND,
+ "pmic", pmic);
+
+ platform_set_drvdata(pdev, pmic);
+
+ /* set over-current protection debounce 8ms*/
+ hi6421_pmic_rmw(pmic, OCP_DEB_CTRL_REG, \
+ OCP_DEB_SEL_MASK | OCP_EN_DEBOUNCE_MASK | OCP_AUTO_STOP_MASK, \
+ OCP_DEB_SEL_8MS | OCP_EN_DEBOUNCE_ENABLE);
+
+ /* populate sub nodes */
+ of_platform_populate(np, of_hi6421_pmic_child_match_tbl, NULL, dev);
+
+ return 0;
+}
+
+static int hi6421_pmic_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hi6421_pmic *pmic = platform_get_drvdata(pdev);
+
+ free_irq(pmic->irq, pmic);
+ gpio_free(pmic->gpio);
+ devm_iounmap(dev, pmic->regs);
+ devm_release_mem_region(dev, pmic->res->start,
+ resource_size(pmic->res));
+ devm_kfree(dev, pmic);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver hi6421_pmic_driver = {
+ .driver = {
+ .name = "hi6421_pmic",
+ .owner = THIS_MODULE,
+ .of_match_table = of_hi6421_pmic_match_tbl,
+ },
+ .probe = hi6421_pmic_probe,
+ .remove = hi6421_pmic_remove,
+};
+module_platform_driver(hi6421_pmic_driver);
+
+MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
+MODULE_DESCRIPTION("Hi6421 PMIC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8bb26446037e..08774c02a840 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -514,5 +514,12 @@ config REGULATOR_AS3711
This driver provides support for the voltage regulators on the
AS3711 PMIC
+config REGULATOR_HI6421
+ tristate "HiSilicon Hi6421 PMIC regulators"
+ depends on MFD_HI6421_PMIC
+ help
+ This driver provides support for the voltage regulators on the
+ HiSilicon Hi6421 PMU / Codec IC.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 47a34ff88f98..5a67225c8973 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -70,6 +70,6 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
-
+obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c
new file mode 100644
index 000000000000..a68b1cb78483
--- /dev/null
+++ b/drivers/regulator/hi6421-regulator.c
@@ -0,0 +1,580 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/hi6421-pmic.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+struct hi6421_regulator_register_info {
+ u32 ctrl_reg;
+ u32 enable_mask;
+ u32 eco_mode_mask;
+ u32 vset_reg;
+ u32 vset_mask;
+};
+
+struct hi6421_regulator {
+ const char *name;
+ struct hi6421_regulator_register_info register_info;
+ struct timeval last_off_time;
+ u32 off_on_delay;
+ u32 eco_uA;
+ struct regulator_desc rdesc;
+ int (*dt_parse)(struct hi6421_regulator *, struct platform_device *);
+};
+
+static DEFINE_MUTEX(enable_mutex);
+struct timeval last_enabled;
+
+
+static inline struct hi6421_pmic *rdev_to_pmic(struct regulator_dev *dev)
+{
+ /* regulator_dev parent to->
+ * hi6421 regulator platform device_dev parent to->
+ * hi6421 pmic platform device_dev
+ */
+ return dev_get_drvdata(rdev_get_dev(dev)->parent->parent);
+}
+
+/* helper function to ensure when it returns it is at least 'delay_us'
+ * microseconds after 'since'.
+ */
+static void ensured_time_after(struct timeval since, u32 delay_us)
+{
+ struct timeval now;
+ u64 elapsed_ns64, delay_ns64;
+ u32 actual_us32;
+
+ delay_ns64 = delay_us * NSEC_PER_USEC;
+ do_gettimeofday(&now);
+ elapsed_ns64 = timeval_to_ns(&now) - timeval_to_ns(&since);
+ if (delay_ns64 > elapsed_ns64) {
+ actual_us32 = ((u32)(delay_ns64 - elapsed_ns64) /
+ NSEC_PER_USEC);
+ if (actual_us32 >= 1000) {
+ mdelay(actual_us32 / 1000);
+ udelay(actual_us32 % 1000);
+ } else if (actual_us32 > 0) {
+ udelay(actual_us32);
+ }
+ }
+ return;
+}
+
+static int hi6421_regulator_is_enabled(struct regulator_dev *dev)
+{
+ u32 reg_val;
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg);
+
+ return ((reg_val & sreg->register_info.enable_mask) != 0);
+}
+
+static int hi6421_regulator_enable(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ /* keep a distance of off_on_delay from last time disabled */
+ ensured_time_after(sreg->last_off_time, sreg->off_on_delay);
+
+ /* cannot enable more than one regulator at one time */
+ mutex_lock(&enable_mutex);
+ ensured_time_after(last_enabled, HI6421_REGS_ENA_PROTECT_TIME);
+
+ /* set enable register */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.enable_mask,
+ sreg->register_info.enable_mask);
+
+ do_gettimeofday(&last_enabled);
+ mutex_unlock(&enable_mutex);
+
+ return 0;
+}
+
+static int hi6421_regulator_disable(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ /* set enable register to 0 */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.enable_mask, 0);
+
+ do_gettimeofday(&sreg->last_off_time);
+
+ return 0;
+}
+
+static int hi6421_regulator_get_voltage(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 reg_val, selector;
+
+ /* get voltage selector */
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.vset_reg);
+ selector = (reg_val & sreg->register_info.vset_mask) >>
+ (ffs(sreg->register_info.vset_mask) - 1);
+
+ return sreg->rdesc.ops->list_voltage(dev, selector);
+}
+
+static int hi6421_regulator_ldo_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 vsel;
+ int ret = 0;
+
+ for (vsel = 0; vsel < sreg->rdesc.n_voltages; vsel++) {
+ int uV = sreg->rdesc.volt_table[vsel];
+ /* Break at the first in-range value */
+ if (min_uV <= uV && uV <= max_uV)
+ break;
+ }
+
+ /* unlikely to happen. sanity test done by regulator core */
+ if (unlikely(vsel == sreg->rdesc.n_voltages))
+ return -EINVAL;
+
+ *selector = vsel;
+ /* set voltage selector */
+ hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg,
+ sreg->register_info.vset_mask,
+ vsel << (ffs(sreg->register_info.vset_mask) - 1));
+
+ return ret;
+}
+
+static int hi6421_regulator_buck012_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 vsel;
+ int ret = 0;
+
+ vsel = DIV_ROUND_UP((max_uV - sreg->rdesc.min_uV),
+ sreg->rdesc.uV_step);
+
+ *selector = vsel;
+ /* set voltage selector */
+ hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg,
+ sreg->register_info.vset_mask,
+ vsel << (ffs(sreg->register_info.vset_mask) - 1));
+
+ return ret;
+}
+
+static unsigned int hi6421_regulator_get_mode(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 reg_val;
+
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg);
+ if (reg_val & sreg->register_info.eco_mode_mask)
+ return REGULATOR_MODE_IDLE;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int hi6421_regulator_set_mode(struct regulator_dev *dev,
+ unsigned int mode)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 eco_mode;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ eco_mode = HI6421_ECO_MODE_DISABLE;
+ break;
+ case REGULATOR_MODE_IDLE:
+ eco_mode = HI6421_ECO_MODE_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set mode */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.eco_mode_mask,
+ eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1));
+
+ return 0;
+}
+
+
+unsigned int hi6421_regulator_get_optimum_mode(struct regulator_dev *dev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+
+ if ((load_uA == 0) || (load_uA > sreg->eco_uA))
+ return REGULATOR_MODE_NORMAL;
+ else
+ return REGULATOR_MODE_IDLE;
+}
+
+static int hi6421_dt_parse_common(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ unsigned int register_info[3];
+ int ret = 0;
+
+ /* parse .register_info.ctrl_reg */
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-ctrl",
+ register_info, 3);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-ctrl property set\n");
+ goto dt_parse_common_end;
+ }
+ sreg->register_info.ctrl_reg = register_info[0];
+ sreg->register_info.enable_mask = register_info[1];
+ sreg->register_info.eco_mode_mask = register_info[2];
+
+ /* parse .register_info.vset_reg */
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-vset",
+ register_info, 2);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-vset property set\n");
+ goto dt_parse_common_end;
+ }
+ sreg->register_info.vset_reg = register_info[0];
+ sreg->register_info.vset_mask = register_info[1];
+
+ /* parse .off-on-delay */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-off-on-delay-us",
+ &sreg->off_on_delay);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-off-on-delay-us property set\n");
+ goto dt_parse_common_end;
+ }
+
+ /* parse .enable_time */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-enable-time-us",
+ &rdesc->enable_time);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-enable-time-us property set\n");
+ goto dt_parse_common_end;
+ }
+
+ /* parse .eco_uA */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-eco-microamp",
+ &sreg->eco_uA);
+ if (ret) {
+ sreg->eco_uA = 0;
+ ret = 0;
+ }
+
+dt_parse_common_end:
+ return ret;
+}
+
+static int hi6421_dt_parse_ldo(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ unsigned int *v_table;
+ int ret = 0;
+
+ /* parse .n_voltages, and .volt_table */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-n-voltages",
+ &rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-n-voltages property set\n");
+ goto dt_parse_ldo_end;
+ }
+
+ /* alloc space for .volt_table */
+ v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages,
+ GFP_KERNEL);
+ if (unlikely(!v_table)) {
+ ret = -ENOMEM;
+ dev_err(dev, "no memory for .volt_table\n");
+ goto dt_parse_ldo_end;
+ }
+
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-vset-table",
+ v_table, rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-vset-table property set\n");
+ goto dt_parse_ldo_end1;
+ }
+ rdesc->volt_table = v_table;
+
+ /* parse hi6421 regulator's dt common part */
+ ret = hi6421_dt_parse_common(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "failure in hi6421_dt_parse_common\n");
+ goto dt_parse_ldo_end1;
+ }
+
+dt_parse_ldo_end1:
+ devm_kfree(dev, v_table);
+dt_parse_ldo_end:
+ return ret;
+}
+
+static int hi6421_dt_parse_buck012(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ int ret = 0;
+
+ /* parse .n_voltages, and .uV_step */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-n-voltages",
+ &rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-n-voltages property set\n");
+ goto dt_parse_buck012_end;
+ }
+ ret = of_property_read_u32(np, "hisilicon,hi6421-uv-step",
+ &rdesc->uV_step);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-uv-step property set\n");
+ goto dt_parse_buck012_end;
+ }
+
+ /* parse hi6421 regulator's dt common part */
+ ret = hi6421_dt_parse_common(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "failure in hi6421_dt_parse_common\n");
+ goto dt_parse_buck012_end;
+ }
+
+dt_parse_buck012_end:
+ return ret;
+}
+
+static struct regulator_ops hi6421_ldo_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_ldo_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static struct regulator_ops hi6421_buck012_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_buck012_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static struct regulator_ops hi6421_buck345_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_ldo_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static const struct hi6421_regulator hi6421_regulator_ldo = {
+ .rdesc = {
+ .ops = &hi6421_ldo_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_ldo,
+};
+
+static const struct hi6421_regulator hi6421_regulator_buck012 = {
+ .rdesc = {
+ .ops = &hi6421_buck012_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_buck012,
+};
+
+static const struct hi6421_regulator hi6421_regulator_buck345 = {
+ .rdesc = {
+ .ops = &hi6421_buck345_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_ldo,
+};
+
+static struct of_device_id of_hi6421_regulator_match_tbl[] = {
+ {
+ .compatible = "hisilicon,hi6421-ldo",
+ .data = &hi6421_regulator_ldo,
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck012",
+ .data = &hi6421_regulator_buck012,
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck345",
+ .data = &hi6421_regulator_buck345,
+ },
+ { /* end */ }
+};
+
+static int hi6421_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc;
+ struct regulator_dev *rdev;
+ struct hi6421_regulator *sreg = NULL;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_config config = { };
+ const struct of_device_id *match;
+ const struct hi6421_regulator *template = NULL;
+ int ret = 0;
+
+ /* to check which type of regulator this is */
+ match = of_match_device(of_hi6421_regulator_match_tbl, &pdev->dev);
+ if (match)
+ template = match->data;
+ else
+ return -EINVAL;
+
+ initdata = of_get_regulator_init_data(dev, np);
+
+ /* hi6421 regulator supports two modes */
+ c = &initdata->constraints;
+ c->valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+ c->valid_ops_mask |= (REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS);
+ c->input_uV = c->min_uV;
+
+ sreg = kmemdup(template, sizeof(*sreg), GFP_KERNEL);
+ if (!sreg)
+ return -ENOMEM;
+
+ sreg->name = initdata->constraints.name;
+ rdesc = &sreg->rdesc;
+ rdesc->name = sreg->name;
+ rdesc->min_uV = initdata->constraints.min_uV;
+
+ /* to parse device tree data for regulator specific */
+ ret = sreg->dt_parse(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "device tree parameter parse error!\n");
+ goto hi6421_probe_end;
+ }
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = sreg;
+ config.of_node = pdev->dev.of_node;
+
+ /* register regulator */
+ rdev = regulator_register(rdesc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s\n",
+ rdesc->name);
+ ret = PTR_ERR(rdev);
+ goto hi6421_probe_end;
+ }
+
+ platform_set_drvdata(pdev, rdev);
+
+hi6421_probe_end:
+ if (ret)
+ kfree(sreg);
+ return ret;
+}
+
+static int hi6421_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct hi6421_regulator *sreg = rdev_get_drvdata(rdev);
+
+ regulator_unregister(rdev);
+
+ /* TODO: should i worry about that? devm_kzalloc */
+ if (sreg->rdesc.volt_table)
+ devm_kfree(&pdev->dev, (unsigned int *)sreg->rdesc.volt_table);
+
+ kfree(sreg);
+ return 0;
+}
+
+static struct platform_driver hi6421_regulator_driver = {
+ .driver = {
+ .name = "hi6421_regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = of_hi6421_regulator_match_tbl,
+ },
+ .probe = hi6421_regulator_probe,
+ .remove = hi6421_regulator_remove,
+};
+
+static int __init hi6421_regulator_init(void)
+{
+ return platform_driver_register(&hi6421_regulator_driver);
+}
+module_init(hi6421_regulator_init);
+
+static void __exit hi6421_regulator_exit(void)
+{
+ platform_driver_unregister(&hi6421_regulator_driver);
+}
+module_exit(hi6421_regulator_exit);
+
+MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
+MODULE_DESCRIPTION("Hi6421 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/hi6421-pmic.h b/include/linux/mfd/hi6421-pmic.h
new file mode 100644
index 000000000000..2f875cf52ae9
--- /dev/null
+++ b/include/linux/mfd/hi6421-pmic.h
@@ -0,0 +1,84 @@
+/*
+ * Header file for device driver Hi6421 PMIC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (C) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __HI6421_PMIC_H
+#define __HI6421_PMIC_H
+
+#include <linux/irqdomain.h>
+
+#define OCP_DEB_CTRL_REG (0x51)
+#define OCP_DEB_SEL_MASK (0x0C)
+#define OCP_DEB_SEL_8MS (0x00)
+#define OCP_DEB_SEL_16MS (0x04)
+#define OCP_DEB_SEL_32MS (0x08)
+#define OCP_DEB_SEL_64MS (0x0C)
+#define OCP_EN_DEBOUNCE_MASK (0x02)
+#define OCP_EN_DEBOUNCE_ENABLE (0x02)
+#define OCP_AUTO_STOP_MASK (0x01)
+#define OCP_AUTO_STOP_ENABLE (0x01)
+#define HI6421_REGS_ENA_PROTECT_TIME (100) /* in microseconds */
+#define HI6421_ECO_MODE_ENABLE (1)
+#define HI6421_ECO_MODE_DISABLE (0)
+
+#define HI6421_NR_IRQ 24
+#define HI6421_MASK_FIELD 0xFF
+#define HI6421_BITS 8
+
+#define HI6421_IRQ_ALARM 0 /* RTC Alarm */
+#define HI6421_IRQ_OTMP 1 /* Temperature too high */
+#define HI6421_IRQ_OCP 2 /* BUCK/LDO overload */
+#define HI6421_IRQ_RESET_IN 3 /* RESETIN_N signal */
+#define HI6421_IRQ_ONKEY_10S 4 /* PWRON key hold for 10s */
+#define HI6421_IRQ_ONKEY_1S 5 /* PWRON key hold for 1s */
+#define HI6421_IRQ_ONKEY_UP 6 /* PWRON key released */
+#define HI6421_IRQ_ONKEY_DOWN 7 /* PWRON key pressed */
+#define HI6421_IRQ_HEADSET_OUT 8 /* Headset plugged out */
+#define HI6421_IRQ_HEADSET_IN 9 /* Headset plugged in */
+#define HI6421_IRQ_COULOMB 10 /* Coulomb Counter */
+#define HI6421_IRQ_VBUS_UP 11 /* VBUS rising */
+#define HI6421_IRQ_VBUS_DOWN 12 /* VBUS falling */
+#define HI6421_IRQ_VBAT_LOW 13 /* VBattery too low */
+#define HI6421_IRQ_VBAT_HIGH 14 /* VBattery overvoltage */
+#define HI6421_IRQ_CHARGE_IN1 15 /* Charger plugged in */
+#define HI6421_IRQ_CHARGE_IN3 16 /* Charger plugged in */
+#define HI6421_IRQ_CHARGE_IN2 17 /* Charger plugged in */
+#define HI6421_IRQ_HS_BTN_DOWN 18 /* Headset button pressed */
+#define HI6421_IRQ_HS_BTN_UP 19 /* Headset button released */
+#define HI6421_IRQ_BATTERY_ON 20 /* Battery inserted */
+#define HI6421_IRQ_RESET 21 /* Reset */
+
+struct hi6421_pmic {
+ struct resource *res;
+ struct device *dev;
+ void __iomem *regs;
+ spinlock_t lock;
+ struct irq_domain *domain;
+ int irq;
+ int gpio;
+};
+
+/* Register Access Helpers */
+u32 hi6421_pmic_read(struct hi6421_pmic *pmic, int reg);
+void hi6421_pmic_write(struct hi6421_pmic *pmic, int reg, u32 val);
+void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg, u32 mask, u32 bits);
+
+#endif /* __HI6421_PMIC_H */