Merge branches 'ib-mfd-clk-gpio-power-regulator-rtc-5.3', 'ib-mfd-clk-regulator-rtc-5.3', 'ib-mfd-cros-5.3' and 'ib-mfd-regulator-5.3' into ibs-for-mfd-merged
diff --git a/Documentation/devicetree/bindings/mfd/lp87565.txt b/Documentation/devicetree/bindings/mfd/lp87565.txt
index a48df7c..41671e0 100644
--- a/Documentation/devicetree/bindings/mfd/lp87565.txt
+++ b/Documentation/devicetree/bindings/mfd/lp87565.txt
@@ -41,3 +41,39 @@
};
};
};
+
+TI LP87561 PMIC:
+
+This is a single output 4-phase regulator configuration
+
+Required properties:
+ - compatible: "ti,lp87561-q1"
+ - reg: I2C slave address.
+ - gpio-controller: Marks the device node as a GPIO Controller.
+ - #gpio-cells: Should be two. The first cell is the pin number and
+ the second cell is used to specify flags.
+ See ../gpio/gpio.txt for more information.
+ - xxx-in-supply: Phandle to parent supply node of each regulator
+ populated under regulators node. xxx should match
+ the supply_name populated in driver.
+Example:
+
+lp87561_pmic: pmic@62 {
+ compatible = "ti,lp87561-q1";
+ reg = <0x62>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ buck3210-in-supply = <&vsys_3v3>;
+
+ regulators: regulators {
+ buck3210_reg: buck3210 {
+ /* VDD_CORE */
+ regulator-name = "buck3210";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/mfd/rk808.txt b/Documentation/devicetree/bindings/mfd/rk808.txt
index 1683ec3..04df07f 100644
--- a/Documentation/devicetree/bindings/mfd/rk808.txt
+++ b/Documentation/devicetree/bindings/mfd/rk808.txt
@@ -3,11 +3,15 @@
The rk8xx family current members:
rk805
rk808
+rk809
+rk817
rk818
Required properties:
- compatible: "rockchip,rk805"
- compatible: "rockchip,rk808"
+- compatible: "rockchip,rk809"
+- compatible: "rockchip,rk817"
- compatible: "rockchip,rk818"
- reg: I2C slave address
- interrupts: the interrupt outputs of the controller.
@@ -45,6 +49,23 @@
the gpio controller. If DVS GPIOs aren't present, voltage changes will happen
very quickly with no slow ramp time.
+Optional shared RK809 and RK817 properties:
+- vcc1-supply: The input supply for DCDC_REG1
+- vcc2-supply: The input supply for DCDC_REG2
+- vcc3-supply: The input supply for DCDC_REG3
+- vcc4-supply: The input supply for DCDC_REG4
+- vcc5-supply: The input supply for LDO_REG1, LDO_REG2, LDO_REG3
+- vcc6-supply: The input supply for LDO_REG4, LDO_REG5, LDO_REG6
+- vcc7-supply: The input supply for LDO_REG7, LDO_REG8, LDO_REG9
+
+Optional RK809 properties:
+- vcc8-supply: The input supply for SWITCH_REG1
+- vcc9-supply: The input supply for DCDC_REG5, SWITCH_REG2
+
+Optional RK817 properties:
+- vcc8-supply: The input supply for BOOST
+- vcc9-supply: The input supply for OTG_SWITCH
+
Optional RK818 properties:
- vcc1-supply: The input supply for DCDC_REG1
- vcc2-supply: The input supply for DCDC_REG2
@@ -86,6 +107,21 @@
- SWITCH_REGn
- valid values for n are 1 to 2
+Following regulators of the RK809 and RK817 PMIC blocks are supported. Note that
+the 'n' in regulator name, as in DCDC_REGn or LDOn, represents the DCDC or LDO
+number as described in RK809 and RK817 datasheets.
+
+ - DCDC_REGn
+ - valid values for n are 1 to 5 for RK809.
+ - valid values for n are 1 to 4 for RK817.
+ - LDO_REGn
+ - valid values for n are 1 to 9 for RK809.
+ - valid values for n are 1 to 9 for RK817.
+ - SWITCH_REGn
+ - valid values for n are 1 to 2 for RK809.
+ - BOOST for RK817
+ - OTG_SWITCH for RK817
+
Following regulators of the RK818 PMIC block are supported. Note that
the 'n' in regulator name, as in DCDC_REGn or LDOn, represents the DCDC or LDO
number as described in RK818 datasheet.
@@ -98,6 +134,14 @@
- HDMI_SWITCH
- OTG_SWITCH
+It is necessary to configure three pins for both the RK809 and RK817, the three
+pins are "gpio_ts" "gpio_gt" "gpio_slp".
+ The gpio_gt and gpio_ts pins support the gpio function.
+ The gpio_slp pin is for controlling the pmic states, as below:
+ - reset
+ - power down
+ - sleep
+
Standard regulator bindings are used inside regulator subnodes. Check
Documentation/devicetree/bindings/regulator/regulator.txt
for more details
diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd70528-pmic.txt b/Documentation/devicetree/bindings/mfd/rohm,bd70528-pmic.txt
new file mode 100644
index 0000000..c3c02ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/rohm,bd70528-pmic.txt
@@ -0,0 +1,102 @@
+* ROHM BD70528 Power Management Integrated Circuit bindings
+
+BD70528MWV is an ultra-low quiescent current general purpose, single-chip,
+power management IC for battery-powered portable devices. The IC
+integrates 3 ultra-low current consumption buck converters, 3 LDOs and 2
+LED Drivers. Also included are 4 GPIOs, a real-time clock (RTC), a 32kHz
+clock gate, high-accuracy VREF for use with an external ADC, flexible
+dual-input power path, 10 bit SAR ADC for battery temperature monitor and
+1S battery charger with scalable charge currents.
+
+Required properties:
+ - compatible : Should be "rohm,bd70528"
+ - reg : I2C slave address.
+ - interrupts : The interrupt line the device is connected to.
+ - interrupt-controller : To indicate BD70528 acts as an interrupt controller.
+ - #interrupt-cells : Should be 2. Usage is compliant to the 2 cells
+ variant of ../interrupt-controller/interrupts.txt
+ - gpio-controller : To indicate BD70528 acts as a GPIO controller.
+ - #gpio-cells : Should be 2. The first cell is the pin number and
+ the second cell is used to specify flags. See
+ ../gpio/gpio.txt for more information.
+ - #clock-cells : Should be 0.
+ - regulators: : List of child nodes that specify the regulators.
+ Please see ../regulator/rohm,bd70528-regulator.txt
+
+Optional properties:
+ - clock-output-names : Should contain name for output clock.
+
+Example:
+/* External oscillator */
+osc: oscillator {
+ compatible = "fixed-clock";
+ #clock-cells = <1>;
+ clock-frequency = <32768>;
+ clock-output-names = "osc";
+};
+
+pmic: pmic@4b {
+ compatible = "rohm,bd70528";
+ reg = <0x4b>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <29 GPIO_ACTIVE_LOW>;
+ clocks = <&osc 0>;
+ #clock-cells = <0>;
+ clock-output-names = "bd70528-32k-out";
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ regulators {
+ buck1: BUCK1 {
+ regulator-name = "buck1";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3400000>;
+ regulator-boot-on;
+ regulator-ramp-delay = <125>;
+ };
+ buck2: BUCK2 {
+ regulator-name = "buck2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-ramp-delay = <125>;
+ };
+ buck3: BUCK3 {
+ regulator-name = "buck3";
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-ramp-delay = <250>;
+ };
+ ldo1: LDO1 {
+ regulator-name = "ldo1";
+ regulator-min-microvolt = <1650000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ };
+ ldo2: LDO2 {
+ regulator-name = "ldo2";
+ regulator-min-microvolt = <1650000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ };
+
+ ldo3: LDO3 {
+ regulator-name = "ldo3";
+ regulator-min-microvolt = <1650000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ led_ldo1: LED_LDO1 {
+ regulator-name = "led_ldo1";
+ regulator-min-microvolt = <200000>;
+ regulator-max-microvolt = <300000>;
+ };
+ led_ldo2: LED_LDO2 {
+ regulator-name = "led_ldo2";
+ regulator-min-microvolt = <200000>;
+ regulator-max-microvolt = <300000>;
+ };
+ };
+};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index fc1e0cf4..7376af2 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -53,13 +53,12 @@
This driver supports Maxim 9485 Programmable Audio Clock Generator
config COMMON_CLK_RK808
- tristate "Clock driver for RK805/RK808/RK818"
+ tristate "Clock driver for RK805/RK808/RK809/RK817/RK818"
depends on MFD_RK808
---help---
- This driver supports RK805, RK808 and RK818 crystal oscillator clock. These
- multi-function devices have two fixed-rate oscillators,
- clocked at 32KHz each. Clkout1 is always on, Clkout2 can off
- by control register.
+ This driver supports RK805, RK809 and RK817, RK808 and RK818 crystal oscillator clock.
+ These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each.
+ Clkout1 is always on, Clkout2 can off by control register.
config COMMON_CLK_HI655X
tristate "Clock driver for Hi655x" if EXPERT
@@ -293,10 +292,10 @@
config COMMON_CLK_BD718XX
tristate "Clock driver for ROHM BD718x7 PMIC"
- depends on MFD_ROHM_BD718XX
+ depends on MFD_ROHM_BD718XX || MFD_ROHM_BD70528
help
- This driver supports ROHM BD71837 and ROHM BD71847
- PMICs clock gates.
+ This driver supports ROHM BD71837, ROHM BD71847 and
+ ROHM BD70528 PMICs clock gates.
config COMMON_CLK_FIXED_MMIO
bool "Clock driver for Memory Mapped Fixed values"
diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index 60422c7..ae6e5ba 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -8,6 +8,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mfd/rohm-bd718x7.h>
+#include <linux/mfd/rohm-bd70528.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/regmap.h>
@@ -17,7 +18,7 @@ struct bd718xx_clk {
u8 reg;
u8 mask;
struct platform_device *pdev;
- struct bd718xx *mfd;
+ struct rohm_regmap_dev *mfd;
};
static int bd71837_clk_set(struct clk_hw *hw, int status)
@@ -68,7 +69,7 @@ static int bd71837_clk_probe(struct platform_device *pdev)
int rval = -ENOMEM;
const char *parent_clk;
struct device *parent = pdev->dev.parent;
- struct bd718xx *mfd = dev_get_drvdata(parent);
+ struct rohm_regmap_dev *mfd = dev_get_drvdata(parent);
struct clk_init_data init = {
.name = "bd718xx-32k-out",
.ops = &bd71837_clk_ops,
@@ -86,9 +87,20 @@ static int bd71837_clk_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "No parent clk found\n");
return -EINVAL;
}
-
- c->reg = BD718XX_REG_OUT32K;
- c->mask = BD718XX_OUT32K_EN;
+ switch (mfd->chip_type) {
+ case ROHM_CHIP_TYPE_BD71837:
+ case ROHM_CHIP_TYPE_BD71847:
+ c->reg = BD718XX_REG_OUT32K;
+ c->mask = BD718XX_OUT32K_EN;
+ break;
+ case ROHM_CHIP_TYPE_BD70528:
+ c->reg = BD70528_REG_CLK_OUT;
+ c->mask = BD70528_CLK_OUT_EN_MASK;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown clk chip\n");
+ return -EINVAL;
+ }
c->mfd = mfd;
c->pdev = pdev;
c->hw.init = &init;
@@ -119,5 +131,5 @@ static struct platform_driver bd71837_clk = {
module_platform_driver(bd71837_clk);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_DESCRIPTION("BD71837 chip clk driver");
+MODULE_DESCRIPTION("BD71837/BD71847/BD70528 chip clk driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-rk808.c b/drivers/clk/clk-rk808.c
index 8d90bdf..75f2cf0 100644
--- a/drivers/clk/clk-rk808.c
+++ b/drivers/clk/clk-rk808.c
@@ -96,6 +96,68 @@ of_clk_rk808_get(struct of_phandle_args *clkspec, void *data)
return idx ? &rk808_clkout->clkout2_hw : &rk808_clkout->clkout1_hw;
}
+static int rk817_clkout2_enable(struct clk_hw *hw, bool enable)
+{
+ struct rk808_clkout *rk808_clkout = container_of(hw,
+ struct rk808_clkout,
+ clkout2_hw);
+ struct rk808 *rk808 = rk808_clkout->rk808;
+
+ return regmap_update_bits(rk808->regmap, RK817_SYS_CFG(1),
+ RK817_CLK32KOUT2_EN,
+ enable ? RK817_CLK32KOUT2_EN : 0);
+}
+
+static int rk817_clkout2_prepare(struct clk_hw *hw)
+{
+ return rk817_clkout2_enable(hw, true);
+}
+
+static void rk817_clkout2_unprepare(struct clk_hw *hw)
+{
+ rk817_clkout2_enable(hw, false);
+}
+
+static int rk817_clkout2_is_prepared(struct clk_hw *hw)
+{
+ struct rk808_clkout *rk808_clkout = container_of(hw,
+ struct rk808_clkout,
+ clkout2_hw);
+ struct rk808 *rk808 = rk808_clkout->rk808;
+ unsigned int val;
+
+ int ret = regmap_read(rk808->regmap, RK817_SYS_CFG(1), &val);
+
+ if (ret < 0)
+ return 0;
+
+ return (val & RK817_CLK32KOUT2_EN) ? 1 : 0;
+}
+
+static const struct clk_ops rk817_clkout2_ops = {
+ .prepare = rk817_clkout2_prepare,
+ .unprepare = rk817_clkout2_unprepare,
+ .is_prepared = rk817_clkout2_is_prepared,
+ .recalc_rate = rk808_clkout_recalc_rate,
+};
+
+static const struct clk_ops *rkpmic_get_ops(long variant)
+{
+ switch (variant) {
+ case RK809_ID:
+ case RK817_ID:
+ return &rk817_clkout2_ops;
+ /*
+ * For the default case, it match the following PMIC type.
+ * RK805_ID
+ * RK808_ID
+ * RK818_ID
+ */
+ default:
+ return &rk808_clkout2_ops;
+ }
+}
+
static int rk808_clkout_probe(struct platform_device *pdev)
{
struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
@@ -127,7 +189,7 @@ static int rk808_clkout_probe(struct platform_device *pdev)
return ret;
init.name = "rk808-clkout2";
- init.ops = &rk808_clkout2_ops;
+ init.ops = rkpmic_get_ops(rk808->variant);
rk808_clkout->clkout2_hw.init = &init;
/* optional override of the clockname */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 8023d03..87dbbd0 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -977,6 +977,17 @@
help
Support for GPIOs on Wolfson Arizona class devices.
+config GPIO_BD70528
+ tristate "ROHM BD70528 GPIO support"
+ depends on MFD_ROHM_BD70528
+ help
+ Support for GPIOs on ROHM BD70528 PMIC. There are four GPIOs
+ available on the ROHM PMIC in total. The GPIOs can also
+ generate interrupts.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-bd70528.
+
config GPIO_BD9571MWV
tristate "ROHM BD9571 GPIO support"
depends on MFD_BD9571MWV
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 6700eee..10efc4f 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -35,6 +35,7 @@
obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
+obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
diff --git a/drivers/gpio/gpio-bd70528.c b/drivers/gpio/gpio-bd70528.c
new file mode 100644
index 0000000..fd85605
--- /dev/null
+++ b/drivers/gpio/gpio-bd70528.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+// gpio-bd70528.c ROHM BD70528MWV gpio driver
+
+#include <linux/gpio/driver.h>
+#include <linux/mfd/rohm-bd70528.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define GPIO_IN_REG(offset) (BD70528_REG_GPIO1_IN + (offset) * 2)
+#define GPIO_OUT_REG(offset) (BD70528_REG_GPIO1_OUT + (offset) * 2)
+
+struct bd70528_gpio {
+ struct rohm_regmap_dev chip;
+ struct gpio_chip gpio;
+};
+
+static int bd70528_set_debounce(struct bd70528_gpio *bdgpio,
+ unsigned int offset, unsigned int debounce)
+{
+ u8 val;
+
+ switch (debounce) {
+ case 0:
+ val = BD70528_DEBOUNCE_DISABLE;
+ break;
+ case 1 ... 15:
+ val = BD70528_DEBOUNCE_15MS;
+ break;
+ case 16 ... 30:
+ val = BD70528_DEBOUNCE_30MS;
+ break;
+ case 31 ... 50:
+ val = BD70528_DEBOUNCE_50MS;
+ break;
+ default:
+ dev_err(bdgpio->chip.dev,
+ "Invalid debouce value %u\n", debounce);
+ return -EINVAL;
+ }
+ return regmap_update_bits(bdgpio->chip.regmap, GPIO_IN_REG(offset),
+ BD70528_DEBOUNCE_MASK, val);
+}
+
+static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
+ int val, ret;
+
+ /* Do we need to do something to IRQs here? */
+ ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val);
+ if (ret) {
+ dev_err(bdgpio->chip.dev, "Could not read gpio direction\n");
+ return ret;
+ }
+
+ return !(val & BD70528_GPIO_OUT_EN_MASK);
+}
+
+static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
+
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ return regmap_update_bits(bdgpio->chip.regmap,
+ GPIO_OUT_REG(offset),
+ BD70528_GPIO_DRIVE_MASK,
+ BD70528_GPIO_OPEN_DRAIN);
+ break;
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ return regmap_update_bits(bdgpio->chip.regmap,
+ GPIO_OUT_REG(offset),
+ BD70528_GPIO_DRIVE_MASK,
+ BD70528_GPIO_PUSH_PULL);
+ break;
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ return bd70528_set_debounce(bdgpio, offset,
+ pinconf_to_config_argument(config));
+ break;
+ default:
+ break;
+ }
+ return -ENOTSUPP;
+}
+
+static int bd70528_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+ struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
+
+ /* Do we need to do something to IRQs here? */
+ return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
+ BD70528_GPIO_OUT_EN_MASK,
+ BD70528_GPIO_OUT_DISABLE);
+}
+
+static void bd70528_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ int ret;
+ struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
+ u8 val = (value) ? BD70528_GPIO_OUT_HI : BD70528_GPIO_OUT_LO;
+
+ ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
+ BD70528_GPIO_OUT_MASK, val);
+ if (ret)
+ dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value);
+}
+
+static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
+
+ bd70528_gpio_set(chip, offset, value);
+ return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
+ BD70528_GPIO_OUT_EN_MASK,
+ BD70528_GPIO_OUT_ENABLE);
+}
+
+#define GPIO_IN_STATE_MASK(offset) (BD70528_GPIO_IN_STATE_BASE << (offset))
+
+static int bd70528_gpio_get_o(struct bd70528_gpio *bdgpio, unsigned int offset)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val);
+ if (!ret)
+ ret = !!(val & BD70528_GPIO_OUT_MASK);
+ else
+ dev_err(bdgpio->chip.dev, "GPIO (out) state read failed\n");
+
+ return ret;
+}
+
+static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(bdgpio->chip.regmap, BD70528_REG_GPIO_STATE, &val);
+
+ if (!ret)
+ ret = !(val & GPIO_IN_STATE_MASK(offset));
+ else
+ dev_err(bdgpio->chip.dev, "GPIO (in) state read failed\n");
+
+ return ret;
+}
+
+static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ int ret = -EINVAL;
+ struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
+
+ /*
+ * There is a race condition where someone might be changing the
+ * GPIO direction after we get it but before we read the value. But
+ * application design where GPIO direction may be changed just when
+ * we read GPIO value would be pointless as reader could not know
+ * whether the returned high/low state is caused by input or output.
+ * Or then there must be other ways to mitigate the issue. Thus
+ * locking would make no sense.
+ */
+ ret = bd70528_get_direction(chip, offset);
+ if (ret == 0)
+ ret = bd70528_gpio_get_o(bdgpio, offset);
+ else if (ret == 1)
+ ret = bd70528_gpio_get_i(bdgpio, offset);
+ else
+ dev_err(bdgpio->chip.dev, "failed to read GPIO direction\n");
+
+ return ret;
+}
+
+static int bd70528_probe(struct platform_device *pdev)
+{
+ struct bd70528_gpio *bdgpio;
+ struct rohm_regmap_dev *bd70528;
+ int ret;
+
+ bd70528 = dev_get_drvdata(pdev->dev.parent);
+ if (!bd70528) {
+ dev_err(&pdev->dev, "No MFD driver data\n");
+ return -EINVAL;
+ }
+
+ bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio),
+ GFP_KERNEL);
+ if (!bdgpio)
+ return -ENOMEM;
+ bdgpio->chip.dev = &pdev->dev;
+ bdgpio->gpio.parent = pdev->dev.parent;
+ bdgpio->gpio.label = "bd70528-gpio";
+ bdgpio->gpio.owner = THIS_MODULE;
+ bdgpio->gpio.get_direction = bd70528_get_direction;
+ bdgpio->gpio.direction_input = bd70528_direction_input;
+ bdgpio->gpio.direction_output = bd70528_direction_output;
+ bdgpio->gpio.set_config = bd70528_gpio_set_config;
+ bdgpio->gpio.can_sleep = true;
+ bdgpio->gpio.get = bd70528_gpio_get;
+ bdgpio->gpio.set = bd70528_gpio_set;
+ bdgpio->gpio.ngpio = 4;
+ bdgpio->gpio.base = -1;
+#ifdef CONFIG_OF_GPIO
+ bdgpio->gpio.of_node = pdev->dev.parent->of_node;
+#endif
+ bdgpio->chip.regmap = bd70528->regmap;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
+ bdgpio);
+ if (ret)
+ dev_err(&pdev->dev, "gpio_init: Failed to add bd70528-gpio\n");
+
+ return ret;
+}
+
+static struct platform_driver bd70528_gpio = {
+ .driver = {
+ .name = "bd70528-gpio"
+ },
+ .probe = bd70528_probe,
+};
+
+module_platform_driver(bd70528_gpio);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD70528 voltage regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 294d956..760b9e2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1030,14 +1030,14 @@
different functionality of the device.
config MFD_RK808
- tristate "Rockchip RK805/RK808/RK818 Power Management Chip"
+ tristate "Rockchip RK805/RK808/RK809/RK817/RK818 Power Management Chip"
depends on I2C && OF
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
- If you say yes here you get support for the RK805, RK808 and RK818
- Power Management chips.
+ If you say yes here you get support for the RK805, RK808, RK809,
+ RK817 and RK818 Power Management chips.
This driver provides common support for accessing the device
through I2C interface. The device supports multiple sub-devices
including interrupts, RTC, LDO & DCDC regulators, and onkey.
@@ -1890,6 +1890,23 @@
NXP i.MX8. It contains 8 BUCK outputs and 7 LDOs, voltage monitoring
and emergency shut down as well as 32,768KHz clock output.
+config MFD_ROHM_BD70528
+ tristate "ROHM BD70528 Power Management IC"
+ depends on I2C=y
+ depends on OF
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ Select this option to get support for the ROHM BD70528 Power
+ Management IC. BD71837 is general purpose single-chip power
+ management IC for battery-powered portable devices. It contains
+ 3 ultra-low current consumption buck converters, 3 LDOs and 2 LED
+ drivers. Also included are 4 GPIOs, a real-time clock (RTC), a 32kHz
+ crystal oscillator, high-accuracy VREF for use with an external ADC,
+ 10 bits SAR ADC for battery temperature monitor and 1S battery
+ charger.
+
config MFD_STM32_LPTIMER
tristate "Support for STM32 Low-Power Timer"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 52b1a90..643d65bc 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -247,5 +247,7 @@
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o
obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
+obj-$(CONFIG_MFD_ROHM_BD70528) += rohm-bd70528.o
obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
obj-$(CONFIG_MFD_STMFX) += stmfx.o
+
diff --git a/drivers/mfd/lp87565.c b/drivers/mfd/lp87565.c
index 32d2a07..8ad688f 100644
--- a/drivers/mfd/lp87565.c
+++ b/drivers/mfd/lp87565.c
@@ -33,6 +33,10 @@ static const struct of_device_id of_lp87565_match_table[] = {
.compatible = "ti,lp87565-q1",
.data = (void *)LP87565_DEVICE_TYPE_LP87565_Q1,
},
+ {
+ .compatible = "ti,lp87561-q1",
+ .data = (void *)LP87565_DEVICE_TYPE_LP87561_Q1,
+ },
{}
};
MODULE_DEVICE_TABLE(of, of_lp87565_match_table);
diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
index 9437778..6ee1c46 100644
--- a/drivers/mfd/rk808.c
+++ b/drivers/mfd/rk808.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
+#include <linux/syscore_ops.h>
struct rk808_reg_data {
int addr;
@@ -62,6 +63,27 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
+static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /*
+ * Notes:
+ * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but
+ * we don't use that feature. It's better to cache.
+ */
+
+ switch (reg) {
+ case RK817_SECONDS_REG ... RK817_WEEKS_REG:
+ case RK817_RTC_STATUS_REG:
+ case RK817_INT_STS_REG0:
+ case RK817_INT_STS_REG1:
+ case RK817_INT_STS_REG2:
+ case RK817_SYS_STS:
+ return true;
+ }
+
+ return true;
+}
+
static const struct regmap_config rk818_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -86,6 +108,14 @@ static const struct regmap_config rk808_regmap_config = {
.volatile_reg = rk808_is_volatile_reg,
};
+static const struct regmap_config rk817_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = RK817_GPIO_INT_CFG,
+ .cache_type = REGCACHE_NONE,
+ .volatile_reg = rk817_is_volatile_reg,
+};
+
static struct resource rtc_resources[] = {
{
.start = RK808_IRQ_RTC_ALARM,
@@ -94,6 +124,10 @@ static struct resource rtc_resources[] = {
}
};
+static struct resource rk817_rtc_resources[] = {
+ DEFINE_RES_IRQ(RK817_IRQ_RTC_ALARM),
+};
+
static struct resource rk805_key_resources[] = {
{
.start = RK805_IRQ_PWRON_FALL,
@@ -107,6 +141,11 @@ static struct resource rk805_key_resources[] = {
}
};
+static struct resource rk817_pwrkey_resources[] = {
+ DEFINE_RES_IRQ(RK817_IRQ_PWRON_RISE),
+ DEFINE_RES_IRQ(RK817_IRQ_PWRON_FALL),
+};
+
static const struct mfd_cell rk805s[] = {
{ .name = "rk808-clkout", },
{ .name = "rk808-regulator", },
@@ -132,6 +171,21 @@ static const struct mfd_cell rk808s[] = {
},
};
+static const struct mfd_cell rk817s[] = {
+ { .name = "rk808-clkout",},
+ { .name = "rk808-regulator",},
+ {
+ .name = "rk8xx-pwrkey",
+ .num_resources = ARRAY_SIZE(rk817_pwrkey_resources),
+ .resources = &rk817_pwrkey_resources[0],
+ },
+ {
+ .name = "rk808-rtc",
+ .num_resources = ARRAY_SIZE(rk817_rtc_resources),
+ .resources = &rk817_rtc_resources[0],
+ },
+};
+
static const struct mfd_cell rk818s[] = {
{ .name = "rk808-clkout", },
{ .name = "rk808-regulator", },
@@ -167,6 +221,13 @@ static const struct rk808_reg_data rk808_pre_init_reg[] = {
VB_LO_SEL_3500MV },
};
+static const struct rk808_reg_data rk817_pre_init_reg[] = {
+ {RK817_RTC_CTRL_REG, RTC_STOP, RTC_STOP},
+ {RK817_GPIO_INT_CFG, RK817_INT_POL_MSK, RK817_INT_POL_H},
+ {RK817_SYS_CFG(1), RK817_HOTDIE_TEMP_MSK | RK817_TSD_TEMP_MSK,
+ RK817_HOTDIE_105 | RK817_TSD_140},
+};
+
static const struct rk808_reg_data rk818_pre_init_reg[] = {
/* improve efficiency */
{ RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_250MA },
@@ -332,6 +393,33 @@ static const struct regmap_irq rk818_irqs[] = {
},
};
+static const struct regmap_irq rk817_irqs[RK817_IRQ_END] = {
+ REGMAP_IRQ_REG_LINE(0, 8),
+ REGMAP_IRQ_REG_LINE(1, 8),
+ REGMAP_IRQ_REG_LINE(2, 8),
+ REGMAP_IRQ_REG_LINE(3, 8),
+ REGMAP_IRQ_REG_LINE(4, 8),
+ REGMAP_IRQ_REG_LINE(5, 8),
+ REGMAP_IRQ_REG_LINE(6, 8),
+ REGMAP_IRQ_REG_LINE(7, 8),
+ REGMAP_IRQ_REG_LINE(8, 8),
+ REGMAP_IRQ_REG_LINE(9, 8),
+ REGMAP_IRQ_REG_LINE(10, 8),
+ REGMAP_IRQ_REG_LINE(11, 8),
+ REGMAP_IRQ_REG_LINE(12, 8),
+ REGMAP_IRQ_REG_LINE(13, 8),
+ REGMAP_IRQ_REG_LINE(14, 8),
+ REGMAP_IRQ_REG_LINE(15, 8),
+ REGMAP_IRQ_REG_LINE(16, 8),
+ REGMAP_IRQ_REG_LINE(17, 8),
+ REGMAP_IRQ_REG_LINE(18, 8),
+ REGMAP_IRQ_REG_LINE(19, 8),
+ REGMAP_IRQ_REG_LINE(20, 8),
+ REGMAP_IRQ_REG_LINE(21, 8),
+ REGMAP_IRQ_REG_LINE(22, 8),
+ REGMAP_IRQ_REG_LINE(23, 8)
+};
+
static struct regmap_irq_chip rk805_irq_chip = {
.name = "rk805",
.irqs = rk805_irqs,
@@ -355,6 +443,18 @@ static const struct regmap_irq_chip rk808_irq_chip = {
.init_ack_masked = true,
};
+static struct regmap_irq_chip rk817_irq_chip = {
+ .name = "rk817",
+ .irqs = rk817_irqs,
+ .num_irqs = ARRAY_SIZE(rk817_irqs),
+ .num_regs = 3,
+ .irq_reg_stride = 2,
+ .status_base = RK817_INT_STS_REG0,
+ .mask_base = RK817_INT_STS_MSK_REG0,
+ .ack_base = RK817_INT_STS_REG0,
+ .init_ack_masked = true,
+};
+
static const struct regmap_irq_chip rk818_irq_chip = {
.name = "rk818",
.irqs = rk818_irqs,
@@ -423,9 +523,33 @@ static void rk818_device_shutdown(void)
dev_err(&rk808_i2c_client->dev, "power off error!\n");
}
+static void rk8xx_syscore_shutdown(void)
+{
+ struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
+ int ret;
+
+ if (system_state == SYSTEM_POWER_OFF &&
+ (rk808->variant == RK809_ID || rk808->variant == RK817_ID)) {
+ ret = regmap_update_bits(rk808->regmap,
+ RK817_SYS_CFG(3),
+ RK817_SLPPIN_FUNC_MSK,
+ SLPPIN_DN_FUN);
+ if (ret) {
+ dev_warn(&rk808_i2c_client->dev,
+ "Cannot switch to power down function\n");
+ }
+ }
+}
+
+static struct syscore_ops rk808_syscore_ops = {
+ .shutdown = rk8xx_syscore_shutdown,
+};
+
static const struct of_device_id rk808_of_match[] = {
{ .compatible = "rockchip,rk805" },
{ .compatible = "rockchip,rk808" },
+ { .compatible = "rockchip,rk809" },
+ { .compatible = "rockchip,rk817" },
{ .compatible = "rockchip,rk818" },
{ },
};
@@ -438,10 +562,11 @@ static int rk808_probe(struct i2c_client *client,
struct rk808 *rk808;
const struct rk808_reg_data *pre_init_reg;
const struct mfd_cell *cells;
- void (*pm_pwroff_fn)(void);
+ void (*pm_pwroff_fn)(void) = NULL;
int nr_pre_init_regs;
int nr_cells;
int pm_off = 0, msb, lsb;
+ unsigned char pmic_id_msb, pmic_id_lsb;
int ret;
int i;
@@ -449,15 +574,24 @@ static int rk808_probe(struct i2c_client *client,
if (!rk808)
return -ENOMEM;
+ if (of_device_is_compatible(np, "rockchip,rk817") ||
+ of_device_is_compatible(np, "rockchip,rk809")) {
+ pmic_id_msb = RK817_ID_MSB;
+ pmic_id_lsb = RK817_ID_LSB;
+ } else {
+ pmic_id_msb = RK808_ID_MSB;
+ pmic_id_lsb = RK808_ID_LSB;
+ }
+
/* Read chip variant */
- msb = i2c_smbus_read_byte_data(client, RK808_ID_MSB);
+ msb = i2c_smbus_read_byte_data(client, pmic_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);
+ lsb = i2c_smbus_read_byte_data(client, pmic_id_lsb);
if (lsb < 0) {
dev_err(&client->dev, "failed to read the chip id at 0x%x\n",
RK808_ID_LSB);
@@ -495,6 +629,16 @@ static int rk808_probe(struct i2c_client *client,
nr_cells = ARRAY_SIZE(rk818s);
pm_pwroff_fn = rk818_device_shutdown;
break;
+ case RK809_ID:
+ case RK817_ID:
+ rk808->regmap_cfg = &rk817_regmap_config;
+ rk808->regmap_irq_chip = &rk817_irq_chip;
+ pre_init_reg = rk817_pre_init_reg;
+ nr_pre_init_regs = ARRAY_SIZE(rk817_pre_init_reg);
+ cells = rk817s;
+ nr_cells = ARRAY_SIZE(rk817s);
+ register_syscore_ops(&rk808_syscore_ops);
+ break;
default:
dev_err(&client->dev, "Unsupported RK8XX ID %lu\n",
rk808->variant);
@@ -568,10 +712,52 @@ static int rk808_remove(struct i2c_client *client)
return 0;
}
+static int rk8xx_suspend(struct device *dev)
+{
+ struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
+ int ret = 0;
+
+ switch (rk808->variant) {
+ case RK809_ID:
+ case RK817_ID:
+ ret = regmap_update_bits(rk808->regmap,
+ RK817_SYS_CFG(3),
+ RK817_SLPPIN_FUNC_MSK,
+ SLPPIN_SLP_FUN);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int rk8xx_resume(struct device *dev)
+{
+ struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
+ int ret = 0;
+
+ switch (rk808->variant) {
+ case RK809_ID:
+ case RK817_ID:
+ ret = regmap_update_bits(rk808->regmap,
+ RK817_SYS_CFG(3),
+ RK817_SLPPIN_FUNC_MSK,
+ SLPPIN_NULL_FUN);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+SIMPLE_DEV_PM_OPS(rk8xx_pm_ops, rk8xx_suspend, rk8xx_resume);
+
static struct i2c_driver rk808_i2c_driver = {
.driver = {
.name = "rk808",
.of_match_table = rk808_of_match,
+ .pm = &rk8xx_pm_ops,
},
.probe = rk808_probe,
.remove = rk808_remove,
diff --git a/drivers/mfd/rohm-bd70528.c b/drivers/mfd/rohm-bd70528.c
new file mode 100644
index 0000000..55599d5
--- /dev/null
+++ b/drivers/mfd/rohm-bd70528.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Copyright (C) 2019 ROHM Semiconductors
+//
+// ROHM BD70528 PMIC driver
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rohm-bd70528.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define BD70528_NUM_OF_GPIOS 4
+
+static const struct resource rtc_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_RTC_ALARM, "bd70528-rtc-alm"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_ELPS_TIM, "bd70528-elapsed-timer"),
+};
+
+static const struct resource charger_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_BAT_OV_RES, "bd70528-bat-ov-res"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_BAT_OV_DET, "bd70528-bat-ov-det"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_DBAT_DET, "bd70528-bat-dead"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_BATTSD_COLD_RES, "bd70528-bat-warmed"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_BATTSD_COLD_DET, "bd70528-bat-cold"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_BATTSD_HOT_RES, "bd70528-bat-cooled"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_BATTSD_HOT_DET, "bd70528-bat-hot"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_CHG_TSD, "bd70528-chg-tshd"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_BAT_RMV, "bd70528-bat-removed"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_BAT_DET, "bd70528-bat-detected"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN2_OV_RES, "bd70528-dcin2-ov-res"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN2_OV_DET, "bd70528-dcin2-ov-det"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN2_RMV, "bd70528-dcin2-removed"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN2_DET, "bd70528-dcin2-detected"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN1_RMV, "bd70528-dcin1-removed"),
+ DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN1_DET, "bd70528-dcin1-detected"),
+};
+
+static struct mfd_cell bd70528_mfd_cells[] = {
+ { .name = "bd70528-pmic", },
+ { .name = "bd70528-gpio", },
+ /*
+ * We use BD71837 driver to drive the clock block. Only differences to
+ * BD70528 clock gate are the register address and mask.
+ */
+ { .name = "bd718xx-clk", },
+ { .name = "bd70528-wdt", },
+ {
+ .name = "bd70528-power",
+ .resources = charger_irqs,
+ .num_resources = ARRAY_SIZE(charger_irqs),
+ }, {
+ .name = "bd70528-rtc",
+ .resources = rtc_irqs,
+ .num_resources = ARRAY_SIZE(rtc_irqs),
+ },
+};
+
+static const struct regmap_range volatile_ranges[] = {
+ {
+ .range_min = BD70528_REG_INT_MAIN,
+ .range_max = BD70528_REG_INT_OP_FAIL,
+ }, {
+ .range_min = BD70528_REG_RTC_COUNT_H,
+ .range_max = BD70528_REG_RTC_ALM_REPEAT,
+ }, {
+ /*
+ * WDT control reg is special. Magic values must be written to
+ * it in order to change the control. Should not be cached.
+ */
+ .range_min = BD70528_REG_WDT_CTRL,
+ .range_max = BD70528_REG_WDT_CTRL,
+ }, {
+ /*
+ * BD70528 also contains a few other registers which require
+ * magic sequences to be written in order to update the value.
+ * At least SHIPMODE, HWRESET, WARMRESET,and STANDBY
+ */
+ .range_min = BD70528_REG_SHIPMODE,
+ .range_max = BD70528_REG_STANDBY,
+ },
+};
+
+static const struct regmap_access_table volatile_regs = {
+ .yes_ranges = &volatile_ranges[0],
+ .n_yes_ranges = ARRAY_SIZE(volatile_ranges),
+};
+
+static struct regmap_config bd70528_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &volatile_regs,
+ .max_register = BD70528_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/*
+ * Mapping of main IRQ register bits to sub-IRQ register offsets so that we can
+ * access corect sub-IRQ registers based on bits that are set in main IRQ
+ * register.
+ */
+
+/* bit [0] - Shutdown register */
+unsigned int bit0_offsets[] = {0}; /* Shutdown register */
+unsigned int bit1_offsets[] = {1}; /* Power failure register */
+unsigned int bit2_offsets[] = {2}; /* VR FAULT register */
+unsigned int bit3_offsets[] = {3}; /* PMU register interrupts */
+unsigned int bit4_offsets[] = {4, 5}; /* Charger 1 and Charger 2 registers */
+unsigned int bit5_offsets[] = {6}; /* RTC register */
+unsigned int bit6_offsets[] = {7}; /* GPIO register */
+unsigned int bit7_offsets[] = {8}; /* Invalid operation register */
+
+static struct regmap_irq_sub_irq_map bd70528_sub_irq_offsets[] = {
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit3_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit4_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit5_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit6_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
+};
+
+static struct regmap_irq bd70528_irqs[] = {
+ REGMAP_IRQ_REG(BD70528_INT_LONGPUSH, 0, BD70528_INT_LONGPUSH_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_WDT, 0, BD70528_INT_WDT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_HWRESET, 0, BD70528_INT_HWRESET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_RSTB_FAULT, 0, BD70528_INT_RSTB_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_VBAT_UVLO, 0, BD70528_INT_VBAT_UVLO_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_TSD, 0, BD70528_INT_TSD_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_RSTIN, 0, BD70528_INT_RSTIN_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK1_FAULT, 1,
+ BD70528_INT_BUCK1_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK2_FAULT, 1,
+ BD70528_INT_BUCK2_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK3_FAULT, 1,
+ BD70528_INT_BUCK3_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LDO1_FAULT, 1, BD70528_INT_LDO1_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LDO2_FAULT, 1, BD70528_INT_LDO2_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LDO3_FAULT, 1, BD70528_INT_LDO3_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LED1_FAULT, 1, BD70528_INT_LED1_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LED2_FAULT, 1, BD70528_INT_LED2_FAULT_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK1_OCP, 2, BD70528_INT_BUCK1_OCP_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK2_OCP, 2, BD70528_INT_BUCK2_OCP_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK3_OCP, 2, BD70528_INT_BUCK3_OCP_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LED1_OCP, 2, BD70528_INT_LED1_OCP_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LED2_OCP, 2, BD70528_INT_LED2_OCP_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK1_FULLON, 2,
+ BD70528_INT_BUCK1_FULLON_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK2_FULLON, 2,
+ BD70528_INT_BUCK2_FULLON_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_SHORTPUSH, 3, BD70528_INT_SHORTPUSH_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_AUTO_WAKEUP, 3,
+ BD70528_INT_AUTO_WAKEUP_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_STATE_CHANGE, 3,
+ BD70528_INT_STATE_CHANGE_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BAT_OV_RES, 4, BD70528_INT_BAT_OV_RES_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BAT_OV_DET, 4, BD70528_INT_BAT_OV_DET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_DBAT_DET, 4, BD70528_INT_DBAT_DET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BATTSD_COLD_RES, 4,
+ BD70528_INT_BATTSD_COLD_RES_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BATTSD_COLD_DET, 4,
+ BD70528_INT_BATTSD_COLD_DET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BATTSD_HOT_RES, 4,
+ BD70528_INT_BATTSD_HOT_RES_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BATTSD_HOT_DET, 4,
+ BD70528_INT_BATTSD_HOT_DET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_CHG_TSD, 4, BD70528_INT_CHG_TSD_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BAT_RMV, 5, BD70528_INT_BAT_RMV_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BAT_DET, 5, BD70528_INT_BAT_DET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_DCIN2_OV_RES, 5,
+ BD70528_INT_DCIN2_OV_RES_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_DCIN2_OV_DET, 5,
+ BD70528_INT_DCIN2_OV_DET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_DCIN2_RMV, 5, BD70528_INT_DCIN2_RMV_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_DCIN2_DET, 5, BD70528_INT_DCIN2_DET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_DCIN1_RMV, 5, BD70528_INT_DCIN1_RMV_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_DCIN1_DET, 5, BD70528_INT_DCIN1_DET_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_RTC_ALARM, 6, BD70528_INT_RTC_ALARM_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_ELPS_TIM, 6, BD70528_INT_ELPS_TIM_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_GPIO0, 7, BD70528_INT_GPIO0_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_GPIO1, 7, BD70528_INT_GPIO1_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_GPIO2, 7, BD70528_INT_GPIO2_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_GPIO3, 7, BD70528_INT_GPIO3_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK1_DVS_OPFAIL, 8,
+ BD70528_INT_BUCK1_DVS_OPFAIL_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK2_DVS_OPFAIL, 8,
+ BD70528_INT_BUCK2_DVS_OPFAIL_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_BUCK3_DVS_OPFAIL, 8,
+ BD70528_INT_BUCK3_DVS_OPFAIL_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LED1_VOLT_OPFAIL, 8,
+ BD70528_INT_LED1_VOLT_OPFAIL_MASK),
+ REGMAP_IRQ_REG(BD70528_INT_LED2_VOLT_OPFAIL, 8,
+ BD70528_INT_LED2_VOLT_OPFAIL_MASK),
+};
+
+static struct regmap_irq_chip bd70528_irq_chip = {
+ .name = "bd70528_irq",
+ .main_status = BD70528_REG_INT_MAIN,
+ .irqs = &bd70528_irqs[0],
+ .num_irqs = ARRAY_SIZE(bd70528_irqs),
+ .status_base = BD70528_REG_INT_SHDN,
+ .mask_base = BD70528_REG_INT_SHDN_MASK,
+ .ack_base = BD70528_REG_INT_SHDN,
+ .type_base = BD70528_REG_GPIO1_IN,
+ .init_ack_masked = true,
+ .num_regs = 9,
+ .num_main_regs = 1,
+ .num_type_reg = 4,
+ .sub_reg_offsets = &bd70528_sub_irq_offsets[0],
+ .num_main_status_bits = 8,
+ .irq_reg_stride = 1,
+};
+
+static int bd70528_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct bd70528_data *bd70528;
+ struct regmap_irq_chip_data *irq_data;
+ int ret, i;
+
+ if (!i2c->irq) {
+ dev_err(&i2c->dev, "No IRQ configured\n");
+ return -EINVAL;
+ }
+
+ bd70528 = devm_kzalloc(&i2c->dev, sizeof(*bd70528), GFP_KERNEL);
+ if (!bd70528)
+ return -ENOMEM;
+
+ mutex_init(&bd70528->rtc_timer_lock);
+
+ dev_set_drvdata(&i2c->dev, &bd70528->chip);
+
+ bd70528->chip.chip_type = ROHM_CHIP_TYPE_BD70528;
+ bd70528->chip.regmap = devm_regmap_init_i2c(i2c, &bd70528_regmap);
+ if (IS_ERR(bd70528->chip.regmap)) {
+ dev_err(&i2c->dev, "Failed to initialize Regmap\n");
+ return PTR_ERR(bd70528->chip.regmap);
+ }
+
+ /*
+ * Disallow type setting for all IRQs by default as most of them do not
+ * support setting type.
+ */
+ for (i = 0; i < ARRAY_SIZE(bd70528_irqs); i++)
+ bd70528_irqs[i].type.types_supported = 0;
+
+ /* Set IRQ typesetting information for GPIO pins 0 - 3 */
+ for (i = 0; i < BD70528_NUM_OF_GPIOS; i++) {
+ struct regmap_irq_type *type;
+
+ type = &bd70528_irqs[BD70528_INT_GPIO0 + i].type;
+ type->type_reg_offset = 2 * i;
+ type->type_rising_val = 0x20;
+ type->type_falling_val = 0x10;
+ type->type_level_high_val = 0x40;
+ type->type_level_low_val = 0x50;
+ type->types_supported = (IRQ_TYPE_EDGE_BOTH |
+ IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW);
+ }
+
+ ret = devm_regmap_add_irq_chip(&i2c->dev, bd70528->chip.regmap,
+ i2c->irq, IRQF_ONESHOT, 0,
+ &bd70528_irq_chip, &irq_data);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to add IRQ chip\n");
+ return ret;
+ }
+ dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
+ bd70528_irq_chip.num_irqs);
+
+ /*
+ * BD70528 IRQ controller is not touching the main mask register.
+ * So enable the GPIO block interrupts at main level. We can just leave
+ * them enabled as the IRQ controller should disable IRQs from
+ * sub-registers when IRQ is disabled or freed.
+ */
+ ret = regmap_update_bits(bd70528->chip.regmap,
+ BD70528_REG_INT_MAIN_MASK,
+ BD70528_INT_GPIO_MASK, 0);
+
+ ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
+ bd70528_mfd_cells,
+ ARRAY_SIZE(bd70528_mfd_cells), NULL, 0,
+ regmap_irq_get_domain(irq_data));
+ if (ret)
+ dev_err(&i2c->dev, "Failed to create subdevices\n");
+
+ return ret;
+}
+
+static const struct of_device_id bd70528_of_match[] = {
+ { .compatible = "rohm,bd70528", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bd70528_of_match);
+
+static struct i2c_driver bd70528_drv = {
+ .driver = {
+ .name = "rohm-bd70528",
+ .of_match_table = bd70528_of_match,
+ },
+ .probe = &bd70528_i2c_probe,
+};
+
+module_i2c_driver(bd70528_drv);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("ROHM BD70528 Power Management IC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index a29d529..7beb444 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -98,18 +98,19 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
bd718xx->chip_irq = i2c->irq;
- bd718xx->chip_type = (unsigned int)(uintptr_t)
+ bd718xx->chip.chip_type = (unsigned int)(uintptr_t)
of_device_get_match_data(&i2c->dev);
- bd718xx->dev = &i2c->dev;
+ bd718xx->chip.dev = &i2c->dev;
dev_set_drvdata(&i2c->dev, bd718xx);
- bd718xx->regmap = devm_regmap_init_i2c(i2c, &bd718xx_regmap_config);
- if (IS_ERR(bd718xx->regmap)) {
+ bd718xx->chip.regmap = devm_regmap_init_i2c(i2c,
+ &bd718xx_regmap_config);
+ if (IS_ERR(bd718xx->chip.regmap)) {
dev_err(&i2c->dev, "regmap initialization failed\n");
- return PTR_ERR(bd718xx->regmap);
+ return PTR_ERR(bd718xx->chip.regmap);
}
- ret = devm_regmap_add_irq_chip(&i2c->dev, bd718xx->regmap,
+ ret = devm_regmap_add_irq_chip(&i2c->dev, bd718xx->chip.regmap,
bd718xx->chip_irq, IRQF_ONESHOT, 0,
&bd718xx_irq_chip, &bd718xx->irq_data);
if (ret) {
@@ -118,7 +119,7 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
}
/* Configure short press to 10 milliseconds */
- ret = regmap_update_bits(bd718xx->regmap,
+ ret = regmap_update_bits(bd718xx->chip.regmap,
BD718XX_REG_PWRONCONFIG0,
BD718XX_PWRBTN_PRESS_DURATION_MASK,
BD718XX_PWRBTN_SHORT_PRESS_10MS);
@@ -129,7 +130,7 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
}
/* Configure long press to 10 seconds */
- ret = regmap_update_bits(bd718xx->regmap,
+ ret = regmap_update_bits(bd718xx->chip.regmap,
BD718XX_REG_PWRONCONFIG1,
BD718XX_PWRBTN_PRESS_DURATION_MASK,
BD718XX_PWRBTN_LONG_PRESS_10S);
@@ -149,7 +150,7 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
button.irq = ret;
- ret = devm_mfd_add_devices(bd718xx->dev, PLATFORM_DEVID_AUTO,
+ ret = devm_mfd_add_devices(bd718xx->chip.dev, PLATFORM_DEVID_AUTO,
bd718xx_mfd_cells,
ARRAY_SIZE(bd718xx_mfd_cells), NULL, 0,
regmap_irq_get_domain(bd718xx->irq_data));
@@ -162,11 +163,11 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
static const struct of_device_id bd718xx_of_match[] = {
{
.compatible = "rohm,bd71837",
- .data = (void *)BD718XX_TYPE_BD71837,
+ .data = (void *)ROHM_CHIP_TYPE_BD71837,
},
{
.compatible = "rohm,bd71847",
- .data = (void *)BD718XX_TYPE_BD71847,
+ .data = (void *)ROHM_CHIP_TYPE_BD71847,
},
{ }
};
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 26dacda..045ae83 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -688,4 +688,13 @@
Say Y to enable support for Microchip UCS1002 Programmable
USB Port Power Controller with Charger Emulation.
+config CHARGER_BD70528
+ tristate "ROHM bd70528 charger driver"
+ depends on MFD_ROHM_BD70528
+ default n
+ help
+ Say Y here to enable support for getting battery status
+ information and altering charger configurations from charger
+ block of the ROHM BD70528 Power Management IC.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index f208273..346a8ef 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -90,3 +90,4 @@
obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o
obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
+obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o
diff --git a/drivers/power/supply/bd70528-charger.c b/drivers/power/supply/bd70528-charger.c
new file mode 100644
index 0000000..1bb32b7
--- /dev/null
+++ b/drivers/power/supply/bd70528-charger.c
@@ -0,0 +1,743 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Copyright (C) 2018 ROHM Semiconductors
+//
+// power-supply driver for ROHM BD70528 PMIC
+
+/*
+ * BD70528 charger HW state machine.
+ *
+ * The thermal shutdown state is not drawn. From any other state but
+ * battery error and suspend it is possible to go to TSD/TMP states
+ * if temperature is out of bounds.
+ *
+ * CHG_RST = H
+ * or CHG_EN=L
+ * or (DCIN2_UVLO=L && DCIN1_UVLO=L)
+ * or (DCIN2_OVLO=H & DCIN1_UVKLO=L)
+ *
+ * +--------------+ +--------------+
+ * | | | |
+ * | Any state +-------> | Suspend |
+ * | | | |
+ * +--------------+ +------+-------+
+ * |
+ * CHG_EN = H && BAT_DET = H && |
+ * No errors (temp, bat_ov, UVLO, |
+ * OVLO...) |
+ * |
+ * BAT_OV or +---------v----------+
+ * (DBAT && TTRI) | |
+ * +-----------------+ Trickle Charge | <---------------+
+ * | | | |
+ * | +-------+------------+ |
+ * | | |
+ * | | ^ |
+ * | V_BAT > VTRI_TH | | VBAT < VTRI_TH - 50mV |
+ * | | | |
+ * | v | |
+ * | | |
+ * | BAT_OV or +----------+----+ |
+ * | (DBAT && TFST) | | |
+ * | +----------------+ Fast Charge | |
+ * | | | | |
+ * v v +----+----------+ |
+ * | |
+ *+----------------+ ILIM_DET=L | ^ ILIM_DET |
+ *| | & CV_DET=H | | or CV_DET=L |
+ *| Battery Error | & VBAT > | | or VBAT < VRECHG_TH |
+ *| | VRECHG_TH | | or IBAT > IFST/x |
+ *+----------------+ & IBAT < | | |
+ * IFST/x v | |
+ * ^ | |
+ * | +---------+-+ |
+ * | | | |
+ * +-------------------+ Top OFF | |
+ * BAT_OV = H or | | |
+ * (DBAT && TFST) +-----+-----+ |
+ * | |
+ * Stay top-off for 15s | |
+ * v |
+ * |
+ * +--------+ |
+ * | | |
+ * | Done +-------------------------+
+ * | |
+ * +--------+ VBAT < VRECHG_TH
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/rohm-bd70528.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#define CHG_STAT_SUSPEND 0x0
+#define CHG_STAT_TRICKLE 0x1
+#define CHG_STAT_FAST 0x3
+#define CHG_STAT_TOPOFF 0xe
+#define CHG_STAT_DONE 0xf
+#define CHG_STAT_OTP_TRICKLE 0x10
+#define CHG_STAT_OTP_FAST 0x11
+#define CHG_STAT_OTP_DONE 0x12
+#define CHG_STAT_TSD_TRICKLE 0x20
+#define CHG_STAT_TSD_FAST 0x21
+#define CHG_STAT_TSD_TOPOFF 0x22
+#define CHG_STAT_BAT_ERR 0x7f
+
+static const char *bd70528_charger_model = "BD70528";
+static const char *bd70528_charger_manufacturer = "ROHM Semiconductors";
+
+#define BD_ERR_IRQ_HND(_name_, _wrn_) \
+static irqreturn_t bd0528_##_name_##_interrupt(int irq, void *arg) \
+{ \
+ struct power_supply *psy = (struct power_supply *)arg; \
+ \
+ power_supply_changed(psy); \
+ dev_err(&psy->dev, (_wrn_)); \
+ \
+ return IRQ_HANDLED; \
+}
+
+#define BD_INFO_IRQ_HND(_name_, _wrn_) \
+static irqreturn_t bd0528_##_name_##_interrupt(int irq, void *arg) \
+{ \
+ struct power_supply *psy = (struct power_supply *)arg; \
+ \
+ power_supply_changed(psy); \
+ dev_dbg(&psy->dev, (_wrn_)); \
+ \
+ return IRQ_HANDLED; \
+}
+
+#define BD_IRQ_HND(_name_) bd0528_##_name_##_interrupt
+
+struct bd70528_psy {
+ struct regmap *regmap;
+ struct device *dev;
+ struct power_supply *psy;
+};
+
+BD_ERR_IRQ_HND(BAT_OV_DET, "Battery overvoltage detected\n");
+BD_ERR_IRQ_HND(DBAT_DET, "Dead battery detected\n");
+BD_ERR_IRQ_HND(COLD_DET, "Battery cold\n");
+BD_ERR_IRQ_HND(HOT_DET, "Battery hot\n");
+BD_ERR_IRQ_HND(CHG_TSD, "Charger thermal shutdown\n");
+BD_ERR_IRQ_HND(DCIN2_OV_DET, "DCIN2 overvoltage detected\n");
+
+BD_INFO_IRQ_HND(BAT_OV_RES, "Battery voltage back to normal\n");
+BD_INFO_IRQ_HND(COLD_RES, "Battery temperature back to normal\n");
+BD_INFO_IRQ_HND(HOT_RES, "Battery temperature back to normal\n");
+BD_INFO_IRQ_HND(BAT_RMV, "Battery removed\n");
+BD_INFO_IRQ_HND(BAT_DET, "Battery detected\n");
+BD_INFO_IRQ_HND(DCIN2_OV_RES, "DCIN2 voltage back to normal\n");
+BD_INFO_IRQ_HND(DCIN2_RMV, "DCIN2 removed\n");
+BD_INFO_IRQ_HND(DCIN2_DET, "DCIN2 detected\n");
+BD_INFO_IRQ_HND(DCIN1_RMV, "DCIN1 removed\n");
+BD_INFO_IRQ_HND(DCIN1_DET, "DCIN1 detected\n");
+
+struct irq_name_pair {
+ const char *n;
+ irqreturn_t (*h)(int irq, void *arg);
+};
+
+static int bd70528_get_irqs(struct platform_device *pdev,
+ struct bd70528_psy *bdpsy)
+{
+ int irq, i, ret;
+ unsigned int mask;
+ static const struct irq_name_pair bd70528_chg_irqs[] = {
+ { .n = "bd70528-bat-ov-res", .h = BD_IRQ_HND(BAT_OV_RES) },
+ { .n = "bd70528-bat-ov-det", .h = BD_IRQ_HND(BAT_OV_DET) },
+ { .n = "bd70528-bat-dead", .h = BD_IRQ_HND(DBAT_DET) },
+ { .n = "bd70528-bat-warmed", .h = BD_IRQ_HND(COLD_RES) },
+ { .n = "bd70528-bat-cold", .h = BD_IRQ_HND(COLD_DET) },
+ { .n = "bd70528-bat-cooled", .h = BD_IRQ_HND(HOT_RES) },
+ { .n = "bd70528-bat-hot", .h = BD_IRQ_HND(HOT_DET) },
+ { .n = "bd70528-chg-tshd", .h = BD_IRQ_HND(CHG_TSD) },
+ { .n = "bd70528-bat-removed", .h = BD_IRQ_HND(BAT_RMV) },
+ { .n = "bd70528-bat-detected", .h = BD_IRQ_HND(BAT_DET) },
+ { .n = "bd70528-dcin2-ov-res", .h = BD_IRQ_HND(DCIN2_OV_RES) },
+ { .n = "bd70528-dcin2-ov-det", .h = BD_IRQ_HND(DCIN2_OV_DET) },
+ { .n = "bd70528-dcin2-removed", .h = BD_IRQ_HND(DCIN2_RMV) },
+ { .n = "bd70528-dcin2-detected", .h = BD_IRQ_HND(DCIN2_DET) },
+ { .n = "bd70528-dcin1-removed", .h = BD_IRQ_HND(DCIN1_RMV) },
+ { .n = "bd70528-dcin1-detected", .h = BD_IRQ_HND(DCIN1_DET) },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(bd70528_chg_irqs); i++) {
+ irq = platform_get_irq_byname(pdev, bd70528_chg_irqs[i].n);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Bad IRQ information for %s (%d)\n",
+ bd70528_chg_irqs[i].n, irq);
+ return irq;
+ }
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ bd70528_chg_irqs[i].h,
+ IRQF_ONESHOT,
+ bd70528_chg_irqs[i].n,
+ bdpsy->psy);
+
+ if (ret)
+ return ret;
+ }
+ /*
+ * BD70528 irq controller is not touching the main mask register.
+ * So enable the charger block interrupts at main level. We can just
+ * leave them enabled as irq-controller should disable irqs
+ * from sub-registers when IRQ is disabled or freed.
+ */
+ mask = BD70528_REG_INT_BAT1_MASK | BD70528_REG_INT_BAT2_MASK;
+ ret = regmap_update_bits(bdpsy->regmap,
+ BD70528_REG_INT_MAIN_MASK, mask, 0);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to enable charger IRQs\n");
+
+ return ret;
+}
+
+static int bd70528_get_charger_status(struct bd70528_psy *bdpsy, int *val)
+{
+ int ret;
+ unsigned int v;
+
+ ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CURR_STAT, &v);
+ if (ret) {
+ dev_err(bdpsy->dev, "Charger state read failure %d\n",
+ ret);
+ return ret;
+ }
+
+ switch (v & BD70528_MASK_CHG_STAT) {
+ case CHG_STAT_SUSPEND:
+ /* Maybe we should check the CHG_TTRI_EN? */
+ case CHG_STAT_OTP_TRICKLE:
+ case CHG_STAT_OTP_FAST:
+ case CHG_STAT_OTP_DONE:
+ case CHG_STAT_TSD_TRICKLE:
+ case CHG_STAT_TSD_FAST:
+ case CHG_STAT_TSD_TOPOFF:
+ case CHG_STAT_BAT_ERR:
+ *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case CHG_STAT_DONE:
+ *val = POWER_SUPPLY_STATUS_FULL;
+ break;
+ case CHG_STAT_TRICKLE:
+ case CHG_STAT_FAST:
+ case CHG_STAT_TOPOFF:
+ *val = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ default:
+ *val = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int bd70528_get_charge_type(struct bd70528_psy *bdpsy, int *val)
+{
+ int ret;
+ unsigned int v;
+
+ ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CURR_STAT, &v);
+ if (ret) {
+ dev_err(bdpsy->dev, "Charger state read failure %d\n",
+ ret);
+ return ret;
+ }
+
+ switch (v & BD70528_MASK_CHG_STAT) {
+ case CHG_STAT_TRICKLE:
+ *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case CHG_STAT_FAST:
+ case CHG_STAT_TOPOFF:
+ *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ break;
+ case CHG_STAT_DONE:
+ case CHG_STAT_SUSPEND:
+ /* Maybe we should check the CHG_TTRI_EN? */
+ case CHG_STAT_OTP_TRICKLE:
+ case CHG_STAT_OTP_FAST:
+ case CHG_STAT_OTP_DONE:
+ case CHG_STAT_TSD_TRICKLE:
+ case CHG_STAT_TSD_FAST:
+ case CHG_STAT_TSD_TOPOFF:
+ case CHG_STAT_BAT_ERR:
+ *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ default:
+ *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int bd70528_get_battery_health(struct bd70528_psy *bdpsy, int *val)
+{
+ int ret;
+ unsigned int v;
+
+ ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_BAT_STAT, &v);
+ if (ret) {
+ dev_err(bdpsy->dev, "Battery state read failure %d\n",
+ ret);
+ return ret;
+ }
+ /* No battery? */
+ if (!(v & BD70528_MASK_CHG_BAT_DETECT))
+ *val = POWER_SUPPLY_HEALTH_DEAD;
+ else if (v & BD70528_MASK_CHG_BAT_OVERVOLT)
+ *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else if (v & BD70528_MASK_CHG_BAT_TIMER)
+ *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+ else
+ *val = POWER_SUPPLY_HEALTH_GOOD;
+
+ return 0;
+}
+
+static int bd70528_get_online(struct bd70528_psy *bdpsy, int *val)
+{
+ int ret;
+ unsigned int v;
+
+ ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_IN_STAT, &v);
+ if (ret) {
+ dev_err(bdpsy->dev, "DC1 IN state read failure %d\n",
+ ret);
+ return ret;
+ }
+
+ *val = (v & BD70528_MASK_CHG_DCIN1_UVLO) ? 1 : 0;
+
+ return 0;
+}
+
+static int bd70528_get_present(struct bd70528_psy *bdpsy, int *val)
+{
+ int ret;
+ unsigned int v;
+
+ ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_BAT_STAT, &v);
+ if (ret) {
+ dev_err(bdpsy->dev, "Battery state read failure %d\n",
+ ret);
+ return ret;
+ }
+
+ *val = (v & BD70528_MASK_CHG_BAT_DETECT) ? 1 : 0;
+
+ return 0;
+}
+
+struct linear_range {
+ int min;
+ int step;
+ int vals;
+ int low_sel;
+};
+
+static const struct linear_range current_limit_ranges[] = {
+ {
+ .min = 5,
+ .step = 1,
+ .vals = 36,
+ .low_sel = 0,
+ },
+ {
+ .min = 40,
+ .step = 5,
+ .vals = 5,
+ .low_sel = 0x23,
+ },
+ {
+ .min = 60,
+ .step = 20,
+ .vals = 8,
+ .low_sel = 0x27,
+ },
+ {
+ .min = 200,
+ .step = 50,
+ .vals = 7,
+ .low_sel = 0x2e,
+ }
+};
+
+/*
+ * BD70528 would support setting and getting own charge current/
+ * voltage for low temperatures. The driver currently only reads
+ * the charge current at room temperature. We do set both though.
+ */
+static const struct linear_range warm_charge_curr[] = {
+ {
+ .min = 10,
+ .step = 10,
+ .vals = 20,
+ .low_sel = 0,
+ },
+ {
+ .min = 200,
+ .step = 25,
+ .vals = 13,
+ .low_sel = 0x13,
+ },
+};
+
+/*
+ * Cold charge current selectors are identical to warm charge current
+ * selectors. The difference is that only smaller currents are available
+ * at cold charge range.
+ */
+#define MAX_COLD_CHG_CURR_SEL 0x15
+#define MAX_WARM_CHG_CURR_SEL 0x1f
+#define MIN_CHG_CURR_SEL 0x0
+
+static int find_value_for_selector_low(const struct linear_range *r,
+ int selectors, unsigned int sel,
+ unsigned int *val)
+{
+ int i;
+
+ for (i = 0; i < selectors; i++) {
+ if (r[i].low_sel <= sel && r[i].low_sel + r[i].vals >= sel) {
+ *val = r[i].min + (sel - r[i].low_sel) * r[i].step;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/*
+ * For BD70528 voltage/current limits we happily accept any value which
+ * belongs the range. We could check if value matching the selector is
+ * desired by computing the range min + (sel - sel_low) * range step - but
+ * I guess it is enough if we use voltage/current which is closest (below)
+ * the requested?
+ */
+static int find_selector_for_value_low(const struct linear_range *r,
+ int selectors, unsigned int val,
+ unsigned int *sel, bool *found)
+{
+ int i;
+ int ret = -EINVAL;
+
+ *found = false;
+ for (i = 0; i < selectors; i++) {
+ if (r[i].min <= val) {
+ if (r[i].min + r[i].step * r[i].vals >= val) {
+ *found = true;
+ *sel = r[i].low_sel + (val - r[i].min) /
+ r[i].step;
+ ret = 0;
+ break;
+ }
+ /*
+ * If the range max is smaller than requested
+ * we can set the max supported value from range
+ */
+ *sel = r[i].low_sel + r[i].vals;
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int get_charge_current(struct bd70528_psy *bdpsy, int *ma)
+{
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CHG_CURR_WARM,
+ &sel);
+ if (ret) {
+ dev_err(bdpsy->dev,
+ "Charge current reading failed (%d)\n", ret);
+ return ret;
+ }
+
+ sel &= BD70528_MASK_CHG_CHG_CURR;
+
+ ret = find_value_for_selector_low(&warm_charge_curr[0],
+ ARRAY_SIZE(warm_charge_curr), sel,
+ ma);
+ if (ret) {
+ dev_err(bdpsy->dev,
+ "Unknown charge current value 0x%x\n",
+ sel);
+ }
+
+ return ret;
+}
+
+static int get_current_limit(struct bd70528_psy *bdpsy, int *ma)
+{
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_DCIN_ILIM,
+ &sel);
+
+ if (ret) {
+ dev_err(bdpsy->dev,
+ "Input current limit reading failed (%d)\n", ret);
+ return ret;
+ }
+
+ sel &= BD70528_MASK_CHG_DCIN_ILIM;
+
+ ret = find_value_for_selector_low(¤t_limit_ranges[0],
+ ARRAY_SIZE(current_limit_ranges), sel,
+ ma);
+
+ if (ret) {
+ /* Unspecified values mean 500 mA */
+ *ma = 500;
+ }
+ return 0;
+}
+
+static enum power_supply_property bd70528_charger_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static int bd70528_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct bd70528_psy *bdpsy = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ return bd70528_get_charger_status(bdpsy, &val->intval);
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ return bd70528_get_charge_type(bdpsy, &val->intval);
+ case POWER_SUPPLY_PROP_HEALTH:
+ return bd70528_get_battery_health(bdpsy, &val->intval);
+ case POWER_SUPPLY_PROP_PRESENT:
+ return bd70528_get_present(bdpsy, &val->intval);
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = get_current_limit(bdpsy, &val->intval);
+ val->intval *= 1000;
+ return ret;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = get_charge_current(bdpsy, &val->intval);
+ val->intval *= 1000;
+ return ret;
+ case POWER_SUPPLY_PROP_ONLINE:
+ return bd70528_get_online(bdpsy, &val->intval);
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = bd70528_charger_model;
+ return 0;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = bd70528_charger_manufacturer;
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int bd70528_prop_is_writable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int set_charge_current(struct bd70528_psy *bdpsy, int ma)
+{
+ unsigned int reg;
+ int ret = 0, tmpret;
+ bool found;
+
+ if (ma > 500) {
+ dev_warn(bdpsy->dev,
+ "Requested charge current %u exceed maximum (500mA)\n",
+ ma);
+ reg = MAX_WARM_CHG_CURR_SEL;
+ goto set;
+ }
+ if (ma < 10) {
+ dev_err(bdpsy->dev,
+ "Requested charge current %u smaller than min (10mA)\n",
+ ma);
+ reg = MIN_CHG_CURR_SEL;
+ ret = -EINVAL;
+ goto set;
+ }
+
+ ret = find_selector_for_value_low(&warm_charge_curr[0],
+ ARRAY_SIZE(warm_charge_curr), ma,
+ ®, &found);
+ if (ret) {
+ reg = MIN_CHG_CURR_SEL;
+ goto set;
+ }
+ if (!found) {
+ /* There was a gap in supported values and we hit it */
+ dev_warn(bdpsy->dev,
+ "Unsupported charge current %u mA\n", ma);
+ }
+set:
+
+ tmpret = regmap_update_bits(bdpsy->regmap,
+ BD70528_REG_CHG_CHG_CURR_WARM,
+ BD70528_MASK_CHG_CHG_CURR, reg);
+ if (tmpret)
+ dev_err(bdpsy->dev,
+ "Charge current write failure (%d)\n", tmpret);
+
+ if (reg > MAX_COLD_CHG_CURR_SEL)
+ reg = MAX_COLD_CHG_CURR_SEL;
+
+ if (!tmpret)
+ tmpret = regmap_update_bits(bdpsy->regmap,
+ BD70528_REG_CHG_CHG_CURR_COLD,
+ BD70528_MASK_CHG_CHG_CURR, reg);
+
+ if (!ret)
+ ret = tmpret;
+
+ return ret;
+}
+
+#define MAX_CURR_LIMIT_SEL 0x34
+#define MIN_CURR_LIMIT_SEL 0x0
+
+static int set_current_limit(struct bd70528_psy *bdpsy, int ma)
+{
+ unsigned int reg;
+ int ret = 0, tmpret;
+ bool found;
+
+ if (ma > 500) {
+ dev_warn(bdpsy->dev,
+ "Requested current limit %u exceed maximum (500mA)\n",
+ ma);
+ reg = MAX_CURR_LIMIT_SEL;
+ goto set;
+ }
+ if (ma < 5) {
+ dev_err(bdpsy->dev,
+ "Requested current limit %u smaller than min (5mA)\n",
+ ma);
+ reg = MIN_CURR_LIMIT_SEL;
+ ret = -EINVAL;
+ goto set;
+ }
+
+ ret = find_selector_for_value_low(¤t_limit_ranges[0],
+ ARRAY_SIZE(current_limit_ranges), ma,
+ ®, &found);
+ if (ret) {
+ reg = MIN_CURR_LIMIT_SEL;
+ goto set;
+ }
+ if (!found) {
+ /* There was a gap in supported values and we hit it ?*/
+ dev_warn(bdpsy->dev, "Unsupported current limit %umA\n",
+ ma);
+ }
+
+set:
+ tmpret = regmap_update_bits(bdpsy->regmap,
+ BD70528_REG_CHG_DCIN_ILIM,
+ BD70528_MASK_CHG_DCIN_ILIM, reg);
+
+ if (!ret)
+ ret = tmpret;
+
+ return ret;
+}
+
+static int bd70528_charger_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct bd70528_psy *bdpsy = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ return set_current_limit(bdpsy, val->intval / 1000);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ return set_charge_current(bdpsy, val->intval / 1000);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static const struct power_supply_desc bd70528_charger_desc = {
+ .name = "bd70528-charger",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = bd70528_charger_props,
+ .num_properties = ARRAY_SIZE(bd70528_charger_props),
+ .get_property = bd70528_charger_get_property,
+ .set_property = bd70528_charger_set_property,
+ .property_is_writeable = bd70528_prop_is_writable,
+};
+
+static int bd70528_power_probe(struct platform_device *pdev)
+{
+ struct bd70528_psy *bdpsy;
+ struct power_supply_config cfg = {};
+
+ bdpsy = devm_kzalloc(&pdev->dev, sizeof(*bdpsy), GFP_KERNEL);
+ if (!bdpsy)
+ return -ENOMEM;
+
+ bdpsy->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!bdpsy->regmap) {
+ dev_err(&pdev->dev, "No regmap found for chip\n");
+ return -EINVAL;
+ }
+ bdpsy->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, bdpsy);
+ cfg.drv_data = bdpsy;
+ cfg.of_node = pdev->dev.parent->of_node;
+
+ bdpsy->psy = devm_power_supply_register(&pdev->dev,
+ &bd70528_charger_desc, &cfg);
+ if (IS_ERR(bdpsy->psy)) {
+ dev_err(&pdev->dev, "failed: power supply register\n");
+ return PTR_ERR(bdpsy->psy);
+ }
+
+ return bd70528_get_irqs(pdev, bdpsy);
+}
+
+static struct platform_driver bd70528_power = {
+ .driver = {
+ .name = "bd70528-power"
+ },
+ .probe = bd70528_power_probe,
+};
+
+module_platform_driver(bd70528_power);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD70528 power-supply driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6c37f0d..214a958 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -762,11 +762,11 @@
outputs which can be controlled by i2c communication.
config REGULATOR_RK808
- tristate "Rockchip RK805/RK808/RK818 Power regulators"
+ tristate "Rockchip RK805/RK808/RK809/RK817/RK818 Power regulators"
depends on MFD_RK808
help
Select this option to enable the power regulator of ROCKCHIP
- PMIC RK805,RK808 and RK818.
+ PMIC RK805,RK809&RK817,RK808 and RK818.
This driver supports the control of different power rails of device
through regulator interface. The device supports multiple DCDC/LDO
outputs which can be controlled by i2c communication.
diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c
index fde4264..ef2fc17 100644
--- a/drivers/regulator/bd718x7-regulator.c
+++ b/drivers/regulator/bd718x7-regulator.c
@@ -1152,12 +1152,12 @@ static int bd718xx_probe(struct platform_device *pdev)
{
struct bd718xx *mfd;
struct regulator_config config = { 0 };
- struct bd718xx_pmic_inits pmic_regulators[] = {
- [BD718XX_TYPE_BD71837] = {
+ struct bd718xx_pmic_inits pmic_regulators[ROHM_CHIP_TYPE_AMOUNT] = {
+ [ROHM_CHIP_TYPE_BD71837] = {
.r_datas = bd71837_regulators,
.r_amount = ARRAY_SIZE(bd71837_regulators),
},
- [BD718XX_TYPE_BD71847] = {
+ [ROHM_CHIP_TYPE_BD71847] = {
.r_datas = bd71847_regulators,
.r_amount = ARRAY_SIZE(bd71847_regulators),
},
@@ -1173,15 +1173,15 @@ static int bd718xx_probe(struct platform_device *pdev)
goto err;
}
- if (mfd->chip_type >= BD718XX_TYPE_AMOUNT ||
- !pmic_regulators[mfd->chip_type].r_datas) {
+ if (mfd->chip.chip_type >= ROHM_CHIP_TYPE_AMOUNT ||
+ !pmic_regulators[mfd->chip.chip_type].r_datas) {
dev_err(&pdev->dev, "Unsupported chip type\n");
err = -EINVAL;
goto err;
}
/* Register LOCK release */
- err = regmap_update_bits(mfd->regmap, BD718XX_REG_REGLOCK,
+ err = regmap_update_bits(mfd->chip.regmap, BD718XX_REG_REGLOCK,
(REGLOCK_PWRSEQ | REGLOCK_VREG), 0);
if (err) {
dev_err(&pdev->dev, "Failed to unlock PMIC (%d)\n", err);
@@ -1200,7 +1200,8 @@ static int bd718xx_probe(struct platform_device *pdev)
* bit allowing HW defaults for power rails to be used
*/
if (!use_snvs) {
- err = regmap_update_bits(mfd->regmap, BD718XX_REG_TRANS_COND1,
+ err = regmap_update_bits(mfd->chip.regmap,
+ BD718XX_REG_TRANS_COND1,
BD718XX_ON_REQ_POWEROFF_MASK |
BD718XX_SWRESET_POWEROFF_MASK |
BD718XX_WDOG_POWEROFF_MASK |
@@ -1215,17 +1216,17 @@ static int bd718xx_probe(struct platform_device *pdev)
}
}
- for (i = 0; i < pmic_regulators[mfd->chip_type].r_amount; i++) {
+ for (i = 0; i < pmic_regulators[mfd->chip.chip_type].r_amount; i++) {
const struct regulator_desc *desc;
struct regulator_dev *rdev;
const struct bd718xx_regulator_data *r;
- r = &pmic_regulators[mfd->chip_type].r_datas[i];
+ r = &pmic_regulators[mfd->chip.chip_type].r_datas[i];
desc = &r->desc;
config.dev = pdev->dev.parent;
- config.regmap = mfd->regmap;
+ config.regmap = mfd->chip.regmap;
rdev = devm_regulator_register(&pdev->dev, desc, &config);
if (IS_ERR(rdev)) {
@@ -1254,7 +1255,7 @@ static int bd718xx_probe(struct platform_device *pdev)
*/
if (!use_snvs || !rdev->constraints->always_on ||
!rdev->constraints->boot_on) {
- err = regmap_update_bits(mfd->regmap, r->init.reg,
+ err = regmap_update_bits(mfd->chip.regmap, r->init.reg,
r->init.mask, r->init.val);
if (err) {
dev_err(&pdev->dev,
@@ -1264,7 +1265,7 @@ static int bd718xx_probe(struct platform_device *pdev)
}
}
for (j = 0; j < r->additional_init_amnt; j++) {
- err = regmap_update_bits(mfd->regmap,
+ err = regmap_update_bits(mfd->chip.regmap,
r->additional_inits[j].reg,
r->additional_inits[j].mask,
r->additional_inits[j].val);
diff --git a/drivers/regulator/lp87565-regulator.c b/drivers/regulator/lp87565-regulator.c
index 81eb4b8..142b6f1 100644
--- a/drivers/regulator/lp87565-regulator.c
+++ b/drivers/regulator/lp87565-regulator.c
@@ -153,6 +153,12 @@ static const struct lp87565_regulator regulators[] = {
LP87565_REG_BUCK2_CTRL_1,
LP87565_BUCK_CTRL_1_EN, 3230,
buck0_1_2_3_ranges, LP87565_REG_BUCK2_CTRL_2),
+ LP87565_REGULATOR("BUCK3210", LP87565_BUCK_3210, "buck3210",
+ lp87565_buck_ops, 256, LP87565_REG_BUCK0_VOUT,
+ LP87565_BUCK_VSET, LP87565_REG_BUCK0_CTRL_1,
+ LP87565_BUCK_CTRL_1_EN |
+ LP87565_BUCK_CTRL_1_FPWM_MP_0_2, 3230,
+ buck0_1_2_3_ranges, LP87565_REG_BUCK0_CTRL_2),
};
static int lp87565_regulator_probe(struct platform_device *pdev)
@@ -169,9 +175,19 @@ static int lp87565_regulator_probe(struct platform_device *pdev)
config.driver_data = lp87565;
config.regmap = lp87565->regmap;
- if (lp87565->dev_type == LP87565_DEVICE_TYPE_LP87565_Q1) {
+ switch (lp87565->dev_type) {
+ case LP87565_DEVICE_TYPE_LP87565_Q1:
min_idx = LP87565_BUCK_10;
max_idx = LP87565_BUCK_23;
+ break;
+ case LP87565_DEVICE_TYPE_LP87561_Q1:
+ min_idx = LP87565_BUCK_3210;
+ max_idx = LP87565_BUCK_3210;
+ break;
+ default:
+ dev_err(lp87565->dev, "Invalid lp config %d\n",
+ lp87565->dev_type);
+ return -EINVAL;
}
for (i = min_idx; i <= max_idx; i++) {
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
index 23713e1..e9b0bb9 100644
--- a/drivers/regulator/rk808-regulator.c
+++ b/drivers/regulator/rk808-regulator.c
@@ -36,6 +36,12 @@
#define RK808_BUCK4_VSEL_MASK 0xf
#define RK808_LDO_VSEL_MASK 0x1f
+#define RK809_BUCK5_VSEL_MASK 0x7
+
+#define RK817_LDO_VSEL_MASK 0x7f
+#define RK817_BOOST_VSEL_MASK 0x7
+#define RK817_BUCK_VSEL_MASK 0x7f
+
#define RK818_BUCK_VSEL_MASK 0x3f
#define RK818_BUCK4_VSEL_MASK 0x1f
#define RK818_LDO_VSEL_MASK 0x1f
@@ -65,30 +71,12 @@
/* max steps for increase voltage of Buck1/2, equal 100mv*/
#define MAX_STEPS_ONE_TIME 8
-#define RK805_DESC(_id, _match, _supply, _min, _max, _step, _vreg, \
- _vmask, _ereg, _emask, _etime) \
- [_id] = { \
- .name = (_match), \
- .supply_name = (_supply), \
- .of_match = of_match_ptr(_match), \
- .regulators_node = of_match_ptr("regulators"), \
- .type = REGULATOR_VOLTAGE, \
- .id = (_id), \
- .n_voltages = (((_max) - (_min)) / (_step) + 1), \
- .owner = THIS_MODULE, \
- .min_uV = (_min) * 1000, \
- .uV_step = (_step) * 1000, \
- .vsel_reg = (_vreg), \
- .vsel_mask = (_vmask), \
- .enable_reg = (_ereg), \
- .enable_mask = (_emask), \
- .enable_time = (_etime), \
- .ops = &rk805_reg_ops, \
- }
+#define ENABLE_MASK(id) (BIT(id) | BIT(4 + (id)))
+#define DISABLE_VAL(id) (BIT(4 + (id)))
-#define RK8XX_DESC(_id, _match, _supply, _min, _max, _step, _vreg, \
- _vmask, _ereg, _emask, _etime) \
- [_id] = { \
+#define RK817_BOOST_DESC(_id, _match, _supply, _min, _max, _step, _vreg,\
+ _vmask, _ereg, _emask, _enval, _disval, _etime, m_drop) \
+ { \
.name = (_match), \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
@@ -103,12 +91,54 @@
.vsel_mask = (_vmask), \
.enable_reg = (_ereg), \
.enable_mask = (_emask), \
+ .enable_val = (_enval), \
+ .disable_val = (_disval), \
.enable_time = (_etime), \
- .ops = &rk808_reg_ops, \
+ .min_dropout_uV = (m_drop) * 1000, \
+ .ops = &rk817_boost_ops, \
}
-#define RK8XX_DESC_SWITCH(_id, _match, _supply, _ereg, _emask) \
- [_id] = { \
+#define RK8XX_DESC_COM(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _enval, _disval, _etime, _ops) \
+ { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = (_id), \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .enable_val = (_enval), \
+ .disable_val = (_disval), \
+ .enable_time = (_etime), \
+ .ops = _ops, \
+ }
+
+#define RK805_DESC(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _etime) \
+ RK8XX_DESC_COM(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, 0, 0, _etime, &rk805_reg_ops)
+
+#define RK8XX_DESC(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _etime) \
+ RK8XX_DESC_COM(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, 0, 0, _etime, &rk808_reg_ops)
+
+#define RK817_DESC(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _disval, _etime) \
+ RK8XX_DESC_COM(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _emask, _disval, _etime, &rk817_reg_ops)
+
+#define RKXX_DESC_SWITCH_COM(_id, _match, _supply, _ereg, _emask, \
+ _enval, _disval, _ops) \
+ { \
.name = (_match), \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
@@ -117,10 +147,20 @@
.id = (_id), \
.enable_reg = (_ereg), \
.enable_mask = (_emask), \
+ .enable_val = (_enval), \
+ .disable_val = (_disval), \
.owner = THIS_MODULE, \
- .ops = &rk808_switch_ops \
+ .ops = _ops \
}
+#define RK817_DESC_SWITCH(_id, _match, _supply, _ereg, _emask, \
+ _disval) \
+ RKXX_DESC_SWITCH_COM(_id, _match, _supply, _ereg, _emask, \
+ _emask, _disval, &rk817_switch_ops)
+
+#define RK8XX_DESC_SWITCH(_id, _match, _supply, _ereg, _emask) \
+ RKXX_DESC_SWITCH_COM(_id, _match, _supply, _ereg, _emask, \
+ 0, 0, &rk808_switch_ops)
struct rk808_regulator_data {
struct gpio_desc *dvs_gpio[2];
@@ -138,6 +178,51 @@ static const struct regulator_linear_range rk808_ldo3_voltage_ranges[] = {
REGULATOR_LINEAR_RANGE(2500000, 15, 15, 0),
};
+#define RK809_BUCK5_SEL_CNT (8)
+
+static const struct regulator_linear_range rk809_buck5_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1500000, 0, 0, 0),
+ REGULATOR_LINEAR_RANGE(1800000, 1, 3, 200000),
+ REGULATOR_LINEAR_RANGE(2800000, 4, 5, 200000),
+ REGULATOR_LINEAR_RANGE(3300000, 6, 7, 300000),
+};
+
+#define RK817_BUCK1_MIN0 500000
+#define RK817_BUCK1_MAX0 1500000
+
+#define RK817_BUCK1_MIN1 1600000
+#define RK817_BUCK1_MAX1 2400000
+
+#define RK817_BUCK3_MAX1 3400000
+
+#define RK817_BUCK1_STP0 12500
+#define RK817_BUCK1_STP1 100000
+
+#define RK817_BUCK1_SEL0 ((RK817_BUCK1_MAX0 - RK817_BUCK1_MIN0) /\
+ RK817_BUCK1_STP0)
+#define RK817_BUCK1_SEL1 ((RK817_BUCK1_MAX1 - RK817_BUCK1_MIN1) /\
+ RK817_BUCK1_STP1)
+
+#define RK817_BUCK3_SEL1 ((RK817_BUCK3_MAX1 - RK817_BUCK1_MIN1) /\
+ RK817_BUCK1_STP1)
+
+#define RK817_BUCK1_SEL_CNT (RK817_BUCK1_SEL0 + RK817_BUCK1_SEL1 + 1)
+#define RK817_BUCK3_SEL_CNT (RK817_BUCK1_SEL0 + RK817_BUCK3_SEL1 + 1)
+
+static const struct regulator_linear_range rk817_buck1_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN0, 0,
+ RK817_BUCK1_SEL0, RK817_BUCK1_STP0),
+ REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN1, RK817_BUCK1_SEL0 + 1,
+ RK817_BUCK1_SEL_CNT, RK817_BUCK1_STP1),
+};
+
+static const struct regulator_linear_range rk817_buck3_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN0, 0,
+ RK817_BUCK1_SEL0, RK817_BUCK1_STP0),
+ REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN1, RK817_BUCK1_SEL0 + 1,
+ RK817_BUCK3_SEL_CNT, RK817_BUCK1_STP1),
+};
+
static int rk808_buck1_2_get_voltage_sel_regmap(struct regulator_dev *rdev)
{
struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev);
@@ -289,6 +374,36 @@ static int rk808_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
RK808_RAMP_RATE_MASK, ramp_value);
}
+/*
+ * RK817 RK809
+ */
+static int rk817_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ unsigned int ramp_value = RK817_RAMP_RATE_25MV_PER_US;
+ unsigned int reg = RK817_BUCK_CONFIG_REG(rdev_get_id(rdev));
+
+ switch (ramp_delay) {
+ case 0 ... 3000:
+ ramp_value = RK817_RAMP_RATE_3MV_PER_US;
+ break;
+ case 3001 ... 6300:
+ ramp_value = RK817_RAMP_RATE_6_3MV_PER_US;
+ break;
+ case 6301 ... 12500:
+ ramp_value = RK817_RAMP_RATE_12_5MV_PER_US;
+ break;
+ case 12501 ... 25000:
+ break;
+ default:
+ dev_warn(&rdev->dev,
+ "%s ramp_delay: %d not supported, setting 10000\n",
+ rdev->desc->name, ramp_delay);
+ }
+
+ return regmap_update_bits(rdev->regmap, reg,
+ RK817_RAMP_RATE_MASK, ramp_value);
+}
+
static int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv)
{
unsigned int reg;
@@ -304,6 +419,21 @@ static int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv)
sel);
}
+static int rk817_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+ unsigned int reg;
+ int sel = regulator_map_voltage_linear(rdev, uv, uv);
+ /* only ldo1~ldo9 */
+ if (sel < 0)
+ return -EINVAL;
+
+ reg = rdev->desc->vsel_reg + RK808_SLP_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->vsel_mask,
+ sel);
+}
+
static int rk808_set_suspend_voltage_range(struct regulator_dev *rdev, int uv)
{
unsigned int reg;
@@ -363,6 +493,131 @@ static int rk808_set_suspend_disable(struct regulator_dev *rdev)
rdev->desc->enable_mask);
}
+static int rk817_set_suspend_enable_ctrl(struct regulator_dev *rdev,
+ unsigned int en)
+{
+ unsigned int reg;
+ int id = rdev_get_id(rdev);
+ unsigned int id_slp, msk, val;
+
+ if (id >= RK817_ID_DCDC1 && id <= RK817_ID_DCDC4)
+ id_slp = id;
+ else if (id >= RK817_ID_LDO1 && id <= RK817_ID_LDO8)
+ id_slp = 8 + (id - RK817_ID_LDO1);
+ else if (id >= RK817_ID_LDO9 && id <= RK809_ID_SW2)
+ id_slp = 4 + (id - RK817_ID_LDO9);
+ else
+ return -EINVAL;
+
+ reg = RK817_POWER_SLP_EN_REG(id_slp / 8);
+
+ msk = BIT(id_slp % 8);
+ if (en)
+ val = msk;
+ else
+ val = 0;
+
+ return regmap_update_bits(rdev->regmap, reg, msk, val);
+}
+
+static int rk817_set_suspend_enable(struct regulator_dev *rdev)
+{
+ return rk817_set_suspend_enable_ctrl(rdev, 1);
+}
+
+static int rk817_set_suspend_disable(struct regulator_dev *rdev)
+{
+ return rk817_set_suspend_enable_ctrl(rdev, 0);
+}
+
+static int rk8xx_set_suspend_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ unsigned int reg;
+
+ reg = rdev->desc->vsel_reg + RK808_SLP_REG_OFFSET;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ return regmap_update_bits(rdev->regmap, reg,
+ PWM_MODE_MSK, FPWM_MODE);
+ case REGULATOR_MODE_NORMAL:
+ return regmap_update_bits(rdev->regmap, reg,
+ PWM_MODE_MSK, AUTO_PWM_MODE);
+ default:
+ dev_err(&rdev->dev, "do not support this mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rk8xx_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,
+ PWM_MODE_MSK, FPWM_MODE);
+ case REGULATOR_MODE_NORMAL:
+ return regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
+ PWM_MODE_MSK, AUTO_PWM_MODE);
+ default:
+ dev_err(&rdev->dev, "do not support this mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned int rk8xx_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 int rk8xx_is_enabled_wmsk_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ /* add write mask bit */
+ val |= (rdev->desc->enable_mask & 0xf0);
+ val &= rdev->desc->enable_mask;
+
+ if (rdev->desc->enable_is_inverted) {
+ if (rdev->desc->enable_val)
+ return val != rdev->desc->enable_val;
+ return (val == 0);
+ }
+ if (rdev->desc->enable_val)
+ return val == rdev->desc->enable_val;
+ return val != 0;
+}
+
+static unsigned int rk8xx_regulator_of_map_mode(unsigned int mode)
+{
+ switch (mode) {
+ case 1:
+ return REGULATOR_MODE_FAST;
+ case 2:
+ return REGULATOR_MODE_NORMAL;
+ default:
+ return -EINVAL;
+ }
+}
+
static const struct regulator_ops rk805_reg_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
@@ -439,6 +694,71 @@ static const struct regulator_linear_range rk805_buck_1_2_voltage_ranges[] = {
REGULATOR_LINEAR_RANGE(2300000, 63, 63, 0),
};
+static struct regulator_ops rk809_buck5_ops_range = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = rk8xx_is_enabled_wmsk_regmap,
+ .set_suspend_voltage = rk808_set_suspend_voltage_range,
+ .set_suspend_enable = rk817_set_suspend_enable,
+ .set_suspend_disable = rk817_set_suspend_disable,
+};
+
+static struct regulator_ops rk817_reg_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = rk8xx_is_enabled_wmsk_regmap,
+ .set_suspend_voltage = rk817_set_suspend_voltage,
+ .set_suspend_enable = rk817_set_suspend_enable,
+ .set_suspend_disable = rk817_set_suspend_disable,
+};
+
+static struct regulator_ops rk817_boost_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = rk8xx_is_enabled_wmsk_regmap,
+ .set_suspend_enable = rk817_set_suspend_enable,
+ .set_suspend_disable = rk817_set_suspend_disable,
+};
+
+static struct regulator_ops rk817_buck_ops_range = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = rk8xx_is_enabled_wmsk_regmap,
+ .set_mode = rk8xx_set_mode,
+ .get_mode = rk8xx_get_mode,
+ .set_suspend_mode = rk8xx_set_suspend_mode,
+ .set_ramp_delay = rk817_set_ramp_delay,
+ .set_suspend_voltage = rk808_set_suspend_voltage_range,
+ .set_suspend_enable = rk817_set_suspend_enable,
+ .set_suspend_disable = rk817_set_suspend_disable,
+};
+
+static struct regulator_ops rk817_switch_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = rk8xx_is_enabled_wmsk_regmap,
+ .set_suspend_enable = rk817_set_suspend_enable,
+ .set_suspend_disable = rk817_set_suspend_disable,
+};
+
static const struct regulator_desc rk805_reg[] = {
{
.name = "DCDC_REG1",
@@ -595,6 +915,271 @@ static const struct regulator_desc rk808_reg[] = {
RK808_DCDC_EN_REG, BIT(6)),
};
+static const struct regulator_desc rk809_reg[] = {
+ {
+ .name = "DCDC_REG1",
+ .supply_name = "vcc1",
+ .of_match = of_match_ptr("DCDC_REG1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK817_ID_DCDC1,
+ .ops = &rk817_buck_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK817_BUCK1_SEL_CNT + 1,
+ .linear_ranges = rk817_buck1_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges),
+ .vsel_reg = RK817_BUCK1_ON_VSEL_REG,
+ .vsel_mask = RK817_BUCK_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(0),
+ .enable_mask = ENABLE_MASK(RK817_ID_DCDC1),
+ .enable_val = ENABLE_MASK(RK817_ID_DCDC1),
+ .disable_val = DISABLE_VAL(RK817_ID_DCDC1),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG2",
+ .supply_name = "vcc2",
+ .of_match = of_match_ptr("DCDC_REG2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK817_ID_DCDC2,
+ .ops = &rk817_buck_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK817_BUCK1_SEL_CNT + 1,
+ .linear_ranges = rk817_buck1_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges),
+ .vsel_reg = RK817_BUCK2_ON_VSEL_REG,
+ .vsel_mask = RK817_BUCK_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(0),
+ .enable_mask = ENABLE_MASK(RK817_ID_DCDC2),
+ .enable_val = ENABLE_MASK(RK817_ID_DCDC2),
+ .disable_val = DISABLE_VAL(RK817_ID_DCDC2),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG3",
+ .supply_name = "vcc3",
+ .of_match = of_match_ptr("DCDC_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK817_ID_DCDC3,
+ .ops = &rk817_buck_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK817_BUCK1_SEL_CNT + 1,
+ .linear_ranges = rk817_buck1_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges),
+ .vsel_reg = RK817_BUCK3_ON_VSEL_REG,
+ .vsel_mask = RK817_BUCK_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(0),
+ .enable_mask = ENABLE_MASK(RK817_ID_DCDC3),
+ .enable_val = ENABLE_MASK(RK817_ID_DCDC3),
+ .disable_val = DISABLE_VAL(RK817_ID_DCDC3),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG4",
+ .supply_name = "vcc4",
+ .of_match = of_match_ptr("DCDC_REG4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK817_ID_DCDC4,
+ .ops = &rk817_buck_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK817_BUCK3_SEL_CNT + 1,
+ .linear_ranges = rk817_buck3_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk817_buck3_voltage_ranges),
+ .vsel_reg = RK817_BUCK4_ON_VSEL_REG,
+ .vsel_mask = RK817_BUCK_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(0),
+ .enable_mask = ENABLE_MASK(RK817_ID_DCDC4),
+ .enable_val = ENABLE_MASK(RK817_ID_DCDC4),
+ .disable_val = DISABLE_VAL(RK817_ID_DCDC4),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "DCDC_REG5",
+ .supply_name = "vcc9",
+ .of_match = of_match_ptr("DCDC_REG5"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK809_ID_DCDC5,
+ .ops = &rk809_buck5_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK809_BUCK5_SEL_CNT,
+ .linear_ranges = rk809_buck5_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk809_buck5_voltage_ranges),
+ .vsel_reg = RK809_BUCK5_CONFIG(0),
+ .vsel_mask = RK809_BUCK5_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(3),
+ .enable_mask = ENABLE_MASK(1),
+ .enable_val = ENABLE_MASK(1),
+ .disable_val = DISABLE_VAL(1),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ },
+ RK817_DESC(RK817_ID_LDO1, "LDO_REG1", "vcc5", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(0), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(1), ENABLE_MASK(0),
+ DISABLE_VAL(0), 400),
+ RK817_DESC(RK817_ID_LDO2, "LDO_REG2", "vcc5", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(1), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(1), ENABLE_MASK(1),
+ DISABLE_VAL(1), 400),
+ RK817_DESC(RK817_ID_LDO3, "LDO_REG3", "vcc5", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(2), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(1), ENABLE_MASK(2),
+ DISABLE_VAL(2), 400),
+ RK817_DESC(RK817_ID_LDO4, "LDO_REG4", "vcc6", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(3), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(1), ENABLE_MASK(3),
+ DISABLE_VAL(3), 400),
+ RK817_DESC(RK817_ID_LDO5, "LDO_REG5", "vcc6", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(4), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(2), ENABLE_MASK(0),
+ DISABLE_VAL(0), 400),
+ RK817_DESC(RK817_ID_LDO6, "LDO_REG6", "vcc6", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(5), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(2), ENABLE_MASK(1),
+ DISABLE_VAL(1), 400),
+ RK817_DESC(RK817_ID_LDO7, "LDO_REG7", "vcc7", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(6), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(2), ENABLE_MASK(2),
+ DISABLE_VAL(2), 400),
+ RK817_DESC(RK817_ID_LDO8, "LDO_REG8", "vcc7", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(7), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(2), ENABLE_MASK(3),
+ DISABLE_VAL(3), 400),
+ RK817_DESC(RK817_ID_LDO9, "LDO_REG9", "vcc7", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(8), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(3), ENABLE_MASK(0),
+ DISABLE_VAL(0), 400),
+ RK817_DESC_SWITCH(RK809_ID_SW1, "SWITCH_REG1", "vcc9",
+ RK817_POWER_EN_REG(3), ENABLE_MASK(2),
+ DISABLE_VAL(2)),
+ RK817_DESC_SWITCH(RK809_ID_SW2, "SWITCH_REG2", "vcc8",
+ RK817_POWER_EN_REG(3), ENABLE_MASK(3),
+ DISABLE_VAL(3)),
+};
+
+static const struct regulator_desc rk817_reg[] = {
+ {
+ .name = "DCDC_REG1",
+ .supply_name = "vcc1",
+ .of_match = of_match_ptr("DCDC_REG1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK817_ID_DCDC1,
+ .ops = &rk817_buck_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK817_BUCK1_SEL_CNT + 1,
+ .linear_ranges = rk817_buck1_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges),
+ .vsel_reg = RK817_BUCK1_ON_VSEL_REG,
+ .vsel_mask = RK817_BUCK_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(0),
+ .enable_mask = ENABLE_MASK(RK817_ID_DCDC1),
+ .enable_val = ENABLE_MASK(RK817_ID_DCDC1),
+ .disable_val = DISABLE_VAL(RK817_ID_DCDC1),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG2",
+ .supply_name = "vcc2",
+ .of_match = of_match_ptr("DCDC_REG2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK817_ID_DCDC2,
+ .ops = &rk817_buck_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK817_BUCK1_SEL_CNT + 1,
+ .linear_ranges = rk817_buck1_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges),
+ .vsel_reg = RK817_BUCK2_ON_VSEL_REG,
+ .vsel_mask = RK817_BUCK_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(0),
+ .enable_mask = ENABLE_MASK(RK817_ID_DCDC2),
+ .enable_val = ENABLE_MASK(RK817_ID_DCDC2),
+ .disable_val = DISABLE_VAL(RK817_ID_DCDC2),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG3",
+ .supply_name = "vcc3",
+ .of_match = of_match_ptr("DCDC_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK817_ID_DCDC3,
+ .ops = &rk817_buck_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK817_BUCK1_SEL_CNT + 1,
+ .linear_ranges = rk817_buck1_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges),
+ .vsel_reg = RK817_BUCK3_ON_VSEL_REG,
+ .vsel_mask = RK817_BUCK_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(0),
+ .enable_mask = ENABLE_MASK(RK817_ID_DCDC3),
+ .enable_val = ENABLE_MASK(RK817_ID_DCDC3),
+ .disable_val = DISABLE_VAL(RK817_ID_DCDC3),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG4",
+ .supply_name = "vcc4",
+ .of_match = of_match_ptr("DCDC_REG4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK817_ID_DCDC4,
+ .ops = &rk817_buck_ops_range,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = RK817_BUCK3_SEL_CNT + 1,
+ .linear_ranges = rk817_buck3_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk817_buck3_voltage_ranges),
+ .vsel_reg = RK817_BUCK4_ON_VSEL_REG,
+ .vsel_mask = RK817_BUCK_VSEL_MASK,
+ .enable_reg = RK817_POWER_EN_REG(0),
+ .enable_mask = ENABLE_MASK(RK817_ID_DCDC4),
+ .enable_val = ENABLE_MASK(RK817_ID_DCDC4),
+ .disable_val = DISABLE_VAL(RK817_ID_DCDC4),
+ .of_map_mode = rk8xx_regulator_of_map_mode,
+ .owner = THIS_MODULE,
+ },
+ RK817_DESC(RK817_ID_LDO1, "LDO_REG1", "vcc5", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(0), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(1), ENABLE_MASK(0),
+ DISABLE_VAL(0), 400),
+ RK817_DESC(RK817_ID_LDO2, "LDO_REG2", "vcc5", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(1), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(1), ENABLE_MASK(1),
+ DISABLE_VAL(1), 400),
+ RK817_DESC(RK817_ID_LDO3, "LDO_REG3", "vcc5", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(2), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(1), ENABLE_MASK(2),
+ DISABLE_VAL(2), 400),
+ RK817_DESC(RK817_ID_LDO4, "LDO_REG4", "vcc6", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(3), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(1), ENABLE_MASK(3),
+ DISABLE_VAL(3), 400),
+ RK817_DESC(RK817_ID_LDO5, "LDO_REG5", "vcc6", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(4), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(2), ENABLE_MASK(0),
+ DISABLE_VAL(0), 400),
+ RK817_DESC(RK817_ID_LDO6, "LDO_REG6", "vcc6", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(5), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(2), ENABLE_MASK(1),
+ DISABLE_VAL(1), 400),
+ RK817_DESC(RK817_ID_LDO7, "LDO_REG7", "vcc7", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(6), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(2), ENABLE_MASK(2),
+ DISABLE_VAL(2), 400),
+ RK817_DESC(RK817_ID_LDO8, "LDO_REG8", "vcc7", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(7), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(2), ENABLE_MASK(3),
+ DISABLE_VAL(3), 400),
+ RK817_DESC(RK817_ID_LDO9, "LDO_REG9", "vcc7", 600, 3400, 25,
+ RK817_LDO_ON_VSEL_REG(8), RK817_LDO_VSEL_MASK,
+ RK817_POWER_EN_REG(3), ENABLE_MASK(0),
+ DISABLE_VAL(0), 400),
+ RK817_BOOST_DESC(RK817_ID_BOOST, "BOOST", "vcc8", 4700, 5400, 100,
+ RK817_BOOST_OTG_CFG, RK817_BOOST_VSEL_MASK,
+ RK817_POWER_EN_REG(3), ENABLE_MASK(1), ENABLE_MASK(1),
+ DISABLE_VAL(1), 400, 3500 - 5400),
+ RK817_DESC_SWITCH(RK817_ID_BOOST_OTG_SW, "OTG_SWITCH", "vcc9",
+ RK817_POWER_EN_REG(3), ENABLE_MASK(2),
+ DISABLE_VAL(2)),
+};
+
static const struct regulator_desc rk818_reg[] = {
{
.name = "DCDC_REG1",
@@ -765,6 +1350,14 @@ static int rk808_regulator_probe(struct platform_device *pdev)
regulators = rk808_reg;
nregulators = RK808_NUM_REGULATORS;
break;
+ case RK809_ID:
+ regulators = rk809_reg;
+ nregulators = RK809_NUM_REGULATORS;
+ break;
+ case RK817_ID:
+ regulators = rk817_reg;
+ nregulators = RK817_NUM_REGULATORS;
+ break;
case RK818_ID:
regulators = rk818_reg;
nregulators = RK818_NUM_REGULATORS;
@@ -803,6 +1396,7 @@ static struct platform_driver rk808_regulator_driver = {
module_platform_driver(rk808_regulator_driver);
MODULE_DESCRIPTION("regulator driver for the RK805/RK808/RK818 series PMICs");
+MODULE_AUTHOR("Tony xie <tony.xie@rock-chips.com>");
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 7b8e156..bd673dd 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -373,11 +373,11 @@
will be called rtc-max77686.
config RTC_DRV_RK808
- tristate "Rockchip RK805/RK808/RK818 RTC"
+ tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC"
depends on MFD_RK808
help
If you say yes here you will get support for the
- RTC of RK805, RK808 and RK818 PMIC.
+ RTC of RK805, RK809 and RK817, RK808 and RK818 PMIC.
This driver can also be built as a module. If so, the module
will be called rk808-rtc.
@@ -497,6 +497,14 @@
help
If you say Y here you will get support for the
watchdog timer in the ST M41T60 and M41T80 RTC chips series.
+config RTC_DRV_BD70528
+ tristate "ROHM BD70528 PMIC RTC"
+ help
+ If you say Y here you will get support for the RTC
+ on ROHM BD70528 Power Management IC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-bd70528.
config RTC_DRV_BQ32K
tristate "TI BQ32000"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 9d997fa..6b09c21 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -38,6 +38,7 @@
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
+obj-$(CONFIG_RTC_DRV_BD70528) += rtc-bd70528.o
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
new file mode 100644
index 0000000..f9bdd55
--- /dev/null
+++ b/drivers/rtc/rtc-bd70528.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Copyright (C) 2018 ROHM Semiconductors
+//
+// RTC driver for ROHM BD70528 PMIC
+
+#include <linux/bcd.h>
+#include <linux/mfd/rohm-bd70528.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+
+/*
+ * We read regs RTC_SEC => RTC_YEAR
+ * this struct is ordered according to chip registers.
+ * Keep it u8 only to avoid padding issues.
+ */
+struct bd70528_rtc_day {
+ u8 sec;
+ u8 min;
+ u8 hour;
+} __packed;
+
+struct bd70528_rtc_data {
+ struct bd70528_rtc_day time;
+ u8 week;
+ u8 day;
+ u8 month;
+ u8 year;
+} __packed;
+
+struct bd70528_rtc_wake {
+ struct bd70528_rtc_day time;
+ u8 ctrl;
+} __packed;
+
+struct bd70528_rtc_alm {
+ struct bd70528_rtc_data data;
+ u8 alm_mask;
+ u8 alm_repeat;
+} __packed;
+
+struct bd70528_rtc {
+ struct rohm_regmap_dev *mfd;
+ struct device *dev;
+};
+
+static int bd70528_set_wake(struct rohm_regmap_dev *bd70528,
+ int enable, int *old_state)
+{
+ int ret;
+ unsigned int ctrl_reg;
+
+ ret = regmap_read(bd70528->regmap, BD70528_REG_WAKE_EN, &ctrl_reg);
+ if (ret)
+ return ret;
+
+ if (old_state) {
+ if (ctrl_reg & BD70528_MASK_WAKE_EN)
+ *old_state |= BD70528_WAKE_STATE_BIT;
+ else
+ *old_state &= ~BD70528_WAKE_STATE_BIT;
+
+ if (!enable == !(*old_state & BD70528_WAKE_STATE_BIT))
+ return 0;
+ }
+
+ if (enable)
+ ctrl_reg |= BD70528_MASK_WAKE_EN;
+ else
+ ctrl_reg &= ~BD70528_MASK_WAKE_EN;
+
+ return regmap_write(bd70528->regmap, BD70528_REG_WAKE_EN,
+ ctrl_reg);
+}
+
+static int bd70528_set_elapsed_tmr(struct rohm_regmap_dev *bd70528,
+ int enable, int *old_state)
+{
+ int ret;
+ unsigned int ctrl_reg;
+
+ /*
+ * TBD
+ * What is the purpose of elapsed timer ?
+ * Is the timeout registers counting down, or is the disable - re-enable
+ * going to restart the elapsed-time counting? If counting is restarted
+ * the timeout should be decreased by the amount of time that has
+ * elapsed since starting the timer. Maybe we should store the monotonic
+ * clock value when timer is started so that if RTC is set while timer
+ * is armed we could do the compensation. This is a hack if RTC/system
+ * clk are drifting. OTOH, RTC controlled via I2C is in any case
+ * inaccurate...
+ */
+ ret = regmap_read(bd70528->regmap, BD70528_REG_ELAPSED_TIMER_EN,
+ &ctrl_reg);
+ if (ret)
+ return ret;
+
+ if (old_state) {
+ if (ctrl_reg & BD70528_MASK_ELAPSED_TIMER_EN)
+ *old_state |= BD70528_ELAPSED_STATE_BIT;
+ else
+ *old_state &= ~BD70528_ELAPSED_STATE_BIT;
+
+ if ((!enable) == (!(*old_state & BD70528_ELAPSED_STATE_BIT)))
+ return 0;
+ }
+
+ if (enable)
+ ctrl_reg |= BD70528_MASK_ELAPSED_TIMER_EN;
+ else
+ ctrl_reg &= ~BD70528_MASK_ELAPSED_TIMER_EN;
+
+ return regmap_write(bd70528->regmap, BD70528_REG_ELAPSED_TIMER_EN,
+ ctrl_reg);
+}
+
+static int bd70528_set_rtc_based_timers(struct bd70528_rtc *r, int new_state,
+ int *old_state)
+{
+ int ret;
+
+ ret = bd70528_wdt_set(r->mfd, new_state & BD70528_WDT_STATE_BIT,
+ old_state);
+ if (ret) {
+ dev_err(r->dev,
+ "Failed to disable WDG for RTC setting (%d)\n", ret);
+ return ret;
+ }
+ ret = bd70528_set_elapsed_tmr(r->mfd,
+ new_state & BD70528_ELAPSED_STATE_BIT,
+ old_state);
+ if (ret) {
+ dev_err(r->dev,
+ "Failed to disable 'elapsed timer' for RTC setting\n");
+ return ret;
+ }
+ ret = bd70528_set_wake(r->mfd, new_state & BD70528_WAKE_STATE_BIT,
+ old_state);
+ if (ret) {
+ dev_err(r->dev,
+ "Failed to disable 'wake timer' for RTC setting\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int bd70528_re_enable_rtc_based_timers(struct bd70528_rtc *r,
+ int old_state)
+{
+ return bd70528_set_rtc_based_timers(r, old_state, NULL);
+}
+
+static int bd70528_disable_rtc_based_timers(struct bd70528_rtc *r,
+ int *old_state)
+{
+ return bd70528_set_rtc_based_timers(r, 0, old_state);
+}
+
+static inline void tmday2rtc(struct rtc_time *t, struct bd70528_rtc_day *d)
+{
+ d->sec &= ~BD70528_MASK_RTC_SEC;
+ d->min &= ~BD70528_MASK_RTC_MINUTE;
+ d->hour &= ~BD70528_MASK_RTC_HOUR;
+ d->sec |= bin2bcd(t->tm_sec);
+ d->min |= bin2bcd(t->tm_min);
+ d->hour |= bin2bcd(t->tm_hour);
+}
+
+static inline void tm2rtc(struct rtc_time *t, struct bd70528_rtc_data *r)
+{
+ r->day &= ~BD70528_MASK_RTC_DAY;
+ r->week &= ~BD70528_MASK_RTC_WEEK;
+ r->month &= ~BD70528_MASK_RTC_MONTH;
+ /*
+ * PM and 24H bits are not used by Wake - thus we clear them
+ * here and not in tmday2rtc() which is also used by wake.
+ */
+ r->time.hour &= ~(BD70528_MASK_RTC_HOUR_PM | BD70528_MASK_RTC_HOUR_24H);
+
+ tmday2rtc(t, &r->time);
+ /*
+ * We do always set time in 24H mode.
+ */
+ r->time.hour |= BD70528_MASK_RTC_HOUR_24H;
+ r->day |= bin2bcd(t->tm_mday);
+ r->week |= bin2bcd(t->tm_wday);
+ r->month |= bin2bcd(t->tm_mon + 1);
+ r->year = bin2bcd(t->tm_year - 100);
+}
+
+static inline void rtc2tm(struct bd70528_rtc_data *r, struct rtc_time *t)
+{
+ t->tm_sec = bcd2bin(r->time.sec & BD70528_MASK_RTC_SEC);
+ t->tm_min = bcd2bin(r->time.min & BD70528_MASK_RTC_MINUTE);
+ t->tm_hour = bcd2bin(r->time.hour & BD70528_MASK_RTC_HOUR);
+ /*
+ * If RTC is in 12H mode, then bit BD70528_MASK_RTC_HOUR_PM
+ * is not BCD value but tells whether it is AM or PM
+ */
+ if (!(r->time.hour & BD70528_MASK_RTC_HOUR_24H)) {
+ t->tm_hour %= 12;
+ if (r->time.hour & BD70528_MASK_RTC_HOUR_PM)
+ t->tm_hour += 12;
+ }
+ t->tm_mday = bcd2bin(r->day & BD70528_MASK_RTC_DAY);
+ t->tm_mon = bcd2bin(r->month & BD70528_MASK_RTC_MONTH) - 1;
+ t->tm_year = 100 + bcd2bin(r->year & BD70528_MASK_RTC_YEAR);
+ t->tm_wday = bcd2bin(r->week & BD70528_MASK_RTC_WEEK);
+}
+
+static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+ struct bd70528_rtc_wake wake;
+ struct bd70528_rtc_alm alm;
+ int ret;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *bd70528 = r->mfd;
+
+ ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_WAKE_START,
+ &wake, sizeof(wake));
+ if (ret) {
+ dev_err(dev, "Failed to read wake regs\n");
+ return ret;
+ }
+
+ ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret) {
+ dev_err(dev, "Failed to read alarm regs\n");
+ return ret;
+ }
+
+ tm2rtc(&a->time, &alm.data);
+ tmday2rtc(&a->time, &wake.time);
+
+ if (a->enabled) {
+ alm.alm_mask &= ~BD70528_MASK_ALM_EN;
+ wake.ctrl |= BD70528_MASK_WAKE_EN;
+ } else {
+ alm.alm_mask |= BD70528_MASK_ALM_EN;
+ wake.ctrl &= ~BD70528_MASK_WAKE_EN;
+ }
+
+ ret = regmap_bulk_write(bd70528->regmap,
+ BD70528_REG_RTC_WAKE_START, &wake,
+ sizeof(wake));
+ if (ret) {
+ dev_err(dev, "Failed to set wake time\n");
+ return ret;
+ }
+ ret = regmap_bulk_write(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret)
+ dev_err(dev, "Failed to set alarm time\n");
+
+ return ret;
+}
+
+static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
+{
+ struct bd70528_rtc_alm alm;
+ int ret;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *bd70528 = r->mfd;
+
+ ret = regmap_bulk_read(bd70528->regmap, BD70528_REG_RTC_ALM_START,
+ &alm, sizeof(alm));
+ if (ret) {
+ dev_err(dev, "Failed to read alarm regs\n");
+ return ret;
+ }
+
+ rtc2tm(&alm.data, &a->time);
+ a->time.tm_mday = -1;
+ a->time.tm_mon = -1;
+ a->time.tm_year = -1;
+ a->enabled = !(alm.alm_mask & BD70528_MASK_ALM_EN);
+ a->pending = 0;
+
+ return 0;
+}
+
+static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
+{
+ int ret, tmpret, old_states;
+ struct bd70528_rtc_data rtc_data;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *bd70528 = r->mfd;
+
+ ret = bd70528_disable_rtc_based_timers(r, &old_states);
+ if (ret)
+ return ret;
+
+ tmpret = regmap_bulk_read(bd70528->regmap,
+ BD70528_REG_RTC_START, &rtc_data,
+ sizeof(rtc_data));
+ if (tmpret) {
+ dev_err(dev, "Failed to read RTC time registers\n");
+ goto renable_out;
+ }
+ tm2rtc(t, &rtc_data);
+
+ tmpret = regmap_bulk_write(bd70528->regmap,
+ BD70528_REG_RTC_START, &rtc_data,
+ sizeof(rtc_data));
+ if (tmpret) {
+ dev_err(dev, "Failed to set RTC time\n");
+ goto renable_out;
+ }
+
+renable_out:
+ ret = bd70528_re_enable_rtc_based_timers(r, old_states);
+ if (tmpret)
+ ret = tmpret;
+
+ return ret;
+}
+
+static int bd70528_set_time(struct device *dev, struct rtc_time *t)
+{
+ int ret;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+
+ bd70528_wdt_lock(r->mfd);
+ ret = bd70528_set_time_locked(dev, t);
+ bd70528_wdt_unlock(r->mfd);
+ return ret;
+}
+
+static int bd70528_get_time(struct device *dev, struct rtc_time *t)
+{
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+ struct rohm_regmap_dev *bd70528 = r->mfd;
+ struct bd70528_rtc_data rtc_data;
+ int ret;
+
+ /* read the RTC date and time registers all at once */
+ ret = regmap_bulk_read(bd70528->regmap,
+ BD70528_REG_RTC_START, &rtc_data,
+ sizeof(rtc_data));
+ if (ret) {
+ dev_err(dev, "Failed to read RTC time (err %d)\n", ret);
+ return ret;
+ }
+
+ rtc2tm(&rtc_data, t);
+
+ return 0;
+}
+
+static int bd70528_alm_enable(struct device *dev, unsigned int enabled)
+{
+ int ret;
+ unsigned int enableval = BD70528_MASK_ALM_EN;
+ struct bd70528_rtc *r = dev_get_drvdata(dev);
+
+ if (enabled)
+ enableval = 0;
+
+ bd70528_wdt_lock(r->mfd);
+ ret = bd70528_set_wake(r->mfd, enabled, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to change wake state\n");
+ goto out_unlock;
+ }
+ ret = regmap_update_bits(r->mfd->regmap, BD70528_REG_RTC_ALM_MASK,
+ BD70528_MASK_ALM_EN, enableval);
+ if (ret)
+ dev_err(dev, "Failed to change alarm state\n");
+
+out_unlock:
+ bd70528_wdt_unlock(r->mfd);
+ return ret;
+}
+
+static const struct rtc_class_ops bd70528_rtc_ops = {
+ .read_time = bd70528_get_time,
+ .set_time = bd70528_set_time,
+ .read_alarm = bd70528_read_alarm,
+ .set_alarm = bd70528_set_alarm,
+ .alarm_irq_enable = bd70528_alm_enable,
+};
+
+static irqreturn_t alm_hndlr(int irq, void *data)
+{
+ struct rtc_device *rtc = data;
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF | RTC_PF);
+ return IRQ_HANDLED;
+}
+
+static int bd70528_probe(struct platform_device *pdev)
+{
+ struct bd70528_rtc *bd_rtc;
+ struct rohm_regmap_dev *mfd;
+ int ret;
+ struct rtc_device *rtc;
+ int irq;
+ unsigned int hr;
+
+ mfd = dev_get_drvdata(pdev->dev.parent);
+ if (!mfd) {
+ dev_err(&pdev->dev, "No MFD driver data\n");
+ return -EINVAL;
+ }
+ bd_rtc = devm_kzalloc(&pdev->dev, sizeof(*bd_rtc), GFP_KERNEL);
+ if (!bd_rtc)
+ return -ENOMEM;
+
+ bd_rtc->mfd = mfd;
+ bd_rtc->dev = &pdev->dev;
+
+ irq = platform_get_irq_byname(pdev, "bd70528-rtc-alm");
+
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq\n");
+ return irq;
+ }
+
+ platform_set_drvdata(pdev, bd_rtc);
+
+ ret = regmap_read(mfd->regmap, BD70528_REG_RTC_HOUR, &hr);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to reag RTC clock\n");
+ return ret;
+ }
+
+ if (!(hr & BD70528_MASK_RTC_HOUR_24H)) {
+ struct rtc_time t;
+
+ ret = bd70528_get_time(&pdev->dev, &t);
+
+ if (!ret)
+ ret = bd70528_set_time(&pdev->dev, &t);
+
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Setting 24H clock for RTC failed\n");
+ return ret;
+ }
+ }
+
+ device_set_wakeup_capable(&pdev->dev, true);
+ device_wakeup_enable(&pdev->dev);
+
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "RTC device creation failed\n");
+ return PTR_ERR(rtc);
+ }
+
+ rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ rtc->range_max = RTC_TIMESTAMP_END_2099;
+ rtc->ops = &bd70528_rtc_ops;
+
+ /* Request alarm IRQ prior to registerig the RTC */
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, &alm_hndlr,
+ IRQF_ONESHOT, "bd70528-rtc", rtc);
+ if (ret)
+ return ret;
+
+ /*
+ * BD70528 irq controller is not touching the main mask register.
+ * So enable the RTC block interrupts at main level. We can just
+ * leave them enabled as irq-controller should disable irqs
+ * from sub-registers when IRQ is disabled or freed.
+ */
+ ret = regmap_update_bits(mfd->regmap,
+ BD70528_REG_INT_MAIN_MASK,
+ BD70528_INT_RTC_MASK, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable RTC interrupts\n");
+ return ret;
+ }
+
+ ret = rtc_register_device(rtc);
+ if (ret)
+ dev_err(&pdev->dev, "Registering RTC failed\n");
+
+ return ret;
+}
+
+static struct platform_driver bd70528_rtc = {
+ .driver = {
+ .name = "bd70528-rtc"
+ },
+ .probe = bd70528_probe,
+};
+
+module_platform_driver(bd70528_rtc);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD70528 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c
index 5c5d9f1..6c6d674 100644
--- a/drivers/rtc/rtc-rk808.c
+++ b/drivers/rtc/rtc-rk808.c
@@ -50,9 +50,18 @@
#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1)
#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1)
+struct rk_rtc_compat_reg {
+ unsigned int ctrl_reg;
+ unsigned int status_reg;
+ unsigned int alarm_seconds_reg;
+ unsigned int int_reg;
+ unsigned int seconds_reg;
+};
+
struct rk808_rtc {
struct rk808 *rk808;
struct rtc_device *rtc;
+ struct rk_rtc_compat_reg *creg;
int irq;
};
@@ -101,7 +110,7 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
int ret;
/* Force an update of the shadowed registers right now */
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_RTC_GET_TIME,
BIT_RTC_CTRL_REG_RTC_GET_TIME);
if (ret) {
@@ -115,7 +124,7 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
* 32khz. If we clear the GET_TIME bit here, the time of i2c transfer
* certainly more than 31.25us: 16 * 2.5us at 400kHz bus frequency.
*/
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_RTC_GET_TIME,
0);
if (ret) {
@@ -123,7 +132,7 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
return ret;
}
- ret = regmap_bulk_read(rk808->regmap, RK808_SECONDS_REG,
+ ret = regmap_bulk_read(rk808->regmap, rk808_rtc->creg->seconds_reg,
rtc_data, NUM_TIME_REGS);
if (ret) {
dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
@@ -162,7 +171,7 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_data[6] = bin2bcd(tm->tm_wday);
/* Stop RTC while updating the RTC registers */
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_STOP_RTC_M,
BIT_RTC_CTRL_REG_STOP_RTC_M);
if (ret) {
@@ -170,14 +179,14 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
return ret;
}
- ret = regmap_bulk_write(rk808->regmap, RK808_SECONDS_REG,
+ ret = regmap_bulk_write(rk808->regmap, rk808_rtc->creg->seconds_reg,
rtc_data, NUM_TIME_REGS);
if (ret) {
dev_err(dev, "Failed to bull write rtc_data: %d\n", ret);
return ret;
}
/* Start RTC again */
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_STOP_RTC_M, 0);
if (ret) {
dev_err(dev, "Failed to update RTC control: %d\n", ret);
@@ -195,8 +204,13 @@ static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
uint32_t int_reg;
int ret;
- ret = regmap_bulk_read(rk808->regmap, RK808_ALARM_SECONDS_REG,
+ ret = regmap_bulk_read(rk808->regmap,
+ rk808_rtc->creg->alarm_seconds_reg,
alrm_data, NUM_ALARM_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to read RTC alarm date REG: %d\n", ret);
+ return ret;
+ }
alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK);
alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK);
@@ -206,7 +220,7 @@ static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;
rockchip_to_gregorian(&alrm->time);
- ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg);
+ ret = regmap_read(rk808->regmap, rk808_rtc->creg->int_reg, &int_reg);
if (ret) {
dev_err(dev, "Failed to read RTC INT REG: %d\n", ret);
return ret;
@@ -225,7 +239,7 @@ static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc)
struct rk808 *rk808 = rk808_rtc->rk808;
int ret;
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->int_reg,
BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0);
return ret;
@@ -236,7 +250,7 @@ static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc)
struct rk808 *rk808 = rk808_rtc->rk808;
int ret;
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->int_reg,
BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,
BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
@@ -266,7 +280,8 @@ static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1);
alrm_data[5] = bin2bcd(alrm->time.tm_year - 100);
- ret = regmap_bulk_write(rk808->regmap, RK808_ALARM_SECONDS_REG,
+ ret = regmap_bulk_write(rk808->regmap,
+ rk808_rtc->creg->alarm_seconds_reg,
alrm_data, NUM_ALARM_REGS);
if (ret) {
dev_err(dev, "Failed to bulk write: %d\n", ret);
@@ -310,7 +325,7 @@ static irqreturn_t rk808_alarm_irq(int irq, void *data)
struct i2c_client *client = rk808->i2c;
int ret;
- ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
+ ret = regmap_write(rk808->regmap, rk808_rtc->creg->status_reg,
RTC_STATUS_MASK);
if (ret) {
dev_err(&client->dev,
@@ -361,6 +376,22 @@ static int rk808_rtc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops,
rk808_rtc_suspend, rk808_rtc_resume);
+static struct rk_rtc_compat_reg rk808_creg = {
+ .ctrl_reg = RK808_RTC_CTRL_REG,
+ .status_reg = RK808_RTC_STATUS_REG,
+ .alarm_seconds_reg = RK808_ALARM_SECONDS_REG,
+ .int_reg = RK808_RTC_INT_REG,
+ .seconds_reg = RK808_SECONDS_REG,
+};
+
+static struct rk_rtc_compat_reg rk817_creg = {
+ .ctrl_reg = RK817_RTC_CTRL_REG,
+ .status_reg = RK817_RTC_STATUS_REG,
+ .alarm_seconds_reg = RK817_ALARM_SECONDS_REG,
+ .int_reg = RK817_RTC_INT_REG,
+ .seconds_reg = RK817_SECONDS_REG,
+};
+
static int rk808_rtc_probe(struct platform_device *pdev)
{
struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
@@ -371,11 +402,20 @@ static int rk808_rtc_probe(struct platform_device *pdev)
if (rk808_rtc == NULL)
return -ENOMEM;
+ switch (rk808->variant) {
+ case RK809_ID:
+ case RK817_ID:
+ rk808_rtc->creg = &rk817_creg;
+ break;
+ default:
+ rk808_rtc->creg = &rk808_creg;
+ break;
+ }
platform_set_drvdata(pdev, rk808_rtc);
rk808_rtc->rk808 = rk808;
/* start rtc running by default, and use shadowed timer. */
- ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,
BIT_RTC_CTRL_REG_STOP_RTC_M |
BIT_RTC_CTRL_REG_RTC_READSEL_M,
BIT_RTC_CTRL_REG_RTC_READSEL_M);
@@ -385,7 +425,7 @@ static int rk808_rtc_probe(struct platform_device *pdev)
return ret;
}
- ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
+ ret = regmap_write(rk808->regmap, rk808_rtc->creg->status_reg,
RTC_STATUS_MASK);
if (ret) {
dev_err(&pdev->dev,
diff --git a/include/linux/mfd/lp87565.h b/include/linux/mfd/lp87565.h
index d0c91ba..9764476 100644
--- a/include/linux/mfd/lp87565.h
+++ b/include/linux/mfd/lp87565.h
@@ -17,6 +17,7 @@
enum lp87565_device_type {
LP87565_DEVICE_TYPE_UNKNOWN = 0,
+ LP87565_DEVICE_TYPE_LP87561_Q1,
LP87565_DEVICE_TYPE_LP87565_Q1,
};
@@ -249,6 +250,7 @@ enum LP87565_regulator_id {
LP87565_BUCK_3,
LP87565_BUCK_10,
LP87565_BUCK_23,
+ LP87565_BUCK_3210,
};
/**
diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h
index d315659..2a9cd01 100644
--- a/include/linux/mfd/rk808.h
+++ b/include/linux/mfd/rk808.h
@@ -382,6 +382,7 @@ enum rk805_reg {
#define SWITCH1_EN BIT(5)
#define DEV_OFF_RST BIT(3)
#define DEV_OFF BIT(0)
+#define RTC_STOP BIT(0)
#define VB_LO_ACT BIT(4)
#define VB_LO_SEL_3500MV (7 << 0)
@@ -395,7 +396,179 @@ enum rk805_reg {
#define SHUTDOWN_FUN (0x2 << 2)
#define SLEEP_FUN (0x1 << 2)
#define RK8XX_ID_MSK 0xfff0
+#define PWM_MODE_MSK BIT(7)
#define FPWM_MODE BIT(7)
+#define AUTO_PWM_MODE 0
+
+enum rk817_reg_id {
+ RK817_ID_DCDC1 = 0,
+ RK817_ID_DCDC2,
+ RK817_ID_DCDC3,
+ RK817_ID_DCDC4,
+ RK817_ID_LDO1,
+ RK817_ID_LDO2,
+ RK817_ID_LDO3,
+ RK817_ID_LDO4,
+ RK817_ID_LDO5,
+ RK817_ID_LDO6,
+ RK817_ID_LDO7,
+ RK817_ID_LDO8,
+ RK817_ID_LDO9,
+ RK817_ID_BOOST,
+ RK817_ID_BOOST_OTG_SW,
+ RK817_NUM_REGULATORS
+};
+
+enum rk809_reg_id {
+ RK809_ID_DCDC5 = RK817_ID_BOOST,
+ RK809_ID_SW1,
+ RK809_ID_SW2,
+ RK809_NUM_REGULATORS
+};
+
+#define RK817_SECONDS_REG 0x00
+#define RK817_MINUTES_REG 0x01
+#define RK817_HOURS_REG 0x02
+#define RK817_DAYS_REG 0x03
+#define RK817_MONTHS_REG 0x04
+#define RK817_YEARS_REG 0x05
+#define RK817_WEEKS_REG 0x06
+#define RK817_ALARM_SECONDS_REG 0x07
+#define RK817_ALARM_MINUTES_REG 0x08
+#define RK817_ALARM_HOURS_REG 0x09
+#define RK817_ALARM_DAYS_REG 0x0a
+#define RK817_ALARM_MONTHS_REG 0x0b
+#define RK817_ALARM_YEARS_REG 0x0c
+#define RK817_RTC_CTRL_REG 0xd
+#define RK817_RTC_STATUS_REG 0xe
+#define RK817_RTC_INT_REG 0xf
+#define RK817_RTC_COMP_LSB_REG 0x10
+#define RK817_RTC_COMP_MSB_REG 0x11
+
+#define RK817_POWER_EN_REG(i) (0xb1 + (i))
+#define RK817_POWER_SLP_EN_REG(i) (0xb5 + (i))
+
+#define RK817_POWER_CONFIG (0xb9)
+
+#define RK817_BUCK_CONFIG_REG(i) (0xba + (i) * 3)
+
+#define RK817_BUCK1_ON_VSEL_REG 0xBB
+#define RK817_BUCK1_SLP_VSEL_REG 0xBC
+
+#define RK817_BUCK2_CONFIG_REG 0xBD
+#define RK817_BUCK2_ON_VSEL_REG 0xBE
+#define RK817_BUCK2_SLP_VSEL_REG 0xBF
+
+#define RK817_BUCK3_CONFIG_REG 0xC0
+#define RK817_BUCK3_ON_VSEL_REG 0xC1
+#define RK817_BUCK3_SLP_VSEL_REG 0xC2
+
+#define RK817_BUCK4_CONFIG_REG 0xC3
+#define RK817_BUCK4_ON_VSEL_REG 0xC4
+#define RK817_BUCK4_SLP_VSEL_REG 0xC5
+
+#define RK817_LDO_ON_VSEL_REG(idx) (0xcc + (idx) * 2)
+#define RK817_BOOST_OTG_CFG (0xde)
+
+#define RK817_ID_MSB 0xed
+#define RK817_ID_LSB 0xee
+
+#define RK817_SYS_STS 0xf0
+#define RK817_SYS_CFG(i) (0xf1 + (i))
+
+#define RK817_ON_SOURCE_REG 0xf5
+#define RK817_OFF_SOURCE_REG 0xf6
+
+/* INTERRUPT REGISTER */
+#define RK817_INT_STS_REG0 0xf8
+#define RK817_INT_STS_MSK_REG0 0xf9
+#define RK817_INT_STS_REG1 0xfa
+#define RK817_INT_STS_MSK_REG1 0xfb
+#define RK817_INT_STS_REG2 0xfc
+#define RK817_INT_STS_MSK_REG2 0xfd
+#define RK817_GPIO_INT_CFG 0xfe
+
+/* IRQ Definitions */
+#define RK817_IRQ_PWRON_FALL 0
+#define RK817_IRQ_PWRON_RISE 1
+#define RK817_IRQ_PWRON 2
+#define RK817_IRQ_PWMON_LP 3
+#define RK817_IRQ_HOTDIE 4
+#define RK817_IRQ_RTC_ALARM 5
+#define RK817_IRQ_RTC_PERIOD 6
+#define RK817_IRQ_VB_LO 7
+#define RK817_IRQ_PLUG_IN 8
+#define RK817_IRQ_PLUG_OUT 9
+#define RK817_IRQ_CHRG_TERM 10
+#define RK817_IRQ_CHRG_TIME 11
+#define RK817_IRQ_CHRG_TS 12
+#define RK817_IRQ_USB_OV 13
+#define RK817_IRQ_CHRG_IN_CLMP 14
+#define RK817_IRQ_BAT_DIS_ILIM 15
+#define RK817_IRQ_GATE_GPIO 16
+#define RK817_IRQ_TS_GPIO 17
+#define RK817_IRQ_CODEC_PD 18
+#define RK817_IRQ_CODEC_PO 19
+#define RK817_IRQ_CLASSD_MUTE_DONE 20
+#define RK817_IRQ_CLASSD_OCP 21
+#define RK817_IRQ_BAT_OVP 22
+#define RK817_IRQ_CHRG_BAT_HI 23
+#define RK817_IRQ_END (RK817_IRQ_CHRG_BAT_HI + 1)
+
+/*
+ * rtc_ctrl 0xd
+ * same as 808, except bit4
+ */
+#define RK817_RTC_CTRL_RSV4 BIT(4)
+
+/* power config 0xb9 */
+#define RK817_BUCK3_FB_RES_MSK BIT(6)
+#define RK817_BUCK3_FB_RES_INTER BIT(6)
+#define RK817_BUCK3_FB_RES_EXT 0
+
+/* buck config 0xba */
+#define RK817_RAMP_RATE_OFFSET 6
+#define RK817_RAMP_RATE_MASK (0x3 << RK817_RAMP_RATE_OFFSET)
+#define RK817_RAMP_RATE_3MV_PER_US (0x0 << RK817_RAMP_RATE_OFFSET)
+#define RK817_RAMP_RATE_6_3MV_PER_US (0x1 << RK817_RAMP_RATE_OFFSET)
+#define RK817_RAMP_RATE_12_5MV_PER_US (0x2 << RK817_RAMP_RATE_OFFSET)
+#define RK817_RAMP_RATE_25MV_PER_US (0x3 << RK817_RAMP_RATE_OFFSET)
+
+/* sys_cfg1 0xf2 */
+#define RK817_HOTDIE_TEMP_MSK (0x3 << 4)
+#define RK817_HOTDIE_85 (0x0 << 4)
+#define RK817_HOTDIE_95 (0x1 << 4)
+#define RK817_HOTDIE_105 (0x2 << 4)
+#define RK817_HOTDIE_115 (0x3 << 4)
+
+#define RK817_TSD_TEMP_MSK BIT(6)
+#define RK817_TSD_140 0
+#define RK817_TSD_160 BIT(6)
+
+#define RK817_CLK32KOUT2_EN BIT(7)
+
+/* sys_cfg3 0xf4 */
+#define RK817_SLPPIN_FUNC_MSK (0x3 << 3)
+#define SLPPIN_NULL_FUN (0x0 << 3)
+#define SLPPIN_SLP_FUN (0x1 << 3)
+#define SLPPIN_DN_FUN (0x2 << 3)
+#define SLPPIN_RST_FUN (0x3 << 3)
+
+#define RK817_RST_FUNC_MSK (0x3 << 6)
+#define RK817_RST_FUNC_SFT (6)
+#define RK817_RST_FUNC_CNT (3)
+#define RK817_RST_FUNC_DEV (0) /* reset the dev */
+#define RK817_RST_FUNC_REG (0x1 << 6) /* reset the reg only */
+
+#define RK817_SLPPOL_MSK BIT(5)
+#define RK817_SLPPOL_H BIT(5)
+#define RK817_SLPPOL_L (0)
+
+/* gpio&int 0xfe */
+#define RK817_INT_POL_MSK BIT(1)
+#define RK817_INT_POL_H BIT(1)
+#define RK817_INT_POL_L 0
+#define RK809_BUCK5_CONFIG(i) (RK817_BOOST_OTG_CFG + (i) * 1)
enum {
BUCK_ILMIN_50MA,
@@ -443,6 +616,8 @@ enum {
enum {
RK805_ID = 0x8050,
RK808_ID = 0x0000,
+ RK809_ID = 0x8090,
+ RK817_ID = 0x8170,
RK818_ID = 0x8181,
};
diff --git a/include/linux/mfd/rohm-bd70528.h b/include/linux/mfd/rohm-bd70528.h
new file mode 100644
index 0000000..1013e60
--- /dev/null
+++ b/include/linux/mfd/rohm-bd70528.h
@@ -0,0 +1,408 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2018 ROHM Semiconductors */
+
+#ifndef __LINUX_MFD_BD70528_H__
+#define __LINUX_MFD_BD70528_H__
+
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/mfd/rohm-generic.h>
+#include <linux/regmap.h>
+
+enum {
+ BD70528_BUCK1,
+ BD70528_BUCK2,
+ BD70528_BUCK3,
+ BD70528_LDO1,
+ BD70528_LDO2,
+ BD70528_LDO3,
+ BD70528_LED1,
+ BD70528_LED2,
+};
+
+struct bd70528_data {
+ struct rohm_regmap_dev chip;
+ struct mutex rtc_timer_lock;
+};
+
+#define BD70528_BUCK_VOLTS 17
+#define BD70528_BUCK_VOLTS 17
+#define BD70528_BUCK_VOLTS 17
+#define BD70528_LDO_VOLTS 0x20
+
+#define BD70528_REG_BUCK1_EN 0x0F
+#define BD70528_REG_BUCK1_VOLT 0x15
+#define BD70528_REG_BUCK2_EN 0x10
+#define BD70528_REG_BUCK2_VOLT 0x16
+#define BD70528_REG_BUCK3_EN 0x11
+#define BD70528_REG_BUCK3_VOLT 0x17
+#define BD70528_REG_LDO1_EN 0x1b
+#define BD70528_REG_LDO1_VOLT 0x1e
+#define BD70528_REG_LDO2_EN 0x1c
+#define BD70528_REG_LDO2_VOLT 0x1f
+#define BD70528_REG_LDO3_EN 0x1d
+#define BD70528_REG_LDO3_VOLT 0x20
+#define BD70528_REG_LED_CTRL 0x2b
+#define BD70528_REG_LED_VOLT 0x29
+#define BD70528_REG_LED_EN 0x2a
+
+/* main irq registers */
+#define BD70528_REG_INT_MAIN 0x7E
+#define BD70528_REG_INT_MAIN_MASK 0x74
+
+/* 'sub irq' registers */
+#define BD70528_REG_INT_SHDN 0x7F
+#define BD70528_REG_INT_PWR_FLT 0x80
+#define BD70528_REG_INT_VR_FLT 0x81
+#define BD70528_REG_INT_MISC 0x82
+#define BD70528_REG_INT_BAT1 0x83
+#define BD70528_REG_INT_BAT2 0x84
+#define BD70528_REG_INT_RTC 0x85
+#define BD70528_REG_INT_GPIO 0x86
+#define BD70528_REG_INT_OP_FAIL 0x87
+
+#define BD70528_REG_INT_SHDN_MASK 0x75
+#define BD70528_REG_INT_PWR_FLT_MASK 0x76
+#define BD70528_REG_INT_VR_FLT_MASK 0x77
+#define BD70528_REG_INT_MISC_MASK 0x78
+#define BD70528_REG_INT_BAT1_MASK 0x79
+#define BD70528_REG_INT_BAT2_MASK 0x7a
+#define BD70528_REG_INT_RTC_MASK 0x7b
+#define BD70528_REG_INT_GPIO_MASK 0x7c
+#define BD70528_REG_INT_OP_FAIL_MASK 0x7d
+
+/* Reset related 'magic' registers */
+#define BD70528_REG_SHIPMODE 0x03
+#define BD70528_REG_HWRESET 0x04
+#define BD70528_REG_WARMRESET 0x05
+#define BD70528_REG_STANDBY 0x06
+
+/* GPIO registers */
+#define BD70528_REG_GPIO_STATE 0x8F
+
+#define BD70528_REG_GPIO1_IN 0x4d
+#define BD70528_REG_GPIO2_IN 0x4f
+#define BD70528_REG_GPIO3_IN 0x51
+#define BD70528_REG_GPIO4_IN 0x53
+#define BD70528_REG_GPIO1_OUT 0x4e
+#define BD70528_REG_GPIO2_OUT 0x50
+#define BD70528_REG_GPIO3_OUT 0x52
+#define BD70528_REG_GPIO4_OUT 0x54
+
+/* clk control */
+
+#define BD70528_REG_CLK_OUT 0x2c
+
+/* RTC */
+
+#define BD70528_REG_RTC_COUNT_H 0x2d
+#define BD70528_REG_RTC_COUNT_L 0x2e
+#define BD70528_REG_RTC_SEC 0x2f
+#define BD70528_REG_RTC_MINUTE 0x30
+#define BD70528_REG_RTC_HOUR 0x31
+#define BD70528_REG_RTC_WEEK 0x32
+#define BD70528_REG_RTC_DAY 0x33
+#define BD70528_REG_RTC_MONTH 0x34
+#define BD70528_REG_RTC_YEAR 0x35
+
+#define BD70528_REG_RTC_ALM_SEC 0x36
+#define BD70528_REG_RTC_ALM_START BD70528_REG_RTC_ALM_SEC
+#define BD70528_REG_RTC_ALM_MINUTE 0x37
+#define BD70528_REG_RTC_ALM_HOUR 0x38
+#define BD70528_REG_RTC_ALM_WEEK 0x39
+#define BD70528_REG_RTC_ALM_DAY 0x3a
+#define BD70528_REG_RTC_ALM_MONTH 0x3b
+#define BD70528_REG_RTC_ALM_YEAR 0x3c
+#define BD70528_REG_RTC_ALM_MASK 0x3d
+#define BD70528_REG_RTC_ALM_REPEAT 0x3e
+#define BD70528_REG_RTC_START BD70528_REG_RTC_SEC
+
+#define BD70528_REG_RTC_WAKE_SEC 0x43
+#define BD70528_REG_RTC_WAKE_START BD70528_REG_RTC_WAKE_SEC
+#define BD70528_REG_RTC_WAKE_MIN 0x44
+#define BD70528_REG_RTC_WAKE_HOUR 0x45
+#define BD70528_REG_RTC_WAKE_CTRL 0x46
+
+#define BD70528_REG_ELAPSED_TIMER_EN 0x42
+#define BD70528_REG_WAKE_EN 0x46
+
+/* WDT registers */
+#define BD70528_REG_WDT_CTRL 0x4A
+#define BD70528_REG_WDT_HOUR 0x49
+#define BD70528_REG_WDT_MINUTE 0x48
+#define BD70528_REG_WDT_SEC 0x47
+
+/* Charger / Battery */
+#define BD70528_REG_CHG_CURR_STAT 0x59
+#define BD70528_REG_CHG_BAT_STAT 0x57
+#define BD70528_REG_CHG_BAT_TEMP 0x58
+#define BD70528_REG_CHG_IN_STAT 0x56
+#define BD70528_REG_CHG_DCIN_ILIM 0x5d
+#define BD70528_REG_CHG_CHG_CURR_WARM 0x61
+#define BD70528_REG_CHG_CHG_CURR_COLD 0x62
+
+/* Masks for main IRQ register bits */
+enum {
+ BD70528_INT_SHDN,
+#define BD70528_INT_SHDN_MASK BIT(BD70528_INT_SHDN)
+ BD70528_INT_PWR_FLT,
+#define BD70528_INT_PWR_FLT_MASK BIT(BD70528_INT_PWR_FLT)
+ BD70528_INT_VR_FLT,
+#define BD70528_INT_VR_FLT_MASK BIT(BD70528_INT_VR_FLT)
+ BD70528_INT_MISC,
+#define BD70528_INT_MISC_MASK BIT(BD70528_INT_MISC)
+ BD70528_INT_BAT1,
+#define BD70528_INT_BAT1_MASK BIT(BD70528_INT_BAT1)
+ BD70528_INT_RTC,
+#define BD70528_INT_RTC_MASK BIT(BD70528_INT_RTC)
+ BD70528_INT_GPIO,
+#define BD70528_INT_GPIO_MASK BIT(BD70528_INT_GPIO)
+ BD70528_INT_OP_FAIL,
+#define BD70528_INT_OP_FAIL_MASK BIT(BD70528_INT_OP_FAIL)
+};
+
+/* IRQs */
+enum {
+ /* Shutdown register IRQs */
+ BD70528_INT_LONGPUSH,
+ BD70528_INT_WDT,
+ BD70528_INT_HWRESET,
+ BD70528_INT_RSTB_FAULT,
+ BD70528_INT_VBAT_UVLO,
+ BD70528_INT_TSD,
+ BD70528_INT_RSTIN,
+ /* Power failure register IRQs */
+ BD70528_INT_BUCK1_FAULT,
+ BD70528_INT_BUCK2_FAULT,
+ BD70528_INT_BUCK3_FAULT,
+ BD70528_INT_LDO1_FAULT,
+ BD70528_INT_LDO2_FAULT,
+ BD70528_INT_LDO3_FAULT,
+ BD70528_INT_LED1_FAULT,
+ BD70528_INT_LED2_FAULT,
+ /* VR FAULT register IRQs */
+ BD70528_INT_BUCK1_OCP,
+ BD70528_INT_BUCK2_OCP,
+ BD70528_INT_BUCK3_OCP,
+ BD70528_INT_LED1_OCP,
+ BD70528_INT_LED2_OCP,
+ BD70528_INT_BUCK1_FULLON,
+ BD70528_INT_BUCK2_FULLON,
+ /* PMU register interrupts */
+ BD70528_INT_SHORTPUSH,
+ BD70528_INT_AUTO_WAKEUP,
+ BD70528_INT_STATE_CHANGE,
+ /* Charger 1 register IRQs */
+ BD70528_INT_BAT_OV_RES,
+ BD70528_INT_BAT_OV_DET,
+ BD70528_INT_DBAT_DET,
+ BD70528_INT_BATTSD_COLD_RES,
+ BD70528_INT_BATTSD_COLD_DET,
+ BD70528_INT_BATTSD_HOT_RES,
+ BD70528_INT_BATTSD_HOT_DET,
+ BD70528_INT_CHG_TSD,
+ /* Charger 2 register IRQs */
+ BD70528_INT_BAT_RMV,
+ BD70528_INT_BAT_DET,
+ BD70528_INT_DCIN2_OV_RES,
+ BD70528_INT_DCIN2_OV_DET,
+ BD70528_INT_DCIN2_RMV,
+ BD70528_INT_DCIN2_DET,
+ BD70528_INT_DCIN1_RMV,
+ BD70528_INT_DCIN1_DET,
+ /* RTC register IRQs */
+ BD70528_INT_RTC_ALARM,
+ BD70528_INT_ELPS_TIM,
+ /* GPIO register IRQs */
+ BD70528_INT_GPIO0,
+ BD70528_INT_GPIO1,
+ BD70528_INT_GPIO2,
+ BD70528_INT_GPIO3,
+ /* Invalid operation register IRQs */
+ BD70528_INT_BUCK1_DVS_OPFAIL,
+ BD70528_INT_BUCK2_DVS_OPFAIL,
+ BD70528_INT_BUCK3_DVS_OPFAIL,
+ BD70528_INT_LED1_VOLT_OPFAIL,
+ BD70528_INT_LED2_VOLT_OPFAIL,
+};
+
+/* Masks */
+#define BD70528_INT_LONGPUSH_MASK 0x1
+#define BD70528_INT_WDT_MASK 0x2
+#define BD70528_INT_HWRESET_MASK 0x4
+#define BD70528_INT_RSTB_FAULT_MASK 0x8
+#define BD70528_INT_VBAT_UVLO_MASK 0x10
+#define BD70528_INT_TSD_MASK 0x20
+#define BD70528_INT_RSTIN_MASK 0x40
+
+#define BD70528_INT_BUCK1_FAULT_MASK 0x1
+#define BD70528_INT_BUCK2_FAULT_MASK 0x2
+#define BD70528_INT_BUCK3_FAULT_MASK 0x4
+#define BD70528_INT_LDO1_FAULT_MASK 0x8
+#define BD70528_INT_LDO2_FAULT_MASK 0x10
+#define BD70528_INT_LDO3_FAULT_MASK 0x20
+#define BD70528_INT_LED1_FAULT_MASK 0x40
+#define BD70528_INT_LED2_FAULT_MASK 0x80
+
+#define BD70528_INT_BUCK1_OCP_MASK 0x1
+#define BD70528_INT_BUCK2_OCP_MASK 0x2
+#define BD70528_INT_BUCK3_OCP_MASK 0x4
+#define BD70528_INT_LED1_OCP_MASK 0x8
+#define BD70528_INT_LED2_OCP_MASK 0x10
+#define BD70528_INT_BUCK1_FULLON_MASK 0x20
+#define BD70528_INT_BUCK2_FULLON_MASK 0x40
+
+#define BD70528_INT_SHORTPUSH_MASK 0x1
+#define BD70528_INT_AUTO_WAKEUP_MASK 0x2
+#define BD70528_INT_STATE_CHANGE_MASK 0x10
+
+#define BD70528_INT_BAT_OV_RES_MASK 0x1
+#define BD70528_INT_BAT_OV_DET_MASK 0x2
+#define BD70528_INT_DBAT_DET_MASK 0x4
+#define BD70528_INT_BATTSD_COLD_RES_MASK 0x8
+#define BD70528_INT_BATTSD_COLD_DET_MASK 0x10
+#define BD70528_INT_BATTSD_HOT_RES_MASK 0x20
+#define BD70528_INT_BATTSD_HOT_DET_MASK 0x40
+#define BD70528_INT_CHG_TSD_MASK 0x80
+
+#define BD70528_INT_BAT_RMV_MASK 0x1
+#define BD70528_INT_BAT_DET_MASK 0x2
+#define BD70528_INT_DCIN2_OV_RES_MASK 0x4
+#define BD70528_INT_DCIN2_OV_DET_MASK 0x8
+#define BD70528_INT_DCIN2_RMV_MASK 0x10
+#define BD70528_INT_DCIN2_DET_MASK 0x20
+#define BD70528_INT_DCIN1_RMV_MASK 0x40
+#define BD70528_INT_DCIN1_DET_MASK 0x80
+
+#define BD70528_INT_RTC_ALARM_MASK 0x1
+#define BD70528_INT_ELPS_TIM_MASK 0x2
+
+#define BD70528_INT_GPIO0_MASK 0x1
+#define BD70528_INT_GPIO1_MASK 0x2
+#define BD70528_INT_GPIO2_MASK 0x4
+#define BD70528_INT_GPIO3_MASK 0x8
+
+#define BD70528_INT_BUCK1_DVS_OPFAIL_MASK 0x1
+#define BD70528_INT_BUCK2_DVS_OPFAIL_MASK 0x2
+#define BD70528_INT_BUCK3_DVS_OPFAIL_MASK 0x4
+#define BD70528_INT_LED1_VOLT_OPFAIL_MASK 0x10
+#define BD70528_INT_LED2_VOLT_OPFAIL_MASK 0x20
+
+#define BD70528_DEBOUNCE_MASK 0x3
+
+#define BD70528_DEBOUNCE_DISABLE 0
+#define BD70528_DEBOUNCE_15MS 1
+#define BD70528_DEBOUNCE_30MS 2
+#define BD70528_DEBOUNCE_50MS 3
+
+#define BD70528_GPIO_DRIVE_MASK 0x2
+#define BD70528_GPIO_PUSH_PULL 0x0
+#define BD70528_GPIO_OPEN_DRAIN 0x2
+
+#define BD70528_GPIO_OUT_EN_MASK 0x80
+#define BD70528_GPIO_OUT_ENABLE 0x80
+#define BD70528_GPIO_OUT_DISABLE 0x0
+
+#define BD70528_GPIO_OUT_HI 0x1
+#define BD70528_GPIO_OUT_LO 0x0
+#define BD70528_GPIO_OUT_MASK 0x1
+
+#define BD70528_GPIO_IN_STATE_BASE 1
+
+#define BD70528_CLK_OUT_EN_MASK 0x1
+
+/* RTC masks to mask out reserved bits */
+
+#define BD70528_MASK_RTC_SEC 0x7f
+#define BD70528_MASK_RTC_MINUTE 0x7f
+#define BD70528_MASK_RTC_HOUR_24H 0x80
+#define BD70528_MASK_RTC_HOUR_PM 0x20
+#define BD70528_MASK_RTC_HOUR 0x1f
+#define BD70528_MASK_RTC_DAY 0x3f
+#define BD70528_MASK_RTC_WEEK 0x07
+#define BD70528_MASK_RTC_MONTH 0x1f
+#define BD70528_MASK_RTC_YEAR 0xff
+#define BD70528_MASK_RTC_COUNT_L 0x7f
+
+#define BD70528_MASK_ELAPSED_TIMER_EN 0x1
+/* Mask second, min and hour fields
+ * HW would support ALM irq for over 24h
+ * (by setting day, month and year too)
+ * but as we wish to keep this same as for
+ * wake-up we limit ALM to 24H and only
+ * unmask sec, min and hour
+ */
+#define BD70528_MASK_ALM_EN 0x7
+#define BD70528_MASK_WAKE_EN 0x1
+
+/* WDT masks */
+#define BD70528_MASK_WDT_EN 0x1
+#define BD70528_MASK_WDT_HOUR 0x1
+#define BD70528_MASK_WDT_MINUTE 0x7f
+#define BD70528_MASK_WDT_SEC 0x7f
+
+#define BD70528_WDT_STATE_BIT 0x1
+#define BD70528_ELAPSED_STATE_BIT 0x2
+#define BD70528_WAKE_STATE_BIT 0x4
+
+/* Charger masks */
+#define BD70528_MASK_CHG_STAT 0x7f
+#define BD70528_MASK_CHG_BAT_TIMER 0x20
+#define BD70528_MASK_CHG_BAT_OVERVOLT 0x10
+#define BD70528_MASK_CHG_BAT_DETECT 0x1
+#define BD70528_MASK_CHG_DCIN1_UVLO 0x1
+#define BD70528_MASK_CHG_DCIN_ILIM 0x3f
+#define BD70528_MASK_CHG_CHG_CURR 0x1f
+#define BD70528_MASK_CHG_TRICKLE_CURR 0x10
+
+/*
+ * Note, external battery register is the lonely rider at
+ * address 0xc5. See how to stuff that in the regmap
+ */
+#define BD70528_MAX_REGISTER 0x94
+
+/* Buck control masks */
+#define BD70528_MASK_RUN_EN 0x4
+#define BD70528_MASK_STBY_EN 0x2
+#define BD70528_MASK_IDLE_EN 0x1
+#define BD70528_MASK_LED1_EN 0x1
+#define BD70528_MASK_LED2_EN 0x10
+
+#define BD70528_MASK_BUCK_VOLT 0xf
+#define BD70528_MASK_LDO_VOLT 0x1f
+#define BD70528_MASK_LED1_VOLT 0x1
+#define BD70528_MASK_LED2_VOLT 0x10
+
+/* Misc irq masks */
+#define BD70528_INT_MASK_SHORT_PUSH 1
+#define BD70528_INT_MASK_AUTO_WAKE 2
+#define BD70528_INT_MASK_POWER_STATE 4
+
+#define BD70528_MASK_BUCK_RAMP 0x10
+#define BD70528_SIFT_BUCK_RAMP 4
+
+#if IS_ENABLED(CONFIG_BD70528_WATCHDOG)
+
+int bd70528_wdt_set(struct rohm_regmap_dev *data, int enable, int *old_state);
+void bd70528_wdt_lock(struct rohm_regmap_dev *data);
+void bd70528_wdt_unlock(struct rohm_regmap_dev *data);
+
+#else /* CONFIG_BD70528_WATCHDOG */
+
+static inline int bd70528_wdt_set(struct rohm_regmap_dev *data, int enable,
+ int *old_state)
+{
+ return 0;
+}
+
+static inline void bd70528_wdt_lock(struct rohm_regmap_dev *data)
+{
+}
+
+static inline void bd70528_wdt_unlock(struct rohm_regmap_dev *data)
+{
+}
+
+#endif /* CONFIG_BD70528_WATCHDOG */
+
+#endif /* __LINUX_MFD_BD70528_H__ */
diff --git a/include/linux/mfd/rohm-bd718x7.h b/include/linux/mfd/rohm-bd718x7.h
index fd194bf..7f2dbde 100644
--- a/include/linux/mfd/rohm-bd718x7.h
+++ b/include/linux/mfd/rohm-bd718x7.h
@@ -4,15 +4,10 @@
#ifndef __LINUX_MFD_BD718XX_H__
#define __LINUX_MFD_BD718XX_H__
+#include <linux/mfd/rohm-generic.h>
#include <linux/regmap.h>
enum {
- BD718XX_TYPE_BD71837 = 0,
- BD718XX_TYPE_BD71847,
- BD718XX_TYPE_AMOUNT
-};
-
-enum {
BD718XX_BUCK1 = 0,
BD718XX_BUCK2,
BD718XX_BUCK3,
@@ -321,18 +316,17 @@ enum {
BD718XX_PWRBTN_LONG_PRESS_15S
};
-struct bd718xx_clk;
-
struct bd718xx {
- unsigned int chip_type;
- struct device *dev;
- struct regmap *regmap;
- unsigned long int id;
+ /*
+ * Please keep this as the first member here as some
+ * drivers (clk) supporting more than one chip may only know this
+ * generic struct 'struct rohm_regmap_dev' and assume it is
+ * the first chunk of parent device's private data.
+ */
+ struct rohm_regmap_dev chip;
int chip_irq;
struct regmap_irq_chip_data *irq_data;
-
- struct bd718xx_clk *clk;
};
#endif /* __LINUX_MFD_BD718XX_H__ */
diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h
new file mode 100644
index 0000000..bff15ac
--- /dev/null
+++ b/include/linux/mfd/rohm-generic.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2018 ROHM Semiconductors */
+
+#ifndef __LINUX_MFD_ROHM_H__
+#define __LINUX_MFD_ROHM_H__
+
+enum {
+ ROHM_CHIP_TYPE_BD71837 = 0,
+ ROHM_CHIP_TYPE_BD71847,
+ ROHM_CHIP_TYPE_BD70528,
+ ROHM_CHIP_TYPE_AMOUNT
+};
+
+struct rohm_regmap_dev {
+ unsigned int chip_type;
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+#endif