diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-11 12:17:45 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-11 12:17:45 -0800 |
commit | fef8dfaea9d6c444b6c2174b3a2b0fca4d226c5e (patch) | |
tree | 082cd90bcaa14a913fefafa83f37657077e73927 /drivers | |
parent | 2d7852c3794085920e50e69bd050881d605bd44d (diff) | |
parent | bfff546aae50ae68ed395bf0e0848188d27b0ba3 (diff) |
Merge tag 'regulator-v5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator
Pull regulator updates from Mark Brown:
"This has been a fairly quiet release for the regulator API, the main
thing has been the addition of helpers for interrupt handling from
Matti Vaittinen.
We do also have support for quite a few new devices.
Summary:
- Helpers for trivial interrupt notifications, making it easier for
drivers to handle error interrupts.
- Support for Dialog DA914x, Maxim MAX2008x, Qualcomm PM8826,
PMG1100, and PM8450 and TI TPS68470"
* tag 'regulator-v5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (30 commits)
regulator: Add MAX20086-MAX20089 driver
dt-bindings: regulators: Add bindings for Maxim MAX20086-MAX20089
regulator: qcom_smd: Align probe function with rpmh-regulator
regulator: remove redundant ret variable
regulator: qcom-labibb: OCP interrupts are not a failure while disabled
regulator: dt-bindings: samsung,s5m8767: Move fixed string BUCK9 to 'properties'
regulator: Introduce tps68470-regulator driver
drivers/regulator: remove redundant ret variable
regulator: fix bullet lists of regulator_ops comment
regulator: Fix type of regulator-coupled-max-spread property
regulator: maxim,max8973: Document interrupts property
regulator: qcom-rpmh: Add support for PM8450 regulators
regulator: qcom,rpmh: Add compatible for PM8450
regulator: da9121: Add DA914x binding info
regulator: da9121: Remove erroneous compatible from binding
regulator: da9121: Add DA914x support
regulator: da9121: Prevent current limit change when enabled
regulator: qcom-rpmh: Add PMG1110 regulators
dt-bindings: regulator: Add compatible for pmg1110
regulator: qcom_spmi: Add pm8226 regulators
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/regulator/Kconfig | 19 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/regulator/bd718x7-regulator.c | 29 | ||||
-rw-r--r-- | drivers/regulator/da9121-regulator.c | 117 | ||||
-rw-r--r-- | drivers/regulator/da9121-regulator.h | 21 | ||||
-rw-r--r-- | drivers/regulator/irq_helpers.c | 41 | ||||
-rw-r--r-- | drivers/regulator/max20086-regulator.c | 332 | ||||
-rw-r--r-- | drivers/regulator/mt6380-regulator.c | 6 | ||||
-rw-r--r-- | drivers/regulator/qcom-labibb-regulator.c | 2 | ||||
-rw-r--r-- | drivers/regulator/qcom-rpmh-regulator.c | 27 | ||||
-rw-r--r-- | drivers/regulator/qcom_smd-regulator.c | 100 | ||||
-rw-r--r-- | drivers/regulator/qcom_spmi-regulator.c | 39 | ||||
-rw-r--r-- | drivers/regulator/rohm-regulator.c | 16 | ||||
-rw-r--r-- | drivers/regulator/tps68470-regulator.c | 201 | ||||
-rw-r--r-- | drivers/regulator/twl-regulator.c | 10 |
15 files changed, 885 insertions, 77 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6be9b1c8a615..1c35fed20d34 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -636,6 +636,15 @@ config REGULATOR_MAX8998 via I2C bus. The provided regulator is suitable for S3C6410 and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages. +config REGULATOR_MAX20086 + tristate "Maxim MAX20086-MAX20089 Camera Power Protectors" + depends on I2C + select REGMAP_I2C + help + This driver controls a Maxim MAX20086-MAX20089 camera power + protectorvia I2C bus. The regulator has 2 or 4 outputs depending on + the device model. This driver is only capable to turn on/off them. + config REGULATOR_MAX77686 tristate "Maxim 77686 regulator" depends on MFD_MAX77686 || COMPILE_TEST @@ -1339,6 +1348,15 @@ config REGULATOR_TPS65912 help This driver supports TPS65912 voltage regulator chip. +config REGULATOR_TPS68470 + tristate "TI TPS68470 PMIC Regulators Driver" + depends on INTEL_SKL_INT3472 || COMPILE_TEST + help + This driver adds support for the TPS68470 PMIC to register + regulators against the usual framework. + + The module will be called "tps68470-regulator". + config REGULATOR_TWL4030 tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC" depends on TWL4030_CORE @@ -1415,4 +1433,3 @@ config REGULATOR_QCOM_LABIBB for LCD display panel. endif - diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index b07d2a22df0b..2e1b087489fa 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997-regulator.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o +obj-$(CONFIG_REGULATOR_MAX20086) += max20086-regulator.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o @@ -159,6 +160,7 @@ obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o +obj-$(CONFIG_REGULATOR_TPS68470) += tps68470-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o obj-$(CONFIG_REGULATOR_UNIPHIER) += uniphier-regulator.o obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index d60fccedb250..00efb18a836c 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -125,27 +125,6 @@ static int bd71837_get_buck34_enable_hwctrl(struct regulator_dev *rdev) return !!(BD718XX_BUCK_RUN_ON & val); } -/* - * On BD71837 (not on BD71847, BD71850, ...) - * Bucks 1 to 4 support DVS. PWM mode is used when voltage is changed. - * Bucks 5 to 8 and LDOs can use PFM and must be disabled when voltage - * is changed. Hence we return -EBUSY for these if voltage is changed - * when BUCK/LDO is enabled. - * - * On BD71847, BD71850, ... The LDO voltage can be changed when LDO is - * enabled. But if voltage is increased the LDO power-good monitoring - * must be disabled for the duration of changing + 1mS to ensure voltage - * has reached the higher level before HW does next under voltage detection - * cycle. - */ -static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev, - unsigned int sel) -{ - if (rdev->desc->ops->is_enabled(rdev)) - return -EBUSY; - - return regulator_set_voltage_sel_regmap(rdev, sel); -} static void voltage_change_done(struct regulator_dev *rdev, unsigned int sel, unsigned int *mask) @@ -642,22 +621,22 @@ BD718XX_OPS(bd71837_pickable_range_buck_ops, bd718x7_set_buck_ovp); BD718XX_OPS(bd71837_ldo_regulator_ops, regulator_list_voltage_linear_range, - NULL, bd71837_set_voltage_sel_restricted, + NULL, rohm_regulator_set_voltage_sel_restricted, regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp, NULL); BD718XX_OPS(bd71837_ldo_regulator_nolinear_ops, regulator_list_voltage_table, - NULL, bd71837_set_voltage_sel_restricted, + NULL, rohm_regulator_set_voltage_sel_restricted, regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp, NULL); BD718XX_OPS(bd71837_buck_regulator_ops, regulator_list_voltage_linear_range, - NULL, bd71837_set_voltage_sel_restricted, + NULL, rohm_regulator_set_voltage_sel_restricted, regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp); BD718XX_OPS(bd71837_buck_regulator_nolinear_ops, regulator_list_voltage_table, - regulator_map_voltage_ascend, bd71837_set_voltage_sel_restricted, + regulator_map_voltage_ascend, rohm_regulator_set_voltage_sel_restricted, regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel, NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp); /* diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index e66925090258..6f21223a488e 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -86,6 +86,22 @@ static struct da9121_range da9121_3A_1phase_current = { .reg_max = 6, }; +static struct da9121_range da914x_40A_4phase_current = { + .val_min = 14000000, + .val_max = 80000000, + .val_stp = 2000000, + .reg_min = 1, + .reg_max = 14, +}; + +static struct da9121_range da914x_20A_2phase_current = { + .val_min = 7000000, + .val_max = 40000000, + .val_stp = 2000000, + .reg_min = 1, + .reg_max = 14, +}; + struct da9121_variant_info { int num_bucks; int num_phases; @@ -97,6 +113,8 @@ static const struct da9121_variant_info variant_parameters[] = { { 2, 1, &da9121_3A_1phase_current }, //DA9121_TYPE_DA9220_DA9132 { 2, 1, &da9121_5A_1phase_current }, //DA9121_TYPE_DA9122_DA9131 { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217 + { 1, 4, &da914x_40A_4phase_current }, //DA9121_TYPE_DA9141 + { 1, 2, &da914x_20A_2phase_current }, //DA9121_TYPE_DA9142 }; struct da9121_field { @@ -253,6 +271,11 @@ static int da9121_set_current_limit(struct regulator_dev *rdev, goto error; } + if (rdev->desc->ops->is_enabled(rdev)) { + ret = -EBUSY; + goto error; + } + ret = da9121_ceiling_selector(rdev, min_ua, max_ua, &sel); if (ret < 0) goto error; @@ -537,11 +560,65 @@ static const struct regulator_desc da9217_reg = { .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, }; +#define DA914X_MIN_MV 500 +#define DA914X_MAX_MV 1000 +#define DA914X_STEP_MV 10 +#define DA914X_MIN_SEL (DA914X_MIN_MV / DA914X_STEP_MV) +#define DA914X_N_VOLTAGES (((DA914X_MAX_MV - DA914X_MIN_MV) / DA914X_STEP_MV) \ + + 1 + DA914X_MIN_SEL) + +static const struct regulator_desc da9141_reg = { + .id = DA9121_IDX_BUCK1, + .name = "DA9141", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA914X_N_VOLTAGES, + .min_uV = DA914X_MIN_MV * 1000, + .uV_step = DA914X_STEP_MV * 1000, + .linear_min_sel = DA914X_MIN_SEL, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + /* Default value of BUCK_BUCK1_0.CH1_SRC_DVC_UP */ + .ramp_delay = 20000, + /* tBUCK_EN */ + .enable_time = 20, +}; + +static const struct regulator_desc da9142_reg = { + .id = DA9121_IDX_BUCK1, + .name = "DA9142 BUCK1", + .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA914X_N_VOLTAGES, + .min_uV = DA914X_MIN_MV * 1000, + .uV_step = DA914X_STEP_MV * 1000, + .linear_min_sel = DA914X_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, +}; + + static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = { [DA9121_TYPE_DA9121_DA9130] = { &da9121_reg, NULL }, [DA9121_TYPE_DA9220_DA9132] = { &da9220_reg[0], &da9220_reg[1] }, [DA9121_TYPE_DA9122_DA9131] = { &da9122_reg[0], &da9122_reg[1] }, [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, + [DA9121_TYPE_DA9141] = { &da9141_reg, NULL }, + [DA9121_TYPE_DA9142] = { &da9142_reg, NULL }, }; static void da9121_status_poll_on(struct work_struct *work) @@ -835,7 +912,7 @@ static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) goto error; } - if (device_id != DA9121_DEVICE_ID) { + if ((device_id != DA9121_DEVICE_ID) && (device_id != DA914x_DEVICE_ID)) { dev_err(chip->dev, "Invalid device ID: 0x%02x\n", device_id); ret = -ENODEV; goto error; @@ -877,6 +954,22 @@ static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) break; } + if (device_id == DA914x_DEVICE_ID) { + switch (chip->subvariant_id) { + case DA9121_SUBTYPE_DA9141: + type = "DA9141"; + config_match = (variant_vrc == DA9141_VARIANT_VRC); + break; + case DA9121_SUBTYPE_DA9142: + type = "DA9142"; + config_match = (variant_vrc == DA9142_VARIANT_VRC); + break; + default: + type = "Unknown"; + break; + } + } + dev_info(chip->dev, "Device detected (device-ID: 0x%02X, var-ID: 0x%02X, %s)\n", device_id, variant_id, type); @@ -890,8 +983,10 @@ static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) variant_mrc = (variant_id & DA9121_MASK_OTP_VARIANT_ID_MRC) >> DA9121_SHIFT_OTP_VARIANT_ID_MRC; - if ((device_id == DA9121_DEVICE_ID) && - (variant_mrc < DA9121_VARIANT_MRC_BASE)) { + if (((device_id == DA9121_DEVICE_ID) && + (variant_mrc < DA9121_VARIANT_MRC_BASE)) || + ((device_id == DA914x_DEVICE_ID) && + (variant_mrc != DA914x_VARIANT_MRC_BASE))) { dev_err(chip->dev, "Cannot support variant MRC: 0x%02X\n", variant_mrc); ret = -EINVAL; @@ -931,6 +1026,14 @@ static int da9121_assign_chip_model(struct i2c_client *i2c, chip->variant_id = DA9121_TYPE_DA9220_DA9132; regmap = &da9121_2ch_regmap_config; break; + case DA9121_SUBTYPE_DA9141: + chip->variant_id = DA9121_TYPE_DA9141; + regmap = &da9121_1ch_regmap_config; + break; + case DA9121_SUBTYPE_DA9142: + chip->variant_id = DA9121_TYPE_DA9142; + regmap = &da9121_2ch_regmap_config; + break; } /* Set these up for of_regulator_match call which may want .of_map_modes */ @@ -1010,6 +1113,8 @@ static const struct of_device_id da9121_dt_ids[] = { { .compatible = "dlg,da9131", .data = (void *) DA9121_SUBTYPE_DA9131 }, { .compatible = "dlg,da9220", .data = (void *) DA9121_SUBTYPE_DA9220 }, { .compatible = "dlg,da9132", .data = (void *) DA9121_SUBTYPE_DA9132 }, + { .compatible = "dlg,da9141", .data = (void *) DA9121_SUBTYPE_DA9141 }, + { .compatible = "dlg,da9142", .data = (void *) DA9121_SUBTYPE_DA9142 }, { } }; MODULE_DEVICE_TABLE(of, da9121_dt_ids); @@ -1065,7 +1170,7 @@ static int da9121_i2c_remove(struct i2c_client *i2c) { struct da9121 *chip = i2c_get_clientdata(i2c); const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; - int ret = 0; + int ret; free_irq(chip->chip_irq, chip); cancel_delayed_work_sync(&chip->work); @@ -1073,7 +1178,7 @@ static int da9121_i2c_remove(struct i2c_client *i2c) ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); if (ret != 0) dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); - return ret; + return 0; } static const struct i2c_device_id da9121_i2c_id[] = { @@ -1084,6 +1189,8 @@ static const struct i2c_device_id da9121_i2c_id[] = { {"da9131", DA9121_TYPE_DA9122_DA9131}, {"da9220", DA9121_TYPE_DA9220_DA9132}, {"da9132", DA9121_TYPE_DA9220_DA9132}, + {"da9141", DA9121_TYPE_DA9141}, + {"da9142", DA9121_TYPE_DA9142}, {}, }; MODULE_DEVICE_TABLE(i2c, da9121_i2c_id); diff --git a/drivers/regulator/da9121-regulator.h b/drivers/regulator/da9121-regulator.h index 357f416e17c1..a328a0bdfa29 100644 --- a/drivers/regulator/da9121-regulator.h +++ b/drivers/regulator/da9121-regulator.h @@ -26,7 +26,9 @@ enum da9121_variant { DA9121_TYPE_DA9121_DA9130, DA9121_TYPE_DA9220_DA9132, DA9121_TYPE_DA9122_DA9131, - DA9121_TYPE_DA9217 + DA9121_TYPE_DA9217, + DA9121_TYPE_DA9141, + DA9121_TYPE_DA9142 }; enum da9121_subvariant { @@ -36,7 +38,9 @@ enum da9121_subvariant { DA9121_SUBTYPE_DA9132, DA9121_SUBTYPE_DA9122, DA9121_SUBTYPE_DA9131, - DA9121_SUBTYPE_DA9217 + DA9121_SUBTYPE_DA9217, + DA9121_SUBTYPE_DA9141, + DA9121_SUBTYPE_DA9142 }; /* Minimum, maximum and default polling millisecond periods are provided @@ -70,6 +74,14 @@ enum da9121_subvariant { #define DA9121_REG_SYS_GPIO1_1 0x13 #define DA9121_REG_SYS_GPIO2_0 0x14 #define DA9121_REG_SYS_GPIO2_1 0x15 +#define DA914x_REG_SYS_GPIO3_0 0x16 +#define DA914x_REG_SYS_GPIO3_1 0x17 +#define DA914x_REG_SYS_GPIO4_0 0x18 +#define DA914x_REG_SYS_GPIO4_1 0x19 +#define DA914x_REG_SYS_ADMUX1_0 0x1A +#define DA914x_REG_SYS_ADMUX1_1 0x1B +#define DA914x_REG_SYS_ADMUX2_0 0x1C +#define DA914x_REG_SYS_ADMUX2_1 0x1D #define DA9121_REG_BUCK_BUCK1_0 0x20 #define DA9121_REG_BUCK_BUCK1_1 0x21 #define DA9121_REG_BUCK_BUCK1_2 0x22 @@ -276,6 +288,7 @@ enum da9121_subvariant { #define DA9121_MASK_OTP_DEVICE_ID_DEV_ID 0xFF #define DA9121_DEVICE_ID 0x05 +#define DA914x_DEVICE_ID 0x26 /* DA9121_REG_OTP_VARIANT_ID */ @@ -293,6 +306,10 @@ enum da9121_subvariant { #define DA9131_VARIANT_VRC 0x1 #define DA9132_VARIANT_VRC 0x2 +#define DA914x_VARIANT_MRC_BASE 0x0 +#define DA9141_VARIANT_VRC 0x1 +#define DA9142_VARIANT_VRC 0x2 + /* DA9121_REG_OTP_CUSTOMER_ID */ #define DA9121_MASK_OTP_CUSTOMER_ID_CUST_ID 0xFF diff --git a/drivers/regulator/irq_helpers.c b/drivers/regulator/irq_helpers.c index 522764435575..fe7ae0f3f46a 100644 --- a/drivers/regulator/irq_helpers.c +++ b/drivers/regulator/irq_helpers.c @@ -320,7 +320,9 @@ static void init_rdev_errors(struct regulator_irq *h) * IRQF_ONESHOT when requesting the (threaded) irq. * @common_errs: Errors which can be flagged by this IRQ for all rdevs. * When IRQ is re-enabled these errors will be cleared - * from all associated regulators + * from all associated regulators. Use this instead of the + * per_rdev_errs if you use + * regulator_irq_map_event_simple() for event mapping. * @per_rdev_errs: Optional error flag array describing errors specific * for only some of the regulators. These errors will be * or'ed with common errors. If this is given the array @@ -395,3 +397,40 @@ void regulator_irq_helper_cancel(void **handle) } } EXPORT_SYMBOL_GPL(regulator_irq_helper_cancel); + +/** + * regulator_irq_map_event_simple - regulator IRQ notification for trivial IRQs + * + * @irq: Number of IRQ that occurred + * @rid: Information about the event IRQ indicates + * @dev_mask: mask indicating the regulator originating the IRQ + * + * Regulators whose IRQ has single, well defined purpose (always indicate + * exactly one event, and are relevant to exactly one regulator device) can + * use this function as their map_event callbac for their regulator IRQ + * notification helperk. Exactly one rdev and exactly one error (in + * "common_errs"-field) can be given at IRQ helper registration for + * regulator_irq_map_event_simple() to be viable. + */ +int regulator_irq_map_event_simple(int irq, struct regulator_irq_data *rid, + unsigned long *dev_mask) +{ + int err = rid->states[0].possible_errs; + + *dev_mask = 1; + /* + * This helper should only be used in a situation where the IRQ + * can indicate only one type of problem for one specific rdev. + * Something fishy is going on if we are having multiple rdevs or ERROR + * flags here. + */ + if (WARN_ON(rid->num_states != 1 || hweight32(err) != 1)) + return 0; + + rid->states[0].errors = err; + rid->states[0].notifs = regulator_err2notif(err); + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_irq_map_event_simple); + diff --git a/drivers/regulator/max20086-regulator.c b/drivers/regulator/max20086-regulator.c new file mode 100644 index 000000000000..fbc56b043071 --- /dev/null +++ b/drivers/regulator/max20086-regulator.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// max20086-regulator.c - MAX20086-MAX20089 camera power protector driver +// +// Copyright (C) 2022 Laurent Pinchart <laurent.pinchart@idesonboard.com> +// Copyright (C) 2018 Avnet, Inc. + +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/slab.h> + +/* Register Offset */ +#define MAX20086_REG_MASK 0x00 +#define MAX20086_REG_CONFIG 0x01 +#define MAX20086_REG_ID 0x02 +#define MAX20086_REG_STAT1 0x03 +#define MAX20086_REG_STAT2_L 0x04 +#define MAX20086_REG_STAT2_H 0x05 +#define MAX20086_REG_ADC1 0x06 +#define MAX20086_REG_ADC2 0x07 +#define MAX20086_REG_ADC3 0x08 +#define MAX20086_REG_ADC4 0x09 + +/* DEVICE IDs */ +#define MAX20086_DEVICE_ID_MAX20086 0x40 +#define MAX20086_DEVICE_ID_MAX20087 0x20 +#define MAX20086_DEVICE_ID_MAX20088 0x10 +#define MAX20086_DEVICE_ID_MAX20089 0x00 +#define DEVICE_ID_MASK 0xf0 + +/* Register bits */ +#define MAX20086_EN_MASK 0x0f +#define MAX20086_EN_OUT1 0x01 +#define MAX20086_EN_OUT2 0x02 +#define MAX20086_EN_OUT3 0x04 +#define MAX20086_EN_OUT4 0x08 +#define MAX20086_INT_DISABLE_ALL 0x3f + +#define MAX20086_MAX_REGULATORS 4 + +struct max20086_chip_info { + u8 id; + unsigned int num_outputs; +}; + +struct max20086_regulator { + struct device_node *of_node; + struct regulator_init_data *init_data; + const struct regulator_desc *desc; + struct regulator_dev *rdev; +}; + +struct max20086 { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *ena_gpiod; + + const struct max20086_chip_info *info; + + struct max20086_regulator regulators[MAX20086_MAX_REGULATORS]; +}; + +static const struct regulator_ops max20086_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +#define MAX20086_REGULATOR_DESC(n) \ +{ \ + .name = "OUT"#n, \ + .supply_name = "in", \ + .id = (n) - 1, \ + .ops = &max20086_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .enable_reg = MAX20086_REG_CONFIG, \ + .enable_mask = 1 << ((n) - 1), \ + .enable_val = 1 << ((n) - 1), \ + .disable_val = 0, \ +} + +static const char * const max20086_output_names[] = { + "OUT1", + "OUT2", + "OUT3", + "OUT4", +}; + +static const struct regulator_desc max20086_regulators[] = { + MAX20086_REGULATOR_DESC(1), + MAX20086_REGULATOR_DESC(2), + MAX20086_REGULATOR_DESC(3), + MAX20086_REGULATOR_DESC(4), +}; + +static int max20086_regulators_register(struct max20086 *chip) +{ + unsigned int i; + + for (i = 0; i < chip->info->num_outputs; i++) { + struct max20086_regulator *reg = &chip->regulators[i]; + struct regulator_config config = { }; + struct regulator_dev *rdev; + + config.dev = chip->dev; + config.init_data = reg->init_data; + config.driver_data = chip; + config.of_node = reg->of_node; + config.regmap = chip->regmap; + config.ena_gpiod = chip->ena_gpiod; + + rdev = devm_regulator_register(chip->dev, reg->desc, &config); + if (IS_ERR(rdev)) { + dev_err(chip->dev, + "Failed to register regulator output %s\n", + reg->desc->name); + return PTR_ERR(rdev); + } + + reg->rdev = rdev; + } + + return 0; +} + +static int max20086_parse_regulators_dt(struct max20086 *chip, bool *boot_on) +{ + struct of_regulator_match matches[MAX20086_MAX_REGULATORS] = { }; + struct device_node *node; + unsigned int i; + int ret; + + node = of_get_child_by_name(chip->dev->of_node, "regulators"); + if (!node) { + dev_err(chip->dev, "regulators node not found\n"); + return PTR_ERR(node); + } + + for (i = 0; i < chip->info->num_outputs; ++i) + matches[i].name = max20086_output_names[i]; + + ret = of_regulator_match(chip->dev, node, matches, + chip->info->num_outputs); + of_node_put(node); + if (ret < 0) { + dev_err(chip->dev, "Failed to match regulators\n"); + return -EINVAL; + } + + *boot_on = false; + + for (i = 0; i < chip->info->num_outputs; i++) { + struct max20086_regulator *reg = &chip->regulators[i]; + + reg->init_data = matches[i].init_data; + reg->of_node = matches[i].of_node; + reg->desc = &max20086_regulators[i]; + + if (reg->init_data) { + if (reg->init_data->constraints.always_on || + reg->init_data->constraints.boot_on) + *boot_on = true; + } + } + + return 0; +} + +static int max20086_detect(struct max20086 *chip) +{ + unsigned int data; + int ret; + + ret = regmap_read(chip->regmap, MAX20086_REG_ID, &data); + if (ret < 0) { + dev_err(chip->dev, "Failed to read DEVICE_ID reg: %d\n", ret); + return ret; + } + + if ((data & DEVICE_ID_MASK) != chip->info->id) { + dev_err(chip->dev, "Invalid device ID 0x%02x\n", data); + return -ENXIO; + } + + return 0; +} + +static bool max20086_gen_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX20086_REG_MASK: + case MAX20086_REG_CONFIG: + return true; + default: + return false; + } +} + +static const struct regmap_config max20086_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = max20086_gen_is_writeable_reg, + .max_register = 0x9, + .cache_type = REGCACHE_NONE, +}; + +static int max20086_i2c_probe(struct i2c_client *i2c) +{ + struct max20086 *chip; + enum gpiod_flags flags; + bool boot_on; + int ret; + + chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &i2c->dev; + chip->info = device_get_match_data(chip->dev); + + i2c_set_clientdata(i2c, chip); + + chip->regmap = devm_regmap_init_i2c(i2c, &max20086_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(chip->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + ret = max20086_parse_regulators_dt(chip, &boot_on); + if (ret < 0) + return ret; + + ret = max20086_detect(chip); + if (ret < 0) + return ret; + + /* Until IRQ support is added, just disable all interrupts. */ + ret = regmap_update_bits(chip->regmap, MAX20086_REG_MASK, + MAX20086_INT_DISABLE_ALL, + MAX20086_INT_DISABLE_ALL); + if (ret < 0) { + dev_err(chip->dev, "Failed to disable interrupts: %d\n", ret); + return ret; + } + + /* + * Get the enable GPIO. If any of the outputs is marked as being + * enabled at boot, request the GPIO with an initial high state to + * avoid disabling outputs that may have been turned on by the boot + * loader. Otherwise, request it with a low state to enter lower-power + * shutdown. + */ + flags = boot_on ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + chip->ena_gpiod = devm_gpiod_get(chip->dev, "enable", flags); + if (IS_ERR(chip->ena_gpiod)) { + ret = PTR_ERR(chip->ena_gpiod); + dev_err(chip->dev, "Failed to get enable GPIO: %d\n", ret); + return ret; + } + + ret = max20086_regulators_register(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to register regulators: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id max20086_i2c_id[] = { + { "max20086" }, + { "max20087" }, + { "max20088" }, + { "max20089" }, + { /* Sentinel */ }, +}; + +MODULE_DEVICE_TABLE(i2c, max20086_i2c_id); + +static const struct of_device_id max20086_dt_ids[] = { + { + .compatible = "maxim,max20086", + .data = &(const struct max20086_chip_info) { + .id = MAX20086_DEVICE_ID_MAX20086, + .num_outputs = 4, + } + }, { + .compatible = "maxim,max20087", + .data = &(const struct max20086_chip_info) { + .id = MAX20086_DEVICE_ID_MAX20087, + .num_outputs = 4, + } + }, { + .compatible = "maxim,max20088", + .data = &(const struct max20086_chip_info) { + .id = MAX20086_DEVICE_ID_MAX20088, + .num_outputs = 2, + } + }, { + .compatible = "maxim,max20089", + .data = &(const struct max20086_chip_info) { + .id = MAX20086_DEVICE_ID_MAX20089, + .num_outputs = 2, + } + }, + { /* Sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, max20086_dt_ids); + +static struct i2c_driver max20086_regulator_driver = { + .driver = { + .name = "max20086", + .of_match_table = of_match_ptr(max20086_dt_ids), + }, + .probe_new = max20086_i2c_probe, + .id_table = max20086_i2c_id, +}; + +module_i2c_driver(max20086_regulator_driver); + +MODULE_AUTHOR("Watson Chow <watson.chow@avnet.com>"); +MODULE_DESCRIPTION("MAX20086-MAX20089 Camera Power Protector Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/mt6380-regulator.c b/drivers/regulator/mt6380-regulator.c index 9efd8710a6f3..2e6b61d3b0cf 100644 --- a/drivers/regulator/mt6380-regulator.c +++ b/drivers/regulator/mt6380-regulator.c @@ -183,7 +183,7 @@ static const unsigned int ldo_volt_table4[] = { static int mt6380_regulator_set_mode(struct regulator_dev *rdev, unsigned int mode) { - int ret, val = 0; + int val = 0; struct mt6380_regulator_info *info = rdev_get_drvdata(rdev); switch (mode) { @@ -199,10 +199,8 @@ static int mt6380_regulator_set_mode(struct regulator_dev *rdev, val <<= ffs(info->modeset_mask) - 1; - ret = regmap_update_bits(rdev->regmap, info->modeset_reg, + return regmap_update_bits(rdev->regmap, info->modeset_reg, info->modeset_mask, val); - - return ret; } static unsigned int mt6380_regulator_get_mode(struct regulator_dev *rdev) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index b3da0dc58782..639b71eb41ff 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -260,7 +260,7 @@ static irqreturn_t qcom_labibb_ocp_isr(int irq, void *chip) /* If the regulator is not enabled, this is a fake event */ if (!ops->is_enabled(vreg->rdev)) - return 0; + return IRQ_HANDLED; /* If we tried to recover for too many times it's not getting better */ if (vreg->ocp_irq_count > LABIBB_MAX_OCP_COUNT) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 12425f667c00..a3bc0eb6ceb8 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -814,6 +814,11 @@ static const struct rpmh_vreg_init_data pm8998_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pmg1110_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + {} +}; + static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = { RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"), {} @@ -969,6 +974,20 @@ static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pm8450_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps520, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps520, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps520, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps520, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps520, "vdd-s6"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo_lv, "vdd-l4"), + {} +}; + static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), @@ -1214,10 +1233,18 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .data = pm8350c_vreg_data, }, { + .compatible = "qcom,pm8450-rpmh-regulators", + .data = pm8450_vreg_data, + }, + { .compatible = "qcom,pm8998-rpmh-regulators", .data = pm8998_vreg_data, }, { + .compatible = "qcom,pmg1110-rpmh-regulators", + .data = pmg1110_vreg_data, + }, + { .compatible = "qcom,pmi8998-rpmh-regulators", .data = pmi8998_vreg_data, }, diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index 8bac024dde8b..9fc666107a06 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -9,6 +9,7 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> #include <linux/soc/qcom/smd-rpm.h> struct qcom_rpm_reg { @@ -1239,52 +1240,91 @@ static const struct of_device_id rpm_of_match[] = { }; MODULE_DEVICE_TABLE(of, rpm_of_match); -static int rpm_reg_probe(struct platform_device *pdev) +/** + * rpm_regulator_init_vreg() - initialize all attributes of a qcom_smd-regulator + * @vreg: Pointer to the individual qcom_smd-regulator resource + * @dev: Pointer to the top level qcom_smd-regulator PMIC device + * @node: Pointer to the individual qcom_smd-regulator resource + * device node + * @rpm: Pointer to the rpm bus node + * @pmic_rpm_data: Pointer to a null-terminated array of qcom_smd-regulator + * resources defined for the top level PMIC device + * + * Return: 0 on success, errno on failure + */ +static int rpm_regulator_init_vreg(struct qcom_rpm_reg *vreg, struct device *dev, + struct device_node *node, struct qcom_smd_rpm *rpm, + const struct rpm_regulator_data *pmic_rpm_data) { - const struct rpm_regulator_data *reg; - const struct of_device_id *match; - struct regulator_config config = { }; + struct regulator_config config = {}; + const struct rpm_regulator_data *rpm_data; struct regulator_dev *rdev; + int ret; + + for (rpm_data = pmic_rpm_data; rpm_data->name; rpm_data++) + if (of_node_name_eq(node, rpm_data->name)) + break; + + if (!rpm_data->name) { + dev_err(dev, "Unknown regulator %pOFn\n", node); + return -EINVAL; + } + + vreg->dev = dev; + vreg->rpm = rpm; + vreg->type = rpm_data->type; + vreg->id = rpm_data->id; + + memcpy(&vreg->desc, rpm_data->desc, sizeof(vreg->desc)); + vreg->desc.name = rpm_data->name; + vreg->desc.supply_name = rpm_data->supply; + vreg->desc.owner = THIS_MODULE; + vreg->desc.type = REGULATOR_VOLTAGE; + vreg->desc.of_match = rpm_data->name; + + config.dev = dev; + config.of_node = node; + config.driver_data = vreg; + + rdev = devm_regulator_register(dev, &vreg->desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "%pOFn: devm_regulator_register() failed, ret=%d\n", node, ret); + return ret; + } + + return 0; +} + +static int rpm_reg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct rpm_regulator_data *vreg_data; + struct device_node *node; struct qcom_rpm_reg *vreg; struct qcom_smd_rpm *rpm; + int ret; rpm = dev_get_drvdata(pdev->dev.parent); if (!rpm) { - dev_err(&pdev->dev, "unable to retrieve handle to rpm\n"); + dev_err(&pdev->dev, "Unable to retrieve handle to rpm\n"); return -ENODEV; } - match = of_match_device(rpm_of_match, &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "failed to match device\n"); + vreg_data = of_device_get_match_data(dev); + if (!vreg_data) return -ENODEV; - } - for (reg = match->data; reg->name; reg++) { + for_each_available_child_of_node(dev->of_node, node) { vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); if (!vreg) return -ENOMEM; - vreg->dev = &pdev->dev; - vreg->type = reg->type; - vreg->id = reg->id; - vreg->rpm = rpm; - - memcpy(&vreg->desc, reg->desc, sizeof(vreg->desc)); - - vreg->desc.id = -1; - vreg->desc.owner = THIS_MODULE; - vreg->desc.type = REGULATOR_VOLTAGE; - vreg->desc.name = reg->name; - vreg->desc.supply_name = reg->supply; - vreg->desc.of_match = reg->name; - - config.dev = &pdev->dev; - config.driver_data = vreg; - rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config); - if (IS_ERR(rdev)) { - dev_err(&pdev->dev, "failed to register %s\n", reg->name); - return PTR_ERR(rdev); + ret = rpm_regulator_init_vreg(vreg, dev, node, rpm, vreg_data); + + if (ret < 0) { + of_node_put(node); + return ret; } } diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 41424a3366d0..02bfce981150 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -1895,6 +1895,44 @@ static const struct spmi_regulator_data pm8941_regulators[] = { { } }; +static const struct spmi_regulator_data pm8226_regulators[] = { + { "s1", 0x1400, "vdd_s1", }, + { "s2", 0x1700, "vdd_s2", }, + { "s3", 0x1a00, "vdd_s3", }, + { "s4", 0x1d00, "vdd_s4", }, + { "s5", 0x2000, "vdd_s5", }, + { "l1", 0x4000, "vdd_l1_l2_l4_l5", }, + { "l2", 0x4100, "vdd_l1_l2_l4_l5", }, + { "l3", 0x4200, "vdd_l3_l24_l26", }, + { "l4", 0x4300, "vdd_l1_l2_l4_l5", }, + { "l5", 0x4400, "vdd_l1_l2_l4_l5", }, + { "l6", 0x4500, "vdd_l6_l7_l8_l9_l27", }, + { "l7", 0x4600, "vdd_l6_l7_l8_l9_l27", }, + { "l8", 0x4700, "vdd_l6_l7_l8_l9_l27", }, + { "l9", 0x4800, "vdd_l6_l7_l8_l9_l27", }, + { "l10", 0x4900, "vdd_l10_l11_l13", }, + { "l11", 0x4a00, "vdd_l10_l11_l13", }, + { "l12", 0x4b00, "vdd_l12_l14", }, + { "l13", 0x4c00, "vdd_l10_l11_l13", }, + { "l14", 0x4d00, "vdd_l12_l14", }, + { "l15", 0x4e00, "vdd_l15_l16_l17_l18", }, + { "l16", 0x4f00, "vdd_l15_l16_l17_l18", }, + { "l17", 0x5000, "vdd_l15_l16_l17_l18", }, + { "l18", 0x5100, "vdd_l15_l16_l17_l18", }, + { "l19", 0x5200, "vdd_l19_l20_l21_l22_l23_l28", }, + { "l20", 0x5300, "vdd_l19_l20_l21_l22_l23_l28", }, + { "l21", 0x5400, "vdd_l19_l20_l21_l22_l23_l28", }, + { "l22", 0x5500, "vdd_l19_l20_l21_l22_l23_l28", }, + { "l23", 0x5600, "vdd_l19_l20_l21_l22_l23_l28", }, + { "l24", 0x5700, "vdd_l3_l24_l26", }, + { "l25", 0x5800, "vdd_l25", }, + { "l26", 0x5900, "vdd_l3_l24_l26", }, + { "l27", 0x5a00, "vdd_l6_l7_l8_l9_l27", }, + { "l28", 0x5b00, "vdd_l19_l20_l21_l22_l23_l28", }, + { "lvs1", 0x8000, "vdd_lvs1", }, + { } +}; + static const struct spmi_regulator_data pm8841_regulators[] = { { "s1", 0x1400, "vdd_s1", }, { "s2", 0x1700, "vdd_s2", NULL, 0x1c08 }, @@ -2095,6 +2133,7 @@ static const struct spmi_regulator_data pms405_regulators[] = { static const struct of_device_id qcom_spmi_regulator_match[] = { { .compatible = "qcom,pm8004-regulators", .data = &pm8004_regulators }, { .compatible = "qcom,pm8005-regulators", .data = &pm8005_regulators }, + { .compatible = "qcom,pm8226-regulators", .data = &pm8226_regulators }, { .compatible = "qcom,pm8841-regulators", .data = &pm8841_regulators }, { .compatible = "qcom,pm8916-regulators", .data = &pm8916_regulators }, { .compatible = "qcom,pm8941-regulators", .data = &pm8941_regulators }, diff --git a/drivers/regulator/rohm-regulator.c b/drivers/regulator/rohm-regulator.c index 6e0d9c08ec1c..f97a9a51ee76 100644 --- a/drivers/regulator/rohm-regulator.c +++ b/drivers/regulator/rohm-regulator.c @@ -112,6 +112,22 @@ int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs, } EXPORT_SYMBOL(rohm_regulator_set_dvs_levels); +/* + * Few ROHM PMIC ICs have constrains on voltage changing: + * BD71837 - only buck 1-4 voltages can be changed when they are enabled. + * Other bucks and all LDOs must be disabled when voltage is changed. + * BD96801 - LDO voltage levels can be changed when LDOs are disabled. + */ +int rohm_regulator_set_voltage_sel_restricted(struct regulator_dev *rdev, + unsigned int sel) +{ + if (rdev->desc->ops->is_enabled(rdev)) + return -EBUSY; + + return regulator_set_voltage_sel_regmap(rdev, sel); +} +EXPORT_SYMBOL_GPL(rohm_regulator_set_voltage_sel_restricted); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers"); diff --git a/drivers/regulator/tps68470-regulator.c b/drivers/regulator/tps68470-regulator.c new file mode 100644 index 000000000000..4bca7c4128ab --- /dev/null +++ b/drivers/regulator/tps68470-regulator.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Regulator driver for TPS68470 PMIC +// +// Copyright (c) 2021 Red Hat Inc. +// Copyright (C) 2018 Intel Corporation +// +// Authors: +// Hans de Goede <hdegoede@redhat.com> +// Zaikuo Wang <zaikuo.wang@intel.com> +// Tianshu Qiu <tian.shu.qiu@intel.com> +// Jian Xu Zheng <jian.xu.zheng@intel.com> +// Yuning Pu <yuning.pu@intel.com> +// Rajmohan Mani <rajmohan.mani@intel.com> + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mfd/tps68470.h> +#include <linux/module.h> +#include <linux/platform_data/tps68470.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +struct tps68470_regulator_data { + struct clk *clk; +}; + +#define TPS68470_REGULATOR(_name, _id, _ops, _n, \ + _vr, _vm, _er, _em, _lr, _nlr) \ + [TPS68470_ ## _name] = { \ + .name = # _name, \ + .id = _id, \ + .ops = &_ops, \ + .n_voltages = _n, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .enable_reg = _er, \ + .enable_mask = _em, \ + .linear_ranges = _lr, \ + .n_linear_ranges = _nlr, \ + } + +static const struct linear_range tps68470_ldo_ranges[] = { + REGULATOR_LINEAR_RANGE(875000, 0, 125, 17800), +}; + +static const struct linear_range tps68470_core_ranges[] = { + REGULATOR_LINEAR_RANGE(900000, 0, 42, 25000), +}; + +static int tps68470_regulator_enable(struct regulator_dev *rdev) +{ + struct tps68470_regulator_data *data = rdev->reg_data; + int ret; + + /* The Core buck regulator needs the PMIC's PLL to be enabled */ + if (rdev->desc->id == TPS68470_CORE) { + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&rdev->dev, "Error enabling TPS68470 clock\n"); + return ret; + } + } + + return regulator_enable_regmap(rdev); +} + +static int tps68470_regulator_disable(struct regulator_dev *rdev) +{ + struct tps68470_regulator_data *data = rdev->reg_data; + + if (rdev->desc->id == TPS68470_CORE) + clk_disable_unprepare(data->clk); + + return regulator_disable_regmap(rdev); +} + +/* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */ +static const struct regulator_ops tps68470_regulator_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = tps68470_regulator_enable, + .disable = tps68470_regulator_disable, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, +}; + +static const struct regulator_ops tps68470_always_on_reg_ops = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, +}; + +static const struct regulator_desc regulators[] = { + TPS68470_REGULATOR(CORE, TPS68470_CORE, tps68470_regulator_ops, 43, + TPS68470_REG_VDVAL, TPS68470_VDVAL_DVOLT_MASK, + TPS68470_REG_VDCTL, TPS68470_VDCTL_EN_MASK, + tps68470_core_ranges, ARRAY_SIZE(tps68470_core_ranges)), + TPS68470_REGULATOR(ANA, TPS68470_ANA, tps68470_regulator_ops, 126, + TPS68470_REG_VAVAL, TPS68470_VAVAL_AVOLT_MASK, + TPS68470_REG_VACTL, TPS68470_VACTL_EN_MASK, + tps68470_ldo_ranges, ARRAY_SIZE(tps68470_ldo_ranges)), + TPS68470_REGULATOR(VCM, TPS68470_VCM, tps68470_regulator_ops, 126, + TPS68470_REG_VCMVAL, TPS68470_VCMVAL_VCVOLT_MASK, + TPS68470_REG_VCMCTL, TPS68470_VCMCTL_EN_MASK, + tps68470_ldo_ranges, ARRAY_SIZE(tps68470_ldo_ranges)), + TPS68470_REGULATOR(VIO, TPS68470_VIO, tps68470_always_on_reg_ops, 126, + TPS68470_REG_VIOVAL, TPS68470_VIOVAL_IOVOLT_MASK, + 0, 0, + tps68470_ldo_ranges, ARRAY_SIZE(tps68470_ldo_ranges)), +/* + * (1) This regulator must have the same voltage as VIO if S_IO LDO is used to + * power a sensor/VCM which I2C is daisy chained behind the PMIC. + * (2) If there is no I2C daisy chain it can be set freely. + */ + TPS68470_REGULATOR(VSIO, TPS68470_VSIO, tps68470_regulator_ops, 126, + TPS68470_REG_VSIOVAL, TPS68470_VSIOVAL_IOVOLT_MASK, + TPS68470_REG_S_I2C_CTL, TPS68470_S_I2C_CTL_EN_MASK, + tps68470_ldo_ranges, ARRAY_SIZE(tps68470_ldo_ranges)), + TPS68470_REGULATOR(AUX1, TPS68470_AUX1, tps68470_regulator_ops, 126, + TPS68470_REG_VAUX1VAL, TPS68470_VAUX1VAL_AUX1VOLT_MASK, + TPS68470_REG_VAUX1CTL, TPS68470_VAUX1CTL_EN_MASK, + tps68470_ldo_ranges, ARRAY_SIZE(tps68470_ldo_ranges)), + TPS68470_REGULATOR(AUX2, TPS68470_AUX2, tps68470_regulator_ops, 126, + TPS68470_REG_VAUX2VAL, TPS68470_VAUX2VAL_AUX2VOLT_MASK, + TPS68470_REG_VAUX2CTL, TPS68470_VAUX2CTL_EN_MASK, + tps68470_ldo_ranges, ARRAY_SIZE(tps68470_ldo_ranges)), +}; + +static int tps68470_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tps68470_regulator_platform_data *pdata = dev_get_platdata(dev); + struct tps68470_regulator_data *data; + struct regulator_config config = { }; + struct regulator_dev *rdev; + int i; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->clk = devm_clk_get(dev, "tps68470-clk"); + if (IS_ERR(data->clk)) + return dev_err_probe(dev, PTR_ERR(data->clk), "getting tps68470-clk\n"); + + config.dev = dev->parent; + config.regmap = dev_get_drvdata(dev->parent); + config.driver_data = data; + + for (i = 0; i < TPS68470_NUM_REGULATORS; i++) { + if (pdata) + config.init_data = pdata->reg_init_data[i]; + else + config.init_data = NULL; + + rdev = devm_regulator_register(dev, ®ulators[i], &config); + if (IS_ERR(rdev)) + return dev_err_probe(dev, PTR_ERR(rdev), + "registering %s regulator\n", + regulators[i].name); + } + + return 0; +} + +static struct platform_driver tps68470_regulator_driver = { + .driver = { + .name = "tps68470-regulator", + }, + .probe = tps68470_regulator_probe, +}; + +/* + * The ACPI tps68470 probe-ordering depends on the clk/gpio/regulator drivers + * registering before the drivers for the camera-sensors which use them bind. + * subsys_initcall() ensures this when the drivers are builtin. + */ +static int __init tps68470_regulator_init(void) +{ + return platform_driver_register(&tps68470_regulator_driver); +} +subsys_initcall(tps68470_regulator_init); + +static void __exit tps68470_regulator_exit(void) +{ + platform_driver_unregister(&tps68470_regulator_driver); +} +module_exit(tps68470_regulator_exit); + +MODULE_ALIAS("platform:tps68470-regulator"); +MODULE_DESCRIPTION("TPS68470 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 4a51cfea45ac..e2a20d512152 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -196,7 +196,6 @@ static int twl4030reg_enable(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); int grp; - int ret; grp = twlreg_grp(rdev); if (grp < 0) @@ -204,16 +203,13 @@ static int twl4030reg_enable(struct regulator_dev *rdev) grp |= P1_GRP_4030; - ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); - - return ret; + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); } static int twl4030reg_disable(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); int grp; - int ret; grp = twlreg_grp(rdev); if (grp < 0) @@ -221,9 +217,7 @@ static int twl4030reg_disable(struct regulator_dev *rdev) grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030); - ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); - - return ret; + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); } static int twl4030reg_get_status(struct regulator_dev *rdev) |