From: 张晴 Date: Thu, 11 Sep 2014 06:28:23 +0000 (+0800) Subject: rk312x:pmic:rt5036:support pmic rt5036 drivers X-Git-Tag: firefly_0821_release~4724^2~5 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=1142cc6021e096e70f059ed5cfeacf15f07765e9;p=firefly-linux-kernel-4.4.55.git rk312x:pmic:rt5036:support pmic rt5036 drivers --- diff --git a/arch/arm/boot/dts/rk312x-sdk.dtsi b/arch/arm/boot/dts/rk312x-sdk.dtsi index 7d364c78fda8..78318f99edf6 100755 --- a/arch/arm/boot/dts/rk312x-sdk.dtsi +++ b/arch/arm/boot/dts/rk312x-sdk.dtsi @@ -172,7 +172,12 @@ rt5025: rt5025@35 { compatible = "rt,rt5025"; reg = <0x35>; - status = "okay"; + status = "disabled"; + }; + rt5036: rt5036@38 { + compatible = "rt,rt5036"; + reg = <0x38>; + status = "disabled"; }; }; @@ -305,6 +310,136 @@ }; }; +/include/ "rt5036.dtsi" +&rt5036 { + + rt5036_dcdc1: regulator_0 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = < 800000>; + regulator-max-microvolt = <3300000>; + qcom,comsumer-supplies = "vdd_arm", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + rt,standby_vol = <950000>; + }; + + rt5036_dcdc2: regulator_1 { + regulator-name = "vdd_logic"; + regulator-min-microvolt = < 800000>; + regulator-max-microvolt = <3300000>; + qcom,comsumer-supplies = "vdd_logic", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + rt,standby_vol = <950000>; + }; + + rt5036_dcdc3: regulator_2 { + regulator-name = "rt5036-dcdc3"; + regulator-min-microvolt = < 800000>; + regulator-max-microvolt = <3300000>; + qcom,comsumer-supplies = "rt5036-dcdc3", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + rt,standby_vol = <2800000>; + }; + + rt5036_dcdc4: regulator_3 { + regulator-name = "rt5036-dcdc4"; + regulator-min-microvolt = < 800000>; + regulator-max-microvolt = <3300000>; + qcom,comsumer-supplies = "rt5036-dcdc4", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + rt,standby_vol = <1200000>; + }; + + rt5036_ldo1: regulator_4 { + regulator-name = "rt5036-ldo1"; + regulator-min-microvolt = < 3000000>; + regulator-max-microvolt = <3000000>; + qcom,comsumer-supplies = "rt5036-ldo1", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + rt,standby_vol = <3000000>; + }; + + rt5036_ldo2: regulator_5 { + regulator-name = "rt5036-ldo2"; + regulator-min-microvolt = < 1100000>; + regulator-max-microvolt = <1100000>; + qcom,comsumer-supplies = "rt5036-ldo2", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + rt,standby_vol = <1100000>; + }; + + rt5036_ldo3: regulator_6 { + regulator-name = "rt5036-ldo3"; + regulator-min-microvolt = < 1800000>; + regulator-max-microvolt = <1800000>; + qcom,comsumer-supplies = "rt5036-ldo3", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + rt,standby_vol = <1800000>; + }; + + rt5036_ldo4: regulator_7 { + regulator-name = "rt5036-ldo4"; + regulator-min-microvolt = < 1800000>; + regulator-max-microvolt = <1800000>; + qcom,comsumer-supplies = "rt5036-ldo4", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + rt,standby_vol = <1800000>; + }; + + rt5036_ldo5: regulator_8 { + regulator-name = "rt5036-ldo5"; + qcom,comsumer-supplies = "rt5036-ldo5", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + }; + + rt5036_ldo6: regulator_9 { + regulator-name = "rt5036-ldo6"; + qcom,comsumer-supplies = "rt5036-ldo6", ""; + regulator-always-on; + regulator-boot-on; + rt,standby_enabled; + }; + + rt5036-irq { + compatible = "rt,rt5036-irq"; + rt,irq-gpio = <&gpio1 GPIO_B1 GPIO_ACTIVE_HIGH>; + }; + + rt5036-charger { + compatible = "rt,rt5036-charger"; + rt,te_en; + rt,iprec = <0x2>; + rt,ieoc = <0x3>; + rt,vprec = <0xA>; + rt,batlv = <0x4>; + rt,vrechg = <1>; + rt,chg_volt = <4200>; + rt,otg_volt = <5025>; + rt,acchg_icc = <2000>; + rt,usbtachg_icc = <2000>; + rt,usbchg_icc = <900>; + /*rt,acdet_gpio = <&gpio1 GPIO_B1 GPIO_ACTIVE_HIGH>;*/ + /*rt,usbdet_gpio = <&gpio1 GPIO_B1 GPIO_ACTIVE_HIGH>;*/ + }; +}; + /include/ "rk818.dtsi" &rk818 { gpios =<&gpio1 GPIO_B1 GPIO_ACTIVE_HIGH>,<&gpio1 GPIO_A1 GPIO_ACTIVE_LOW>; @@ -483,11 +618,11 @@ &clk_core_dvfs_table { operating-points = < /* KHz uV */ - 408000 1250000 - 600000 1250000 - 696000 1250000 - 816000 1250000 - 1008000 1250000 + 408000 1300000 + 600000 1300000 + 696000 1300000 + 816000 1300000 + 1008000 1300000 >; status="okay"; }; diff --git a/arch/arm/boot/dts/rt5036.dtsi b/arch/arm/boot/dts/rt5036.dtsi new file mode 100755 index 000000000000..fd59ded35fe8 --- /dev/null +++ b/arch/arm/boot/dts/rt5036.dtsi @@ -0,0 +1,108 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + */ + +&rt5036 { + compatible = "rt,rt5036"; + + rt5036_dcdc1: regulator_0 { + compatible = "rt,rt5036-dcdc1"; + cell-index = <0>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_dcdc2: regulator_1 { + compatible = "rt,rt5036-dcdc2"; + cell-index = <1>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_dcdc3: regulator_2 { + compatible = "rt,rt5036-dcdc3"; + cell-index = <2>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_dcdc4: regulator_3 { + compatible = "rt,rt5036-dcdc4"; + cell-index = <3>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_ldo1: regulator_4 { + supply-regulator = "rt5036-dcdc3"; + compatible = "rt,rt5036-ldo1"; + cell-index = <4>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_ldo2: regulator_5 { + supply-regulator = "rt5036-dcdc3"; + compatible = "rt,rt5036-ldo2"; + cell-index = <5>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_ldo3: regulator_6 { + supply-regulator = "rt5036-dcdc3"; + compatible = "rt,rt5036-ldo3"; + cell-index = <6>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_ldo4: regulator_7 { + supply-regulator = "rt5036-dcdc3"; + compatible = "rt,rt5036-ldo4"; + cell-index = <7>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_lsw1: regulator_8 { + supply-regulator = "rt5036-dcdc3"; + compatible = "rt,rt5036-lsw1"; + cell-index = <8>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036_lsw2: regulator_9 { + supply-regulator = "rt5036-dcdc3"; + compatible = "rt,rt5036-lsw2"; + cell-index = <9>; + rt,nramp_sel = <0x00>; + rt,sramp_sel = <0x00>; + rt,allow_mode_mask; + }; + rt5036-rtc { + compatible = "rt,rt5036-rtc"; + }; + rt5036-misc { + compatible = "rt,rt5036-misc"; + rt,shdn_press = <0x1>; + rt,stb_en = <1>; + rt,lp_enshdn; + rt,vsysuvlo = <0x2>; + rt,syslv_enshdn; + rt,system-power-controller; + }; + rt5036-debug { + compatible = "rt,rt5036-debug"; + }; + }; + diff --git a/arch/arm/configs/rockchip_defconfig b/arch/arm/configs/rockchip_defconfig old mode 100644 new mode 100755 index bd288d7b6422..929d664b3c98 --- a/arch/arm/configs/rockchip_defconfig +++ b/arch/arm/configs/rockchip_defconfig @@ -329,12 +329,23 @@ CONFIG_SPI_ROCKCHIP_DMA=y CONFIG_SPI_ROCKCHIP_TEST=y CONFIG_DEBUG_GPIO=y CONFIG_GPIO_SYSFS=y +CONFIG_CHARGER_RT5025=y +CONFIG_BATTERY_RT5025=y +CONFIG_CHARGER_RT5036=y +CONFIG_RT_POWER=y CONFIG_BATTERY_RICOH619=y CONFIG_BATTERY_BQ24296=y CONFIG_BATTERY_BQ27320=y CONFIG_CW2015_BATTERY=y CONFIG_SENSORS_ROCKCHIP_TSADC=y CONFIG_THERMAL=y +CONFIG_MFD_RT5025=y +CONFIG_MISC_RT5025=y +CONFIG_IRQ_RT5025=y +CONFIG_DEBUG_RT5025=y +CONFIG_MFD_RT5036=y +CONFIG_MISC_RT5036=y +CONFIG_IRQ_RT5036=y CONFIG_MFD_RK808=y CONFIG_MFD_RK818=y CONFIG_MFD_RICOH619=y @@ -342,6 +353,8 @@ CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_ACT8846=y CONFIG_ACT8846_SUPPORT_RESET=y +CONFIG_REGULATOR_RT5025=y +CONFIG_REGULATOR_RT5036=y CONFIG_ROCKCHIP_PWM_REGULATOR=y CONFIG_REGULATOR_SYR82X=y CONFIG_REGULATOR_RICOH619=y @@ -509,6 +522,7 @@ CONFIG_SWITCH=y CONFIG_RTC_CLASS=y CONFIG_RTC_HYM8563=y CONFIG_RK808_RTC=y +CONFIG_RTC_RT5036=y CONFIG_RTC_DRV_RC5T619=y CONFIG_STAGING=y CONFIG_ZSMALLOC=y diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9fda33029457..e2422f44a4c1 100755 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -205,6 +205,49 @@ config MFD_RT_SHOW_INFO help Enable the RT5025 PMIC debug log. +config MFD_RT5036 + bool "Richtek RT5036 PMIC support" + select MFD_CORE + default n + help + Enable the RT5036 MFD driver. + +config MFD_RT5036_DBGINFO + bool "Richtek RT5036 debug message enable." + depends on MFD_RT5036 + default n + help + Enable the RT5036 debug log. + +config MISC_RT5036 + bool "Richtek RT5036 MISC option driver support" + depends on MFD_RT5036 + default n + help + Enable the RT5036 Misc option driver support. + +config MISC_RT5036_PWRKEY + bool "Richtek RT5036 Power key report in Misc module" + depends on MISC_RT5036 + default n + help + Enable the RT5036 Power Key report in Misc module. + +config IRQ_RT5036 + bool "Richtek RT5036 irq option driver support" + depends on MFD_RT5036 + default n + help + Enable the Rt5036 IRQ option driver support. + +config DEBUG_RT5036 + bool "Richtek RT5036 PMIC DEBUGFS Support" + depends on DEBUG_FS && MFD_RT5036 + default n + help + Enable the RT5036 debugfs node that support + read/write registers. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 1a69560d8dcb..ff346f12f533 100755 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -60,6 +60,11 @@ obj-$(CONFIG_MISC_RT5025) += rt5025-misc.o obj-$(CONFIG_IRQ_RT5025) += rt5025-irq.o obj-$(CONFIG_DEBUG_RT5025) += rt5025-debug.o +obj-$(CONFIG_MFD_RT5036) += rt5036-i2c.o rt5036-core.o +obj-$(CONFIG_MISC_RT5036) += rt5036-misc.o +obj-$(CONFIG_IRQ_RT5036) += rt5036-irq.o +obj-$(CONFIG_DEBUG_RT5036) += rt5036-debug.o + obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o diff --git a/drivers/mfd/rt5025-i2c.c b/drivers/mfd/rt5025-i2c.c index 5690e050403e..18ec6e5c4e24 100755 --- a/drivers/mfd/rt5025-i2c.c +++ b/drivers/mfd/rt5025-i2c.c @@ -205,24 +205,31 @@ static int rt5025_i2c_remove(struct i2c_client *client) return 0; } -static int rt5025_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +static int rt5025_i2c_suspend(struct device *dev) { - struct rt5025_chip *chip = i2c_get_clientdata(client); + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct rt5025_chip *chip = i2c_get_clientdata(i2c); - chip->suspend = 1; RTINFO("\n"); + chip->suspend = 1; return 0; } -static int rt5025_i2c_resume(struct i2c_client *client) +static int rt5025_i2c_resume(struct device *dev) { - struct rt5025_chip *chip = i2c_get_clientdata(client); + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct rt5025_chip *chip = i2c_get_clientdata(i2c); - chip->suspend = 0; RTINFO("\n"); + chip->suspend = 0; return 0; } +static const struct dev_pm_ops rt5025_pm_ops = { + .suspend = rt5025_i2c_suspend, + .resume = rt5025_i2c_resume, +}; + static const struct i2c_device_id rt5025_id_table[] = { { RT5025_DEV_NAME, 0 }, { }, @@ -238,12 +245,11 @@ static struct i2c_driver rt5025_i2c_driver = { .driver = { .name = RT5025_DEV_NAME, .owner = THIS_MODULE, + .pm = &rt5025_pm_ops, .of_match_table = rt_match_table, }, .probe = rt5025_i2c_probe, .remove = rt5025_i2c_remove, - .suspend = rt5025_i2c_suspend, - .resume = rt5025_i2c_resume, .id_table = rt5025_id_table, }; diff --git a/drivers/mfd/rt5036-core.c b/drivers/mfd/rt5036-core.c new file mode 100755 index 000000000000..2560be6c114a --- /dev/null +++ b/drivers/mfd/rt5036-core.c @@ -0,0 +1,268 @@ +/* + * drivers/mfd/rt5036-core.c + * Driver for Richtek RT5036 Core PMIC + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include + +#ifdef CONFIG_REGULATOR_RT5036 +#ifdef CONFIG_OF +#define RT5036_BUCKVR_DEVS(_id, _idx) \ +{ \ + .name = RT5036_DEV_NAME "-regulator", \ + .num_resources = 0, \ + .of_compatible = "rt," RT5036_DEV_NAME "-dcdc" #_idx, \ + .id = RT5036_ID_##_id, \ +} + +#define RT5036_LDOVR_DEVS(_id, _idx) \ +{ \ + .name = RT5036_DEV_NAME "-regulator", \ + .num_resources = 0, \ + .of_compatible = "rt," RT5036_DEV_NAME "-ldo" #_idx, \ + .id = RT5036_ID_##_id, \ +} + +#define RT5036_LSWVR_DEVS(_id, _idx) \ +{ \ + .name = RT5036_DEV_NAME "-regulator", \ + .num_resources = 0, \ + .of_compatible = "rt," RT5036_DEV_NAME "-lsw" #_idx, \ + .id = RT5036_ID_##_id, \ +} +#else +#define RT5036_BUCKVR_DEVS(_id, _idx) \ +{ \ + .name = RT5036_DEV_NAME "-regulator", \ + .num_resources = 0, \ + .id = RT5036_ID_##_id, \ +} + +#define RT5036_LDOVR_DEVS(_id, _idx) \ +{ \ + .name = RT5036_DEV_NAME "-regulator", \ + .num_resources = 0, \ + .id = RT5036_ID_##_id, \ +} + +#define RT5036_LSWVR_DEVS(_id, _idx) \ +{ \ + .name = RT5036_DEV_NAME "-regulator", \ + .num_resources = 0, \ + .id = RT5036_ID_##_id, \ +} +#endif /* #ifdef CONFIG_OF */ + +static struct mfd_cell regulator_devs[] = { + RT5036_BUCKVR_DEVS(DCDC1, 1), + RT5036_BUCKVR_DEVS(DCDC2, 2), + RT5036_BUCKVR_DEVS(DCDC3, 3), + RT5036_BUCKVR_DEVS(DCDC4, 4), + RT5036_LDOVR_DEVS(LDO1, 1), + RT5036_LDOVR_DEVS(LDO2, 2), + RT5036_LDOVR_DEVS(LDO3, 3), + RT5036_LDOVR_DEVS(LDO4, 4), + RT5036_LSWVR_DEVS(LSW1, 1), + RT5036_LSWVR_DEVS(LSW2, 2), +}; +#endif /* CONFIG_REGULATOR_RT5036 */ + +#ifdef CONFIG_CHARGER_RT5036 +static struct mfd_cell chg_devs[] = { + { + .name = RT5036_DEV_NAME "-charger", + .id = -1, + .num_resources = 0, +#ifdef CONFIG_OF + .of_compatible = "rt," RT5036_DEV_NAME "-charger", +#endif /*#ifdef CONFIG_OF */ + }, +}; +#endif /* CONFIG_CHARGER_RT5036 */ + +#ifdef CONFIG_RTC_RT5036 +static struct mfd_cell rtc_devs[] = { + { + .name = RT5036_DEV_NAME "-rtc", + .id = -1, + .num_resources = 0, +#ifdef CONFIG_OF + .of_compatible = "rt," RT5036_DEV_NAME "-rtc", +#endif /*#ifdef CONFIG_OF */ + }, +}; +#endif /* CONFIG_RTC_RT5036 */ + +#ifdef CONFIG_MISC_RT5036 +static struct mfd_cell misc_devs[] = { + { + .name = RT5036_DEV_NAME "-misc", + .id = -1, + .num_resources = 0, +#ifdef CONFIG_OF + .of_compatible = "rt," RT5036_DEV_NAME "-misc", +#endif /*#ifdef CONFIG_OF */ + }, +}; +#endif /* CONFIG_MISC_RT5036 */ + +#ifdef CONFIG_IRQ_RT5036 +static struct mfd_cell irq_devs[] = { + { + .name = RT5036_DEV_NAME "-irq", + .id = -1, + .num_resources = 0, +#ifdef CONFIG_OF + .of_compatible = "rt," RT5036_DEV_NAME "-irq", +#endif /*#ifdef CONFIG_OF */ + }, +}; +#endif /* CONFIG_IRQ_RT5036 */ + +#ifdef CONFIG_DEBUG_RT5036 +static struct mfd_cell debug_devs[] = { + { + .name = RT5036_DEV_NAME "-debug", + .id = -1, + .num_resources = 0, +#ifdef CONFIG_OF + .of_compatible = "rt," RT5036_DEV_NAME "-debug", +#endif /*#ifdef CONFIG_OF */ + }, +}; +#endif /* CONFIG_DEBUG_RT5036 */ + +int rt5036_core_init(struct device *dev, + struct rt5036_platform_data *pdata) +{ + int ret = 0; + + RTINFO("Start to initialize all device\n"); +#ifdef CONFIG_REGULATOR_RT5036 + if (dev->of_node || (pdata && pdata->regulator[0])) { + RTINFO("mfd add regulators dev\n"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + ret = mfd_add_devices(dev, 0, ®ulator_devs[0], + ARRAY_SIZE(regulator_devs), + NULL, 0, NULL); +#else + ret = mfd_add_devices(dev, 0, ®ulator_devs[0], + ARRAY_SIZE(regulator_devs), NULL, 0); +#endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(dev, "Failed to add regulator subdev\n"); + goto out_dev; + } + } +#endif /* CONFIG_REGULATOR_RT5036 */ + +#ifdef CONFIG_CHARGER_RT5036 + if (dev->of_node || (pdata && pdata->chg_pdata)) { + RTINFO("mfd add charger dev\n"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + ret = mfd_add_devices(dev, 0, &chg_devs[0], + ARRAY_SIZE(chg_devs), NULL, 0, NULL); +#else + ret = mfd_add_devices(dev, 0, &chg_devs[0], + ARRAY_SIZE(chg_devs), NULL, 0); +#endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(dev, "Failed to add charger subdev\n"); + goto out_dev; + } + } +#endif /* CONFIG_CHARGER_RT5036 */ + +#ifdef CONFIG_RTC_RT5036 + if (dev->of_node || pdata) { + RTINFO("mfd add rtc dev\n"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + ret = mfd_add_devices(dev, 0, &rtc_devs[0], + ARRAY_SIZE(rtc_devs), NULL, 0, NULL); +#else + ret = mfd_add_devices(dev, 0, &rtc_devs[0], + ARRAY_SIZE(rtc_devs), NULL, 0); +#endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(dev, "Failed to add rtc subdev\n"); + goto out_dev; + } + } +#endif /* CONFIG_RTC_RT5036 */ + +#ifdef CONFIG_MISC_RT5036 + if (dev->of_node || (pdata && pdata->misc_pdata)) { + RTINFO("mfd add misc dev\n"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + ret = mfd_add_devices(dev, 0, &misc_devs[0], + ARRAY_SIZE(misc_devs), NULL, 0, NULL); +#else + ret = mfd_add_devices(dev, 0, &misc_devs[0], + ARRAY_SIZE(misc_devs), NULL, 0); +#endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(dev, "Failed to add misc subdev\n"); + goto out_dev; + } + } +#endif /* CONFIG_MISC_RT5036 */ + +#ifdef CONFIG_IRQ_RT5036 + if (dev->of_node || (pdata && pdata->irq_pdata)) { + RTINFO("mfd add irq dev\n"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + ret = mfd_add_devices(dev, 0, &irq_devs[0], + ARRAY_SIZE(irq_devs), NULL, 0, NULL); +#else + ret = mfd_add_devices(dev, 0, &irq_devs[0], + ARRAY_SIZE(irq_devs), NULL, 0); +#endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(dev, "Failed to add irq subdev\n"); + goto out_dev; + } + } +#endif /* CONFIG_IRQ_RT5036 */ + +#ifdef CONFIG_DEBUG_RT5036 + RTINFO("mfd add debug dev\n"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + ret = mfd_add_devices(dev, 0, &debug_devs[0], + ARRAY_SIZE(debug_devs), NULL, 0, NULL); +#else + ret = mfd_add_devices(dev, 0, &debug_devs[0], + ARRAY_SIZE(debug_devs), NULL, 0); +#endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(dev, "Failed to add debug subdev\n"); + goto out_dev; + } +#endif /* CONFIG_DEBUG_RT5036 */ + + RTINFO("Initialize all device successfully\n"); + return ret; +out_dev: + mfd_remove_devices(dev); + return ret; +} +EXPORT_SYMBOL(rt5036_core_init); + +int rt5036_core_deinit(struct device *dev) +{ + mfd_remove_devices(dev); + return 0; +} +EXPORT_SYMBOL(rt5036_core_deinit); diff --git a/drivers/mfd/rt5036-debug.c b/drivers/mfd/rt5036-debug.c new file mode 100755 index 000000000000..6f81c29747b7 --- /dev/null +++ b/drivers/mfd/rt5036-debug.c @@ -0,0 +1,261 @@ +/* + * drivers/mfd/rt5036-debug.c + * Driver for Richtek RT5036 PMIC debug + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct rt5036_debug_info { + struct i2c_client *i2c; + unsigned char reg_addr; + unsigned char reg_data; +}; + +struct rt_debug_st { + void *info; + int id; +}; + +enum { + RT5036_DBG_REG, + RT5036_DBG_DATA, + RT5036_DBG_REGS, + RT5036_DBG_MAX +}; + +static struct dentry *debugfs_rt_dent; +static struct dentry *debugfs_file[RT5036_DBG_MAX]; +static struct rt_debug_st rtdbg_data[RT5036_DBG_MAX]; + +static int reg_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtoul(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else { + return -EINVAL; + } + } + return 0; +} + +static ssize_t reg_debug_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct rt_debug_st *st = filp->private_data; + struct rt5036_debug_info *di = st->info; + char lbuf[1000]; + int i = 0, j = 0; + + lbuf[0] = '\0'; + switch (st->id) { + case RT5036_DBG_REG: + snprintf(lbuf, sizeof(lbuf), "0x%x\n", di->reg_addr); + break; + case RT5036_DBG_DATA: + di->reg_data = + (unsigned char)rt5036_reg_read(di->i2c, di->reg_addr); + snprintf(lbuf, sizeof(lbuf), "0x%x\n", di->reg_data); + break; + case RT5036_DBG_REGS: + for (i = RT5036_REG_RANGE1START; i <= RT5036_REG_RANGE1END; i++) + j += sprintf(lbuf + j, "reg_%02x:%02x\n", i, + rt5036_reg_read(di->i2c, i)); + for (i = RT5036_REG_RANGE2START; i <= RT5036_REG_RANGE2END; i++) + j += sprintf(lbuf + j, "reg_%02x:%02x\n", i, + rt5036_reg_read(di->i2c, i)); + for (i = RT5036_REG_RANGE3START; i <= RT5036_REG_RANGE3END; i++) + j += sprintf(lbuf + j, "reg_%02x:%02x\n", i, + rt5036_reg_read(di->i2c, i)); + for (i = RT5036_REG_RANGE4START; i <= RT5036_REG_RANGE4END; i++) + j += sprintf(lbuf + j, "reg_%02x:%02x\n", i, + rt5036_reg_read(di->i2c, i)); + for (i = RT5036_REG_RANGE5START; i <= RT5036_REG_RANGE5END; i++) + j += sprintf(lbuf + j, "reg_%02x:%02x\n", i, + rt5036_reg_read(di->i2c, i)); + for (i = RT5036_REG_RANGE6START; i <= RT5036_REG_RANGE6END; i++) + j += sprintf(lbuf + j, "reg_%02x:%02x\n", i, + rt5036_reg_read(di->i2c, i)); + break; + default: + return -EINVAL; + } + return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf)); +} + +static ssize_t reg_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, + loff_t *ppos) +{ + struct rt_debug_st *st = filp->private_data; + struct rt5036_debug_info *di = st->info; + char lbuf[32]; + int rc; + long int param[5]; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + + switch (st->id) { + case RT5036_DBG_REG: + rc = get_parameters(lbuf, param, 1); + if ((param[0] < RT5036_REG_MAX) && (rc == 0)) { + if ((param[0] >= RT5036_REG_RANGE1START + && param[0] <= RT5036_REG_RANGE1END) + || (param[0] >= RT5036_REG_RANGE2START + && param[0] <= RT5036_REG_RANGE2END) + || (param[0] >= RT5036_REG_RANGE3START + && param[0] <= RT5036_REG_RANGE3END) + || (param[0] >= RT5036_REG_RANGE4START + && param[0] <= RT5036_REG_RANGE4END) + || (param[0] >= RT5036_REG_RANGE5START + && param[0] <= RT5036_REG_RANGE5END)) + di->reg_addr = (unsigned char)param[0]; + else + rc = -EINVAL; + } else + rc = -EINVAL; + break; + case RT5036_DBG_DATA: + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= 0xff) && (rc == 0)) + rt5036_reg_write(di->i2c, di->reg_addr, param[0]); + else + rc = -EINVAL; + break; + default: + return -EINVAL; + } + if (rc == 0) + rc = cnt; + return rc; +} + +static const struct file_operations reg_debug_ops = { + .open = reg_debug_open, + .write = reg_debug_write, + .read = reg_debug_read +}; + +static int rt5036_debug_probe(struct platform_device *pdev) +{ + struct rt5036_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5036_debug_info *di; + + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) + return -ENOMEM; + + di->i2c = chip->i2c; + + RTINFO("add debugfs for RT5036"); + debugfs_rt_dent = debugfs_create_dir("rt5036_dbg", 0); + if (!IS_ERR(debugfs_rt_dent)) { + rtdbg_data[0].info = di; + rtdbg_data[0].id = RT5036_DBG_REG; + debugfs_file[0] = debugfs_create_file("reg", + S_IFREG | S_IRUGO, + debugfs_rt_dent, + (void *)&rtdbg_data[0], + ®_debug_ops); + + rtdbg_data[1].info = di; + rtdbg_data[1].id = RT5036_DBG_DATA; + debugfs_file[1] = debugfs_create_file("data", + S_IFREG | S_IRUGO, + debugfs_rt_dent, + (void *)&rtdbg_data[1], + ®_debug_ops); + + rtdbg_data[2].info = di; + rtdbg_data[2].id = RT5036_DBG_REGS; + debugfs_file[2] = debugfs_create_file("regs", + S_IFREG | S_IRUGO, + debugfs_rt_dent, + (void *)&rtdbg_data[2], + ®_debug_ops); + } + platform_set_drvdata(pdev, di); + return 0; +} + +static int rt5036_debug_remove(struct platform_device *pdev) +{ + if (!IS_ERR(debugfs_rt_dent)) + debugfs_remove_recursive(debugfs_rt_dent); + return 0; +} + +static const struct of_device_id rt_match_table[] = { + {.compatible = "rt,rt5036-debug",}, + {}, +}; + +static struct platform_driver rt5036_debug_driver = { + .driver = { + .name = RT5036_DEV_NAME "-debug", + .owner = THIS_MODULE, + .of_match_table = rt_match_table, + }, + .probe = rt5036_debug_probe, + .remove = rt5036_debug_remove, +}; + +static int __init rt5036_debug_init(void) +{ + return platform_driver_register(&rt5036_debug_driver); +} +module_init(rt5036_debug_init); + +static void __exit rt5036_debug_exit(void) +{ + platform_driver_unregister(&rt5036_debug_driver); +} +module_exit(rt5036_debug_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CY Huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include + +static inline int rt5036_read_device(struct i2c_client *i2c, + int reg, int bytes, void *dest) +{ + int ret; + + if (bytes > 1) { + ret = i2c_smbus_read_i2c_block_data(i2c, reg, bytes, dest); + } else { + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret < 0) + return ret; + *(unsigned char *)dest = (unsigned char)ret; + } + return ret; +} + +int rt5036_reg_block_read(struct i2c_client *i2c, + int reg, int bytes, void *dest) +{ + return rt5036_read_device(i2c, reg, bytes, dest); +} +EXPORT_SYMBOL(rt5036_reg_block_read); + +static inline int rt5036_write_device(struct i2c_client *i2c, + int reg, int bytes, void *dest) +{ + int ret; + + if (bytes > 1) { + ret = i2c_smbus_write_i2c_block_data(i2c, reg, bytes, dest); + } else { + ret = i2c_smbus_write_byte_data(i2c, reg, *(u8 *) dest); + if (ret < 0) + return ret; + } + return ret; +} + +int rt5036_reg_block_write(struct i2c_client *i2c, + int reg, int bytes, void *dest) +{ + return rt5036_write_device(i2c, reg, bytes, dest); +} +EXPORT_SYMBOL(rt5036_reg_block_write); + +int rt5036_reg_read(struct i2c_client *i2c, int reg) +{ + struct rt5036_chip *chip = i2c_get_clientdata(i2c); + int ret; + + RTINFO("I2C Read (client : 0x%x) reg = 0x%x\n", + (unsigned int)i2c, (unsigned int)reg); + mutex_lock(&chip->io_lock); + ret = i2c_smbus_read_byte_data(i2c, reg); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(rt5036_reg_read); + +int rt5036_reg_write(struct i2c_client *i2c, int reg, unsigned char data) +{ + struct rt5036_chip *chip = i2c_get_clientdata(i2c); + int ret; + + RTINFO("I2C Write (client : 0x%x) reg = 0x%x, data = 0x%x\n", + (unsigned int)i2c, (unsigned int)reg, (unsigned int)data); + mutex_lock(&chip->io_lock); + ret = i2c_smbus_write_byte_data(i2c, reg, data); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(rt5036_reg_write); + +int rt5036_assign_bits(struct i2c_client *i2c, int reg, + unsigned char mask, unsigned char data) +{ + struct rt5036_chip *chip = i2c_get_clientdata(i2c); + unsigned char value; + int ret; + + mutex_lock(&chip->io_lock); + ret = rt5036_read_device(i2c, reg, 1, &value); + + if (ret < 0) + goto out; + value &= ~mask; + value |= (data & mask); + ret = i2c_smbus_write_byte_data(i2c, reg, value); +out: + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(rt5036_assign_bits); + +int rt5036_set_bits(struct i2c_client *i2c, int reg, unsigned char mask) +{ + return rt5036_assign_bits(i2c, reg, mask, mask); +} +EXPORT_SYMBOL(rt5036_set_bits); + +int rt5036_clr_bits(struct i2c_client *i2c, int reg, unsigned char mask) +{ + return rt5036_assign_bits(i2c, reg, mask, 0); +} +EXPORT_SYMBOL(rt5036_clr_bits); + +static int rt_parse_dt(struct rt5036_chip *chip, struct device *dev) +{ + RTINFO("\n"); + return 0; +} + +static int rt_parse_pdata(struct rt5036_chip *chip, + struct device *dev) +{ + RTINFO("\n"); + return 0; +} + +static int rt5036_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rt5036_platform_data *pdata = client->dev.platform_data; + struct rt5036_chip *chip; + bool use_dt = client->dev.of_node; + int val, ret = 0; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->i2c = client; + mutex_init(&chip->io_lock); + i2c_set_clientdata(client, chip); + + val = rt5036_reg_read(client, RT5036_REG_DEVID); + if (val < 0) { + ret = -EIO; + goto out_err; + } else { + if (val != 0x36) { + dev_err(&client->dev, "id 0x%02x is not correct\n", + val); + ret = ENODEV; + goto out_err; + } + val = rt5036_reg_read(client, RT5036_REG_ONOFFEVENT); + dev_info(&client->dev, "last onoff event %02x\n", val); + /*set ldo lsw vrc to default enable*/ + rt5036_reg_write(client, 0x4D, 0xF0); + rt5036_reg_write(client, 0x7D, 0xF0); + rt5036_reg_write(client, 0x85, 0xCC); + } + + if (use_dt) { + rt_parse_dt(chip, &client->dev); + } else { + if (!pdata) { + dev_err(&client->dev, "no platform data included\n"); + ret = -EINVAL; + goto out_err; + } + rt_parse_pdata(chip, &client->dev); + } + + ret = rt5036_core_init(&client->dev, pdata); + if (ret < 0) { + ret = -EINVAL; + goto out_err; + } + + if (pdata && pdata->pre_init) { + ret = pdata->pre_init(chip); + if (ret != 0) + dev_err(&client->dev, "pre_init() failed: %d\n", ret); + } + if (pdata && pdata->post_init) { + ret = pdata->post_init(); + if (ret != 0) + dev_err(&client->dev, "post_init() failed: %d\n", ret); + } + dev_info(&client->dev, "driver successfully loaded\n"); + return 0; +out_err: + return ret; +} + +static int rt5036_i2c_remove(struct i2c_client *client) +{ + RTINFO("\n"); + rt5036_core_deinit(&client->dev); + return 0; +} + +static int rt5036_i2c_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct rt5036_chip *chip = i2c_get_clientdata(i2c); + + RTINFO("\n"); + chip->suspend = 1; + return 0; +} + +static int rt5036_i2c_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct rt5036_chip *chip = i2c_get_clientdata(i2c); + + RTINFO("\n"); + chip->suspend = 0; + return 0; +} + +static const struct dev_pm_ops rt5036_pm_ops = { + .suspend = rt5036_i2c_suspend, + .resume = rt5036_i2c_resume, +}; + +static const struct i2c_device_id rt5036_id_table[] = { + {RT5036_DEV_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, rt5036_id_table); + +static const struct of_device_id rt_match_table[] = { + {.compatible = "rt,rt5036",}, + {}, +}; + +static struct i2c_driver rt5036_driver = { + .driver = { + .name = RT5036_DEV_NAME, + .owner = THIS_MODULE, + .pm = &rt5036_pm_ops, + .of_match_table = rt_match_table, + }, + .probe = rt5036_i2c_probe, + .remove = rt5036_i2c_remove, + .id_table = rt5036_id_table, +}; + +static int __init rt5036_i2c_init(void) +{ + return i2c_add_driver(&rt5036_driver); +} +subsys_initcall_sync(rt5036_i2c_init); + +static void __exit rt5036_i2c_exit(void) +{ + i2c_del_driver(&rt5036_driver); +} +module_exit(rt5036_i2c_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("I2C Driver for Richtek RT5036"); +MODULE_AUTHOR("CY Huang "); +MODULE_VERSION(RT5036_DRV_VER); diff --git a/drivers/mfd/rt5036-irq.c b/drivers/mfd/rt5036-irq.c new file mode 100755 index 000000000000..68df9af134a2 --- /dev/null +++ b/drivers/mfd/rt5036-irq.c @@ -0,0 +1,245 @@ +/* + * drivers/mfd/rt5036-irq.c + * Driver for Richtek RT5036 PMIC IRQ driver + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct rt5036_irq_info { + struct i2c_client *i2c; + struct rt5036_chip *chip; + struct device *dev; + int irq; + unsigned char suspend:1; + struct delayed_work irq_delayed_work; +}; + +static irqreturn_t rt5036_irq_handler(int irqno, void *param) +{ + struct rt5036_irq_info *ii = param; + unsigned char regval[3]; + unsigned int irq_event; + int ret = 0; + + if (ii->suspend) { + schedule_delayed_work(&ii->irq_delayed_work, + msecs_to_jiffies(10)); + goto irq_fin; + } + + ret = + rt5036_reg_block_read(ii->i2c, RT5036_REG_CHGIRQ1, + ARRAY_SIZE(regval), regval); + if (ret < 0) { + dev_err(ii->dev, "read charger irq event fail\n"); + } else { + irq_event = regval[0] << 16 | regval[1] << 8 | regval[2]; + RTINFO("chg event %06x\n", irq_event); +#ifdef CONFIG_CHARGER_RT5036 + if (irq_event && ii->chip->chg_info) + rt5036_charger_irq_handler(ii->chip->chg_info, + irq_event); +#endif /* #ifdef CONFIG_CHARGER_RT5036 */ + } + + ret = + rt5036_reg_block_read(ii->i2c, RT5036_REG_BUCKLDOIRQ, + ARRAY_SIZE(regval), regval); + if (ret < 0) { + dev_err(ii->dev, "read misc irq event fail\n"); + } else { + irq_event = regval[0] << 16 | regval[1] << 8 | regval[2]; + RTINFO("misc event %06x\n", irq_event); +#ifdef CONFIG_MISC_RT5036 + if (irq_event && ii->chip->misc_info) + rt5036_misc_irq_handler(ii->chip->misc_info, irq_event); +#endif /* #ifdef CONFIG_MISC_RT5036 */ + } + + ret = rt5036_reg_read(ii->i2c, RT5036_REG_STBWACKIRQ); + if (ret < 0) { + dev_err(ii->dev, "read rtc irq event fail\n"); + } else { + irq_event = ret; + RTINFO("rtc event %02x\n", irq_event); +#ifdef CONFIG_RTC_RT5036 + if (irq_event && ii->chip->rtc_info) + rt5036_rtc_irq_handler(ii->chip->rtc_info, irq_event); +#endif /* #ifdef CONFIG_RTC_RT5036 */ + } + rt5036_set_bits(ii->i2c, RT5036_REG_BUCKVN1, RT5036_IRQPREZ_MASK); +irq_fin: + return IRQ_HANDLED; +} + +static void rt5036_irq_delayed_work(struct work_struct *work) +{ + struct rt5036_irq_info *ii = + (struct rt5036_irq_info *)container_of(work, struct rt5036_irq_info, + irq_delayed_work.work); + rt5036_irq_handler(ii->irq, ii); +} + +static int rt_parse_dt(struct rt5036_irq_info *ii, struct device *dev) +{ +#ifdef CONFIG_OF + struct device_node *np = dev->of_node; + int val; + + val = of_get_named_gpio(np, "rt,irq-gpio", 0); + if (gpio_is_valid(val)) { + if (gpio_request(val, "rt5036_irq") >= 0) { + gpio_direction_input(val); + ii->irq = gpio_to_irq(val); + } else { + ii->irq = -1; + } + } else { + ii->irq = -1; + } +#endif /* #ifdef CONFIG_OF */ + RTINFO("\n"); + return 0; +} + +static int rt_parse_pdata(struct rt5036_irq_info *ii, + struct device *dev) +{ + struct rt5036_irq_data *pdata = dev->platform_data; + + if (gpio_is_valid(pdata->irq_gpio)) { + if (gpio_request(pdata->irq_gpio, "rt5036_irq") >= 0) { + gpio_direction_input(pdata->irq_gpio); + ii->irq = gpio_to_irq(pdata->irq_gpio); + } else { + ii->irq = -1; + } + } else { + ii->irq = -1; + } + RTINFO("\n"); + return 0; +} + +static int rt5036_irq_probe(struct platform_device *pdev) +{ + struct rt5036_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5036_platform_data *pdata = (pdev->dev.parent)->platform_data; + struct rt5036_irq_info *ii; + bool use_dt = pdev->dev.of_node; + + ii = devm_kzalloc(&pdev->dev, sizeof(*ii), GFP_KERNEL); + if (!ii) + return -ENOMEM; + + ii->i2c = chip->i2c; + ii->chip = chip; + ii->dev = &pdev->dev; + if (use_dt) { + rt_parse_dt(ii, &pdev->dev); + } else { + if (!pdata) + goto out_dev; + pdev->dev.platform_data = pdata->irq_pdata; + rt_parse_pdata(ii, &pdev->dev); + dev_info(&pdev->dev, "ii->irq %d\n", ii->irq); + } + INIT_DELAYED_WORK(&ii->irq_delayed_work, rt5036_irq_delayed_work); + + platform_set_drvdata(pdev, ii); + if (ii->irq >= 0) { + if (devm_request_irq + (&pdev->dev, ii->irq, rt5036_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND | IRQF_DISABLED, + "rt5036_irq", ii)) { + dev_err(&pdev->dev, "request threaded irq fail\n"); + goto out_dev; + } + enable_irq_wake(ii->irq); + schedule_delayed_work(&ii->irq_delayed_work, + msecs_to_jiffies(500)); + } + dev_info(&pdev->dev, "driver successfully loaded\n"); + return 0; +out_dev: + return -EINVAL; +} + +static int rt5036_irq_remove(struct platform_device *pdev) +{ + struct rt5036_irq_info *ii = platform_get_drvdata(pdev); + + if (ii->irq >= 0) + devm_free_irq(&pdev->dev, ii->irq, ii); + return 0; +} + +static int rt5036_irq_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rt5036_irq_info *ii = platform_get_drvdata(pdev); + + ii->suspend = 1; + return 0; +} + +static int rt5036_irq_resume(struct platform_device *pdev) +{ + struct rt5036_irq_info *ii = platform_get_drvdata(pdev); + + ii->suspend = 0; + return 0; +} + +static const struct of_device_id rt_match_table[] = { + {.compatible = "rt,rt5036-irq",}, + {}, +}; + +static struct platform_driver rt5036_irq_driver = { + .driver = { + .name = RT5036_DEV_NAME "-irq", + .owner = THIS_MODULE, + .of_match_table = rt_match_table, + }, + .probe = rt5036_irq_probe, + .remove = rt5036_irq_remove, + .suspend = rt5036_irq_suspend, + .resume = rt5036_irq_resume, +}; + +static int __init rt5036_irq_init(void) +{ + return platform_driver_register(&rt5036_irq_driver); +} +subsys_initcall_sync(rt5036_irq_init); + +static void __exit rt5036_irq_exit(void) +{ + platform_driver_unregister(&rt5036_irq_driver); +} +module_exit(rt5036_irq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CY Huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MISC_RT5036_PWRKEY +#include +#endif /* #ifdef CONFIG_MISC_RT5036_PWRKEY */ + +#include +#include + +static struct i2c_client *g_shdn; + +static unsigned char misc_init_regval[] = { + 0xA8, /*REG 0x51*/ + 0x96, /*REG 0x52*/ + 0x48, /*REG 0x53*/ + 0x00, /*REG 0x54*/ + 0x06, /*REG 0x55*/ + 0xA0, /*REG 0x65*/ + 0xFF, /*REG 0x5A*/ + 0xE0, /*REG 0x5B*/ +#ifdef CONFIG_MISC_RT5036_PWRKEY + 0x18, /*REG 0x5C*/ +#else + 0x78, /*REG 0x5C*/ +#endif /* #ifdef CONFIG_RT5036_PWRKEY */ +}; + +int rt5036_vin_exist(void) +{ + int ret = 0; +#ifdef CONFIG_CHARGER_RT5036 + union power_supply_propval pval; + struct power_supply *psy = power_supply_get_by_name("rt-charger"); + + if (psy) { + ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval); + if (ret < 0) + ret = 0; + else + ret = pval.intval; + } else { + pr_err("couldn't get rt-charger psy\n"); + } + return ret; +#else + if (g_shdn) + ret = rt5036_reg_read(g_shdn, RT5036_REG_CHGSTAT2); + return ret < 0 ? 0 : ret & RT5036_PWRRDY_MASK; +#endif /* #ifdef CONFIG_CHARGER_RT5036 */ +} +EXPORT_SYMBOL(rt5036_vin_exist); +static bool rt_pm_off; +void rt5036_chip_shutdown(void) +{ + if (g_shdn) { + rt5036_set_bits(g_shdn, RT5036_REG_MISC3, RT5036_CHIPSHDN_MASK); + rt5036_clr_bits(g_shdn, RT5036_REG_MISC3, RT5036_CHIPSHDN_MASK); + } +} +EXPORT_SYMBOL(rt5036_chip_shutdown); + +static void rt5036_general_irq_handler(void *info, int eventno) +{ + struct rt5036_misc_info *mi = info; + + dev_info(mi->dev, "eventno=%02d\n", eventno); +#ifdef CONFIG_MISC_RT5036_PWRKEY + switch (eventno) { + case MISCEVENT_PWRONF: + if (!mi->pwr_key_pressed) { + input_report_key(mi->pwr_key, KEY_POWER, 1); + input_sync(mi->pwr_key); + mi->pwr_key_pressed = 1; + } + break; + case MISCEVENT_PWRONR: + if (mi->pwr_key_pressed) { + input_report_key(mi->pwr_key, KEY_POWER, 0); + input_sync(mi->pwr_key); + mi->pwr_key_pressed = 0; + } + break; + default: + break; + } +#endif /* #ifdef CONFIG_MISC_RT5036_PWRKEY */ +} + +static rt_irq_handler rt_miscirq_handler[MISCEVENT_MAX] = { + [MISCEVENT_PWRONLP] = rt5036_general_irq_handler, + [MISCEVENT_PWRONSP] = rt5036_general_irq_handler, + [MISCEVENT_PWRONF] = rt5036_general_irq_handler, + [MISCEVENT_PWRONR] = rt5036_general_irq_handler, + [MISCEVENT_KPSHDN] = rt5036_general_irq_handler, + [MISCEVENT_VDDALV] = rt5036_general_irq_handler, + [MISCEVNET_OTM] = rt5036_general_irq_handler, + [MISCEVENT_PMICSYSLV] = rt5036_general_irq_handler, + [MISCEVENT_LSW2LV] = rt5036_general_irq_handler, + [MISCEVENT_LSW1LV] = rt5036_general_irq_handler, + [MISCEVENT_LDO4LV] = rt5036_general_irq_handler, + [MISCEVENT_LDO3LV] = rt5036_general_irq_handler, + [MISCEVENT_LDO2LV] = rt5036_general_irq_handler, + [MISCEVENT_LDO1LV] = rt5036_general_irq_handler, + [MISCEVENT_BUCK4LV] = rt5036_general_irq_handler, + [MISCEVENT_BUCK3LV] = rt5036_general_irq_handler, + [MISCEVENT_BUCK2LV] = rt5036_general_irq_handler, + [MISCEVENT_BUCK1LV] = rt5036_general_irq_handler, +}; + +void rt5036_misc_irq_handler(struct rt5036_misc_info *mi, unsigned int irqevent) +{ + int i; + unsigned int masked_irq_event = + (misc_init_regval[6] << 16) | (misc_init_regval[7] << 8) | + misc_init_regval[8]; + unsigned int final_irq_event = irqevent & (~masked_irq_event); + + for (i = 0; i < MISCEVENT_MAX; i++) { + if ((final_irq_event & (1 << i)) && rt_miscirq_handler[i]) + rt_miscirq_handler[i] (mi, i); + } +} +EXPORT_SYMBOL(rt5036_misc_irq_handler); + +static int rt5036_misc_reginit(struct i2c_client *i2c) +{ + rt5036_reg_write(i2c, RT5036_REG_MISC6, misc_init_regval[5]); + rt5036_reg_block_write(i2c, RT5036_REG_MISC1, 5, misc_init_regval); + rt5036_reg_block_write(i2c, RT5036_REG_BUCKLDOIRQMASK, + 3, &misc_init_regval[6]); + /*always clear at the first time*/ + rt5036_reg_read(i2c, RT5036_REG_BUCKLDOIRQ); + rt5036_reg_read(i2c, RT5036_REG_LSWBASEIRQ); + rt5036_reg_read(i2c, RT5036_REG_PWRKEYIRQ); + return 0; +} + +static int rt_parse_dt(struct rt5036_misc_info *mi, + struct device *dev) +{ +#ifdef CONFIG_OF + struct device_node *np = dev->of_node; + u32 val; + + if (of_property_read_u32(np, "rt,shdn_press", &val)) { + dev_info(dev, + "no shut_lpress property, use the default value\n"); + } else { + if (val > RT5036_SHDNPRESS_MASK) + val = RT5036_SHDNPRESS_MAX; + misc_init_regval[1] &= (~RT5036_SHDNPRESS_MASK); + misc_init_regval[1] |= (val << RT5036_SHDNPRESS_SHIFT); + } + + if (of_property_read_u32(np, "rt,stb_en", &val)) { + dev_info(dev, "no stb_en prpperty , use the default value\n"); + } else { + if (val > RT5036_STB_MAX) + val = RT5036_STB_MAX; + misc_init_regval[2] &= (~RT5036_STBEN_MASK); + misc_init_regval[2] |= (val << RT5036_STBEN_SHIFT); + } + + if (of_property_read_bool(np, "rt,lp_enshdn")) + misc_init_regval[4] |= RT5036_LPSHDNEN_MASK; + else + misc_init_regval[4] &= (~RT5036_LPSHDNEN_MASK); + + if (of_property_read_u32(np, "rt,vsysuvlo", &val)) { + dev_info(dev, "no vsysuvlo prpperty , use the default value\n"); + } else { + if (val > RT5036_SYSLV_MAX) + val = RT5036_SYSLV_MAX; + misc_init_regval[5] &= (~RT5036_SYSUVLO_MASK); + misc_init_regval[5] |= (val << RT5036_SYSUVLO_SHIFT); + } + + if (of_property_read_bool(np, "rt,syslv_enshdn")) + misc_init_regval[4] |= RT5036_SYSLVENSHDN_MASK; + else + misc_init_regval[4] &= (~RT5036_SYSLVENSHDN_MASK); + + rt_pm_off = of_property_read_bool(np, "rt,system-power-controller"); +#endif /* #ifdef CONFIG_OF */ + rt5036_misc_reginit(mi->i2c); + RTINFO("\n"); + return 0; +} + +static int rt_parse_pdata(struct rt5036_misc_info *mi, + struct device *dev) +{ + struct rt5036_misc_data *misc_pdata = dev->platform_data; + /*SHDN_PRESS_TIME property*/ + misc_init_regval[1] &= (~RT5036_SHDNPRESS_MASK); + misc_init_regval[1] |= + (misc_pdata->shdn_press << RT5036_SHDNPRESS_SHIFT); + /*STB_EN property*/ + misc_init_regval[2] &= (~RT5036_STBEN_MASK); + misc_init_regval[2] |= (misc_pdata->stb_en << RT5036_STBEN_SHIFT); + /*LP_ENSHEN property*/ + if (misc_pdata->lp_enshdn) + misc_init_regval[4] |= RT5036_LPSHDNEN_MASK; + else + misc_init_regval[4] &= (~RT5036_LPSHDNEN_MASK); + + misc_init_regval[5] &= (~RT5036_SYSUVLO_MASK); + misc_init_regval[5] |= (misc_pdata->vsysuvlo << RT5036_SYSUVLO_SHIFT); + + if (misc_pdata->syslv_enshdn) + misc_init_regval[4] |= RT5036_SYSLVENSHDN_MASK; + else + misc_init_regval[4] &= (~RT5036_SYSLVENSHDN_MASK); + + rt5036_misc_reginit(mi->i2c); + RTINFO("\n"); + return 0; +} + +static int rt5036_misc_probe(struct platform_device *pdev) +{ + struct rt5036_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5036_platform_data *pdata = (pdev->dev.parent)->platform_data; + struct rt5036_misc_info *mi; + bool use_dt = pdev->dev.of_node; + + mi = devm_kzalloc(&pdev->dev, sizeof(*mi), GFP_KERNEL); + if (!mi) + return -ENOMEM; + + mi->i2c = chip->i2c; + if (use_dt) { + rt_parse_dt(mi, &pdev->dev); + } else { + if (!pdata) + goto out_dev; + pdev->dev.platform_data = pdata->misc_pdata; + rt_parse_pdata(mi, &pdev->dev); + } +#ifdef CONFIG_MISC_RT5036_PWRKEY + mi->pwr_key = input_allocate_device(); + if (!mi->pwr_key) { + dev_err(&pdev->dev, "Allocate pwr_key input fail\n"); + goto out_dev; + } + input_set_capability(mi->pwr_key, EV_KEY, KEY_POWER); + mi->pwr_key->name = "rt5036_pwr_key"; + mi->pwr_key->phys = "rt5036_pwr_key/input0"; + mi->pwr_key->dev.parent = &pdev->dev; + if (input_register_device(mi->pwr_key)) { + dev_err(&pdev->dev, "register pwr key fail\n"); + goto free_input; + } +#endif /* #ifdef CONFIG_MISC_RT5036_PWRKEY */ + mi->dev = &pdev->dev; + g_shdn = mi->i2c; + chip->misc_info = mi; + platform_set_drvdata(pdev, mi); + + if (rt_pm_off && !pm_power_off) + pm_power_off = rt5036_chip_shutdown; + + dev_info(&pdev->dev, "driver successfully loaded\n"); + return 0; +#ifdef CONFIG_MISC_RT5036_PWRKEY +free_input: + input_free_device(mi->pwr_key); +#endif /* #ifdef CONFIG_MISC_RT5036_PWRKEY */ +out_dev: + return -EINVAL; +} + +static int rt5036_misc_remove(struct platform_device *pdev) +{ +#ifdef CONFIG_MISC_RT5036_PWRKEY + struct rt5036_misc_info *mi = platform_get_drvdata(pdev); + + input_unregister_device(mi->pwr_key); + input_free_device(mi->pwr_key); +#endif /* #ifdef CONFIG_MISC_RT5036_PWRKEY */ + return 0; +} + +static const struct of_device_id rt_match_table[] = { + {.compatible = "rt,rt5036-misc",}, + {}, +}; + +static struct platform_driver rt5036_misc_driver = { + .driver = { + .name = RT5036_DEV_NAME "-misc", + .owner = THIS_MODULE, + .of_match_table = rt_match_table, + }, + .probe = rt5036_misc_probe, + .remove = rt5036_misc_remove, +}; + +static int __init rt5036_misc_init(void) +{ + return platform_driver_register(&rt5036_misc_driver); +} +subsys_initcall(rt5036_misc_init); + +static void __exit rt5036_misc_exit(void) +{ + platform_driver_unregister(&rt5036_misc_driver); +} +module_exit(rt5036_misc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CY Huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct rt_battery_info { + struct device *dev; + struct power_supply psy; + int chg_status; + unsigned char batt_present:1; + unsigned char suspend:1; +}; + +static enum power_supply_property rt_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static int rt_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct rt_battery_info *rbi = dev_get_drvdata(psy->dev->parent); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + rbi->chg_status = val->intval; + break; + case POWER_SUPPLY_PROP_PRESENT: + rbi->batt_present = val->intval; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_HEALTH: + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_CAPACITY: + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int rt_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt_battery_info *rbi = dev_get_drvdata(psy->dev->parent); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = rbi->chg_status; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = rbi->batt_present; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = 4000 * 1000; + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (rbi->chg_status == POWER_SUPPLY_STATUS_FULL) + val->intval = 100; + else + val->intval = 50; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int rt_battery_probe(struct platform_device *pdev) +{ + struct rt_battery_info *rbi; + int ret; + + rbi = devm_kzalloc(&pdev->dev, sizeof(*rbi), GFP_KERNEL); + if (!rbi) + return -ENOMEM; + rbi->dev = &pdev->dev; + rbi->chg_status = POWER_SUPPLY_STATUS_DISCHARGING; + rbi->batt_present = 1; + platform_set_drvdata(pdev, rbi); + + rbi->psy.name = RT_BATT_NAME; + rbi->psy.type = POWER_SUPPLY_TYPE_BATTERY; + rbi->psy.set_property = rt_battery_set_property; + rbi->psy.get_property = rt_battery_get_property; + rbi->psy.properties = rt_battery_props; + rbi->psy.num_properties = ARRAY_SIZE(rt_battery_props); + ret = power_supply_register(&pdev->dev, &rbi->psy); + if (ret < 0) { + dev_err(&pdev->dev, "battery supply registered fail\n"); + goto out_dev; + } + dev_info(&pdev->dev, "driver successfully loaded\n"); + return 0; +out_dev: + return ret; +} + +static int rt_battery_remove(struct platform_device *pdev) +{ + struct rt_battery_info *rbi = platform_get_drvdata(pdev); + + power_supply_unregister(&rbi->psy); + return 0; +} + +static int rt_battery_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rt_battery_info *rbi = platform_get_drvdata(pdev); + + rbi->suspend = 1; + return 0; +} + +static int rt_battery_resume(struct platform_device *pdev) +{ + struct rt_battery_info *rbi = platform_get_drvdata(pdev); + + rbi->suspend = 0; + return 0; +} + +static const struct of_device_id rt_match_table[] = { + {.compatible = "rt,rt-battery",}, + {}, +}; + +static struct platform_driver rt_battery_driver = { + .driver = { + .name = "rt-battery", + .owner = THIS_MODULE, + .of_match_table = rt_match_table, + }, + .probe = rt_battery_probe, + .remove = rt_battery_remove, + .suspend = rt_battery_suspend, + .resume = rt_battery_resume, +}; + +static int __init rt_battery_init(void) +{ + return platform_driver_register(&rt_battery_driver); +} +subsys_initcall(rt_battery_init); + +static void __exit rt_battery_exit(void) +{ + platform_driver_unregister(&rt_battery_driver); +} +module_exit(rt_battery_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CY Huang "); +MODULE_DESCRIPTION("RT Test Battery driver"); +MODULE_ALIAS("platform:rt-battery"); +MODULE_VERSION("1.0.0_G"); diff --git a/drivers/power/rt-power.c b/drivers/power/rt-power.c old mode 100755 new mode 100644 index 22fc92c49946..055d54e7b374 --- a/drivers/power/rt-power.c +++ b/drivers/power/rt-power.c @@ -58,135 +58,164 @@ static int rtpower_set_charger(struct rt_power_info *pi) chg_psy = power_supply_get_by_name("rt-charger"); if (chg_psy) { - rc = chg_psy->get_property(chg_psy, POWER_SUPPLY_PROP_ONLINE, &pval); + rc = chg_psy->get_property(chg_psy, POWER_SUPPLY_PROP_ONLINE, + &pval); if (rc < 0) dev_err(pi->dev, "get chg online prop fail\n"); else is_chg_on = pval.intval; if (pi->ac_online) { pval.intval = pi->acchg_icc; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CURRENT_AVG,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CURRENT_AVG, + &pval); if (rc < 0) dev_err(pi->dev, "set acchg aicr fail\n"); pval.intval = 500; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CURRENT_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set acchg icc fail\n"); pval.intval = POWER_SUPPLY_TYPE_MAINS; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CHARGE_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CHARGE_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set charge cable fail\n"); if (!is_chg_on) { pval.intval = pi->chg_volt; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,\ + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); if (rc < 0) - dev_err(pi->dev, "set chg voltage fail\n"); + dev_err(pi->dev, + "set chg voltage fail\n"); pval.intval = 1; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_ONLINE,\ + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_ONLINE, &pval); if (rc < 0) - dev_err(pi->dev, "set charger online fail\n"); + dev_err(pi->dev, + "set charger online fail\n"); } pval.intval = 1; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_PRESENT,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_PRESENT, + &pval); if (rc < 0) dev_err(pi->dev, "set charger present fail\n"); } else if (pi->usbta_online) { pval.intval = pi->usbtachg_icc; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CURRENT_AVG,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CURRENT_AVG, + &pval); if (rc < 0) dev_err(pi->dev, "set usbtachg aicr fail\n"); pval.intval = 500; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CURRENT_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set usbtachg icc fail\n"); pval.intval = POWER_SUPPLY_TYPE_USB_DCP; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CHARGE_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CHARGE_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set charge cable fail\n"); if (!is_chg_on) { pval.intval = pi->chg_volt; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &pval); if (rc < 0) - dev_err(pi->dev, "set chg voltage fail\n"); + dev_err(pi->dev, + "set chg voltage fail\n"); pval.intval = 1; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_ONLINE,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_ONLINE, + &pval); if (rc < 0) - dev_err(pi->dev, "set charger online fail\n"); + dev_err(pi->dev, + "set charger online fail\n"); } pval.intval = 1; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_PRESENT,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_PRESENT, + &pval); if (rc < 0) dev_err(pi->dev, "set charger present fail\n"); } else if (pi->usb_online) { pval.intval = pi->usbchg_icc; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CURRENT_AVG,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CURRENT_AVG, + &pval); if (rc < 0) dev_err(pi->dev, "set usbchg aicr fail\n"); pval.intval = 500; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CURRENT_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set usbchg icc fail\n"); pval.intval = POWER_SUPPLY_TYPE_USB; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CHARGE_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CHARGE_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set charge cable fail\n"); if (!is_chg_on) { pval.intval = pi->chg_volt; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &pval); if (rc < 0) - dev_err(pi->dev, "set chg voltage fail\n"); + dev_err(pi->dev, + "set chg voltage fail\n"); pval.intval = 1; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_ONLINE,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_ONLINE, + &pval); if (rc < 0) - dev_err(pi->dev, "set charger online fail\n"); + dev_err(pi->dev, + "set charger online fail\n"); } pval.intval = 1; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_PRESENT,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_PRESENT, + &pval); if (rc < 0) dev_err(pi->dev, "set charger present fail\n"); - } else { pval.intval = 0; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_ONLINE,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_ONLINE, + &pval); if (rc < 0) dev_err(pi->dev, "set charger online fail\n"); pval.intval = 0; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CHARGE_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CHARGE_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set charge cable fail\n"); pval.intval = pi->chg_volt; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set chg voltage fail\n"); pval.intval = 500; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CURRENT_AVG,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CURRENT_AVG, + &pval); if (rc < 0) dev_err(pi->dev, "set chg aicr fail\n"); pval.intval = 500; - rc = chg_psy->set_property(chg_psy, POWER_SUPPLY_PROP_CURRENT_NOW,\ - &pval); + rc = chg_psy->set_property(chg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, + &pval); if (rc < 0) dev_err(pi->dev, "set chg icc fail\n"); } @@ -198,8 +227,9 @@ static int rtpower_set_charger(struct rt_power_info *pi) return rc; } -static int rtpower_get_property(struct power_supply *psy, enum power_supply_property psp, \ - union power_supply_propval *val) +static int rtpower_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) { struct rt_power_info *pi = dev_get_drvdata(psy->dev->parent); int rc = 0; @@ -207,7 +237,8 @@ static int rtpower_get_property(struct power_supply *psy, enum power_supply_prop switch (psp) { case POWER_SUPPLY_PROP_ONLINE: if (psy->type == POWER_SUPPLY_TYPE_MAINS) - val->intval = (pi->ac_online || pi->usbta_online)?1 : 0; + val->intval = (pi->ac_online + || pi->usbta_online) ? 1 : 0; else if (psy->type == POWER_SUPPLY_TYPE_USB) val->intval = pi->usb_online; else @@ -220,7 +251,8 @@ static int rtpower_get_property(struct power_supply *psy, enum power_supply_prop return rc; } -static int rtpower_set_property(struct power_supply *psy, enum power_supply_property psp, \ +static int rtpower_set_property(struct power_supply *psy, + enum power_supply_property psp, const union power_supply_propval *val) { struct rt_power_info *pi = dev_get_drvdata(psy->dev->parent); @@ -239,13 +271,16 @@ static int rtpower_set_property(struct power_supply *psy, enum power_supply_prop if (val->intval) { pi->usbcnt = 0; wake_lock(&pi->usbdet_wakelock); - schedule_delayed_work(&pi->usbdet_work, 1*HZ); + schedule_delayed_work(&pi->usbdet_work, + 1 * HZ); } else { pi->usbcnt = RT_USBCNT_MAX; - schedule_delayed_work(&pi->usbdet_work, 0); + schedule_delayed_work(&pi->usbdet_work, + 0); if (pi->usbta_online) { pi->usbta_online = 0; - power_supply_changed(&pi->ac_psy); + power_supply_changed + (&pi->ac_psy); } } rc = rtpower_set_charger(pi); diff --git a/drivers/power/rt5036-charger.c b/drivers/power/rt5036-charger.c new file mode 100755 index 000000000000..85830781e589 --- /dev/null +++ b/drivers/power/rt5036-charger.c @@ -0,0 +1,1234 @@ +/* + * drivers/power/rt5036-charger.c + * Driver for Richtek RT5036 PMIC Charger driver + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN +#include +#include +#include +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + +#include +#include +#ifdef CONFIG_RT_POWER +#include +#endif /* #ifdef CONFIG_RT_POWER */ +#ifdef CONFIG_RT_BATTERY +#include +#endif /* #ifdef CONFIG_RT_BATTERY */ + +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN +#define RT5036_ACIN_LEVEL 0 +#define RT5036_USBIN_LEVEL 1 +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + +static unsigned char chg_init_regval[] = { + 0xb0, /*REG 0x01*/ + 0x58, /*REG 0x02*/ + 0x00, /*REG 0x03*/ + 0xFE, /*REG 0x04*/ + 0x93, /*REG 0x05*/ + 0xAD, /*REG 0x06*/ + 0xB4, /*REG 0x07*/ + 0x01, /*REG 0x08*/ + 0x0C, /*REG 0x13*/ + 0x80, /*REG 0x14*/ + 0x00, /*REG 0x15*/ + 0x70, /*REG 0x18*/ +}; + +static char *rtdef_chg_name = "rt-charger"; + +static char *rt_charger_supply_list[] = { + "none", +}; + +static enum power_supply_property rt_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, +}; + +static int rt_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt5036_charger_info *ci = dev_get_drvdata(psy->dev->parent); + int ret = 0; + int regval = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = ci->online; + break; + case POWER_SUPPLY_PROP_STATUS: + regval = rt5036_reg_read(ci->i2c, RT5036_REG_CHGSTAT1); + if (regval < 0) { + ret = -EINVAL; + } else { + regval &= RT5036_CHGSTAT_MASK; + regval >>= RT5036_CHGSTAT_SHIFT; + switch (regval) { + case 0: + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case 1: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case 2: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case 3: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + } + } + break; + case POWER_SUPPLY_PROP_PRESENT: + regval = rt5036_reg_read(ci->i2c, RT5036_REG_CHGSTAT1); + if (regval < 0) { + ret = -EINVAL; + } else { + if (regval & RT5036_CHGDIS_MASK) + val->intval = 0; + else + val->intval = 1; + } + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = ci->charge_cable; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = 2000; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + regval = rt5036_reg_read(ci->i2c, RT5036_REG_CHGCTL1); + if (regval < 0) { + ret = -EINVAL; + } else { + regval &= RT5036_CHGAICR_MASK; + regval >>= RT5036_CHGAICR_SHIFT; + switch (regval) { + case 0: + val->intval = 0; + break; + case 1: + val->intval = 100; + break; + case 2: + val->intval = 500; + break; + case 3: + val->intval = 700; + break; + case 4: + val->intval = 900; + break; + case 5: + val->intval = 1000; + break; + case 6: + val->intval = 1500; + break; + case 7: + val->intval = 2000; + break; + } + } + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + regval = rt5036_reg_read(ci->i2c, RT5036_REG_CHGSTAT1); + if (regval < 0) { + ret = -EINVAL; + } else { + if (regval & RT5036_CHGOPASTAT_MASK) { + val->intval = -1; + } else { + regval = + rt5036_reg_read(ci->i2c, + RT5036_REG_CHGCTL5); + if (regval < 0) { + ret = -EINVAL; + } else { + regval &= RT5036_CHGICC_MASK; + regval >>= RT5036_CHGICC_SHIFT; + val->intval = 500 + regval * 100; + } + } + } + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + regval = rt5036_reg_read(ci->i2c, RT5036_REG_CHGCTL2); + if (regval < 0) { + ret = -EINVAL; + } else { + regval &= RT5036_CHGCV_MASK; + regval >>= RT5036_CHGCV_SHIFT; + val->intval = regval * 25 + 3650; + } + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = 3650; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = 4400; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int rt_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct rt5036_charger_info *ci = dev_get_drvdata(psy->dev->parent); + int ret = 0; + int regval = 0; + + RTINFO("prop = %d, val->intval = %d\n", psp, val->intval); + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ci->online = val->intval; + if (ci->online) { +#ifdef CONFIG_RT_BATTERY + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name(RT_BATT_NAME); + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_CHARGING; + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get RT battery\n"); + } +#else +#ifdef CONFIG_RT9420_FUELGAUGE + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name("rt-fuelgauge"); + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_CHARGING; + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, + "couldn't get rt fuelgauge battery\n"); + } +#else + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name("battery"); + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_CHARGING; + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get battery\n"); + } +#endif /* #ifdef CONFIG_RT9420_FUELGAUGE */ +#endif /* #ifdef CONFIG_RT_BATTERY */ + if (ci->te_en) + ret = + rt5036_set_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGTEEN_MASK); + } else { +#ifdef CONFIG_RT_BATTERY + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name(RT_BATT_NAME); + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_DISCHARGING; + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get RT battery\n"); + } +#else +#ifdef CONFIG_RT9420_FUELGAUGE + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name("rt-fuelgauge"); + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_DISCHARGING; + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, + "couldn't get rt fuelgauge battery\n"); + } +#else + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name("battery"); + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_DISCHARGING; + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get battery\n"); + } +#endif /* #ifdef CONFIG_RT9420_FUELGAUGE */ +#endif /* #ifdef CONFIG_RT_BATTERY */ + if (ci->te_en) { + ret = + rt5036_clr_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGTEEN_MASK); + /* te rst workaround */ + rt5036_set_bits(ci->i2c, 0x20, + RT5036_TERST_MASK); + rt5036_clr_bits(ci->i2c, 0x20, + RT5036_TERST_MASK); + } + } + break; + case POWER_SUPPLY_PROP_PRESENT: + if (ci->online && val->intval) { + int icc = 0; + union power_supply_propval pval; + + if (ci->charge_cable == POWER_SUPPLY_TYPE_MAINS) + icc = ci->acchg_icc; + else if (ci->charge_cable == POWER_SUPPLY_TYPE_USB_DCP) + icc = ci->usbtachg_icc; + else + icc = ci->usbchg_icc; + pval.intval = icc; + ret = + psy->set_property(psy, + POWER_SUPPLY_PROP_CURRENT_NOW, + &pval); + if (ret < 0) + dev_err(ci->dev, "set final icc fail\n"); + } + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ci->charge_cable = val->intval; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + if (val->intval <= 0) + regval = 0; + else if (val->intval <= 100) + regval = 1; + else if (val->intval <= 500) + regval = 2; + else if (val->intval <= 700) + regval = 3; + else if (val->intval <= 900) + regval = 4; + else if (val->intval <= 1000) + regval = 5; + else if (val->intval <= 1500) + regval = 6; + else if (val->intval <= 2000) + regval = 7; + else + regval = 0; + if (!ci->batabs) + ret = rt5036_assign_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGAICR_MASK, + regval << + RT5036_CHGAICR_SHIFT); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + if (val->intval < 0) { + union power_supply_propval pval; +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + struct power_supply *psy = + power_supply_get_by_name(RT_USB_NAME); + pval.intval = 0; + psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval); + power_supply_changed(psy); + ci->otg_en = 1; + if (ci->charge_cable != POWER_SUPPLY_TYPE_MAINS) { + /* otg drop fix */ + rt5036_set_bits(ci->i2c, 0x23, 0x3); + pval.intval = ci->otg_volt; + ci->psy.set_property(&ci->psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &pval); + ret = + rt5036_set_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGOPAMODE_MASK); + /* otg drop fix */ + mdelay(10); + rt5036_clr_bits(ci->i2c, 0x23, 0x3); + } +#else + ci->otg_en = 1; + /* otg drop fix */ + rt5036_set_bits(ci->i2c, 0x23, 0x3); + pval.intval = ci->otg_volt; + ci->psy.set_property(&ci->psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &pval); + ret = + rt5036_set_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGOPAMODE_MASK); + /* otg drop fix */ + mdelay(10); + rt5036_clr_bits(ci->i2c, 0x23, 0x3); +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + } else if (val->intval == 0) { +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + if (ci->charge_cable != POWER_SUPPLY_TYPE_MAINS) + ret = + rt5036_clr_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGOPAMODE_MASK); +#else + ret = + rt5036_clr_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGOPAMODE_MASK); +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + ci->otg_en = 0; + } else if (val->intval < 500) + regval = 0; + else if (val->intval > 2000) + regval = 15; + else + regval = (val->intval - 500) / 100; + regval += 5; + if (regval > 15) + regval = 15; + if (!ci->batabs && val->intval > 0) + ret = + rt5036_assign_bits(ci->i2c, RT5036_REG_CHGCTL5, + RT5036_CHGICC_MASK, + regval << RT5036_CHGICC_SHIFT); + rt5036_reg_write(ci->i2c, RT5036_REG_CHGCTL4, + chg_init_regval[4]); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (val->intval < 3650) + regval = 0; + else if (val->intval > 5225) + regval = 0x3F; + else + regval = (val->intval - 3650) / 25; + ret = + rt5036_assign_bits(ci->i2c, RT5036_REG_CHGCTL2, + RT5036_CHGCV_MASK, + regval << RT5036_CHGCV_SHIFT); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + case POWER_SUPPLY_PROP_CURRENT_MAX: + case POWER_SUPPLY_PROP_STATUS: + default: + ret = -EINVAL; + } + return ret; +} + +static void rt5036_stat2alrt_irq_handler(void *info, int eventno) +{ + struct rt5036_charger_info *ci = info; + unsigned char old_stat, new_stat; + int ret; + + old_stat = ci->stat2; + ret = rt5036_reg_read(ci->i2c, RT5036_REG_CHGSTAT2); + if (ret < 0) { + dev_err(ci->dev, "read stat io fail\n"); + return; + } + new_stat = ret; + /*cablein status change*/ + if ((old_stat ^ new_stat) & RT5036_PWRRDY_MASK) { + if (new_stat & RT5036_PWRRDY_MASK) { +#ifndef CONFIG_RT_SUPPORT_ACUSB_DUALIN + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name(RT_USB_NAME); + if (psy) { + pval.intval = 1; + psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get RT usb\n"); + } +#endif /* #ifndef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + dev_info(ci->dev, "cable in\n"); + } else { +#ifndef CONFIG_RT_SUPPORT_ACUSB_DUALIN + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name(RT_USB_NAME); + if (psy) { + pval.intval = 0; + psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get RT usb\n"); + } +#endif /* #ifndef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + dev_info(ci->dev, "cable out\n"); + } + } + /*jeita status change*/ + old_stat = new_stat & RT5036_TSEVENT_MASK; + if (old_stat & RT5036_TSWC_MASK) { + dev_info(ci->dev, "warm or cool parameter\n"); + rt5036_set_bits(ci->i2c, RT5036_REG_CHGCTL7, + RT5036_CCJEITA_MASK); + } else if (old_stat & RT5036_TSHC_MASK) { + dev_info(ci->dev, "hot or cold temperature\n"); + rt5036_clr_bits(ci->i2c, RT5036_REG_CHGCTL7, + RT5036_CCJEITA_MASK); + } else { + dev_info(ci->dev, "normal temperature\n"); + rt5036_clr_bits(ci->i2c, RT5036_REG_CHGCTL7, + RT5036_CCJEITA_MASK); + } + ci->stat2 = new_stat; +} + +static void rt5036_batabs_irq_handler(void *info, int eventno) +{ + struct rt5036_charger_info *ci = info; + struct power_supply *psy = &ci->psy; + union power_supply_propval val; + int ret; + /*disable battery detection*/ + ret = rt5036_clr_bits(ci->i2c, RT5036_REG_CHGCTL6, RT5036_BATDEN_MASK); + if (ret < 0) + dev_err(ci->dev, "set battary detection disable fail\n"); + /*set aicr to 2000*/ + val.intval = 2000; + ret = psy->set_property(psy, POWER_SUPPLY_PROP_CURRENT_AVG, &val); + if (ret < 0) + dev_err(ci->dev, "set aicr to 2000 fail\n"); + /*set icc to 2000*/ + val.intval = 2000; + ret = psy->set_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, &val); + if (ret < 0) + dev_err(ci->dev, "set icc to 2000 fail\n"); + /*set charger offline*/ + val.intval = 0; + ret = psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, &val); + if (ret < 0) + dev_err(ci->dev, "set charger offline fail\n"); +#ifdef CONFIG_RT_BATTERY + psy = power_supply_get_by_name(RT_BATT_NAME); + if (psy) { + val.intval = 0; + ret = psy->set_property(psy, POWER_SUPPLY_PROP_PRESENT, &val); + if (ret < 0) + dev_err(ci->dev, "set battery not present fail\n"); + } else { + dev_err(ci->dev, "couldn't get batt psy\n"); + } +#else +#ifdef CONFIG_RT9420_FUELGAUGE + psy = power_supply_get_by_name("rt-fuelgauge"); + if (psy) { + val.intval = 0; + ret = psy->set_property(psy, POWER_SUPPLY_PROP_PRESENT, &val); + if (ret < 0) + dev_err(ci->dev, "set battery not present fail\n"); + } else { + dev_err(ci->dev, "couldn't get rt fuelgauge psy\n"); + } +#else + psy = power_supply_get_by_name("battery"); + if (psy) { + val.intval = 0; + ret = psy->set_property(psy, POWER_SUPPLY_PROP_PRESENT, &val); + if (ret < 0) + dev_err(ci->dev, "set battery not present fail\n"); + } else { + dev_err(ci->dev, "couldn't get battery psy\n"); + } +#endif /* #ifdef CONFIG_RT9420_FUELGAUGE */ +#endif /* #ifdef CONFIG_RT_BATTERY */ + ci->batabs = 1; +} + +static void rt5036_general_irq_handler(void *info, int eventno) +{ + struct rt5036_charger_info *ci = info; + + RTINFO("eventno=%02d\n", eventno); + switch (eventno) { + case CHGEVENT_CHTMRFI: + rt5036_clr_bits(ci->i2c, RT5036_REG_CHGCTL3, + RT5036_CHGOTGEN_MASK); + msleep(5); + rt5036_set_bits(ci->i2c, RT5036_REG_CHGCTL3, + RT5036_CHGOTGEN_MASK); + break; + case CHGEVENT_CHRCHGI: + if (!ci->online) { + dev_warn(ci->dev, "recharge false alarm\n"); + } else { +#ifdef CONFIG_RT_BATTERY + struct power_supply *psy = + power_supply_get_by_name(RT_BATT_NAME); + union power_supply_propval pval; + + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_CHARGING; + /*set battery status*/ + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get batt psy\n"); + } +#else +#ifdef CONFIG_RT9420_FUELGAUGE + struct power_supply *psy = + power_supply_get_by_name("rt-fuelgauge"); + union power_supply_propval pval; + + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_CHARGING; + /*set battery status*/ + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, + "couldn't get rt fuelgauge psy\n"); + } +#else + struct power_supply *psy = + power_supply_get_by_name("battery"); + union power_supply_propval pval; + + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_CHARGING; + /*set battery status*/ + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get battery psy\n"); + } +#endif /* #ifdef CONFIG_RT9420_FUELGAUGE */ +#endif /* #ifdfef CONFIG_RT_BATTERY */ + dev_info(ci->dev, "recharge occur\n"); + } + break; + case CHGEVENT_IEOCI: + if (!ci->online) { + dev_warn(ci->dev, "eoc false alarm\n"); + } else { +#ifdef CONFIG_RT_BATTERY + struct power_supply *psy = + power_supply_get_by_name(RT_BATT_NAME); + union power_supply_propval pval; + + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_FULL; + /*set battery status*/ + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get batt psy\n"); + } +#else +#ifdef CONFIG_RT9420_FUELGAUGE + struct power_supply *psy = + power_supply_get_by_name("rt-fuelgauge"); + union power_supply_propval pval; + + if (psy) { + /*set battery status*/ + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, + "couldn't get rt fuelgauge psy\n"); + } +#else + struct power_supply *psy = + power_supply_get_by_name("battery"); + union power_supply_propval pval; + + if (psy) { + pval.intval = POWER_SUPPLY_STATUS_FULL; + /*set battery status*/ + psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, + &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get battery psy\n"); + } +#endif /* #ifdef CONFIG_RT9420_FUELGAUGE */ +#endif /* #ifdfef CONFIG_RT_BATTERY */ + dev_info(ci->dev, "eoc really occur\n"); + } + break; + default: + break; + } +} + +static rt_irq_handler rt_chgirq_handler[CHGEVENT_MAX] = { + [CHGEVENT_STAT2ALT] = rt5036_stat2alrt_irq_handler, + [CHGEVENT_CHBSTLOWVI] = rt5036_general_irq_handler, + [CHGEVENT_BSTOLI] = rt5036_general_irq_handler, + [CHGEVENT_BSTVIMIDOVP] = rt5036_general_irq_handler, + [CHGEVENT_CHTMRFI] = rt5036_general_irq_handler, + [CHGEVENT_CHRCHGI] = rt5036_general_irq_handler, + [CHGEVENT_CHTERMI] = rt5036_general_irq_handler, + [CHGEVENT_CHBATOVI] = rt5036_general_irq_handler, + [CHGEVENT_CHRVPI] = rt5036_general_irq_handler, + [CHGEVENT_BATABSENSE] = rt5036_batabs_irq_handler, + [CHGEVENT_CHBADADPI] = rt5036_general_irq_handler, + [CHGEVENT_VINCHGPLUGOUT] = rt5036_general_irq_handler, + [CHGEVENT_VINCHGPLUGIN] = rt5036_general_irq_handler, + [CHGEVENT_PPBATLVI] = rt5036_general_irq_handler, + [CHGEVENT_IEOCI] = rt5036_general_irq_handler, + [CHGEVENT_VINOVPI] = rt5036_general_irq_handler, +}; + +void rt5036_charger_irq_handler(struct rt5036_charger_info *ci, + unsigned int irqevent) +{ + int i; + unsigned int masked_irq_event = + (chg_init_regval[8] << 16) | (chg_init_regval[9] << 8) | + chg_init_regval[10]; + unsigned int final_irq_event = irqevent & (~masked_irq_event); + + for (i = 0; i < CHGEVENT_MAX; i++) { + if ((final_irq_event & (1 << i)) && rt_chgirq_handler[i]) + rt_chgirq_handler[i] (ci, i); + } +} +EXPORT_SYMBOL(rt5036_charger_irq_handler); + +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN +static irqreturn_t rt5036_acdet_irq_handler(int irqno, void *param) +{ + struct rt5036_charger_info *ci = param; + + if (RT5036_ACIN_LEVEL == gpio_get_value(ci->acdet_gpio)) { + union power_supply_propval pval; + struct power_supply *psy = power_supply_get_by_name(RT_AC_NAME); + + if (RT5036_ACIN_LEVEL) + irq_set_irq_type(ci->acdet_irq, IRQF_TRIGGER_FALLING); + else + irq_set_irq_type(ci->acdet_irq, IRQF_TRIGGER_RISING); + if (psy) { + if (ci->otg_en) + rt5036_clr_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGOPAMODE_MASK); + pval.intval = 1; + psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get RT ac\n"); + } + dev_info(ci->dev, "ac in\n"); + } else { + union power_supply_propval pval; + struct power_supply *psy = power_supply_get_by_name(RT_AC_NAME); + + if (RT5036_ACIN_LEVEL) + irq_set_irq_type(ci->acdet_irq, IRQF_TRIGGER_RISING); + else + irq_set_irq_type(ci->acdet_irq, IRQF_TRIGGER_FALLING); + if (psy) { + pval.intval = 0; + psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval); + power_supply_changed(psy); + if (ci->otg_en) { + /*set otg voltage*/ + pval.intval = ci->otg_volt; + ci->psy.set_property(&ci->psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &pval); + rt5036_set_bits(ci->i2c, RT5036_REG_CHGCTL1, + RT5036_CHGOPAMODE_MASK); + } + } else { + dev_err(ci->dev, "couldn't get RT ac\n"); + } + dev_info(ci->dev, "ac out\n"); + } + return IRQ_HANDLED; +} + +static irqreturn_t rt5036_usbdet_irq_handler(int irqno, void *param) +{ + struct rt5036_charger_info *ci = param; + + if (ci->otg_en) { + dev_info(ci->dev, "currently in otg mode\n"); + goto usb_out; + } + if (RT5036_USBIN_LEVEL == gpio_get_value(ci->usbdet_gpio)) { + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name(RT_USB_NAME); + if (RT5036_USBIN_LEVEL) + irq_set_irq_type(ci->usbdet_irq, IRQF_TRIGGER_FALLING); + else + irq_set_irq_type(ci->usbdet_irq, IRQF_TRIGGER_RISING); + if (psy) { + pval.intval = 1; + psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get RT usb\n"); + } + dev_info(ci->dev, "usb in\n"); + } else { + union power_supply_propval pval; + struct power_supply *psy = + power_supply_get_by_name(RT_USB_NAME); + if (RT5036_USBIN_LEVEL) + irq_set_irq_type(ci->usbdet_irq, IRQF_TRIGGER_RISING); + else + irq_set_irq_type(ci->usbdet_irq, IRQF_TRIGGER_FALLING); + if (psy) { + pval.intval = 0; + psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval); + power_supply_changed(psy); + } else { + dev_err(ci->dev, "couldn't get RT usb\n"); + } + dev_info(ci->dev, "usb out\n"); + } +usb_out: + return IRQ_HANDLED; +} + +static int rt5036_acusb_irqinit(struct rt5036_charger_info *ci) +{ + int rc = 0; + + if (gpio_is_valid(ci->acdet_gpio) && gpio_is_valid(ci->usbdet_gpio)) { + rc = gpio_request(ci->acdet_gpio, "rt5036_acdet_gpio"); + if (rc < 0) { + dev_err(ci->dev, "request acdet gpio fail\n"); + goto irq_out; + } + rc = gpio_request(ci->usbdet_gpio, "rt5036_usbdet_gpio"); + if (rc < 0) { + dev_err(ci->dev, "request usbdet gpio fail\n"); + goto irq_out; + } + gpio_direction_input(ci->acdet_gpio); + gpio_direction_input(ci->usbdet_gpio); + + ci->acdet_irq = gpio_to_irq(ci->acdet_gpio); + ci->usbdet_irq = gpio_to_irq(ci->usbdet_gpio); + + rc = devm_request_threaded_irq(ci->dev, ci->acdet_irq, NULL, + rt5036_acdet_irq_handler, + IRQF_TRIGGER_FALLING | + IRQF_DISABLED, + "rt5036_acdet_irq", ci); + if (rc < 0) { + dev_err(ci->dev, "request acdet irq fail\n"); + goto irq_out; + } + rc = devm_request_threaded_irq(ci->dev, ci->usbdet_irq, NULL, + rt5036_usbdet_irq_handler, + IRQF_TRIGGER_FALLING | + IRQF_DISABLED, + "rt5036_usbdet_irq", ci); + if (rc < 0) { + dev_err(ci->dev, "request usbdet irq fail\n"); + goto irq_out; + } + enable_irq_wake(ci->acdet_irq); + enable_irq_wake(ci->usbdet_irq); + } else { + rc = -EINVAL; + } +irq_out: + return rc; +} + +static void rt5036_acusb_irqdeinit(struct rt5036_charger_info *ci) +{ + devm_free_irq(ci->dev, ci->acdet_irq, ci); + devm_free_irq(ci->dev, ci->usbdet_irq, ci); + gpio_free(ci->acdet_gpio); + gpio_free(ci->usbdet_gpio); +} +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + +static void rt5036_chg_dwork_func(struct work_struct *work) +{ + struct rt5036_charger_info *ci = + container_of(work, struct rt5036_charger_info, + dwork.work); + rt5036_stat2alrt_irq_handler(ci, 0); +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + rt5036_acdet_irq_handler(ci->acdet_irq, ci); + rt5036_usbdet_irq_handler(ci->usbdet_irq, ci); +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ +} + +static int rt5036_charger_reginit(struct i2c_client *client) +{ + /*thermal HGM*/ + rt5036_set_bits(client, 0x20, 0x40); + /*charger fix in rev D IC*/ + rt5036_reg_write(client, 0x22, 0xE0); + /*write charger init val*/ + rt5036_reg_block_write(client, RT5036_REG_CHGCTL1, 8, chg_init_regval); + rt5036_reg_block_write(client, RT5036_REG_CHGIRQMASK1, 3, + &chg_init_regval[8]); + rt5036_reg_write(client, RT5036_REG_CHGSTAT2MASK, chg_init_regval[11]); + /*always read at first time*/ + rt5036_reg_read(client, RT5036_REG_CHGIRQ1); + rt5036_reg_read(client, RT5036_REG_CHGIRQ2); + rt5036_reg_read(client, RT5036_REG_CHGIRQ3); + rt5036_set_bits(client, 0x20, RT5036_TERST_MASK); + rt5036_clr_bits(client, 0x20, RT5036_TERST_MASK); + RTINFO("\n"); + return 0; +} + +static int rt_parse_dt(struct rt5036_charger_info *ci, + struct device *dev) +{ +#ifdef CONFIG_OF + struct device_node *np = dev->of_node; + u32 val; + + if (of_property_read_bool(np, "rt,te_en")) + ci->te_en = 1; + + if (of_property_read_u32(np, "rt,iprec", &val)) { + dev_info(dev, "no iprec property, use the default value\n"); + } else { + if (val > RT5036_IPREC_MAX) + val = RT5036_IPREC_MAX; + chg_init_regval[4] &= (~RT5036_CHGIPREC_MASK); + chg_init_regval[4] |= (val << RT5036_CHGIPREC_SHIFT); + } + + if (of_property_read_u32(np, "rt,ieoc", &val)) { + dev_info(dev, "no ieoc property, use the default value\n"); + } else { + if (val > RT5036_IEOC_MAX) + val = RT5036_IEOC_MAX; + chg_init_regval[4] &= (~RT5036_CHGIEOC_MASK); + chg_init_regval[4] |= val; + } + + if (of_property_read_u32(np, "rt,vprec", &val)) { + dev_info(dev, "no vprec property, use the default value\n"); + } else { + if (val > RT5036_VPREC_MAX) + val = RT5036_VPREC_MAX; + chg_init_regval[5] &= (~RT5036_CHGVPREC_MASK); + chg_init_regval[5] |= val; + } + + if (of_property_read_u32(np, "rt,batlv", &val)) { + dev_info(dev, "no batlv property, use the default value\n"); + } else { + if (val > RT5036_BATLV_MAX) + val = RT5036_BATLV_MAX; + chg_init_regval[6] &= (~RT5036_CHGBATLV_MASK); + chg_init_regval[6] |= val; + } + + if (of_property_read_u32(np, "rt,vrechg", &val)) { + dev_info(dev, "no vrechg property, use the default value\n"); + } else { + if (val > RT5036_VRECHG_MAX) + val = RT5036_VRECHG_MAX; + chg_init_regval[7] &= (~RT5036_CHGVRECHG_MASK); + chg_init_regval[7] |= (val << RT5036_CHGVRECHG_SHIFT); + } + + if (of_property_read_u32(np, "rt,chg_volt", &val)) { + dev_info(dev, + "no chg_volt property, use 4200 as the default value\n"); + ci->chg_volt = 4200; + } else { + ci->chg_volt = val; + } + + if (of_property_read_u32(np, "rt,otg_volt", &val)) { + dev_info(dev, + "no otg_volt property, use 5025 as the default value\n"); + ci->otg_volt = 5025; + } else { + ci->otg_volt = val; + } + + if (of_property_read_u32(np, "rt,acchg_icc", &val)) { + dev_info(dev, + "no acchg_icc property, use 2000 as the default value\n"); + ci->acchg_icc = 2000; + } else { + ci->acchg_icc = val; + } + + if (of_property_read_u32(np, "rt,usbtachg_icc", &val)) { + dev_info(dev, + "no usbtachg_icc property, use 2000 as the default value\n"); + ci->usbtachg_icc = 2000; + } else { + ci->usbtachg_icc = val; + } + + if (of_property_read_u32(np, "rt,usbchg_icc", &val)) { + dev_info(dev, + "no usbchg_icc property, use 500 as the default value\n"); + ci->usbchg_icc = 500; + } else { + ci->usbchg_icc = val; + } + +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + ci->acdet_gpio = of_get_named_gpio(np, "rt,acdet_gpio", 0); + ci->usbdet_gpio = of_get_named_gpio(np, "rt,usbdet_gpio", 0); +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ +#endif /* #ifdef CONFIG_OF */ + rt5036_charger_reginit(ci->i2c); + RTINFO("\n"); + return 0; +} + +static int rt_parse_pdata(struct rt5036_charger_info *ci, + struct device *dev) +{ + struct rt5036_chg_data *pdata = dev->platform_data; + + if (pdata->te_en) + ci->te_en = 1; + + chg_init_regval[4] &= (~RT5036_CHGIPREC_MASK); + chg_init_regval[4] |= (pdata->iprec << RT5036_CHGIPREC_SHIFT); + + chg_init_regval[4] &= (~RT5036_CHGIEOC_MASK); + chg_init_regval[4] |= pdata->ieoc; + + chg_init_regval[5] &= (~RT5036_CHGVPREC_MASK); + chg_init_regval[5] |= pdata->vprec; + + chg_init_regval[6] &= (~RT5036_CHGBATLV_MASK); + chg_init_regval[6] |= pdata->batlv; + + chg_init_regval[7] &= (~RT5036_CHGVRECHG_MASK); + chg_init_regval[7] |= (pdata->vrechg << RT5036_CHGVRECHG_SHIFT); + + ci->chg_volt = pdata->chg_volt; + ci->otg_volt = pdata->otg_volt; + ci->acchg_icc = pdata->acchg_icc; + ci->usbtachg_icc = pdata->usbtachg_icc; + ci->usbchg_icc = pdata->usbchg_icc; +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + ci->acdet_gpio = pdata->acdet_gpio; + ci->usbdet_gpio = pdata->usbdet_gpio; +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + rt5036_charger_reginit(ci->i2c); + RTINFO("\n"); + return 0; +} + +#ifdef CONFIG_RT_POWER +static struct platform_device rt_power_dev = { + .name = "rt-power", + .id = -1, +}; +#endif /* #ifdef CONFIG_RT_POWER */ + +#ifdef CONFIG_RT_BATTERY +static struct platform_device rt_battery_dev = { + .name = "rt-battery", + .id = -1, +}; +#endif /* #ifdef CONFIG_RT_BATTERY */ + +static int rt5036_charger_probe(struct platform_device *pdev) +{ + struct rt5036_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5036_platform_data *pdata = (pdev->dev.parent)->platform_data; +#ifdef CONFIG_RT_POWER + struct rt_power_data *rt_power_pdata; +#endif /* #ifdef CONFIG_RT_POWER */ + struct rt5036_charger_info *ci; + bool use_dt = pdev->dev.of_node; + int ret = 0; + + ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL); + if (!ci) + return -ENOMEM; + + ci->i2c = chip->i2c; + if (use_dt) { + rt_parse_dt(ci, &pdev->dev); + } else { + if (!pdata) { + dev_err(&pdev->dev, "platform data invalid\n"); + ret = -EINVAL; + goto out_dev; + } + pdev->dev.platform_data = pdata->chg_pdata; + rt_parse_pdata(ci, &pdev->dev); + } + + ci->dev = &pdev->dev; + INIT_DELAYED_WORK(&ci->dwork, rt5036_chg_dwork_func); + + platform_set_drvdata(pdev, ci); + /*power supply register*/ + ci->psy.name = rtdef_chg_name; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) + ci->psy.type = POWER_SUPPLY_TYPE_UNKNOWN; +#else + ci->psy.type = -1; +#endif /* #ifdef (LINUX_VERSION_CODE */ + ci->psy.supplied_to = rt_charger_supply_list; + ci->psy.properties = rt_charger_props; + ci->psy.num_properties = ARRAY_SIZE(rt_charger_props); + ci->psy.get_property = rt_charger_get_property; + ci->psy.set_property = rt_charger_set_property; + ret = power_supply_register(&pdev->dev, &ci->psy); + if (ret < 0) { + dev_err(&pdev->dev, + "couldn't create power supply for rt-charger\n"); + goto out_dev; + } +#ifdef CONFIG_RT_BATTERY + rt_battery_dev.dev.parent = &pdev->dev; + ret = platform_device_register(&rt_battery_dev); + if (ret < 0) + goto out_dev; +#endif /* #ifdef CONFIG_RT_BATTERY */ + +#ifdef CONFIG_RT_POWER + rt_power_pdata = + devm_kzalloc(&pdev->dev, sizeof(*rt_power_pdata), GFP_KERNEL); + if (!rt_power_pdata) { + ret = -ENOMEM; + goto out_psy; + } + rt_power_pdata->chg_volt = ci->chg_volt; + rt_power_pdata->acchg_icc = ci->acchg_icc; + rt_power_pdata->usbtachg_icc = ci->usbtachg_icc; + rt_power_pdata->usbchg_icc = ci->usbchg_icc; + + rt_power_dev.dev.platform_data = rt_power_pdata; + rt_power_dev.dev.parent = &pdev->dev; + ret = platform_device_register(&rt_power_dev); + if (ret < 0) + goto out_psy; +#endif /* #ifdef CONFIG_RT_POWER */ + +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + ret = rt5036_acusb_irqinit(ci); + if (ret < 0) + goto out_all; +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + + schedule_delayed_work(&ci->dwork, msecs_to_jiffies(100)); + chip->chg_info = ci; + dev_info(&pdev->dev, "driver successfully loaded\n"); + return 0; +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN +out_all: + platform_device_unregister(&rt_power_dev); + devm_kfree(&pdev->dev, rt_power_pdata); +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ +#ifdef CONFIG_RT_POWER +out_psy: +#endif /* #ifdef CONFIG_RT_POEWR */ +#ifdef CONFIG_RT_BATTERY + platform_device_unregister(&rt_battery_dev); +#endif /* #ifdef CONFIG_RT_BATTERY */ + power_supply_unregister(&ci->psy); +out_dev: + return ret; +} + +static int rt5036_charger_remove(struct platform_device *pdev) +{ + struct rt5036_charger_info *ci = platform_get_drvdata(pdev); +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + rt5036_acusb_irqdeinit(ci); +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ +#ifdef CONFIG_RT_POWER + platform_device_unregister(&rt_power_dev); +#endif /* #ifdef CONFIG_RT_POWER */ +#ifdef CONFIG_RT_BATTERY + platform_device_unregister(&rt_battery_dev); +#endif /* #ifdef CONFIG_RT_BATTERY */ + power_supply_unregister(&ci->psy); + return 0; +} + +static const struct of_device_id rt_match_table[] = { + {.compatible = "rt,rt5036-charger",}, + {}, +}; + +static struct platform_driver rt5036_charger_driver = { + .driver = { + .name = RT5036_DEV_NAME "-charger", + .owner = THIS_MODULE, + .of_match_table = rt_match_table, + }, + .probe = rt5036_charger_probe, + .remove = rt5036_charger_remove, +}; + +static int __init rt5036_charger_init(void) +{ + return platform_driver_register(&rt5036_charger_driver); +} +subsys_initcall(rt5036_charger_init); + +static void __exit rt5036_charger_exit(void) +{ + platform_driver_unregister(&rt5036_charger_driver); +} +module_exit(rt5036_charger_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CY Huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif /* #ifdef CONFIG_OF */ + +#include +#include + +struct rt5036_regulator_info { + struct regulator_desc desc; + struct regulator_dev *regulator; + struct i2c_client *i2c; + const unsigned int *vol_output_list; + const int vol_output_size; + int min_uV; + int max_uV; + unsigned char nvol_reg; + unsigned char nvol_shift; + unsigned char nvol_mask; + unsigned char nenable_reg; + unsigned char nenable_bit; + unsigned char nmode_reg; + unsigned char nmode_bit; + unsigned char nramp_reg; + unsigned char nramp_bit; + unsigned char nramp_shift; + unsigned char svol_reg; + unsigned char svol_shift; + unsigned char svol_mask; + unsigned char senable_reg; + unsigned char senable_bit; + unsigned char smode_reg; + unsigned char smode_bit; + unsigned char sramp_reg; + unsigned char sramp_bit; + unsigned char sramp_shift; +}; + +/*For DCDC1~4 and LDO1~4 and LSW1~2*/ +static const unsigned int rt5036_vol_output_list[] = { + /*0~7 */ + 800 * 1000, 825 * 1000, 850 * 1000, 875 * 1000, 900 * 1000, 925 * 1000, + 950 * 1000, 975 * 1000, + /*8~15 */ + 1000 * 1000, 1025 * 1000, 1050 * 1000, 1075 * 1000, 1100 * 1000, + 1125 * 1000, 1150 * 1000, 1175 * 1000, + /*16~23 */ + 1200 * 1000, 1225 * 1000, 1250 * 1000, 1275 * 1000, 1300 * 1000, + 1325 * 1000, 1350 * 1000, 1375 * 1000, + /*24~31 */ + 1400 * 1000, 1425 * 1000, 1450 * 1000, 1475 * 1000, 1500 * 1000, + 1525 * 1000, 1550 * 1000, 1575 * 1000, + /*32~39 */ + 1600 * 1000, 1625 * 1000, 1650 * 1000, 1675 * 1000, 1700 * 1000, + 1725 * 1000, 1750 * 1000, 1775 * 1000, + /*40~47 */ + 1800 * 1000, 1825 * 1000, 1850 * 1000, 1875 * 1000, 1900 * 1000, + 1925 * 1000, 1950 * 1000, 1975 * 1000, + /*48~55 */ + 2000 * 1000, 2025 * 1000, 2050 * 1000, 2075 * 1000, 2100 * 1000, + 2125 * 1000, 2150 * 1000, 2175 * 1000, + /*56~63 */ + 2200 * 1000, 2225 * 1000, 2250 * 1000, 2275 * 1000, 2300 * 1000, + 2325 * 1000, 2350 * 1000, 2375 * 1000, + /*64~71 */ + 2400 * 1000, 2425 * 1000, 2450 * 1000, 2475 * 1000, 2500 * 1000, + 2525 * 1000, 2550 * 1000, 2575 * 1000, + /*72~79 */ + 2600 * 1000, 2625 * 1000, 2650 * 1000, 2675 * 1000, 2700 * 1000, + 2725 * 1000, 2750 * 1000, 2775 * 1000, + /*80~87 */ + 2800 * 1000, 2825 * 1000, 2850 * 1000, 2875 * 1000, 2900 * 1000, + 2925 * 1000, 2950 * 1000, 2975 * 1000, + /*88~95 */ + 3000 * 1000, 3025 * 1000, 3050 * 1000, 3075 * 1000, 3100 * 1000, + 3125 * 1000, 3150 * 1000, 3175 * 1000, + /*96~103 */ + 3200 * 1000, 3225 * 1000, 3250 * 1000, 3275 * 1000, 3300 * 1000, + 3300 * 1000, 3300 * 1000, 3300 * 1000, + /*104~111 */ + 3300 * 1000, 3300 * 1000, 3300 * 1000, 3300 * 1000, 3300 * 1000, + 3300 * 1000, 3300 * 1000, 3300 * 1000, + /*112~119 */ + 3300 * 1000, 3300 * 1000, 3300 * 1000, 3300 * 1000, 3300 * 1000, + 3300 * 1000, 3300 * 1000, 3300 * 1000, + /*120~127 */ + 3300 * 1000, 3300 * 1000, 3300 * 1000, 3300 * 1000, 3300 * 1000, + 3300 * 1000, 3300 * 1000, 3300 * 1000, +}; + +#define rt5036_vol_output_size ARRAY_SIZE(rt5036_vol_output_list) + +static inline int check_range(struct rt5036_regulator_info *info, + int min_uV, int max_uV) +{ + if (min_uV < info->min_uV || min_uV > info->max_uV) + return -EINVAL; + return 0; +} + +static int rt5036_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + + return (index >= info->vol_output_size) ? + -EINVAL : info->vol_output_list[index]; +} + +static int rt5036_find_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + int i = 0; + const int count = info->vol_output_size; + + for (i = 0; i < count; i++) { + if ((info->vol_output_list[i] >= min_uV) + && (info->vol_output_list[i] <= max_uV)) + return i; + } + return -EINVAL; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) +static int rt5036_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data; + const int count = info->vol_output_size; + + if (selector > count) + return -EINVAL; + data = (unsigned char)selector; + data <<= info->nvol_shift; + return rt5036_assign_bits(info->i2c, info->nvol_reg, info->nvol_mask, + data); +} + +static int rt5036_get_voltage_sel(struct regulator_dev *rdev) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + ret = rt5036_reg_read(info->i2c, info->nvol_reg); + if (ret < 0) + return ret; + return (ret & info->nvol_mask) >> info->nvol_shift; +} +#else + +static int rt5036_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data; + + if (check_range(info, min_uV, max_uV)) { + dev_err(&rdev->dev, "invalid voltage range (%d, %d) uV\n", + min_uV, max_uV); + return -EINVAL; + } + data = rt5036_find_voltage(rdev, min_uV, max_uV); + data <<= info->nvol_shift; + return rt5036_assign_bits(info->i2c, info->nvol_reg, info->nvol_mask, + data); +} + +static int rt5036_get_voltage(struct regulator_dev *rdev) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + ret = rt5036_reg_read(info->i2c, info->nvol_reg); + if (ret < 0) + return ret; + ret = (ret & info->nvol_mask) >> info->nvol_shift; + return rt5036_list_voltage(rdev, ret); +} +#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38) */ + +static int rt5036_enable(struct regulator_dev *rdev) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + + return rt5036_set_bits(info->i2c, info->nenable_reg, info->nenable_bit); +} + +static int rt5036_disable(struct regulator_dev *rdev) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + + return rt5036_clr_bits(info->i2c, info->nenable_reg, info->nenable_bit); +} + +static int rt5036_is_enabled(struct regulator_dev *rdev) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + ret = rt5036_reg_read(info->i2c, info->nenable_reg); + if (ret < 0) + return ret; + return (ret & info->nenable_bit) ? 1 : 0; +} + +static int rt5036_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + ret = + rt5036_set_bits(info->i2c, info->nmode_reg, + info->nmode_bit); + break; + case REGULATOR_MODE_FAST: + ret = + rt5036_clr_bits(info->i2c, info->nmode_reg, + info->nmode_bit); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static unsigned int rt5036_get_mode(struct regulator_dev *rdev) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + unsigned int mode; + int data; + + data = rt5036_reg_read(info->i2c, info->nmode_reg); + mode = + (data & info->nmode_bit) ? REGULATOR_MODE_NORMAL : + REGULATOR_MODE_FAST; + return mode; +} + +static int rt5036_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data; + + if (check_range(info, uV, uV)) { + dev_err(&rdev->dev, "invalid voltage range (%d, %d) uV\n", + uV, uV); + return -EINVAL; + } + data = rt5036_find_voltage(rdev, uV, uV); + data <<= info->svol_shift; + return rt5036_assign_bits(info->i2c, info->svol_reg, info->svol_mask, + data); +} + +static int rt5036_set_suspend_enable(struct regulator_dev *rdev) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + + return rt5036_set_bits(info->i2c, info->senable_reg, info->senable_bit); +} + +static int rt5036_set_suspend_disable(struct regulator_dev *rdev) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + + return rt5036_clr_bits(info->i2c, info->senable_reg, info->senable_bit); +} + +static int rt5036_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct rt5036_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + ret = + rt5036_set_bits(info->i2c, info->smode_reg, + info->smode_bit); + break; + case REGULATOR_MODE_FAST: + ret = + rt5036_clr_bits(info->i2c, info->smode_reg, + info->smode_bit); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static struct regulator_ops rt5036_regulator_ops = { + .list_voltage = rt5036_list_voltage, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) + .get_voltage_sel = rt5036_get_voltage_sel, + .set_voltage_sel = rt5036_set_voltage_sel, +#else + .set_voltage = rt5036_set_voltage, + .get_voltage = rt5036_get_voltage, +#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38) */ + .enable = rt5036_enable, + .disable = rt5036_disable, + .is_enabled = rt5036_is_enabled, + .set_mode = rt5036_set_mode, + .get_mode = rt5036_get_mode, + .set_suspend_voltage = rt5036_set_suspend_voltage, + .set_suspend_enable = rt5036_set_suspend_enable, + .set_suspend_disable = rt5036_set_suspend_disable, + .set_suspend_mode = rt5036_set_suspend_mode, +}; + +#define RT5036_VOUT_LIST rt5036_vol_output_list +#define RT5036_VOUT_SIZE rt5036_vol_output_size + +#define RT5036_DCDC(_id, min, max) \ +{ \ + .desc = { \ + .name = "rt5036-dcdc" #_id, \ + .n_voltages = RT5036_VOUT_SIZE, \ + .ops = &rt5036_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = RT5036_ID_DCDC##_id, \ + .owner = THIS_MODULE, \ + }, \ + .vol_output_list = RT5036_VOUT_LIST, \ + .vol_output_size = RT5036_VOUT_SIZE, \ + .min_uV = min * 1000, \ + .max_uV = max * 1000, \ + .nvol_reg = RT5036_REG_BUCKVN##_id, \ + .nvol_shift = RT5036_DCDCVOUT_SHIFT##_id, \ + .nvol_mask = RT5036_DCDCVOUT_MASK##_id, \ + .nenable_reg = RT5036_REG_BUCKLDONEN, \ + .nenable_bit = RT5036_DCDCEN_MASK##_id, \ + .nmode_reg = RT5036_REG_BUCKVRCNEN, \ + .nmode_bit = RT5036_DCDCMODE_MASK##_id, \ + .nramp_reg = RT5036_REG_BUCKVRCN, \ + .nramp_bit = RT5036_DCDCRAMP_MASK##_id, \ + .nramp_shift = RT5036_DCDCRAMP_SHIFT##_id, \ + .svol_reg = RT5036_REG_BUCKVS##_id, \ + .svol_shift = RT5036_DCDCVOUT_SHIFT##_id, \ + .svol_mask = RT5036_DCDCVOUT_MASK##_id, \ + .senable_reg = RT5036_REG_BUCKLDOSEN, \ + .senable_bit = RT5036_DCDCEN_MASK##_id, \ + .smode_reg = RT5036_REG_BUCKVRCSEN, \ + .smode_bit = RT5036_DCDCMODE_MASK##_id, \ + .sramp_reg = RT5036_REG_BUCKVRCS, \ + .sramp_bit = RT5036_DCDCRAMP_MASK##_id, \ + .sramp_shift = RT5036_DCDCRAMP_SHIFT##_id, \ +} + +#define RT5036_LDO(_id, min, max) \ +{ \ + .desc = { \ + .name = "rt5036-ldo" #_id, \ + .n_voltages = RT5036_VOUT_SIZE, \ + .ops = &rt5036_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = RT5036_ID_LDO##_id, \ + .owner = THIS_MODULE, \ + }, \ + .vol_output_list = RT5036_VOUT_LIST, \ + .vol_output_size = RT5036_VOUT_SIZE, \ + .min_uV = min * 1000, \ + .max_uV = max * 1000, \ + .nvol_reg = RT5036_REG_LDOVN##_id, \ + .nvol_shift = RT5036_LDOVOUT_SHIFT##_id, \ + .nvol_mask = RT5036_LDOVOUT_MASK##_id, \ + .nenable_reg = RT5036_REG_BUCKLDONEN, \ + .nenable_bit = RT5036_LDOEN_MASK##_id, \ + .nmode_reg = RT5036_REG_LDOVRCNEN, \ + .nmode_bit = RT5036_LDOMODE_MASK##_id, \ + .nramp_reg = RT5036_REG_LDOVRCN, \ + .nramp_bit = RT5036_LDORAMP_MASK##_id, \ + .nramp_shift = RT5036_LDORAMP_SHIFT##_id, \ + .svol_reg = RT5036_REG_LDOVS##_id, \ + .svol_shift = RT5036_LDOVOUT_SHIFT##_id, \ + .svol_mask = RT5036_LDOVOUT_MASK##_id, \ + .senable_reg = RT5036_REG_BUCKLDOSEN, \ + .senable_bit = RT5036_LDOEN_MASK##_id, \ + .smode_reg = RT5036_REG_LDOVRCSEN, \ + .smode_bit = RT5036_LDOMODE_MASK##_id, \ + .sramp_reg = RT5036_REG_LDOVRCS, \ + .sramp_bit = RT5036_LDORAMP_MASK##_id, \ + .sramp_shift = RT5036_LDORAMP_SHIFT##_id, \ +} + +#define RT5036_LSW(_id, min, max) \ +{ \ + .desc = { \ + .name = "rt5036-lsw" #_id, \ + .n_voltages = RT5036_VOUT_SIZE, \ + .ops = &rt5036_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = RT5036_ID_LSW##_id, \ + .owner = THIS_MODULE, \ + }, \ + .vol_output_list = RT5036_VOUT_LIST, \ + .vol_output_size = RT5036_VOUT_SIZE, \ + .min_uV = min * 1000, \ + .max_uV = max * 1000, \ + .nvol_reg = RT5036_REG_LSWVN##_id, \ + .nvol_shift = RT5036_LSWVOUT_SHIFT##_id, \ + .nvol_mask = RT5036_LSWVOUT_MASK##_id, \ + .nenable_reg = RT5036_REG_LSWEN, \ + .nenable_bit = RT5036_LSWNEN_MASK##_id, \ + .nmode_reg = RT5036_REG_LSWVRCEN, \ + .nmode_bit = RT5036_LSWNMODE_MASK##_id, \ + .nramp_reg = RT5036_REG_LSWVRC, \ + .nramp_bit = RT5036_LSWNRAMP_MASK##_id, \ + .nramp_shift = RT5036_LSWNRAMP_SHIFT##_id, \ + .svol_reg = RT5036_REG_LSWVS##_id, \ + .svol_shift = RT5036_LSWVOUT_SHIFT##_id, \ + .svol_mask = RT5036_LSWVOUT_MASK##_id, \ + .senable_reg = RT5036_REG_LSWEN, \ + .senable_bit = RT5036_LSWSEN_MASK##_id, \ + .smode_reg = RT5036_REG_LSWVRCEN, \ + .smode_bit = RT5036_LSWSMODE_MASK##_id, \ + .sramp_reg = RT5036_REG_LSWVRC, \ + .sramp_bit = RT5036_LSWSRAMP_MASK##_id, \ + .sramp_shift = RT5036_LSWSRAMP_SHIFT##_id, \ +} + +static struct rt5036_regulator_info rt5036_regulator_info[] = { + RT5036_DCDC(1, 800, 3300), + RT5036_DCDC(2, 800, 3300), + RT5036_DCDC(3, 800, 3300), + RT5036_DCDC(4, 800, 3300), + RT5036_LDO(1, 800, 3300), + RT5036_LDO(2, 800, 3300), + RT5036_LDO(3, 800, 3300), + RT5036_LDO(4, 800, 3300), + RT5036_LSW(1, 800, 3300), + RT5036_LSW(2, 800, 3300), +}; + +static struct rt5036_regulator_info *find_regulator_info(int id) +{ + struct rt5036_regulator_info *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(rt5036_regulator_info); i++) { + ri = &rt5036_regulator_info[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +inline struct regulator_dev *rt5036_regulator_register(struct regulator_desc + *regulator_desc, + struct device *dev, + struct + regulator_init_data + *init_data, + void *driver_data) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) + struct regulator_config config = { + .dev = dev, + .init_data = init_data, + .driver_data = driver_data, + .of_node = dev->of_node, + }; + return regulator_register(regulator_desc, &config); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 37)) + return regulator_register(regulator_desc, dev, init_data, driver_data, + dev->of_node); +#else + return regulator_register(regulator_desc, dev, init_data, driver_data); +#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(3,5,0)) */ +} + +static struct regulator_init_data *rt_parse_dt(struct + rt5036_regulator_info + *ri, + struct device *dev) +{ + struct regulator_init_data *init_data = NULL; +#ifdef CONFIG_OF + struct device_node *np = dev->of_node; + int rc; + u32 tmp; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) + init_data = of_get_regulator_init_data(dev, dev->of_node); +#else + init_data = of_get_regulator_init_data(dev); +#endif /* #if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(3,3,0)) */ + + if (init_data) { + init_data->supply_regulator = (char *)of_get_property(np, + "supply-regulator", + NULL); + rc = of_property_read_u32(np, "rt,standby_vol", &tmp); + if (rc) + dev_info(dev, "no standby voltage specified\n"); + else + init_data->constraints.state_standby.uV = tmp; + if (of_property_read_bool(np, "rt,standby_enabled")) { + init_data->constraints.state_standby.enabled = 1; + init_data->constraints.initial_state = + PM_SUSPEND_STANDBY; + } + if (of_property_read_bool(np, "rt,standby_disabled")) { + init_data->constraints.state_standby.disabled = 1; + init_data->constraints.initial_state = + PM_SUSPEND_STANDBY; + } + } + + rc = of_property_read_u32(np, "rt,nramp_sel", &tmp); + if (rc) { + dev_info(dev, "no nramp_sel property, use default value\n"); + } else { + if (tmp > RT5036_RAMP_MAX) + tmp = RT5036_RAMP_MAX; + rt5036_assign_bits(ri->i2c, ri->nramp_reg, ri->nramp_bit, + tmp << ri->nramp_shift); + } + + rc = of_property_read_u32(np, "rt,sramp_sel", &tmp); + if (rc) { + dev_info(dev, "no sramp_sel property, use default value\n"); + } else { + if (tmp > RT5036_RAMP_MAX) + tmp = RT5036_RAMP_MAX; + rt5036_assign_bits(ri->i2c, ri->sramp_reg, ri->sramp_bit, + tmp << ri->sramp_shift); + } + + if (of_property_read_bool(np, "rt,allow_mode_mask")) { + init_data->constraints.valid_modes_mask |= + (REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL); + init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE; + } +#endif /* #ifdef CONFIG_OF */ + return init_data; +} + +static int rt5036_regulator_probe(struct platform_device *pdev) +{ + struct rt5036_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5036_platform_data *pdata = (pdev->dev.parent)->platform_data; + struct rt5036_regulator_info *ri; + struct rt5036_regulator_ramp *ramp; + struct regulator_dev *rdev; + struct regulator_init_data *init_data; + bool use_dt = pdev->dev.of_node; + + ri = find_regulator_info(pdev->id); + if (ri == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + + ri->i2c = chip->i2c; + if (use_dt) { + init_data = rt_parse_dt(ri, &pdev->dev); + } else { + if (!pdata) + return -EINVAL; + init_data = pdata->regulator[pdev->id]; + ramp = init_data ? init_data->driver_data : NULL; + if (ramp) { + rt5036_assign_bits(ri->i2c, ri->nramp_reg, + ri->nramp_bit, + ramp->nramp_sel << ri->nramp_shift); + rt5036_assign_bits(ri->i2c, ri->sramp_reg, + ri->sramp_bit, + ramp->sramp_sel << ri->sramp_shift); + } + } + + if (!init_data) { + dev_err(&pdev->dev, "no initializing data\n"); + return -EINVAL; + } + + rdev = rt5036_regulator_register(&ri->desc, &pdev->dev, init_data, ri); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + + platform_set_drvdata(pdev, rdev); + /*dev_info(&pdev->dev, "regulator successfully registered\n");*/ + RTINFO("\n"); + return 0; +} + +static int rt5036_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + RTINFO("\n"); + return 0; +} + +static const struct of_device_id rt_match_table[] = { + {.compatible = "rt,rt5036-dcdc1",}, + {.compatible = "rt,rt5036-dcdc2",}, + {.compatible = "rt,rt5036-dcdc3",}, + {.compatible = "rt,rt5036-dcdc4",}, + {.compatible = "rt,rt5036-ldo1",}, + {.compatible = "rt,rt5036-ldo2",}, + {.compatible = "rt,rt5036-ldo3",}, + {.compatible = "rt,rt5036-ldo4",}, + {.compatible = "rt,rt5036-lsw1",}, + {.compatible = "rt,rt5036-lsw2",}, + {}, +}; + +static struct platform_driver rt5036_regulator_driver = { + .driver = { + .name = RT5036_DEV_NAME "-regulator", + .owner = THIS_MODULE, + .of_match_table = rt_match_table, + }, + .probe = rt5036_regulator_probe, + .remove = rt5036_regulator_remove, +}; + +static int __init rt5036_regulator_init(void) +{ + return platform_driver_register(&rt5036_regulator_driver); +} +subsys_initcall(rt5036_regulator_init); + +static void __exit rt5036_regulator_exit(void) +{ + platform_driver_unregister(&rt5036_regulator_driver); +} +module_exit(rt5036_regulator_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CY Huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static unsigned char rtc_init_regval[] = { + 0x1, /*REG 0x97*/ + 0x3, /*REG 0xA5*/ +}; + +static int rt5036_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rt5036_rtc_info *ri = dev_get_drvdata(dev); + unsigned char val[6]; + int rc; + + RTINFO("\n"); + rc = rt5036_reg_block_read(ri->i2c, RT5036_REG_RTCTSEC, ARRAY_SIZE(val), + val); + if (rc < 0) { + dev_err(dev, "reading rtc time io error\n"); + } else { + tm->tm_sec = val[0] & RT5036_RTC_SECMASK; + tm->tm_min = val[1] & RT5036_RTC_MINMASK; + tm->tm_hour = val[2] & RT5036_RTC_HOURMASK; + tm->tm_year = (val[3] & RT5036_RTC_YEARMASK) + 100; + tm->tm_mon = (val[4] & RT5036_RTC_MONMASK) - 1; + tm->tm_mday = val[5] & RT5036_RTC_DAYMASK; + RTINFO("%04d:%02d:%02d, %02d:%02d:%02d\n", tm->tm_year + 1900, + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + rc = rtc_valid_tm(tm); + if (rc < 0) { + dev_err(dev, "not invalid time reading from RT5036\n"); + return -EINVAL; + } + } + return rc; +} + +static int rt5036_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rt5036_rtc_info *ri = dev_get_drvdata(dev); + unsigned char val[6]; + int rc; + + RTINFO("\n"); + rc = rt5036_reg_block_read(ri->i2c, RT5036_REG_RTCTSEC, ARRAY_SIZE(val), + val); + if (rc < 0) { + dev_err(dev, "reading rtc time io error\n"); + } else { + val[0] &= ~RT5036_RTC_SECMASK; + val[0] |= (tm->tm_sec & RT5036_RTC_SECMASK); + val[1] &= ~RT5036_RTC_MINMASK; + val[1] |= (tm->tm_min & RT5036_RTC_MINMASK); + val[2] &= ~RT5036_RTC_HOURMASK; + val[2] |= (tm->tm_hour & RT5036_RTC_HOURMASK); + val[3] &= ~RT5036_RTC_YEARMASK; + val[3] |= ((tm->tm_year - 100) & RT5036_RTC_YEARMASK); + val[4] &= ~RT5036_RTC_MONMASK; + val[4] |= ((tm->tm_mon + 1) & RT5036_RTC_MONMASK); + val[5] &= ~RT5036_RTC_DAYMASK; + val[5] |= (tm->tm_mday & RT5036_RTC_DAYMASK); + val[5] &= ~RT5036_RTC_WEEKMASK; + val[5] |= + ((tm-> + tm_wday & RT5036_RTC_WEEKMASK) << RT5036_RTC_WEEKSHIFT); + RTINFO("%04d:%02d:%02d, %02d:%02d:%02d\n", tm->tm_year + 1900, + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + + if (tm->tm_year < 100) + return -EINVAL; + + rc = rt5036_reg_block_write(ri->i2c, RT5036_REG_RTCTSEC, + ARRAY_SIZE(val), val); + if (rc < 0) { + dev_err(dev, "writing rtc time io error\n"); + return rc; + } + } + return rc; +} + +static int rt5036_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rt5036_rtc_info *ri = dev_get_drvdata(dev); + struct rtc_time *tm = &(alarm->time); + unsigned char val[6]; + int rc; + + RTINFO("\n"); + rc = rt5036_reg_block_read(ri->i2c, RT5036_REG_RTCASEC, ARRAY_SIZE(val), + val); + if (rc < 0) { + dev_err(dev, "reading alarm time io error\n"); + } else { + tm->tm_sec = val[0] & RT5036_RTC_SECMASK; + tm->tm_min = val[1] & RT5036_RTC_MINMASK; + tm->tm_hour = val[2] & RT5036_RTC_HOURMASK; + tm->tm_year = (val[3] & RT5036_RTC_YEARMASK) + 100; + tm->tm_mon = (val[4] & RT5036_RTC_MONMASK) - 1; + tm->tm_mday = val[5] & RT5036_RTC_DAYMASK; + RTINFO("%04d_%02d_%02d, %02d:%02d:%02d\n", tm->tm_year + 1900, + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + rc = rtc_valid_tm(tm); + if (rc < 0) { + dev_err(dev, "not invalid alarm reading from RT5036\n"); + return -EINVAL; + } + } + return rc; +} + +static int rt5036_alarm_irq_enable(struct device *dev, unsigned int enabled); + +static int rt5036_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rt5036_rtc_info *ri = dev_get_drvdata(dev); + struct rtc_time *tm = &alarm->time; + unsigned char val[6]; + int rc; + + RTINFO("\n"); + rt5036_alarm_irq_enable(ri->dev, 0); + rc = rt5036_reg_block_read(ri->i2c, RT5036_REG_RTCASEC, ARRAY_SIZE(val), + val); + if (rc < 0) { + dev_err(dev, "reading rtc time io error\n"); + } else { + val[0] &= ~RT5036_RTC_SECMASK; + val[0] |= (tm->tm_sec & RT5036_RTC_SECMASK); + val[1] &= ~RT5036_RTC_MINMASK; + val[1] |= (tm->tm_min & RT5036_RTC_MINMASK); + val[2] &= ~RT5036_RTC_HOURMASK; + val[2] |= (tm->tm_hour & RT5036_RTC_HOURMASK); + val[3] &= ~RT5036_RTC_YEARMASK; + val[3] |= ((tm->tm_year - 100) & RT5036_RTC_YEARMASK); + val[4] &= ~RT5036_RTC_MONMASK; + val[4] |= ((tm->tm_mon + 1) & RT5036_RTC_MONMASK); + val[5] &= ~RT5036_RTC_DAYMASK; + val[5] |= (tm->tm_mday & RT5036_RTC_DAYMASK); + RTINFO("%04d:%02d:%02d, %02d:%02d:%02d\n", tm->tm_year + 1900, + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + + if (tm->tm_year < 100) + return -EINVAL; + + rc = rt5036_reg_block_write(ri->i2c, RT5036_REG_RTCASEC, + ARRAY_SIZE(val), val); + if (rc < 0) { + dev_err(dev, "writing alarm time io error\n"); + return rc; + } + } + rt5036_alarm_irq_enable(ri->dev, alarm->enabled); + return rc; +} + +static int rt5036_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rt5036_rtc_info *ri = dev_get_drvdata(dev); + + RTINFO("enable=%d\n", enabled); + if (enabled) { + rt5036_clr_bits(ri->i2c, RT5036_REG_STBWACKIRQMASK, + RT5036_RTCAIRQ_MASK); + rt5036_set_bits(ri->i2c, RT5036_REG_STBMODE, + RT5036_RTCAEN_MASK); + } else { + rt5036_clr_bits(ri->i2c, RT5036_REG_STBMODE, + RT5036_RTCAEN_MASK); + rt5036_set_bits(ri->i2c, RT5036_REG_STBWACKIRQMASK, + RT5036_RTCAIRQ_MASK); + } + return 0; +} + +static const struct rtc_class_ops rt5036_rtc_ops = { + .read_time = rt5036_read_time, + .set_time = rt5036_set_time, + .read_alarm = rt5036_read_alarm, + .set_alarm = rt5036_set_alarm, + .alarm_irq_enable = rt5036_alarm_irq_enable, +}; + +static void rt5036_general_irq_handler(void *info, int eventno) +{ + struct rt5036_rtc_info *ri = info; + + dev_info(ri->dev, "eventno=%02d\n", eventno); + switch (eventno) { + case RTCEVENT_CAIRQ: + rt5036_alarm_irq_enable(ri->dev, 0); + break; + default: + break; + } +} + +static rt_irq_handler rt_rtcirq_handler[RTCEVENT_MAX] = { + [RTCEVENT_CAIRQ] = rt5036_general_irq_handler, + [RTCEVENT_CDIRQ] = rt5036_general_irq_handler, +}; + +void rt5036_rtc_irq_handler(struct rt5036_rtc_info *ri, unsigned int irqevent) +{ + int i; + + for (i = 0; i < RTCEVENT_MAX; i++) { + if ((irqevent & (1 << i)) && rt_rtcirq_handler[i]) + rt_rtcirq_handler[i] (ri, i); + } +} +EXPORT_SYMBOL(rt5036_rtc_irq_handler); + +static int rt5036_rtc_reginit(struct i2c_client *i2c) +{ + rt5036_reg_write(i2c, RT5036_REG_STBMODE, rtc_init_regval[0]); + rt5036_reg_write(i2c, RT5036_REG_STBWACKIRQMASK, rtc_init_regval[1]); + /*always clear at the fist time*/ + rt5036_reg_read(i2c, RT5036_REG_STBWACKIRQ); + return 0; +} + +static int rt_parse_dt(struct rt5036_rtc_info *ri, struct device *dev) +{ + rt5036_rtc_reginit(ri->i2c); + RTINFO("\n"); + return 0; +} + +static int rt_parse_pdata(struct rt5036_rtc_info *ri, + struct device *dev) +{ + rt5036_rtc_reginit(ri->i2c); + RTINFO("\n"); + return 0; +} + +static int rt5036_rtc_probe(struct platform_device *pdev) +{ + struct rt5036_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5036_rtc_info *ri; + bool use_dt = pdev->dev.of_node; + + ri = devm_kzalloc(&pdev->dev, sizeof(*ri), GFP_KERNEL); + if (!ri) + return -ENOMEM; + + ri->i2c = chip->i2c; + if (use_dt) + rt_parse_dt(ri, &pdev->dev); + else + rt_parse_pdata(ri, &pdev->dev); + + ri->dev = &pdev->dev; + platform_set_drvdata(pdev, ri); + + ri->rtc = rtc_device_register("rt5036-rtc", &pdev->dev, + &rt5036_rtc_ops, THIS_MODULE); + if (IS_ERR(ri->rtc)) { + dev_err(&pdev->dev, "rtc device register failed\n"); + goto out_dev; + } + chip->rtc_info = ri; + device_init_wakeup(&pdev->dev, 1); + dev_info(&pdev->dev, "driver successfully loaded\n"); + return 0; +out_dev: + return -EINVAL; +} + +static int rt5036_rtc_remove(struct platform_device *pdev) +{ + struct rt5036_rtc_info *ri = platform_get_drvdata(pdev); + + rtc_device_unregister(ri->rtc); + return 0; +} + +static const struct of_device_id rt_match_table[] = { + {.compatible = "rt,rt5036-rtc",}, + {}, +}; + +static struct platform_driver rt5036_rtc_driver = { + .driver = { + .name = RT5036_DEV_NAME "-rtc", + .owner = THIS_MODULE, + .of_match_table = rt_match_table, + }, + .probe = rt5036_rtc_probe, + .remove = rt5036_rtc_remove, +}; + +static int __init rt5036_rtc_init(void) +{ + return platform_driver_register(&rt5036_rtc_driver); +} +subsys_initcall(rt5036_rtc_init); + +static void __exit rt5036_rtc_exit(void) +{ + platform_driver_unregister(&rt5036_rtc_driver); +} +module_exit(rt5036_rtc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("CY Huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_MFD_RT5036_IRQ_H +#define _LINUX_MFD_RT5036_IRQ_H + +#define RT5036_IRQPREZ_MASK 0x80 + +#endif /* #ifndef _LINUX_MFD_RT5036_IRQ_H */ diff --git a/include/linux/mfd/rt5036/rt5036-misc.h b/include/linux/mfd/rt5036/rt5036-misc.h new file mode 100755 index 000000000000..3533539ce48d --- /dev/null +++ b/include/linux/mfd/rt5036/rt5036-misc.h @@ -0,0 +1,55 @@ +/* + * include/linux/mfd/rt5036/rt5036-misc.h + * Include header file for Richtek RT5036 Misc option + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_MFD_RT5036_MISC_H +#define _LINUX_MFD_RT5036_MISC_H + +enum { + MISCEVENT_PWRONLP = 3, + MISCEVENT_PWRONSP, + MISCEVENT_PWRONF, + MISCEVENT_PWRONR, + MISCEVENT_KPSHDN, + MISCEVENT_VDDALV, + MISCEVNET_OTM, + MISCEVENT_PMICSYSLV = 13, + MISCEVENT_LSW2LV, + MISCEVENT_LSW1LV, + MISCEVENT_LDO4LV, + MISCEVENT_LDO3LV, + MISCEVENT_LDO2LV, + MISCEVENT_LDO1LV, + MISCEVENT_BUCK4LV, + MISCEVENT_BUCK3LV, + MISCEVENT_BUCK2LV, + MISCEVENT_BUCK1LV, + MISCEVENT_MAX, +}; + +#define RT5036_SHDNPRESS_MASK 0x0C +#define RT5036_SHDNPRESS_SHIFT 2 + +#define RT5036_STBEN_MASK 0x06 +#define RT5036_STBEN_SHIFT 1 + +#define RT5036_LPSHDNEN_MASK 0x04 +#define RT5036_CHIPSHDN_MASK 0x80 +#define RT5036_SYSUVLO_SHIFT 5 +#define RT5036_SYSUVLO_MASK 0xE0 +#define RT5036_SYSLVENSHDN_MASK 0x20 +#define RT5036_PWRRDY_MASK 0x80 + +extern int rt5036_vin_exist(void); +extern void rt5036_chip_shutdown(void); + +#endif /* #ifndef _LINUX_MFD_RT5036_MISC_H */ diff --git a/include/linux/mfd/rt5036/rt5036.h b/include/linux/mfd/rt5036/rt5036.h new file mode 100644 index 000000000000..2b56d411a1c8 --- /dev/null +++ b/include/linux/mfd/rt5036/rt5036.h @@ -0,0 +1,369 @@ +/* + * include/linux/mfd/rt5036/rt5036.h + * Include header file for Richtek RT5036 Core file + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_MFD_RT5036_H +#define _LINUX_MFD_RT5036_H +#include + +#define RT5036_DEV_NAME "rt5036" +#define RT5036_DRV_VER "1.0.8_R" + +enum { + RT5036_ID_DCDC1, + RT5036_ID_DCDC2, + RT5036_ID_DCDC3, + RT5036_ID_DCDC4, + RT5036_ID_LDO1, + RT5036_ID_LDO2, + RT5036_ID_LDO3, + RT5036_ID_LDO4, + RT5036_ID_LSW1, + RT5036_ID_LSW2, + RT5036_MAX_REGULATOR, +}; + +enum { + RT5036_REG_DEVID, + RT5036_REG_RANGE1START = RT5036_REG_DEVID, + RT5036_REG_CHGCTL1, + RT5036_REG_CHGCTL2, + RT5036_REG_RESV1, + RT5036_REG_CHGCTL3, + RT5036_REG_CHGCTL4, + RT5036_REG_CHGCTL5, + RT5036_REG_CHGCTL6, + RT5036_REG_CHGCTL7, + RT5036_REG_RSTCHG, + RT5036_REG_RANGE1END = RT5036_REG_RSTCHG, + RT5036_REG_CHGIRQ1 = 0x10, + RT5036_REG_RANGE2START = RT5036_REG_CHGIRQ1, + RT5036_REG_CHGIRQ2, + RT5036_REG_CHGIRQ3, + RT5036_REG_CHGIRQMASK1, + RT5036_REG_CHGIRQMASK2, + RT5036_REG_CHGIRQMASK3, + RT5036_REG_CHGSTAT1, + RT5036_REG_CHGSTAT2, + RT5036_REG_CHGSTAT2MASK, + RT5036_REG_RANGE2END = RT5036_REG_CHGSTAT2MASK, + RT5036_REG_BUCKVN1 = 0x41, + RT5036_REG_RANGE3START = RT5036_REG_BUCKVN1, + RT5036_REG_BUCKVN2, + RT5036_REG_BUCKVN3, + RT5036_REG_BUCKVN4, + RT5036_REG_BUCKVRCN, + RT5036_REG_BUCKVRCNEN, + RT5036_REG_BUCKMODE, + RT5036_REG_LDOVN1, + RT5036_REG_LDOVN2, + RT5036_REG_LDOVN3, + RT5036_REG_LDOVN4, + RT5036_REG_LDOVRCN, + RT5036_REG_LDOVRCNEN, + RT5036_REG_LDOMODE, + RT5036_REG_BUCKLDONEN, + RT5036_REG_LSWEN, + RT5036_REG_MISC1, + RT5036_REG_MISC2, + RT5036_REG_MISC3, + RT5036_REG_MISC4, + RT5036_REG_MISC5, + RT5036_REG_ONOFFEVENT, + RT5036_REG_BUCKLDOIRQ, + RT5036_REG_LSWBASEIRQ, + RT5036_REG_PWRKEYIRQ, + RT5036_REG_BUCKLDOIRQMASK, + RT5036_REG_LSWBASEIRQMASK, + RT5036_REG_PWRKEYIRQMASK, + RT5036_REG_RANGE3END = RT5036_REG_PWRKEYIRQMASK, + RT5036_REG_MISC6 = 0x65, + RT5036_REG_RANGE4START = RT5036_REG_MISC6, + RT5036_REG_RANGE4END = RT5036_REG_MISC6, + RT5036_REG_BUCKVS1 = 0x71, + RT5036_REG_RANGE5START = RT5036_REG_BUCKVS1, + RT5036_REG_BUCKVS2, + RT5036_REG_BUCKVS3, + RT5036_REG_BUCKVS4, + RT5036_REG_BUCKVRCS, + RT5036_REG_BUCKVRCSEN, + RT5036_REG_RESV2, + RT5036_REG_LDOVS1, + RT5036_REG_LDOVS2, + RT5036_REG_LDOVS3, + RT5036_REG_LDOVS4, + RT5036_REG_LDOVRCS, + RT5036_REG_LDOVRCSEN, + RT5036_REG_RESV3, + RT5036_REG_BUCKLDOSEN, + RT5036_REG_LSWVN2, + RT5036_REG_LSWVN1, + RT5036_REG_LSWVS2, + RT5036_REG_LSWVS1, + RT5036_REG_LSWVRC, + RT5036_REG_LSWVRCEN, + RT5036_REG_BUCKOCPSEL, + RT5036_REG_RANGE5END = RT5036_REG_BUCKOCPSEL, + RT5036_REG_RTCADJ = 0x90, + RT5036_REG_RANGE6START = RT5036_REG_RTCADJ, + RT5036_REG_RTCTSEC, + RT5036_REG_RTCTMINUTE, + RT5036_REG_RTCTHOUR, + RT5036_REG_RTCTYEAR, + RT5036_REG_RTCTMON, + RT5036_REG_RTCTDATEW, + RT5036_REG_STBMODE, + RT5036_REG_RTCASEC, + RT5036_REG_RTCAMINUTE, + RT5036_REG_RTCAHOUR, + RT5036_REG_RTCAYEAR, + RT5036_REG_RTCAMONTH, + RT5036_REG_RTCADATE, + RT5036_REG_STBCDSEC, + RT5036_REG_STBCDMINUTE, + RT5036_REG_STBCDHOUR, + RT5036_REG_STBCDDATEL, + RT5036_REG_STBCDDATEH, + RT5036_REG_RESV4, + RT5036_REG_STBWACKIRQ, + RT5036_REG_STBWACKIRQMASK, + RT5036_REG_RANGE6END = RT5036_REG_STBWACKIRQMASK, + RT5036_REG_MAX, +}; + +enum { + RT5036_RAMP_25mV, + RT5036_RAMP_50mV, + RT5036_RAMP_75mV, + RT5036_RAMP_100mV, + RT5036_RAMP_MAX = RT5036_RAMP_100mV, +}; + +enum { + RT5036_SHDHPRESS_4S, + RT5036_SHDNPRESS_6S, + RT5036_SHDNPRESS_8S, + RT5036_SHDNPRESS_10S, + RT5036_SHDNPRESS_MAX = RT5036_SHDNPRESS_10S, +}; + +enum { + RT5036_STB_DISABLE, + RT5036_STB_EN1MS, + RT5036_STB_EN2MS, + RT5036_STB_EN4MS, + RT5036_STB_MAX = RT5036_STB_EN4MS, +}; + +enum { + RT5036_SYSLV_2P8V, + RT5036_SYSLV_2P9V, + RT5036_SYSLV_3P0V, + RT5036_SYSLV_3P1V, + RT5036_SYSLV_3P2V, + RT5036_SYSLV_3P3V, + RT5036_SYSLV_3P4V, + RT5036_SYSLV_3P5V, + RT5036_SYSLV_MAX = RT5036_SYSLV_3P5V +}; + +enum { + RT5036_IPREC_150mA, + RT5036_IPREC_250mA, + RT5036_IPREC_350mA, + RT5036_IPREC_450mA, + RT5036_IPREC_MAX = RT5036_IPREC_450mA, +}; + +enum { + RT5036_IEOC_DISABLE, + RT5036_IEOC_150mA, + RT5036_IEOC_200mA, + RT5036_IEOC_250mA, + RT5036_IEOC_300mA, + RT5036_IEOC_400mA, + RT5036_IEOC_500mA, + RT5036_IEOC_600mA, + RT5036_IEOC_MAX = RT5036_IEOC_600mA, +}; + +enum { + RT5036_VPREC_2P3V, + RT5036_VPREC_2P4V, + RT5036_VPREC_2P5V, + RT5036_VPREC_2P6V, + RT5036_VPREC_2P7V, + RT5036_VPREC_2P8V, + RT5036_VPREC_2P9V, + RT5036_VPREC_3P0V, + RT5036_VPREC_3P1V, + RT5036_VPREC_3P2V, + RT5036_VPREC_3P3V, + RT5036_VPREC_3P4V, + RT5036_VPREC_3P5V, + RT5036_VPREC_3P6V, + RT5036_VPREC_3P7V, + RT5036_VPREC_3P8V, + RT5036_VPREC_MAX = RT5036_VPREC_3P8V, +}; + +enum { + RT5036_BATLV_2P4V, + RT5036_BATLV_2P5V, + RT5036_BATLV_2P6V, + RT5036_BATLV_2P7V, + RT5036_BATLV_2P8V, + RT5036_BATLV_2P9V, + RT5036_BATLV_3P0V, + RT5036_BATLV_3P1V, + RT5036_BATLV_MAX = RT5036_BATLV_3P1V, +}; + +enum { + RT5036_VRECHG_0P1V, + RT5036_VRECHG_0P2V, + RT5036_VRECHG_0P3V, + RT5036_VRECHG_0P3V_1, + RT5036_VRECHG_MAX = RT5036_VRECHG_0P3V_1, +}; + +typedef void (*rt_irq_handler) (void *info, int eventno); + +struct rt5036_regulator_ramp { + unsigned char nramp_sel:2; + unsigned char sramp_sel:2; +}; + +struct rt5036_chg_data { + int chg_volt; + int otg_volt; + int acchg_icc; + int usbtachg_icc; + int usbchg_icc; +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + int acdet_gpio; + int usbdet_gpio; +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + u16 te_en:1; + u16 iprec:2; + u16 ieoc:3; + u16 vprec:4; + u16 batlv:3; + u16 vrechg:2; +}; + +struct rt5036_misc_data { + u16 shdn_press:2; + u16 stb_en:2; + u16 lp_enshdn:1; + u16 vsysuvlo:3; + u16 syslv_enshdn:1; +}; + +struct rt5036_irq_data { + int irq_gpio; +}; + +struct rt5036_chip; +struct rt5036_platform_data { + struct regulator_init_data *regulator[RT5036_MAX_REGULATOR]; + struct rt5036_chg_data *chg_pdata; + struct rt5036_misc_data *misc_pdata; + struct rt5036_irq_data *irq_pdata; + int (*pre_init)(struct rt5036_chip *rt5036_chip); + int (*post_init)(void); +}; + +struct rt5036_charger_info { + struct i2c_client *i2c; + struct device *dev; + struct power_supply psy; + struct delayed_work dwork; + int chg_volt; + int otg_volt; + int acchg_icc; + int usbtachg_icc; + int usbchg_icc; + int charge_cable; +#ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN + int acdet_gpio; + int usbdet_gpio; + int acdet_irq; + int usbdet_irq; + unsigned char usbinit_delay:1; +#endif /* #ifdef CONFIG_RT_SUPPORT_ACUSB_DUALIN */ + unsigned char online:1; + unsigned char batabs:1; + unsigned char te_en:1; + unsigned char otg_en:1; + unsigned char stat2; +}; + +struct rt5036_misc_info { + struct i2c_client *i2c; + struct device *dev; +#ifdef CONFIG_MISC_RT5036_PWRKEY + struct input_dev *pwr_key; + unsigned char pwr_key_pressed:1; +#endif /* #ifdef CONFIG_MISC_RT5036_PWRKEY */ +}; + +struct rt5036_rtc_info { + struct i2c_client *i2c; + struct device *dev; + struct rtc_device *rtc; +}; + +struct rt5036_chip { + struct i2c_client *i2c; + struct rt5036_charger_info *chg_info; + struct rt5036_misc_info *misc_info; + struct rt5036_rtc_info *rtc_info; + struct mutex io_lock; + unsigned char suspend:1; +}; + +#ifdef CONFIG_CHARGER_RT5036 +void rt5036_charger_irq_handler(struct rt5036_charger_info *ci, + unsigned int event); +#endif /* #ifdef CONFIG_CHARGER_RT5036 */ +#ifdef CONFIG_MISC_RT5036 +void rt5036_misc_irq_handler(struct rt5036_misc_info *mi, unsigned int event); +#endif /* #ifdef CONFIG_MISC_RT5036 */ +#ifdef CONFIG_RTC_RT5036 +void rt5036_rtc_irq_handler(struct rt5036_rtc_info *ri, unsigned int event); +#endif /* #ifdef CONFIG_RTC_RT5036 */ + +extern int rt5036_reg_block_read(struct i2c_client *i2c, int reg, int byte, + void *dest); +extern int rt5036_reg_block_write(struct i2c_client *i2c, int reg, int byte, + void *dest); +extern int rt5036_reg_read(struct i2c_client *i2c, int reg); +extern int rt5036_reg_write(struct i2c_client *i2c, int reg, + unsigned char data); +extern int rt5036_assign_bits(struct i2c_client *i2c, int reg, + unsigned char mask, unsigned char data); +extern int rt5036_set_bits(struct i2c_client *i2c, int reg, unsigned char mask); +extern int rt5036_clr_bits(struct i2c_client *i2c, int reg, unsigned char mask); + +extern int rt5036_core_init(struct device *dev, + struct rt5036_platform_data *pdata); +extern int rt5036_core_deinit(struct device *dev); + +#ifdef CONFIG_MFD_RT5036_DBGINFO +#define RTINFO(format, args...) \ + pr_info("%s:%s() line-%d: " format, RT5036_DEV_NAME, __func__, \ + __LINE__, ##args) +#else +#define RTINFO(format, args...) +#endif /* CONFIG_MFD_RT5036_DBGINFO */ +#endif /* #ifndef _LINUX_MFD_RT5036_H */ diff --git a/include/linux/power/rt-battery.h b/include/linux/power/rt-battery.h new file mode 100755 index 000000000000..ab0c7da1f876 --- /dev/null +++ b/include/linux/power/rt-battery.h @@ -0,0 +1,19 @@ +/* + * include/linux/power/rt-battery.h + * Include header file for Richtek Richtek Battery Driver + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef __LINUX_RT_BATTERY_H +#define __LINUX_RT_BATTERY_H + +#define RT_BATT_NAME "rt-battery" + +#endif /* #ifndef __LINUX_RT_BATTERY_H */ diff --git a/include/linux/power/rt-power.h b/include/linux/power/rt-power.h old mode 100755 new mode 100644 index 7c5b64af747c..75335480581e --- a/include/linux/power/rt-power.h +++ b/include/linux/power/rt-power.h @@ -1,6 +1,6 @@ /* - * include/linux/power/rt5025/rt-power.h - * Include header file for Richtek RT5025 Core charger Driver + * include/linux/power/rt-power.h + * Include header file for Richtek Richtek Power Driver * * Copyright (C) 2014 Richtek Technology Corp. * cy_huang diff --git a/include/linux/power/rt5036-charger.h b/include/linux/power/rt5036-charger.h new file mode 100755 index 000000000000..6ea60e37377e --- /dev/null +++ b/include/linux/power/rt5036-charger.h @@ -0,0 +1,72 @@ +/* + * include/linux/power/rt5036-charger.h + * Include header file for Richtek RT5036 Charger Driver + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_POWER_RT5036_CHARGER_H +#define _LINUX_POWER_RT5036_CHARGER_H + +enum { + CHGEVENT_STAT2ALT, + CHGEVENT_CHBSTLOWVI = 5, + CHGEVENT_BSTOLI, + CHGEVENT_BSTVIMIDOVP, + CHGEVENT_CHTMRFI = 10, + CHGEVENT_CHRCHGI, + CHGEVENT_CHTERMI, + CHGEVENT_CHBATOVI, + CHGEVENT_CHRVPI = 15, + CHGEVENT_BATABSENSE, + CHGEVENT_CHBADADPI, + CHGEVENT_VINCHGPLUGOUT, + CHGEVENT_VINCHGPLUGIN, + CHGEVENT_PPBATLVI, + CHGEVENT_IEOCI, + CHGEVENT_VINOVPI, + CHGEVENT_MAX, +}; + +#define RT5036_CHGTEEN_MASK 0x08 + +#define RT5036_CHGIPREC_MASK 0x18 +#define RT5036_CHGIPREC_SHIFT 3 + +#define RT5036_CHGIEOC_MASK 0x07 + +#define RT5036_CHGVPREC_MASK 0x0F + +#define RT5036_CHGBATLV_MASK 0x07 + +#define RT5036_CHGVRECHG_MASK 0x0C +#define RT5036_CHGVRECHG_SHIFT 2 + +#define RT5036_CHGSTAT_MASK 0x30 +#define RT5036_CHGSTAT_SHIFT 4 +#define RT5036_CHGDIS_MASK 0x01 +#define RT5036_CHGAICR_MASK 0xE0 +#define RT5036_CHGAICR_SHIFT 5 +#define RT5036_CHGICC_MASK 0xF0 +#define RT5036_CHGICC_SHIFT 4 +#define RT5036_CHGCV_MASK 0xFC +#define RT5036_CHGCV_SHIFT 2 +#define RT5036_CHGOPAMODE_MASK 0x01 +#define RT5036_CHGOPASTAT_MASK 0x08 + +#define RT5036_PWRRDY_MASK 0x80 +#define RT5036_TSEVENT_MASK 0x0F +#define RT5036_TSWC_MASK 0x06 +#define RT5036_TSHC_MASK 0x09 +#define RT5036_CCJEITA_MASK 0x80 +#define RT5036_CHGOTGEN_MASK 0x40 +#define RT5036_BATDEN_MASK 0x80 +#define RT5036_TERST_MASK 0x10 + +#endif /* #ifndef _LINUX_POWER_RT5036_CHARGER_H */ diff --git a/include/linux/regulator/rt5036-regulator.h b/include/linux/regulator/rt5036-regulator.h new file mode 100755 index 000000000000..e6578b745d0d --- /dev/null +++ b/include/linux/regulator/rt5036-regulator.h @@ -0,0 +1,91 @@ +/* + * include/linux/regulator/rt5036-regulator.h + * Include header file to Richtek RT5036 Regulator driver + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef __LINUX_RT5036_REGULATOR_H +#define __LINUX_RT5036_REGULATOR_H + +/*BUCK*/ +#define RT5036_DCDCVOUT_SHIFT1 0 +#define RT5036_DCDCVOUT_MASK1 0x7F +#define RT5036_DCDCVOUT_SHIFT2 0 +#define RT5036_DCDCVOUT_MASK2 0x7F +#define RT5036_DCDCVOUT_SHIFT3 0 +#define RT5036_DCDCVOUT_MASK3 0x7F +#define RT5036_DCDCVOUT_SHIFT4 0 +#define RT5036_DCDCVOUT_MASK4 0x7F +#define RT5036_DCDCEN_MASK1 0x08 +#define RT5036_DCDCEN_MASK2 0x04 +#define RT5036_DCDCEN_MASK3 0x02 +#define RT5036_DCDCEN_MASK4 0x01 +#define RT5036_DCDCMODE_MASK1 0x80 +#define RT5036_DCDCMODE_MASK2 0x40 +#define RT5036_DCDCMODE_MASK3 0x20 +#define RT5036_DCDCMODE_MASK4 0x10 +#define RT5036_DCDCRAMP_MASK1 0xC0 +#define RT5036_DCDCRAMP_SHIFT1 6 +#define RT5036_DCDCRAMP_MASK2 0x30 +#define RT5036_DCDCRAMP_SHIFT2 4 +#define RT5036_DCDCRAMP_MASK3 0x0C +#define RT5036_DCDCRAMP_SHIFT3 2 +#define RT5036_DCDCRAMP_MASK4 0x03 +#define RT5036_DCDCRAMP_SHIFT4 0 + +/*LDO*/ +#define RT5036_LDOVOUT_SHIFT1 0 +#define RT5036_LDOVOUT_MASK1 0x7F +#define RT5036_LDOVOUT_SHIFT2 0 +#define RT5036_LDOVOUT_MASK2 0x7F +#define RT5036_LDOVOUT_SHIFT3 0 +#define RT5036_LDOVOUT_MASK3 0x7F +#define RT5036_LDOVOUT_SHIFT4 0 +#define RT5036_LDOVOUT_MASK4 0x7F +#define RT5036_LDOEN_MASK1 0x80 +#define RT5036_LDOEN_MASK2 0x40 +#define RT5036_LDOEN_MASK3 0x20 +#define RT5036_LDOEN_MASK4 0x10 +#define RT5036_LDOMODE_MASK1 0x80 +#define RT5036_LDOMODE_MASK2 0x40 +#define RT5036_LDOMODE_MASK3 0x20 +#define RT5036_LDOMODE_MASK4 0x10 +#define RT5036_LDORAMP_MASK1 0xC0 +#define RT5036_LDORAMP_SHIFT1 6 +#define RT5036_LDORAMP_MASK2 0x30 +#define RT5036_LDORAMP_SHIFT2 4 +#define RT5036_LDORAMP_MASK3 0x0C +#define RT5036_LDORAMP_SHIFT3 2 +#define RT5036_LDORAMP_MASK4 0x03 +#define RT5036_LDORAMP_SHIFT4 0 + +/*LSW*/ +#define RT5036_LSWVOUT_SHIFT1 0 +#define RT5036_LSWVOUT_MASK1 0x7F +#define RT5036_LSWVOUT_SHIFT2 0 +#define RT5036_LSWVOUT_MASK2 0x7F +#define RT5036_LSWNEN_MASK2 0x02 +#define RT5036_LSWNEN_MASK1 0x01 +#define RT5036_LSWSEN_MASK2 0x08 +#define RT5036_LSWSEN_MASK1 0x04 +#define RT5036_LSWNMODE_MASK2 0x80 +#define RT5036_LSWNMODE_MASK1 0x40 +#define RT5036_LSWSMODE_MASK2 0x08 +#define RT5036_LSWSMODE_MASK1 0x04 +#define RT5036_LSWNRAMP_MASK2 0xC0 +#define RT5036_LSWNRAMP_SHIFT2 6 +#define RT5036_LSWNRAMP_MASK1 0x30 +#define RT5036_LSWNRAMP_SHIFT1 4 +#define RT5036_LSWSRAMP_MASK2 0x0C +#define RT5036_LSWSRAMP_SHIFT2 2 +#define RT5036_LSWSRAMP_MASK1 0x03 +#define RT5036_LSWSRAMP_SHIFT1 0 + +#endif /* __LINUX_RT5036_REGULATOR_H */ diff --git a/include/linux/rtc/rtc-rt5036.h b/include/linux/rtc/rtc-rt5036.h new file mode 100755 index 000000000000..2ddb801b47d0 --- /dev/null +++ b/include/linux/rtc/rtc-rt5036.h @@ -0,0 +1,39 @@ +/* + * include/linux/rtc/rt5036-rtc.h + * Include header file for Richtek RT5036 RTC Driver + * + * Copyright (C) 2014 Richtek Technology Corp. + * cy_huang + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RTC_RT5036_RTC_H +#define _LINUX_RTC_RT5036_RTC_H + +enum { + RTCEVENT_CAIRQ, + RTCEVENT_CDIRQ, + RTCEVENT_MAX, +}; + +#define RT5036_RTC_SECMASK 0x3F +#define RT5036_RTC_MINMASK 0x3F +#define RT5036_RTC_HOURMASK 0x1F +#define RT5036_RTC_YEARMASK 0x7F +#define RT5036_RTC_MONMASK 0x0F +#define RT5036_RTC_DAYMASK 0x1F +#define RT5036_RTC_WEEKMASK 0xE0 +#define RT5036_RTC_WEEKSHIFT 5 + +#define RT5036_STBCTL_MASK 0x01 +#define RT5036_RTCCDEN_MASK 0x02 +#define RT5036_RTCAEN_MASK 0x04 + +#define RT5036_RTCCDIRQ_MASK 0x02 +#define RT5036_RTCAIRQ_MASK 0x01 + +#endif /* #ifndef _LINUX_RTC_RT5036_RTC_H */