From 01afa13ceb6b3e695c1a886b25f97a28c5c8d4d7 Mon Sep 17 00:00:00 2001 From: Jianhong Chen Date: Fri, 6 Jan 2017 16:52:49 +0800 Subject: [PATCH] mfd: rk808: add rk805 support include sub modules: regulator, rtc, gpio, pwrkey Change-Id: I19796e2a94764f95588d4b90bca1f3bc616f4f56 Signed-off-by: Jianhong Chen --- .../devicetree/bindings/mfd/rk805.txt | 196 +++++++++ drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-rk8xx.c | 262 ++++++++++++ drivers/input/misc/Kconfig | 6 + drivers/input/misc/Makefile | 1 + drivers/input/misc/rk8xx-pwrkey.c | 144 +++++++ drivers/mfd/rk808.c | 319 ++++++++++---- drivers/regulator/rk818-regulator.c | 388 ++++++++++++++++-- drivers/rtc/rtc-rk808.c | 13 + include/linux/mfd/rk808.h | 100 +++++ 11 files changed, 1325 insertions(+), 111 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/rk805.txt create mode 100644 drivers/gpio/gpio-rk8xx.c create mode 100644 drivers/input/misc/rk8xx-pwrkey.c diff --git a/Documentation/devicetree/bindings/mfd/rk805.txt b/Documentation/devicetree/bindings/mfd/rk805.txt new file mode 100644 index 000000000000..33ccefb186dc --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/rk805.txt @@ -0,0 +1,196 @@ +RK805 Power Management Integrated Circuit + +Required properties: +- compatible: "rockchip,rk805" +- reg: I2C slave address +- interrupt-parent: The parent interrupt controller. +- interrupts: the interrupt outputs of the controller. + +Optional properties: +- rockchip,system-power-controller: Telling whether or not this pmic is controlling + the system power. +- gpio-controller: Specifies that the node is a gpio controller when you attemp to + use the OUT1 or OUT2 pin of RK805 by GPIO general interface. +- #gpio-cells: Should be two. The first cell is the GPIO number and the second cell + is used to specify the GPIO polarity. +- wakeup-source: Flag to indicate this device can wake system (suspend/resume) +- vcc1-supply: The input supply for RK805_DCDC_REG1 +- vcc2-supply: The input supply for RK805_DCDC_REG2 +- vcc3-supply: The input supply for RK805_DCDC_REG3 +- vcc4-supply: The input supply for RK805_DCDC_REG4 +- vcc5-supply: The input supply for RK805_LDO_REG1, RK805_LDO_REG2 +- vcc6-supply: The input supply for RK805_LDO_REG3 + +Regulators: All the regulators of RK805 to be instantiated shall be +listed in a child node named 'regulators'. Each regulator is represented +by a child node of the 'regulators' node. + + regulator-name { + /* standard regulator bindings here */ + }; + +Following regulators of the RK805 PMIC block are supported. Note that +the 'n' in regulator name, as in RK805_DCDC_REGn or RK805_LDOn, represents the DCDC +or LDO number as described in RK805 datasheet. + + - RK805_DCDC_REGn + - valid values for n are 1 to 4. + - RK805_LDO_REGn + - valid values for n are 1 to 3. + +Standard regulator bindings are used inside regulator subnodes. Check + Documentation/devicetree/bindings/regulator/regulator.txt +for more details + +Gpio, Rtc, Pwrkey: the node are represented like below. When you attemp to enable + the module, setting the "status" to be "okay", otherwise "disabled". + + rtc { + status = "okay"; + }; + + pwrkey { + status = "okay"; + }; + + gpio { + status = "okay"; + }; + +Example: + rk805: pmic@1c { + compatible = "rockchip,rk805"; + status = "okay"; + reg = <0x18>; + interrupt-parent = <&gpio2>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + gpio-controller; + #gpio-cells = <2>; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_sys>; + vcc6-supply = <&vcc_sys>; + + rtc { + status = "okay"; + }; + + pwrkey { + status = "okay"; + }; + + gpio { + status = "okay"; + }; + + regulators { + compatible = "rk805-regulator"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_logic: RK805_DCDC1@0 { + regulator-compatible = "RK805_DCDC1"; + regulator-name = "vdd_logic"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-boot-on; + regulator-always-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + + vdd_arm: RK805_DCDC2@1 { + regulator-compatible = "RK805_DCDC2"; + regulator-name = "vdd_arm"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-boot-on; + regulator-always-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <950000>; + }; + }; + + vcc_ddr: RK805_DCDC3@2 { + regulator-compatible = "RK805_DCDC3"; + regulator-name = "vcc_ddr"; + regulator-initial-mode = <0x1>; + regulator-boot-on; + regulator-always-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + }; + }; + + vcc_io: RK805_DCDC4@3 { + regulator-compatible = "RK805_DCDC4"; + regulator-name = "vcc_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <0x1>; + regulator-boot-on; + regulator-always-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vdd_18: RK805_LDO1@4 { + regulator-compatible = "RK805_LDO1"; + regulator-name = "vdd_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc_18emmc: RK805_LDO2@5 { + regulator-compatible = "RK805_LDO2"; + regulator-name = "vcc_18emmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_10: RK805_LDO3@6 { + regulator-compatible = "RK805_LDO3"; + regulator-name = "vdd_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-boot-on; + regulator-always-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + }; + }; \ No newline at end of file diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 469dc378adeb..e7d1cc2591e8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -815,6 +815,12 @@ config GPIO_RC5T583 This driver provides the support for driving/reading the gpio pins of RC5T583 device through standard gpio library. +config GPIO_RK8XX + tristate "Rockchip RK8XX gpio driver" + depends on MFD_RK808 + help + Select this option to enable the gpio module of Rockchip RK8XX PMIC. + config GPIO_STMPE bool "STMPE GPIOs" depends on MFD_STMPE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 986dbd838cea..1a29d53e92d1 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o +obj-$(CONFIG_GPIO_RK8XX) += gpio-rk8xx.o obj-$(CONFIG_GPIO_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o diff --git a/drivers/gpio/gpio-rk8xx.c b/drivers/gpio/gpio-rk8xx.c new file mode 100644 index 000000000000..e7707e2fd5c9 --- /dev/null +++ b/drivers/gpio/gpio-rk8xx.c @@ -0,0 +1,262 @@ +/* + * drivers/gpio/gpio-rk8xx.c + * Driver for Rockchip RK8xx PMIC GPIO + * + * Copyright (C) 2017, Rockchip Technology Co., Ltd. + * Author: Chen Jianhong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK805_GPIO_REG RK805_OUT_REG +#define RK805_OUT0_VALMASK BIT(0) +#define RK805_OUT1_VALMASK BIT(1) + +struct rk8xx_gpio_reg { + u8 reg; + u8 dir_msk; + u8 val_msk; + u8 fun_msk; +}; + +struct rk8xx_gpio_info { + struct rk808 *rk8xx; + struct gpio_chip gpio_chip; + struct rk8xx_gpio_reg *gpio_reg; + int gpio_nr; +}; + +static int rk8xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + int err; + struct rk8xx_gpio_info *gi = dev_get_drvdata(chip->dev); + + /* iomux */ + if (gi->gpio_reg[offset].fun_msk) { + err = regmap_update_bits(gi->rk8xx->regmap, + gi->gpio_reg[offset].reg, + gi->gpio_reg[offset].fun_msk, + gi->gpio_reg[offset].fun_msk); + if (err) { + dev_err(chip->dev, "set gpio%d func fail: %d\n", + offset, err); + return err; + } + } + + /* direction */ + if (gi->gpio_reg[offset].dir_msk) { + err = regmap_update_bits(gi->rk8xx->regmap, + gi->gpio_reg[offset].reg, + gi->gpio_reg[offset].dir_msk, + 0); + if (err) { + dev_err(chip->dev, "set gpio%d input fail: %d\n", + offset, err); + return err; + } + } + + return 0; +} + +static int rk8xx_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + int err; + struct rk8xx_gpio_info *gi = dev_get_drvdata(chip->dev); + + /* iomux */ + if (gi->gpio_reg[offset].fun_msk) { + err = regmap_update_bits(gi->rk8xx->regmap, + gi->gpio_reg[offset].reg, + gi->gpio_reg[offset].fun_msk, + gi->gpio_reg[offset].fun_msk); + if (err) { + dev_err(chip->dev, "set gpio%d func fail: %d\n", + offset, err); + return err; + } + } + + /* direction */ + if (gi->gpio_reg[offset].dir_msk) { + err = regmap_update_bits(gi->rk8xx->regmap, + gi->gpio_reg[offset].reg, + gi->gpio_reg[offset].dir_msk, + gi->gpio_reg[offset].dir_msk); + if (err) { + dev_err(chip->dev, + "set gpio%d output fail: %d\n", offset, err); + return err; + } + } + + if (value) + err = regmap_update_bits(gi->rk8xx->regmap, + gi->gpio_reg[offset].reg, + gi->gpio_reg[offset].val_msk, + gi->gpio_reg[offset].val_msk); + else + err = regmap_update_bits(gi->rk8xx->regmap, + gi->gpio_reg[offset].reg, + gi->gpio_reg[offset].val_msk, + 0); + if (err) { + dev_err(chip->dev, "set gpio%d value fail: %d\n", offset, err); + return err; + } + + return 0; +} + +static int rk8xx_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + int err; + unsigned int val; + struct rk8xx_gpio_info *gi = dev_get_drvdata(chip->dev); + + err = regmap_read(gi->rk8xx->regmap, gi->gpio_reg[offset].reg, &val); + if (err) { + dev_err(chip->dev, "get gpio%d value fail: %d\n", offset, err); + return err; + } + + return (val & gi->gpio_reg[offset].val_msk) ? 1 : 0; +} + +static void rk8xx_gpio_set_value(struct gpio_chip *chip, + unsigned offset, int value) +{ + int err; + struct rk8xx_gpio_info *gi = dev_get_drvdata(chip->dev); + + if (value) + err = regmap_update_bits(gi->rk8xx->regmap, + gi->gpio_reg[offset].reg, + gi->gpio_reg[offset].val_msk, + gi->gpio_reg[offset].val_msk); + else + err = regmap_update_bits(gi->rk8xx->regmap, + gi->gpio_reg[offset].reg, + gi->gpio_reg[offset].val_msk, + 0); + if (err) + dev_err(chip->dev, "set gpio%d value fail: %d\n", offset, err); +} + +/* rk805: two gpio: output only */ +static struct rk8xx_gpio_reg rk805_gpio_reg[] = { + { + .reg = RK805_GPIO_REG, + .val_msk = RK805_OUT0_VALMASK, + }, + { + .reg = RK805_GPIO_REG, + .val_msk = RK805_OUT1_VALMASK, + }, +}; + +static int rk8xx_gpio_probe(struct platform_device *pdev) +{ + struct rk808 *rk8xx = dev_get_drvdata(pdev->dev.parent); + struct rk8xx_gpio_info *gi; + struct device_node *np; + int ret; + + np = of_find_node_by_name(pdev->dev.parent->of_node, "gpio"); + if (np) { + if (!of_device_is_available(np)) { + dev_info(&pdev->dev, "device is disabled\n"); + return -EINVAL; + } + } + + gi = devm_kzalloc(&pdev->dev, sizeof(*gi), GFP_KERNEL); + if (!gi) + return -ENOMEM; + + switch (rk8xx->variant) { + case RK805_ID: + gi->gpio_reg = rk805_gpio_reg; + gi->gpio_nr = ARRAY_SIZE(rk805_gpio_reg); + break; + default: + dev_err(&pdev->dev, "unsupported RK8XX ID %lu\n", + rk8xx->variant); + return -EINVAL; + } + + gi->rk8xx = rk8xx; + gi->gpio_chip.base = -1; + gi->gpio_chip.can_sleep = true; + gi->gpio_chip.dev = &pdev->dev; + gi->gpio_chip.ngpio = gi->gpio_nr; + gi->gpio_chip.label = pdev->name; + gi->gpio_chip.get = rk8xx_gpio_get_value; + gi->gpio_chip.set = rk8xx_gpio_set_value; + gi->gpio_chip.direction_input = rk8xx_gpio_direction_input; + gi->gpio_chip.direction_output = rk8xx_gpio_direction_output; + gi->gpio_chip.owner = THIS_MODULE; +#ifdef CONFIG_OF_GPIO + gi->gpio_chip.of_node = rk8xx->i2c->dev.of_node; +#endif + platform_set_drvdata(pdev, gi); + + ret = gpiochip_add(&gi->gpio_chip); + if (ret) + dev_err(&pdev->dev, "register rk8xx gpiochip fail: %d\n", ret); + + return ret; +} + +static int rk8xx_gpio_remove(struct platform_device *pdev) +{ + struct rk8xx_gpio_info *gi = platform_get_drvdata(pdev); + + gpiochip_remove(&gi->gpio_chip); + + return 0; +} + +static struct platform_driver rk8xx_gpio_driver = { + .probe = rk8xx_gpio_probe, + .remove = rk8xx_gpio_remove, + .driver = { + .name = "rk8xx-gpio", + .owner = THIS_MODULE, + }, +}; + +static int rk8xx_gpio_init(void) +{ + return platform_driver_register(&rk8xx_gpio_driver); +} +subsys_initcall(rk8xx_gpio_init); + +static void rk8xx_gpio_exit(void) +{ + platform_driver_unregister(&rk8xx_gpio_driver); +} +module_exit(rk8xx_gpio_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("RK8xx GPIO driver"); +MODULE_AUTHOR("Chen Jianhong "); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 25ac47b9a180..0fd4e48f6450 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -449,6 +449,12 @@ config INPUT_RETU_PWRBUTTON To compile this driver as a module, choose M here. The module will be called retu-pwrbutton. +config INPUT_RK8XX_PWRKEY + tristate "Rockchip RK8XX pwrkey driver" + depends on MFD_RK808 + help + Select this option to enable the pwrkey module of Rockchip RK8XX PMIC. + config INPUT_TPS65218_PWRBUTTON tristate "TPS65218 Power button driver" depends on MFD_TPS65218 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 66c3cc9f181c..02cc87f3ca4c 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o +obj-$(CONFIG_INPUT_RK8XX_PWRKEY) += rk8xx-pwrkey.o obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o diff --git a/drivers/input/misc/rk8xx-pwrkey.c b/drivers/input/misc/rk8xx-pwrkey.c new file mode 100644 index 000000000000..6901c40e4b2c --- /dev/null +++ b/drivers/input/misc/rk8xx-pwrkey.c @@ -0,0 +1,144 @@ +/* + * driver/input/misc/rk8xx-pwrkey.c + * Power Key driver for RK8xx PMIC Power Button. + * + * Copyright (C) 2017, Rockchip Technology Co., Ltd. + * Author: Chen Jianhong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rk8xx_pwrkey { + struct rk808 *rk8xx; + struct input_dev *input_dev; + int report_key; +}; + +static irqreturn_t rk8xx_pwrkey_irq_falling(int irq, void *data) +{ + struct rk8xx_pwrkey *pwr = data; + + input_report_key(pwr->input_dev, pwr->report_key, 1); + input_sync(pwr->input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t rk8xx_pwrkey_irq_rising(int irq, void *data) +{ + struct rk8xx_pwrkey *pwr = data; + + input_report_key(pwr->input_dev, pwr->report_key, 0); + input_sync(pwr->input_dev); + + return IRQ_HANDLED; +} + +static int rk8xx_pwrkey_probe(struct platform_device *pdev) +{ + struct rk808 *rk8xx = dev_get_drvdata(pdev->dev.parent); + struct rk8xx_pwrkey *pwrkey; + int fall_irq, rise_irq, err; + struct device_node *np; + + np = of_find_node_by_name(pdev->dev.parent->of_node, "pwrkey"); + if (np) { + if (!of_device_is_available(np)) { + dev_info(&pdev->dev, "device is disabled\n"); + return -EINVAL; + } + } + + pwrkey = devm_kzalloc(&pdev->dev, + sizeof(struct rk8xx_pwrkey), GFP_KERNEL); + if (!pwrkey) + return -ENOMEM; + + pwrkey->input_dev = devm_input_allocate_device(&pdev->dev); + if (!pwrkey->input_dev) { + dev_err(&pdev->dev, "Can't allocate power button\n"); + return -ENOMEM; + } + + /* init struct input_dev */ + pwrkey->rk8xx = rk8xx; + pwrkey->report_key = KEY_POWER; + pwrkey->input_dev->name = "rk8xx_pwrkey"; + pwrkey->input_dev->phys = "rk8xx_pwrkey/input0"; + pwrkey->input_dev->dev.parent = pdev->dev.parent; + pwrkey->input_dev->evbit[0] = BIT_MASK(EV_KEY); + pwrkey->input_dev->keybit[BIT_WORD(pwrkey->report_key)] = + BIT_MASK(pwrkey->report_key); + platform_set_drvdata(pdev, pwrkey); + + /* requeset rise and fall irqs */ + rise_irq = platform_get_irq(pdev, 0); + if (rise_irq < 0) { + dev_err(&pdev->dev, "no IRQ for rise: %d\n", rise_irq); + return rise_irq; + } + + fall_irq = platform_get_irq(pdev, 1); + if (fall_irq < 0) { + dev_err(&pdev->dev, "no IRQ for fall: %d\n", fall_irq); + return fall_irq; + } + + err = devm_request_threaded_irq(&pdev->dev, fall_irq, + NULL, rk8xx_pwrkey_irq_falling, + IRQF_TRIGGER_FALLING, + "rk8xx_pwrkey_fall", pwrkey); + if (err) { + dev_err(&pdev->dev, "Can't get fall irq for pwrkey: %d\n", err); + return err; + } + err = devm_request_threaded_irq(&pdev->dev, rise_irq, + NULL, rk8xx_pwrkey_irq_rising, + IRQF_TRIGGER_RISING, + "rk8xx_pwrkey_rise", pwrkey); + if (err) { + dev_err(&pdev->dev, "Can't get rise irq for pwrkey: %d\n", err); + return err; + } + + /* register input device */ + err = input_register_device(pwrkey->input_dev); + if (err) { + dev_err(&pdev->dev, "Can't register power button: %d\n", err); + return err; + } + + return 0; +} + +static struct platform_driver rk8xx_pwrkey_driver = { + .probe = rk8xx_pwrkey_probe, + .driver = { + .name = "rk8xx-pwrkey", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(rk8xx_pwrkey_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("RK8xx Power Button"); +MODULE_AUTHOR("Chen Jianhong "); diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 0db26cd7876e..9d9327828ca5 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -18,11 +18,12 @@ #include #include +#include #include #include #include -#include #include +#include struct rk808_reg_data { int addr; @@ -30,17 +31,6 @@ struct rk808_reg_data { int value; }; -struct rk8xx_power_data { - char *name; - const struct rk808_reg_data *rk8xx_pre_init_reg; - int reg_num; - const struct regmap_config *rk8xx_regmap_config; - const struct mfd_cell *rk8xx_cell; - int cell_num; - struct regmap_irq_chip *rk8xx_irq_chip; - int (*pm_shutdown)(struct regmap *regmap); -}; - static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg) { /* @@ -89,6 +79,25 @@ static int rk818_shutdown(struct regmap *regmap) return ret; } +static int rk805_shutdown_prepare(struct regmap *regmap) +{ + int ret; + + /* close rtc int when power off */ + regmap_update_bits(regmap, + RK808_INT_STS_MSK_REG1, + (0x3 << 5), (0x3 << 5)); + regmap_update_bits(regmap, + RK808_RTC_INT_REG, + (0x3 << 2), (0x0 << 2)); + + /* pmic sleep shutdown function */ + ret = regmap_update_bits(regmap, + RK805_GPIO_IO_POL_REG, + SLP_SD_MSK, SHUTDOWN_FUN); + return ret; +} + static bool rk818_is_volatile_reg(struct device *dev, unsigned int reg) { /* @@ -129,6 +138,14 @@ static const struct regmap_config rk808_regmap_config = { .volatile_reg = rk808_is_volatile_reg, }; +static const struct regmap_config rk805_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RK805_OFF_SOURCE_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = rk808_is_volatile_reg, +}; + static struct resource rtc_resources[] = { { .start = RK808_IRQ_RTC_ALARM, @@ -137,6 +154,19 @@ static struct resource rtc_resources[] = { } }; +static struct resource pwrkey_resources[] = { + { + .start = RK805_IRQ_PWRON_RISE, + .end = RK805_IRQ_PWRON_RISE, + .flags = IORESOURCE_IRQ, + }, + { + .start = RK805_IRQ_PWRON_FALL, + .end = RK805_IRQ_PWRON_FALL, + .flags = IORESOURCE_IRQ, + }, +}; + static const struct mfd_cell rk808s[] = { { .name = "rk808-clkout", }, { .name = "rk808-regulator", }, @@ -147,7 +177,7 @@ static const struct mfd_cell rk808s[] = { }, }; -static const struct rk808_reg_data pre_init_reg[] = { +static const struct rk808_reg_data rk808_pre_init_reg[] = { { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA }, { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA }, { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, @@ -322,31 +352,96 @@ static struct regmap_irq_chip rk818_irq_chip = { .init_ack_masked = true, }; -static struct rk8xx_power_data rk808_power_data = { - .name = "rk808", - .rk8xx_pre_init_reg = pre_init_reg, - .reg_num = ARRAY_SIZE(pre_init_reg), - .rk8xx_regmap_config = &rk808_regmap_config, - .rk8xx_cell = rk808s, - .cell_num = ARRAY_SIZE(rk808s), - .rk8xx_irq_chip = &rk808_irq_chip, - .pm_shutdown = rk808_shutdown, +static const struct regmap_irq rk805_irqs[] = { + [RK805_IRQ_PWRON_RISE] = { + .mask = RK805_IRQ_PWRON_RISE_MSK, + .reg_offset = 0, + }, + [RK805_IRQ_VB_LOW] = { + .mask = RK805_IRQ_VB_LOW_MSK, + .reg_offset = 0, + }, + [RK805_IRQ_PWRON] = { + .mask = RK805_IRQ_PWRON_MSK, + .reg_offset = 0, + }, + [RK805_IRQ_PWRON_LP] = { + .mask = RK805_IRQ_PWRON_LP_MSK, + .reg_offset = 0, + }, + [RK805_IRQ_HOTDIE] = { + .mask = RK805_IRQ_HOTDIE_MSK, + .reg_offset = 0, + }, + [RK805_IRQ_RTC_ALARM] = { + .mask = RK805_IRQ_RTC_ALARM_MSK, + .reg_offset = 0, + }, + [RK805_IRQ_RTC_PERIOD] = { + .mask = RK805_IRQ_RTC_PERIOD_MSK, + .reg_offset = 0, + }, + [RK805_IRQ_PWRON_FALL] = { + .mask = RK805_IRQ_PWRON_FALL_MSK, + .reg_offset = 0, + }, }; -static struct rk8xx_power_data rk818_power_data = { - .name = "rk818", - .rk8xx_pre_init_reg = rk818_pre_init_reg, - .reg_num = ARRAY_SIZE(rk818_pre_init_reg), - .rk8xx_regmap_config = &rk818_regmap_config, - .rk8xx_cell = rk818s, - .cell_num = ARRAY_SIZE(rk818s), - .rk8xx_irq_chip = &rk818_irq_chip, - .pm_shutdown = rk818_shutdown, +static struct regmap_irq_chip rk805_irq_chip = { + .name = "rk805", + .irqs = rk805_irqs, + .num_irqs = ARRAY_SIZE(rk805_irqs), + .num_regs = 1, + .status_base = RK805_INT_STS_REG, + .mask_base = RK805_INT_STS_MSK_REG, + .ack_base = RK805_INT_STS_REG, + .init_ack_masked = true, +}; + +static const struct mfd_cell rk805s[] = { + { .name = "rk818-regulator", }, + { .name = "rk8xx-gpio", }, + { + .name = "rk8xx-pwrkey", + .num_resources = ARRAY_SIZE(pwrkey_resources), + .resources = &pwrkey_resources[0], + }, + { + .name = "rk808-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), + .resources = &rtc_resources[0], + }, +}; + +static const struct rk808_reg_data rk805_pre_init_reg[] = { + {RK805_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_400MA}, + {RK805_GPIO_IO_POL_REG, SLP_SD_MSK, SLEEP_FUN}, + {RK805_THERMAL_REG, TEMP_HOTDIE_MSK, TEMP115C}, }; static int (*pm_shutdown)(struct regmap *regmap); +static int (*pm_shutdown_prepare)(struct regmap *regmap); static struct i2c_client *rk808_i2c_client; +static void rk808_device_shutdown_prepare(void) +{ + int ret; + struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); + + if (!rk808) { + dev_warn(&rk808_i2c_client->dev, + "have no rk808, so do nothing here\n"); + return; + } + + if (pm_shutdown_prepare) { + ret = pm_shutdown_prepare(rk808->regmap); + if (ret) + dev_err(&rk808_i2c_client->dev, + "power off prepare error!\n"); + } +} + static void rk808_device_shutdown(void) { int ret; @@ -365,42 +460,37 @@ static void rk808_device_shutdown(void) regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG, (0x3 << 2), (0x0 << 2)); - - ret = pm_shutdown(rk808->regmap); - if (ret) - dev_err(&rk808_i2c_client->dev, "power off error!\n"); + if (pm_shutdown) { + ret = pm_shutdown(rk808->regmap); + if (ret) + dev_err(&rk808_i2c_client->dev, "power off error!\n"); + } } static const struct of_device_id rk808_of_match[] = { - { - .compatible = "rockchip,rk808", - .data = &rk808_power_data, - }, - { - .compatible = "rockchip,rk818", - .data = &rk818_power_data, - }, + { .compatible = "rockchip,rk805" }, + { .compatible = "rockchip,rk808" }, + { .compatible = "rockchip,rk818" }, { }, }; + MODULE_DEVICE_TABLE(of, rk808_of_match); static int rk808_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct of_device_id *of_id = - of_match_device(rk808_of_match, &client->dev); - const struct rk8xx_power_data *pdata = of_id->data; struct device_node *np = client->dev.of_node; struct rk808 *rk808; - int pm_off = 0; - int ret; - int i; - int on_source, off_source; - - if (!of_id) { - dev_err(&client->dev, "Failed to find matching dt id\n"); - return -ENODEV; - } + int (*pm_shutdown_fn)(struct regmap *regmap) = NULL; + int (*pm_shutdown_prepare_fn)(struct regmap *regmap) = NULL; + const struct rk808_reg_data *pre_init_reg; + const struct regmap_config *regmap_config; + const struct regmap_irq_chip *irq_chip; + const struct mfd_cell *cell; + u8 on_source = 0, off_source = 0; + int msb, lsb, reg_num, cell_num; + int ret, i, pm_off = 0; + unsigned int on, off; if (!client->irq) { dev_err(&client->dev, "No interrupt support, no core IRQ\n"); @@ -411,46 +501,103 @@ static int rk808_probe(struct i2c_client *client, if (!rk808) return -ENOMEM; - rk808->regmap = devm_regmap_init_i2c(client, - pdata->rk8xx_regmap_config); + /* read Chip variant */ + msb = i2c_smbus_read_byte_data(client, RK808_ID_MSB); + if (msb < 0) { + dev_err(&client->dev, "failed to read the chip id at 0x%x\n", + RK808_ID_MSB); + return msb; + } + + lsb = i2c_smbus_read_byte_data(client, RK808_ID_LSB); + if (lsb < 0) { + dev_err(&client->dev, "failed to read the chip id at 0x%x\n", + RK808_ID_LSB); + return lsb; + } + + rk808->variant = ((msb << 8) | lsb) & RK8XX_ID_MSK; + dev_info(&client->dev, "Pmic Chip id: 0x%lx\n", rk808->variant); + + /* set Chip platform init data*/ + switch (rk808->variant) { + case RK818_ID: + cell = rk818s; + cell_num = ARRAY_SIZE(rk818s); + pre_init_reg = rk818_pre_init_reg; + reg_num = ARRAY_SIZE(rk818_pre_init_reg); + regmap_config = &rk818_regmap_config; + irq_chip = &rk818_irq_chip; + pm_shutdown_fn = rk818_shutdown; + on_source = RK818_ON_SOURCE_REG; + off_source = RK818_OFF_SOURCE_REG; + break; + case RK808_ID: + cell = rk808s; + cell_num = ARRAY_SIZE(rk808s); + pre_init_reg = rk808_pre_init_reg; + reg_num = ARRAY_SIZE(rk808_pre_init_reg); + regmap_config = &rk808_regmap_config; + irq_chip = &rk808_irq_chip; + pm_shutdown_fn = rk808_shutdown; + break; + case RK805_ID: + cell = rk805s; + cell_num = ARRAY_SIZE(rk805s); + pre_init_reg = rk805_pre_init_reg; + reg_num = ARRAY_SIZE(rk805_pre_init_reg); + regmap_config = &rk805_regmap_config; + irq_chip = &rk805_irq_chip; + pm_shutdown_prepare_fn = rk805_shutdown_prepare; + on_source = RK805_ON_SOURCE_REG; + off_source = RK805_OFF_SOURCE_REG; + break; + default: + dev_err(&client->dev, "unsupported RK8XX ID 0x%lx\n", + rk808->variant); + return -EINVAL; + } + + rk808->regmap = devm_regmap_init_i2c(client, regmap_config); if (IS_ERR(rk808->regmap)) { dev_err(&client->dev, "regmap initialization failed\n"); return PTR_ERR(rk808->regmap); } - pm_shutdown = pdata->pm_shutdown; - if (!pm_shutdown) { - dev_err(&client->dev, "shutdown initialization failed\n"); - return -EINVAL; - } + /* on & off source */ + if (on_source && off_source) { + ret = regmap_read(rk808->regmap, on_source, &on); + if (ret) { + dev_err(&client->dev, "read 0x%x failed\n", on_source); + return ret; + } - if (strcmp(pdata->name, "rk818") == 0) { - ret = regmap_read(rk808->regmap, RK818_ON_SOURCE_REG, &on_source); - if (ret) - dev_err(&client->dev, "read reg:0x%x failed\n", RK818_ON_SOURCE_REG); - ret = regmap_read(rk808->regmap, RK818_OFF_SOURCE_REG, &off_source); - if (ret) - dev_err(&client->dev, "read reg:0x%x failed\n", RK818_OFF_SOURCE_REG); - dev_info(&client->dev, "ON_SOURCE:0x%02x OFF_SOURCE:0x%02x\n", - on_source, off_source); + ret = regmap_read(rk808->regmap, off_source, &off); + if (ret) { + dev_err(&client->dev, "read 0x%x failed\n", off_source); + return ret; + } + + dev_info(&client->dev, "source: on=0x%02x, off=0x%02x\n", + on, off); } - for (i = 0; i < pdata->reg_num; i++) { + for (i = 0; i < reg_num; i++) { ret = regmap_update_bits(rk808->regmap, - pdata->rk8xx_pre_init_reg[i].addr, - pdata->rk8xx_pre_init_reg[i].mask, - pdata->rk8xx_pre_init_reg[i].value); + pre_init_reg[i].addr, + pre_init_reg[i].mask, + pre_init_reg[i].value); if (ret) { dev_err(&client->dev, "0x%x write err\n", - pdata->rk8xx_pre_init_reg[i].addr); + pre_init_reg[i].addr); return ret; } } ret = regmap_add_irq_chip(rk808->regmap, client->irq, IRQF_ONESHOT, -1, - pdata->rk8xx_irq_chip, &rk808->irq_data); + irq_chip, &rk808->irq_data); if (ret) { dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); return ret; @@ -460,7 +607,7 @@ static int rk808_probe(struct i2c_client *client, i2c_set_clientdata(client, rk808); ret = mfd_add_devices(&client->dev, -1, - pdata->rk8xx_cell, pdata->cell_num, + cell, cell_num, NULL, 0, regmap_irq_get_domain(rk808->irq_data)); if (ret) { dev_err(&client->dev, "failed to add MFD devices %d\n", ret); @@ -471,7 +618,14 @@ static int rk808_probe(struct i2c_client *client, "rockchip,system-power-controller"); if (pm_off) { rk808_i2c_client = client; - pm_power_off = rk808_device_shutdown; + if (pm_shutdown_prepare_fn) { + pm_shutdown_prepare = pm_shutdown_prepare_fn; + pm_power_off_prepare = rk808_device_shutdown_prepare; + } + if (pm_shutdown_fn) { + pm_shutdown = pm_shutdown_fn; + pm_power_off = rk808_device_shutdown; + } } return 0; @@ -487,12 +641,16 @@ static int rk808_remove(struct i2c_client *client) regmap_del_irq_chip(client->irq, rk808->irq_data); mfd_remove_devices(&client->dev); - pm_power_off = NULL; + if (pm_power_off == rk808_device_shutdown) + pm_power_off = NULL; + if (pm_power_off_prepare == rk808_device_shutdown_prepare) + pm_power_off_prepare = NULL; return 0; } static const struct i2c_device_id rk808_ids[] = { + { "rk805" }, { "rk808" }, { "rk818" }, { }, @@ -514,4 +672,5 @@ module_i2c_driver(rk808_i2c_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chris Zhong "); MODULE_AUTHOR("Zhang Qing "); +MODULE_AUTHOR("Chen jianhong "); MODULE_DESCRIPTION("RK808 PMIC driver"); diff --git a/drivers/regulator/rk818-regulator.c b/drivers/regulator/rk818-regulator.c index 6fe92365498a..e7ffb2e643bb 100644 --- a/drivers/regulator/rk818-regulator.c +++ b/drivers/regulator/rk818-regulator.c @@ -41,12 +41,22 @@ #define RK818_RAMP_RATE_6MV_PER_US (2 << RK818_RAMP_RATE_OFFSET) #define RK818_RAMP_RATE_10MV_PER_US (3 << RK818_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_OFFSET 3 +#define RK805_RAMP_RATE_MASK (3 << RK805_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_3MV_PER_US (0 << RK805_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_6MV_PER_US (1 << RK805_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_12_5MV_PER_US (2 << RK805_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_25MV_PER_US (3 << RK805_RAMP_RATE_OFFSET) + /* Offset from XXX_ON_VSEL to XXX_SLP_VSEL */ #define RK818_SLP_REG_OFFSET 1 /* Offset from XXX_EN_REG to SLEEP_SET_OFF_XXX */ #define RK818_SLP_SET_OFF_REG_OFFSET 2 +#define RK805_SLP_LDO_EN_OFFSET -1 +#define RK805_SLP_DCDC_EN_OFFSET 2 + /* max steps for increase voltage of Buck1/2, equal 100mv*/ #define MAX_STEPS_ONE_TIME 8 @@ -57,6 +67,26 @@ static const int rk818_buck_config_regs[] = { RK818_BUCK4_CONFIG_REG, }; +/* rk805 */ +#define ENABLE_MASK(id) (BIT(id) | BIT(4 + (id))) +#define DISABLE_VAL(id) (BIT(4 + (id))) + +static const struct regulator_linear_range rk805_buck_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(712500, 0, 59, 12500), /* 0.7125v - 1.45v */ + REGULATOR_LINEAR_RANGE(1800000, 60, 62, 200000),/* 1.8v - 2.2v */ + REGULATOR_LINEAR_RANGE(2300000, 63, 63, 0), /* 2.3v - 2.3v */ +}; + +static const struct regulator_linear_range rk805_buck4_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(800000, 0, 26, 100000), /* 0.8v - 3.4 */ + REGULATOR_LINEAR_RANGE(3500000, 27, 31, 0), /* 3.5v */ +}; + +static const struct regulator_linear_range rk805_ldo_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(800000, 0, 26, 100000), /* 0.8v - 3.4 */ +}; + +/* rk818 */ static const struct regulator_linear_range rk818_buck_voltage_ranges[] = { REGULATOR_LINEAR_RANGE(712500, 0, 63, 12500), }; @@ -80,25 +110,53 @@ static const struct regulator_linear_range rk818_ldo6_voltage_ranges[] = { static int rk818_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) { + struct rk808 *rk818 = rdev->reg_data; unsigned int ramp_value = RK818_RAMP_RATE_10MV_PER_US; unsigned int reg = rk818_buck_config_regs[rdev->desc->id - RK818_ID_DCDC1]; - switch (ramp_delay) { - case 1 ... 2000: - ramp_value = RK818_RAMP_RATE_2MV_PER_US; - break; - case 2001 ... 4000: - ramp_value = RK818_RAMP_RATE_4MV_PER_US; - break; - case 4001 ... 6000: - ramp_value = RK818_RAMP_RATE_6MV_PER_US; + switch (rk818->variant) { + case RK818_ID: + switch (ramp_delay) { + case 1 ... 2000: + ramp_value = RK818_RAMP_RATE_2MV_PER_US; + break; + case 2001 ... 4000: + ramp_value = RK818_RAMP_RATE_4MV_PER_US; + break; + case 4001 ... 6000: + ramp_value = RK818_RAMP_RATE_6MV_PER_US; + break; + case 6001 ... 10000: + break; + default: + pr_warn("%s ramp_delay: %d not supported, set 10000\n", + rdev->desc->name, ramp_delay); + } break; - case 6001 ... 10000: + case RK805_ID: + switch (ramp_delay) { + case 3000: + ramp_value = RK805_RAMP_RATE_3MV_PER_US; + break; + case 6000: + ramp_value = RK805_RAMP_RATE_6MV_PER_US; + break; + case 12500: + ramp_value = RK805_RAMP_RATE_12_5MV_PER_US; + break; + case 25000: + ramp_value = RK805_RAMP_RATE_25MV_PER_US; + break; + default: + pr_warn("%s ramp_delay: %d not supported\n", + rdev->desc->name, ramp_delay); + } break; default: - pr_warn("%s ramp_delay: %d not supported, setting 10000\n", - rdev->desc->name, ramp_delay); + dev_err(&rdev->dev, "%s: unsupported RK8XX ID %lu\n", + __func__, rk818->variant); + return -EINVAL; } return regmap_update_bits(rdev->regmap, reg, @@ -122,24 +180,124 @@ static int rk818_set_suspend_voltage(struct regulator_dev *rdev, int uv) static int rk818_set_suspend_enable(struct regulator_dev *rdev) { - unsigned int reg; + unsigned int reg, enable_val; + int offset = 0; + struct rk808 *rk818 = rdev->reg_data; + + switch (rk818->variant) { + case RK818_ID: + offset = RK818_SLP_SET_OFF_REG_OFFSET; + enable_val = 0; + break; + case RK805_ID: + if (rdev->desc->id >= RK805_ID_LDO1) + offset = RK805_SLP_LDO_EN_OFFSET; + else + offset = RK805_SLP_DCDC_EN_OFFSET; + enable_val = rdev->desc->enable_mask; + break; + default: + dev_err(&rdev->dev, "not define sleep en reg offset!!\n"); + return -EINVAL; + } - reg = rdev->desc->enable_reg + RK818_SLP_SET_OFF_REG_OFFSET; + reg = rdev->desc->enable_reg + offset; return regmap_update_bits(rdev->regmap, reg, rdev->desc->enable_mask, - 0); + enable_val); } static int rk818_set_suspend_disable(struct regulator_dev *rdev) { - unsigned int reg; + int offset = 0; + unsigned int reg, disable_val; + struct rk808 *rk818 = rdev->reg_data; + + switch (rk818->variant) { + case RK818_ID: + offset = RK818_SLP_SET_OFF_REG_OFFSET; + disable_val = rdev->desc->enable_mask; + break; + case RK805_ID: + if (rdev->desc->id >= RK805_ID_LDO1) + offset = RK805_SLP_LDO_EN_OFFSET; + else + offset = RK805_SLP_DCDC_EN_OFFSET; + disable_val = 0; + break; + default: + dev_err(&rdev->dev, "not define sleep en reg offset!!\n"); + return -EINVAL; + } - reg = rdev->desc->enable_reg + RK818_SLP_SET_OFF_REG_OFFSET; + reg = rdev->desc->enable_reg + offset; return regmap_update_bits(rdev->regmap, reg, rdev->desc->enable_mask, - rdev->desc->enable_mask); + disable_val); +} + +static int rk818_set_suspend_mode(struct regulator_dev *rdev, unsigned int mode) +{ + unsigned int reg; + + reg = rdev->desc->vsel_reg + RK818_SLP_REG_OFFSET; + + switch (mode) { + case REGULATOR_MODE_FAST: + return regmap_update_bits(rdev->regmap, reg, + FPWM_MODE, FPWM_MODE); + case REGULATOR_MODE_NORMAL: + return regmap_update_bits(rdev->regmap, reg, FPWM_MODE, 0); + default: + pr_err("do not support this mode\n"); + return -EINVAL; + } + + return 0; +} + +static int rk818_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + switch (mode) { + case REGULATOR_MODE_FAST: + return regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg, + FPWM_MODE, FPWM_MODE); + case REGULATOR_MODE_NORMAL: + return regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg, + FPWM_MODE, 0); + default: + pr_err("do not support this mode\n"); + return -EINVAL; + } + + return 0; +} + +static unsigned int rk818_get_mode(struct regulator_dev *rdev) +{ + unsigned int val; + int err; + + err = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val); + if (err) + return err; + + if (val & FPWM_MODE) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static unsigned int rk818_regulator_of_map_mode(unsigned int mode) +{ + if (mode == 1) + return REGULATOR_MODE_FAST; + if (mode == 2) + return REGULATOR_MODE_NORMAL; + + return -EINVAL; } static struct regulator_ops rk818_buck1_2_ops = { @@ -151,7 +309,10 @@ static struct regulator_ops rk818_buck1_2_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, + .set_mode = rk818_set_mode, + .get_mode = rk818_get_mode, .set_ramp_delay = rk818_set_ramp_delay, + .set_suspend_mode = rk818_set_suspend_mode, .set_suspend_voltage = rk818_set_suspend_voltage, .set_suspend_enable = rk818_set_suspend_enable, .set_suspend_disable = rk818_set_suspend_disable, @@ -164,7 +325,10 @@ static struct regulator_ops rk818_reg_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, + .set_mode = rk818_set_mode, + .get_mode = rk818_get_mode, .is_enabled = regulator_is_enabled_regmap, + .set_suspend_mode = rk818_set_suspend_mode, .set_suspend_voltage = rk818_set_suspend_voltage, .set_suspend_enable = rk818_set_suspend_enable, .set_suspend_disable = rk818_set_suspend_disable, @@ -176,9 +340,12 @@ static struct regulator_ops rk818_switch_ops = { .is_enabled = regulator_is_enabled_regmap, .set_suspend_enable = rk818_set_suspend_enable, .set_suspend_disable = rk818_set_suspend_disable, + .set_mode = rk818_set_mode, + .get_mode = rk818_get_mode, + .set_suspend_mode = rk818_set_suspend_mode, }; -static const struct regulator_desc rk818_reg[] = { +static const struct regulator_desc rk818_desc[] = { { .name = "DCDC_REG1", .supply_name = "vcc1", @@ -395,9 +562,145 @@ static struct of_regulator_match rk818_reg_matches[] = { [RK818_ID_SWITCH] = { .name = "SWITCH_REG" }, }; +static const struct regulator_desc rk805_desc[] = { + { + .name = "DCDC_REG1", + .supply_name = "vcc1", + .id = RK805_ID_DCDC1, + .ops = &rk818_buck1_2_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 64, + .linear_ranges = rk805_buck_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk805_buck_voltage_ranges), + .vsel_reg = RK805_BUCK1_ON_VSEL_REG, + .vsel_mask = RK818_BUCK_VSEL_MASK, + .enable_reg = RK805_DCDC_EN_REG, + .enable_mask = ENABLE_MASK(0), + .disable_val = DISABLE_VAL(0), + .of_map_mode = rk818_regulator_of_map_mode, + .owner = THIS_MODULE, + }, { + .name = "DCDC_REG2", + .supply_name = "vcc2", + .id = RK805_ID_DCDC2, + .ops = &rk818_buck1_2_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 64, + .linear_ranges = rk805_buck_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk805_buck_voltage_ranges), + .vsel_reg = RK805_BUCK2_ON_VSEL_REG, + .vsel_mask = RK818_BUCK_VSEL_MASK, + .enable_reg = RK805_DCDC_EN_REG, + .enable_mask = ENABLE_MASK(1), + .disable_val = DISABLE_VAL(1), + .of_map_mode = rk818_regulator_of_map_mode, + .owner = THIS_MODULE, + }, { + .name = "DCDC_REG3", + .supply_name = "vcc3", + .id = RK805_ID_DCDC3, + .ops = &rk818_switch_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 1, + .enable_reg = RK805_DCDC_EN_REG, + .enable_mask = ENABLE_MASK(2), + .disable_val = DISABLE_VAL(2), + .of_map_mode = rk818_regulator_of_map_mode, + .owner = THIS_MODULE, + }, { + .name = "DCDC_REG4", + .supply_name = "vcc4", + .id = RK805_ID_DCDC4, + .ops = &rk818_reg_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 32, + .linear_ranges = rk805_buck4_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk805_buck4_voltage_ranges), + .vsel_reg = RK805_BUCK4_ON_VSEL_REG, + .vsel_mask = RK818_BUCK4_VSEL_MASK, + .enable_reg = RK805_DCDC_EN_REG, + .enable_mask = ENABLE_MASK(3), + .disable_val = DISABLE_VAL(3), + .of_map_mode = rk818_regulator_of_map_mode, + .owner = THIS_MODULE, + }, { + .name = "LDO_REG1", + .supply_name = "vcc5", + .id = RK805_ID_LDO1, + .ops = &rk818_reg_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 27, + .linear_ranges = rk805_ldo_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk805_ldo_voltage_ranges), + .vsel_reg = RK805_LDO1_ON_VSEL_REG, + .vsel_mask = RK818_LDO_VSEL_MASK, + .enable_reg = RK805_LDO_EN_REG, + .enable_mask = ENABLE_MASK(0), + .disable_val = DISABLE_VAL(0), + .enable_time = 400, + .owner = THIS_MODULE, + }, { + .name = "LDO_REG2", + .supply_name = "vcc5", + .id = RK805_ID_LDO2, + .ops = &rk818_reg_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 27, + .linear_ranges = rk805_ldo_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk805_ldo_voltage_ranges), + .vsel_reg = RK805_LDO2_ON_VSEL_REG, + .vsel_mask = RK818_LDO_VSEL_MASK, + .enable_reg = RK805_LDO_EN_REG, + .enable_mask = ENABLE_MASK(1), + .disable_val = DISABLE_VAL(1), + .enable_time = 400, + .owner = THIS_MODULE, + }, { + .name = "LDO_REG3", + .supply_name = "vcc6", + .id = RK805_ID_LDO3, + .ops = &rk818_reg_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 27, + .linear_ranges = rk805_ldo_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk805_ldo_voltage_ranges), + .vsel_reg = RK805_LDO3_ON_VSEL_REG, + .vsel_mask = RK818_LDO_VSEL_MASK, + .enable_reg = RK805_LDO_EN_REG, + .enable_mask = ENABLE_MASK(2), + .disable_val = DISABLE_VAL(2), + .enable_time = 400, + .owner = THIS_MODULE, + }, +}; + +static struct of_regulator_match rk805_reg_matches[] = { + [RK805_ID_DCDC1] = { + .name = "RK805_DCDC1", + .desc = &rk805_desc[RK805_ID_DCDC1] /* for of_map_node */ + }, + [RK805_ID_DCDC2] = { + .name = "RK805_DCDC2", + .desc = &rk805_desc[RK805_ID_DCDC2] + }, + [RK805_ID_DCDC3] = { + .name = "RK805_DCDC3", + .desc = &rk805_desc[RK805_ID_DCDC3] + }, + [RK805_ID_DCDC4] = { + .name = "RK805_DCDC4", + .desc = &rk805_desc[RK805_ID_DCDC4] + }, + [RK805_ID_LDO1] = { .name = "RK805_LDO1", }, + [RK805_ID_LDO2] = { .name = "RK805_LDO2", }, + [RK805_ID_LDO3] = { .name = "RK805_LDO3", }, +}; + static int rk818_regulator_dt_parse_pdata(struct device *dev, struct device *client_dev, - struct regmap *map) + struct regmap *map, + struct of_regulator_match *reg_matches, + int regulator_nr) { struct device_node *np; int ret; @@ -406,8 +709,7 @@ static int rk818_regulator_dt_parse_pdata(struct device *dev, if (!np) return -ENXIO; - ret = of_regulator_match(dev, np, rk818_reg_matches, - RK818_NUM_REGULATORS); + ret = of_regulator_match(dev, np, reg_matches, regulator_nr); of_node_put(np); return ret; @@ -419,26 +721,47 @@ static int rk818_regulator_probe(struct platform_device *pdev) struct i2c_client *client = rk818->i2c; struct regulator_config config = {}; struct regulator_dev *rk818_rdev; - int ret, i; + int ret, i, reg_nr; + const struct regulator_desc *reg_desc; + struct of_regulator_match *reg_matches; + + switch (rk818->variant) { + case RK818_ID: + reg_desc = rk818_desc; + reg_matches = rk818_reg_matches; + reg_nr = RK818_NUM_REGULATORS; + break; + case RK805_ID: + reg_desc = rk805_desc; + reg_matches = rk805_reg_matches; + reg_nr = RK805_NUM_REGULATORS; + break; + default: + dev_err(&client->dev, "unsupported RK8XX ID %lu\n", + rk818->variant); + return -EINVAL; + } ret = rk818_regulator_dt_parse_pdata(&pdev->dev, &client->dev, - rk818->regmap); + rk818->regmap, + reg_matches, reg_nr); if (ret < 0) return ret; /* Instantiate the regulators */ - for (i = 0; i < RK818_NUM_REGULATORS; i++) { - if (!rk818_reg_matches[i].init_data || - !rk818_reg_matches[i].of_node) + for (i = 0; i < reg_nr; i++) { + if (!reg_matches[i].init_data || + !reg_matches[i].of_node) continue; + config.driver_data = rk818; config.dev = &client->dev; config.regmap = rk818->regmap; - config.of_node = rk818_reg_matches[i].of_node; - config.init_data = rk818_reg_matches[i].init_data; - + config.of_node = reg_matches[i].of_node; + config.init_data = reg_matches[i].init_data; rk818_rdev = devm_regulator_register(&pdev->dev, - &rk818_reg[i], &config); + ®_desc[i], + &config); if (IS_ERR(rk818_rdev)) { dev_err(&client->dev, "failed to register %d regulator\n", i); @@ -446,6 +769,8 @@ static int rk818_regulator_probe(struct platform_device *pdev) } } + dev_info(&client->dev, "register rk%lx regulators\n", rk818->variant); + return 0; } @@ -462,6 +787,7 @@ module_platform_driver(rk818_regulator_driver); MODULE_DESCRIPTION("regulator driver for the rk818 series PMICs"); MODULE_AUTHOR("xsf"); MODULE_AUTHOR("Zhang Qing"); +MODULE_AUTHOR("chen Jianhong"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:rk818-regulator"); diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c index 35c9aada07c8..55406c430232 100644 --- a/drivers/rtc/rtc-rk808.c +++ b/drivers/rtc/rtc-rk808.c @@ -376,8 +376,21 @@ static int rk808_rtc_probe(struct platform_device *pdev) struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); struct rk808_rtc *rk808_rtc; struct rtc_time tm; + struct device_node *np; int ret; + switch (rk808->variant) { + case RK805_ID: + np = of_find_node_by_name(pdev->dev.parent->of_node, "rtc"); + if (np && !of_device_is_available(np)) { + dev_info(&pdev->dev, "device is disabled\n"); + return -EINVAL; + } + break; + default: + break; + } + rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL); if (rk808_rtc == NULL) return -ENOMEM; diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index d1ee93ef6823..59aa1eeb2845 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -64,6 +64,16 @@ enum rk818_reg { RK818_ID_SWITCH, }; +enum rk805_reg { + RK805_ID_DCDC1, + RK805_ID_DCDC2, + RK805_ID_DCDC3, + RK805_ID_DCDC4, + RK805_ID_LDO1, + RK805_ID_LDO2, + RK805_ID_LDO3, +}; + #define RK808_SECONDS_REG 0x00 #define RK808_MINUTES_REG 0x01 #define RK808_HOURS_REG 0x02 @@ -82,6 +92,8 @@ enum rk818_reg { #define RK808_RTC_INT_REG 0x12 #define RK808_RTC_COMP_LSB_REG 0x13 #define RK808_RTC_COMP_MSB_REG 0x14 +#define RK808_ID_MSB 0x17 +#define RK808_ID_LSB 0x18 #define RK808_CLK32OUT_REG 0x20 #define RK808_VB_MON_REG 0x21 #define RK808_THERMAL_REG 0x22 @@ -441,6 +453,86 @@ enum rk818_reg { #define CHG_CVTLIM_ENABLE BIT(6) #define DISCHG_ILIM_ENABLE BIT(7) +/* IRQ Definitions */ +#define RK805_IRQ_PWRON_RISE 0 +#define RK805_IRQ_VB_LOW 1 +#define RK805_IRQ_PWRON 2 +#define RK805_IRQ_PWRON_LP 3 +#define RK805_IRQ_HOTDIE 4 +#define RK805_IRQ_RTC_ALARM 5 +#define RK805_IRQ_RTC_PERIOD 6 +#define RK805_IRQ_PWRON_FALL 7 + +#define RK805_IRQ_PWRON_RISE_MSK BIT(0) +#define RK805_IRQ_VB_LOW_MSK BIT(1) +#define RK805_IRQ_PWRON_MSK BIT(2) +#define RK805_IRQ_PWRON_LP_MSK BIT(3) +#define RK805_IRQ_HOTDIE_MSK BIT(4) +#define RK805_IRQ_RTC_ALARM_MSK BIT(5) +#define RK805_IRQ_RTC_PERIOD_MSK BIT(6) +#define RK805_IRQ_PWRON_FALL_MSK BIT(7) + +#define RK805_PWR_RISE_INT_STATUS BIT(0) +#define RK805_VB_LOW_INT_STATUS BIT(1) +#define RK805_PWRON_INT_STATUS BIT(2) +#define RK805_PWRON_LP_INT_STATUS BIT(3) +#define RK805_HOTDIE_INT_STATUS BIT(4) +#define RK805_ALARM_INT_STATUS BIT(5) +#define RK805_PERIOD_INT_STATUS BIT(6) +#define RK805_PWR_FALL_INT_STATUS BIT(7) + +/*INTERRUPT REGISTER*/ +#define RK805_INT_STS_REG 0x4C +#define RK805_INT_STS_MSK_REG 0x4D +#define RK805_GPIO_IO_POL_REG 0x50 +#define RK805_OUT_REG 0x52 +#define RK805_ON_SOURCE_REG 0xAE +#define RK805_OFF_SOURCE_REG 0xAF + +/*POWER CHANNELS ENABLE REGISTER*/ +#define RK805_DCDC_EN_REG 0x23 +#define RK805_SLP_DCDC_EN_REG 0x25 +#define RK805_SLP_LDO_EN_REG 0x26 +#define RK805_LDO_EN_REG 0x27 + +/*CONFIG REGISTER*/ +#define RK805_THERMAL_REG 0x22 + +/*BUCK AND LDO CONFIG REGISTER*/ +#define RK805_BUCK_LDO_SLP_LP_EN_REG 0x2A +#define RK805_BUCK1_CONFIG_REG 0x2E +#define RK805_BUCK1_ON_VSEL_REG 0x2F +#define RK805_BUCK1_SLP_VSEL_REG 0x30 +#define RK805_BUCK2_CONFIG_REG 0x32 +#define RK805_BUCK2_ON_VSEL_REG 0x33 +#define RK805_BUCK2_SLP_VSEL_REG 0x34 +#define RK805_BUCK3_CONFIG_REG 0x36 +#define RK805_BUCK4_CONFIG_REG 0x37 +#define RK805_BUCK4_ON_VSEL_REG 0x38 +#define RK805_BUCK4_SLP_VSEL_REG 0x39 +#define RK805_LDO1_ON_VSEL_REG 0x3B +#define RK805_LDO1_SLP_VSEL_REG 0x3C +#define RK805_LDO2_ON_VSEL_REG 0x3D +#define RK805_LDO2_SLP_VSEL_REG 0x3E +#define RK805_LDO3_ON_VSEL_REG 0x3F +#define RK805_LDO3_SLP_VSEL_REG 0x40 +#define RK805_OUT_REG 0x52 +#define RK805_ON_SOURCE_REG 0xAE +#define RK805_OFF_SOURCE_REG 0xAF + +#define RK805_NUM_REGULATORS 7 + +#define RK805_PWRON_FALL_RISE_INT_EN 0x0 +#define RK805_PWRON_FALL_RISE_INT_MSK 0x81 + +#define TEMP115C 0x0c +#define TEMP_HOTDIE_MSK 0x0c +#define SLP_SD_MSK (0x3 << 2) +#define SHUTDOWN_FUN (0x2 << 2) +#define SLEEP_FUN (0x1 << 2) +#define RK8XX_ID_MSK 0xfff0 +#define FPWM_MODE BIT(7) + enum { BUCK_ILMIN_50MA, BUCK_ILMIN_100MA, @@ -467,5 +559,13 @@ struct rk808 { struct i2c_client *i2c; struct regmap_irq_chip_data *irq_data; struct regmap *regmap; + long variant; }; + +enum { + RK805_ID = 0x8050, + RK808_ID = 0x0000, + RK818_ID = 0x8180, +}; + #endif /* __LINUX_REGULATOR_rk808_H */ -- 2.34.1