diff --git a/Documentation/devicetree/bindings/mfd/adi,max42500.yaml b/Documentation/devicetree/bindings/mfd/adi,max42500.yaml new file mode 100644 index 00000000000000..cb51cd9bc66260 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/adi,max42500.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/adi,max42500.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX42500 Industrial Power System Monitor + +maintainers: + - Kent Libetario + +description: | + Analog Devices MAX42500 Industrial Power System Monitor Family + https://www.analog.com/media/en/technical-documentation/data-sheets/max42500.pdf + +properties: + compatible: + enum: + - adi,max42500 + reg: + maxItems: 1 + + poweroff-gpios: + description: + This property sets the GPIO as output. Then used to control the power and + mode state of the device. This can be enabled optionally to control the + state of comparators in the voltage monitoring and/or trigger timestamp + recording in the flexible power sequence recording functions. Check the + state transition conditions in datasheet for more information. + maxItems: 1 + + sleepoff-gpios: + description: + This property sets the GPIO as output. Then used to control the power and + mode state of the device. This can be enabled and masked optionally to + control the state of comparators in the voltage monitoring and/or trigger + timestamp recording in the flexible power sequence recording functions. + Check the state transition conditions in datasheet for more information. + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hwmon@28 { + compatible = "adi,max42500"; + reg = <0x28>; + }; + }; diff --git a/Documentation/hwmon/max42500-hwmon.rst b/Documentation/hwmon/max42500-hwmon.rst new file mode 100644 index 00000000000000..5a8acaa701c9a2 --- /dev/null +++ b/Documentation/hwmon/max42500-hwmon.rst @@ -0,0 +1,135 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver max42500 +====================== + +Supported chips: + * Analog Devices MAX42500 + + Prefix: 'max42500' + + Addresses scanned: I2C 0x28 to 0x2B + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max42500.pdf + +Author: Kent Libetario + +Description +----------- + +This driver supports hardware monitoring of MAX42500 power system with up +to seven voltage monitor inputs. Each input has programmable overvoltage +(OV) and undervoltage (UV) thresholds where two of the inputs support +dynamic voltage scaling (DVS). Additionally, the MAX42500 features a +programmable flexible power sequence recorder that stores timestamps +separately. And also, the MAX42500 has a programmable challenge and +response watchdog with a configurable RESET output. + + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst +for details. + +Due to its multi-functionality, the MAX42500 is split into three drivers: +the mfd driver as the main device, and the hwmon and watchdog drivers as +the sub-devices. The mfd driver is a client to the core driver and +consumed by both the hwmon and watchdog drivers. + +Optionally, two power management GPIOs are provided by the mfd driver and +consumed only by the hwmon sub-device driver. The pins are fixed-outputs +to control the voltage monitor comparators and trigger the power sequence +timestamp recording of the device. Please see the datasheet for details. + +For the two GPIO output pins to be consumed, the device tree will look +like this: + +.. code-block:: + + i2c { + #address-cells = <1>; + #size-cells = <0>; + poweroff-gpios + sleepoff-gpios + + hwmon@28 { + compatible = "adi,max42500"; + reg = <0x28>; + }; + }; + +Otherwise, the two pins maybe omitted in the device tree if unused. + + +Platform data support +--------------------- + +The Hwmon driver supports standard Hwmon ABI driver and sysfs platform +data. While the watchdog driver supports the standard watchdog ABI driver +and sysfs platform data. + + +Hwmon Sysfs entries +------------------- + +The following attributes are supported. Limits are read-write; all other +attributes are read-only. + + +Chip +~~~~ + +======================= ======================================================= +chip_pec Enable or disable PEC: PECE bit in CONFIG1 register +======================= ======================================================= + +In +~~ + +======================= ====================================================== +in[1-7]_label "VMON[1-7]" +in[1-7]_enable Enable or disable voltage monitors: VM1 to VM7 bits of + VMON register +in[1-5]_min Nominal Voltage set point: VIN1 to VIN5 registers +in[1-5]_lcrit IN1-IN5 UV threshold: UV1 to UV5 nibbles of OVUV1 to + OVUV5 registers +in[6-7]_lcrit IN6-IN7 UV threshold: VINU6 to VINU7 registers +in[1-5]_crit IN1-IN5 OV threshold: OV1 to OV5 nibbles of OVUV1 to + OVUV5 registers +in[6-7]_crit IN6-IN7 OV threshold: VINO6 to VINO7 registers +in[1-7]_reset_history Enable or disable reset mapping: IN1 to IN7 bits of + RSTMAP register +======================= ====================================================== + +Power +~~~~~ + +=============================== =============================================== +power[1-7]_label "STATUS[1-7]" +power_enable OFF comparator status: STATOFF register +power_lcrit_alarm UV comparator status: STATUV register +power_crit_alarm OV comparator status: STATOV register +power[1-7]_average_interval_min Power-Down sequence time-stamp: DTIME1 to + DTIME7 registers +power[1-7]_average_interval_max Power-Up sequence time-stamp: UTIME1 to + UTIME7 registers +=============================== =============================================== + + +Watchdog Sysfs entries +---------------------- + +======================= ======================================================= +start Enable the watchdog: WDEN bit of WDCFG2 register +stop Disable the watchdog: WDEN bit of WDCFG2 register +ping Set the watchdog key to the device: WDKEY register +status Watchdog status: WDSTAT register +set_timeout Watchdog Clock Divider: WDIV bits of WDCDIV register +set_pretimeout First Update Extension: 1UD bit of WDCFG2 register +restart Reset Hold or Active Timeout: RHLD bits of RSTCTRL + register +======================= ======================================================= + +.. diff --git a/Kconfig.adi b/Kconfig.adi index 307c1bab7b03c7..30e894a1bab123 100644 --- a/Kconfig.adi +++ b/Kconfig.adi @@ -96,6 +96,8 @@ config KERNEL_ALL_ADI_DRIVERS imply REGULATOR_MAX77857 imply REGULATOR_MAX77541 imply MFD_MAX77541 + imply MAX42500_WDT + imply MFD_MAX42500 source "drivers/clk/Kconfig.adi" source "drivers/hwmon/Kconfig.adi" diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b7fa01e9dd892e..d72debedd2839b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1131,6 +1131,18 @@ config MAX31827 This driver can also be built as a module. If so, the module will be called max31827. +config SENSORS_MAX42500 + tristate "MAX42500 Industrial Power System Monitor" + depends on MFD_MAX42500 + help + If you say yes here you get support for MAX42500 SoC power-system + monitor with up to seven voltage monitor. The driver also contains a + programmable challenge/response watchdog, which is accessible through + the I2C interface, along with a configurable RESET output. + + This driver can also be built as a module. If so, the module + will be called max42500-hwmon. + config SENSORS_MAX6620 tristate "Maxim MAX6620 fan controller" depends on I2C diff --git a/drivers/hwmon/Kconfig.adi b/drivers/hwmon/Kconfig.adi index db613969acc86c..df7e06f8e46de0 100644 --- a/drivers/hwmon/Kconfig.adi +++ b/drivers/hwmon/Kconfig.adi @@ -44,4 +44,5 @@ config HWMON_ALL_ADI_DRIVERS imply MAX31827 imply SENSORS_MAX31760 imply SENSORS_LT7182S + imply SENSORS_MAX42500 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index f324d057535ad9..91605b3368c552 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_MAX197) += max197.o obj-$(CONFIG_SENSORS_MAX31722) += max31722.o obj-$(CONFIG_SENSORS_MAX31730) += max31730.o obj-$(CONFIG_SENSORS_MAX31760) += max31760.o +obj-$(CONFIG_SENSORS_MAX42500) += max42500-hwmon.o obj-$(CONFIG_SENSORS_MAX6620) += max6620.o obj-$(CONFIG_SENSORS_MAX6621) += max6621.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o diff --git a/drivers/hwmon/max42500-hwmon.c b/drivers/hwmon/max42500-hwmon.c new file mode 100644 index 00000000000000..350cc1c9e29e26 --- /dev/null +++ b/drivers/hwmon/max42500-hwmon.c @@ -0,0 +1,1348 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MAX42500 - Industrial Power System Monitor + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum MAX42500_chip { + MAX42500_CHIP_LABEL, + MAX42500_CHIP_NAME, + MAX42500_CHIP_RELOAD_OTP_ENABLE, + MAX42500_CHIP_BIST_RESET_ENABLE, + MAX42500_CHIP_PEC_ENABLE, + MAX42500_CHIP_CONFIG_STATUS, + MAX42500_CHIP_MAX +}; + +static const u8 max42500_reg_chip[] = { + MAX42500_REG_ID, + MAX42500_REG_CID, + MAX42500_REG_CONFIG1, + MAX42500_REG_CONFIG1, + MAX42500_REG_CONFIG1, + MAX42500_REG_CONFIG2 +}; + +enum MAX42500_vmon { + MAX42500_VMON_IN1_ENABLE, + MAX42500_VMON_IN2_ENABLE, + MAX42500_VMON_IN3_ENABLE, + MAX42500_VMON_IN4_ENABLE, + MAX42500_VMON_IN5_ENABLE, + MAX42500_VMON_IN6_ENABLE, + MAX42500_VMON_IN7_ENABLE, + MAX42500_VMON_PDN_ENABLE, + MAX42500_VMON_IN1_RESET_ENABLE, + MAX42500_VMON_IN2_RESET_ENABLE, + MAX42500_VMON_IN3_RESET_ENABLE, + MAX42500_VMON_IN4_RESET_ENABLE, + MAX42500_VMON_IN5_RESET_ENABLE, + MAX42500_VMON_IN6_RESET_ENABLE, + MAX42500_VMON_IN7_RESET_ENABLE, + MAX42500_VMON_PAR_ENABLE, + MAX42500_VMON_OV_STATUS, + MAX42500_VMON_UV_STATUS, + MAX42500_VMON_OFF_STATUS, + MAX42500_VMON_IN1_NOMINAL, + MAX42500_VMON_IN2_NOMINAL, + MAX42500_VMON_IN3_NOMINAL, + MAX42500_VMON_IN4_NOMINAL, + MAX42500_VMON_IN5_NOMINAL, + MAX42500_VMON_IN1_OV_THRESH, + MAX42500_VMON_IN2_OV_THRESH, + MAX42500_VMON_IN3_OV_THRESH, + MAX42500_VMON_IN4_OV_THRESH, + MAX42500_VMON_IN5_OV_THRESH, + MAX42500_VMON_IN6_CRIT, + MAX42500_VMON_IN7_CRIT, + MAX42500_VMON_IN1_UV_THRESH, + MAX42500_VMON_IN2_UV_THRESH, + MAX42500_VMON_IN3_UV_THRESH, + MAX42500_VMON_IN4_UV_THRESH, + MAX42500_VMON_IN5_UV_THRESH, + MAX42500_VMON_IN6_LCRIT, + MAX42500_VMON_IN7_LCRIT, + MAX42500_VMON_MAX +}; + +static const u8 max42500_reg_vmon[] = { + MAX42500_REG_VMON, + MAX42500_REG_VMON, + MAX42500_REG_VMON, + MAX42500_REG_VMON, + MAX42500_REG_VMON, + MAX42500_REG_VMON, + MAX42500_REG_VMON, + MAX42500_REG_VMON, + MAX42500_REG_RSTMAP, + MAX42500_REG_RSTMAP, + MAX42500_REG_RSTMAP, + MAX42500_REG_RSTMAP, + MAX42500_REG_RSTMAP, + MAX42500_REG_RSTMAP, + MAX42500_REG_RSTMAP, + MAX42500_REG_RSTMAP, + MAX42500_REG_STATOV, + MAX42500_REG_STATUV, + MAX42500_REG_STATOFF, + MAX42500_REG_VIN1, + MAX42500_REG_VIN2, + MAX42500_REG_VIN3, + MAX42500_REG_VIN4, + MAX42500_REG_VIN5, + MAX42500_REG_OVUV1, + MAX42500_REG_OVUV2, + MAX42500_REG_OVUV3, + MAX42500_REG_OVUV4, + MAX42500_REG_OVUV5, + MAX42500_REG_VINO6, + MAX42500_REG_VINO7, + MAX42500_REG_OVUV1, + MAX42500_REG_OVUV2, + MAX42500_REG_OVUV3, + MAX42500_REG_OVUV4, + MAX42500_REG_OVUV5, + MAX42500_REG_VINU6, + MAX42500_REG_VINU7 +}; + +enum max42500_fpsr { + MAX42500_FPSR_FPS_STATUS, + MAX42500_FPSR_POWERUP_SEQ_CAPTURE_FINISH, + MAX42500_FPSR_POWERDN_SEQ_CAPTURE_FINISH, + MAX42500_FPSR_POWERUP_NO_INTERRUPT_ENABLE, + MAX42500_FPSR_POWERDN_NO_INTERRUPT_ENABLE, + MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE, + MAX42500_FPSR_FPS_CLOCK_DIVIDER, + MAX42500_FPSR_POWER1_AVERAGE_INTERVAL_MAX, + MAX42500_FPSR_POWER2_AVERAGE_INTERVAL_MAX, + MAX42500_FPSR_POWER3_AVERAGE_INTERVAL_MAX, + MAX42500_FPSR_POWER4_AVERAGE_INTERVAL_MAX, + MAX42500_FPSR_POWER5_AVERAGE_INTERVAL_MAX, + MAX42500_FPSR_POWER6_AVERAGE_INTERVAL_MAX, + MAX42500_FPSR_POWER7_AVERAGE_INTERVAL_MAX, + MAX42500_FPSR_POWER1_AVERAGE_INTERVAL_MIN, + MAX42500_FPSR_POWER2_AVERAGE_INTERVAL_MIN, + MAX42500_FPSR_POWER3_AVERAGE_INTERVAL_MIN, + MAX42500_FPSR_POWER4_AVERAGE_INTERVAL_MIN, + MAX42500_FPSR_POWER5_AVERAGE_INTERVAL_MIN, + MAX42500_FPSR_POWER6_AVERAGE_INTERVAL_MIN, + MAX42500_FPSR_POWER7_AVERAGE_INTERVAL_MIN, + MAX42500_FPSR_MAX +}; + +static const u8 max42500_reg_fpsr[] = { + MAX42500_REG_FPSTAT1, + MAX42500_REG_FPSCFG1, + MAX42500_REG_FPSCFG1, + MAX42500_REG_FPSCFG1, + MAX42500_REG_FPSCFG1, + MAX42500_REG_FPSCFG1, + MAX42500_REG_FPSCFG1, + MAX42500_REG_UTIME1, + MAX42500_REG_UTIME2, + MAX42500_REG_UTIME3, + MAX42500_REG_UTIME4, + MAX42500_REG_UTIME5, + MAX42500_REG_UTIME6, + MAX42500_REG_UTIME7, + MAX42500_REG_DTIME1, + MAX42500_REG_DTIME2, + MAX42500_REG_DTIME3, + MAX42500_REG_DTIME4, + MAX42500_REG_DTIME5, + MAX42500_REG_DTIME6, + MAX42500_REG_DTIME7 +}; + +struct max42500_state { + struct i2c_client *client; + struct regmap *regmap; + struct mutex lock; /* Protects access during data transfer */ + struct gpio_desc *power_gpio; + struct gpio_desc *sleep_gpio; + u8 chip[MAX42500_CHIP_MAX]; + u8 vmon[MAX42500_VMON_MAX]; + u8 fpsr[MAX42500_FPSR_MAX]; +}; + +static int max42500_chip_get_update(struct max42500_state *st, + enum MAX42500_chip index) +{ + unsigned int reg_val; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, max42500_reg_chip[index], ®_val); + if (ret) + return ret; + + switch (index) { + case MAX42500_CHIP_LABEL: /* ID */ + st->chip[index] = MAX42500_CHIP_ID(reg_val); + return 0; + case MAX42500_CHIP_NAME: /* CID */ + st->chip[index] = MAX42500_CHIP_CID(reg_val); + return 0; + case MAX42500_CHIP_CONFIG_STATUS: /* CFG2 */ + st->chip[index] = MAX42500_CHIP_CFG2(reg_val); + return 0; + case MAX42500_CHIP_PEC_ENABLE: /* PECE */ + st->chip[index] = MAX42500_CHIP_PECE(reg_val); + return 0; + case MAX42500_CHIP_BIST_RESET_ENABLE: /* MBST */ + st->chip[index] = MAX42500_CHIP_MBST(reg_val); + return 0; + case MAX42500_CHIP_RELOAD_OTP_ENABLE: /* RR */ + st->chip[index] = MAX42500_CHIP_RR(reg_val); + return 0; + default: + return -EINVAL; + } +} + +static int max42500_vmon_get_update(struct max42500_state *st, + enum MAX42500_vmon index) +{ + unsigned int reg_val; + int ret = 0; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, max42500_reg_vmon[index], ®_val); + if (ret) + return ret; + + switch (index) { + case MAX42500_VMON_IN1_ENABLE: /* VIN1 */ + st->vmon[index] = MAX42500_VMON_VM1(reg_val); + return 0; + case MAX42500_VMON_IN1_RESET_ENABLE: /* RST1 */ + st->vmon[index] = MAX42500_VMON_RST1(reg_val); + return 0; + case MAX42500_VMON_IN2_ENABLE: /* VIN2 */ + st->vmon[index] = MAX42500_VMON_VM2(reg_val); + return 0; + case MAX42500_VMON_IN2_RESET_ENABLE: /* RST2 */ + st->vmon[index] = MAX42500_VMON_RST2(reg_val); + return 0; + case MAX42500_VMON_IN3_ENABLE: /* VIN3 */ + st->vmon[index] = MAX42500_VMON_VM3(reg_val); + return 0; + case MAX42500_VMON_IN3_RESET_ENABLE: /* RST3 */ + st->vmon[index] = MAX42500_VMON_RST3(reg_val); + return 0; + case MAX42500_VMON_IN4_ENABLE: /* VIN4 */ + st->vmon[index] = MAX42500_VMON_VM4(reg_val); + return 0; + case MAX42500_VMON_IN4_RESET_ENABLE: /* RST4 */ + st->vmon[index] = MAX42500_VMON_RST4(reg_val); + return 0; + case MAX42500_VMON_IN5_ENABLE: /* VIN5 */ + st->vmon[index] = MAX42500_VMON_VM5(reg_val); + return 0; + case MAX42500_VMON_IN5_RESET_ENABLE: /* RST5 */ + st->vmon[index] = MAX42500_VMON_RST5(reg_val); + return 0; + case MAX42500_VMON_IN6_ENABLE: /* VIN6 */ + st->vmon[index] = MAX42500_VMON_VM6(reg_val); + return 0; + case MAX42500_VMON_IN6_RESET_ENABLE: /*RST6 */ + st->vmon[index] = MAX42500_VMON_RST6(reg_val); + return 0; + case MAX42500_VMON_IN7_ENABLE: /* VIN7 */ + st->vmon[index] = MAX42500_VMON_VM7(reg_val); + return 0; + case MAX42500_VMON_IN7_RESET_ENABLE: /* RST7 */ + st->vmon[index] = MAX42500_VMON_RST7(reg_val); + return 0; + case MAX42500_VMON_PDN_ENABLE: /* VMPD */ + st->vmon[index] = MAX42500_VMON_VMPD(reg_val); + return 0; + case MAX42500_VMON_PAR_ENABLE: /* PARM */ + st->vmon[index] = MAX42500_VMON_PARM(reg_val); + return 0; + case MAX42500_VMON_IN1_NOMINAL: /* VIN1 */ + case MAX42500_VMON_IN2_NOMINAL: /* VIN2 */ + case MAX42500_VMON_IN3_NOMINAL: /* VIN3 */ + case MAX42500_VMON_IN4_NOMINAL: /* VIN4 */ + case MAX42500_VMON_IN5_NOMINAL: /* VIN5 */ + st->vmon[index] = MAX42500_VMON_1VIN5(reg_val); + return 0; + case MAX42500_VMON_IN1_UV_THRESH: /* VINU1 */ + case MAX42500_VMON_IN2_UV_THRESH: /* VINU2 */ + case MAX42500_VMON_IN3_UV_THRESH: /* VINU3 */ + case MAX42500_VMON_IN4_UV_THRESH: /* VINU4 */ + case MAX42500_VMON_IN5_UV_THRESH: /* VINU5 */ + st->vmon[index] = MAX42500_VMON_1VINU5(reg_val); + return 0; + case MAX42500_VMON_IN6_LCRIT: /* VINU6 */ + case MAX42500_VMON_IN7_LCRIT: /* VINU7 */ + st->vmon[index] = MAX42500_VMON_6VINU7(reg_val); + return 0; + case MAX42500_VMON_IN1_OV_THRESH: /* VINO1 */ + case MAX42500_VMON_IN2_OV_THRESH: /* VINO2 */ + case MAX42500_VMON_IN3_OV_THRESH: /* VINO3 */ + case MAX42500_VMON_IN4_OV_THRESH: /* VINO4 */ + case MAX42500_VMON_IN5_OV_THRESH: /* VINO5 */ + st->vmon[index] = MAX42500_VMON_1VINO5(reg_val); + return 0; + case MAX42500_VMON_IN6_CRIT: /* VINO6 */ + case MAX42500_VMON_IN7_CRIT: /* VINO7 */ + st->vmon[index] = MAX42500_VMON_6VINO7(reg_val); + return 0; + case MAX42500_VMON_OFF_STATUS: /* STATOFF */ + st->vmon[index] = MAX42500_VMON_STATOFF(reg_val); + return 0; + case MAX42500_VMON_UV_STATUS: /* STATUV */ + st->vmon[index] = MAX42500_VMON_STATUV(reg_val); + return 0; + case MAX42500_VMON_OV_STATUS: /* STATOV */ + st->vmon[index] = MAX42500_VMON_STATOV(reg_val); + return 0; + default: + return -EINVAL; + } +} + +static int max42500_fpsr_get_update(struct max42500_state *st, + enum max42500_fpsr index) +{ + unsigned int reg_val; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, max42500_reg_fpsr[index], ®_val); + if (ret) + return ret; + + switch (index) { + case MAX42500_FPSR_FPS_STATUS: /* FPSTAT1 */ + st->fpsr[index] = MAX42500_FPSR_FPSTAT1(reg_val); + return 0; + case MAX42500_FPSR_FPS_CLOCK_DIVIDER: /* FDIV */ + st->fpsr[index] = MAX42500_FPSR_FDIV(reg_val); + return 0; + case MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE: /* FPSEN1 */ + st->fpsr[index] = MAX42500_FPSR_FPSEN1(reg_val); + return 0; + case MAX42500_FPSR_POWERDN_NO_INTERRUPT_ENABLE: /* DVALM */ + st->fpsr[index] = MAX42500_FPSR_DVALM(reg_val); + return 0; + case MAX42500_FPSR_POWERUP_NO_INTERRUPT_ENABLE: /* UVALM */ + st->fpsr[index] = MAX42500_FPSR_UVALM(reg_val); + return 0; + case MAX42500_FPSR_POWERDN_SEQ_CAPTURE_FINISH: /* DVAL */ + st->fpsr[index] = MAX42500_FPSR_DVAL(reg_val); + return 0; + case MAX42500_FPSR_POWERUP_SEQ_CAPTURE_FINISH: /* UVAL */ + st->fpsr[index] = MAX42500_FPSR_UVAL(reg_val); + return 0; + case MAX42500_FPSR_POWER1_AVERAGE_INTERVAL_MAX: /* UTIM1 */ + case MAX42500_FPSR_POWER2_AVERAGE_INTERVAL_MAX: /* UTIM2 */ + case MAX42500_FPSR_POWER3_AVERAGE_INTERVAL_MAX: /* UTIM3 */ + case MAX42500_FPSR_POWER4_AVERAGE_INTERVAL_MAX: /* UTIM4 */ + case MAX42500_FPSR_POWER5_AVERAGE_INTERVAL_MAX: /* UTIM5 */ + case MAX42500_FPSR_POWER6_AVERAGE_INTERVAL_MAX: /* UTIM6 */ + case MAX42500_FPSR_POWER7_AVERAGE_INTERVAL_MAX: /* UTIM7 */ + st->fpsr[index] = MAX42500_FPSR_1UTIM7(reg_val); + return 0; + case MAX42500_FPSR_POWER1_AVERAGE_INTERVAL_MIN: /* DTIM1 */ + case MAX42500_FPSR_POWER2_AVERAGE_INTERVAL_MIN: /* DTIM2 */ + case MAX42500_FPSR_POWER3_AVERAGE_INTERVAL_MIN: /* DTIM3 */ + case MAX42500_FPSR_POWER4_AVERAGE_INTERVAL_MIN: /* DTIM4 */ + case MAX42500_FPSR_POWER5_AVERAGE_INTERVAL_MIN: /* DTIM5 */ + case MAX42500_FPSR_POWER6_AVERAGE_INTERVAL_MIN: /* DTIM6 */ + case MAX42500_FPSR_POWER7_AVERAGE_INTERVAL_MIN: /* DTIM7 */ + st->fpsr[index] = MAX42500_FPSR_1DTIM7(reg_val); + return 0; + default: + return -EINVAL; + } +} + +static int max42500_chip_set_update(struct max42500_state *st, long value, + enum MAX42500_chip index) +{ + bool pec_flag = false; + unsigned int reg_mask; + int ret; + + guard(mutex)(&st->lock); + + switch (index) { + case MAX42500_CHIP_PEC_ENABLE: /* PECE */ + reg_mask = MAX42500_CHIP_PECE_MASK; + pec_flag = true; + break; + case MAX42500_CHIP_BIST_RESET_ENABLE: /* MBST */ + reg_mask = MAX42500_CHIP_MBST_MASK; + break; + case MAX42500_CHIP_RELOAD_OTP_ENABLE: /* RR */ + reg_mask = MAX42500_CHIP_RR_MASK; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->regmap, max42500_reg_chip[index], + reg_mask, value); + if (ret) + return ret; + + /* Set the PEC of the device first then set in here after. */ + if (pec_flag) + st->client->flags |= FIELD_PREP(BIT(I2C_CLIENT_PEC), value); + + /* Update after successful register write */ + st->chip[index] = FIELD_GET(GENMASK(7, 0), value); + + return 0; +} + +static int max42500_vmon_set_update(struct max42500_state *st, long value, + enum MAX42500_vmon index) +{ + unsigned int reg_mask; + int ret; + + guard(mutex)(&st->lock); + + switch (index) { + case MAX42500_VMON_IN1_ENABLE: /* VIN1 */ + reg_mask = MAX42500_VMON_VM1_MASK; + break; + case MAX42500_VMON_IN1_RESET_ENABLE: /* RST1 */ + reg_mask = MAX42500_VMON_RST1_MASK; + break; + case MAX42500_VMON_IN2_ENABLE: /* VIN2 */ + reg_mask = MAX42500_VMON_VM2_MASK; + break; + case MAX42500_VMON_IN2_RESET_ENABLE: /* RST2 */ + reg_mask = MAX42500_VMON_RST2_MASK; + break; + case MAX42500_VMON_IN3_ENABLE: /* VIN3 */ + reg_mask = MAX42500_VMON_VM3_MASK; + break; + case MAX42500_VMON_IN3_RESET_ENABLE: /* RST3 */ + reg_mask = MAX42500_VMON_RST3_MASK; + break; + case MAX42500_VMON_IN4_ENABLE: /* VIN4 */ + reg_mask = MAX42500_VMON_VM4_MASK; + break; + case MAX42500_VMON_IN4_RESET_ENABLE: /* RST4 */ + reg_mask = MAX42500_VMON_RST4_MASK; + break; + case MAX42500_VMON_IN5_ENABLE: /* VIN5 */ + reg_mask = MAX42500_VMON_VM5_MASK; + break; + case MAX42500_VMON_IN5_RESET_ENABLE: /* RST5 */ + reg_mask = MAX42500_VMON_RST5_MASK; + break; + case MAX42500_VMON_IN6_ENABLE: /* VIN6 */ + reg_mask = MAX42500_VMON_VM6_MASK; + break; + case MAX42500_VMON_IN6_RESET_ENABLE: /*RST6 */ + reg_mask = MAX42500_VMON_RST6_MASK; + break; + case MAX42500_VMON_IN7_ENABLE: /* VIN7 */ + reg_mask = MAX42500_VMON_VM7_MASK; + break; + case MAX42500_VMON_IN7_RESET_ENABLE: /* RST7 */ + reg_mask = MAX42500_VMON_RST7_MASK; + break; + case MAX42500_VMON_PDN_ENABLE: /* VMPD */ + reg_mask = MAX42500_VMON_VMPD_MASK; + break; + case MAX42500_VMON_PAR_ENABLE: /* PARM */ + reg_mask = MAX42500_VMON_PARM_MASK; + break; + case MAX42500_VMON_IN1_NOMINAL: /* VIN1 */ + case MAX42500_VMON_IN2_NOMINAL: /* VIN2 */ + case MAX42500_VMON_IN3_NOMINAL: /* VIN3 */ + case MAX42500_VMON_IN4_NOMINAL: /* VIN4 */ + case MAX42500_VMON_IN5_NOMINAL: /* VIN5 */ + reg_mask = MAX42500_VMON_1VIN5_MASK; + break; + case MAX42500_VMON_IN1_UV_THRESH: /* VINU1 */ + case MAX42500_VMON_IN2_UV_THRESH: /* VINU2 */ + case MAX42500_VMON_IN3_UV_THRESH: /* VINU3 */ + case MAX42500_VMON_IN4_UV_THRESH: /* VINU4 */ + case MAX42500_VMON_IN5_UV_THRESH: /* VINU5 */ + reg_mask = MAX42500_VMON_1VINU5_MASK; + break; + case MAX42500_VMON_IN6_LCRIT: /* VINU6 */ + case MAX42500_VMON_IN7_LCRIT: /* VINU7 */ + reg_mask = MAX42500_VMON_6VINU7_MASK; + break; + case MAX42500_VMON_IN1_OV_THRESH: /* VINO1 */ + case MAX42500_VMON_IN2_OV_THRESH: /* VINO2 */ + case MAX42500_VMON_IN3_OV_THRESH: /* VINO3 */ + case MAX42500_VMON_IN4_OV_THRESH: /* VINO4 */ + case MAX42500_VMON_IN5_OV_THRESH: /* VINO5 */ + reg_mask = MAX42500_VMON_1VINO5_MASK; + break; + case MAX42500_VMON_IN6_CRIT: /* VINO6 */ + case MAX42500_VMON_IN7_CRIT: /* VINO7 */ + reg_mask = MAX42500_VMON_6VINO7_MASK; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->regmap, max42500_reg_vmon[index], + reg_mask, value); + if (ret) + return ret; + + /* Update after successful register write */ + st->vmon[index] = FIELD_GET(GENMASK(7, 0), value); + + return 0; +} + +static int max42500_fpsr_set_update(struct max42500_state *st, long value, + enum max42500_fpsr index) +{ + unsigned int reg_mask; + int ret; + + guard(mutex)(&st->lock); + + switch (index) { + case MAX42500_FPSR_FPS_CLOCK_DIVIDER: /* FDIV */ + reg_mask = MAX42500_FPSR_FDIV_MASK; + break; + case MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE: /* FPSEN1 */ + reg_mask = MAX42500_FPSR_FPSEN1_MASK; + break; + case MAX42500_FPSR_POWERDN_NO_INTERRUPT_ENABLE: /* DVALM */ + reg_mask = MAX42500_FPSR_DVALM_MASK; + break; + case MAX42500_FPSR_POWERUP_NO_INTERRUPT_ENABLE: /* UVALM */ + reg_mask = MAX42500_FPSR_UVALM_MASK; + break; + case MAX42500_FPSR_POWERDN_SEQ_CAPTURE_FINISH: /* DVAL */ + reg_mask = MAX42500_FPSR_DVAL_MASK; + break; + case MAX42500_FPSR_POWERUP_SEQ_CAPTURE_FINISH: /* UVAL */ + reg_mask = MAX42500_FPSR_UVAL_MASK; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->regmap, max42500_reg_fpsr[index], + reg_mask, value); + if (ret) + return ret; + + /* Update after successful register write */ + st->fpsr[index] = FIELD_GET(GENMASK(7, 0), value); + + return 0; +} + +static int max42500_vmon_read(struct max42500_state *st, long *value, + enum MAX42500_vmon index) +{ + int ret; + + ret = max42500_vmon_get_update(st, index); + if (ret) + return ret; + + switch (index) { + case MAX42500_VMON_IN1_NOMINAL: /* VIN1 */ + case MAX42500_VMON_IN2_NOMINAL: /* VIN2 */ + case MAX42500_VMON_IN3_NOMINAL: /* VIN3 */ + case MAX42500_VMON_IN4_NOMINAL: /* VIN4 */ + *value = 500000 + (12500 * st->vmon[index]); + return 0; + case MAX42500_VMON_IN5_NOMINAL: /* VIN5 */ + *value = 500000 + (20000 * st->vmon[index]); + return 0; + case MAX42500_VMON_IN6_CRIT: /* VINO6 */ + case MAX42500_VMON_IN6_LCRIT: /* VINU6 */ + case MAX42500_VMON_IN7_CRIT: /* VINO7 */ + case MAX42500_VMON_IN7_LCRIT: /* VINU7 */ + *value = 500000 + (5000 * st->vmon[index]); + return 0; + case MAX42500_VMON_IN1_OV_THRESH: /* VINO1 */ + case MAX42500_VMON_IN2_OV_THRESH: /* VINO2 */ + case MAX42500_VMON_IN3_OV_THRESH: /* VINO3 */ + case MAX42500_VMON_IN4_OV_THRESH: /* VINO4 */ + case MAX42500_VMON_IN5_OV_THRESH: /* VINO5 */ + *value = 102500 + (500 * st->vmon[index]); + return 0; + case MAX42500_VMON_IN1_UV_THRESH: /* VINU1 */ + case MAX42500_VMON_IN2_UV_THRESH: /* VINU2 */ + case MAX42500_VMON_IN3_UV_THRESH: /* VINU3 */ + case MAX42500_VMON_IN4_UV_THRESH: /* VINU4 */ + case MAX42500_VMON_IN5_UV_THRESH: /* VINU5 */ + *value = 97500 - (500 * st->vmon[index]); + return 0; + default: + *value = st->vmon[index]; + return 0; + } +} + +static int max42500_fpsr_read(struct max42500_state *st, long *value, + enum max42500_fpsr index) +{ + u8 clk_div = MAX42500_FPSR_FPS_CLOCK_DIVIDER; + int ret; + + ret = max42500_fpsr_get_update(st, index); + if (ret) + return ret; + + switch (index) { + case MAX42500_FPSR_POWER1_AVERAGE_INTERVAL_MAX: /* UTIM1 */ + case MAX42500_FPSR_POWER2_AVERAGE_INTERVAL_MAX: /* UTIM2 */ + case MAX42500_FPSR_POWER3_AVERAGE_INTERVAL_MAX: /* UTIM3 */ + case MAX42500_FPSR_POWER4_AVERAGE_INTERVAL_MAX: /* UTIM4 */ + case MAX42500_FPSR_POWER5_AVERAGE_INTERVAL_MAX: /* UTIM5 */ + case MAX42500_FPSR_POWER6_AVERAGE_INTERVAL_MAX: /* UTIM6 */ + case MAX42500_FPSR_POWER7_AVERAGE_INTERVAL_MAX: /* UTIM7 */ + case MAX42500_FPSR_POWER1_AVERAGE_INTERVAL_MIN: /* DTIM1 */ + case MAX42500_FPSR_POWER2_AVERAGE_INTERVAL_MIN: /* DTIM2 */ + case MAX42500_FPSR_POWER3_AVERAGE_INTERVAL_MIN: /* DTIM3 */ + case MAX42500_FPSR_POWER4_AVERAGE_INTERVAL_MIN: /* DTIM4 */ + case MAX42500_FPSR_POWER5_AVERAGE_INTERVAL_MIN: /* DTIM5 */ + case MAX42500_FPSR_POWER6_AVERAGE_INTERVAL_MIN: /* DTIM6 */ + case MAX42500_FPSR_POWER7_AVERAGE_INTERVAL_MIN: /* DTIM7 */ + /* Get the latest FPS clock divider for the timestamp */ + ret = max42500_fpsr_get_update(st, clk_div); + if (ret) + return ret; + + *value = (st->fpsr[index] - 1) * 25 * (1 << st->fpsr[clk_div]); + return 0; + default: + *value = st->fpsr[index]; + return 0; + } +} + +static int max42500_vmon_write(struct max42500_state *st, long value, + enum MAX42500_vmon index) +{ + long vmon; + + switch (index) { + case MAX42500_VMON_IN1_NOMINAL: /* VIN1 */ + case MAX42500_VMON_IN2_NOMINAL: /* VIN2 */ + case MAX42500_VMON_IN3_NOMINAL: /* VIN3 */ + case MAX42500_VMON_IN4_NOMINAL: /* VIN4 */ + vmon = (value - 500000) / 12500; + return max42500_vmon_set_update(st, vmon, index); + case MAX42500_VMON_IN5_NOMINAL: /* VIN5 */ + vmon = (value - 500000) / 20000; + return max42500_vmon_set_update(st, vmon, index); + case MAX42500_VMON_IN6_CRIT: /* VINO6 */ + case MAX42500_VMON_IN6_LCRIT: /* VINU6 */ + case MAX42500_VMON_IN7_CRIT: /* VINO7 */ + case MAX42500_VMON_IN7_LCRIT: /* VINU7 */ + vmon = (value - 500000) / 5000; + return max42500_vmon_set_update(st, vmon, index); + case MAX42500_VMON_IN1_OV_THRESH: /* VINO1 */ + case MAX42500_VMON_IN2_OV_THRESH: /* VINO2 */ + case MAX42500_VMON_IN3_OV_THRESH: /* VINO3 */ + case MAX42500_VMON_IN4_OV_THRESH: /* VINO4 */ + case MAX42500_VMON_IN5_OV_THRESH: /* VINO5 */ + vmon = (value - 102500) / 500; + return max42500_vmon_set_update(st, vmon, index); + case MAX42500_VMON_IN1_UV_THRESH: /* VINU1 */ + case MAX42500_VMON_IN2_UV_THRESH: /* VINU2 */ + case MAX42500_VMON_IN3_UV_THRESH: /* VINU3 */ + case MAX42500_VMON_IN4_UV_THRESH: /* VINU4 */ + case MAX42500_VMON_IN5_UV_THRESH: /* VINU5 */ + vmon = (97500 - value) / 500; + return max42500_vmon_set_update(st, vmon, index); + default: + return -EOPNOTSUPP; + } +} + +static int max42500_read_chip(struct max42500_state *st, const u32 attr, + long *val) +{ + int ret; + + switch (attr) { + case hwmon_chip_pec: + ret = max42500_chip_get_update(st, MAX42500_CHIP_PEC_ENABLE); + if (ret) + return ret; + + *val = st->chip[MAX42500_CHIP_PEC_ENABLE]; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int max42500_read_in(struct max42500_state *st, const u32 attr, + long *val, const int channel) +{ + int index; + + switch (attr) { + case hwmon_in_enable: + index = MAX42500_VMON_IN1_ENABLE + channel; + return max42500_vmon_read(st, val, index); + case hwmon_in_lcrit: + index = MAX42500_VMON_IN1_UV_THRESH + channel; + return max42500_vmon_read(st, val, index); + case hwmon_in_crit: + index = MAX42500_VMON_IN1_OV_THRESH + channel; + return max42500_vmon_read(st, val, index); + case hwmon_in_min: + index = MAX42500_VMON_IN1_NOMINAL + channel; + return max42500_vmon_read(st, val, index); + default: + return -EOPNOTSUPP; + } +} + +static int max42500_read_power(struct max42500_state *st, const u32 attr, + long *val, const int channel) +{ + int index; + + switch (attr) { + case hwmon_power_enable: + return max42500_vmon_read(st, val, MAX42500_VMON_OFF_STATUS); + case hwmon_power_lcrit_alarm: + return max42500_vmon_read(st, val, MAX42500_VMON_UV_STATUS); + case hwmon_power_crit_alarm: + return max42500_vmon_read(st, val, MAX42500_VMON_OV_STATUS); + case hwmon_power_average_interval_max: + index = MAX42500_FPSR_POWER1_AVERAGE_INTERVAL_MAX + channel; + return max42500_fpsr_read(st, val, index); + case hwmon_power_average_interval_min: + index = MAX42500_FPSR_POWER1_AVERAGE_INTERVAL_MIN + channel; + return max42500_fpsr_read(st, val, index); + default: + return -EOPNOTSUPP; + } +} + +static int max42500_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct max42500_state *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_chip: + return max42500_read_chip(st, attr, val); + case hwmon_in: + return max42500_read_in(st, attr, val, channel); + case hwmon_power: + return max42500_read_power(st, attr, val, channel); + default: + return -EOPNOTSUPP; + } +} + +static int max42500_write_chip(struct max42500_state *st, const u32 attr, + long val) +{ + switch (attr) { + case hwmon_chip_pec: + return max42500_chip_set_update(st, val, + MAX42500_CHIP_PEC_ENABLE); + default: + return -EOPNOTSUPP; + } +} + +static int max42500_write_in(struct max42500_state *st, const u32 attr, + long val, const int channel) +{ + int index; + + switch (attr) { + case hwmon_in_enable: + index = MAX42500_VMON_IN1_ENABLE + channel; + return max42500_vmon_write(st, val, index); + case hwmon_in_lcrit: + index = MAX42500_VMON_IN1_UV_THRESH + channel; + return max42500_vmon_write(st, val, index); + case hwmon_in_crit: + index = MAX42500_VMON_IN1_OV_THRESH + channel; + return max42500_vmon_write(st, val, index); + case hwmon_in_min: + index = MAX42500_VMON_IN1_NOMINAL + channel; + return max42500_vmon_write(st, val, index); + case hwmon_in_reset_history: + index = MAX42500_VMON_IN1_RESET_ENABLE + channel; + return max42500_vmon_write(st, val, index); + default: + return -EOPNOTSUPP; + } +} + +static int max42500_write(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct max42500_state *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_chip: + return max42500_write_chip(st, attr, val); + case hwmon_in: + return max42500_write_in(st, attr, val, channel); + default: + return -EOPNOTSUPP; + } +} + +static int max42500_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_in: + *str = "VMON"; + return 0; + case hwmon_power: + *str = "STATUS"; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int max42500_chip_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_chip_pec: + return 0644; + default: + return 0; + } +} + +static int max42500_in_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_in_label: + return 0444; + case hwmon_in_reset_history: + return 0200; + case hwmon_in_enable: + case hwmon_in_lcrit: + case hwmon_in_crit: + case hwmon_in_min: + return 0644; + default: + return 0; + } +} + +static int max42500_power_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_power_enable: + case hwmon_power_lcrit_alarm: + case hwmon_power_crit_alarm: + case hwmon_power_average_interval_min: + case hwmon_power_average_interval_max: + return 0444; + default: + return 0; + } +} + +static umode_t max42500_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_chip: + return max42500_chip_is_visible(attr); + case hwmon_in: + return max42500_in_is_visible(attr); + case hwmon_power: + return max42500_power_is_visible(attr); + default: + return 0; + } +} + +static const struct hwmon_channel_info * const max42500_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_PEC), + HWMON_CHANNEL_INFO(in, + HWMON_I_ENABLE | HWMON_I_LCRIT | HWMON_I_CRIT | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL | HWMON_I_MIN, + HWMON_I_ENABLE | HWMON_I_LCRIT | HWMON_I_CRIT | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL | HWMON_I_MIN, + HWMON_I_ENABLE | HWMON_I_LCRIT | HWMON_I_CRIT | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL | HWMON_I_MIN, + HWMON_I_ENABLE | HWMON_I_LCRIT | HWMON_I_CRIT | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL | HWMON_I_MIN, + HWMON_I_ENABLE | HWMON_I_LCRIT | HWMON_I_CRIT | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL | HWMON_I_MIN, + HWMON_I_ENABLE | HWMON_I_LCRIT | HWMON_I_CRIT | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL, + HWMON_I_ENABLE | HWMON_I_LCRIT | HWMON_I_CRIT | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(power, + HWMON_P_ENABLE | + HWMON_P_LCRIT_ALARM | + HWMON_P_CRIT_ALARM | + HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX, + HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX, + HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX, + HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX, + HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX, + HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX, + HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX), + NULL +}; + +static const struct hwmon_ops max42500_hwmon_ops = { + .read = max42500_read, + .write = max42500_write, + .is_visible = max42500_is_visible, + .read_string = max42500_read_labels, +}; + +static const struct hwmon_chip_info max42500_chip_info = { + .ops = &max42500_hwmon_ops, + .info = max42500_info, +}; + +static int max42500_show_chip_label_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_chip_get_update(st, MAX42500_CHIP_LABEL); + if (ret) + return ret; + + *val = st->chip[MAX42500_CHIP_LABEL]; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_chip_label_log, + max42500_show_chip_label_log, NULL, "%llu\n"); + +static int max42500_show_chip_name_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_chip_get_update(st, MAX42500_CHIP_NAME); + if (ret) + return ret; + + *val = st->chip[MAX42500_CHIP_NAME]; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_chip_name_log, + max42500_show_chip_name_log, NULL, "%llu\n"); + +static int max42500_show_chip_otp_enable_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_chip_get_update(st, MAX42500_CHIP_RELOAD_OTP_ENABLE); + if (ret) + return ret; + + *val = st->chip[MAX42500_CHIP_RELOAD_OTP_ENABLE]; + + return 0; +} + +static int max42500_store_chip_otp_enable_log(void *arg, u64 val) +{ + return max42500_chip_set_update(arg, val, + MAX42500_CHIP_RELOAD_OTP_ENABLE); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_chip_otp_enable_log, + max42500_show_chip_otp_enable_log, + max42500_store_chip_otp_enable_log, "%llu\n"); + +static int max42500_show_chip_bist_rst_enable_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_chip_get_update(st, MAX42500_CHIP_BIST_RESET_ENABLE); + if (ret) + return ret; + + *val = st->chip[MAX42500_CHIP_BIST_RESET_ENABLE]; + + return 0; +} + +static int max42500_store_chip_bist_rst_enable_log(void *arg, u64 val) +{ + return max42500_chip_set_update(arg, val, + MAX42500_CHIP_BIST_RESET_ENABLE); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_chip_bist_rst_enable_log, + max42500_show_chip_bist_rst_enable_log, + max42500_store_chip_bist_rst_enable_log, "%llu\n"); + +static int max42500_show_chip_config_status_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_chip_get_update(st, MAX42500_CHIP_CONFIG_STATUS); + if (ret) + return ret; + + *val = st->chip[MAX42500_CHIP_CONFIG_STATUS]; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_chip_config_status_log, + max42500_show_chip_config_status_log, NULL, "%llu\n"); + +static int max42500_show_fpsr_status_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_fpsr_get_update(st, MAX42500_FPSR_FPS_STATUS); + if (ret) + return ret; + + *val = st->fpsr[MAX42500_FPSR_FPS_STATUS]; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_fpsr_status_log, + max42500_show_fpsr_status_log, NULL, "%llu\n"); + +static int max42500_show_pwrup_capt_finish_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_fpsr_get_update(st, + MAX42500_FPSR_POWERUP_SEQ_CAPTURE_FINISH); + if (ret) + return ret; + + *val = st->fpsr[MAX42500_FPSR_POWERUP_SEQ_CAPTURE_FINISH]; + + return 0; +} + +static int max42500_store_pwrup_capt_finish_log(void *arg, u64 val) +{ + return max42500_fpsr_set_update(arg, val, + MAX42500_FPSR_POWERUP_SEQ_CAPTURE_FINISH); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_pwrup_capt_finish_log, + max42500_show_pwrup_capt_finish_log, + max42500_store_pwrup_capt_finish_log, "%llu\n"); + +static int max42500_show_pwrup_capt_enable_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_fpsr_get_update(st, + MAX42500_FPSR_POWERUP_NO_INTERRUPT_ENABLE); + if (ret) + return ret; + + *val = st->fpsr[MAX42500_FPSR_POWERUP_NO_INTERRUPT_ENABLE]; + + return 0; +} + +static int max42500_store_pwrup_capt_enable_log(void *arg, u64 val) +{ + return max42500_fpsr_set_update(arg, val, + MAX42500_FPSR_POWERUP_NO_INTERRUPT_ENABLE); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_pwrup_capt_enable_log, + max42500_show_pwrup_capt_enable_log, + max42500_store_pwrup_capt_enable_log, "%llu\n"); + +static int max42500_show_pwrdn_capt_finish_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_fpsr_get_update(st, + MAX42500_FPSR_POWERDN_SEQ_CAPTURE_FINISH); + if (ret) + return ret; + + *val = st->fpsr[MAX42500_FPSR_POWERDN_SEQ_CAPTURE_FINISH]; + + return 0; +} + +static int max42500_store_pwrdn_capt_finish_log(void *arg, u64 val) +{ + return max42500_fpsr_set_update(arg, val, + MAX42500_FPSR_POWERDN_SEQ_CAPTURE_FINISH); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_pwrdn_capt_finish_log, + max42500_show_pwrdn_capt_finish_log, + max42500_store_pwrdn_capt_finish_log, "%llu\n"); + +static int max42500_show_fps_clk_div_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_fpsr_get_update(st, MAX42500_FPSR_FPS_CLOCK_DIVIDER); + if (ret) + return ret; + + *val = st->fpsr[MAX42500_FPSR_FPS_CLOCK_DIVIDER]; + + return 0; +} + +static int max42500_store_fps_clk_div_log(void *arg, u64 val) +{ + return max42500_fpsr_set_update(arg, val, + MAX42500_FPSR_FPS_CLOCK_DIVIDER); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_fps_clk_div_log, + max42500_show_fps_clk_div_log, + max42500_store_fps_clk_div_log, "%llu\n"); + +static int max42500_show_pwrdn_capt_enable_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_fpsr_get_update(st, + MAX42500_FPSR_POWERDN_NO_INTERRUPT_ENABLE); + if (ret) + return ret; + + *val = st->fpsr[MAX42500_FPSR_POWERDN_NO_INTERRUPT_ENABLE]; + + return 0; +} + +static int max42500_store_pwrdn_capt_enable_log(void *arg, u64 val) +{ + return max42500_fpsr_set_update(arg, val, + MAX42500_FPSR_POWERDN_NO_INTERRUPT_ENABLE); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_pwrdn_capt_enable_log, + max42500_show_pwrdn_capt_enable_log, + max42500_store_pwrdn_capt_enable_log, "%llu\n"); + +static int max42500_show_fps_en1_timer_enable_log(void *arg, u64 *val) +{ + struct max42500_state *st = arg; + int ret; + + ret = max42500_fpsr_get_update(st, + MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE); + if (ret) + return ret; + + *val = st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE]; + + return 0; +} + +static int max42500_store_fps_en1_timer_enable_log(void *arg, u64 val) +{ + return max42500_fpsr_set_update(arg, val, + MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_fps_en1_timer_enable_log, + max42500_show_fps_en1_timer_enable_log, + max42500_store_fps_en1_timer_enable_log, "%llu\n"); + +static void max42500_hwmon_debugfs_remove(void *dir) +{ + debugfs_remove_recursive(dir); +} + +static void max42500_hwmon_debugfs_init(struct max42500_state *st, + struct platform_device *pdev, + const struct device *hwmon) +{ + const char *debugfs_name; + struct dentry *dentry; + int ret; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + + debugfs_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "max42500-%s", + dev_name(hwmon)); + if (!debugfs_name) + return; + + dentry = debugfs_create_dir(debugfs_name, NULL); + if (IS_ERR(dentry)) + return; + + ret = devm_add_action_or_reset(&pdev->dev, + max42500_hwmon_debugfs_remove, + dentry); + if (ret) + return; + + /* Chip registers */ + debugfs_create_file_unsafe("chip_label_log", 0400, dentry, st, + &max42500_chip_label_log); + debugfs_create_file_unsafe("chip_name_log", 0400, dentry, st, + &max42500_chip_name_log); + debugfs_create_file_unsafe("chip_otp_enable_log", 0644, dentry, st, + &max42500_chip_otp_enable_log); + debugfs_create_file_unsafe("chip_bist_rst_enable_log", 0644, dentry, st, + &max42500_chip_bist_rst_enable_log); + debugfs_create_file_unsafe("chip_config_status_log", 0400, dentry, st, + &max42500_chip_config_status_log); + + /* FPSR registers */ + debugfs_create_file_unsafe("fpsr_status_log", 0400, dentry, st, + &max42500_fpsr_status_log); + debugfs_create_file_unsafe("pwrup_capt_finish_log", 0644, dentry, st, + &max42500_pwrup_capt_finish_log); + debugfs_create_file_unsafe("pwrdn_capt_finish_log", 0644, dentry, st, + &max42500_pwrdn_capt_finish_log); + debugfs_create_file_unsafe("pwrup_capt_enable_log", 0644, dentry, st, + &max42500_pwrup_capt_enable_log); + debugfs_create_file_unsafe("pwrdn_capt_enable", 0644, dentry, st, + &max42500_pwrdn_capt_enable_log); + debugfs_create_file_unsafe("fps_clk_div_log", 0644, dentry, st, + &max42500_fps_clk_div_log); + debugfs_create_file_unsafe("fps_en1_timer_enable_log", 0644, dentry, st, + &max42500_fps_en1_timer_enable_log); +} + +static int max42500_hwmon_probe(struct platform_device *pdev) +{ + struct device *parent_dev = pdev->dev.parent; + struct regmap *regmap = dev_get_drvdata(parent_dev); + struct i2c_client *client = to_i2c_client(parent_dev); + struct device *dev = &pdev->dev; + struct device *hwmon_dev; + struct max42500_state *st; + int ret; + + st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + platform_set_drvdata(pdev, st); + + st->client = client; + st->regmap = regmap; + + device_set_of_node_from_dev(dev, dev->parent); + + st->power_gpio = devm_gpiod_get_optional(dev, "poweroff-gpios", + GPIOD_OUT_HIGH); + if (IS_ERR(st->power_gpio)) + return PTR_ERR(st->power_gpio); + + st->sleep_gpio = devm_gpiod_get_optional(dev, "sleepoff-gpios", + GPIOD_OUT_HIGH); + if (IS_ERR(st->sleep_gpio)) + return PTR_ERR(st->sleep_gpio); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "max42500", + st, &max42500_chip_info, + NULL); + + max42500_hwmon_debugfs_init(st, pdev, hwmon_dev); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static int max42500_resume(struct device *dev) +{ + struct max42500_state *st = dev_get_drvdata(dev); + + gpiod_set_value(st->power_gpio, GPIOD_OUT_HIGH); + + gpiod_set_value(st->sleep_gpio, + !st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE]); + + return 0; +} + +static int max42500_suspend(struct device *dev) +{ + struct max42500_state *st = dev_get_drvdata(dev); + + gpiod_set_value(st->power_gpio, GPIOD_OUT_LOW); + + gpiod_set_value(st->sleep_gpio, + !!st->fpsr[MAX42500_FPSR_FPS_EN1_START_TIMER_ENABLE]); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(max42500_pm_ops, max42500_suspend, + max42500_resume); + +static const struct platform_device_id max42500_hwmon_id_table[] = { + { "max42500-hwmon" }, + { } +}; +MODULE_DEVICE_TABLE(platform, max42500_hwmon_id_table); + +static struct platform_driver max42500_hwmon_driver = { + .driver = { + .name = "max42500-hwmon", + .pm = pm_sleep_ptr(&max42500_pm_ops), + }, + .probe = max42500_hwmon_probe, + .id_table = max42500_hwmon_id_table, +}; +module_platform_driver(max42500_hwmon_driver); + +MODULE_AUTHOR("Kent Libetario "); +MODULE_DESCRIPTION("Hwmon driver for MAX42500"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 90ce58fd629e5f..60e19d1db3a96d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -808,6 +808,19 @@ config MFD_MAX14577 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX42500 + bool "Analog Devices MAX42500 Industrial Power System Monitor" + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + Say yes here to add support for MAX42500 SoC power-system monitor + with up to seven voltage monitor. The driver also contains a + programmable challenge/response watchdog, which is accessible through + the I2C interface, along with a configurable RESET output. Additional + drivers can be enabled in order to use the following functionalities + of the device: GPIO. + config MFD_MAX77541 tristate "Analog Devices MAX77541/77540 PMIC Support" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c66f07edcd0e62..9a3358fd41d9d7 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -157,6 +157,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o obj-$(CONFIG_MFD_DA9150) += da9150-core.o obj-$(CONFIG_MFD_MAX14577) += max14577.o +obj-$(CONFIG_MFD_MAX42500) += max42500.o obj-$(CONFIG_MFD_MAX77541) += max77541.o obj-$(CONFIG_MFD_MAX77620) += max77620.o obj-$(CONFIG_MFD_MAX77650) += max77650.o diff --git a/drivers/mfd/max42500.c b/drivers/mfd/max42500.c new file mode 100644 index 00000000000000..5564cd9f6d3cb2 --- /dev/null +++ b/drivers/mfd/max42500.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MAX42500 - MFD Power System Monitor + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell max42500_subdev[] = { + MFD_CELL_NAME("max42500-hwmon"), + MFD_CELL_NAME("max42500-wdt"), +}; + +static const struct regmap_config max42500_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX42500_REG_CID, +}; + +static int max42500_probe(struct i2c_client *i2c) +{ + struct regmap *map; + + map = devm_regmap_init_i2c(i2c, &max42500_regmap_config); + if (IS_ERR(map)) + return PTR_ERR(map); + + return devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, + max42500_subdev, ARRAY_SIZE(max42500_subdev), + NULL, 0, NULL); +} + +static const struct of_device_id max42500_of_match[] = { + { .compatible = "adi,max42500" }, + { } +}; +MODULE_DEVICE_TABLE(of, max42500_of_match); + +static struct i2c_driver max42500_driver = { + .driver = { + .name = "max42500", + .of_match_table = max42500_of_match, + }, + .probe = max42500_probe, +}; +module_i2c_driver(max42500_driver); + +MODULE_AUTHOR("Kent Libetario "); +MODULE_DESCRIPTION("MFD driver for MAX42500"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 751458959411cc..e748fb96534604 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -703,6 +703,16 @@ config MAX77620_WATCHDOG MAX77620 chips. To compile this driver as a module, choose M here: the module will be called max77620_wdt. +config MAX42500_WDT + tristate "MAX42500 Programmable Windowed Watchdog" + depends on MFD_MAX42500 + select WATCHDOG_CORE + help + This is the driver for the MAX42500 watchdog timer. + Say 'Y' here to enable the watchdog timer support for + MAX42500 chips. To compile this driver as a module, + choose M here: the module will be called max42500-wdt. + config IMX2_WDT tristate "IMX2+ Watchdog" depends on ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7eab9de311cb93..a4cfe1e81abf99 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -226,6 +226,7 @@ obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o +obj-$(CONFIG_MAX42500_WDT) += max42500-wdt.o obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o obj-$(CONFIG_MAX77620_WATCHDOG) += max77620_wdt.o obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o diff --git a/drivers/watchdog/max42500-wdt.c b/drivers/watchdog/max42500-wdt.c new file mode 100644 index 00000000000000..813dda580627ad --- /dev/null +++ b/drivers/watchdog/max42500-wdt.c @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MAX42500 - Programmable Windowed Watchdog + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum max42500_wdt_wd_mode { + MAX42500_WD_MODE_CH_RESP, + MAX42500_WD_MODE_SIMPLE, + MAX42500_WD_MODE_MAX +}; + +enum max42500_wdt_wdog_attr { + MAX42500_WDOG_WD_STATUS, + MAX42500_WDOG_WD_SIMPLE_MODE_ENABLE, + MAX42500_WDOG_WD_CLOCK_DIVIDER, + MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL, + MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL, + MAX42500_WDOG_WD_ENABLE, + MAX42500_WDOG_WD_FIRST_UPDATE_INTERVAL, + MAX42500_WDOG_WD_KEY, + MAX42500_WDOG_WD_LOCK_ENABLE, + MAX42500_WDOG_WD_RESET_TWO_COUNT_ENABLE, + MAX42500_WDOG_WD_RESET_HOLD_INTERVAL, + MAX42500_WDOG_MAX +}; + +static const u8 max42500_wdt_reg_wdog[] = { + MAX42500_REG_WDSTAT, + MAX42500_REG_WDCDIV, + MAX42500_REG_WDCDIV, + MAX42500_REG_WDCFG1, + MAX42500_REG_WDCFG1, + MAX42500_REG_WDCFG2, + MAX42500_REG_WDCFG2, + MAX42500_REG_WDKEY, + MAX42500_REG_WDLOCK, + MAX42500_REG_RSTCTRL, + MAX42500_REG_RSTCTRL +}; + +struct max42500_wdt_state { + struct watchdog_device wwd; + struct regmap *regmap; + struct mutex lock; /* Protects access during data transfer */ + u8 wdog[MAX42500_WDOG_MAX]; +}; + +/* Watchdog Reset hold time (usec) to Register Conversion */ +static int max42500_wdt_convert_wd_holdreset_to_reg(long value, long *reg_val) +{ + /* Check values if divisible by 8. */ + if (value % 8) + return -EINVAL; + + /* Valid are 0, 8, 16 and 32 only. */ + value = clamp_val(value, 0, 32); + if (value == 0) { + *reg_val = value; + return 0; + } + + /* Test for valid bits 1, 2 or 4. */ + if (((value >> 3) ^ 3)) { + *reg_val = (value >> 4) + 1; + return 0; + } + + return -EINVAL; +} + +/* Watchdog Key Challenge-Response Computation */ +static int max42500_wdt_set_watchdog_key(struct max42500_wdt_state *st, + enum max42500_wdt_wd_mode mode) +{ + unsigned int reg_val = 0; + unsigned int key; + u8 lfsr; + int ret; + + switch (mode) { + case MAX42500_WD_MODE_CH_RESP: + scoped_guard(mutex, &st->lock) { + ret = regmap_read(st->regmap, MAX42500_REG_WDKEY, &key); + if (ret) + return ret; + + /* Linear-Feedback Shift Register (LFSR) Polynomial Equation */ + lfsr = ((key >> 7) ^ (key >> 5) ^ (key >> 4) ^ + (key >> 3)) & 1; + reg_val = (key << 1) | lfsr; + } + + return regmap_write(st->regmap, MAX42500_REG_WDKEY, reg_val); + case MAX42500_WD_MODE_SIMPLE: + /* Any write to WDKEY register will update the watchdog */ + return regmap_write(st->regmap, MAX42500_REG_WDKEY, reg_val); + default: + return -EINVAL; + } +} + +static int max42500_wdt_wdog_get_update(struct max42500_wdt_state *st, + enum max42500_wdt_wdog_attr index) +{ + unsigned int reg_val; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, max42500_wdt_reg_wdog[index], ®_val); + if (ret) + return ret; + + switch (index) { + case MAX42500_WDOG_WD_STATUS: /* WDSTAT */ + st->wdog[index] = MAX42500_WDOG_WDSTAT(reg_val); + return 0; + case MAX42500_WDOG_WD_CLOCK_DIVIDER: /* WDIV */ + st->wdog[index] = MAX42500_WDOG_WDIV(reg_val); + return 0; + case MAX42500_WDOG_WD_SIMPLE_MODE_ENABLE: /* SWW */ + st->wdog[index] = MAX42500_WDOG_SWW(reg_val); + return 0; + case MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL: /* WDOPEN */ + st->wdog[index] = MAX42500_WDOG_WDOPEN(reg_val); + return 0; + case MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL: /* WDCLOSE */ + st->wdog[index] = MAX42500_WDOG_WDCLOSE(reg_val); + return 0; + case MAX42500_WDOG_WD_FIRST_UPDATE_INTERVAL: /* 1UD */ + st->wdog[index] = MAX42500_WDOG_1UD(reg_val); + return 0; + case MAX42500_WDOG_WD_ENABLE: /* WDEN */ + st->wdog[index] = MAX42500_WDOG_WDEN(reg_val); + return 0; + case MAX42500_WDOG_WD_KEY: /* WDKEY */ + st->wdog[index] = MAX42500_WDOG_WDKEY(reg_val); + return 0; + case MAX42500_WDOG_WD_LOCK_ENABLE: /* WDLOCK */ + st->wdog[index] = MAX42500_WDOG_WDLOCK(reg_val); + return 0; + case MAX42500_WDOG_WD_RESET_HOLD_INTERVAL: /* RHLD */ + st->wdog[index] = MAX42500_WDOG_RHLD(reg_val); + return 0; + case MAX42500_WDOG_WD_RESET_TWO_COUNT_ENABLE: /* MR1 */ + st->wdog[index] = MAX42500_WDOG_MR1(reg_val); + return 0; + default: + return -EINVAL; + } +} + +static int max42500_wdt_wdog_set_update(struct max42500_wdt_state *st, + long value, + enum max42500_wdt_wdog_attr index) +{ + unsigned int reg_mask; + int ret; + + guard(mutex)(&st->lock); + + switch (index) { + case MAX42500_WDOG_WD_CLOCK_DIVIDER: /* WDIV */ + reg_mask = MAX42500_WDOG_WDIV_MASK; + break; + case MAX42500_WDOG_WD_SIMPLE_MODE_ENABLE: /* SWW */ + reg_mask = MAX42500_WDOG_SWW_MASK; + break; + case MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL: /* WDOPEN */ + reg_mask = MAX42500_WDOG_WDOPEN_MASK; + break; + case MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL: /* WDCLOSE */ + reg_mask = MAX42500_WDOG_WDCLOSE_MASK; + break; + case MAX42500_WDOG_WD_FIRST_UPDATE_INTERVAL: /* 1UD */ + reg_mask = MAX42500_WDOG_1UD_MASK; + break; + case MAX42500_WDOG_WD_ENABLE: /* WDEN */ + reg_mask = MAX42500_WDOG_WDEN_MASK; + break; + case MAX42500_WDOG_WD_KEY: /* WDKEY */ + reg_mask = MAX42500_WDOG_WDKEY_MASK; + break; + case MAX42500_WDOG_WD_LOCK_ENABLE: /* WDLOCK */ + reg_mask = MAX42500_WDOG_WDLOCK_MASK; + break; + case MAX42500_WDOG_WD_RESET_HOLD_INTERVAL: /* RHLD */ + reg_mask = MAX42500_WDOG_RHLD_MASK; + break; + case MAX42500_WDOG_WD_RESET_TWO_COUNT_ENABLE: /* MR1 */ + reg_mask = MAX42500_WDOG_MR1_MASK; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->regmap, max42500_wdt_reg_wdog[index], + reg_mask, value); + if (ret) + return ret; + + /* Update after successful register write */ + st->wdog[index] = FIELD_GET(GENMASK(7, 0), value); + + return 0; +} + +static int max42500_wdt_wdog_read(struct max42500_wdt_state *st, long *value, + enum max42500_wdt_wdog_attr index) +{ + u8 clk_div = MAX42500_WDOG_WD_CLOCK_DIVIDER; + u8 opn_win = MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL; + u8 clo_win = MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL; + int ret; + + ret = max42500_wdt_wdog_get_update(st, index); + if (ret) + return ret; + + switch (index) { + case MAX42500_WDOG_WD_CLOCK_DIVIDER: /* WDIV */ + *value = (st->wdog[index] + 1) * 200; + return 0; + case MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL: /* WDOPEN */ + case MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL: /* WDCLOSE */ + ret = max42500_wdt_wdog_get_update(st, clk_div); + if (ret) + return ret; + + *value = ((st->wdog[index] + 1) << 3) * st->wdog[clk_div]; + return 0; + case MAX42500_WDOG_WD_FIRST_UPDATE_INTERVAL: /* 1UD */ + ret = max42500_wdt_wdog_get_update(st, clo_win); + if (ret) + return ret; + + ret = max42500_wdt_wdog_get_update(st, opn_win); + if (ret) + return ret; + + *value = (st->wdog[clo_win] + st->wdog[opn_win]) * + ((st->wdog[index] << 1) + 1); + return 0; + case MAX42500_WDOG_WD_RESET_HOLD_INTERVAL: /* RHLD */ + *value = st->wdog[index] ? (1 << (st->wdog[index] + 3)) : 0; + return 0; + default: + *value = st->wdog[index]; + return 0; + } +} + +static int max42500_wdt_wdog_write(struct max42500_wdt_state *st, long value, + enum max42500_wdt_wdog_attr index) +{ + u8 clk_div = MAX42500_WDOG_WD_CLOCK_DIVIDER; + u8 win_mode = MAX42500_WDOG_WD_SIMPLE_MODE_ENABLE; + u8 opn_win = MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL; + u8 clo_win = MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL; + long wdog; + int ret; + + switch (index) { + case MAX42500_WDOG_WD_CLOCK_DIVIDER: /* WDIV */ + wdog = (value - 200) / 200; + break; + case MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL: /* WDOPEN */ + case MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL: /* WDCLOSE */ + wdog = value / (st->wdog[clk_div] << 3); + break; + case MAX42500_WDOG_WD_FIRST_UPDATE_INTERVAL: /* 1UD */ + wdog = ((value / (st->wdog[clo_win] + st->wdog[opn_win])) + - 1) >> 1; + break; + case MAX42500_WDOG_WD_RESET_HOLD_INTERVAL: /* RHLD */ + /* Valid values are 0, 8, 16, 32 and truncates larger values */ + ret = max42500_wdt_convert_wd_holdreset_to_reg(value, &wdog); + if (ret) + return ret; + break; + case MAX42500_WDOG_WD_KEY: /* WDKEY */ + /* User cannot input watchdog key but can trigger key update */ + ret = max42500_wdt_set_watchdog_key(st, st->wdog[win_mode]); + if (ret) + return ret; + + return 0; + default: + return -EINVAL; + } + + return max42500_wdt_wdog_set_update(st, wdog, index); +} + +static int max42500_wdt_show_wd_simp_mode_enable_log(void *arg, u64 *val) +{ + long wdog_val; + int ret; + + ret = max42500_wdt_wdog_read(arg, &wdog_val, + MAX42500_WDOG_WD_SIMPLE_MODE_ENABLE); + if (ret) + return ret; + + *val = wdog_val; + return 0; +} + +static int max42500_wdt_store_wd_simp_mode_enable_log(void *arg, u64 val) +{ + return max42500_wdt_wdog_write(arg, val, + MAX42500_WDOG_WD_SIMPLE_MODE_ENABLE); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_wdt_wd_simp_mode_enable_log, + max42500_wdt_show_wd_simp_mode_enable_log, + max42500_wdt_store_wd_simp_mode_enable_log, "%llu\n"); + +static int max42500_wdt_show_wd_open_win_log(void *arg, u64 *val) +{ + long wdog_val; + int ret; + + ret = max42500_wdt_wdog_read(arg, &wdog_val, + MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL); + if (ret) + return ret; + + *val = wdog_val; + return 0; +} + +static int max42500_wdt_store_wd_open_win_log(void *arg, u64 val) +{ + return max42500_wdt_wdog_write(arg, val, + MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_wdt_wd_open_win_log, + max42500_wdt_show_wd_open_win_log, + max42500_wdt_store_wd_open_win_log, "%llu\n"); + +static int max42500_wdt_show_wd_close_win_log(void *arg, u64 *val) +{ + long wdog_val; + int ret; + + ret = max42500_wdt_wdog_read(arg, &wdog_val, + MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL); + if (ret) + return ret; + + *val = wdog_val; + return 0; +} + +static int max42500_wdt_store_wd_close_win_log(void *arg, u64 val) +{ + return max42500_wdt_wdog_write(arg, val, + MAX42500_WDOG_WD_CLOSE_WINDOW_INTERVAL); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_wdt_wd_close_win_log, + max42500_wdt_show_wd_close_win_log, + max42500_wdt_store_wd_close_win_log, "%llu\n"); + +static int max42500_wdt_show_wd_lock_enablelog(void *arg, u64 *val) +{ + long wdog_val; + int ret; + + ret = max42500_wdt_wdog_read(arg, &wdog_val, + MAX42500_WDOG_WD_LOCK_ENABLE); + if (ret) + return ret; + + *val = wdog_val; + return 0; +} + +static int max42500_wdt_store_wd_lock_enable_log(void *arg, u64 val) +{ + return max42500_wdt_wdog_write(arg, val, + MAX42500_WDOG_WD_LOCK_ENABLE); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_wdt_wd_lock_enable_log, + max42500_wdt_show_wd_lock_enablelog, + max42500_wdt_store_wd_lock_enable_log, "%llu\n"); + +static int max42500_wdt_show_wd_rst2_cnt_enable_log(void *arg, u64 *val) +{ + long wdog_val; + int ret; + + ret = max42500_wdt_wdog_read(arg, &wdog_val, + MAX42500_WDOG_WD_RESET_TWO_COUNT_ENABLE); + if (ret) + return ret; + + *val = wdog_val; + return 0; +} + +static int max42500_wdt_store_wd_rst2_cnt_enable_log(void *arg, u64 val) +{ + return max42500_wdt_wdog_write(arg, val, + MAX42500_WDOG_WD_RESET_TWO_COUNT_ENABLE); +} +DEFINE_DEBUGFS_ATTRIBUTE(max42500_wdt_wd_rst2_cnt_enable_log, + max42500_wdt_show_wd_rst2_cnt_enable_log, + max42500_wdt_store_wd_rst2_cnt_enable_log, "%llu\n"); + +static void max42500_wdt_debugfs_remove(void *dir) +{ + debugfs_remove_recursive(dir); +} + +static void max42500_wdt_debugfs_init(struct max42500_wdt_state *st, + struct platform_device *pdev, + const struct device *watchdog) +{ + const char *debugfs_name; + struct dentry *dentry; + int ret; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + + debugfs_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "max42500-%s", + dev_name(watchdog)); + if (!debugfs_name) + return; + + dentry = debugfs_create_dir(debugfs_name, NULL); + if (IS_ERR(dentry)) + return; + + ret = devm_add_action_or_reset(&pdev->dev, max42500_wdt_debugfs_remove, + dentry); + if (ret) + return; + + /* Watchdog registers */ + debugfs_create_file_unsafe("wd_simp_mode_enable_log", 0644, dentry, st, + &max42500_wdt_wd_simp_mode_enable_log); + debugfs_create_file_unsafe("wd_open_win_log", 0644, dentry, st, + &max42500_wdt_wd_open_win_log); + debugfs_create_file_unsafe("wd_close_win_log", 0644, dentry, st, + &max42500_wdt_wd_close_win_log); + debugfs_create_file_unsafe("wd_lock_enable_log", 0644, dentry, st, + &max42500_wdt_wd_lock_enable_log); + debugfs_create_file_unsafe("wd_rst2_cnt_enable_log", 0644, dentry, st, + &max42500_wdt_wd_rst2_cnt_enable_log); +} + +static int max42500_wdt_start(struct watchdog_device *wdd) +{ + return max42500_wdt_wdog_write(watchdog_get_drvdata(wdd), 1, + MAX42500_WDOG_WD_ENABLE); +} + +static int max42500_wdt_stop(struct watchdog_device *wdd) +{ + return max42500_wdt_wdog_write(watchdog_get_drvdata(wdd), 0, + MAX42500_WDOG_WD_ENABLE); +} + +static int max42500_wdt_ping(struct watchdog_device *wdd) +{ + return max42500_wdt_wdog_write(watchdog_get_drvdata(wdd), 0, + MAX42500_WDOG_WD_KEY); +} + +static unsigned int max42500_wdt_status(struct watchdog_device *wdd) +{ + long wdog_val; + int ret; + + ret = max42500_wdt_wdog_read(watchdog_get_drvdata(wdd), &wdog_val, + MAX42500_WDOG_WD_STATUS); + if (ret) + return MAX42500_WDOG_WDSTAT_MASK; + + return wdog_val; +} + +static int max42500_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int val) +{ + return max42500_wdt_wdog_write(watchdog_get_drvdata(wdd), val, + MAX42500_WDOG_WD_CLOCK_DIVIDER); +} + +static int max42500_wdt_set_pretimeout(struct watchdog_device *wdd, + unsigned int val) +{ + return max42500_wdt_wdog_write(watchdog_get_drvdata(wdd), val, + MAX42500_WDOG_WD_FIRST_UPDATE_INTERVAL); +} + +static int max42500_wdt_restart(struct watchdog_device *wdd, + unsigned long val, void *info) +{ + return max42500_wdt_wdog_write(watchdog_get_drvdata(wdd), val, + MAX42500_WDOG_WD_RESET_HOLD_INTERVAL); +} + +static const struct watchdog_ops max42500_wdt_watchdog_ops = { + .owner = THIS_MODULE, + .start = max42500_wdt_start, + .stop = max42500_wdt_stop, + .ping = max42500_wdt_ping, + .status = max42500_wdt_status, + .set_timeout = max42500_wdt_set_timeout, + .set_pretimeout = max42500_wdt_set_pretimeout, + .restart = max42500_wdt_restart, +}; + +static const struct watchdog_info max42500_wdt_watchdog_info = { + .options = WDIOF_KEEPALIVEPING, + .identity = "max42500_wdt Watchdog", +}; + +static int max42500_wdt_probe(struct platform_device *pdev) +{ + struct device *parent_dev = pdev->dev.parent; + struct regmap *regmap = dev_get_drvdata(parent_dev); + struct device *dev = &pdev->dev; + struct watchdog_device *wdog_dev; + struct max42500_wdt_state *st; + long open_wdt, close_wdt; + int ret; + + st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->regmap = regmap; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + wdog_dev = &st->wwd; + wdog_dev->parent = &pdev->dev; + wdog_dev->info = &max42500_wdt_watchdog_info; + wdog_dev->ops = &max42500_wdt_watchdog_ops; + + ret = max42500_wdt_wdog_read(st, &open_wdt, + MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL); + if (ret) + return ret; + + wdog_dev->min_timeout = open_wdt; + + ret = max42500_wdt_wdog_read(st, &close_wdt, + MAX42500_WDOG_WD_OPEN_WINDOW_INTERVAL); + if (ret) + return ret; + + wdog_dev->max_timeout = open_wdt + close_wdt; + + platform_set_drvdata(pdev, st); + + max42500_wdt_debugfs_init(st, pdev, dev); + + watchdog_set_drvdata(wdog_dev, st); + + return devm_watchdog_register_device(dev, wdog_dev); +} + +static const struct platform_device_id max42500_wdt_id_table[] = { + { "max42500-wdt" }, + { } +}; +MODULE_DEVICE_TABLE(platform, max42500_wdt_id_table); + +static struct platform_driver max42500_wdt_driver = { + .driver = { + .name = "max42500-wdt", + }, + .probe = max42500_wdt_probe, + .id_table = max42500_wdt_id_table, +}; +module_platform_driver(max42500_wdt_driver); + +MODULE_AUTHOR("Kent Libetario "); +MODULE_DESCRIPTION("Watchdog driver for MAX42500"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max42500.h b/include/linux/mfd/max42500.h new file mode 100644 index 00000000000000..2ecf5185a90aa6 --- /dev/null +++ b/include/linux/mfd/max42500.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * MAX42500 - Industrial Power System Monitor + * + * Copyright 2025 Analog Devices Inc. + */ + +#ifndef __MFD_MAX42500_H +#define __MFD_MAX42500_H + +#include +#include + +/* Command registers */ +#define MAX42500_REG_ID 0x00 +#define MAX42500_REG_CONFIG1 0x01 +#define MAX42500_REG_CONFIG2 0x02 +#define MAX42500_REG_VMON 0x03 +#define MAX42500_REG_RSTMAP 0x04 +#define MAX42500_REG_STATOV 0x05 +#define MAX42500_REG_STATUV 0x06 +#define MAX42500_REG_STATOFF 0x07 +#define MAX42500_REG_VIN1 0x08 +#define MAX42500_REG_VIN2 0x09 +#define MAX42500_REG_VIN3 0x0A +#define MAX42500_REG_VIN4 0x0B +#define MAX42500_REG_VIN5 0x0C +#define MAX42500_REG_VINO6 0x0D +#define MAX42500_REG_VINU6 0x0E +#define MAX42500_REG_VINO7 0x0F +#define MAX42500_REG_VINU7 0x10 +#define MAX42500_REG_OVUV1 0x11 +#define MAX42500_REG_OVUV2 0x12 +#define MAX42500_REG_OVUV3 0x13 +#define MAX42500_REG_OVUV4 0x14 +#define MAX42500_REG_OVUV5 0x15 +#define MAX42500_REG_FPSTAT1 0x16 +#define MAX42500_REG_FPSCFG1 0x17 +#define MAX42500_REG_UTIME1 0x18 +#define MAX42500_REG_UTIME2 0x19 +#define MAX42500_REG_UTIME3 0x1A +#define MAX42500_REG_UTIME4 0x1B +#define MAX42500_REG_UTIME5 0x1C +#define MAX42500_REG_UTIME6 0x1D +#define MAX42500_REG_UTIME7 0x1E +#define MAX42500_REG_DTIME1 0x1F +#define MAX42500_REG_DTIME2 0x20 +#define MAX42500_REG_DTIME3 0x21 +#define MAX42500_REG_DTIME4 0x22 +#define MAX42500_REG_DTIME5 0x23 +#define MAX42500_REG_DTIME6 0x24 +#define MAX42500_REG_DTIME7 0x25 +#define MAX42500_REG_WDSTAT 0x26 +#define MAX42500_REG_WDCDIV 0x27 +#define MAX42500_REG_WDCFG1 0x28 +#define MAX42500_REG_WDCFG2 0x29 +#define MAX42500_REG_WDKEY 0x2A +#define MAX42500_REG_WDLOCK 0x2B +#define MAX42500_REG_RSTCTRL 0x2C +#define MAX42500_REG_CID 0x2D + +/* Device chip registers */ +#define MAX42500_CHIP_ID_MASK GENMASK(7, 0) +#define MAX42500_CHIP_ID(x) FIELD_GET(MAX42500_CHIP_ID_MASK, x) +#define MAX42500_CHIP_CID_MASK GENMASK(7, 0) +#define MAX42500_CHIP_CID(x) FIELD_GET(MAX42500_CHIP_CID_MASK, x) +#define MAX42500_CHIP_CFG2_MASK GENMASK(7, 0) +#define MAX42500_CHIP_CFG2(x) FIELD_GET(MAX42500_CHIP_PECE_MASK, x) +#define MAX42500_CHIP_PECE_MASK BIT(0) +#define MAX42500_CHIP_PECE(x) FIELD_GET(MAX42500_CHIP_PECE_MASK, x) +#define MAX42500_CHIP_MBST_MASK BIT(1) +#define MAX42500_CHIP_MBST(x) FIELD_GET(MAX42500_CHIP_MBST_MASK, x) +#define MAX42500_CHIP_RR_MASK BIT(2) +#define MAX42500_CHIP_RR(x) FIELD_GET(MAX42500_CHIP_RR_MASK, x) + +/* Voltage Monitor registers */ +#define MAX42500_VMON_VM1_MASK BIT(0) +#define MAX42500_VMON_VM1(x) FIELD_GET(MAX42500_VMON_VM1_MASK, x) +#define MAX42500_VMON_VM2_MASK BIT(1) +#define MAX42500_VMON_VM2(x) FIELD_GET(MAX42500_VMON_VM2_MASK, x) +#define MAX42500_VMON_VM3_MASK BIT(2) +#define MAX42500_VMON_VM3(x) FIELD_GET(MAX42500_VMON_VM3_MASK, x) +#define MAX42500_VMON_VM4_MASK BIT(3) +#define MAX42500_VMON_VM4(x) FIELD_GET(MAX42500_VMON_VM4_MASK, x) +#define MAX42500_VMON_VM5_MASK BIT(4) +#define MAX42500_VMON_VM5(x) FIELD_GET(MAX42500_VMON_VM5_MASK, x) +#define MAX42500_VMON_VM6_MASK BIT(5) +#define MAX42500_VMON_VM6(x) FIELD_GET(MAX42500_VMON_VM6_MASK, x) +#define MAX42500_VMON_VM7_MASK BIT(6) +#define MAX42500_VMON_VM7(x) FIELD_GET(MAX42500_VMON_VM7_MASK, x) +#define MAX42500_VMON_VMPD_MASK BIT(7) +#define MAX42500_VMON_VMPD(x) FIELD_GET(MAX42500_VMON_VMPD_MASK, x) +#define MAX42500_VMON_RST1_MASK BIT(0) +#define MAX42500_VMON_RST1(x) FIELD_GET(MAX42500_VMON_RST1_MASK, x) +#define MAX42500_VMON_RST2_MASK BIT(1) +#define MAX42500_VMON_RST2(x) FIELD_GET(MAX42500_VMON_RST2_MASK, x) +#define MAX42500_VMON_RST3_MASK BIT(2) +#define MAX42500_VMON_RST3(x) FIELD_GET(MAX42500_VMON_RST3_MASK, x) +#define MAX42500_VMON_RST4_MASK BIT(3) +#define MAX42500_VMON_RST4(x) FIELD_GET(MAX42500_VMON_RST4_MASK, x) +#define MAX42500_VMON_RST5_MASK BIT(4) +#define MAX42500_VMON_RST5(x) FIELD_GET(MAX42500_VMON_RST5_MASK, x) +#define MAX42500_VMON_RST6_MASK BIT(5) +#define MAX42500_VMON_RST6(x) FIELD_GET(MAX42500_VMON_RST6_MASK, x) +#define MAX42500_VMON_RST7_MASK BIT(6) +#define MAX42500_VMON_RST7(x) FIELD_GET(MAX42500_VMON_RST7_MASK, x) +#define MAX42500_VMON_PARM_MASK BIT(7) +#define MAX42500_VMON_PARM(x) FIELD_GET(MAX42500_VMON_PARM_MASK, x) +#define MAX42500_VMON_1VIN5_MASK GENMASK(7, 0) +#define MAX42500_VMON_1VIN5(x) FIELD_GET(MAX42500_VMON_1VIN5_MASK, x) +#define MAX42500_VMON_1VINU5_MASK GENMASK(3, 0) +#define MAX42500_VMON_1VINU5(x) FIELD_GET(MAX42500_VMON_1VINU5_MASK, x) +#define MAX42500_VMON_6VINU7_MASK GENMASK(7, 0) +#define MAX42500_VMON_6VINU7(x) FIELD_GET(MAX42500_VMON_6VINU7_MASK, x) +#define MAX42500_VMON_1VINO5_MASK GENMASK(7, 4) +#define MAX42500_VMON_1VINO5(x) FIELD_GET(MAX42500_VMON_1VINO5_MASK, x) +#define MAX42500_VMON_6VINO7_MASK GENMASK(7, 0) +#define MAX42500_VMON_6VINO7(x) FIELD_GET(MAX42500_VMON_6VINO7_MASK, x) +#define MAX42500_VMON_STATOFF_MASK GENMASK(7, 0) +#define MAX42500_VMON_STATOFF(x) FIELD_GET(MAX42500_VMON_STATOFF_MASK, x) +#define MAX42500_VMON_STATUV_MASK GENMASK(7, 0) +#define MAX42500_VMON_STATUV(x) FIELD_GET(MAX42500_VMON_STATUV_MASK, x) +#define MAX42500_VMON_STATOV_MASK GENMASK(7, 0) +#define MAX42500_VMON_STATOV(x) FIELD_GET(MAX42500_VMON_STATOV_MASK, x) + +/* Sequence recorder registers */ +#define MAX42500_FPSR_FPSTAT1_MASK GENMASK(7, 0) +#define MAX42500_FPSR_FPSTAT1(x) FIELD_GET(MAX42500_FPSR_FPSTAT1_MASK, x) +#define MAX42500_FPSR_FDIV_MASK GENMASK(2, 0) +#define MAX42500_FPSR_FDIV(x) FIELD_GET(MAX42500_FPSR_FDIV_MASK, x) +#define MAX42500_FPSR_FPSEN1_MASK BIT(3) +#define MAX42500_FPSR_FPSEN1(x) FIELD_GET(MAX42500_FPSR_FPSEN1_MASK, x) +#define MAX42500_FPSR_DVALM_MASK BIT(4) +#define MAX42500_FPSR_DVALM(x) FIELD_GET(MAX42500_FPSR_DVALM_MASK, x) +#define MAX42500_FPSR_UVALM_MASK BIT(5) +#define MAX42500_FPSR_UVALM(x) FIELD_GET(MAX42500_FPSR_UVALM_MASK, x) +#define MAX42500_FPSR_DVAL_MASK BIT(6) +#define MAX42500_FPSR_DVAL(x) FIELD_GET(MAX42500_FPSR_DVAL_MASK, x) +#define MAX42500_FPSR_UVAL_MASK BIT(7) +#define MAX42500_FPSR_UVAL(x) FIELD_GET(MAX42500_FPSR_UVAL_MASK, x) +#define MAX42500_FPSR_1UTIM7_MASK GENMASK(7, 0) +#define MAX42500_FPSR_1UTIM7(x) FIELD_GET(MAX42500_FPSR_1UTIM7_MASK, x) +#define MAX42500_FPSR_1DTIM7_MASK GENMASK(7, 0) +#define MAX42500_FPSR_1DTIM7(x) FIELD_GET(MAX42500_FPSR_1DTIM7_MASK, x) + +/* Windowed watchdog register*/ +#define MAX42500_WDOG_WDSTAT_MASK GENMASK(7, 0) +#define MAX42500_WDOG_WDSTAT(x) FIELD_GET(MAX42500_WDOG_WDSTAT_MASK, x) +#define MAX42500_WDOG_WDIV_MASK GENMASK(5, 0) +#define MAX42500_WDOG_WDIV(x) FIELD_GET(MAX42500_WDOG_WDIV_MASK, x) +#define MAX42500_WDOG_SWW_MASK BIT(6) +#define MAX42500_WDOG_SWW(x) FIELD_GET(MAX42500_WDOG_SWW_MASK, x) +#define MAX42500_WDOG_WDOPEN_MASK GENMASK(3, 0) +#define MAX42500_WDOG_WDOPEN(x) FIELD_GET(MAX42500_WDOG_WDOPEN_MASK, x) +#define MAX42500_WDOG_WDCLOSE_MASK GENMASK(7, 4) +#define MAX42500_WDOG_WDCLOSE(x) FIELD_GET(MAX42500_WDOG_WDCLOSE_MASK, x) +#define MAX42500_WDOG_1UD_MASK GENMASK(2, 0) +#define MAX42500_WDOG_1UD(x) FIELD_GET(MAX42500_WDOG_1UD_MASK, x) +#define MAX42500_WDOG_WDEN_MASK BIT(3) +#define MAX42500_WDOG_WDEN(x) FIELD_GET(MAX42500_WDOG_WDEN_MASK, x) +#define MAX42500_WDOG_WDKEY_MASK GENMASK(7, 0) +#define MAX42500_WDOG_WDKEY(x) FIELD_GET(MAX42500_WDOG_WDKEY_MASK, x) +#define MAX42500_WDOG_WDLOCK_MASK BIT(0) +#define MAX42500_WDOG_WDLOCK(x) FIELD_GET(MAX42500_WDOG_WDLOCK_MASK, x) +#define MAX42500_WDOG_RHLD_MASK GENMASK(1, 0) +#define MAX42500_WDOG_RHLD(x) FIELD_GET(MAX42500_WDOG_RHLD_MASK, x) +#define MAX42500_WDOG_MR1_MASK BIT(2) +#define MAX42500_WDOG_MR1(x) FIELD_GET(MAX42500_WDOG_MR1_MASK, x) + +#endif /* __MFD_MAX42500_H */