diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml index 4fc78efaa550..0a67de054674 100644 --- a/Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml @@ -15,6 +15,9 @@ description: | for LED and a 500 mA single-cell linear charger. Also included is a Coulomb counter, a real-time clock (RTC), and a 32.768 kHz clock gate and two GPOs. +allOf: + - $ref: rohm,pmic-pins.yaml# + properties: compatible: const: rohm,bd71815 @@ -44,11 +47,7 @@ properties: clock-output-names: const: bd71815-32k-out - rohm,clkout-open-drain: - description: clk32kout mode. Set to 1 for "open-drain" or 0 for "cmos". - $ref: /schemas/types.yaml#/definitions/uint32 - minimum: 0 - maximum: 1 + rohm,clkout-open-drain: true rohm,charger-sense-resistor-micro-ohms: minimum: 10000 diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml index 09e7d68e92bf..e5043dec990d 100644 --- a/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/rohm,bd71828-pmic.yaml @@ -15,6 +15,9 @@ description: | single-cell linear charger. Also included is a Coulomb counter, a real-time clock (RTC), and a 32.768 kHz clock gate. +allOf: + - $ref: rohm,pmic-pins.yaml# + properties: compatible: oneOf: @@ -49,11 +52,7 @@ properties: clock-output-names: const: bd71828-32k-out - rohm,clkout-open-drain: - description: clk32kout mode. Set to 1 for "open-drain" or 0 for "cmos". - $ref: /schemas/types.yaml#/definitions/uint32 - minimum: 0 - maximum: 1 + rohm,clkout-open-drain: true rohm,charger-sense-resistor-micro-ohms: minimum: 10000 diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml index b094542339e8..52d0f6e4ef0f 100644 --- a/Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml @@ -15,6 +15,9 @@ description: and a 3000 mA switching charger. ICs also include a Coulomb counter, a real-time clock (RTC), GPIOs and a 32.768 kHz clock gate. +allOf: + - $ref: rohm,pmic-pins.yaml# + # In addition to the properties found from the charger node, PMICs # use properties from a static battery node. Please see the: # Documentation/devicetree/bindings/power/supply/battery.yaml @@ -80,10 +83,7 @@ properties: clock-output-names: const: bd71828-32k-out - rohm,clkout-open-drain: - description: clk32kout mode. Set to 1 for "open-drain" or 0 for "cmos". - $ref: /schemas/types.yaml#/definitions/uint32 - maximum: 1 + rohm,clkout-open-drain: true rohm,charger-sense-resistor-micro-ohms: minimum: 10000 @@ -103,14 +103,6 @@ properties: $ref: /schemas/leds/rohm,bd71828-leds.yaml rohm,pin-fault_b: - $ref: /schemas/types.yaml#/definitions/string - description: - PMIC has an OTP option to use fault_b-pin for different - purposes. Set this property accordingly. OTP options are - OTP0 - bi-directional FAULT_B or READY indicator depending on a - 'sub option' - OTP1 - GPO - OTP2 - Power sequencer output. enum: - faultb - readyind @@ -119,14 +111,6 @@ properties: patternProperties: "^rohm,pin-dvs[0-1]$": - $ref: /schemas/types.yaml#/definitions/string - description: - PMIC has 4 different OTP options to determine the use of dvs-pins. - OTP0 - regulator RUN state control. - OTP1 - GPI. - OTP2 - GPO. - OTP3 - Power sequencer output. - This property specifies the use of the pin. enum: - dvs-input - gpi @@ -134,11 +118,6 @@ patternProperties: - pwrseq "^rohm,pin-exten[0-1]$": - $ref: /schemas/types.yaml#/definitions/string - description: PMIC has an OTP option to use exten0-pin for different - purposes. Set this property accordingly. - OTP0 - GPO - OTP1 - Power sequencer output. enum: - gpo - pwrseq diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd73800-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd73800-pmic.yaml new file mode 100644 index 000000000000..8371e7f2ab84 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/rohm,bd73800-pmic.yaml @@ -0,0 +1,218 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/rohm,bd73800-pmic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BD73800 Power Management Integrated Circuit + +maintainers: + - Matti Vaittinen + +description: + BD73800GW is a single-chip power management IC for battery-powered + portable devices. It integrates 8 buck converters, 4 LDOs and a current + sense amplifier with ADC. Also included is a Real Time Clock (RTC) and a + 32.768 kHz clock gate. Depending on the OTP configuration the BD73800 + may also have interrupt controller and GPIOs. + + There are also different variants called BD71851 and BD71885 which are + almost identical from the software point of view. + +allOf: + - $ref: rohm,pmic-pins.yaml# + +properties: + compatible: + oneOf: + - const: rohm,bd73800 + + - items: + - enum: + - rohm,bd71851 + - rohm,bd71885 + - const: rohm,bd73800 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + # The GPIO1, CLKOUT (GPIO2), FAULT_B and EXTEN_OUT pins can be + # configured to interrupt pins by OTP. + interrupt-controller: true + + "#interrupt-cells": + const: 1 + description: + The IRQ number. 0 is GPIO1, 1 CLKOUT (GPIO2), 2 FAULT_B and 3 EXTEN_OUT. + NOTE, A pin can operate as IRQ source only when the OTP + configuration for it has been set to GPI. + + gpio-controller: true + + "#gpio-cells": + const: 2 + + rohm,pin-gpio1: + $ref: /schemas/types.yaml#/definitions/string + description: + Indicate if the GPIO1 pin has been set to GPI or GPO at manufacturing. + The GPIO1, CLKOUT, FAULT_B and EXTEN_OUT pins may be configured for a + specific purpose (like ADC input, 32.768 clk output, fault indicator or + delivering power sequence to a companion PMIC when multiple PMICs are + used) - but also to be either a GPO or GPI. (When used as a GPI the pin + can also be used as an IRQ source). The pin purpose is determined by + OTP (One Time Programmable memory), typically during device + manufacturing. The OTP can't be read at runtime so device-tree should + describe the GPIO pins. + enum: [gpi, gpo] + + rohm,pin-clkout: + enum: [gpi, gpo] + + rohm,pin-fault_b: + enum: [gpi, gpo] + + rohm,pin-exten: + enum: [gpi, gpo] + + clocks: + description: + The CLKOUT pin may have its purpose overridden by OTP configuration. It + is possible the BD73800 does not output a clock signal. Hence the clk + properties are optional. + maxItems: 1 + + "#clock-cells": + const: 0 + + clock-output-names: + maxItems: 1 + + rohm,clkout-open-drain: true + + regulators: + $ref: /schemas/regulator/rohm,bd73800-regulator.yaml + description: + List of child nodes that specify the regulators. + +required: + - compatible + - reg + - interrupts + - regulators + +additionalProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + pmic: pmic@4b { + compatible = "rohm,bd73800"; + reg = <0x4b>; + + interrupt-parent = <&gpio1>; + interrupts = <29 IRQ_TYPE_LEVEL_LOW>; + + clocks = <&osc 0>; + #clock-cells = <0>; + clock-output-names = "bd73800-32k-out"; + + gpio-controller; + #gpio-cells = <2>; + + rohm,pin-gpio1 = "gpo"; + rohm,pin-exten = "gpi"; + + regulators { + buck1: buck1 { + regulator-name = "buck1"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2000000>; + regulator-always-on; + regulator-ramp-delay = <1250>; + rohm,dvs-run-voltage = <1150000>; + rohm,dvs-suspend-voltage = <950000>; + }; + buck2: buck2 { + regulator-name = "buck2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2000000>; + regulator-always-on; + regulator-ramp-delay = <1250>; + rohm,dvs-run-voltage = <1150000>; + rohm,dvs-suspend-voltage = <950000>; + }; + buck3: buck3 { + regulator-name = "buck3"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <2700000>; + rohm,dvs-run-voltage = <1150000>; + rohm,dvs-idle-voltage = <1100000>; + rohm,dvs-suspend-voltage = <950000>; + regulator-always-on; + }; + buck4: buck4 { + regulator-name = "buck4"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1850000>; + regulator-always-on; + }; + buck5: buck5 { + regulator-name = "buck5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + buck6: buck6 { + regulator-name = "buck6"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + buck7: buck7 { + regulator-name = "buck7"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + buck8: buck8 { + regulator-name = "buck8"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + }; + ldo1: ldo1 { + regulator-name = "ldo1"; + rohm,ldo-range-high; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + ldo2: ldo2 { + regulator-name = "ldo2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + ldo3: ldo3 { + regulator-name = "ldo3"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + ldo4: ldo4 { + regulator-name = "ldo4"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <1750000>; + regulator-always-on; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/rohm,pmic-pins.yaml b/Documentation/devicetree/bindings/mfd/rohm,pmic-pins.yaml new file mode 100644 index 000000000000..0d1082804f5a --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/rohm,pmic-pins.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/rohm,pmic-pins.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM PMICs' OTP configured pin descriptions + +maintainers: + - Matti Vaittinen + +description: + Collection of properties which are commonly used to describe various + ROHM PMIC's pins. + +properties: + rohm,clkout-open-drain: + description: clk32kout mode. Set to 1 for "open-drain" or 0 for "cmos" + a.k.a "push-pull". + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 1 + + rohm,pin-clkout: + $ref: /schemas/types.yaml#/definitions/string + description: + The IC has different OTP options to use clkout-pin for different + purposes. Set this property to indicate the OTP variant. + + Many of the ROHM ICs have pins, which function can be defined in + OTP. Some of them provide no means to detect the pin configuration + at run-time, leaving software unaware of which features the IC + really has. These properties can be used to inform which pins + provide which functionality. IOW, these properties aren't used to + configure pin, they are used to tell what functionality pins on + this specific IC have. + + # Supported functionalities depend on the exact IC model. Following + # values are already defined: gpi, gpo. + # Please, re-use them when adding new ICs. + + rohm,pin-fault_b: + $ref: /schemas/types.yaml#/definitions/string + description: + The IC has different OTP options to use fault_b-pin for different + purposes. Set this property to indicate the OTP variant. + + # Supported functionalities depend on the exact IC model. Following + # values are already defined: gpi, gpo, faultb, readyind, pwrseq. + # Please, re-use them when adding new ICs. + +patternProperties: + "^rohm,pin-dvs[0-1]$": + $ref: /schemas/types.yaml#/definitions/string + description: + The IC has different OTP options to use dvs-pins for different + purposes. Set this property to indicate the OTP variant. + # Supported functionalities depend on the exact IC model. Following + # values are already defined: dvs-input, gpi, gpo, pwrseq. + # Please, re-use them when adding new ICs. + + "^rohm,pin-exten([0-1])?$": + $ref: /schemas/types.yaml#/definitions/string + description: + The IC has an OTP option to use exten -pin(s) for different purposes. + Set this property to indicate the OTP variant. + # Supported functionalities depend on the exact IC model. Following + # values are already defined: gpi, gpo, pwrseq. + # Please, re-use them when adding new ICs. + +# The schema is expected to be reference by IC schemas, which may introduce +# other properties that must be allowed. +additionalProperties: true diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd73800-regulator.yaml b/Documentation/devicetree/bindings/regulator/rohm,bd73800-regulator.yaml new file mode 100644 index 000000000000..2db4585272c5 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/rohm,bd73800-regulator.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/rohm,bd73800-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BD73800 Power Management Integrated Circuit regulators + +maintainers: + - Matti Vaittinen + +description: | + This module is part of the ROHM BD73800 MFD device. For more details + see Documentation/devicetree/bindings/mfd/rohm,bd73800-pmic.yaml. + + The regulator controller is represented as a sub-node of the PMIC node + on the device tree. + + Regulator nodes should be named to buck and ldo. + The valid names for BD73800 regulator nodes are + buck1, buck2, buck3, buck4, buck5, buck6, buck7, buck8 + ldo1, ldo2, ldo3, ldo4 + +patternProperties: + "^buck[1-8]$": + type: object + description: + Properties for a single BUCK regulator. + allOf: + - $ref: regulator.yaml# + - $ref: rohm,pmic-states.yaml# + + properties: + regulator-name: + pattern: "^buck[1-8]$" + description: + should be "buck1", ..., "buck8" + + rohm,dvs-run-voltage: + minimum: 0 + maximum: 3500000 + + rohm,dvs-idle-voltage: + minimum: 0 + maximum: 3500000 + + rohm,dvs-suspend-voltage: + minimum: 0 + maximum: 3500000 + + # BUCKs 1,2,3,4 and 8 support voltages 0.5 - 1.3V + # BUCK 5 supports voltages 0.3 - 1.3V + # BUCKs 6 and 7 support voltages 1.5 - 3.5V + + required: + - regulator-name + + unevaluatedProperties: false + + "^ldo[1-4]$": + type: object + description: + Properties for single LDO regulator. + allOf: + - $ref: regulator.yaml# + - $ref: rohm,pmic-states.yaml# + + properties: + regulator-name: + pattern: "^ldo[1-4]$" + description: + should be "ldo1", ..., "ldo4" + + rohm,dvs-run-voltage: + minimum: 0 + maximum: 1 + + rohm,dvs-idle-voltage: + minimum: 0 + maximum: 1 + + rohm,dvs-suspend-voltage: + minimum: 0 + maximum: 1 + + rohm,ldo-range-high: + type: boolean + description: + LDO1 and LDO3 voltage ranges can be "high" or "low" depending on + OTP. Indicate that the "high" range is used. See comment below + for voltages. + + # LDOs 2 and 4 support voltages 0.75 - 3.3V + # LDOs 1 and 3 may support different ranges depending on OTP. + # either 0.6 - 1.8V or 0.75 - 3.3V. + + unevaluatedProperties: false + +additionalProperties: false diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..b0925fa212cc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23821,6 +23821,7 @@ F: drivers/clk/clk-bd718x7.c F: drivers/gpio/gpio-bd71815.c F: drivers/gpio/gpio-bd71828.c F: drivers/gpio/gpio-bd72720.c +F: drivers/gpio/gpio-bd73800.c F: drivers/mfd/rohm-bd71828.c F: drivers/mfd/rohm-bd718x7.c F: drivers/mfd/rohm-bd9576.c @@ -23840,6 +23841,7 @@ 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-bd72720.h +F: include/linux/mfd/rohm-bd73800.h F: include/linux/mfd/rohm-bd957x.h F: include/linux/mfd/rohm-bd96801.h F: include/linux/mfd/rohm-bd96802.h diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c index 8ddb50c67757..1698b763bcb7 100644 --- a/drivers/clk/clk-bd718x7.c +++ b/drivers/clk/clk-bd718x7.c @@ -21,6 +21,9 @@ #define BD718XX_REG_OUT32K 0x2E /* BD72720 */ #define BD72720_REG_OUT32K 0x9a +/* BD73800 */ +#define BD73800_REG_OUT32K 0x50 + /* * BD71837, BD71847, and BD71828 all use bit [0] to clk output control */ @@ -123,6 +126,10 @@ static int bd71837_clk_probe(struct platform_device *pdev) c->reg = BD72720_REG_OUT32K; c->mask = CLK_OUT_EN_MASK; break; + case ROHM_CHIP_TYPE_BD73800: + c->reg = BD73800_REG_OUT32K; + c->mask = CLK_OUT_EN_MASK; + break; default: dev_err(&pdev->dev, "Unknown clk chip\n"); return -EINVAL; @@ -152,6 +159,7 @@ static const struct platform_device_id bd718x7_clk_id[] = { { .name = "bd71828-clk", .driver_data = ROHM_CHIP_TYPE_BD71828 }, { .name = "bd71815-clk", .driver_data = ROHM_CHIP_TYPE_BD71815 }, { .name = "bd72720-clk", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { .name = "bd73800-clk", .driver_data = ROHM_CHIP_TYPE_BD73800 }, { } }; MODULE_DEVICE_TABLE(platform, bd718x7_clk_id); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 07b7f8ab0ccb..4bf91d309079 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1385,6 +1385,18 @@ config GPIO_BD72720 be configured to GPO on the ROHM PMIC. The pin configuration is done on OTP at manufacturing. +config GPIO_BD73800 + tristate "ROHM BD73800 GPIO support" + depends on MFD_ROHM_BD71828 + select GPIO_REGMAP + help + Support for GPIOs on ROHM BD73800 PMIC. There can be up to 4 + GPI or GPO pins available on the PMIC in total. The purpose of + the pins is decided at the device manufacturing by OTP + configuration and can't be reconfigured later. Enable this + if your PMIC has pins set as GPIs or GPOs and if you wish to + control the pins via the GPIO framework. + config GPIO_BD9571MWV tristate "ROHM BD9571 GPIO support" depends on MFD_BD9571MWV diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ce56e9b3f55d..8b7ac831fa58 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o obj-$(CONFIG_GPIO_BD72720) += gpio-bd72720.o +obj-$(CONFIG_GPIO_BD73800) += gpio-bd73800.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o diff --git a/drivers/gpio/gpio-bd73800.c b/drivers/gpio/gpio-bd73800.c new file mode 100644 index 000000000000..2be2a885820e --- /dev/null +++ b/drivers/gpio/gpio-bd73800.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support to GPIOs on ROHM BD73800 + * Copyright 2024 ROHM Semiconductors. + * Author: Matti Vaittinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BD73800_GPIO_MAX_PINS 4 +/* + * The BD73800 has several "one time programmable" (OTP) configurations which + * can be set at manufacturing phase. Some of these options allow using + * individual pins as GPI or GPO (not both at the same time). The OTP + * configuration can't be read at run-time, so drivers rely on device-tree to + * advertise the OTP programmed in manufacturing. + * + * The pins which can be used as GPIO are: + * GPIO1, CLKOUT (GPIO2), FAULT_B, EXTEN_OUT. + * + * The OTP options 2 and 3 state for all the pins: + * - OTP2: GPI (also IRQ source) + * - OTP3: GPO (NOTE: This is actually 2 different OTP options. Either a + * register controllable output or a power-sequence controlled output. + * The "gpo" referred here means only the register controllable output.) + * The datasheet refers to this as: " output is controlled by + * GPIO_OUT or power on/off sequencer to control external VRs. ON/OFF + * sequence timing is configurable." + * + * The data-sheet further says that the GPI/GPO is not a default OTP + * configuration for any of the pins. Hence the GPIO driver defaults to a pin + * not being a GPI or GPO, but requires the pin to be explicitly marked as a + * GPI or GPO in the device-tree. + * + * DT properties: + * "rohm,pin-gpio1", "rohm,pin-clkout", "rohm,pin-fault_b", "rohm,pin-exten" + * can be set to one of the values "gpi" or "gpo" to enable them to be used as + * GPIO. + * + * The amount of GPIO lines the chip exposes to the user-space (chip.ngpio) is + * always the same, regardless of the OTP variant in use. The lines which are + * not usable as GPIO on a given OTP variant are marked invalid via the GPIO + * valid_mask. This way the user-space always sees a constant amount of GPIO + * lines, where numbering (relative to the chip's base) stays also the same. + * Users can then use the valid_mask to find out which of the lines are usable. + */ + +struct bd73800_gpio { + /* dev points to the platform device for devm and prints */ + struct device *dev; + struct regmap *regmap; + /* Pins which have been OTP configured as GPI or GPO */ + DECLARE_BITMAP(valid_mask, BD73800_GPIO_MAX_PINS); + /* Subset of valid_mask - pins which have been OTP configured as GPO */ + DECLARE_BITMAP(output_mask, BD73800_GPIO_MAX_PINS); +}; + +static const char * const bd73800_gpio_properties[BD73800_GPIO_MAX_PINS] = { + "rohm,pin-gpio1", + "rohm,pin-clkout", + "rohm,pin-fault_b", + "rohm,pin-exten" +}; + +static int bd73800_gpio_get_pins(struct bd73800_gpio *data) +{ + struct device *parent = data->dev->parent; + const char *val; + int i, ret; + + for (i = 0; i < BD73800_GPIO_MAX_PINS; i++) { + ret = device_property_read_string(parent, + bd73800_gpio_properties[i], + &val); + if (ret) { + if (ret == -EINVAL) + continue; + + return dev_err_probe(data->dev, ret, + "pin %d (%s), bad configuration\n", i, + bd73800_gpio_properties[i]); + } + + if (!strcmp(val, "gpi")) { + __set_bit(i, data->valid_mask); + } else if (!strcmp(val, "gpo")) { + __set_bit(i, data->valid_mask); + __set_bit(i, data->output_mask); + } else { + dev_warn(data->dev, + "pin %d (%s), unknown value '%s' ignored\n", i, + bd73800_gpio_properties[i], val); + } + } + + return 0; +} + +static int bd73800_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct gpio_regmap *gpio = gpiochip_get_data(gc); + struct bd73800_gpio *data = gpio_regmap_get_drvdata(gpio); + + bitmap_copy(valid_mask, data->valid_mask, ngpios); + + return 0; +} + +/* + * The used register depends on OTP: + * - If OTP has set pin as GPO, only the GPO_OUT register is valid. + * - If OTP has set pin as GPI, only the INT_5_SRC register is valid. + */ +static int bd73800_gpio_reg_mask_xlate(struct gpio_regmap *gpio, + enum gpio_regmap_operation op, + unsigned int base, unsigned int offset, + unsigned int *reg, unsigned int *mask) +{ + struct bd73800_gpio *data = gpio_regmap_get_drvdata(gpio); + bool is_output = test_bit(offset, data->output_mask); + + if (is_output) + *reg = BD73800_REG_GPO_OUT; + else + *reg = base; + + *mask = BIT(offset); + + return 0; +} + +static int gpo_bd73800_probe(struct platform_device *pdev) +{ + struct gpio_regmap_config config = { }; + struct bd73800_gpio *data; + struct device *parent, *dev; + int ret; + + dev = &pdev->dev; + /* The device-tree and regmap come from MFD => use parent for that */ + parent = dev->parent; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + data->regmap = dev_get_regmap(parent, NULL); + if (!data->regmap) + return dev_err_probe(dev, -ENODEV, "no parent regmap\n"); + + ret = bd73800_gpio_get_pins(data); + if (ret) + return ret; + + if (bitmap_empty(data->valid_mask, BD73800_GPIO_MAX_PINS)) { + /* + * The BD73800 may or may not have pins allocated for GPIO + * depending on the OTP used at manufacturing. + * If there are no pins, then we have nothing to do. + */ + dev_dbg(dev, "no GPIO pins\n"); + return -ENODEV; + } + + config.parent = parent; + config.regmap = data->regmap; + config.label = "bd73800"; + config.ngpio = BD73800_GPIO_MAX_PINS; + config.reg_dat_base = BD73800_REG_INT_5_SRC; + config.reg_set_base = BD73800_REG_GPO_OUT; + config.reg_mask_xlate = bd73800_gpio_reg_mask_xlate; + config.init_valid_mask = bd73800_gpio_init_valid_mask; + /* All pins that are valid GPIO lines also have a fixed direction */ + config.fixed_direction_mask = data->valid_mask; + config.fixed_direction_output = data->output_mask; + config.drvdata = data; + + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config)); +} + +static const struct platform_device_id bd73800_gpio_id[] = { + { "bd73800-gpio" }, + { }, +}; +MODULE_DEVICE_TABLE(platform, bd73800_gpio_id); + +static struct platform_driver gpo_bd73800_driver = { + .driver = { + .name = "bd73800-gpio", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = gpo_bd73800_probe, + .id_table = bd73800_gpio_id, +}; +module_platform_driver(gpo_bd73800_driver); + +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("GPIO interface for BD73800"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index e4fd4572472f..439dbf5ee110 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2226,7 +2226,7 @@ config MFD_ROHM_BD718XX and emergency shut down as well as 32,768KHz clock output. config MFD_ROHM_BD71828 - tristate "ROHM BD718[15/28/79], BD72720 and BD73900 PMICs" + tristate "ROHM BD718[15/28/79], BD72720 and BD73[8/9]00 PMICs" depends on I2C=y depends on OF select REGMAP_I2C @@ -2235,14 +2235,17 @@ config MFD_ROHM_BD71828 select MFD_ROHM_PWRBUTTON help Select this option to get support for the ROHM BD71815, BD71828, - BD71879, BD72720 and BD73900 Power Management ICs (PMICs). These are - single-chip Power Management ICs (PMIC), mainly for battery-powered - portable devices. + BD71879, BD72720, BD73800 and BD73900 Power Management ICs (PMICs). + These are single-chip Power Management ICs (PMIC), mainly for + battery-powered portable devices. The BD71815 has 5 bucks, 7 LDOs, and a boost for driving LEDs. The BD718[28/79] have 7 buck converters and 7 LDOs. The BD72720 and the BD73900 have 10 bucks and 11 LDOs. - All ICs provide a single-cell linear charger, a Coulomb counter, - a Real-Time Clock (RTC), GPIOs and a 32.768 kHz clock gate. + All the above ICs provide a single-cell linear charger, and a Coulomb + counter. + The BD73800 has 8 bucks, 4 LDOs and ADC for power/current + accumulation but no charging logic. All these PMICs integrate also 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" diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c index bf4df8e1b34c..46faef7058f8 100644 --- a/drivers/mfd/rohm-bd71828.c +++ b/drivers/mfd/rohm-bd71828.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2019 ROHM Semiconductors + * Copyright (C) 2019, 2026 ROHM Semiconductor * - * ROHM BD718[15/28/79] and BD72720 PMIC driver + * ROHM BD718[15/28/79], BD72720 and BD73[8/9]00 PMIC driver */ #include @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,12 @@ static const struct resource bd72720_rtc_irqs[] = { DEFINE_RES_IRQ_NAMED(BD72720_INT_RTC2, "bd70528-rtc-alm-2"), }; +static const struct resource bd73800_rtc_irqs[] = { + DEFINE_RES_IRQ_NAMED(BD73800_INT_RTC0, "bd70528-rtc-alm-0"), + DEFINE_RES_IRQ_NAMED(BD73800_INT_RTC1, "bd70528-rtc-alm-1"), + DEFINE_RES_IRQ_NAMED(BD73800_INT_RTC2, "bd70528-rtc-alm-2"), +}; + static const struct resource bd71815_power_irqs[] = { DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_RMV, "bd71815-dcin-rmv"), DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_OUT, "bd71815-dcin-clps-out"), @@ -230,6 +237,17 @@ static const struct mfd_cell bd72720_mfd_cells[] = { /* Power button is registered separately */ }; +static const struct mfd_cell bd73800_mfd_cells[] = { + { .name = "bd73800-pmic", }, + { .name = "bd73800-clk", }, + { .name = "bd73800-gpio", }, + { + .name = "bd73800-rtc", + .num_resources = ARRAY_SIZE(bd73800_rtc_irqs), + .resources = bd73800_rtc_irqs, + }, +}; + static const struct regmap_range bd71815_volatile_ranges[] = { regmap_reg_range(BD71815_REG_SEC, BD71815_REG_YEAR), regmap_reg_range(BD71815_REG_CONF, BD71815_REG_BAT_TEMP), @@ -339,6 +357,17 @@ static const struct regmap_range bd72720_volatile_ranges_4c[] = { BD72720_UNWRAP_REG_RANGE(BD72720_REG_IMPCHK_CTRL, BD72720_REG_IMPCHK_CTRL), }; +static const struct regmap_range bd73800_volatile_ranges[] = { + regmap_reg_range(BD73800_REG_POR_REASON, BD73800_REG_POW_STATE), + regmap_reg_range(BD73800_REG_PS_CTRL_1, BD73800_REG_PS_CTRL_2), + regmap_reg_range(BD73800_REG_RCVNUM, BD73800_REG_RCVNUM), + regmap_reg_range(BD73800_REG_RTC_SEC, BD73800_REG_RTC_YEAR), + regmap_reg_range(BD73800_REG_RTC_CONF, BD73800_REG_RTC_CONF), + regmap_reg_range(BD73800_REG_ADC_ACCUM_KICK, BD73800_REG_ADC_TEMP_VAL0), + regmap_reg_range(BD73800_REG_INT_MAIN_STAT, BD73800_REG_INT_5_STAT), + regmap_reg_range(BD73800_REG_INT_MAIN_SRC, BD73800_REG_INT_5_SRC), +}; + static const struct regmap_access_table bd71815_volatile_regs = { .yes_ranges = &bd71815_volatile_ranges[0], .n_yes_ranges = ARRAY_SIZE(bd71815_volatile_ranges), @@ -364,6 +393,24 @@ static const struct regmap_access_table bd72720_volatile_regs_4c = { .n_yes_ranges = ARRAY_SIZE(bd72720_volatile_ranges_4c), }; +static const struct regmap_access_table bd73800_volatile_regs = { + .yes_ranges = bd73800_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(bd73800_volatile_ranges), +}; + +static const struct regmap_range bd73800_read_only_ranges[] = { + regmap_reg_range(BD73800_REG_PRODUCT_ID, BD73800_REG_NVMVERSION), + regmap_reg_range(BD73800_REG_POW_STATE, BD73800_REG_POW_STATE), + regmap_reg_range(BD73800_REG_ADC_ACCUM_CNT2, BD73800_REG_ADC_TEMP_VAL0), + regmap_reg_range(BD73800_REG_INT_MAIN_STAT, BD73800_REG_INT_MAIN_STAT), + regmap_reg_range(BD73800_REG_INT_MAIN_SRC, BD73800_REG_INT_5_SRC), +}; + +static const struct regmap_access_table bd73800_ro_regs = { + .no_ranges = bd73800_read_only_ranges, + .n_no_ranges = ARRAY_SIZE(bd73800_read_only_ranges), +}; + static const struct regmap_config bd71815_regmap = { .reg_bits = 8, .val_bits = 8, @@ -448,6 +495,15 @@ static const struct regmap_config bd72720_regmap_4c = { .cache_type = REGCACHE_MAPLE, }; +static const struct regmap_config bd73800_regmap = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &bd73800_ro_regs, + .volatile_table = &bd73800_volatile_regs, + .max_register = BD73800_MAX_REGISTER - 1, + .cache_type = REGCACHE_MAPLE, +}; + /* * 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 @@ -779,6 +835,60 @@ static int bd72720_set_type_config(unsigned int **buf, unsigned int type, return regmap_irq_set_type_config_simple(buf, type, irq_data, idx, irq_drv_data); } +static const struct regmap_irq bd73800_irqs[] = { + /* INT_STAT_1 register IRQs, ADC and RTC */ + REGMAP_IRQ_REG(BD73800_INT_ADC_ACCUM_DONE, 0, BD73800_INT_ADC_ACCUM_DONE_MASK), + REGMAP_IRQ_REG(BD73800_INT_ADC_ACCUM_OVF, 0, BD73800_INT_ADC_ACCUM_OVF_MASK), + REGMAP_IRQ_REG(BD73800_INT_ADC_ACCUM_VAL, 0, BD73800_INT_ADC_ACCUM_VAL_MASK), + REGMAP_IRQ_REG(BD73800_INT_ADC_ACCUM_TW, 0, BD73800_INT_ADC_ACCUM_TW_MASK), + REGMAP_IRQ_REG(BD73800_INT_ADC_POW_VAL, 0, BD73800_INT_ADC_POW_VAL_MASK), + REGMAP_IRQ_REG(BD73800_INT_RTC0, 0, BD73800_INT_RTC0_MASK), + REGMAP_IRQ_REG(BD73800_INT_RTC1, 0, BD73800_INT_RTC1_MASK), + REGMAP_IRQ_REG(BD73800_INT_RTC2, 0, BD73800_INT_RTC2_MASK), + + /* BUCK reg interrupts */ + /* INT_STAT_2 IRQs */ + REGMAP_IRQ_REG(BD73800_INT_BUCK1_DVS_DONE, 1, BD73800_INT_BUCK1_DVS_DONE_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK2_DVS_DONE, 1, BD73800_INT_BUCK2_DVS_DONE_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK3_DVS_DONE, 1, BD73800_INT_BUCK3_DVS_DONE_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK4_DVS_DONE, 1, BD73800_INT_BUCK4_DVS_DONE_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK5_DVS_DONE, 1, BD73800_INT_BUCK5_DVS_DONE_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK6_DVS_DONE, 1, BD73800_INT_BUCK6_DVS_DONE_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK7_DVS_DONE, 1, BD73800_INT_BUCK7_DVS_DONE_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK8_DVS_DONE, 1, BD73800_INT_BUCK8_DVS_DONE_MASK), + /* INT_STAT_3 IRQs */ + REGMAP_IRQ_REG(BD73800_INT_BUCK1_OCP, 2, BD73800_INT_BUCK1_OCP_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK2_OCP, 2, BD73800_INT_BUCK2_OCP_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK3_OCP, 2, BD73800_INT_BUCK3_OCP_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK4_OCP, 2, BD73800_INT_BUCK4_OCP_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK5_OCP, 2, BD73800_INT_BUCK5_OCP_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK6_OCP, 2, BD73800_INT_BUCK6_OCP_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK7_OCP, 2, BD73800_INT_BUCK7_OCP_MASK), + REGMAP_IRQ_REG(BD73800_INT_BUCK8_OCP, 2, BD73800_INT_BUCK8_OCP_MASK), + + /* INT_STAT_4 IRQs, power-button, WDG and reset */ + REGMAP_IRQ_REG(BD73800_INT_PBTN_LONG_PRESS, 3, BD73800_INT_PBTN_LONG_PRESS_MASK), + REGMAP_IRQ_REG(BD73800_INT_PBTN_MID_PRESS, 3, BD73800_INT_PBTN_MID_PRESS_MASK), + /* + * The SHORT_PUSH is generated when button is first pressed (longer + * than configured time limit), and then released before the MID_PRESS + * time limit. The SHORT_PRESS is generated immediately when button is + * pressed for longer than configured limit, whether it is released or + * not. + */ + REGMAP_IRQ_REG(BD73800_INT_PBTN_SHORT_PUSH, 3, BD73800_INT_PBTN_SHORT_PUSH_MASK), + REGMAP_IRQ_REG(BD73800_INT_PBTN_SHORT_PRESS, 3, BD73800_INT_PBTN_SHORT_PRESS_MASK), + REGMAP_IRQ_REG(BD73800_INT_WDG, 3, BD73800_INT_WDG_MASK), + REGMAP_IRQ_REG(BD73800_INT_SWRESET, 3, BD73800_INT_SWRESET_MASK), + REGMAP_IRQ_REG(BD73800_INT_SEQ_DONE, 3, BD73800_INT_SEQ_DONE_MASK), + + /* INT_STAT_5 IRQs, GPIO */ + REGMAP_IRQ_REG(BD73800_INT_GPIO1, 4, BD73800_INT_GPIO1_MASK), + REGMAP_IRQ_REG(BD73800_INT_GPIO2, 4, BD73800_INT_GPIO2_MASK), + REGMAP_IRQ_REG(BD73800_INT_GPIO3, 4, BD73800_INT_GPIO3_MASK), + REGMAP_IRQ_REG(BD73800_INT_GPIO4, 4, BD73800_INT_GPIO4_MASK), +}; + static const struct regmap_irq_chip bd71828_irq_chip = { .name = "bd71828_irq", .main_status = BD71828_REG_INT_MAIN, @@ -833,6 +943,25 @@ static const struct regmap_irq_chip bd72720_irq_chip = { .irq_reg_stride = 1, }; +static const struct regmap_irq_chip bd73800_irq_chip = { + .name = "bd73800_irq", + .main_status = BD73800_REG_INT_MAIN_STAT, + .irqs = bd73800_irqs, + .num_irqs = ARRAY_SIZE(bd73800_irqs), + .status_base = BD73800_REG_INT_1_STAT, + .unmask_base = BD73800_REG_INT_1_EN, + .ack_base = BD73800_REG_INT_1_STAT, + .init_ack_masked = true, + .num_regs = 5, + .num_main_regs = 1, + /* + * Ignore mirrored bits [7:5]. They are handled as part of normal INT_4 + * handling. + */ + .num_main_status_bits = 5, + .irq_reg_stride = 1, +}; + static int set_clk_mode(struct device *dev, struct regmap *regmap, int clkmode_reg) { @@ -971,6 +1100,17 @@ static int bd71828_i2c_probe(struct i2c_client *i2c) main_lvl_val = BD72720_MASK_LVL1_EN_ALL; break; } + case ROHM_CHIP_TYPE_BD73800: + mfd = bd73800_mfd_cells; + cells = ARRAY_SIZE(bd73800_mfd_cells); + regmap_config = &bd73800_regmap; + irqchip = &bd73800_irq_chip; + clkmode_reg = BD73800_REG_OUT32K; + button_irq = BD73800_INT_PBTN_SHORT_PUSH; + main_lvl_mask_reg = BD73800_REG_INT_MAIN_EN; + main_lvl_val = BD73800_INT_MAIN_EN_ALL; + break; + default: dev_err(&i2c->dev, "Unknown device type"); return -EINVAL; @@ -1051,6 +1191,9 @@ static const struct of_device_id bd71828_of_match[] = { }, { .compatible = "rohm,bd72720", .data = (void *)ROHM_CHIP_TYPE_BD72720, + }, { + .compatible = "rohm,bd73800", + .data = (void *)ROHM_CHIP_TYPE_BD73800, }, { }, }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 89789ac7a786..13928af8cad4 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -241,12 +241,12 @@ config REGULATOR_BD71815 will be called bd71815-regulator. config REGULATOR_BD71828 - tristate "ROHM BD71828, BD72720 and BD73900 Power Regulators" + tristate "ROHM BD71828, BD72720 and BD73[8/9]00 Power Regulators" depends on MFD_ROHM_BD71828 select REGULATOR_ROHM help This driver supports voltage regulators on ROHM BD71828, - BD71879, BD72720 and BD73900 PMICs. This will enable + BD71879, BD72720 and BD73[8/9]00 PMICs. This will enable support for the software controllable buck and LDO regulators. This driver can also be built as a module. If so, the module diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c index 2ced81df0c02..dea551cf658a 100644 --- a/drivers/regulator/bd71828-regulator.c +++ b/drivers/regulator/bd71828-regulator.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2019 ROHM Semiconductors // bd71828-regulator.c ROHM BD71828GW-DS1 regulator driver -// #include #include @@ -10,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +31,13 @@ struct bd71828_regulator_data { int reg_init_amnt; }; +/* + * LDO1 and LDO3 node names are used also in the driver to find OTP setting for + * the used voltage range. + */ +#define LDO1_NODE_NAME "ldo1" +#define LDO3_NODE_NAME "ldo3" + static const struct reg_init bd71828_buck1_inits[] = { /* * DVS Buck voltages can be changed by register values or via GPIO. @@ -172,6 +179,55 @@ static const struct linear_range bd72720_ldo6_volts[] = { REGULATOR_LINEAR_RANGE(1800000, 0x79, 0x7f, 0), }; +static const struct linear_range bd73800_buck12348_volts[] = { + REGULATOR_LINEAR_RANGE(500000, 0x00, 0x50, 10000), + REGULATOR_LINEAR_RANGE(1300000, 0x51, 0xff, 0), +}; + +static const unsigned int bd73800_buck5_volt_range_sel[] = { + 0x0, 0x0, 0x1, 0x1, +}; + +static const struct linear_range bd73800_buck5_volts[] = { + /* range selector 0 */ + REGULATOR_LINEAR_RANGE(500000, 0x00, 0x50, 10000), + REGULATOR_LINEAR_RANGE(1300000, 0x51, 0x7f, 0), + /* range selector 1 */ + REGULATOR_LINEAR_RANGE(300000, 0x00, 0x46, 10000), + REGULATOR_LINEAR_RANGE(1000000, 0x47, 0x7f, 0), +}; + +/* + * BUCK5 has two different pickable ranges, each having voltages matching + * selectors 0x0 to 0x7f. This gives 0x7f + 1 different voltages for each of + * the ranges. + */ +#define BD73800_NUM_BUCK5_VOLTS ((0x7f + 1) * 2) + +static const struct linear_range bd73800_buck67_volts[] = { + REGULATOR_LINEAR_RANGE(1500000, 0x00, 0xc8, 10000), + REGULATOR_LINEAR_RANGE(3500000, 0xc9, 0xff, 0), +}; + +/* + * On the BD73800 the LDO1 and LDO3 support two different voltage areas. + * Whether to use 'high' or 'low' ranges is configured by OTP. There seems + * to be no register to read the configured area so it must be given via + * device-tree properties. + */ +static const struct linear_range bd73800_ldo13_low_volts[] = { + REGULATOR_LINEAR_RANGE(600000, 0x00, 0x78, 10000), + REGULATOR_LINEAR_RANGE(1800000, 0x79, 0xff, 0), +}; + +static const struct linear_range bd73800_ldo13_high_volts[] = { + REGULATOR_LINEAR_RANGE(750000, 0x00, 0xff, 10000), +}; + +static const struct linear_range bd73800_ldo24_volts[] = { + REGULATOR_LINEAR_RANGE(750000, 0x00, 0xff, 10000), +}; + static const unsigned int bd71828_ramp_delay[] = { 2500, 5000, 10000, 20000 }; /* @@ -225,6 +281,36 @@ static int bd71828_ldo6_parse_dt(struct device_node *np, return 0; } +/* Operations for all other BUCKs but BUCK 5 */ +static const struct regulator_ops bd73800_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_ramp_delay = regulator_set_ramp_delay_regmap, +}; + +static const struct regulator_ops bd73800_buck5_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_pickable_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_pickable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap, + .set_ramp_delay = regulator_set_ramp_delay_regmap, +}; + +static const struct regulator_ops bd73800_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + static const struct regulator_ops bd71828_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -1565,6 +1651,412 @@ static const struct bd71828_regulator_data bd72720_rdata[] = { }, }; +static const struct bd71828_regulator_data bd73800_rdata[] = { + { + .desc = { + .name = "buck1", + .of_match = of_match_ptr("buck1"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_BUCK1, + .ops = &bd73800_buck_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_buck12348_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_BUCK1_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_BUCK1_VOLT_RUN, + .vsel_mask = BD73800_MASK_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD73800_REG_BUCK1_MODE, + .ramp_mask = BD73800_MASK_RAMP_DELAY, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_BUCK1_VOLT_RUN, + .run_mask = BD73800_MASK_VOLT, + .idle_reg = BD73800_REG_BUCK1_VOLT_IDLE, + .idle_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_reg = BD73800_REG_BUCK1_VOLT_SUSP, + .suspend_mask = BD73800_MASK_VOLT, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "buck2", + .of_match = of_match_ptr("buck2"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_BUCK2, + .ops = &bd73800_buck_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_buck12348_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_BUCK2_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_BUCK2_VOLT_RUN, + .vsel_mask = BD73800_MASK_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD73800_REG_BUCK2_MODE, + .ramp_mask = BD73800_MASK_RAMP_DELAY, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_BUCK2_VOLT_RUN, + .run_mask = BD73800_MASK_VOLT, + .idle_reg = BD73800_REG_BUCK2_VOLT_IDLE, + .idle_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_reg = BD73800_REG_BUCK2_VOLT_SUSP, + .suspend_mask = BD73800_MASK_VOLT, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "buck3", + .of_match = of_match_ptr("buck3"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_BUCK3, + .ops = &bd73800_buck_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_buck12348_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_BUCK3_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_BUCK3_VOLT_RUN, + .vsel_mask = BD73800_MASK_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD73800_REG_BUCK3_MODE, + .ramp_mask = BD73800_MASK_RAMP_DELAY, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_BUCK3_VOLT_RUN, + .run_mask = BD73800_MASK_VOLT, + .idle_reg = BD73800_REG_BUCK3_VOLT_IDLE, + .idle_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_reg = BD73800_REG_BUCK3_VOLT_SUSP, + .suspend_mask = BD73800_MASK_VOLT, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + + }, { + .desc = { + .name = "buck4", + .of_match = of_match_ptr("buck4"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_BUCK4, + .ops = &bd73800_buck_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_buck12348_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_BUCK4_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_BUCK4_VOLT_RUN, + .vsel_mask = BD73800_MASK_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD73800_REG_BUCK4_MODE, + .ramp_mask = BD73800_MASK_RAMP_DELAY, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_BUCK4_VOLT_RUN, + .run_mask = BD73800_MASK_VOLT, + .idle_reg = BD73800_REG_BUCK4_VOLT_IDLE, + .idle_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_reg = BD73800_REG_BUCK4_VOLT_SUSP, + .suspend_mask = BD73800_MASK_VOLT, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "buck5", + .of_match = of_match_ptr("buck5"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_BUCK5, + .ops = &bd73800_buck5_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_buck5_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_buck5_volts), + .linear_range_selectors_bitfield = + bd73800_buck5_volt_range_sel, + .n_voltages = BD73800_NUM_BUCK5_VOLTS, + .enable_reg = BD73800_REG_BUCK5_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_BUCK5_VOLT_RUN, + .vsel_mask = BD73800_MASK_BUCK5_VOLT, + .vsel_range_reg = BD73800_REG_BUCK5_MODE, + .vsel_range_mask = BD73800_BUCK5_RANGE_MASK, + .range_applied_by_vsel = true, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD73800_REG_BUCK5_MODE, + .ramp_mask = BD73800_MASK_RAMP_DELAY, + .owner = THIS_MODULE, + }, + .dvs = { + /* + * We don't support setting the IDLE / SUSPEND voltages + * for the BUCK 5 for now. Reason is that the pickable + * range selector bit is common for all states (RUN, + * SUSPEND and IDLE), and toggling it for one impacts + * also others. + * Furthermore, writing the BD73800 range selector does + * not impact the RUN voltage until a new voltage value + * is also written to the vsel register. Hence the + * voltage change can be done without any 'intermediate + * voltages', even when the range is changed when the + * BUCK is enabled. + * It, however, is not documented how the IDLE / SUSPEND + * states work in this regard. I expect the full + * combination of the range selector and vsel is taken + * into account at the state change, no matter when they + * have been written to. + */ + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_BUCK5_VOLT_RUN, + .run_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + + }, { + .desc = { + .name = "buck6", + .of_match = of_match_ptr("buck6"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_BUCK6, + .ops = &bd73800_buck_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_buck67_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_buck67_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_BUCK6_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_BUCK6_VOLT_RUN, + .vsel_mask = BD73800_MASK_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD73800_REG_BUCK6_MODE, + .ramp_mask = BD73800_MASK_RAMP_DELAY, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_BUCK6_VOLT_RUN, + .run_mask = BD73800_MASK_VOLT, + .idle_reg = BD73800_REG_BUCK6_VOLT_IDLE, + .idle_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_reg = BD73800_REG_BUCK6_VOLT_SUSP, + .suspend_mask = BD73800_MASK_VOLT, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "buck7", + .of_match = of_match_ptr("buck7"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_BUCK7, + .ops = &bd73800_buck_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_buck67_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_buck67_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_BUCK7_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_BUCK7_VOLT_RUN, + .vsel_mask = BD73800_MASK_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD73800_REG_BUCK7_MODE, + .ramp_mask = BD73800_MASK_RAMP_DELAY, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_BUCK7_VOLT_RUN, + .run_mask = BD73800_MASK_VOLT, + .idle_reg = BD73800_REG_BUCK7_VOLT_IDLE, + .idle_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_reg = BD73800_REG_BUCK7_VOLT_SUSP, + .suspend_mask = BD73800_MASK_VOLT, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "buck8", + .of_match = of_match_ptr("buck8"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_BUCK8, + .ops = &bd73800_buck_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_buck12348_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_BUCK8_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_BUCK8_VOLT_RUN, + .vsel_mask = BD73800_MASK_VOLT, + .ramp_delay_table = bd71828_ramp_delay, + .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay), + .ramp_reg = BD73800_REG_BUCK8_MODE, + .ramp_mask = BD73800_MASK_RAMP_DELAY, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_BUCK8_VOLT_RUN, + .run_mask = BD73800_MASK_VOLT, + .idle_reg = BD73800_REG_BUCK8_VOLT_IDLE, + .idle_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_reg = BD73800_REG_BUCK8_VOLT_SUSP, + .suspend_mask = BD73800_MASK_VOLT, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "ldo1", + .of_match = of_match_ptr(LDO1_NODE_NAME), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_LDO1, + .ops = &bd73800_ldo_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_ldo13_low_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_ldo13_low_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_LDO1_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_LDO1_VOLT, + .vsel_mask = BD73800_MASK_VOLT, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + /* + * LDOs support only single voltage for all states. + * Voltage can be individually enabled for each state + * though. Allow setting enable/disable config by + * setting the _on_mask for all supported states. + */ + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_LDO1_VOLT, + .run_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "ldo2", + .of_match = of_match_ptr("ldo2"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_LDO2, + .ops = &bd73800_ldo_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_ldo24_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_ldo24_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_LDO2_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_LDO2_VOLT, + .vsel_mask = BD73800_MASK_VOLT, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_LDO2_VOLT, + .run_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "ldo3", + .of_match = of_match_ptr(LDO3_NODE_NAME), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_LDO3, + .ops = &bd73800_ldo_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_ldo13_low_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_ldo13_low_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_LDO3_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_LDO3_VOLT, + .vsel_mask = BD73800_MASK_VOLT, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_LDO3_VOLT, + .run_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, { + .desc = { + .name = "ldo4", + .of_match = of_match_ptr("ldo4"), + .regulators_node = of_match_ptr("regulators"), + .id = BD73800_LDO4, + .ops = &bd73800_ldo_ops, + .type = REGULATOR_VOLTAGE, + .linear_ranges = bd73800_ldo24_volts, + .n_linear_ranges = ARRAY_SIZE(bd73800_ldo24_volts), + .n_voltages = BD73800_NUM_VOLTS, + .enable_reg = BD73800_REG_LDO4_ON, + .enable_mask = BD73800_MASK_RUN_EN, + .vsel_reg = BD73800_REG_LDO4_VOLT, + .vsel_mask = BD73800_MASK_VOLT, + .owner = THIS_MODULE, + .of_parse_cb = buck_set_hw_dvs_levels, + }, + .dvs = { + .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE | + ROHM_DVS_LEVEL_SUSPEND, + .run_reg = BD73800_REG_LDO4_VOLT, + .run_mask = BD73800_MASK_VOLT, + .idle_on_mask = BD73800_MASK_IDLE_EN, + .suspend_on_mask = BD73800_MASK_SUSP_EN, + }, + }, +}; + static int bd72720_buck10_ldon_head_mode(struct device *dev, struct device_node *npreg, struct regmap *regmap, @@ -1619,6 +2111,52 @@ static int bd72720_dt_parse(struct device *dev, return bd72720_buck10_ldon_head_mode(dev, nproot, regmap, buck10_desc); } +static bool bd73800_use_ldo_high_range(struct device_node *nproot, + const char *ldo_node) +{ + struct device_node *np __free(device_node) = + of_get_child_by_name(nproot, ldo_node); + + if (!np) + return false; + + return of_property_read_bool(np, "rohm,ldo-range-high"); +} + +static int bd73800_check_ldo_otp_options(struct device *dev, + struct bd71828_regulator_data *d, + unsigned int num_reg_data) +{ + struct device_node *nproot __free(device_node) = + of_get_child_by_name(dev->parent->of_node, "regulators"); + + /* + * The code assumes regulator IDs to start from 0 and to match the + * indexing of regulator data arrays. WARN if someone changes this. + */ + WARN_ON(d[BD73800_LDO1].desc.id != BD73800_LDO1); + WARN_ON(d[BD73800_LDO3].desc.id != BD73800_LDO3); + + if (!nproot) { + dev_err(dev, "failed to find regulators node\n"); + return -ENODEV; + } + + if (bd73800_use_ldo_high_range(nproot, LDO1_NODE_NAME)) { + d[BD73800_LDO1].desc.linear_ranges = bd73800_ldo13_high_volts; + d[BD73800_LDO1].desc.n_linear_ranges = + ARRAY_SIZE(bd73800_ldo13_high_volts); + } + + if (bd73800_use_ldo_high_range(nproot, LDO3_NODE_NAME)) { + d[BD73800_LDO3].desc.linear_ranges = bd73800_ldo13_high_volts; + d[BD73800_LDO3].desc.n_linear_ranges = + ARRAY_SIZE(bd73800_ldo13_high_volts); + } + + return 0; +} + static int bd71828_probe(struct platform_device *pdev) { int i, j, ret, num_regulators; @@ -1656,6 +2194,23 @@ static int bd71828_probe(struct platform_device *pdev) num_regulators = ARRAY_SIZE(bd71828_rdata); break; + + case ROHM_CHIP_TYPE_BD73800: + { + rdata = devm_kmemdup(&pdev->dev, bd73800_rdata, + sizeof(bd73800_rdata), GFP_KERNEL); + if (!rdata) + return -ENOMEM; + + num_regulators = ARRAY_SIZE(bd73800_rdata); + + ret = bd73800_check_ldo_otp_options(&pdev->dev, rdata, + num_regulators); + if (ret) + return ret; + + break; + } default: return dev_err_probe(&pdev->dev, -EINVAL, "Unsupported device\n"); @@ -1692,6 +2247,7 @@ static int bd71828_probe(struct platform_device *pdev) static const struct platform_device_id bd71828_pmic_id[] = { { .name = "bd71828-pmic", .driver_data = ROHM_CHIP_TYPE_BD71828 }, { .name = "bd72720-pmic", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { .name = "bd73800-pmic", .driver_data = ROHM_CHIP_TYPE_BD73800 }, { } }; MODULE_DEVICE_TABLE(platform, bd71828_pmic_id); diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c index 482810b61495..fd415e327ea6 100644 --- a/drivers/rtc/rtc-bd70528.c +++ b/drivers/rtc/rtc-bd70528.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -284,6 +285,12 @@ static int bd70528_probe(struct platform_device *pdev) bd_rtc->bd718xx_alm_block_start = BD72720_REG_RTC_ALM_START; hour_reg = BD72720_REG_RTC_HOUR; break; + case ROHM_CHIP_TYPE_BD73800: + bd_rtc->reg_time_start = BD73800_REG_RTC_START; + bd_rtc->bd718xx_alm_block_start = BD73800_REG_RTC_ALM_START; + hour_reg = BD73800_REG_RTC_HOUR; + break; + default: dev_err(&pdev->dev, "Unknown chip\n"); return -ENOENT; @@ -344,6 +351,7 @@ static const struct platform_device_id bd718x7_rtc_id[] = { { .name = "bd71828-rtc", .driver_data = ROHM_CHIP_TYPE_BD71828 }, { .name = "bd71815-rtc", .driver_data = ROHM_CHIP_TYPE_BD71815 }, { .name = "bd72720-rtc", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { .name = "bd73800-rtc", .driver_data = ROHM_CHIP_TYPE_BD73800 }, { } }; MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id); diff --git a/include/linux/mfd/rohm-bd73800.h b/include/linux/mfd/rohm-bd73800.h new file mode 100644 index 000000000000..c6c0d453a40d --- /dev/null +++ b/include/linux/mfd/rohm-bd73800.h @@ -0,0 +1,306 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2026 ROHM Semiconductors. + * + * Author: Matti Vaittinen + */ + +#ifndef _MFD_BD73800_H +#define _MFD_BD73800_H + +#include + +enum { + BD73800_BUCK1 = 0, + BD73800_BUCK2, + BD73800_BUCK3, + BD73800_BUCK4, + BD73800_BUCK5, + BD73800_BUCK6, + BD73800_BUCK7, + BD73800_BUCK8, + BD73800_LDO1, + BD73800_LDO2, + BD73800_LDO3, + BD73800_LDO4, +}; + +/* + * All regulators except BUCK 5 have full 8-bit register of valid voltage + * values, including 0. + */ +#define BD73800_NUM_VOLTS (0xff + 1) +/* + * BUCK 5 has two sets of voltage ranges, both having valid voltage selectors + * from 0x0 to 0x7f + */ +#define BD73800_BUCK5_VOLTS (0x80 + 0x80) + +/* BD73800 interrupts */ +enum { + /* INT_STAT_1 register IRQs, ADC and RTC */ + BD73800_INT_ADC_ACCUM_DONE, + BD73800_INT_ADC_ACCUM_OVF, + BD73800_INT_ADC_ACCUM_VAL, + BD73800_INT_ADC_ACCUM_TW, + BD73800_INT_ADC_POW_VAL, + BD73800_INT_RTC0, + BD73800_INT_RTC1, + BD73800_INT_RTC2, + + /* BUCK reg interrupts */ + /* INT_STAT_2 IRQs */ + BD73800_INT_BUCK1_DVS_DONE, + BD73800_INT_BUCK2_DVS_DONE, + BD73800_INT_BUCK3_DVS_DONE, + BD73800_INT_BUCK4_DVS_DONE, + BD73800_INT_BUCK5_DVS_DONE, + BD73800_INT_BUCK6_DVS_DONE, + BD73800_INT_BUCK7_DVS_DONE, + BD73800_INT_BUCK8_DVS_DONE, + /* INT_STAT_3 IRQs */ + BD73800_INT_BUCK1_OCP, + BD73800_INT_BUCK2_OCP, + BD73800_INT_BUCK3_OCP, + BD73800_INT_BUCK4_OCP, + BD73800_INT_BUCK5_OCP, + BD73800_INT_BUCK6_OCP, + BD73800_INT_BUCK7_OCP, + BD73800_INT_BUCK8_OCP, + + /* INT_STAT_4 IRQs, power-button, WDG and reset */ + BD73800_INT_PBTN_LONG_PRESS, + BD73800_INT_PBTN_MID_PRESS, + /* + * The SHORT_PUSH is generated when button is first pressed (longer + * than configured time limit), and then released before the MID_PRESS + * time limit. The SHORT_PRESS is generated immediately when button is + * pressed for longer than configured limit, whether it is released or + * not. + */ + BD73800_INT_PBTN_SHORT_PUSH, + BD73800_INT_PBTN_SHORT_PRESS, + BD73800_INT_WDG, + BD73800_INT_SWRESET, + BD73800_INT_SEQ_DONE, + + /* INT_STAT_5 IRQs, GPIO */ + BD73800_INT_GPIO1, + BD73800_INT_GPIO2, + BD73800_INT_GPIO3, + BD73800_INT_GPIO4, +}; + +#define BD73800_MASK_RUN_EN BIT(2) +#define BD73800_MASK_SUSP_EN BIT(1) +#define BD73800_MASK_IDLE_EN BIT(0) +#define BD73800_MASK_VOLT GENMASK(7, 0) +#define BD73800_MASK_BUCK5_VOLT GENMASK(6, 0) +#define BD73800_MASK_RAMP_DELAY GENMASK(2, 1) +#define BD73800_BUCK5_RANGE_MASK BIT(7) + +/* BD73800 registers */ +enum { + BD73800_REG_PRODUCT_ID = 0x0, + BD73800_REG_MANUFACTURER_ID, + BD73800_REG_REVISION, + BD73800_REG_NVMVERSION, + BD73800_REG_POR_REASON, + BD73800_REG_RESET_REASON1, + BD73800_REG_RESET_REASON2, + BD73800_REG_RESET_REASON3, + BD73800_REG_POW_STATE, + BD73800_REG_WRST_SEL, + BD73800_REG_PS_CTRL_1, + BD73800_REG_PS_CTRL_2, + BD73800_REG_RCVCFG, + BD73800_REG_RCVNUM, + BD73800_REG_CRDCFG, /* 0x0f, followed by undocumented reg */ + + BD73800_REG_BUCK1_ON = 0x11, + BD73800_REG_BUCK1_MODE, + BD73800_REG_BUCK1_VOLT_RUN, + BD73800_REG_BUCK1_VOLT_IDLE, + BD73800_REG_BUCK1_VOLT_SUSP, /* 0x15, followed by undocumented reg */ + + BD73800_REG_BUCK2_ON = 0x17, + BD73800_REG_BUCK2_MODE, + BD73800_REG_BUCK2_VOLT_RUN, + BD73800_REG_BUCK2_VOLT_IDLE, + BD73800_REG_BUCK2_VOLT_SUSP, /* 0x1b */ + + BD73800_REG_BUCK3_ON = 0x1d, + BD73800_REG_BUCK3_MODE, + BD73800_REG_BUCK3_VOLT_RUN, + BD73800_REG_BUCK3_VOLT_IDLE, + BD73800_REG_BUCK3_VOLT_SUSP, /* 0x21 */ + + BD73800_REG_BUCK4_ON = 0x23, + BD73800_REG_BUCK4_MODE, + BD73800_REG_BUCK4_VOLT_RUN, + BD73800_REG_BUCK4_VOLT_IDLE, + BD73800_REG_BUCK4_VOLT_SUSP, /* 0x27 */ + + BD73800_REG_BUCK5_ON = 0x29, + BD73800_REG_BUCK5_MODE, + BD73800_REG_BUCK5_VOLT_RUN, + BD73800_REG_BUCK5_VOLT_IDLE, + BD73800_REG_BUCK5_VOLT_SUSP, /* 0x2d */ + + BD73800_REG_BUCK6_ON = 0x2f, + BD73800_REG_BUCK6_MODE, + BD73800_REG_BUCK6_VOLT_RUN, + BD73800_REG_BUCK6_VOLT_IDLE, + BD73800_REG_BUCK6_VOLT_SUSP, /* 0x33 */ + + BD73800_REG_BUCK7_ON = 0x35, + BD73800_REG_BUCK7_MODE, + BD73800_REG_BUCK7_VOLT_RUN, + BD73800_REG_BUCK7_VOLT_IDLE, + BD73800_REG_BUCK7_VOLT_SUSP, /* 0x39 */ + + BD73800_REG_BUCK8_ON = 0x3b, + BD73800_REG_BUCK8_MODE, + BD73800_REG_BUCK8_VOLT_RUN, + BD73800_REG_BUCK8_VOLT_IDLE, + BD73800_REG_BUCK8_VOLT_SUSP, /* 0x3f */ + + BD73800_REG_LDO1_ON = 0x41, + BD73800_REG_LDO1_VOLT, + BD73800_REG_LDO1_MODE, + BD73800_REG_LDO2_ON, + BD73800_REG_LDO2_VOLT, + BD73800_REG_LDO2_MODE, + BD73800_REG_LDO3_ON, + BD73800_REG_LDO3_VOLT, + BD73800_REG_LDO3_MODE, + BD73800_REG_LDO4_ON, + BD73800_REG_LDO4_VOLT, + BD73800_REG_LDO4_MODE, /* 0x4c */ + + BD73800_REG_GPO_OUT = 0x4e, + BD73800_REG_OUT32K = 0x50, + BD73800_REG_RTC_SEC, + BD73800_REG_RTC_MIN, + BD73800_REG_RTC_HOUR, + BD73800_REG_RTC_WEEK, + BD73800_REG_RTC_DAY, + BD73800_REG_RTC_MONTH, + BD73800_REG_RTC_YEAR, + BD73800_REG_RTC_ALM0_SEC, + BD73800_REG_RTC_ALM0_MIN, + BD73800_REG_RTC_ALM0_HOUR, + BD73800_REG_RTC_ALM0_WEEK, + BD73800_REG_RTC_ALM0_DAY, + BD73800_REG_RTC_ALM0_MONTH, + BD73800_REG_RTC_ALM0_YEAR, + BD73800_REG_RTC_ALM1_SEC, + BD73800_REG_RTC_ALM1_MIN, + BD73800_REG_RTC_ALM1_HOUR, + BD73800_REG_RTC_ALM1_WEEK, + BD73800_REG_RTC_ALM1_DAY, + BD73800_REG_RTC_ALM1_MONTH, + BD73800_REG_RTC_ALM1_YEAR, + BD73800_REG_RTC_ALM2, + BD73800_REG_RTC_CONF, /* 0x69 */ + + BD73800_REG_ADC_CTRL_1 = 0x6b, + BD73800_REG_ADC_CTRL_2, + BD73800_REG_ADC_ACCUM_NUM2, + BD73800_REG_ADC_ACCUM_NUM1, + BD73800_REG_ADC_ACCUM_NUM0, + BD73800_REG_ADC_ACCUM_KICK, + BD73800_REG_ADC_ACCUM_CNT2, + BD73800_REG_ADC_ACCUM_CNT1, + BD73800_REG_ADC_ACCUM_CNT0, + BD73800_REG_ADC_ACCUM_VAL2, + BD73800_REG_ADC_ACCUM_VAL1, + BD73800_REG_ADC_ACCUM_VAL0, + BD73800_REG_ADC_VOL_VAL1, + BD73800_REG_ADC_VOL_VAL0, + BD73800_REG_ADC_CUR_VAL1, + BD73800_REG_ADC_CUR_VAL0, + BD73800_REG_ADC_POW_VAL1, + BD73800_REG_ADC_POW_VAL0, + BD73800_REG_ADC_TEMP_VAL1, + BD73800_REG_ADC_TEMP_VAL0, + BD73800_REG_ADC_ACCUM_VAL_INT_TH4, + BD73800_REG_ADC_ACCUM_VAL_INT_TH3, + BD73800_REG_ADC_ACCUM_VAL_INT_TH2, + BD73800_REG_ADC_ACCUM_VAL_INT_TH1, + BD73800_REG_ADC_ACCUM_VAL_INT_TH0, + BD73800_REG_ADC_WARN_TEMP_INT_TH1, + BD73800_REG_ADC_WARN_TEMP_INT_TH0, + BD73800_REG_ADC_POW_VAL_INT_TH1, + BD73800_REG_ADC_POW_VAL_INT_TH0, /* 0x89 */ + + BD73800_REG_PBTN_CONF = 0x8b, + + BD73800_REG_INT_MAIN_EN = 0x8f, + BD73800_REG_INT_1_EN, + BD73800_REG_INT_2_EN, + BD73800_REG_INT_3_EN, + BD73800_REG_INT_4_EN, + BD73800_REG_INT_5_EN, /* 0x94 */ + + BD73800_REG_INT_MAIN_STAT = 0x96, + BD73800_REG_INT_1_STAT, + BD73800_REG_INT_2_STAT, + BD73800_REG_INT_3_STAT, + BD73800_REG_INT_4_STAT, + BD73800_REG_INT_5_STAT, /* 0x9b */ + + BD73800_REG_INT_MAIN_SRC = 0x9d, + BD73800_REG_INT_1_SRC, + BD73800_REG_INT_2_SRC, + BD73800_REG_INT_3_SRC, + BD73800_REG_INT_4_SRC, + BD73800_REG_INT_5_SRC, /* 0xa2 */ + + BD73800_REG_RST_MASK = 0xaf, + BD73800_MAX_REGISTER, +}; + +#define BD73800_REG_RTC_START BD73800_REG_RTC_SEC +#define BD73800_REG_RTC_ALM_START BD73800_REG_RTC_ALM0_SEC + +/* BD73800 IRQ register masks */ + +#define BD73800_INT_MAIN_EN_ALL GENMASK(4, 0) +#define BD73800_INT_ADC_ACCUM_DONE_MASK BIT(0) +#define BD73800_INT_ADC_ACCUM_OVF_MASK BIT(1) +#define BD73800_INT_ADC_ACCUM_VAL_MASK BIT(2) +#define BD73800_INT_ADC_ACCUM_TW_MASK BIT(3) +#define BD73800_INT_ADC_POW_VAL_MASK BIT(4) +#define BD73800_INT_RTC0_MASK BIT(5) +#define BD73800_INT_RTC1_MASK BIT(6) +#define BD73800_INT_RTC2_MASK BIT(7) +#define BD73800_INT_BUCK1_DVS_DONE_MASK BIT(0) +#define BD73800_INT_BUCK2_DVS_DONE_MASK BIT(1) +#define BD73800_INT_BUCK3_DVS_DONE_MASK BIT(2) +#define BD73800_INT_BUCK4_DVS_DONE_MASK BIT(3) +#define BD73800_INT_BUCK5_DVS_DONE_MASK BIT(4) +#define BD73800_INT_BUCK6_DVS_DONE_MASK BIT(5) +#define BD73800_INT_BUCK7_DVS_DONE_MASK BIT(6) +#define BD73800_INT_BUCK8_DVS_DONE_MASK BIT(7) +#define BD73800_INT_BUCK1_OCP_MASK BIT(0) +#define BD73800_INT_BUCK2_OCP_MASK BIT(1) +#define BD73800_INT_BUCK3_OCP_MASK BIT(2) +#define BD73800_INT_BUCK4_OCP_MASK BIT(3) +#define BD73800_INT_BUCK5_OCP_MASK BIT(4) +#define BD73800_INT_BUCK6_OCP_MASK BIT(5) +#define BD73800_INT_BUCK7_OCP_MASK BIT(6) +#define BD73800_INT_BUCK8_OCP_MASK BIT(7) +#define BD73800_INT_PBTN_LONG_PRESS_MASK BIT(0) +#define BD73800_INT_PBTN_MID_PRESS_MASK BIT(1) +#define BD73800_INT_PBTN_SHORT_PUSH_MASK BIT(2) +#define BD73800_INT_PBTN_SHORT_PRESS_MASK BIT(3) +#define BD73800_INT_WDG_MASK BIT(4) +#define BD73800_INT_SWRESET_MASK BIT(5) +#define BD73800_INT_SEQ_DONE_MASK BIT(6) +#define BD73800_INT_GPIO1_MASK BIT(0) +#define BD73800_INT_GPIO2_MASK BIT(1) +#define BD73800_INT_GPIO3_MASK BIT(2) +#define BD73800_INT_GPIO4_MASK BIT(3) + +#endif /* _MFD_BD73800_H */ diff --git a/include/linux/mfd/rohm-generic.h b/include/linux/mfd/rohm-generic.h index 0a284919a6c3..3ec87428ee97 100644 --- a/include/linux/mfd/rohm-generic.h +++ b/include/linux/mfd/rohm-generic.h @@ -17,6 +17,7 @@ enum rohm_chip_type { ROHM_CHIP_TYPE_BD71837, ROHM_CHIP_TYPE_BD71847, ROHM_CHIP_TYPE_BD72720, + ROHM_CHIP_TYPE_BD73800, ROHM_CHIP_TYPE_BD96801, ROHM_CHIP_TYPE_BD96802, ROHM_CHIP_TYPE_BD96805,