diff options
-rw-r--r-- | Documentation/devicetree/bindings/mfd/rohm,bd9576-pmic.yaml | 123 | ||||
-rw-r--r-- | MAINTAINERS | 4 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/rohm-bd9576.c | 189 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 13 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/bd9576_wdt.c | 291 | ||||
-rw-r--r-- | include/linux/mfd/rohm-bd957x.h | 140 | ||||
-rw-r--r-- | include/linux/mfd/rohm-generic.h | 2 |
10 files changed, 775 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd9576-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd9576-pmic.yaml new file mode 100644 index 000000000000..6483860da955 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/rohm,bd9576-pmic.yaml @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/rohm,bd9576-pmic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BD9576MUF and BD9573MUF Power Management Integrated Circuit bindings + +maintainers: + - Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> + +description: | + BD9576MUF and BD9573MUF are power management ICs primarily intended for + powering the R-Car series processors. + The IC provides 6 power outputs with configurable sequencing and safety + monitoring. A watchdog logic with slow ping/windowed modes is also included. + +properties: + compatible: + enum: + - rohm,bd9576 + - rohm,bd9573 + + reg: + description: + I2C slave address. + maxItems: 1 + + interrupts: + maxItems: 1 + + rohm,vout1-en-low: + description: + BD9576 and BD9573 VOUT1 regulator enable state can be individually + controlled by a GPIO. This is dictated by state of vout1-en pin during + the PMIC startup. If vout1-en is LOW during PMIC startup then the VOUT1 + enable sate is controlled via this pin. Set this property if vout1-en + is wired to be down at PMIC start-up. + type: boolean + + rohm,vout1-en-gpios: + description: + GPIO specifier to specify the GPIO connected to vout1-en for vout1 ON/OFF + state control. + maxItems: 1 + + rohm,ddr-sel-low: + description: + The BD9576 and BD9573 output voltage for DDR can be selected by setting + the ddr-sel pin low or high. Set this property if ddr-sel is grounded. + type: boolean + + rohm,watchdog-enable-gpios: + description: The GPIO line used to enable the watchdog. + maxItems: 1 + + rohm,watchdog-ping-gpios: + description: The GPIO line used to ping the watchdog. + maxItems: 1 + + rohm,hw-timeout-ms: + maxItems: 2 + description: + Watchog timeout in milliseconds. If single value is given it is + the maximum timeout. Eg. if pinging watchdog is not done within this time + limit the watchdog will be triggered. If two values are given watchdog + is configured in "window mode". Then first value is limit for short-ping + Eg. if watchdog is pinged sooner than that the watchdog will trigger. + When two values is given the second value is the maximum timeout. + # (HW) minimum for short timeout is 2ms, maximum 220 ms. + # (HW) minimum for max timeout is 4ms, maximum 4416 ms. + + regulators: + $ref: ../regulator/rohm,bd9576-regulator.yaml + description: + List of child nodes that specify the regulators. + +required: + - compatible + - reg + - regulators + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/leds/common.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + pmic: pmic@30 { + compatible = "rohm,bd9576"; + reg = <0x30>; + rohm,vout1-en-low; + rohm,vout1-en-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; + rohm,ddr-sel-low; + rohm,watchdog-enable-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; + rohm,watchdog-ping-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>; + rohm,hw-timeout-ms = <150>, <2300>; + + regulators { + boost1: regulator-vd50 { + regulator-name = "VD50"; + }; + buck1: regulator-vd18 { + regulator-name = "VD18"; + }; + buck2: regulator-vdddr { + regulator-name = "VDDDR"; + }; + buck3: regulator-vd10 { + regulator-name = "VD10"; + }; + ldo: regulator-voutl1 { + regulator-name = "VOUTL1"; + }; + sw: regulator-vouts1 { + regulator-name = "VOUTS1"; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 0386d7f9bc13..d45380362cf4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15478,18 +15478,22 @@ F: drivers/gpio/gpio-bd71828.c F: drivers/mfd/rohm-bd70528.c F: drivers/mfd/rohm-bd71828.c F: drivers/mfd/rohm-bd718x7.c +F: drivers/mfd/rohm-bd9576.c F: drivers/power/supply/bd70528-charger.c F: drivers/regulator/bd70528-regulator.c F: drivers/regulator/bd71815-regulator.c F: drivers/regulator/bd71828-regulator.c F: drivers/regulator/bd718x7-regulator.c +F: drivers/regulator/bd9576-regulator.c F: drivers/regulator/rohm-regulator.c F: drivers/rtc/rtc-bd70528.c F: drivers/watchdog/bd70528_wdt.c +F: drivers/watchdog/bd9576_wdt.c F: include/linux/mfd/rohm-bd70528.h F: include/linux/mfd/rohm-bd71815.h F: include/linux/mfd/rohm-bd71828.h F: include/linux/mfd/rohm-bd718x7.h +F: include/linux/mfd/rohm-bd957x.h F: include/linux/mfd/rohm-generic.h F: include/linux/mfd/rohm-shared.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7605bed6914e..6e31210915c7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2001,6 +2001,17 @@ config MFD_ROHM_BD71828 also a single-cell linear charger, a Coulomb counter, a real-time clock (RTC), GPIOs and a 32.768 kHz clock gate. +config MFD_ROHM_BD957XMUF + tristate "ROHM BD9576MUF and BD9573MUF Power Management ICs" + depends on I2C=y + depends on OF + select REGMAP_I2C + select MFD_CORE + help + Select this option to get support for the ROHM BD9576MUF and + BD9573MUF Power Management ICs. BD9576 and BD9573 are primarily + designed to be used to power R-Car series processors. + 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 20b7aeb71d38..279b80822147 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -262,6 +262,7 @@ obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o obj-$(CONFIG_MFD_ROHM_BD70528) += rohm-bd70528.o obj-$(CONFIG_MFD_ROHM_BD71828) += rohm-bd71828.o obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o +obj-$(CONFIG_MFD_ROHM_BD957XMUF) += rohm-bd9576.o obj-$(CONFIG_MFD_STMFX) += stmfx.o obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o diff --git a/drivers/mfd/rohm-bd9576.c b/drivers/mfd/rohm-bd9576.c new file mode 100644 index 000000000000..6661a27d69a8 --- /dev/null +++ b/drivers/mfd/rohm-bd9576.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 ROHM Semiconductors + * + * ROHM BD9576MUF and BD9573MUF 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-bd957x.h> +#include <linux/mfd/rohm-generic.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +enum { + BD957X_REGULATOR_CELL, + BD957X_WDT_CELL, +}; + +/* + * Due to the BD9576MUF nasty IRQ behaiour we don't always populate IRQs. + * These will be added to regulator resources only if IRQ information for the + * PMIC is populated in device-tree. + */ +static const struct resource bd9576_regulator_irqs[] = { + DEFINE_RES_IRQ_NAMED(BD9576_INT_THERM, "bd9576-temp"), + DEFINE_RES_IRQ_NAMED(BD9576_INT_OVD, "bd9576-ovd"), + DEFINE_RES_IRQ_NAMED(BD9576_INT_UVD, "bd9576-uvd"), +}; + +static struct mfd_cell bd9573_mfd_cells[] = { + [BD957X_REGULATOR_CELL] = { .name = "bd9573-regulator", }, + [BD957X_WDT_CELL] = { .name = "bd9576-wdt", }, +}; + +static struct mfd_cell bd9576_mfd_cells[] = { + [BD957X_REGULATOR_CELL] = { .name = "bd9576-regulator", }, + [BD957X_WDT_CELL] = { .name = "bd9576-wdt", }, +}; + +static const struct regmap_range volatile_ranges[] = { + regmap_reg_range(BD957X_REG_SMRB_ASSERT, BD957X_REG_SMRB_ASSERT), + regmap_reg_range(BD957X_REG_PMIC_INTERNAL_STAT, + BD957X_REG_PMIC_INTERNAL_STAT), + regmap_reg_range(BD957X_REG_INT_THERM_STAT, BD957X_REG_INT_THERM_STAT), + regmap_reg_range(BD957X_REG_INT_OVP_STAT, BD957X_REG_INT_SYS_STAT), + regmap_reg_range(BD957X_REG_INT_MAIN_STAT, BD957X_REG_INT_MAIN_STAT), +}; + +static const struct regmap_access_table volatile_regs = { + .yes_ranges = &volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(volatile_ranges), +}; + +static struct regmap_config bd957x_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &volatile_regs, + .max_register = BD957X_MAX_REGISTER, + .cache_type = REGCACHE_RBTREE, +}; + +static struct regmap_irq bd9576_irqs[] = { + REGMAP_IRQ_REG(BD9576_INT_THERM, 0, BD957X_MASK_INT_MAIN_THERM), + REGMAP_IRQ_REG(BD9576_INT_OVP, 0, BD957X_MASK_INT_MAIN_OVP), + REGMAP_IRQ_REG(BD9576_INT_SCP, 0, BD957X_MASK_INT_MAIN_SCP), + REGMAP_IRQ_REG(BD9576_INT_OCP, 0, BD957X_MASK_INT_MAIN_OCP), + REGMAP_IRQ_REG(BD9576_INT_OVD, 0, BD957X_MASK_INT_MAIN_OVD), + REGMAP_IRQ_REG(BD9576_INT_UVD, 0, BD957X_MASK_INT_MAIN_UVD), + REGMAP_IRQ_REG(BD9576_INT_UVP, 0, BD957X_MASK_INT_MAIN_UVP), + REGMAP_IRQ_REG(BD9576_INT_SYS, 0, BD957X_MASK_INT_MAIN_SYS), +}; + +static struct regmap_irq_chip bd9576_irq_chip = { + .name = "bd9576_irq", + .irqs = &bd9576_irqs[0], + .num_irqs = ARRAY_SIZE(bd9576_irqs), + .status_base = BD957X_REG_INT_MAIN_STAT, + .mask_base = BD957X_REG_INT_MAIN_MASK, + .ack_base = BD957X_REG_INT_MAIN_STAT, + .init_ack_masked = true, + .num_regs = 1, + .irq_reg_stride = 1, +}; + +static int bd957x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret; + struct regmap *regmap; + struct mfd_cell *cells; + int num_cells; + unsigned long chip_type; + struct irq_domain *domain; + bool usable_irqs; + + chip_type = (unsigned long)of_device_get_match_data(&i2c->dev); + + switch (chip_type) { + case ROHM_CHIP_TYPE_BD9576: + cells = bd9576_mfd_cells; + num_cells = ARRAY_SIZE(bd9576_mfd_cells); + usable_irqs = !!i2c->irq; + break; + case ROHM_CHIP_TYPE_BD9573: + cells = bd9573_mfd_cells; + num_cells = ARRAY_SIZE(bd9573_mfd_cells); + /* + * BD9573 only supports fatal IRQs which we can not handle + * because SoC is going to lose the power. + */ + usable_irqs = false; + break; + default: + dev_err(&i2c->dev, "Unknown device type"); + return -EINVAL; + } + + regmap = devm_regmap_init_i2c(i2c, &bd957x_regmap); + if (IS_ERR(regmap)) { + dev_err(&i2c->dev, "Failed to initialize Regmap\n"); + return PTR_ERR(regmap); + } + + /* + * BD9576 behaves badly. It kepts IRQ line asserted for the whole + * duration of detected HW condition (like over temperature). So we + * don't require IRQ to be populated. + * If IRQ information is not given, then we mask all IRQs and do not + * provide IRQ resources to regulator driver - which then just omits + * the notifiers. + */ + if (usable_irqs) { + struct regmap_irq_chip_data *irq_data; + struct mfd_cell *regulators; + + regulators = &bd9576_mfd_cells[BD957X_REGULATOR_CELL]; + regulators->resources = bd9576_regulator_irqs; + regulators->num_resources = ARRAY_SIZE(bd9576_regulator_irqs); + + ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq, + IRQF_ONESHOT, 0, + &bd9576_irq_chip, &irq_data); + if (ret) { + dev_err(&i2c->dev, "Failed to add IRQ chip\n"); + return ret; + } + domain = regmap_irq_get_domain(irq_data); + } else { + ret = regmap_update_bits(regmap, BD957X_REG_INT_MAIN_MASK, + BD957X_MASK_INT_ALL, + BD957X_MASK_INT_ALL); + if (ret) + return ret; + domain = NULL; + } + + ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, cells, + num_cells, NULL, 0, domain); + if (ret) + dev_err(&i2c->dev, "Failed to create subdevices\n"); + + return ret; +} + +static const struct of_device_id bd957x_of_match[] = { + { .compatible = "rohm,bd9576", .data = (void *)ROHM_CHIP_TYPE_BD9576, }, + { .compatible = "rohm,bd9573", .data = (void *)ROHM_CHIP_TYPE_BD9573, }, + { }, +}; +MODULE_DEVICE_TABLE(of, bd957x_of_match); + +static struct i2c_driver bd957x_drv = { + .driver = { + .name = "rohm-bd957x", + .of_match_table = bd957x_of_match, + }, + .probe = &bd957x_i2c_probe, +}; +module_i2c_driver(bd957x_drv); + +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); +MODULE_DESCRIPTION("ROHM BD9576MUF and BD9573MUF Power Management IC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1fe0042a48d2..355100dad60a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -172,6 +172,19 @@ config BD70528_WATCHDOG Alternatively say M to compile the driver as a module, which will be called bd70528_wdt. +config BD957XMUF_WATCHDOG + tristate "ROHM BD9576MUF and BD9573MUF PMIC Watchdog" + depends on MFD_ROHM_BD957XMUF + select WATCHDOG_CORE + help + Support for the watchdog in the ROHM BD9576 and BD9573 PMICs. + These PMIC ICs contain watchdog block which can be configured + to toggle reset line if SoC fails to ping watchdog via GPIO. + + Say Y here to include support for the ROHM BD9576 or BD9573 + watchdog. Alternatively say M to compile the driver as a module, + which will be called bd9576_wdt. + config DA9052_WATCHDOG tristate "Dialog DA9052 Watchdog" depends on PMIC_DA9052 || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f3a6540e725e..a7eade8b4d45 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -204,6 +204,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o # Architecture Independent obj-$(CONFIG_BD70528_WATCHDOG) += bd70528_wdt.o +obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o diff --git a/drivers/watchdog/bd9576_wdt.c b/drivers/watchdog/bd9576_wdt.c new file mode 100644 index 000000000000..0b6999f3b6e8 --- /dev/null +++ b/drivers/watchdog/bd9576_wdt.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 ROHM Semiconductors + * + * ROHM BD9576MUF and BD9573MUF Watchdog driver + */ + +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/mfd/rohm-bd957x.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/watchdog.h> + +static bool nowayout; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=\"false\")"); + +#define HW_MARGIN_MIN 2 +#define HW_MARGIN_MAX 4416 +#define BD957X_WDT_DEFAULT_MARGIN 4416 +#define WATCHDOG_TIMEOUT 30 + +struct bd9576_wdt_priv { + struct gpio_desc *gpiod_ping; + struct gpio_desc *gpiod_en; + struct device *dev; + struct regmap *regmap; + bool always_running; + struct watchdog_device wdd; +}; + +static void bd9576_wdt_disable(struct bd9576_wdt_priv *priv) +{ + gpiod_set_value_cansleep(priv->gpiod_en, 0); +} + +static int bd9576_wdt_ping(struct watchdog_device *wdd) +{ + struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd); + + /* Pulse */ + gpiod_set_value_cansleep(priv->gpiod_ping, 1); + gpiod_set_value_cansleep(priv->gpiod_ping, 0); + + return 0; +} + +static int bd9576_wdt_start(struct watchdog_device *wdd) +{ + struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd); + + gpiod_set_value_cansleep(priv->gpiod_en, 1); + + return bd9576_wdt_ping(wdd); +} + +static int bd9576_wdt_stop(struct watchdog_device *wdd) +{ + struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd); + + if (!priv->always_running) + bd9576_wdt_disable(priv); + else + set_bit(WDOG_HW_RUNNING, &wdd->status); + + return 0; +} + +static const struct watchdog_info bd957x_wdt_ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT, + .identity = "BD957x Watchdog", +}; + +static const struct watchdog_ops bd957x_wdt_ops = { + .owner = THIS_MODULE, + .start = bd9576_wdt_start, + .stop = bd9576_wdt_stop, + .ping = bd9576_wdt_ping, +}; + +/* Unit is hundreds of uS */ +#define FASTNG_MIN 23 + +static int find_closest_fast(int target, int *sel, int *val) +{ + int i; + int window = FASTNG_MIN; + + for (i = 0; i < 8 && window < target; i++) + window <<= 1; + + *val = window; + *sel = i; + + if (i == 8) + return -EINVAL; + + return 0; + +} + +static int find_closest_slow_by_fast(int fast_val, int target, int *slowsel) +{ + int sel; + static const int multipliers[] = {2, 3, 7, 15}; + + for (sel = 0; sel < ARRAY_SIZE(multipliers) && + multipliers[sel] * fast_val < target; sel++) + ; + + if (sel == ARRAY_SIZE(multipliers)) + return -EINVAL; + + *slowsel = sel; + + return 0; +} + +static int find_closest_slow(int target, int *slow_sel, int *fast_sel) +{ + static const int multipliers[] = {2, 3, 7, 15}; + int i, j; + int val = 0; + int window = FASTNG_MIN; + + for (i = 0; i < 8; i++) { + for (j = 0; j < ARRAY_SIZE(multipliers); j++) { + int slow; + + slow = window * multipliers[j]; + if (slow >= target && (!val || slow < val)) { + val = slow; + *fast_sel = i; + *slow_sel = j; + } + } + window <<= 1; + } + if (!val) + return -EINVAL; + + return 0; +} + +#define BD957X_WDG_TYPE_WINDOW BIT(5) +#define BD957X_WDG_TYPE_SLOW 0 +#define BD957X_WDG_TYPE_MASK BIT(5) +#define BD957X_WDG_NG_RATIO_MASK 0x18 +#define BD957X_WDG_FASTNG_MASK 0x7 + +static int bd957x_set_wdt_mode(struct bd9576_wdt_priv *priv, int hw_margin, + int hw_margin_min) +{ + int ret, fastng, slowng, type, reg, mask; + struct device *dev = priv->dev; + + /* convert to 100uS */ + hw_margin *= 10; + hw_margin_min *= 10; + if (hw_margin_min) { + int min; + + type = BD957X_WDG_TYPE_WINDOW; + dev_dbg(dev, "Setting type WINDOW 0x%x\n", type); + ret = find_closest_fast(hw_margin_min, &fastng, &min); + if (ret) { + dev_err(dev, "bad WDT window for fast timeout\n"); + return ret; + } + + ret = find_closest_slow_by_fast(min, hw_margin, &slowng); + if (ret) { + dev_err(dev, "bad WDT window\n"); + return ret; + } + + } else { + type = BD957X_WDG_TYPE_SLOW; + dev_dbg(dev, "Setting type SLOW 0x%x\n", type); + ret = find_closest_slow(hw_margin, &slowng, &fastng); + if (ret) { + dev_err(dev, "bad WDT window\n"); + return ret; + } + } + + slowng <<= ffs(BD957X_WDG_NG_RATIO_MASK) - 1; + reg = type | slowng | fastng; + mask = BD957X_WDG_TYPE_MASK | BD957X_WDG_NG_RATIO_MASK | + BD957X_WDG_FASTNG_MASK; + ret = regmap_update_bits(priv->regmap, BD957X_REG_WDT_CONF, + mask, reg); + + return ret; +} + +static int bd9576_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->parent->of_node; + struct bd9576_wdt_priv *priv; + u32 hw_margin[2]; + u32 hw_margin_max = BD957X_WDT_DEFAULT_MARGIN, hw_margin_min = 0; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + priv->dev = dev; + priv->regmap = dev_get_regmap(dev->parent, NULL); + if (!priv->regmap) { + dev_err(dev, "No regmap found\n"); + return -ENODEV; + } + + priv->gpiod_en = devm_gpiod_get_from_of_node(dev, dev->parent->of_node, + "rohm,watchdog-enable-gpios", + 0, GPIOD_OUT_LOW, + "watchdog-enable"); + if (IS_ERR(priv->gpiod_en)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_en), + "getting watchdog-enable GPIO failed\n"); + + priv->gpiod_ping = devm_gpiod_get_from_of_node(dev, dev->parent->of_node, + "rohm,watchdog-ping-gpios", + 0, GPIOD_OUT_LOW, + "watchdog-ping"); + if (IS_ERR(priv->gpiod_ping)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_ping), + "getting watchdog-ping GPIO failed\n"); + + ret = of_property_read_variable_u32_array(np, "rohm,hw-timeout-ms", + &hw_margin[0], 1, 2); + if (ret < 0 && ret != -EINVAL) + return ret; + + if (ret == 1) + hw_margin_max = hw_margin[0]; + + if (ret == 2) { + hw_margin_max = hw_margin[1]; + hw_margin_min = hw_margin[0]; + } + + ret = bd957x_set_wdt_mode(priv, hw_margin_max, hw_margin_min); + if (ret) + return ret; + + priv->always_running = of_property_read_bool(np, "always-running"); + + watchdog_set_drvdata(&priv->wdd, priv); + + priv->wdd.info = &bd957x_wdt_ident; + priv->wdd.ops = &bd957x_wdt_ops; + priv->wdd.min_hw_heartbeat_ms = hw_margin_min; + priv->wdd.max_hw_heartbeat_ms = hw_margin_max; + priv->wdd.parent = dev; + priv->wdd.timeout = WATCHDOG_TIMEOUT; + + watchdog_init_timeout(&priv->wdd, 0, dev); + watchdog_set_nowayout(&priv->wdd, nowayout); + + watchdog_stop_on_reboot(&priv->wdd); + + if (priv->always_running) + bd9576_wdt_start(&priv->wdd); + + return devm_watchdog_register_device(dev, &priv->wdd); +} + +static struct platform_driver bd9576_wdt_driver = { + .driver = { + .name = "bd9576-wdt", + }, + .probe = bd9576_wdt_probe, +}; + +module_platform_driver(bd9576_wdt_driver); + +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); +MODULE_DESCRIPTION("ROHM BD9576/BD9573 Watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd9576-wdt"); diff --git a/include/linux/mfd/rohm-bd957x.h b/include/linux/mfd/rohm-bd957x.h new file mode 100644 index 000000000000..acc920b64f75 --- /dev/null +++ b/include/linux/mfd/rohm-bd957x.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2021 ROHM Semiconductors */ + +#ifndef __LINUX_MFD_BD957X_H__ +#define __LINUX_MFD_BD957X_H__ + +enum { + BD957X_VD50, + BD957X_VD18, + BD957X_VDDDR, + BD957X_VD10, + BD957X_VOUTL1, + BD957X_VOUTS1, +}; + +/* + * The BD9576 has own IRQ 'blocks' for: + * - I2C/thermal, + * - Over voltage protection + * - Short-circuit protection + * - Over current protection + * - Over voltage detection + * - Under voltage detection + * - Under voltage protection + * - 'system interrupt'. + * + * Each of the blocks have a status register giving more accurate IRQ source + * information - for example which of the regulators have over-voltage. + * + * On top of this, there is "main IRQ" status register where each bit indicates + * which of sub-blocks have active IRQs. Fine. That would fit regmap-irq main + * status handling. Except that: + * - Only some sub-IRQs can be masked. + * - The IRQ informs us about fault-condition, not when fault state changes. + * The IRQ line it is kept asserted until the detected condition is acked + * AND cleared in HW. This is annoying for IRQs like the one informing high + * temperature because if IRQ is not disabled it keeps the CPU in IRQ + * handling loop. + * + * For now we do just use the main-IRQ register as source for our IRQ + * information and bind the regmap-irq to this. We leave fine-grained sub-IRQ + * register handling to handlers in sub-devices. The regulator driver shall + * read which regulators are source for problem - or if the detected error is + * regulator temperature error. The sub-drivers do also handle masking of "sub- + * IRQs" if this is supported/needed. + * + * To overcome the problem with HW keeping IRQ asserted we do call + * disable_irq_nosync() from sub-device handler and add a delayed work to + * re-enable IRQ roughly 1 second later. This should keep our CPU out of + * busy-loop. + */ +#define IRQS_SILENT_MS 1000 + +enum { + BD9576_INT_THERM, + BD9576_INT_OVP, + BD9576_INT_SCP, + BD9576_INT_OCP, + BD9576_INT_OVD, + BD9576_INT_UVD, + BD9576_INT_UVP, + BD9576_INT_SYS, +}; + +#define BD957X_REG_SMRB_ASSERT 0x15 +#define BD957X_REG_PMIC_INTERNAL_STAT 0x20 +#define BD957X_REG_INT_THERM_STAT 0x23 +#define BD957X_REG_INT_THERM_MASK 0x24 +#define BD957X_REG_INT_OVP_STAT 0x25 +#define BD957X_REG_INT_SCP_STAT 0x26 +#define BD957X_REG_INT_OCP_STAT 0x27 +#define BD957X_REG_INT_OVD_STAT 0x28 +#define BD957X_REG_INT_UVD_STAT 0x29 +#define BD957X_REG_INT_UVP_STAT 0x2a +#define BD957X_REG_INT_SYS_STAT 0x2b +#define BD957X_REG_INT_SYS_MASK 0x2c +#define BD957X_REG_INT_MAIN_STAT 0x30 +#define BD957X_REG_INT_MAIN_MASK 0x31 + +#define UVD_IRQ_VALID_MASK 0x6F +#define OVD_IRQ_VALID_MASK 0x2F + +#define BD957X_MASK_INT_MAIN_THERM BIT(0) +#define BD957X_MASK_INT_MAIN_OVP BIT(1) +#define BD957X_MASK_INT_MAIN_SCP BIT(2) +#define BD957X_MASK_INT_MAIN_OCP BIT(3) +#define BD957X_MASK_INT_MAIN_OVD BIT(4) +#define BD957X_MASK_INT_MAIN_UVD BIT(5) +#define BD957X_MASK_INT_MAIN_UVP BIT(6) +#define BD957X_MASK_INT_MAIN_SYS BIT(7) +#define BD957X_MASK_INT_ALL 0xff + +#define BD957X_REG_WDT_CONF 0x16 + +#define BD957X_REG_POW_TRIGGER1 0x41 +#define BD957X_REG_POW_TRIGGER2 0x42 +#define BD957X_REG_POW_TRIGGER3 0x43 +#define BD957X_REG_POW_TRIGGER4 0x44 +#define BD957X_REG_POW_TRIGGERL1 0x45 +#define BD957X_REG_POW_TRIGGERS1 0x46 + +#define BD957X_REGULATOR_EN_MASK 0xff +#define BD957X_REGULATOR_DIS_VAL 0xff + +#define BD957X_VSEL_REG_MASK 0xff + +#define BD957X_MASK_VOUT1_TUNE 0x87 +#define BD957X_MASK_VOUT2_TUNE 0x87 +#define BD957X_MASK_VOUT3_TUNE 0x1f +#define BD957X_MASK_VOUT4_TUNE 0x1f +#define BD957X_MASK_VOUTL1_TUNE 0x87 + +#define BD957X_REG_VOUT1_TUNE 0x50 +#define BD957X_REG_VOUT2_TUNE 0x53 +#define BD957X_REG_VOUT3_TUNE 0x56 +#define BD957X_REG_VOUT4_TUNE 0x59 +#define BD957X_REG_VOUTL1_TUNE 0x5c + +#define BD9576_REG_VOUT1_OVD 0x51 +#define BD9576_REG_VOUT1_UVD 0x52 +#define BD9576_REG_VOUT2_OVD 0x54 +#define BD9576_REG_VOUT2_UVD 0x55 +#define BD9576_REG_VOUT3_OVD 0x57 +#define BD9576_REG_VOUT3_UVD 0x58 +#define BD9576_REG_VOUT4_OVD 0x5a +#define BD9576_REG_VOUT4_UVD 0x5b +#define BD9576_REG_VOUTL1_OVD 0x5d +#define BD9576_REG_VOUTL1_UVD 0x5e + +#define BD9576_MASK_XVD 0x7f + +#define BD9576_REG_VOUT1S_OCW 0x5f +#define BD9576_REG_VOUT1S_OCP 0x60 + +#define BD9576_MASK_VOUT1S_OCW 0x3f +#define BD9576_MASK_VOUT1S_OCP 0x3f + +#define BD957X_MAX_REGISTER 0x61 + +#endif diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h index a9144284cf6d..35b392a0d73a 100644 --- a/include/linux/mfd/rohm-generic.h +++ b/include/linux/mfd/rohm-generic.h @@ -9,7 +9,9 @@ enum rohm_chip_type { ROHM_CHIP_TYPE_BD9571, + ROHM_CHIP_TYPE_BD9573, ROHM_CHIP_TYPE_BD9574, + ROHM_CHIP_TYPE_BD9576, ROHM_CHIP_TYPE_BD70528, ROHM_CHIP_TYPE_BD71815, ROHM_CHIP_TYPE_BD71828, |