diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-21 18:27:05 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-21 18:27:05 -0700 |
commit | d6ccf45113fb6799885950293401e4b104c9b276 (patch) | |
tree | 531afa0db00c91d2af94422a5be3617441ebb5de /drivers/regulator | |
parent | a50a8c3833864d959c18f742bf6aa1d38fbe28c9 (diff) | |
parent | 5999f85ddeb436b4007878f251a30ccc8b9c638b (diff) |
Merge tag 'regulator-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator
Pull regulator updates from Mark Brown:
"Quite a quiet release for the regulator API, mainly a few new drivers
plus a lot of fixes for the Raspberry Pi panel driver.
There's also a SPI commit in here which I managed to apply to the
wrong tree and then didn't notice until there were too many commits on
top of it, sorry about that.
- Make it easier to use the virtual consumer test driver with DT
systems.
- Substantial overhaul providing various fixes and robustness
improvements for the Raspberry Pi panel driver.
- Support for Qualcomm PMX65 and SDX65, Richtek RT5190A, and Texas
Instruments TPS62864x"
* tag 'regulator-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (26 commits)
regulator: qcom-rpmh: Add support for SDX65
regulator: dt-bindings: Add PMX65 compatibles
regulator: vctrl: Use min() instead of doing it manually
regulator: rt5190a: Add support for Richtek RT5190A PMIC
regulator: Add bindings for Richtek RT5190A PMIC
regulator: Convert TPS62360 binding to json-schema
regulator: cleanup comments
regulator: virtual: add devicetree support
regulator: virtual: warn against production use
regulator: virtual: use dev_err_probe()
regulator: tps62864: Fix bindings for SW property
regulator: Add support for TPS6286x
regulator: Add bindings for TPS62864x
regulator/rpi-panel-attiny: Use two transactions for I2C read
regulator/rpi-panel-attiny: Use the regmap cache
regulator: rpi-panel: Remove get_brightness hook
regulator: rpi-panel: Add GPIO control for panel and touch resets
regulator: rpi-panel: Convert to drive lines directly
regulator: rpi-panel: Ensure the backlight is off during probe.
regulator: rpi-panel: Serialise operations.
...
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 20 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/regulator/max8973-regulator.c | 2 | ||||
-rw-r--r-- | drivers/regulator/qcom-rpmh-regulator.c | 37 | ||||
-rw-r--r-- | drivers/regulator/qcom_smd-regulator.c | 4 | ||||
-rw-r--r-- | drivers/regulator/rpi-panel-attiny-regulator.c | 291 | ||||
-rw-r--r-- | drivers/regulator/rt5190a-regulator.c | 513 | ||||
-rw-r--r-- | drivers/regulator/sc2731-regulator.c | 2 | ||||
-rw-r--r-- | drivers/regulator/ti-abb-regulator.c | 6 | ||||
-rw-r--r-- | drivers/regulator/tps6286x-regulator.c | 159 | ||||
-rw-r--r-- | drivers/regulator/vctrl-regulator.c | 5 | ||||
-rw-r--r-- | drivers/regulator/virtual.c | 41 | ||||
-rw-r--r-- | drivers/regulator/wm8350-regulator.c | 2 |
13 files changed, 1023 insertions, 61 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 1c35fed20d34..c8ce6e5eea24 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -984,6 +984,7 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY tristate "Raspberry Pi 7-inch touchscreen panel ATTINY regulator" depends on BACKLIGHT_CLASS_DEVICE depends on I2C + depends on OF_GPIO select REGMAP_I2C help This driver supports ATTINY regulator on the Raspberry Pi 7-inch @@ -1046,6 +1047,16 @@ config REGULATOR_RT5033 RT5033 PMIC. The device supports multiple regulators like current source, LDO and Buck. +config REGULATOR_RT5190A + tristate "Richtek RT5190A PMIC" + depends on I2C + select REGMAP_I2C + help + This adds support for voltage regulator in Richtek RT5190A PMIC. + It integratas 1 channel buck controller, 3 channels high efficiency + buck converters, 1 LDO, mute AC OFF depop function, with the general + I2C control interface. + config REGULATOR_RT6160 tristate "Richtek RT6160 BuckBoost voltage regulator" depends on I2C @@ -1263,6 +1274,15 @@ config REGULATOR_TPS62360 high-frequency synchronous step down dc-dc converter optimized for battery-powered portable applications. +config REGULATOR_TPS6286X + tristate "TI TPS6286x Power Regulator" + depends on I2C && OF + select REGMAP_I2C + help + This driver supports TPS6236x voltage regulator chips. These are + high-frequency synchronous step-down converters with an I2C + interface. + config REGULATOR_TPS65023 tristate "TI TPS65023 Power regulators" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 2e1b087489fa..1b64ad5767be 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_REGULATOR_ROHM) += rohm-regulator.o obj-$(CONFIG_REGULATOR_RT4801) += rt4801-regulator.o obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o +obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o @@ -149,6 +150,7 @@ obj-$(CONFIG_REGULATOR_SY8827N) += sy8827n.o obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o +obj-$(CONFIG_REGULATOR_TPS6286X) += tps6286x-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_TPS65086) += tps65086-regulator.o diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index 80b65cb87cef..cb7e50003f70 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -459,7 +459,7 @@ static int max8973_thermal_read_temp(void *data, int *temp) return ret; } - /* +1 degC to trigger cool devive */ + /* +1 degC to trigger cool device */ if (val & MAX77621_CHIPID_TJINT_S) *temp = mchip->junction_temp_warning + 1000; else diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index a3bc0eb6ceb8..561de6b2e6e3 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1121,6 +1121,39 @@ static const struct rpmh_vreg_init_data pmx55_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pmx65_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l18"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6-l16"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6-l16"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8-l9"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l8-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l11-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l11-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l6-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_nldo, "vdd-l17"), + /* ldo18 not configured */ + RPMH_VREG("ldo19", "ldo%s19", &pmic5_nldo, "vdd-l19"), + RPMH_VREG("ldo20", "ldo%s20", &pmic5_nldo, "vdd-l20"), + RPMH_VREG("ldo21", "ldo%s21", &pmic5_nldo, "vdd-l21"), + {} +}; + static const struct rpmh_vreg_init_data pm7325_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), @@ -1277,6 +1310,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .data = pmx55_vreg_data, }, { + .compatible = "qcom,pmx65-rpmh-regulators", + .data = pmx65_vreg_data, + }, + { .compatible = "qcom,pm7325-rpmh-regulators", .data = pm7325_vreg_data, }, diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index 9fc666107a06..8490aa8eecb1 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -1317,8 +1317,10 @@ static int rpm_reg_probe(struct platform_device *pdev) for_each_available_child_of_node(dev->of_node, node) { vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); - if (!vreg) + if (!vreg) { + of_node_put(node); return -ENOMEM; + } ret = rpm_regulator_init_vreg(vreg, dev, node, rpm, vreg_data); diff --git a/drivers/regulator/rpi-panel-attiny-regulator.c b/drivers/regulator/rpi-panel-attiny-regulator.c index ee46bfbf5eee..f7df0f4b2f87 100644 --- a/drivers/regulator/rpi-panel-attiny-regulator.c +++ b/drivers/regulator/rpi-panel-attiny-regulator.c @@ -8,6 +8,7 @@ #include <linux/backlight.h> #include <linux/err.h> #include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -21,63 +22,146 @@ /* I2C registers of the Atmel microcontroller. */ #define REG_ID 0x80 #define REG_PORTA 0x81 -#define REG_PORTA_HF BIT(2) -#define REG_PORTA_VF BIT(3) #define REG_PORTB 0x82 +#define REG_PORTC 0x83 #define REG_POWERON 0x85 #define REG_PWM 0x86 +#define REG_ADDR_L 0x8c +#define REG_ADDR_H 0x8d +#define REG_WRITE_DATA_H 0x90 +#define REG_WRITE_DATA_L 0x91 + +#define PA_LCD_DITHB BIT(0) +#define PA_LCD_MODE BIT(1) +#define PA_LCD_LR BIT(2) +#define PA_LCD_UD BIT(3) + +#define PB_BRIDGE_PWRDNX_N BIT(0) +#define PB_LCD_VCC_N BIT(1) +#define PB_LCD_MAIN BIT(7) + +#define PC_LED_EN BIT(0) +#define PC_RST_TP_N BIT(1) +#define PC_RST_LCD_N BIT(2) +#define PC_RST_BRIDGE_N BIT(3) + +enum gpio_signals { + RST_BRIDGE_N, /* TC358762 bridge reset */ + RST_TP_N, /* Touch controller reset */ + NUM_GPIO +}; + +struct gpio_signal_mappings { + unsigned int reg; + unsigned int mask; +}; + +static const struct gpio_signal_mappings mappings[NUM_GPIO] = { + [RST_BRIDGE_N] = { REG_PORTC, PC_RST_BRIDGE_N | PC_RST_LCD_N }, + [RST_TP_N] = { REG_PORTC, PC_RST_TP_N }, +}; + +struct attiny_lcd { + /* lock to serialise overall accesses to the Atmel */ + struct mutex lock; + struct regmap *regmap; + bool gpio_states[NUM_GPIO]; + u8 port_states[3]; + + struct gpio_chip gc; +}; static const struct regmap_config attiny_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = REG_PWM, - .cache_type = REGCACHE_NONE, + .disable_locking = 1, + .max_register = REG_WRITE_DATA_L, + .cache_type = REGCACHE_RBTREE, +}; + +static int attiny_set_port_state(struct attiny_lcd *state, int reg, u8 val) +{ + state->port_states[reg - REG_PORTA] = val; + return regmap_write(state->regmap, reg, val); +}; + +static u8 attiny_get_port_state(struct attiny_lcd *state, int reg) +{ + return state->port_states[reg - REG_PORTA]; }; static int attiny_lcd_power_enable(struct regulator_dev *rdev) { - unsigned int data; + struct attiny_lcd *state = rdev_get_drvdata(rdev); - regmap_write(rdev->regmap, REG_POWERON, 1); - /* Wait for nPWRDWN to go low to indicate poweron is done. */ - regmap_read_poll_timeout(rdev->regmap, REG_PORTB, data, - data & BIT(0), 10, 1000000); + mutex_lock(&state->lock); + + /* Ensure bridge, and tp stay in reset */ + attiny_set_port_state(state, REG_PORTC, 0); + usleep_range(5000, 10000); /* Default to the same orientation as the closed source * firmware used for the panel. Runtime rotation * configuration will be supported using VC4's plane * orientation bits. */ - regmap_write(rdev->regmap, REG_PORTA, BIT(2)); + attiny_set_port_state(state, REG_PORTA, PA_LCD_LR); + usleep_range(5000, 10000); + /* Main regulator on, and power to the panel (LCD_VCC_N) */ + attiny_set_port_state(state, REG_PORTB, PB_LCD_MAIN); + usleep_range(5000, 10000); + /* Bring controllers out of reset */ + attiny_set_port_state(state, REG_PORTC, PC_LED_EN); + + msleep(80); + + mutex_unlock(&state->lock); return 0; } static int attiny_lcd_power_disable(struct regulator_dev *rdev) { + struct attiny_lcd *state = rdev_get_drvdata(rdev); + + mutex_lock(&state->lock); + regmap_write(rdev->regmap, REG_PWM, 0); - regmap_write(rdev->regmap, REG_POWERON, 0); - udelay(1); + usleep_range(5000, 10000); + + attiny_set_port_state(state, REG_PORTA, 0); + usleep_range(5000, 10000); + attiny_set_port_state(state, REG_PORTB, PB_LCD_VCC_N); + usleep_range(5000, 10000); + attiny_set_port_state(state, REG_PORTC, 0); + msleep(30); + + mutex_unlock(&state->lock); + return 0; } static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) { + struct attiny_lcd *state = rdev_get_drvdata(rdev); unsigned int data; - int ret; + int ret, i; - ret = regmap_read(rdev->regmap, REG_POWERON, &data); - if (ret < 0) - return ret; + mutex_lock(&state->lock); - if (!(data & BIT(0))) - return 0; + for (i = 0; i < 10; i++) { + ret = regmap_read(rdev->regmap, REG_PORTC, &data); + if (!ret) + break; + usleep_range(10000, 12000); + } + + mutex_unlock(&state->lock); - ret = regmap_read(rdev->regmap, REG_PORTB, &data); if (ret < 0) return ret; - return data & BIT(0); + return data & PC_RST_BRIDGE_N; } static const struct regulator_init_data attiny_regulator_default = { @@ -101,33 +185,104 @@ static const struct regulator_desc attiny_regulator = { static int attiny_update_status(struct backlight_device *bl) { - struct regmap *regmap = bl_get_data(bl); + struct attiny_lcd *state = bl_get_data(bl); + struct regmap *regmap = state->regmap; int brightness = bl->props.brightness; + int ret, i; + + mutex_lock(&state->lock); if (bl->props.power != FB_BLANK_UNBLANK || bl->props.fb_blank != FB_BLANK_UNBLANK) brightness = 0; - return regmap_write(regmap, REG_PWM, brightness); -} - -static int attiny_get_brightness(struct backlight_device *bl) -{ - struct regmap *regmap = bl_get_data(bl); - int ret, brightness; + for (i = 0; i < 10; i++) { + ret = regmap_write(regmap, REG_PWM, brightness); + if (!ret) + break; + } - ret = regmap_read(regmap, REG_PWM, &brightness); - if (ret) - return ret; + mutex_unlock(&state->lock); - return brightness; + return ret; } static const struct backlight_ops attiny_bl = { .update_status = attiny_update_status, - .get_brightness = attiny_get_brightness, }; +static int attiny_gpio_get_direction(struct gpio_chip *gc, unsigned int off) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static void attiny_gpio_set(struct gpio_chip *gc, unsigned int off, int val) +{ + struct attiny_lcd *state = gpiochip_get_data(gc); + u8 last_val; + + if (off >= NUM_GPIO) + return; + + mutex_lock(&state->lock); + + last_val = attiny_get_port_state(state, mappings[off].reg); + if (val) + last_val |= mappings[off].mask; + else + last_val &= ~mappings[off].mask; + + attiny_set_port_state(state, mappings[off].reg, last_val); + + if (off == RST_BRIDGE_N && val) { + usleep_range(5000, 8000); + regmap_write(state->regmap, REG_ADDR_H, 0x04); + usleep_range(5000, 8000); + regmap_write(state->regmap, REG_ADDR_L, 0x7c); + usleep_range(5000, 8000); + regmap_write(state->regmap, REG_WRITE_DATA_H, 0x00); + usleep_range(5000, 8000); + regmap_write(state->regmap, REG_WRITE_DATA_L, 0x00); + + msleep(100); + } + + mutex_unlock(&state->lock); +} + +static int attiny_i2c_read(struct i2c_client *client, u8 reg, unsigned int *buf) +{ + struct i2c_msg msgs[1]; + u8 addr_buf[1] = { reg }; + u8 data_buf[1] = { 0, }; + int ret; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + usleep_range(5000, 10000); + + /* Read data from register */ + msgs[0].addr = client->addr; + msgs[0].flags = I2C_M_RD; + msgs[0].len = 1; + msgs[0].buf = data_buf; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *buf = data_buf[0]; + return 0; +} + /* * I2C driver interface functions */ @@ -138,22 +293,30 @@ static int attiny_i2c_probe(struct i2c_client *i2c, struct regulator_config config = { }; struct backlight_device *bl; struct regulator_dev *rdev; + struct attiny_lcd *state; struct regmap *regmap; unsigned int data; int ret; + state = devm_kzalloc(&i2c->dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + mutex_init(&state->lock); + i2c_set_clientdata(i2c, state); + regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - return ret; + goto error; } - ret = regmap_read(regmap, REG_ID, &data); + ret = attiny_i2c_read(i2c, REG_ID, &data); if (ret < 0) { dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret); - return ret; + goto error; } switch (data) { @@ -162,34 +325,73 @@ static int attiny_i2c_probe(struct i2c_client *i2c, break; default: dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data); - return -ENODEV; + ret = -ENODEV; + goto error; } regmap_write(regmap, REG_POWERON, 0); - mdelay(1); + msleep(30); + regmap_write(regmap, REG_PWM, 0); config.dev = &i2c->dev; config.regmap = regmap; config.of_node = i2c->dev.of_node; config.init_data = &attiny_regulator_default; + config.driver_data = state; rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config); if (IS_ERR(rdev)) { dev_err(&i2c->dev, "Failed to register ATTINY regulator\n"); - return PTR_ERR(rdev); + ret = PTR_ERR(rdev); + goto error; } props.type = BACKLIGHT_RAW; props.max_brightness = 0xff; - bl = devm_backlight_device_register(&i2c->dev, - "7inch-touchscreen-panel-bl", - &i2c->dev, regmap, &attiny_bl, + + state->regmap = regmap; + + bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev), + &i2c->dev, state, &attiny_bl, &props); - if (IS_ERR(bl)) - return PTR_ERR(bl); + if (IS_ERR(bl)) { + ret = PTR_ERR(bl); + goto error; + } bl->props.brightness = 0xff; + state->gc.parent = &i2c->dev; + state->gc.label = i2c->name; + state->gc.owner = THIS_MODULE; + state->gc.of_node = i2c->dev.of_node; + state->gc.base = -1; + state->gc.ngpio = NUM_GPIO; + + state->gc.set = attiny_gpio_set; + state->gc.get_direction = attiny_gpio_get_direction; + state->gc.can_sleep = true; + + ret = devm_gpiochip_add_data(&i2c->dev, &state->gc, state); + if (ret) { + dev_err(&i2c->dev, "Failed to create gpiochip: %d\n", ret); + goto error; + } + + return 0; + +error: + mutex_destroy(&state->lock); + + return ret; +} + +static int attiny_i2c_remove(struct i2c_client *client) +{ + struct attiny_lcd *state = i2c_get_clientdata(client); + + mutex_destroy(&state->lock); + return 0; } @@ -205,6 +407,7 @@ static struct i2c_driver attiny_regulator_driver = { .of_match_table = of_match_ptr(attiny_dt_ids), }, .probe = attiny_i2c_probe, + .remove = attiny_i2c_remove, }; module_i2c_driver(attiny_regulator_driver); diff --git a/drivers/regulator/rt5190a-regulator.c b/drivers/regulator/rt5190a-regulator.c new file mode 100644 index 000000000000..155d4afd00b1 --- /dev/null +++ b/drivers/regulator/rt5190a-regulator.c @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <dt-bindings/regulator/richtek,rt5190a-regulator.h> +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +#define RT5190A_REG_MANUFACTURE 0x00 +#define RT5190A_REG_BUCK2VSEL 0x04 +#define RT5190A_REG_BUCK3VSEL 0x05 +#define RT5190A_REG_DCDCCNTL 0x06 +#define RT5190A_REG_ENABLE 0x07 +#define RT5190A_REG_DISCHARGE 0x09 +#define RT5190A_REG_PROTMODE 0x0A +#define RT5190A_REG_MUTECNTL 0x0B +#define RT5190A_REG_PGSTAT 0x0F +#define RT5190A_REG_OVINT 0x10 +#define RT5190A_REG_HOTDIEMASK 0x17 + +#define RT5190A_VSEL_MASK GENMASK(6, 0) +#define RT5190A_RID_BITMASK(rid) BIT(rid + 1) +#define RT5190A_BUCK1_DISCHG_MASK GENMASK(1, 0) +#define RT5190A_BUCK1_DISCHG_ONVAL 0x01 +#define RT5190A_OVERVOLT_MASK GENMASK(7, 0) +#define RT5190A_UNDERVOLT_MASK GENMASK(15, 8) +#define RT5190A_CH234OT_MASK BIT(29) +#define RT5190A_CHIPOT_MASK BIT(28) + +#define RT5190A_BUCK23_MINUV 600000 +#define RT5190A_BUCK23_MAXUV 1400000 +#define RT5190A_BUCK23_STEPUV 10000 +#define RT5190A_BUCK23_STEPNUM ((1400000 - 600000) / 10000 + 1) + +enum { + RT5190A_IDX_BUCK1 = 0, + RT5190A_IDX_BUCK2, + RT5190A_IDX_BUCK3, + RT5190A_IDX_BUCK4, + RT5190A_IDX_LDO, + RT5190A_MAX_IDX +}; + +struct rt5190a_priv { + struct device *dev; + struct regmap *regmap; + struct regulator_desc rdesc[RT5190A_MAX_IDX]; + struct regulator_dev *rdev[RT5190A_MAX_IDX]; +}; + +static int rt5190a_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int pgood_stat; + int ret; + + ret = regmap_read(regmap, RT5190A_REG_PGSTAT, &pgood_stat); + if (ret) + return ret; + + if (!(pgood_stat & RT5190A_RID_BITMASK(rid))) + *flags = REGULATOR_ERROR_FAIL; + else + *flags = 0; + + return 0; +} + +static int rt5190a_fixed_buck_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int mask = RT5190A_RID_BITMASK(rid), val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = mask; + break; + case REGULATOR_MODE_NORMAL: + val = 0; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(regmap, RT5190A_REG_DCDCCNTL, mask, val); +} + +static unsigned int rt5190a_fixed_buck_get_mode(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int val; + int ret; + + ret = regmap_read(regmap, RT5190A_REG_DCDCCNTL, &val); + if (ret) { + dev_err(&rdev->dev, "Failed to get mode [%d]\n", ret); + return ret; + } + + if (val & RT5190A_RID_BITMASK(rid)) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static const struct regulator_ops rt5190a_ranged_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = rt5190a_get_error_flags, +}; + +static const struct regulator_ops rt5190a_fixed_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_mode = rt5190a_fixed_buck_set_mode, + .get_mode = rt5190a_fixed_buck_get_mode, + .get_error_flags = rt5190a_get_error_flags, +}; + +static const struct regulator_ops rt5190a_fixed_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = rt5190a_get_error_flags, +}; + +static irqreturn_t rt5190a_irq_handler(int irq, void *data) +{ + struct rt5190a_priv *priv = data; + __le32 raws; + unsigned int events, fields; + static const struct { + unsigned int bitmask; + unsigned int report; + } event_tbl[] = { + { RT5190A_OVERVOLT_MASK, REGULATOR_ERROR_REGULATION_OUT }, + { RT5190A_UNDERVOLT_MASK, REGULATOR_ERROR_UNDER_VOLTAGE } + }; + int i, j, ret; + + ret = regmap_raw_read(priv->regmap, RT5190A_REG_OVINT, &raws, + sizeof(raws)); + if (ret) { + dev_err(priv->dev, "Failed to read events\n"); + return IRQ_NONE; + } + + events = le32_to_cpu(raws); + + ret = regmap_raw_write(priv->regmap, RT5190A_REG_OVINT, &raws, + sizeof(raws)); + if (ret) + dev_err(priv->dev, "Failed to write-clear events\n"); + + /* Handle OV,UV events */ + for (i = 0; i < ARRAY_SIZE(event_tbl); i++) { + fields = events & event_tbl[i].bitmask; + fields >>= ffs(event_tbl[i].bitmask) - 1; + + for (j = 0; j < RT5190A_MAX_IDX; j++) { + if (!(fields & RT5190A_RID_BITMASK(j))) + continue; + + regulator_notifier_call_chain(priv->rdev[j], + event_tbl[i].report, + NULL); + } + } + + /* Handle CH234 OT event */ + if (events & RT5190A_CH234OT_MASK) { + for (j = RT5190A_IDX_BUCK2; j < RT5190A_IDX_LDO; j++) { + regulator_notifier_call_chain(priv->rdev[j], + REGULATOR_ERROR_OVER_TEMP, + NULL); + } + } + + /* Warning if CHIP OT occur */ + if (events & RT5190A_CHIPOT_MASK) + dev_warn(priv->dev, "CHIP overheat\n"); + + return IRQ_HANDLED; +} + +static unsigned int rt5190a_of_map_mode(unsigned int mode) +{ + switch (mode) { + case RT5190A_OPMODE_AUTO: + return REGULATOR_MODE_NORMAL; + case RT5190A_OPMODE_FPWM: + return REGULATOR_MODE_FAST; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int rt5190a_of_parse_cb(struct rt5190a_priv *priv, int rid, + struct of_regulator_match *match) +{ + struct regulator_desc *desc = priv->rdesc + rid; + struct regulator_init_data *init_data = match->init_data; + struct device_node *np = match->of_node; + bool latchup_enable; + unsigned int mask = RT5190A_RID_BITMASK(rid), val; + + switch (rid) { + case RT5190A_IDX_BUCK1: + case RT5190A_IDX_BUCK4: + case RT5190A_IDX_LDO: + init_data->constraints.apply_uV = 0; + + if (init_data->constraints.min_uV == + init_data->constraints.max_uV) + desc->fixed_uV = init_data->constraints.min_uV; + else { + dev_err(priv->dev, + "Variable voltage for fixed regulator\n"); + return -EINVAL; + } + break; + default: + break; + } + + latchup_enable = of_property_read_bool(np, "richtek,latchup-enable"); + + /* latchup: 0, default hiccup: 1 */ + val = !latchup_enable ? mask : 0; + + return regmap_update_bits(priv->regmap, RT5190A_REG_PROTMODE, mask, val); +} + +static void rt5190a_fillin_regulator_desc(struct regulator_desc *desc, int rid) +{ + static const char * const regu_name[] = { "buck1", "buck2", + "buck3", "buck4", + "ldo" }; + static const char * const supply[] = { NULL, "vin2", "vin3", "vin4", + "vinldo" }; + + desc->name = regu_name[rid]; + desc->supply_name = supply[rid]; + desc->owner = THIS_MODULE; + desc->type = REGULATOR_VOLTAGE; + desc->id = rid; + desc->enable_reg = RT5190A_REG_ENABLE; + desc->enable_mask = RT5190A_RID_BITMASK(rid); + desc->active_discharge_reg = RT5190A_REG_DISCHARGE; + desc->active_discharge_mask = RT5190A_RID_BITMASK(rid); + desc->active_discharge_on = RT5190A_RID_BITMASK(rid); + + switch (rid) { + case RT5190A_IDX_BUCK1: + desc->active_discharge_mask = RT5190A_BUCK1_DISCHG_MASK; + desc->active_discharge_on = RT5190A_BUCK1_DISCHG_ONVAL; + desc->n_voltages = 1; + desc->ops = &rt5190a_fixed_buck_ops; + desc->of_map_mode = rt5190a_of_map_mode; + break; + case RT5190A_IDX_BUCK2: + desc->vsel_reg = RT5190A_REG_BUCK2VSEL; + desc->vsel_mask = RT5190A_VSEL_MASK; + desc->min_uV = RT5190A_BUCK23_MINUV; + desc->uV_step = RT5190A_BUCK23_STEPUV; + desc->n_voltages = RT5190A_BUCK23_STEPNUM; + desc->ops = &rt5190a_ranged_buck_ops; + break; + case RT5190A_IDX_BUCK3: + desc->vsel_reg = RT5190A_REG_BUCK3VSEL; + desc->vsel_mask = RT5190A_VSEL_MASK; + desc->min_uV = RT5190A_BUCK23_MINUV; + desc->uV_step = RT5190A_BUCK23_STEPUV; + desc->n_voltages = RT5190A_BUCK23_STEPNUM; + desc->ops = &rt5190a_ranged_buck_ops; + break; + case RT5190A_IDX_BUCK4: + desc->n_voltages = 1; + desc->ops = &rt5190a_fixed_buck_ops; + desc->of_map_mode = rt5190a_of_map_mode; + break; + case RT5190A_IDX_LDO: + desc->n_voltages = 1; + desc->ops = &rt5190a_fixed_ldo_ops; + break; + } +} + +static struct of_regulator_match rt5190a_regulator_match[] = { + { .name = "buck1", }, + { .name = "buck2", }, + { .name = "buck3", }, + { .name = "buck4", }, + { .name = "ldo", } +}; + +static int rt5190a_parse_regulator_dt_data(struct rt5190a_priv *priv) +{ + struct device_node *regulator_np; + struct regulator_desc *reg_desc; + struct of_regulator_match *match; + int i, ret; + + for (i = 0; i < RT5190A_MAX_IDX; i++) { + reg_desc = priv->rdesc + i; + match = rt5190a_regulator_match + i; + + rt5190a_fillin_regulator_desc(reg_desc, i); + + match->desc = reg_desc; + } + + regulator_np = of_get_child_by_name(priv->dev->of_node, "regulators"); + if (!regulator_np) { + dev_err(priv->dev, "Could not find 'regulators' node\n"); + return -ENODEV; + } + + ret = of_regulator_match(priv->dev, regulator_np, + rt5190a_regulator_match, + ARRAY_SIZE(rt5190a_regulator_match)); + + of_node_put(regulator_np); + + if (ret < 0) { + dev_err(priv->dev, + "Error parsing regulator init data: %d\n", ret); + return ret; + } + + for (i = 0; i < RT5190A_MAX_IDX; i++) { + match = rt5190a_regulator_match + i; + + ret = rt5190a_of_parse_cb(priv, i, match); + if (ret) { + dev_err(priv->dev, "Failed in [%d] of_parse_cb\n", i); + return ret; + } + } + + return 0; +} + +static const struct reg_sequence rt5190a_init_patch[] = { + { 0x09, 0x3d, }, + { 0x0a, 0x3e, }, + { 0x0b, 0x01, }, + { 0x10, 0xff, }, + { 0x11, 0xff, }, + { 0x12, 0xff, }, + { 0x13, 0xff, }, + { 0x14, 0, }, + { 0x15, 0, }, + { 0x16, 0x3e, }, + { 0x17, 0, } +}; + +static int rt5190a_device_initialize(struct rt5190a_priv *priv) +{ + bool mute_enable; + int ret; + + ret = regmap_register_patch(priv->regmap, rt5190a_init_patch, + ARRAY_SIZE(rt5190a_init_patch)); + if (ret) { + dev_err(priv->dev, "Failed to do register patch\n"); + return ret; + } + + mute_enable = device_property_read_bool(priv->dev, + "richtek,mute-enable"); + + if (mute_enable) { + ret = regmap_write(priv->regmap, RT5190A_REG_MUTECNTL, 0x00); + if (ret) { + dev_err(priv->dev, "Failed to enable mute function\n"); + return ret; + } + } + + return 0; +} + +static int rt5190a_device_check(struct rt5190a_priv *priv) +{ + u16 devid; + int ret; + + ret = regmap_raw_read(priv->regmap, RT5190A_REG_MANUFACTURE, &devid, + sizeof(devid)); + if (ret) + return ret; + + if (devid) { + dev_err(priv->dev, "Incorrect device id 0x%04x\n", devid); + return -ENODEV; + } + + return 0; +} + +static const struct regmap_config rt5190a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RT5190A_REG_HOTDIEMASK, +}; + +static int rt5190a_probe(struct i2c_client *i2c) +{ + struct rt5190a_priv *priv; + struct regulator_config cfg = {}; + int i, ret; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &i2c->dev; + + priv->regmap = devm_regmap_init_i2c(i2c, &rt5190a_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&i2c->dev, "Failed to allocate regmap\n"); + return PTR_ERR(priv->regmap); + } + + ret = rt5190a_device_check(priv); + if (ret) { + dev_err(&i2c->dev, "Failed to check device %d\n", ret); + return ret; + } + + ret = rt5190a_device_initialize(priv); + if (ret) { + dev_err(&i2c->dev, "Failed to initialize the device\n"); + return ret; + } + + ret = rt5190a_parse_regulator_dt_data(priv); + if (ret) { + dev_err(&i2c->dev, "Failed to parse regulator dt\n"); + return ret; + } + + cfg.dev = &i2c->dev; + cfg.regmap = priv->regmap; + + for (i = 0; i < RT5190A_MAX_IDX; i++) { + struct regulator_desc *desc = priv->rdesc + i; + struct of_regulator_match *match = rt5190a_regulator_match + i; + + cfg.init_data = match->init_data; + cfg.of_node = match->of_node; + + priv->rdev[i] = devm_regulator_register(&i2c->dev, desc, &cfg); + if (IS_ERR(priv->rdev[i])) { + dev_err(&i2c->dev, "Failed to register regulator %s\n", + desc->name); + return PTR_ERR(priv->rdev[i]); + } + } + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5190a_irq_handler, + IRQF_ONESHOT, + dev_name(&i2c->dev), priv); + if (ret) { + dev_err(&i2c->dev, "Failed to register interrupt\n"); + return ret; + } + } + + return 0; +} + +static const struct of_device_id __maybe_unused rt5190a_device_table[] = { + { .compatible = "richtek,rt5190a", }, + {} +}; +MODULE_DEVICE_TABLE(of, rt5190a_device_table); + +static struct i2c_driver rt5190a_driver = { + .driver = { + .name = "rt5190a", + .of_match_table = rt5190a_device_table, + }, + .probe_new = rt5190a_probe, +}; +module_i2c_driver(rt5190a_driver); + +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); +MODULE_DESCRIPTION("Richtek RT5190A Regulator Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/sc2731-regulator.c b/drivers/regulator/sc2731-regulator.c index 0f21f95c8981..71e5ceb679f4 100644 --- a/drivers/regulator/sc2731-regulator.c +++ b/drivers/regulator/sc2731-regulator.c @@ -1,4 +1,4 @@ - //SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 Spreadtrum Communications Inc. */ diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index 2931a0b89bff..bd7b2f287250 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -42,7 +42,7 @@ /** * struct ti_abb_info - ABB information per voltage setting * @opp_sel: one of TI_ABB macro - * @vset: (optional) vset value that LDOVBB needs to be overriden with. + * @vset: (optional) vset value that LDOVBB needs to be overridden with. * * Array of per voltage entries organized in the same order as regulator_desc's * volt_table list. (selector is used to index from this array) @@ -484,7 +484,7 @@ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) /* Calculate cycle rate */ cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate); - /* Calulate SR2_WTCNT_VALUE */ + /* Calculate SR2_WTCNT_VALUE */ sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate); dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, @@ -688,7 +688,7 @@ MODULE_DEVICE_TABLE(of, ti_abb_of_match); * @pdev: ABB platform device * * Initializes an individual ABB LDO for required Body-Bias. ABB is used to - * addional bias supply to SoC modules for power savings or mandatory stability + * additional bias supply to SoC modules for power savings or mandatory stability * configuration at certain Operating Performance Points(OPPs). * * Return: 0 on success or appropriate error value when fails diff --git a/drivers/regulator/tps6286x-regulator.c b/drivers/regulator/tps6286x-regulator.c new file mode 100644 index 000000000000..e29deda30d75 --- /dev/null +++ b/drivers/regulator/tps6286x-regulator.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright Axis Communications AB + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/of_regulator.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/driver.h> + +#include <dt-bindings/regulator/ti,tps62864.h> + +#define TPS6286X_VOUT1 0x01 +#define TPS6286X_VOUT1_VO1_SET GENMASK(7, 0) + +#define TPS6286X_CONTROL 0x03 +#define TPS6286X_CONTROL_FPWM BIT(4) +#define TPS6286X_CONTROL_SWEN BIT(5) + +#define TPS6286X_MIN_MV 400 +#define TPS6286X_MAX_MV 1675 +#define TPS6286X_STEP_MV 5 + +static const struct regmap_config tps6286x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int tps6286x_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + unsigned int val; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = 0; + break; + case REGULATOR_MODE_FAST: + val = TPS6286X_CONTROL_FPWM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(rdev->regmap, TPS6286X_CONTROL, + TPS6286X_CONTROL_FPWM, val); +} + +static unsigned int tps6286x_get_mode(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, TPS6286X_CONTROL, &val); + if (ret < 0) + return 0; + + return (val & TPS6286X_CONTROL_FPWM) ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static const struct regulator_ops tps6286x_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_mode = tps6286x_set_mode, + .get_mode = tps6286x_get_mode, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +static unsigned int tps6286x_of_map_mode(unsigned int mode) +{ + switch (mode) { + case TPS62864_MODE_NORMAL: + return REGULATOR_MODE_NORMAL; + case TPS62864_MODE_FPWM: + return REGULATOR_MODE_FAST; + default: + return REGULATOR_MODE_INVALID; + } +} + +static const struct regulator_desc tps6286x_reg = { + .name = "tps6286x", + .of_match = of_match_ptr("SW"), + .owner = THIS_MODULE, + .ops = &tps6286x_regulator_ops, + .of_map_mode = tps6286x_of_map_mode, + .regulators_node = of_match_ptr("regulators"), + .type = REGULATOR_VOLTAGE, + .n_voltages = ((TPS6286X_MAX_MV - TPS6286X_MIN_MV) / TPS6286X_STEP_MV) + 1, + .min_uV = TPS6286X_MIN_MV * 1000, + .uV_step = TPS6286X_STEP_MV * 1000, + .vsel_reg = TPS6286X_VOUT1, + .vsel_mask = TPS6286X_VOUT1_VO1_SET, + .enable_reg = TPS6286X_CONTROL, + .enable_mask = TPS6286X_CONTROL_SWEN, + .ramp_delay = 1000, + /* tDelay + tRamp, rounded up */ + .enable_time = 3000, +}; + +static const struct of_device_id tps6286x_dt_ids[] = { + { .compatible = "ti,tps62864", }, + { .compatible = "ti,tps62866", }, + { .compatible = "ti,tps62868", }, + { .compatible = "ti,tps62869", }, + { } +}; +MODULE_DEVICE_TABLE(of, tps6286x_dt_ids); + +static int tps6286x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct regulator_config config = {}; + struct regulator_dev *rdev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &tps6286x_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + config.dev = &i2c->dev; + config.of_node = dev->of_node; + config.regmap = regmap; + + rdev = devm_regulator_register(&i2c->dev, &tps6286x_reg, &config); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, "Failed to register tps6286x regulator\n"); + return PTR_ERR(rdev); + } + + return 0; +} + +static const struct i2c_device_id tps6286x_i2c_id[] = { + { "tps62864", 0 }, + { "tps62866", 0 }, + { "tps62868", 0 }, + { "tps62869", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, tps6286x_i2c_id); + +static struct i2c_driver tps6286x_regulator_driver = { + .driver = { + .name = "tps6286x", + .of_match_table = of_match_ptr(tps6286x_dt_ids), + }, + .probe = tps6286x_i2c_probe, + .id_table = tps6286x_i2c_id, +}; + +module_i2c_driver(tps6286x_regulator_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c index d2a37978fc3a..aac7be3b33f7 100644 --- a/drivers/regulator/vctrl-regulator.c +++ b/drivers/regulator/vctrl-regulator.c @@ -185,10 +185,7 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev, unsigned int next_sel; int delay; - if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel) - next_sel = selector; - else - next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel; + next_sel = max_t(unsigned int, selector, vctrl->vtable[vctrl->sel].ovp_min_sel); ret = regulator_set_voltage_rdev(rdev->supply->rdev, vctrl->vtable[next_sel].ctrl, diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 52c5a0e0acd8..5d32628a5011 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -13,6 +13,7 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/of.h> struct virtual_consumer_data { struct mutex lock; @@ -281,26 +282,53 @@ static const struct attribute_group regulator_virtual_attr_group = { .attrs = regulator_virtual_attributes, }; +#ifdef CONFIG_OF +static const struct of_device_id regulator_virtual_consumer_of_match[] = { + { .compatible = "regulator-virtual-consumer" }, + {}, +}; +MODULE_DEVICE_TABLE(of, regulator_virtual_consumer_of_match); +#endif + static int regulator_virtual_probe(struct platform_device *pdev) { char *reg_id = dev_get_platdata(&pdev->dev); struct virtual_consumer_data *drvdata; + static bool warned; int ret; + if (!warned) { + warned = true; + pr_warn("**********************************************************\n"); + pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); + pr_warn("** **\n"); + pr_warn("** regulator-virtual-consumer is only for testing and **\n"); + pr_warn("** debugging. Do not use it in a production kernel. **\n"); + pr_warn("** **\n"); + pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); + pr_warn("**********************************************************\n"); + } + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data), GFP_KERNEL); if (drvdata == NULL) return -ENOMEM; + /* + * This virtual consumer does not have any hardware-defined supply + * name, so just allow the regulator to be specified in a property + * named "default-supply" when we're being probed from devicetree. + */ + if (!reg_id && pdev->dev.of_node) + reg_id = "default"; + mutex_init(&drvdata->lock); drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id); - if (IS_ERR(drvdata->regulator)) { - ret = PTR_ERR(drvdata->regulator); - dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n", - reg_id, ret); - return ret; - } + if (IS_ERR(drvdata->regulator)) + return dev_err_probe(&pdev->dev, PTR_ERR(drvdata->regulator), + "Failed to obtain supply '%s'\n", + reg_id); ret = sysfs_create_group(&pdev->dev.kobj, ®ulator_virtual_attr_group); @@ -334,6 +362,7 @@ static struct platform_driver regulator_virtual_consumer_driver = { .remove = regulator_virtual_remove, .driver = { .name = "reg-virt-consumer", + .of_match_table = of_match_ptr(regulator_virtual_consumer_of_match), }, }; diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 6579bfdb0c26..b1d5aac8917d 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1112,7 +1112,7 @@ static int wm8350_regulator_probe(struct platform_device *pdev) if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B) return -ENODEV; - /* do any regulatior specific init */ + /* do any regulator specific init */ switch (pdev->id) { case WM8350_DCDC_1: val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); |