From 49f661ba99324a3f7eef0befbdaa4f22dee02b97 Mon Sep 17 00:00:00 2001 From: Naresh Solanki Date: Thu, 27 Apr 2023 13:30:45 +0200 Subject: mfd: max5970: Rename driver and remove wildcard The previous version of this driver included wildcards in file names and descriptions. This patch renames the driver to only support MAX5970 and MAX5978, which are the only chips that the driver actually supports. Signed-off-by: Naresh Solanki Link: https://lore.kernel.org/r/20230427113046.3971425-1-Naresh.Solanki@9elements.com Signed-off-by: Lee Jones --- include/linux/mfd/max5970.h | 96 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/max597x.h | 96 --------------------------------------------- 2 files changed, 96 insertions(+), 96 deletions(-) create mode 100644 include/linux/mfd/max5970.h delete mode 100644 include/linux/mfd/max597x.h (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/max5970.h b/include/linux/mfd/max5970.h new file mode 100644 index 000000000000..762a7d40c843 --- /dev/null +++ b/include/linux/mfd/max5970.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Device driver for regulators in MAX5970 and MAX5978 IC + * + * Copyright (c) 2022 9elements GmbH + * + * Author: Patrick Rudolph + */ + +#ifndef _MFD_MAX5970_H +#define _MFD_MAX5970_H + +#include + +#define MAX5970_NUM_SWITCHES 2 +#define MAX5978_NUM_SWITCHES 1 +#define MAX5970_NUM_LEDS 4 + +struct max5970_data { + int num_switches; + u32 irng[MAX5970_NUM_SWITCHES]; + u32 mon_rng[MAX5970_NUM_SWITCHES]; + u32 shunt_micro_ohms[MAX5970_NUM_SWITCHES]; +}; + +enum max5970_chip_type { + TYPE_MAX5978 = 1, + TYPE_MAX5970, +}; + +#define MAX5970_REG_CURRENT_L(ch) (0x01 + (ch) * 4) +#define MAX5970_REG_CURRENT_H(ch) (0x00 + (ch) * 4) +#define MAX5970_REG_VOLTAGE_L(ch) (0x03 + (ch) * 4) +#define MAX5970_REG_VOLTAGE_H(ch) (0x02 + (ch) * 4) +#define MAX5970_REG_MON_RANGE 0x18 +#define MAX5970_MON_MASK 0x3 +#define MAX5970_MON(reg, ch) (((reg) >> ((ch) * 2)) & MAX5970_MON_MASK) +#define MAX5970_MON_MAX_RANGE_UV 16000000 + +#define MAX5970_REG_CH_UV_WARN_H(ch) (0x1A + (ch) * 10) +#define MAX5970_REG_CH_UV_WARN_L(ch) (0x1B + (ch) * 10) +#define MAX5970_REG_CH_UV_CRIT_H(ch) (0x1C + (ch) * 10) +#define MAX5970_REG_CH_UV_CRIT_L(ch) (0x1D + (ch) * 10) +#define MAX5970_REG_CH_OV_WARN_H(ch) (0x1E + (ch) * 10) +#define MAX5970_REG_CH_OV_WARN_L(ch) (0x1F + (ch) * 10) +#define MAX5970_REG_CH_OV_CRIT_H(ch) (0x20 + (ch) * 10) +#define MAX5970_REG_CH_OV_CRIT_L(ch) (0x21 + (ch) * 10) + +#define MAX5970_VAL2REG_H(x) (((x) >> 2) & 0xFF) +#define MAX5970_VAL2REG_L(x) ((x) & 0x3) + +#define MAX5970_REG_DAC_FAST(ch) (0x2E + (ch)) + +#define MAX5970_FAST2SLOW_RATIO 200 + +#define MAX5970_REG_STATUS0 0x31 +#define MAX5970_CB_IFAULTF(ch) (1 << (ch)) +#define MAX5970_CB_IFAULTS(ch) (1 << ((ch) + 4)) + +#define MAX5970_REG_STATUS1 0x32 +#define STATUS1_PROT_MASK 0x3 +#define STATUS1_PROT(reg) \ + (((reg) >> 6) & STATUS1_PROT_MASK) +#define STATUS1_PROT_SHUTDOWN 0 +#define STATUS1_PROT_CLEAR_PG 1 +#define STATUS1_PROT_ALERT_ONLY 2 + +#define MAX5970_REG_STATUS2 0x33 +#define MAX5970_IRNG_MASK 0x3 +#define MAX5970_IRNG(reg, ch) \ + (((reg) >> ((ch) * 2)) & MAX5970_IRNG_MASK) + +#define MAX5970_REG_STATUS3 0x34 +#define MAX5970_STATUS3_ALERT BIT(4) +#define MAX5970_STATUS3_PG(ch) BIT(ch) + +#define MAX5970_REG_FAULT0 0x35 +#define UV_STATUS_WARN(ch) (1 << (ch)) +#define UV_STATUS_CRIT(ch) (1 << ((ch) + 4)) + +#define MAX5970_REG_FAULT1 0x36 +#define OV_STATUS_WARN(ch) (1 << (ch)) +#define OV_STATUS_CRIT(ch) (1 << ((ch) + 4)) + +#define MAX5970_REG_FAULT2 0x37 +#define OC_STATUS_WARN(ch) (1 << (ch)) + +#define MAX5970_REG_CHXEN 0x3b +#define CHXEN(ch) (3 << ((ch) * 2)) + +#define MAX5970_REG_LED_FLASH 0x43 + +#define MAX_REGISTERS 0x49 +#define ADC_MASK 0x3FF + +#endif /* _MFD_MAX5970_H */ diff --git a/include/linux/mfd/max597x.h b/include/linux/mfd/max597x.h deleted file mode 100644 index a850b2e02e6a..000000000000 --- a/include/linux/mfd/max597x.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Device driver for regulators in MAX5970 and MAX5978 IC - * - * Copyright (c) 2022 9elements GmbH - * - * Author: Patrick Rudolph - */ - -#ifndef _MFD_MAX597X_H -#define _MFD_MAX597X_H - -#include - -#define MAX5970_NUM_SWITCHES 2 -#define MAX5978_NUM_SWITCHES 1 -#define MAX597X_NUM_LEDS 4 - -struct max597x_data { - int num_switches; - u32 irng[MAX5970_NUM_SWITCHES]; - u32 mon_rng[MAX5970_NUM_SWITCHES]; - u32 shunt_micro_ohms[MAX5970_NUM_SWITCHES]; -}; - -enum max597x_chip_type { - MAX597x_TYPE_MAX5978 = 1, - MAX597x_TYPE_MAX5970, -}; - -#define MAX5970_REG_CURRENT_L(ch) (0x01 + (ch) * 4) -#define MAX5970_REG_CURRENT_H(ch) (0x00 + (ch) * 4) -#define MAX5970_REG_VOLTAGE_L(ch) (0x03 + (ch) * 4) -#define MAX5970_REG_VOLTAGE_H(ch) (0x02 + (ch) * 4) -#define MAX5970_REG_MON_RANGE 0x18 -#define MAX5970_MON_MASK 0x3 -#define MAX5970_MON(reg, ch) (((reg) >> ((ch) * 2)) & MAX5970_MON_MASK) -#define MAX5970_MON_MAX_RANGE_UV 16000000 - -#define MAX5970_REG_CH_UV_WARN_H(ch) (0x1A + (ch) * 10) -#define MAX5970_REG_CH_UV_WARN_L(ch) (0x1B + (ch) * 10) -#define MAX5970_REG_CH_UV_CRIT_H(ch) (0x1C + (ch) * 10) -#define MAX5970_REG_CH_UV_CRIT_L(ch) (0x1D + (ch) * 10) -#define MAX5970_REG_CH_OV_WARN_H(ch) (0x1E + (ch) * 10) -#define MAX5970_REG_CH_OV_WARN_L(ch) (0x1F + (ch) * 10) -#define MAX5970_REG_CH_OV_CRIT_H(ch) (0x20 + (ch) * 10) -#define MAX5970_REG_CH_OV_CRIT_L(ch) (0x21 + (ch) * 10) - -#define MAX5970_VAL2REG_H(x) (((x) >> 2) & 0xFF) -#define MAX5970_VAL2REG_L(x) ((x) & 0x3) - -#define MAX5970_REG_DAC_FAST(ch) (0x2E + (ch)) - -#define MAX5970_FAST2SLOW_RATIO 200 - -#define MAX5970_REG_STATUS0 0x31 -#define MAX5970_CB_IFAULTF(ch) (1 << (ch)) -#define MAX5970_CB_IFAULTS(ch) (1 << ((ch) + 4)) - -#define MAX5970_REG_STATUS1 0x32 -#define STATUS1_PROT_MASK 0x3 -#define STATUS1_PROT(reg) \ - (((reg) >> 6) & STATUS1_PROT_MASK) -#define STATUS1_PROT_SHUTDOWN 0 -#define STATUS1_PROT_CLEAR_PG 1 -#define STATUS1_PROT_ALERT_ONLY 2 - -#define MAX5970_REG_STATUS2 0x33 -#define MAX5970_IRNG_MASK 0x3 -#define MAX5970_IRNG(reg, ch) \ - (((reg) >> ((ch) * 2)) & MAX5970_IRNG_MASK) - -#define MAX5970_REG_STATUS3 0x34 -#define MAX5970_STATUS3_ALERT BIT(4) -#define MAX5970_STATUS3_PG(ch) BIT(ch) - -#define MAX5970_REG_FAULT0 0x35 -#define UV_STATUS_WARN(ch) (1 << (ch)) -#define UV_STATUS_CRIT(ch) (1 << ((ch) + 4)) - -#define MAX5970_REG_FAULT1 0x36 -#define OV_STATUS_WARN(ch) (1 << (ch)) -#define OV_STATUS_CRIT(ch) (1 << ((ch) + 4)) - -#define MAX5970_REG_FAULT2 0x37 -#define OC_STATUS_WARN(ch) (1 << (ch)) - -#define MAX5970_REG_CHXEN 0x3b -#define CHXEN(ch) (3 << ((ch) * 2)) - -#define MAX5970_REG_LED_FLASH 0x43 - -#define MAX_REGISTERS 0x49 -#define ADC_MASK 0x3FF - -#endif /* _MFD_MAX597X_H */ -- cgit v1.2.3-59-g8ed1b From c4747d7ce3948766cdae2404b4a796ba4cda9d7d Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Mon, 15 May 2023 22:57:11 +0200 Subject: mfd: rt5033: Fix chip revision readout After reading the data from the DEVICE_ID register, mask 0x0f needs to be applied to extract the revision of the chip [1]. The other part of the DEVICE_ID register, mask 0xf0, is a vendor identification code. That's how it is set up at similar products of Richtek, e.g. RT9455 [2] page 21 top. [1] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/mfd/rt5033_core.c#L484 [2] https://www.richtek.com/assets/product_file/RT9455/DS9455-00.pdf Signed-off-by: Jakob Hauser Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/9a98521ffdf76851d5d344afa6ce65f692ecc024.1684182964.git.jahau@rocketmail.com --- drivers/mfd/rt5033.c | 5 +++-- include/linux/mfd/rt5033-private.h | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/rt5033.c b/drivers/mfd/rt5033.c index 8029d444b794..3eee4242ee02 100644 --- a/drivers/mfd/rt5033.c +++ b/drivers/mfd/rt5033.c @@ -55,7 +55,7 @@ static const struct regmap_config rt5033_regmap_config = { static int rt5033_i2c_probe(struct i2c_client *i2c) { struct rt5033_dev *rt5033; - unsigned int dev_id; + unsigned int dev_id, chip_rev; int ret; rt5033 = devm_kzalloc(&i2c->dev, sizeof(*rt5033), GFP_KERNEL); @@ -78,7 +78,8 @@ static int rt5033_i2c_probe(struct i2c_client *i2c) dev_err(&i2c->dev, "Device not found\n"); return -ENODEV; } - dev_info(&i2c->dev, "Device found Device ID: %04x\n", dev_id); + chip_rev = dev_id & RT5033_CHIP_REV_MASK; + dev_info(&i2c->dev, "Device found (rev. %d)\n", chip_rev); ret = regmap_add_irq_chip(rt5033->regmap, rt5033->irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, diff --git a/include/linux/mfd/rt5033-private.h b/include/linux/mfd/rt5033-private.h index 6bb432f6a96c..b035a67cec73 100644 --- a/include/linux/mfd/rt5033-private.h +++ b/include/linux/mfd/rt5033-private.h @@ -71,6 +71,10 @@ enum rt5033_reg { /* RT5033 CHGCTRL2 register */ #define RT5033_CHGCTRL2_CV_MASK 0xfc +/* RT5033 DEVICE_ID register */ +#define RT5033_VENDOR_ID_MASK 0xf0 +#define RT5033_CHIP_REV_MASK 0x0f + /* RT5033 CHGCTRL3 register */ #define RT5033_CHGCTRL3_CFO_EN_MASK 0x40 #define RT5033_CHGCTRL3_TIMER_MASK 0x38 -- cgit v1.2.3-59-g8ed1b From 4f7a2a08c735003893da177071bf5e5f6f14e39c Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Mon, 15 May 2023 22:57:12 +0200 Subject: mfd: rt5033: Fix STAT_MASK, HZ_MASK and AICR defines The charger state mask RT5033_CHG_STAT_MASK should be 0x30 [1][2]. The high impedance mask RT5033_RT_HZ_MASK is actually value 0x02 [3] and is assosiated to the RT5033 CHGCTRL1 register [4]. Accordingly also change RT5033_CHARGER_HZ_ENABLE to 0x02 to avoid the need of a bit shift upon application. For input current limiting AICR mode, the define for the 1000 mA step was missing [5]. Additionally add the define for DISABLE option. Concerning the mask, remove RT5033_AICR_MODE_MASK because there is already RT5033_CHGCTRL1_IAICR_MASK further up. They are redundant and the upper one makes more sense to have the masks of a register colleted there as an overview. [1] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L669-L682 [2] https://github.com/torvalds/linux/blob/v6.0/include/linux/mfd/rt5033-private.h#L59-L62 [3] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/include/linux/battery/charger/rt5033_charger.h#L44 [4] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L223 [5] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/battery/rt5033_charger.c#L278 Signed-off-by: Jakob Hauser Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/2f17beec3d6c59b41d7e2451d177dc8aaeb7efe2.1684182964.git.jahau@rocketmail.com --- include/linux/mfd/rt5033-private.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/rt5033-private.h b/include/linux/mfd/rt5033-private.h index b035a67cec73..b6773ebf4e6b 100644 --- a/include/linux/mfd/rt5033-private.h +++ b/include/linux/mfd/rt5033-private.h @@ -55,7 +55,7 @@ enum rt5033_reg { }; /* RT5033 Charger state register */ -#define RT5033_CHG_STAT_MASK 0x20 +#define RT5033_CHG_STAT_MASK 0x30 #define RT5033_CHG_STAT_DISCHARGING 0x00 #define RT5033_CHG_STAT_FULL 0x10 #define RT5033_CHG_STAT_CHARGING 0x20 @@ -67,6 +67,7 @@ enum rt5033_reg { /* RT5033 CHGCTRL1 register */ #define RT5033_CHGCTRL1_IAICR_MASK 0xe0 #define RT5033_CHGCTRL1_MODE_MASK 0x01 +#define RT5033_CHGCTRL1_HZ_MASK 0x02 /* RT5033 CHGCTRL2 register */ #define RT5033_CHGCTRL2_CV_MASK 0xfc @@ -92,7 +93,6 @@ enum rt5033_reg { /* RT5033 RT CTRL1 register */ #define RT5033_RT_CTRL1_UUG_MASK 0x02 -#define RT5033_RT_HZ_MASK 0x01 /* RT5033 control register */ #define RT5033_CTRL_FCCM_BUCK_MASK BIT(0) @@ -119,13 +119,14 @@ enum rt5033_reg { * register), AICR mode limits the input current. For example, the AIRC 100 * mode limits the input current to 100 mA. */ +#define RT5033_AICR_DISABLE 0x00 #define RT5033_AICR_100_MODE 0x20 #define RT5033_AICR_500_MODE 0x40 #define RT5033_AICR_700_MODE 0x60 #define RT5033_AICR_900_MODE 0x80 +#define RT5033_AICR_1000_MODE 0xa0 #define RT5033_AICR_1500_MODE 0xc0 #define RT5033_AICR_2000_MODE 0xe0 -#define RT5033_AICR_MODE_MASK 0xe0 /* RT5033 use internal timer need to set time */ #define RT5033_FAST_CHARGE_TIMER4 0x00 @@ -195,7 +196,7 @@ enum rt5033_reg { /* RT5033 charger high impedance mode */ #define RT5033_CHARGER_HZ_DISABLE 0x00 -#define RT5033_CHARGER_HZ_ENABLE 0x01 +#define RT5033_CHARGER_HZ_ENABLE 0x02 /* RT5033 regulator BUCK output voltage uV */ #define RT5033_REGULATOR_BUCK_VOLTAGE_MIN 1000000U -- cgit v1.2.3-59-g8ed1b From 3d897612107a3d7b4d70ca4296a6fd80bfd1b04f Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Mon, 15 May 2023 22:57:13 +0200 Subject: mfd: rt5033: Apply preparatory changes before adding rt5033-charger driver Order the register blocks to have the masks in descending manner. Add new defines for constant voltage shift (RT5033_CHGCTRL2_CV_SHIFT), MIVR mask (RT5033_CHGCTRL4_MIVR_MASK), pre-charge current shift (RT5033_CHGCTRL4_IPREC_SHIFT), internal timer disable (RT5033_INT_TIMER_DISABLE), termination disable (RT5033_TE_DISABLE), CFO disable (RT5033_CFO_DISABLE), UUG disable (RT5033_CHARGER_UUG_DISABLE). The fast charge timer type needs to be written on mask 0x38 (RT5033_CHGCTRL3_TIMER_MASK). To avoid a bit shift on application, change the values of the timer types to fit the mask. Added the timout duration as a comment. And the timer between TIMER8 and TIMER12 is most likely TIMER10, see e.g. RT5036 [1] page 28 bottom. Add value options for MIVR (Minimum Input Voltage Regulation). Move RT5033_TE_ENABLE_MASK to the block "RT5033 CHGCTRL1 register", in order to have the masks of the register collected there. To fit the naming scheme, rename it to RT5033_CHGCTRL1_TE_EN_MASK. Move RT5033_CHG_MAX_CURRENT to the block "RT5033 charger fast-charge current". Add new defines RT5033_CV_MAX_VOLTAGE and RT5033_CHG_MAX_PRE_CURRENT to the blocks "RT5033 charger constant charge voltage" and "RT5033 charger pre-charge current limits". In include/linux/mfd/rt5033.h, turn power_supply "psy" into a pointer in order to use it in devm_power_supply_register(). [1] https://media.digikey.com/pdf/Data%20Sheets/Richtek%20PDF/RT5036%20%20Preliminary.pdf Signed-off-by: Jakob Hauser Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/31c750ae13a1c1896b51d8f0a0d9869f8b85624f.1684182964.git.jahau@rocketmail.com --- include/linux/mfd/rt5033-private.h | 53 +++++++++++++++++++++++++------------- include/linux/mfd/rt5033.h | 2 +- 2 files changed, 36 insertions(+), 19 deletions(-) (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/rt5033-private.h b/include/linux/mfd/rt5033-private.h index b6773ebf4e6b..0221f806d139 100644 --- a/include/linux/mfd/rt5033-private.h +++ b/include/linux/mfd/rt5033-private.h @@ -55,22 +55,24 @@ enum rt5033_reg { }; /* RT5033 Charger state register */ +#define RT5033_CHG_STAT_TYPE_MASK 0x60 +#define RT5033_CHG_STAT_TYPE_PRE 0x20 +#define RT5033_CHG_STAT_TYPE_FAST 0x60 #define RT5033_CHG_STAT_MASK 0x30 #define RT5033_CHG_STAT_DISCHARGING 0x00 #define RT5033_CHG_STAT_FULL 0x10 #define RT5033_CHG_STAT_CHARGING 0x20 #define RT5033_CHG_STAT_NOT_CHARGING 0x30 -#define RT5033_CHG_STAT_TYPE_MASK 0x60 -#define RT5033_CHG_STAT_TYPE_PRE 0x20 -#define RT5033_CHG_STAT_TYPE_FAST 0x60 /* RT5033 CHGCTRL1 register */ #define RT5033_CHGCTRL1_IAICR_MASK 0xe0 -#define RT5033_CHGCTRL1_MODE_MASK 0x01 +#define RT5033_CHGCTRL1_TE_EN_MASK 0x08 #define RT5033_CHGCTRL1_HZ_MASK 0x02 +#define RT5033_CHGCTRL1_MODE_MASK 0x01 /* RT5033 CHGCTRL2 register */ #define RT5033_CHGCTRL2_CV_MASK 0xfc +#define RT5033_CHGCTRL2_CV_SHIFT 0x02 /* RT5033 DEVICE_ID register */ #define RT5033_VENDOR_ID_MASK 0xf0 @@ -82,14 +84,15 @@ enum rt5033_reg { #define RT5033_CHGCTRL3_TIMER_EN_MASK 0x01 /* RT5033 CHGCTRL4 register */ -#define RT5033_CHGCTRL4_EOC_MASK 0x07 +#define RT5033_CHGCTRL4_MIVR_MASK 0xe0 #define RT5033_CHGCTRL4_IPREC_MASK 0x18 +#define RT5033_CHGCTRL4_IPREC_SHIFT 0x03 +#define RT5033_CHGCTRL4_EOC_MASK 0x07 /* RT5033 CHGCTRL5 register */ -#define RT5033_CHGCTRL5_VPREC_MASK 0x0f #define RT5033_CHGCTRL5_ICHG_MASK 0xf0 #define RT5033_CHGCTRL5_ICHG_SHIFT 0x04 -#define RT5033_CHG_MAX_CURRENT 0x0d +#define RT5033_CHGCTRL5_VPREC_MASK 0x0f /* RT5033 RT CTRL1 register */ #define RT5033_RT_CTRL1_UUG_MASK 0x02 @@ -128,20 +131,28 @@ enum rt5033_reg { #define RT5033_AICR_1500_MODE 0xc0 #define RT5033_AICR_2000_MODE 0xe0 -/* RT5033 use internal timer need to set time */ -#define RT5033_FAST_CHARGE_TIMER4 0x00 -#define RT5033_FAST_CHARGE_TIMER6 0x01 -#define RT5033_FAST_CHARGE_TIMER8 0x02 -#define RT5033_FAST_CHARGE_TIMER9 0x03 -#define RT5033_FAST_CHARGE_TIMER12 0x04 -#define RT5033_FAST_CHARGE_TIMER14 0x05 -#define RT5033_FAST_CHARGE_TIMER16 0x06 +/* RT5033 charger minimum input voltage regulation */ +#define RT5033_CHARGER_MIVR_DISABLE 0x00 +#define RT5033_CHARGER_MIVR_4200MV 0x20 +#define RT5033_CHARGER_MIVR_4300MV 0x40 +#define RT5033_CHARGER_MIVR_4400MV 0x60 +#define RT5033_CHARGER_MIVR_4500MV 0x80 +#define RT5033_CHARGER_MIVR_4600MV 0xa0 +#define RT5033_CHARGER_MIVR_4700MV 0xc0 +#define RT5033_CHARGER_MIVR_4800MV 0xe0 +/* RT5033 use internal timer need to set time */ +#define RT5033_FAST_CHARGE_TIMER4 0x00 /* 4 hrs */ +#define RT5033_FAST_CHARGE_TIMER6 0x08 /* 6 hrs */ +#define RT5033_FAST_CHARGE_TIMER8 0x10 /* 8 hrs */ +#define RT5033_FAST_CHARGE_TIMER10 0x18 /* 10 hrs */ +#define RT5033_FAST_CHARGE_TIMER12 0x20 /* 12 hrs */ +#define RT5033_FAST_CHARGE_TIMER14 0x28 /* 14 hrs */ +#define RT5033_FAST_CHARGE_TIMER16 0x30 /* 16 hrs */ + +#define RT5033_INT_TIMER_DISABLE 0x00 #define RT5033_INT_TIMER_ENABLE 0x01 -/* RT5033 charger termination enable mask */ -#define RT5033_TE_ENABLE_MASK 0x08 - /* * RT5033 charger opa mode. RT5033 has two opa modes for OTG: charger mode * and boost mode. @@ -150,25 +161,30 @@ enum rt5033_reg { #define RT5033_BOOST_MODE 0x01 /* RT5033 charger termination enable */ +#define RT5033_TE_DISABLE 0x00 #define RT5033_TE_ENABLE 0x08 /* RT5033 charger CFO enable */ +#define RT5033_CFO_DISABLE 0x00 #define RT5033_CFO_ENABLE 0x40 /* RT5033 charger constant charge voltage (as in CHGCTRL2 register), uV */ #define RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MIN 3650000U #define RT5033_CHARGER_CONST_VOLTAGE_STEP_NUM 25000U #define RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MAX 4400000U +#define RT5033_CV_MAX_VOLTAGE 0x1e /* RT5033 charger pre-charge current limits (as in CHGCTRL4 register), uA */ #define RT5033_CHARGER_PRE_CURRENT_LIMIT_MIN 350000U #define RT5033_CHARGER_PRE_CURRENT_STEP_NUM 100000U #define RT5033_CHARGER_PRE_CURRENT_LIMIT_MAX 650000U +#define RT5033_CHG_MAX_PRE_CURRENT 0x03 /* RT5033 charger fast-charge current (as in CHGCTRL5 register), uA */ #define RT5033_CHARGER_FAST_CURRENT_MIN 700000U #define RT5033_CHARGER_FAST_CURRENT_STEP_NUM 100000U #define RT5033_CHARGER_FAST_CURRENT_MAX 2000000U +#define RT5033_CHG_MAX_CURRENT 0x0d /* * RT5033 charger const-charge end of charger current ( @@ -192,6 +208,7 @@ enum rt5033_reg { * RT5033 charger UUG. It enables MOS auto control by H/W charger * circuit. */ +#define RT5033_CHARGER_UUG_DISABLE 0x00 #define RT5033_CHARGER_UUG_ENABLE 0x02 /* RT5033 charger high impedance mode */ diff --git a/include/linux/mfd/rt5033.h b/include/linux/mfd/rt5033.h index 8f306ac15a27..e99e2ab0c1c1 100644 --- a/include/linux/mfd/rt5033.h +++ b/include/linux/mfd/rt5033.h @@ -51,7 +51,7 @@ struct rt5033_charger_data { struct rt5033_charger { struct device *dev; struct rt5033_dev *rt5033; - struct power_supply psy; + struct power_supply *psy; struct rt5033_charger_data *chg; }; -- cgit v1.2.3-59-g8ed1b From b7653853d5b12637c16b5ca5c8bab39b486bfe99 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Mon, 15 May 2023 22:57:14 +0200 Subject: power: supply: rt5033_charger: Add RT5033 charger device driver This patch adds device driver of Richtek RT5033 PMIC. The driver supports switching charger. rt5033 charger provides three charging modes. The charging modes are pre-charge mode, fast charge mode and constant voltage mode. They vary in charge rate, the charge parameters can be controlled by i2c interface. Tested-by: Raymond Hackley Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Acked-by: Sebastian Reichel Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/9556d4ebb30fd321e37aa0eb343554122e4720c9.1684182964.git.jahau@rocketmail.com --- drivers/power/supply/Kconfig | 8 + drivers/power/supply/Makefile | 1 + drivers/power/supply/rt5033_charger.c | 472 ++++++++++++++++++++++++++++++++++ include/linux/mfd/rt5033.h | 16 -- 4 files changed, 481 insertions(+), 16 deletions(-) create mode 100644 drivers/power/supply/rt5033_charger.c (limited to 'include/linux/mfd') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index c78be9f322e6..ea11797670ca 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -766,6 +766,14 @@ config BATTERY_RT5033 The fuelgauge calculates and determines the battery state of charge according to battery open circuit voltage. +config CHARGER_RT5033 + tristate "RT5033 battery charger support" + depends on MFD_RT5033 + help + This adds support for battery charger in Richtek RT5033 PMIC. + The device supports pre-charge mode, fast charge mode and + constant voltage mode. + config CHARGER_RT9455 tristate "Richtek RT9455 battery charger driver" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 4adbfba02d05..dfc624bbcf1d 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o +obj-$(CONFIG_CHARGER_RT5033) += rt5033_charger.o obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o obj-$(CONFIG_CHARGER_RT9467) += rt9467-charger.o obj-$(CONFIG_CHARGER_RT9471) += rt9471.o diff --git a/drivers/power/supply/rt5033_charger.c b/drivers/power/supply/rt5033_charger.c new file mode 100644 index 000000000000..5218dfbf5e1b --- /dev/null +++ b/drivers/power/supply/rt5033_charger.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Battery charger driver for RT5033 + * + * Copyright (C) 2014 Samsung Electronics, Co., Ltd. + * Author: Beomho Seo + */ + +#include +#include +#include +#include +#include +#include + +struct rt5033_charger_data { + unsigned int pre_uamp; + unsigned int pre_uvolt; + unsigned int const_uvolt; + unsigned int eoc_uamp; + unsigned int fast_uamp; +}; + +struct rt5033_charger { + struct device *dev; + struct regmap *regmap; + struct power_supply *psy; + struct rt5033_charger_data *chg; +}; + +static int rt5033_get_charger_state(struct rt5033_charger *charger) +{ + struct regmap *regmap = charger->regmap; + unsigned int reg_data; + int state; + + if (!regmap) + return POWER_SUPPLY_STATUS_UNKNOWN; + + regmap_read(regmap, RT5033_REG_CHG_STAT, ®_data); + + switch (reg_data & RT5033_CHG_STAT_MASK) { + case RT5033_CHG_STAT_DISCHARGING: + state = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case RT5033_CHG_STAT_CHARGING: + state = POWER_SUPPLY_STATUS_CHARGING; + break; + case RT5033_CHG_STAT_FULL: + state = POWER_SUPPLY_STATUS_FULL; + break; + case RT5033_CHG_STAT_NOT_CHARGING: + state = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + default: + state = POWER_SUPPLY_STATUS_UNKNOWN; + } + + return state; +} + +static int rt5033_get_charger_type(struct rt5033_charger *charger) +{ + struct regmap *regmap = charger->regmap; + unsigned int reg_data; + int state; + + regmap_read(regmap, RT5033_REG_CHG_STAT, ®_data); + + switch (reg_data & RT5033_CHG_STAT_TYPE_MASK) { + case RT5033_CHG_STAT_TYPE_FAST: + state = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case RT5033_CHG_STAT_TYPE_PRE: + state = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + default: + state = POWER_SUPPLY_CHARGE_TYPE_NONE; + } + + return state; +} + +static int rt5033_get_charger_current_limit(struct rt5033_charger *charger) +{ + struct regmap *regmap = charger->regmap; + unsigned int state, reg_data, data; + + regmap_read(regmap, RT5033_REG_CHG_CTRL5, ®_data); + + state = (reg_data & RT5033_CHGCTRL5_ICHG_MASK) + >> RT5033_CHGCTRL5_ICHG_SHIFT; + + data = RT5033_CHARGER_FAST_CURRENT_MIN + + RT5033_CHARGER_FAST_CURRENT_STEP_NUM * state; + + return data; +} + +static int rt5033_get_charger_const_voltage(struct rt5033_charger *charger) +{ + struct regmap *regmap = charger->regmap; + unsigned int state, reg_data, data; + + regmap_read(regmap, RT5033_REG_CHG_CTRL2, ®_data); + + state = (reg_data & RT5033_CHGCTRL2_CV_MASK) + >> RT5033_CHGCTRL2_CV_SHIFT; + + data = RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MIN + + RT5033_CHARGER_CONST_VOLTAGE_STEP_NUM * state; + + return data; +} + +static inline int rt5033_init_const_charge(struct rt5033_charger *charger) +{ + struct rt5033_charger_data *chg = charger->chg; + int ret; + unsigned int val; + u8 reg_data; + + /* Set constant voltage mode */ + if (chg->const_uvolt < RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MIN || + chg->const_uvolt > RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MAX) { + dev_err(charger->dev, + "Value 'constant-charge-voltage-max-microvolt' out of range\n"); + return -EINVAL; + } + + if (chg->const_uvolt == RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MIN) + reg_data = 0x00; + else if (chg->const_uvolt == RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MAX) + reg_data = RT5033_CV_MAX_VOLTAGE; + else { + val = chg->const_uvolt; + val -= RT5033_CHARGER_CONST_VOLTAGE_LIMIT_MIN; + val /= RT5033_CHARGER_CONST_VOLTAGE_STEP_NUM; + reg_data = val; + } + + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL2, + RT5033_CHGCTRL2_CV_MASK, + reg_data << RT5033_CHGCTRL2_CV_SHIFT); + if (ret) { + dev_err(charger->dev, "Failed regmap update\n"); + return -EINVAL; + } + + /* Set end of charge current */ + if (chg->eoc_uamp < RT5033_CHARGER_EOC_MIN || + chg->eoc_uamp > RT5033_CHARGER_EOC_MAX) { + dev_err(charger->dev, + "Value 'charge-term-current-microamp' out of range\n"); + return -EINVAL; + } + + if (chg->eoc_uamp == RT5033_CHARGER_EOC_MIN) + reg_data = 0x01; + else if (chg->eoc_uamp == RT5033_CHARGER_EOC_MAX) + reg_data = 0x07; + else { + val = chg->eoc_uamp; + if (val < RT5033_CHARGER_EOC_REF) { + val -= RT5033_CHARGER_EOC_MIN; + val /= RT5033_CHARGER_EOC_STEP_NUM1; + reg_data = 0x01 + val; + } else if (val > RT5033_CHARGER_EOC_REF) { + val -= RT5033_CHARGER_EOC_REF; + val /= RT5033_CHARGER_EOC_STEP_NUM2; + reg_data = 0x04 + val; + } else { + reg_data = 0x04; + } + } + + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL4, + RT5033_CHGCTRL4_EOC_MASK, reg_data); + if (ret) { + dev_err(charger->dev, "Failed regmap update\n"); + return -EINVAL; + } + + return 0; +} + +static inline int rt5033_init_fast_charge(struct rt5033_charger *charger) +{ + struct rt5033_charger_data *chg = charger->chg; + int ret; + unsigned int val; + u8 reg_data; + + /* Set limit input current */ + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL1, + RT5033_CHGCTRL1_IAICR_MASK, RT5033_AICR_2000_MODE); + if (ret) { + dev_err(charger->dev, "Failed regmap update\n"); + return -EINVAL; + } + + /* Set fast-charge mode charging current */ + if (chg->fast_uamp < RT5033_CHARGER_FAST_CURRENT_MIN || + chg->fast_uamp > RT5033_CHARGER_FAST_CURRENT_MAX) { + dev_err(charger->dev, + "Value 'constant-charge-current-max-microamp' out of range\n"); + return -EINVAL; + } + + if (chg->fast_uamp == RT5033_CHARGER_FAST_CURRENT_MIN) + reg_data = 0x00; + else if (chg->fast_uamp == RT5033_CHARGER_FAST_CURRENT_MAX) + reg_data = RT5033_CHG_MAX_CURRENT; + else { + val = chg->fast_uamp; + val -= RT5033_CHARGER_FAST_CURRENT_MIN; + val /= RT5033_CHARGER_FAST_CURRENT_STEP_NUM; + reg_data = val; + } + + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL5, + RT5033_CHGCTRL5_ICHG_MASK, + reg_data << RT5033_CHGCTRL5_ICHG_SHIFT); + if (ret) { + dev_err(charger->dev, "Failed regmap update\n"); + return -EINVAL; + } + + return 0; +} + +static inline int rt5033_init_pre_charge(struct rt5033_charger *charger) +{ + struct rt5033_charger_data *chg = charger->chg; + int ret; + unsigned int val; + u8 reg_data; + + /* Set pre-charge threshold voltage */ + if (chg->pre_uvolt < RT5033_CHARGER_PRE_THRESHOLD_LIMIT_MIN || + chg->pre_uvolt > RT5033_CHARGER_PRE_THRESHOLD_LIMIT_MAX) { + dev_err(charger->dev, + "Value 'precharge-upper-limit-microvolt' out of range\n"); + return -EINVAL; + } + + if (chg->pre_uvolt == RT5033_CHARGER_PRE_THRESHOLD_LIMIT_MIN) + reg_data = 0x00; + else if (chg->pre_uvolt == RT5033_CHARGER_PRE_THRESHOLD_LIMIT_MAX) + reg_data = 0x0f; + else { + val = chg->pre_uvolt; + val -= RT5033_CHARGER_PRE_THRESHOLD_LIMIT_MIN; + val /= RT5033_CHARGER_PRE_THRESHOLD_STEP_NUM; + reg_data = val; + } + + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL5, + RT5033_CHGCTRL5_VPREC_MASK, reg_data); + if (ret) { + dev_err(charger->dev, "Failed regmap update\n"); + return -EINVAL; + } + + /* Set pre-charge mode charging current */ + if (chg->pre_uamp < RT5033_CHARGER_PRE_CURRENT_LIMIT_MIN || + chg->pre_uamp > RT5033_CHARGER_PRE_CURRENT_LIMIT_MAX) { + dev_err(charger->dev, + "Value 'precharge-current-microamp' out of range\n"); + return -EINVAL; + } + + if (chg->pre_uamp == RT5033_CHARGER_PRE_CURRENT_LIMIT_MIN) + reg_data = 0x00; + else if (chg->pre_uamp == RT5033_CHARGER_PRE_CURRENT_LIMIT_MAX) + reg_data = RT5033_CHG_MAX_PRE_CURRENT; + else { + val = chg->pre_uamp; + val -= RT5033_CHARGER_PRE_CURRENT_LIMIT_MIN; + val /= RT5033_CHARGER_PRE_CURRENT_STEP_NUM; + reg_data = val; + } + + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL4, + RT5033_CHGCTRL4_IPREC_MASK, + reg_data << RT5033_CHGCTRL4_IPREC_SHIFT); + if (ret) { + dev_err(charger->dev, "Failed regmap update\n"); + return -EINVAL; + } + + return 0; +} + +static int rt5033_charger_reg_init(struct rt5033_charger *charger) +{ + int ret = 0; + + /* Enable charging termination */ + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL1, + RT5033_CHGCTRL1_TE_EN_MASK, RT5033_TE_ENABLE); + if (ret) { + dev_err(charger->dev, "Failed to enable charging termination.\n"); + return -EINVAL; + } + + /* + * Disable minimum input voltage regulation (MIVR), this improves + * the charging performance. + */ + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL4, + RT5033_CHGCTRL4_MIVR_MASK, RT5033_CHARGER_MIVR_DISABLE); + if (ret) { + dev_err(charger->dev, "Failed to disable MIVR.\n"); + return -EINVAL; + } + + ret = rt5033_init_pre_charge(charger); + if (ret) + return ret; + + ret = rt5033_init_fast_charge(charger); + if (ret) + return ret; + + ret = rt5033_init_const_charge(charger); + if (ret) + return ret; + + return 0; +} + +static enum power_supply_property rt5033_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_ONLINE, +}; + +static int rt5033_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt5033_charger *charger = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = rt5033_get_charger_state(charger); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = rt5033_get_charger_type(charger); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = rt5033_get_charger_current_limit(charger); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + val->intval = rt5033_get_charger_const_voltage(charger); + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = RT5033_CHARGER_MODEL; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = RT5033_MANUFACTURER; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = (rt5033_get_charger_state(charger) == + POWER_SUPPLY_STATUS_CHARGING); + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct rt5033_charger_data *rt5033_charger_dt_init( + struct rt5033_charger *charger) +{ + struct rt5033_charger_data *chg; + struct power_supply_battery_info *info; + int ret; + + chg = devm_kzalloc(charger->dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return ERR_PTR(-ENOMEM); + + ret = power_supply_get_battery_info(charger->psy, &info); + if (ret) + return ERR_PTR(dev_err_probe(charger->dev, -EINVAL, + "missing battery info\n")); + + /* Assign data. Validity will be checked in the init functions. */ + chg->pre_uamp = info->precharge_current_ua; + chg->fast_uamp = info->constant_charge_current_max_ua; + chg->eoc_uamp = info->charge_term_current_ua; + chg->pre_uvolt = info->precharge_voltage_max_uv; + chg->const_uvolt = info->constant_charge_voltage_max_uv; + + return chg; +} + +static const struct power_supply_desc rt5033_charger_desc = { + .name = "rt5033-charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = rt5033_charger_props, + .num_properties = ARRAY_SIZE(rt5033_charger_props), + .get_property = rt5033_charger_get_property, +}; + +static int rt5033_charger_probe(struct platform_device *pdev) +{ + struct rt5033_charger *charger; + struct power_supply_config psy_cfg = {}; + int ret; + + charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); + if (!charger) + return -ENOMEM; + + platform_set_drvdata(pdev, charger); + charger->dev = &pdev->dev; + charger->regmap = dev_get_regmap(pdev->dev.parent, NULL); + + psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.drv_data = charger; + + charger->psy = devm_power_supply_register(&pdev->dev, + &rt5033_charger_desc, + &psy_cfg); + if (IS_ERR(charger->psy)) + return dev_err_probe(&pdev->dev, PTR_ERR(charger->psy), + "Failed to register power supply\n"); + + charger->chg = rt5033_charger_dt_init(charger); + if (IS_ERR_OR_NULL(charger->chg)) + return PTR_ERR(charger->chg); + + ret = rt5033_charger_reg_init(charger); + if (ret) + return ret; + + return 0; +} + +static const struct platform_device_id rt5033_charger_id[] = { + { "rt5033-charger", }, + { } +}; +MODULE_DEVICE_TABLE(platform, rt5033_charger_id); + +static const struct of_device_id rt5033_charger_of_match[] = { + { .compatible = "richtek,rt5033-charger", }, + { } +}; +MODULE_DEVICE_TABLE(of, rt5033_charger_of_match); + +static struct platform_driver rt5033_charger_driver = { + .driver = { + .name = "rt5033-charger", + .of_match_table = rt5033_charger_of_match, + }, + .probe = rt5033_charger_probe, + .id_table = rt5033_charger_id, +}; +module_platform_driver(rt5033_charger_driver); + +MODULE_DESCRIPTION("Richtek RT5033 charger driver"); +MODULE_AUTHOR("Beomho Seo "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/rt5033.h b/include/linux/mfd/rt5033.h index e99e2ab0c1c1..3992fb2ef0a8 100644 --- a/include/linux/mfd/rt5033.h +++ b/include/linux/mfd/rt5033.h @@ -39,20 +39,4 @@ struct rt5033_battery { struct power_supply *psy; }; -/* RT5033 charger platform data */ -struct rt5033_charger_data { - unsigned int pre_uamp; - unsigned int pre_uvolt; - unsigned int const_uvolt; - unsigned int eoc_uamp; - unsigned int fast_uamp; -}; - -struct rt5033_charger { - struct device *dev; - struct rt5033_dev *rt5033; - struct power_supply *psy; - struct rt5033_charger_data *chg; -}; - #endif /* __RT5033_H__ */ -- cgit v1.2.3-59-g8ed1b From 49b435908b2aeb0f3a8a5256256019594a5adc1c Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Mon, 15 May 2023 22:57:16 +0200 Subject: power: supply: rt5033_battery: Move struct rt5033_battery to battery driver Move struct rt5033_battery from the mfd header into the battery driver because it's not used by others. Within struct rt5033_battery, remove the line "struct rt5033_dev *rt5033;" because it doesn't get used. In rt5033.h, remove #include , it's not necessary anymore. In rt5033_battery.c, remove #include , it's not necessary anymore either. Instead add #include and Signed-off-by: Jakob Hauser Acked-by: Sebastian Reichel Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/736e1cbee257853cb3d1da6f05c184e9a053263b.1684182964.git.jahau@rocketmail.com --- drivers/power/supply/rt5033_battery.c | 9 ++++++++- include/linux/mfd/rt5033.h | 8 -------- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/power/supply/rt5033_battery.c b/drivers/power/supply/rt5033_battery.c index 5c04cf305219..91e1efd81f69 100644 --- a/drivers/power/supply/rt5033_battery.c +++ b/drivers/power/supply/rt5033_battery.c @@ -6,11 +6,18 @@ * Author: Beomho Seo */ +#include #include #include #include +#include #include -#include + +struct rt5033_battery { + struct i2c_client *client; + struct regmap *regmap; + struct power_supply *psy; +}; static int rt5033_battery_get_capacity(struct i2c_client *client) { diff --git a/include/linux/mfd/rt5033.h b/include/linux/mfd/rt5033.h index 3992fb2ef0a8..bb3d18945d21 100644 --- a/include/linux/mfd/rt5033.h +++ b/include/linux/mfd/rt5033.h @@ -12,7 +12,6 @@ #include #include #include -#include /* RT5033 regulator IDs */ enum rt5033_regulators { @@ -32,11 +31,4 @@ struct rt5033_dev { bool wakeup; }; -struct rt5033_battery { - struct i2c_client *client; - struct rt5033_dev *rt5033; - struct regmap *regmap; - struct power_supply *psy; -}; - #endif /* __RT5033_H__ */ -- cgit v1.2.3-59-g8ed1b From c452e3bd91b30a8ef7889fa06a50f54158c720d6 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 17 Apr 2023 12:26:51 +0300 Subject: mfd: intel-m10-bmc: Create m10bmc_sys_update_bits() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap regmap_update_bits() with m10bmc_sys_update_bits() in order to be able to add additional checks into it. Co-developed-by: Russ Weight Signed-off-by: Russ Weight Signed-off-by: Ilpo Järvinen Reviewed-by: Xu Yilun Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230417092653.16487-3-ilpo.jarvinen@linux.intel.com --- drivers/fpga/intel-m10-bmc-sec-update.c | 30 ++++++++++++++---------------- drivers/mfd/intel-m10-bmc-core.c | 9 +++++++++ include/linux/mfd/intel-m10-bmc.h | 4 ++++ 3 files changed, 27 insertions(+), 16 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index d7e2f9f461bc..fe0127a58eff 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -376,12 +376,11 @@ static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec) u32 doorbell_reg, progress, status; int ret, err; - ret = regmap_update_bits(sec->m10bmc->regmap, - csr_map->base + csr_map->doorbell, - DRBL_RSU_REQUEST | DRBL_HOST_STATUS, - DRBL_RSU_REQUEST | - FIELD_PREP(DRBL_HOST_STATUS, - HOST_STATUS_IDLE)); + ret = m10bmc_sys_update_bits(sec->m10bmc, csr_map->doorbell, + DRBL_RSU_REQUEST | DRBL_HOST_STATUS, + DRBL_RSU_REQUEST | + FIELD_PREP(DRBL_HOST_STATUS, + HOST_STATUS_IDLE)); if (ret) return FW_UPLOAD_ERR_RW_ERROR; @@ -450,11 +449,10 @@ static enum fw_upload_err rsu_send_data(struct m10bmc_sec *sec) u32 doorbell_reg, status; int ret; - ret = regmap_update_bits(sec->m10bmc->regmap, - csr_map->base + csr_map->doorbell, - DRBL_HOST_STATUS, - FIELD_PREP(DRBL_HOST_STATUS, - HOST_STATUS_WRITE_DONE)); + ret = m10bmc_sys_update_bits(sec->m10bmc, csr_map->doorbell, + DRBL_HOST_STATUS, + FIELD_PREP(DRBL_HOST_STATUS, + HOST_STATUS_WRITE_DONE)); if (ret) return FW_UPLOAD_ERR_RW_ERROR; @@ -517,11 +515,10 @@ static enum fw_upload_err rsu_cancel(struct m10bmc_sec *sec) if (rsu_prog(doorbell) != RSU_PROG_READY) return FW_UPLOAD_ERR_BUSY; - ret = regmap_update_bits(sec->m10bmc->regmap, - csr_map->base + csr_map->doorbell, - DRBL_HOST_STATUS, - FIELD_PREP(DRBL_HOST_STATUS, - HOST_STATUS_ABORT_RSU)); + ret = m10bmc_sys_update_bits(sec->m10bmc, csr_map->doorbell, + DRBL_HOST_STATUS, + FIELD_PREP(DRBL_HOST_STATUS, + HOST_STATUS_ABORT_RSU)); if (ret) return FW_UPLOAD_ERR_RW_ERROR; @@ -764,3 +761,4 @@ module_platform_driver(intel_m10bmc_sec_driver); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel MAX10 BMC Secure Update"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(INTEL_M10_BMC_CORE); diff --git a/drivers/mfd/intel-m10-bmc-core.c b/drivers/mfd/intel-m10-bmc-core.c index c0b8209fd842..dab1bb152fd6 100644 --- a/drivers/mfd/intel-m10-bmc-core.c +++ b/drivers/mfd/intel-m10-bmc-core.c @@ -12,6 +12,15 @@ #include #include +int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset, + unsigned int msk, unsigned int val) +{ + const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; + + return regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); +} +EXPORT_SYMBOL_NS_GPL(m10bmc_sys_update_bits, INTEL_M10_BMC_CORE); + static ssize_t bmc_version_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/include/linux/mfd/intel-m10-bmc.h b/include/linux/mfd/intel-m10-bmc.h index 1812ebfa11a8..5418f7279ed0 100644 --- a/include/linux/mfd/intel-m10-bmc.h +++ b/include/linux/mfd/intel-m10-bmc.h @@ -251,6 +251,7 @@ struct intel_m10bmc { * * m10bmc_raw_read - read m10bmc register per addr * m10bmc_sys_read - read m10bmc system register per offset + * m10bmc_sys_update_bits - update m10bmc system register per offset */ static inline int m10bmc_raw_read(struct intel_m10bmc *m10bmc, unsigned int addr, @@ -282,6 +283,9 @@ static inline int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offs return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); } +int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset, + unsigned int msk, unsigned int val); + /* * MAX10 BMC Core support */ -- cgit v1.2.3-59-g8ed1b From e9c154eed8aa166330eb0a8dc84642a8675c31e6 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 17 Apr 2023 12:26:52 +0300 Subject: mfd: intel-m10-bmc: Move m10bmc_sys_read() away from header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move m10bmc_sys_read() out from the header to prepare it for adding more code into the function which would make it too large to be a static inline any more. While at it, replace the vague wording in function comment with more precise statements. Reviewed-by: Russ Weight Acked-by: Guenter Roeck # For hwmon Signed-off-by: Ilpo Järvinen Reviewed-by: Xu Yilun Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230417092653.16487-4-ilpo.jarvinen@linux.intel.com --- drivers/hwmon/intel-m10-bmc-hwmon.c | 1 + drivers/mfd/intel-m10-bmc-core.c | 14 ++++++++++++++ include/linux/mfd/intel-m10-bmc.h | 17 +---------------- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index 6512f4bec79a..6500ca548f9c 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -794,3 +794,4 @@ MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel MAX 10 BMC hardware monitor"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(INTEL_M10_BMC_CORE); diff --git a/drivers/mfd/intel-m10-bmc-core.c b/drivers/mfd/intel-m10-bmc-core.c index dab1bb152fd6..51f865174796 100644 --- a/drivers/mfd/intel-m10-bmc-core.c +++ b/drivers/mfd/intel-m10-bmc-core.c @@ -12,6 +12,20 @@ #include #include +/* + * This function helps to simplify the accessing of the system registers. + * + * The base of the system registers is configured through the struct + * csr_map. + */ +int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int *val) +{ + const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; + + return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); +} +EXPORT_SYMBOL_NS_GPL(m10bmc_sys_read, INTEL_M10_BMC_CORE); + int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int msk, unsigned int val) { diff --git a/include/linux/mfd/intel-m10-bmc.h b/include/linux/mfd/intel-m10-bmc.h index 5418f7279ed0..252644fa61be 100644 --- a/include/linux/mfd/intel-m10-bmc.h +++ b/include/linux/mfd/intel-m10-bmc.h @@ -267,22 +267,7 @@ m10bmc_raw_read(struct intel_m10bmc *m10bmc, unsigned int addr, return ret; } -/* - * The base of the system registers could be configured by HW developers, and - * in HW SPEC, the base is not added to the addresses of the system registers. - * - * This function helps to simplify the accessing of the system registers. And if - * the base is reconfigured in HW, SW developers could simply change the - * csr_map's base accordingly. - */ -static inline int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, - unsigned int *val) -{ - const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; - - return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); -} - +int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int *val); int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int msk, unsigned int val); -- cgit v1.2.3-59-g8ed1b From 867cae44f8ae150d0e303cfd62a01d0d7cd7f7a5 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 17 Apr 2023 12:26:53 +0300 Subject: mfd: intel-m10-bmc: Manage access to MAX 10 fw handshake registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some MAX 10 cards, the BMC firmware is not available to service handshake registers during secure update erase and write phases at normal speeds. This problem affects at least hwmon driver. When the MAX 10 hwmon driver tries to read the sensor values during a secure update, the reads are slowed down (e.g., reading all D5005 sensors takes ~24s which is magnitudes worse than the normal <0.02s). Manage access to the handshake registers using a rw semaphore and a FW state variable to prevent accesses during those secure update phases and return -EBUSY instead. If handshake_sys_reg_nranges == 0, don't update bwcfw_state as it is not used. This avoids the locking cost. Co-developed-by: Russ Weight Signed-off-by: Russ Weight Co-developed-by: Xu Yilun Signed-off-by: Xu Yilun Signed-off-by: Ilpo Järvinen Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230417092653.16487-5-ilpo.jarvinen@linux.intel.com --- drivers/fpga/intel-m10-bmc-sec-update.c | 17 +++++++-- drivers/mfd/intel-m10-bmc-core.c | 67 ++++++++++++++++++++++++++++++++- drivers/mfd/intel-m10-bmc-spi.c | 14 +++++++ include/linux/mfd/intel-m10-bmc.h | 28 ++++++++++++++ 4 files changed, 121 insertions(+), 5 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index fe0127a58eff..31af2e08c825 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -544,21 +544,28 @@ static enum fw_upload_err m10bmc_sec_prepare(struct fw_upload *fwl, if (ret != FW_UPLOAD_ERR_NONE) goto unlock_flash; + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_SEC_UPDATE_PREPARE); + ret = rsu_update_init(sec); if (ret != FW_UPLOAD_ERR_NONE) - goto unlock_flash; + goto fw_state_exit; ret = rsu_prog_ready(sec); if (ret != FW_UPLOAD_ERR_NONE) - goto unlock_flash; + goto fw_state_exit; if (sec->cancel_request) { ret = rsu_cancel(sec); - goto unlock_flash; + goto fw_state_exit; } + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_SEC_UPDATE_WRITE); + return FW_UPLOAD_ERR_NONE; +fw_state_exit: + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_NORMAL); + unlock_flash: if (sec->m10bmc->flash_bulk_ops) sec->m10bmc->flash_bulk_ops->unlock_write(sec->m10bmc); @@ -607,6 +614,8 @@ static enum fw_upload_err m10bmc_sec_poll_complete(struct fw_upload *fwl) if (sec->cancel_request) return rsu_cancel(sec); + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_SEC_UPDATE_PROGRAM); + result = rsu_send_data(sec); if (result != FW_UPLOAD_ERR_NONE) return result; @@ -650,6 +659,8 @@ static void m10bmc_sec_cleanup(struct fw_upload *fwl) (void)rsu_cancel(sec); + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_NORMAL); + if (sec->m10bmc->flash_bulk_ops) sec->m10bmc->flash_bulk_ops->unlock_write(sec->m10bmc); } diff --git a/drivers/mfd/intel-m10-bmc-core.c b/drivers/mfd/intel-m10-bmc-core.c index 51f865174796..8ad5b3821584 100644 --- a/drivers/mfd/intel-m10-bmc-core.c +++ b/drivers/mfd/intel-m10-bmc-core.c @@ -12,6 +12,46 @@ #include #include +void m10bmc_fw_state_set(struct intel_m10bmc *m10bmc, enum m10bmc_fw_state new_state) +{ + /* bmcfw_state is only needed if handshake_sys_reg_nranges > 0 */ + if (!m10bmc->info->handshake_sys_reg_nranges) + return; + + down_write(&m10bmc->bmcfw_lock); + m10bmc->bmcfw_state = new_state; + up_write(&m10bmc->bmcfw_lock); +} +EXPORT_SYMBOL_NS_GPL(m10bmc_fw_state_set, INTEL_M10_BMC_CORE); + +/* + * For some Intel FPGA devices, the BMC firmware is not available to service + * handshake registers during a secure update. + */ +static bool m10bmc_reg_always_available(struct intel_m10bmc *m10bmc, unsigned int offset) +{ + if (!m10bmc->info->handshake_sys_reg_nranges) + return true; + + return !regmap_reg_in_ranges(offset, m10bmc->info->handshake_sys_reg_ranges, + m10bmc->info->handshake_sys_reg_nranges); +} + +/* + * m10bmc_handshake_reg_unavailable - Checks if reg access collides with secure update state + * @m10bmc: M10 BMC structure + * + * For some Intel FPGA devices, the BMC firmware is not available to service + * handshake registers during a secure update erase and write phases. + * + * Context: @m10bmc->bmcfw_lock must be held. + */ +static bool m10bmc_handshake_reg_unavailable(struct intel_m10bmc *m10bmc) +{ + return m10bmc->bmcfw_state == M10BMC_FW_STATE_SEC_UPDATE_PREPARE || + m10bmc->bmcfw_state == M10BMC_FW_STATE_SEC_UPDATE_WRITE; +} + /* * This function helps to simplify the accessing of the system registers. * @@ -21,8 +61,19 @@ int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int *val) { const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; + int ret; + + if (m10bmc_reg_always_available(m10bmc, offset)) + return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); - return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); + down_read(&m10bmc->bmcfw_lock); + if (m10bmc_handshake_reg_unavailable(m10bmc)) + ret = -EBUSY; /* Reg not available during secure update */ + else + ret = m10bmc_raw_read(m10bmc, csr_map->base + offset, val); + up_read(&m10bmc->bmcfw_lock); + + return ret; } EXPORT_SYMBOL_NS_GPL(m10bmc_sys_read, INTEL_M10_BMC_CORE); @@ -30,8 +81,19 @@ int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int msk, unsigned int val) { const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; + int ret; - return regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); + if (m10bmc_reg_always_available(m10bmc, offset)) + return regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); + + down_read(&m10bmc->bmcfw_lock); + if (m10bmc_handshake_reg_unavailable(m10bmc)) + ret = -EBUSY; /* Reg not available during secure update */ + else + ret = regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); + up_read(&m10bmc->bmcfw_lock); + + return ret; } EXPORT_SYMBOL_NS_GPL(m10bmc_sys_update_bits, INTEL_M10_BMC_CORE); @@ -129,6 +191,7 @@ int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platf m10bmc->info = info; dev_set_drvdata(m10bmc->dev, m10bmc); + init_rwsem(&m10bmc->bmcfw_lock); ret = devm_mfd_add_devices(m10bmc->dev, PLATFORM_DEVID_AUTO, info->cells, info->n_cells, diff --git a/drivers/mfd/intel-m10-bmc-spi.c b/drivers/mfd/intel-m10-bmc-spi.c index edd266557ab9..cbeb7de9e041 100644 --- a/drivers/mfd/intel-m10-bmc-spi.c +++ b/drivers/mfd/intel-m10-bmc-spi.c @@ -116,12 +116,20 @@ static struct mfd_cell m10bmc_d5005_subdevs[] = { { .name = "d5005bmc-sec-update" }, }; +static const struct regmap_range m10bmc_d5005_fw_handshake_regs[] = { + regmap_reg_range(M10BMC_N3000_TELEM_START, M10BMC_D5005_TELEM_END), +}; + static struct mfd_cell m10bmc_pacn3000_subdevs[] = { { .name = "n3000bmc-hwmon" }, { .name = "n3000bmc-retimer" }, { .name = "n3000bmc-sec-update" }, }; +static const struct regmap_range m10bmc_n3000_fw_handshake_regs[] = { + regmap_reg_range(M10BMC_N3000_TELEM_START, M10BMC_N3000_TELEM_END), +}; + static struct mfd_cell m10bmc_n5010_subdevs[] = { { .name = "n5010bmc-hwmon" }, }; @@ -129,18 +137,24 @@ static struct mfd_cell m10bmc_n5010_subdevs[] = { static const struct intel_m10bmc_platform_info m10bmc_spi_n3000 = { .cells = m10bmc_pacn3000_subdevs, .n_cells = ARRAY_SIZE(m10bmc_pacn3000_subdevs), + .handshake_sys_reg_ranges = m10bmc_n3000_fw_handshake_regs, + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_n3000_fw_handshake_regs), .csr_map = &m10bmc_n3000_csr_map, }; static const struct intel_m10bmc_platform_info m10bmc_spi_d5005 = { .cells = m10bmc_d5005_subdevs, .n_cells = ARRAY_SIZE(m10bmc_d5005_subdevs), + .handshake_sys_reg_ranges = m10bmc_d5005_fw_handshake_regs, + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_d5005_fw_handshake_regs), .csr_map = &m10bmc_n3000_csr_map, }; static const struct intel_m10bmc_platform_info m10bmc_spi_n5010 = { .cells = m10bmc_n5010_subdevs, .n_cells = ARRAY_SIZE(m10bmc_n5010_subdevs), + .handshake_sys_reg_ranges = m10bmc_n3000_fw_handshake_regs, + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_n3000_fw_handshake_regs), .csr_map = &m10bmc_n3000_csr_map, }; diff --git a/include/linux/mfd/intel-m10-bmc.h b/include/linux/mfd/intel-m10-bmc.h index 252644fa61be..ee66c9751003 100644 --- a/include/linux/mfd/intel-m10-bmc.h +++ b/include/linux/mfd/intel-m10-bmc.h @@ -11,6 +11,7 @@ #include #include #include +#include #define M10BMC_N3000_LEGACY_BUILD_VER 0x300468 #define M10BMC_N3000_SYS_BASE 0x300800 @@ -39,6 +40,11 @@ #define M10BMC_N3000_VER_PCB_INFO_MSK GENMASK(31, 24) #define M10BMC_N3000_VER_LEGACY_INVALID 0xffffffff +/* Telemetry registers */ +#define M10BMC_N3000_TELEM_START 0x100 +#define M10BMC_N3000_TELEM_END 0x250 +#define M10BMC_D5005_TELEM_END 0x300 + /* Secure update doorbell register, in system register region */ #define M10BMC_N3000_DOORBELL 0x400 @@ -205,11 +211,15 @@ struct m10bmc_csr_map { * struct intel_m10bmc_platform_info - Intel MAX 10 BMC platform specific information * @cells: MFD cells * @n_cells: MFD cells ARRAY_SIZE() + * @handshake_sys_reg_ranges: array of register ranges for fw handshake regs + * @handshake_sys_reg_nranges: number of register ranges for fw handshake regs * @csr_map: the mappings for register definition of MAX10 BMC */ struct intel_m10bmc_platform_info { struct mfd_cell *cells; int n_cells; + const struct regmap_range *handshake_sys_reg_ranges; + unsigned int handshake_sys_reg_nranges; const struct m10bmc_csr_map *csr_map; }; @@ -232,18 +242,30 @@ struct intel_m10bmc_flash_bulk_ops { void (*unlock_write)(struct intel_m10bmc *m10bmc); }; +enum m10bmc_fw_state { + M10BMC_FW_STATE_NORMAL, + M10BMC_FW_STATE_SEC_UPDATE_PREPARE, + M10BMC_FW_STATE_SEC_UPDATE_WRITE, + M10BMC_FW_STATE_SEC_UPDATE_PROGRAM, +}; + /** * struct intel_m10bmc - Intel MAX 10 BMC parent driver data structure * @dev: this device * @regmap: the regmap used to access registers by m10bmc itself * @info: the platform information for MAX10 BMC * @flash_bulk_ops: optional device specific operations for flash R/W + * @bmcfw_lock: read/write semaphore to BMC firmware running state + * @bmcfw_state: BMC firmware running state. Available only when + * handshake_sys_reg_nranges > 0. */ struct intel_m10bmc { struct device *dev; struct regmap *regmap; const struct intel_m10bmc_platform_info *info; const struct intel_m10bmc_flash_bulk_ops *flash_bulk_ops; + struct rw_semaphore bmcfw_lock; /* Protects bmcfw_state */ + enum m10bmc_fw_state bmcfw_state; }; /* @@ -271,6 +293,12 @@ int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned i int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int msk, unsigned int val); +/* + * Track the state of the firmware, as it is not available for register + * handshakes during secure updates on some MAX 10 cards. + */ +void m10bmc_fw_state_set(struct intel_m10bmc *m10bmc, enum m10bmc_fw_state new_state); + /* * MAX10 BMC Core support */ -- cgit v1.2.3-59-g8ed1b From 63eeabbc9dbddd7381409feccd9082e5ffabfe59 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Thu, 11 May 2023 10:26:08 +0100 Subject: mfd: axp20x: Add support for AXP192 The AXP192 PMIC is similar to the AXP202/AXP209, but with different regulators, additional GPIOs, and a different IRQ register layout. Signed-off-by: Aidan MacDonald Link: https://lore.kernel.org/r/20230511092609.76183-1-aidanmacdonald.0x0@gmail.com Signed-off-by: Lee Jones --- drivers/mfd/axp20x-i2c.c | 2 + drivers/mfd/axp20x.c | 141 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/axp20x.h | 84 +++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c index da0e538c18bd..68d3560cfe4a 100644 --- a/drivers/mfd/axp20x-i2c.c +++ b/drivers/mfd/axp20x-i2c.c @@ -59,6 +59,7 @@ static void axp20x_i2c_remove(struct i2c_client *i2c) #ifdef CONFIG_OF static const struct of_device_id axp20x_i2c_of_match[] = { { .compatible = "x-powers,axp152", .data = (void *)AXP152_ID }, + { .compatible = "x-powers,axp192", .data = (void *)AXP192_ID }, { .compatible = "x-powers,axp202", .data = (void *)AXP202_ID }, { .compatible = "x-powers,axp209", .data = (void *)AXP209_ID }, { .compatible = "x-powers,axp221", .data = (void *)AXP221_ID }, @@ -74,6 +75,7 @@ MODULE_DEVICE_TABLE(of, axp20x_i2c_of_match); static const struct i2c_device_id axp20x_i2c_id[] = { { "axp152", 0 }, + { "axp192", 0 }, { "axp202", 0 }, { "axp209", 0 }, { "axp221", 0 }, diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 07a846ecbf18..c03bc5cda080 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -34,6 +34,7 @@ static const char * const axp20x_model_names[] = { "AXP152", + "AXP192", "AXP202", "AXP209", "AXP221", @@ -94,6 +95,35 @@ static const struct regmap_access_table axp20x_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), }; +static const struct regmap_range axp192_writeable_ranges[] = { + regmap_reg_range(AXP192_DATACACHE(0), AXP192_DATACACHE(5)), + regmap_reg_range(AXP192_PWR_OUT_CTRL, AXP192_IRQ5_STATE), + regmap_reg_range(AXP20X_DCDC_MODE, AXP192_N_RSTO_CTRL), + regmap_reg_range(AXP20X_CC_CTRL, AXP20X_CC_CTRL), +}; + +static const struct regmap_range axp192_volatile_ranges[] = { + regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP192_USB_OTG_STATUS), + regmap_reg_range(AXP192_IRQ1_STATE, AXP192_IRQ4_STATE), + regmap_reg_range(AXP192_IRQ5_STATE, AXP192_IRQ5_STATE), + regmap_reg_range(AXP20X_ACIN_V_ADC_H, AXP20X_IPSOUT_V_HIGH_L), + regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL), + regmap_reg_range(AXP192_GPIO2_0_STATE, AXP192_GPIO2_0_STATE), + regmap_reg_range(AXP192_GPIO4_3_STATE, AXP192_GPIO4_3_STATE), + regmap_reg_range(AXP192_N_RSTO_CTRL, AXP192_N_RSTO_CTRL), + regmap_reg_range(AXP20X_CHRG_CC_31_24, AXP20X_CC_CTRL), +}; + +static const struct regmap_access_table axp192_writeable_table = { + .yes_ranges = axp192_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp192_writeable_ranges), +}; + +static const struct regmap_access_table axp192_volatile_table = { + .yes_ranges = axp192_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(axp192_volatile_ranges), +}; + /* AXP22x ranges are shared with the AXP809, as they cover the same range */ static const struct regmap_range axp22x_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), @@ -220,6 +250,19 @@ static const struct resource axp152_pek_resources[] = { DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"), }; +static const struct resource axp192_ac_power_supply_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_PLUGIN, "ACIN_PLUGIN"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_REMOVAL, "ACIN_REMOVAL"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_OVER_V, "ACIN_OVER_V"), +}; + +static const struct resource axp192_usb_power_supply_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_VALID, "VBUS_VALID"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_NOT_VALID, "VBUS_NOT_VALID"), +}; + static const struct resource axp20x_ac_power_supply_resources[] = { DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_PLUGIN, "ACIN_PLUGIN"), DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_REMOVAL, "ACIN_REMOVAL"), @@ -302,6 +345,15 @@ static const struct regmap_config axp152_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const struct regmap_config axp192_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp192_writeable_table, + .volatile_table = &axp192_volatile_table, + .max_register = AXP20X_CC_CTRL, + .cache_type = REGCACHE_RBTREE, +}; + static const struct regmap_config axp20x_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -379,6 +431,42 @@ static const struct regmap_irq axp152_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP152, GPIO0_INPUT, 2, 0), }; +static const struct regmap_irq axp192_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP192, ACIN_OVER_V, 0, 7), + INIT_REGMAP_IRQ(AXP192, ACIN_PLUGIN, 0, 6), + INIT_REGMAP_IRQ(AXP192, ACIN_REMOVAL, 0, 5), + INIT_REGMAP_IRQ(AXP192, VBUS_OVER_V, 0, 4), + INIT_REGMAP_IRQ(AXP192, VBUS_PLUGIN, 0, 3), + INIT_REGMAP_IRQ(AXP192, VBUS_REMOVAL, 0, 2), + INIT_REGMAP_IRQ(AXP192, VBUS_V_LOW, 0, 1), + INIT_REGMAP_IRQ(AXP192, BATT_PLUGIN, 1, 7), + INIT_REGMAP_IRQ(AXP192, BATT_REMOVAL, 1, 6), + INIT_REGMAP_IRQ(AXP192, BATT_ENT_ACT_MODE, 1, 5), + INIT_REGMAP_IRQ(AXP192, BATT_EXIT_ACT_MODE, 1, 4), + INIT_REGMAP_IRQ(AXP192, CHARG, 1, 3), + INIT_REGMAP_IRQ(AXP192, CHARG_DONE, 1, 2), + INIT_REGMAP_IRQ(AXP192, BATT_TEMP_HIGH, 1, 1), + INIT_REGMAP_IRQ(AXP192, BATT_TEMP_LOW, 1, 0), + INIT_REGMAP_IRQ(AXP192, DIE_TEMP_HIGH, 2, 7), + INIT_REGMAP_IRQ(AXP192, CHARG_I_LOW, 2, 6), + INIT_REGMAP_IRQ(AXP192, DCDC1_V_LONG, 2, 5), + INIT_REGMAP_IRQ(AXP192, DCDC2_V_LONG, 2, 4), + INIT_REGMAP_IRQ(AXP192, DCDC3_V_LONG, 2, 3), + INIT_REGMAP_IRQ(AXP192, PEK_SHORT, 2, 1), + INIT_REGMAP_IRQ(AXP192, PEK_LONG, 2, 0), + INIT_REGMAP_IRQ(AXP192, N_OE_PWR_ON, 3, 7), + INIT_REGMAP_IRQ(AXP192, N_OE_PWR_OFF, 3, 6), + INIT_REGMAP_IRQ(AXP192, VBUS_VALID, 3, 5), + INIT_REGMAP_IRQ(AXP192, VBUS_NOT_VALID, 3, 4), + INIT_REGMAP_IRQ(AXP192, VBUS_SESS_VALID, 3, 3), + INIT_REGMAP_IRQ(AXP192, VBUS_SESS_END, 3, 2), + INIT_REGMAP_IRQ(AXP192, LOW_PWR_LVL, 3, 0), + INIT_REGMAP_IRQ(AXP192, TIMER, 4, 7), + INIT_REGMAP_IRQ(AXP192, GPIO2_INPUT, 4, 2), + INIT_REGMAP_IRQ(AXP192, GPIO1_INPUT, 4, 1), + INIT_REGMAP_IRQ(AXP192, GPIO0_INPUT, 4, 0), +}; + static const struct regmap_irq axp20x_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7), INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6), @@ -615,6 +703,32 @@ static const struct regmap_irq_chip axp152_regmap_irq_chip = { .num_regs = 3, }; +static unsigned int axp192_get_irq_reg(struct regmap_irq_chip_data *data, + unsigned int base, int index) +{ + /* linear mapping for IRQ1 to IRQ4 */ + if (index < 4) + return base + index; + + /* handle IRQ5 separately */ + if (base == AXP192_IRQ1_EN) + return AXP192_IRQ5_EN; + + return AXP192_IRQ5_STATE; +} + +static const struct regmap_irq_chip axp192_regmap_irq_chip = { + .name = "axp192_irq_chip", + .status_base = AXP192_IRQ1_STATE, + .ack_base = AXP192_IRQ1_STATE, + .unmask_base = AXP192_IRQ1_EN, + .init_ack_masked = true, + .irqs = axp192_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp192_regmap_irqs), + .num_regs = 5, + .get_irq_reg = axp192_get_irq_reg, +}; + static const struct regmap_irq_chip axp20x_regmap_irq_chip = { .name = "axp20x_irq_chip", .status_base = AXP20X_IRQ1_STATE, @@ -705,6 +819,27 @@ static const struct regmap_irq_chip axp15060_regmap_irq_chip = { .num_regs = 2, }; +static const struct mfd_cell axp192_cells[] = { + { + .name = "axp192-adc", + .of_compatible = "x-powers,axp192-adc", + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp192-battery-power-supply", + }, { + .name = "axp20x-ac-power-supply", + .of_compatible = "x-powers,axp202-ac-power-supply", + .num_resources = ARRAY_SIZE(axp192_ac_power_supply_resources), + .resources = axp192_ac_power_supply_resources, + }, { + .name = "axp20x-usb-power-supply", + .of_compatible = "x-powers,axp192-usb-power-supply", + .num_resources = ARRAY_SIZE(axp192_usb_power_supply_resources), + .resources = axp192_usb_power_supply_resources, + }, + { .name = "axp20x-regulator" }, +}; + static const struct mfd_cell axp20x_cells[] = { { .name = "axp20x-gpio", @@ -1022,6 +1157,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_cfg = &axp152_regmap_config; axp20x->regmap_irq_chip = &axp152_regmap_irq_chip; break; + case AXP192_ID: + axp20x->nr_cells = ARRAY_SIZE(axp192_cells); + axp20x->cells = axp192_cells; + axp20x->regmap_cfg = &axp192_regmap_config; + axp20x->regmap_irq_chip = &axp192_regmap_irq_chip; + break; case AXP202_ID: case AXP209_ID: axp20x->nr_cells = ARRAY_SIZE(axp20x_cells); diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index fff7fa6b7c5d..f1755163dd9f 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -12,6 +12,7 @@ enum axp20x_variants { AXP152_ID = 0, + AXP192_ID, AXP202_ID, AXP209_ID, AXP221_ID, @@ -26,6 +27,7 @@ enum axp20x_variants { NR_AXP20X_VARIANTS, }; +#define AXP192_DATACACHE(m) (0x06 + (m)) #define AXP20X_DATACACHE(m) (0x04 + (m)) /* Power supply */ @@ -47,6 +49,13 @@ enum axp20x_variants { #define AXP152_DCDC_FREQ 0x37 #define AXP152_DCDC_MODE 0x80 +#define AXP192_USB_OTG_STATUS 0x04 +#define AXP192_PWR_OUT_CTRL 0x12 +#define AXP192_DCDC2_V_OUT 0x23 +#define AXP192_DCDC1_V_OUT 0x26 +#define AXP192_DCDC3_V_OUT 0x27 +#define AXP192_LDO2_3_V_OUT 0x28 + #define AXP20X_PWR_INPUT_STATUS 0x00 #define AXP20X_PWR_OP_MODE 0x01 #define AXP20X_USB_OTG_STATUS 0x02 @@ -185,6 +194,17 @@ enum axp20x_variants { #define AXP152_IRQ2_STATE 0x49 #define AXP152_IRQ3_STATE 0x4a +#define AXP192_IRQ1_EN 0x40 +#define AXP192_IRQ2_EN 0x41 +#define AXP192_IRQ3_EN 0x42 +#define AXP192_IRQ4_EN 0x43 +#define AXP192_IRQ1_STATE 0x44 +#define AXP192_IRQ2_STATE 0x45 +#define AXP192_IRQ3_STATE 0x46 +#define AXP192_IRQ4_STATE 0x47 +#define AXP192_IRQ5_EN 0x4a +#define AXP192_IRQ5_STATE 0x4d + #define AXP20X_IRQ1_EN 0x40 #define AXP20X_IRQ2_EN 0x41 #define AXP20X_IRQ3_EN 0x42 @@ -204,6 +224,11 @@ enum axp20x_variants { #define AXP15060_IRQ2_STATE 0x49 /* ADC */ +#define AXP192_GPIO2_V_ADC_H 0x68 +#define AXP192_GPIO2_V_ADC_L 0x69 +#define AXP192_GPIO3_V_ADC_H 0x6a +#define AXP192_GPIO3_V_ADC_L 0x6b + #define AXP20X_ACIN_V_ADC_H 0x56 #define AXP20X_ACIN_V_ADC_L 0x57 #define AXP20X_ACIN_I_ADC_H 0x58 @@ -233,6 +258,8 @@ enum axp20x_variants { #define AXP20X_IPSOUT_V_HIGH_L 0x7f /* Power supply */ +#define AXP192_GPIO30_IN_RANGE 0x85 + #define AXP20X_DCDC_MODE 0x80 #define AXP20X_ADC_EN1 0x82 #define AXP20X_ADC_EN2 0x83 @@ -261,6 +288,16 @@ enum axp20x_variants { #define AXP152_PWM1_FREQ_Y 0x9c #define AXP152_PWM1_DUTY_CYCLE 0x9d +#define AXP192_GPIO0_CTRL 0x90 +#define AXP192_LDO_IO0_V_OUT 0x91 +#define AXP192_GPIO1_CTRL 0x92 +#define AXP192_GPIO2_CTRL 0x93 +#define AXP192_GPIO2_0_STATE 0x94 +#define AXP192_GPIO4_3_CTRL 0x95 +#define AXP192_GPIO4_3_STATE 0x96 +#define AXP192_GPIO2_0_PULL 0x97 +#define AXP192_N_RSTO_CTRL 0x9e + #define AXP20X_GPIO0_CTRL 0x90 #define AXP20X_LDO5_V_OUT 0x91 #define AXP20X_GPIO1_CTRL 0x92 @@ -340,6 +377,17 @@ enum axp20x_variants { #define AXP288_FG_TUNE5 0xed /* Regulators IDs */ +enum { + AXP192_DCDC1 = 0, + AXP192_DCDC2, + AXP192_DCDC3, + AXP192_LDO1, + AXP192_LDO2, + AXP192_LDO3, + AXP192_LDO_IO0, + AXP192_REG_ID_MAX +}; + enum { AXP20X_LDO1 = 0, AXP20X_LDO2, @@ -531,6 +579,42 @@ enum { AXP152_IRQ_GPIO0_INPUT, }; +enum axp192_irqs { + AXP192_IRQ_ACIN_OVER_V = 1, + AXP192_IRQ_ACIN_PLUGIN, + AXP192_IRQ_ACIN_REMOVAL, + AXP192_IRQ_VBUS_OVER_V, + AXP192_IRQ_VBUS_PLUGIN, + AXP192_IRQ_VBUS_REMOVAL, + AXP192_IRQ_VBUS_V_LOW, + AXP192_IRQ_BATT_PLUGIN, + AXP192_IRQ_BATT_REMOVAL, + AXP192_IRQ_BATT_ENT_ACT_MODE, + AXP192_IRQ_BATT_EXIT_ACT_MODE, + AXP192_IRQ_CHARG, + AXP192_IRQ_CHARG_DONE, + AXP192_IRQ_BATT_TEMP_HIGH, + AXP192_IRQ_BATT_TEMP_LOW, + AXP192_IRQ_DIE_TEMP_HIGH, + AXP192_IRQ_CHARG_I_LOW, + AXP192_IRQ_DCDC1_V_LONG, + AXP192_IRQ_DCDC2_V_LONG, + AXP192_IRQ_DCDC3_V_LONG, + AXP192_IRQ_PEK_SHORT = 22, + AXP192_IRQ_PEK_LONG, + AXP192_IRQ_N_OE_PWR_ON, + AXP192_IRQ_N_OE_PWR_OFF, + AXP192_IRQ_VBUS_VALID, + AXP192_IRQ_VBUS_NOT_VALID, + AXP192_IRQ_VBUS_SESS_VALID, + AXP192_IRQ_VBUS_SESS_END, + AXP192_IRQ_LOW_PWR_LVL = 31, + AXP192_IRQ_TIMER, + AXP192_IRQ_GPIO2_INPUT = 37, + AXP192_IRQ_GPIO1_INPUT, + AXP192_IRQ_GPIO0_INPUT, +}; + enum { AXP20X_IRQ_ACIN_OVER_V = 1, AXP20X_IRQ_ACIN_PLUGIN, -- cgit v1.2.3-59-g8ed1b From 48b4371b98676e8db3f72e4355af7707103d9c07 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Fri, 2 Jun 2023 08:24:25 +0200 Subject: mfd: stpmic1: Fixup main control register and bits naming Fixup main control register and bits naming so the match the naming from the datasheet. https://www.st.com/resource/en/datasheet/stpmic1.pdf Signed-off-by: Sean Nyekjaer Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230602062426.3947116-1-sean@geanix.com --- drivers/mfd/stpmic1.c | 4 ++-- include/linux/mfd/stpmic1.h | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c index d0ff46961910..a79dfb9923d7 100644 --- a/drivers/mfd/stpmic1.c +++ b/drivers/mfd/stpmic1.c @@ -19,7 +19,7 @@ static const struct regmap_range stpmic1_readable_ranges[] = { regmap_reg_range(TURN_ON_SR, VERSION_SR), - regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR), + regmap_reg_range(MAIN_CR, LDO6_STDBY_CR), regmap_reg_range(BST_SW_CR, BST_SW_CR), regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4), regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4), @@ -30,7 +30,7 @@ static const struct regmap_range stpmic1_readable_ranges[] = { }; static const struct regmap_range stpmic1_writeable_ranges[] = { - regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR), + regmap_reg_range(MAIN_CR, LDO6_STDBY_CR), regmap_reg_range(BST_SW_CR, BST_SW_CR), regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4), regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4), diff --git a/include/linux/mfd/stpmic1.h b/include/linux/mfd/stpmic1.h index fa3f99f7e9a1..dc00bac24f5a 100644 --- a/include/linux/mfd/stpmic1.h +++ b/include/linux/mfd/stpmic1.h @@ -15,7 +15,7 @@ #define RREQ_STATE_SR 0x5 #define VERSION_SR 0x6 -#define SWOFF_PWRCTRL_CR 0x10 +#define MAIN_CR 0x10 #define PADS_PULL_CR 0x11 #define BUCKS_PD_CR 0x12 #define LDO14_PD_CR 0x13 @@ -148,14 +148,14 @@ #define LDO_BYPASS_MASK BIT(7) /* Main PMIC Control Register - * SWOFF_PWRCTRL_CR + * MAIN_CR * Address : 0x10 */ -#define ICC_EVENT_ENABLED BIT(4) +#define OCP_OFF_DBG BIT(4) #define PWRCTRL_POLARITY_HIGH BIT(3) -#define PWRCTRL_PIN_VALID BIT(2) -#define RESTART_REQUEST_ENABLED BIT(1) -#define SOFTWARE_SWITCH_OFF_ENABLED BIT(0) +#define PWRCTRL_ENABLE BIT(2) +#define RESTART_REQUEST_ENABLE BIT(1) +#define SOFTWARE_SWITCH_OFF BIT(0) /* Main PMIC PADS Control Register * PADS_PULL_CR -- cgit v1.2.3-59-g8ed1b From e0cbc202388af454eb771043b20db6dfe68199ec Mon Sep 17 00:00:00 2001 From: Okan Sahin Date: Wed, 12 Apr 2023 14:12:46 +0300 Subject: mfd: max77541: Add ADI MAX77541/MAX77540 PMIC Support MFD driver for MAX77541/MAX77540 to enable its sub devices. The MAX77541 is a multi-function devices. It includes buck converter and ADC. The MAX77540 is a high-efficiency buck converter with two 3A switching phases. They have same regmap except for ADC part of MAX77541. Signed-off-by: Okan Sahin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230412111256.40013-6-okan.sahin@analog.com Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 13 +++ drivers/mfd/Makefile | 1 + drivers/mfd/max77541.c | 224 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/max77541.h | 91 ++++++++++++++++++ 4 files changed, 329 insertions(+) create mode 100644 drivers/mfd/max77541.c create mode 100644 include/linux/mfd/max77541.h (limited to 'include/linux/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2ddfc11b8f2b..6f5b259a6d6a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -784,6 +784,19 @@ config MFD_MAX14577 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX77541 + tristate "Analog Devices MAX77541/77540 PMIC Support" + depends on I2C=y + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Say yes here to add support for Analog Devices MAX77541 and + MAX77540 Power Management ICs. This driver provides + common support for accessing the device; additional drivers + must be enabled in order to use the functionality of the device. + There are regulators and adc. + config MFD_MAX77620 bool "Maxim Semiconductor MAX77620 and MAX20024 PMIC Support" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 39c461536181..f3d1f1dc73b5 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -154,6 +154,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o obj-$(CONFIG_MFD_DA9150) += da9150-core.o obj-$(CONFIG_MFD_MAX14577) += max14577.o +obj-$(CONFIG_MFD_MAX77541) += max77541.o obj-$(CONFIG_MFD_MAX77620) += max77620.o obj-$(CONFIG_MFD_MAX77650) += max77650.o obj-$(CONFIG_MFD_MAX77686) += max77686.o diff --git a/drivers/mfd/max77541.c b/drivers/mfd/max77541.c new file mode 100644 index 000000000000..4a3bad3493b3 --- /dev/null +++ b/drivers/mfd/max77541.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Analog Devices, Inc. + * Driver for the MAX77540 and MAX77541 + */ + +#include +#include +#include +#include +#include +#include + +static const struct regmap_config max77541_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct regmap_irq max77541_src_irqs[] = { + { .mask = MAX77541_BIT_INT_SRC_TOPSYS }, + { .mask = MAX77541_BIT_INT_SRC_BUCK }, +}; + +static const struct regmap_irq_chip max77541_src_irq_chip = { + .name = "max77541-src", + .status_base = MAX77541_REG_INT_SRC, + .mask_base = MAX77541_REG_INT_SRC_M, + .num_regs = 1, + .irqs = max77541_src_irqs, + .num_irqs = ARRAY_SIZE(max77541_src_irqs), +}; + +static const struct regmap_irq max77541_topsys_irqs[] = { + { .mask = MAX77541_BIT_TOPSYS_INT_TJ_120C }, + { .mask = MAX77541_BIT_TOPSYS_INT_TJ_140C }, + { .mask = MAX77541_BIT_TOPSYS_INT_TSHDN }, + { .mask = MAX77541_BIT_TOPSYS_INT_UVLO }, + { .mask = MAX77541_BIT_TOPSYS_INT_ALT_SWO }, + { .mask = MAX77541_BIT_TOPSYS_INT_EXT_FREQ_DET }, +}; + +static const struct regmap_irq_chip max77541_topsys_irq_chip = { + .name = "max77541-topsys", + .status_base = MAX77541_REG_TOPSYS_INT, + .mask_base = MAX77541_REG_TOPSYS_INT_M, + .num_regs = 1, + .irqs = max77541_topsys_irqs, + .num_irqs = ARRAY_SIZE(max77541_topsys_irqs), +}; + +static const struct regmap_irq max77541_buck_irqs[] = { + { .mask = MAX77541_BIT_BUCK_INT_M1_POK_FLT }, + { .mask = MAX77541_BIT_BUCK_INT_M2_POK_FLT }, + { .mask = MAX77541_BIT_BUCK_INT_M1_SCFLT }, + { .mask = MAX77541_BIT_BUCK_INT_M2_SCFLT }, +}; + +static const struct regmap_irq_chip max77541_buck_irq_chip = { + .name = "max77541-buck", + .status_base = MAX77541_REG_BUCK_INT, + .mask_base = MAX77541_REG_BUCK_INT_M, + .num_regs = 1, + .irqs = max77541_buck_irqs, + .num_irqs = ARRAY_SIZE(max77541_buck_irqs), +}; + +static const struct regmap_irq max77541_adc_irqs[] = { + { .mask = MAX77541_BIT_ADC_INT_CH1_I }, + { .mask = MAX77541_BIT_ADC_INT_CH2_I }, + { .mask = MAX77541_BIT_ADC_INT_CH3_I }, + { .mask = MAX77541_BIT_ADC_INT_CH6_I }, +}; + +static const struct regmap_irq_chip max77541_adc_irq_chip = { + .name = "max77541-adc", + .status_base = MAX77541_REG_ADC_INT, + .mask_base = MAX77541_REG_ADC_INT_M, + .num_regs = 1, + .irqs = max77541_adc_irqs, + .num_irqs = ARRAY_SIZE(max77541_adc_irqs), +}; + +static const struct mfd_cell max77540_devs[] = { + MFD_CELL_OF("max77540-regulator", NULL, NULL, 0, 0, NULL), +}; + +static const struct mfd_cell max77541_devs[] = { + MFD_CELL_OF("max77541-regulator", NULL, NULL, 0, 0, NULL), + MFD_CELL_OF("max77541-adc", NULL, NULL, 0, 0, NULL), +}; + +static int max77541_pmic_irq_init(struct device *dev) +{ + struct max77541 *max77541 = dev_get_drvdata(dev); + int irq = max77541->i2c->irq; + int ret; + + ret = devm_regmap_add_irq_chip(dev, max77541->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77541_src_irq_chip, + &max77541->irq_data); + if (ret) + return ret; + + ret = devm_regmap_add_irq_chip(dev, max77541->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77541_topsys_irq_chip, + &max77541->irq_topsys); + if (ret) + return ret; + + ret = devm_regmap_add_irq_chip(dev, max77541->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77541_buck_irq_chip, + &max77541->irq_buck); + if (ret) + return ret; + + if (max77541->id == MAX77541) { + ret = devm_regmap_add_irq_chip(dev, max77541->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77541_adc_irq_chip, + &max77541->irq_adc); + if (ret) + return ret; + } + + return 0; +} + +static int max77541_pmic_setup(struct device *dev) +{ + struct max77541 *max77541 = dev_get_drvdata(dev); + const struct mfd_cell *cells; + int n_devs; + int ret; + + switch (max77541->id) { + case MAX77540: + cells = max77540_devs; + n_devs = ARRAY_SIZE(max77540_devs); + break; + case MAX77541: + cells = max77541_devs; + n_devs = ARRAY_SIZE(max77541_devs); + break; + default: + return -EINVAL; + } + + ret = max77541_pmic_irq_init(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize IRQ\n"); + + ret = device_init_wakeup(dev, true); + if (ret) + return dev_err_probe(dev, ret, "Unable to init wakeup\n"); + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + cells, n_devs, NULL, 0, NULL); +} + +static int max77541_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct device *dev = &client->dev; + struct max77541 *max77541; + + max77541 = devm_kzalloc(dev, sizeof(*max77541), GFP_KERNEL); + if (!max77541) + return -ENOMEM; + + i2c_set_clientdata(client, max77541); + max77541->i2c = client; + + max77541->id = (enum max7754x_ids)device_get_match_data(dev); + if (!max77541->id) + max77541->id = (enum max7754x_ids)id->driver_data; + + if (!max77541->id) + return -EINVAL; + + max77541->regmap = devm_regmap_init_i2c(client, + &max77541_regmap_config); + if (IS_ERR(max77541->regmap)) + return dev_err_probe(dev, PTR_ERR(max77541->regmap), + "Failed to allocate register map\n"); + + return max77541_pmic_setup(dev); +} + +static const struct of_device_id max77541_of_id[] = { + { + .compatible = "adi,max77540", + .data = (void *)MAX77540, + }, + { + .compatible = "adi,max77541", + .data = (void *)MAX77541, + }, + { } +}; +MODULE_DEVICE_TABLE(of, max77541_of_id); + +static const struct i2c_device_id max77541_id[] = { + { "max77540", MAX77540 }, + { "max77541", MAX77541 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max77541_id); + +static struct i2c_driver max77541_driver = { + .driver = { + .name = "max77541", + .of_match_table = max77541_of_id, + }, + .probe_new = max77541_probe, + .id_table = max77541_id, +}; +module_i2c_driver(max77541_driver); + +MODULE_DESCRIPTION("MAX7740/MAX7741 Driver"); +MODULE_AUTHOR("Okan Sahin "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max77541.h b/include/linux/mfd/max77541.h new file mode 100644 index 000000000000..fe5c0a3dc637 --- /dev/null +++ b/include/linux/mfd/max77541.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __MFD_MAX77541_H +#define __MFD_MAX77541_H + +#include +#include + +/* REGISTERS */ +#define MAX77541_REG_INT_SRC 0x00 +#define MAX77541_REG_INT_SRC_M 0x01 + +#define MAX77541_BIT_INT_SRC_TOPSYS BIT(0) +#define MAX77541_BIT_INT_SRC_BUCK BIT(1) + +#define MAX77541_REG_TOPSYS_INT 0x02 +#define MAX77541_REG_TOPSYS_INT_M 0x03 + +#define MAX77541_BIT_TOPSYS_INT_TJ_120C BIT(0) +#define MAX77541_BIT_TOPSYS_INT_TJ_140C BIT(1) +#define MAX77541_BIT_TOPSYS_INT_TSHDN BIT(2) +#define MAX77541_BIT_TOPSYS_INT_UVLO BIT(3) +#define MAX77541_BIT_TOPSYS_INT_ALT_SWO BIT(4) +#define MAX77541_BIT_TOPSYS_INT_EXT_FREQ_DET BIT(5) + +/* REGULATORS */ +#define MAX77541_REG_BUCK_INT 0x20 +#define MAX77541_REG_BUCK_INT_M 0x21 + +#define MAX77541_BIT_BUCK_INT_M1_POK_FLT BIT(0) +#define MAX77541_BIT_BUCK_INT_M2_POK_FLT BIT(1) +#define MAX77541_BIT_BUCK_INT_M1_SCFLT BIT(4) +#define MAX77541_BIT_BUCK_INT_M2_SCFLT BIT(5) + +#define MAX77541_REG_EN_CTRL 0x0B + +#define MAX77541_BIT_M1_EN BIT(0) +#define MAX77541_BIT_M2_EN BIT(1) + +#define MAX77541_REG_M1_VOUT 0x23 +#define MAX77541_REG_M2_VOUT 0x33 + +#define MAX77541_BITS_MX_VOUT GENMASK(7, 0) + +#define MAX77541_REG_M1_CFG1 0x25 +#define MAX77541_REG_M2_CFG1 0x35 + +#define MAX77541_BITS_MX_CFG1_RNG GENMASK(7, 6) + +/* ADC */ +#define MAX77541_REG_ADC_INT 0x70 +#define MAX77541_REG_ADC_INT_M 0x71 + +#define MAX77541_BIT_ADC_INT_CH1_I BIT(0) +#define MAX77541_BIT_ADC_INT_CH2_I BIT(1) +#define MAX77541_BIT_ADC_INT_CH3_I BIT(2) +#define MAX77541_BIT_ADC_INT_CH6_I BIT(5) + +#define MAX77541_REG_ADC_DATA_CH1 0x72 +#define MAX77541_REG_ADC_DATA_CH2 0x73 +#define MAX77541_REG_ADC_DATA_CH3 0x74 +#define MAX77541_REG_ADC_DATA_CH6 0x77 + +/* INTERRUPT MASKS*/ +#define MAX77541_REG_INT_SRC_MASK 0x00 +#define MAX77541_REG_TOPSYS_INT_MASK 0x00 +#define MAX77541_REG_BUCK_INT_MASK 0x00 + +#define MAX77541_MAX_REGULATORS 2 + +enum max7754x_ids { + MAX77540 = 1, + MAX77541, +}; + +struct regmap; +struct regmap_irq_chip_data; +struct i2c_client; + +struct max77541 { + struct i2c_client *i2c; + struct regmap *regmap; + enum max7754x_ids id; + + struct regmap_irq_chip_data *irq_data; + struct regmap_irq_chip_data *irq_buck; + struct regmap_irq_chip_data *irq_topsys; + struct regmap_irq_chip_data *irq_adc; +}; + +#endif /* __MFD_MAX77541_H */ -- cgit v1.2.3-59-g8ed1b