162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device driver for regulators in MAX5970 and MAX5978 IC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2022 9elements GmbH 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Patrick Rudolph <patrick.rudolph@9elements.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitops.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/i2c.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/regulator/driver.h> 1962306a36Sopenharmony_ci#include <linux/regulator/machine.h> 2062306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/mfd/max5970.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct max5970_regulator { 2662306a36Sopenharmony_ci int num_switches, mon_rng, irng, shunt_micro_ohms, lim_uA; 2762306a36Sopenharmony_ci struct regmap *regmap; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cienum max597x_regulator_id { 3162306a36Sopenharmony_ci MAX597X_sw0, 3262306a36Sopenharmony_ci MAX597X_sw1, 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int max597x_uvp_ovp_check_mode(struct regulator_dev *rdev, int severity) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int ret, reg; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Status1 register contains the soft strap values sampled at POR */ 4062306a36Sopenharmony_ci ret = regmap_read(rdev->regmap, MAX5970_REG_STATUS1, ®); 4162306a36Sopenharmony_ci if (ret) 4262306a36Sopenharmony_ci return ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* Check soft straps match requested mode */ 4562306a36Sopenharmony_ci if (severity == REGULATOR_SEVERITY_PROT) { 4662306a36Sopenharmony_ci if (STATUS1_PROT(reg) != STATUS1_PROT_SHUTDOWN) 4762306a36Sopenharmony_ci return -EOPNOTSUPP; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci if (STATUS1_PROT(reg) == STATUS1_PROT_SHUTDOWN) 5262306a36Sopenharmony_ci return -EOPNOTSUPP; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int max597x_set_vp(struct regulator_dev *rdev, int lim_uV, int severity, 5862306a36Sopenharmony_ci bool enable, bool overvoltage) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci int off_h, off_l, reg, ret; 6162306a36Sopenharmony_ci struct max5970_regulator *data = rdev_get_drvdata(rdev); 6262306a36Sopenharmony_ci int channel = rdev_get_id(rdev); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (overvoltage) { 6562306a36Sopenharmony_ci if (severity == REGULATOR_SEVERITY_WARN) { 6662306a36Sopenharmony_ci off_h = MAX5970_REG_CH_OV_WARN_H(channel); 6762306a36Sopenharmony_ci off_l = MAX5970_REG_CH_OV_WARN_L(channel); 6862306a36Sopenharmony_ci } else { 6962306a36Sopenharmony_ci off_h = MAX5970_REG_CH_OV_CRIT_H(channel); 7062306a36Sopenharmony_ci off_l = MAX5970_REG_CH_OV_CRIT_L(channel); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci } else { 7362306a36Sopenharmony_ci if (severity == REGULATOR_SEVERITY_WARN) { 7462306a36Sopenharmony_ci off_h = MAX5970_REG_CH_UV_WARN_H(channel); 7562306a36Sopenharmony_ci off_l = MAX5970_REG_CH_UV_WARN_L(channel); 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci off_h = MAX5970_REG_CH_UV_CRIT_H(channel); 7862306a36Sopenharmony_ci off_l = MAX5970_REG_CH_UV_CRIT_L(channel); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (enable) 8362306a36Sopenharmony_ci /* reg = ADC_MASK * (lim_uV / 1000000) / (data->mon_rng / 1000000) */ 8462306a36Sopenharmony_ci reg = ADC_MASK * lim_uV / data->mon_rng; 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci reg = 0; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ret = regmap_write(rdev->regmap, off_h, MAX5970_VAL2REG_H(reg)); 8962306a36Sopenharmony_ci if (ret) 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = regmap_write(rdev->regmap, off_l, MAX5970_VAL2REG_L(reg)); 9362306a36Sopenharmony_ci if (ret) 9462306a36Sopenharmony_ci return ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int max597x_set_uvp(struct regulator_dev *rdev, int lim_uV, int severity, 10062306a36Sopenharmony_ci bool enable) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci int ret; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * MAX5970 has enable control as a special value in limit reg. Can't 10662306a36Sopenharmony_ci * set limit but keep feature disabled or enable W/O given limit. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci if ((lim_uV && !enable) || (!lim_uV && enable)) 10962306a36Sopenharmony_ci return -EINVAL; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ret = max597x_uvp_ovp_check_mode(rdev, severity); 11262306a36Sopenharmony_ci if (ret) 11362306a36Sopenharmony_ci return ret; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return max597x_set_vp(rdev, lim_uV, severity, enable, false); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int max597x_set_ovp(struct regulator_dev *rdev, int lim_uV, int severity, 11962306a36Sopenharmony_ci bool enable) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci int ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * MAX5970 has enable control as a special value in limit reg. Can't 12562306a36Sopenharmony_ci * set limit but keep feature disabled or enable W/O given limit. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci if ((lim_uV && !enable) || (!lim_uV && enable)) 12862306a36Sopenharmony_ci return -EINVAL; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ret = max597x_uvp_ovp_check_mode(rdev, severity); 13162306a36Sopenharmony_ci if (ret) 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return max597x_set_vp(rdev, lim_uV, severity, enable, true); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int max597x_set_ocp(struct regulator_dev *rdev, int lim_uA, 13862306a36Sopenharmony_ci int severity, bool enable) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int val, reg; 14162306a36Sopenharmony_ci unsigned int vthst, vthfst; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci struct max5970_regulator *data = rdev_get_drvdata(rdev); 14462306a36Sopenharmony_ci int rdev_id = rdev_get_id(rdev); 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * MAX5970 doesn't has enable control for ocp. 14762306a36Sopenharmony_ci * If limit is specified but enable is not set then hold the value in 14862306a36Sopenharmony_ci * variable & later use it when ocp needs to be enabled. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci if (lim_uA != 0 && lim_uA != data->lim_uA) 15162306a36Sopenharmony_ci data->lim_uA = lim_uA; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (severity != REGULATOR_SEVERITY_PROT) 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (enable) { 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Calc Vtrip threshold in uV. */ 15962306a36Sopenharmony_ci vthst = 16062306a36Sopenharmony_ci div_u64(mul_u32_u32(data->shunt_micro_ohms, data->lim_uA), 16162306a36Sopenharmony_ci 1000000); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * As recommended in datasheed, add 20% margin to avoid 16562306a36Sopenharmony_ci * spurious event & passive component tolerance. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci vthst = div_u64(mul_u32_u32(vthst, 120), 100); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Calc fast Vtrip threshold in uV */ 17062306a36Sopenharmony_ci vthfst = vthst * (MAX5970_FAST2SLOW_RATIO / 100); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (vthfst > data->irng) { 17362306a36Sopenharmony_ci dev_err(&rdev->dev, "Current limit out of range\n"); 17462306a36Sopenharmony_ci return -EINVAL; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci /* Fast trip threshold to be programmed */ 17762306a36Sopenharmony_ci val = div_u64(mul_u32_u32(0xFF, vthfst), data->irng); 17862306a36Sopenharmony_ci } else 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * Since there is no option to disable ocp, set limit to max 18162306a36Sopenharmony_ci * value 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci val = 0xFF; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci reg = MAX5970_REG_DAC_FAST(rdev_id); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return regmap_write(rdev->regmap, reg, val); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int max597x_get_status(struct regulator_dev *rdev) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int val, ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci ret = regmap_read(rdev->regmap, MAX5970_REG_STATUS3, &val); 19562306a36Sopenharmony_ci if (ret) 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (val & MAX5970_STATUS3_ALERT) 19962306a36Sopenharmony_ci return REGULATOR_STATUS_ERROR; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = regulator_is_enabled_regmap(rdev); 20262306a36Sopenharmony_ci if (ret < 0) 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (ret) 20662306a36Sopenharmony_ci return REGULATOR_STATUS_ON; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return REGULATOR_STATUS_OFF; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic const struct regulator_ops max597x_switch_ops = { 21262306a36Sopenharmony_ci .enable = regulator_enable_regmap, 21362306a36Sopenharmony_ci .disable = regulator_disable_regmap, 21462306a36Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 21562306a36Sopenharmony_ci .get_status = max597x_get_status, 21662306a36Sopenharmony_ci .set_over_voltage_protection = max597x_set_ovp, 21762306a36Sopenharmony_ci .set_under_voltage_protection = max597x_set_uvp, 21862306a36Sopenharmony_ci .set_over_current_protection = max597x_set_ocp, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int max597x_dt_parse(struct device_node *np, 22262306a36Sopenharmony_ci const struct regulator_desc *desc, 22362306a36Sopenharmony_ci struct regulator_config *cfg) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct max5970_regulator *data = cfg->driver_data; 22662306a36Sopenharmony_ci int ret = 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ret = 22962306a36Sopenharmony_ci of_property_read_u32(np, "shunt-resistor-micro-ohms", 23062306a36Sopenharmony_ci &data->shunt_micro_ohms); 23162306a36Sopenharmony_ci if (ret < 0) 23262306a36Sopenharmony_ci dev_err(cfg->dev, 23362306a36Sopenharmony_ci "property 'shunt-resistor-micro-ohms' not found, err %d\n", 23462306a36Sopenharmony_ci ret); 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci#define MAX597X_SWITCH(_ID, _ereg, _chan, _supply) { \ 24062306a36Sopenharmony_ci .name = #_ID, \ 24162306a36Sopenharmony_ci .of_match = of_match_ptr(#_ID), \ 24262306a36Sopenharmony_ci .ops = &max597x_switch_ops, \ 24362306a36Sopenharmony_ci .regulators_node = of_match_ptr("regulators"), \ 24462306a36Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 24562306a36Sopenharmony_ci .id = MAX597X_##_ID, \ 24662306a36Sopenharmony_ci .owner = THIS_MODULE, \ 24762306a36Sopenharmony_ci .supply_name = _supply, \ 24862306a36Sopenharmony_ci .enable_reg = _ereg, \ 24962306a36Sopenharmony_ci .enable_mask = CHXEN((_chan)), \ 25062306a36Sopenharmony_ci .of_parse_cb = max597x_dt_parse, \ 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic const struct regulator_desc regulators[] = { 25462306a36Sopenharmony_ci MAX597X_SWITCH(sw0, MAX5970_REG_CHXEN, 0, "vss1"), 25562306a36Sopenharmony_ci MAX597X_SWITCH(sw1, MAX5970_REG_CHXEN, 1, "vss2"), 25662306a36Sopenharmony_ci}; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int max597x_regmap_read_clear(struct regmap *map, unsigned int reg, 25962306a36Sopenharmony_ci unsigned int *val) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = regmap_read(map, reg, val); 26462306a36Sopenharmony_ci if (ret) 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (*val) 26862306a36Sopenharmony_ci return regmap_write(map, reg, 0); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int max597x_irq_handler(int irq, struct regulator_irq_data *rid, 27462306a36Sopenharmony_ci unsigned long *dev_mask) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct regulator_err_state *stat; 27762306a36Sopenharmony_ci struct max5970_regulator *d = (struct max5970_regulator *)rid->data; 27862306a36Sopenharmony_ci int val, ret, i; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci ret = max597x_regmap_read_clear(d->regmap, MAX5970_REG_FAULT0, &val); 28162306a36Sopenharmony_ci if (ret) 28262306a36Sopenharmony_ci return REGULATOR_FAILED_RETRY; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci *dev_mask = 0; 28562306a36Sopenharmony_ci for (i = 0; i < d->num_switches; i++) { 28662306a36Sopenharmony_ci stat = &rid->states[i]; 28762306a36Sopenharmony_ci stat->notifs = 0; 28862306a36Sopenharmony_ci stat->errors = 0; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci for (i = 0; i < d->num_switches; i++) { 29262306a36Sopenharmony_ci stat = &rid->states[i]; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (val & UV_STATUS_CRIT(i)) { 29562306a36Sopenharmony_ci *dev_mask |= 1 << i; 29662306a36Sopenharmony_ci stat->notifs |= REGULATOR_EVENT_UNDER_VOLTAGE; 29762306a36Sopenharmony_ci stat->errors |= REGULATOR_ERROR_UNDER_VOLTAGE; 29862306a36Sopenharmony_ci } else if (val & UV_STATUS_WARN(i)) { 29962306a36Sopenharmony_ci *dev_mask |= 1 << i; 30062306a36Sopenharmony_ci stat->notifs |= REGULATOR_EVENT_UNDER_VOLTAGE_WARN; 30162306a36Sopenharmony_ci stat->errors |= REGULATOR_ERROR_UNDER_VOLTAGE_WARN; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci ret = max597x_regmap_read_clear(d->regmap, MAX5970_REG_FAULT1, &val); 30662306a36Sopenharmony_ci if (ret) 30762306a36Sopenharmony_ci return REGULATOR_FAILED_RETRY; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci for (i = 0; i < d->num_switches; i++) { 31062306a36Sopenharmony_ci stat = &rid->states[i]; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (val & OV_STATUS_CRIT(i)) { 31362306a36Sopenharmony_ci *dev_mask |= 1 << i; 31462306a36Sopenharmony_ci stat->notifs |= REGULATOR_EVENT_REGULATION_OUT; 31562306a36Sopenharmony_ci stat->errors |= REGULATOR_ERROR_REGULATION_OUT; 31662306a36Sopenharmony_ci } else if (val & OV_STATUS_WARN(i)) { 31762306a36Sopenharmony_ci *dev_mask |= 1 << i; 31862306a36Sopenharmony_ci stat->notifs |= REGULATOR_EVENT_OVER_VOLTAGE_WARN; 31962306a36Sopenharmony_ci stat->errors |= REGULATOR_ERROR_OVER_VOLTAGE_WARN; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ret = max597x_regmap_read_clear(d->regmap, MAX5970_REG_FAULT2, &val); 32462306a36Sopenharmony_ci if (ret) 32562306a36Sopenharmony_ci return REGULATOR_FAILED_RETRY; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci for (i = 0; i < d->num_switches; i++) { 32862306a36Sopenharmony_ci stat = &rid->states[i]; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (val & OC_STATUS_WARN(i)) { 33162306a36Sopenharmony_ci *dev_mask |= 1 << i; 33262306a36Sopenharmony_ci stat->notifs |= REGULATOR_EVENT_OVER_CURRENT_WARN; 33362306a36Sopenharmony_ci stat->errors |= REGULATOR_ERROR_OVER_CURRENT_WARN; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci ret = regmap_read(d->regmap, MAX5970_REG_STATUS0, &val); 33862306a36Sopenharmony_ci if (ret) 33962306a36Sopenharmony_ci return REGULATOR_FAILED_RETRY; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci for (i = 0; i < d->num_switches; i++) { 34262306a36Sopenharmony_ci stat = &rid->states[i]; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if ((val & MAX5970_CB_IFAULTF(i)) 34562306a36Sopenharmony_ci || (val & MAX5970_CB_IFAULTS(i))) { 34662306a36Sopenharmony_ci *dev_mask |= 1 << i; 34762306a36Sopenharmony_ci stat->notifs |= 34862306a36Sopenharmony_ci REGULATOR_EVENT_OVER_CURRENT | 34962306a36Sopenharmony_ci REGULATOR_EVENT_DISABLE; 35062306a36Sopenharmony_ci stat->errors |= 35162306a36Sopenharmony_ci REGULATOR_ERROR_OVER_CURRENT | REGULATOR_ERROR_FAIL; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* Clear the sub-IRQ status */ 35462306a36Sopenharmony_ci regulator_disable_regmap(stat->rdev); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int max597x_adc_range(struct regmap *regmap, const int ch, 36162306a36Sopenharmony_ci u32 *irng, u32 *mon_rng) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci unsigned int reg; 36462306a36Sopenharmony_ci int ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Decode current ADC range */ 36762306a36Sopenharmony_ci ret = regmap_read(regmap, MAX5970_REG_STATUS2, ®); 36862306a36Sopenharmony_ci if (ret) 36962306a36Sopenharmony_ci return ret; 37062306a36Sopenharmony_ci switch (MAX5970_IRNG(reg, ch)) { 37162306a36Sopenharmony_ci case 0: 37262306a36Sopenharmony_ci *irng = 100000; /* 100 mV */ 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case 1: 37562306a36Sopenharmony_ci *irng = 50000; /* 50 mV */ 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci case 2: 37862306a36Sopenharmony_ci *irng = 25000; /* 25 mV */ 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci default: 38162306a36Sopenharmony_ci return -EINVAL; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Decode current voltage monitor range */ 38562306a36Sopenharmony_ci ret = regmap_read(regmap, MAX5970_REG_MON_RANGE, ®); 38662306a36Sopenharmony_ci if (ret) 38762306a36Sopenharmony_ci return ret; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci *mon_rng = MAX5970_MON_MAX_RANGE_UV >> MAX5970_MON(reg, ch); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int max597x_setup_irq(struct device *dev, 39562306a36Sopenharmony_ci int irq, 39662306a36Sopenharmony_ci struct regulator_dev *rdevs[MAX5970_NUM_SWITCHES], 39762306a36Sopenharmony_ci int num_switches, struct max5970_regulator *data) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct regulator_irq_desc max597x_notif = { 40062306a36Sopenharmony_ci .name = "max597x-irq", 40162306a36Sopenharmony_ci .map_event = max597x_irq_handler, 40262306a36Sopenharmony_ci .data = data, 40362306a36Sopenharmony_ci }; 40462306a36Sopenharmony_ci int errs = REGULATOR_ERROR_UNDER_VOLTAGE | 40562306a36Sopenharmony_ci REGULATOR_ERROR_UNDER_VOLTAGE_WARN | 40662306a36Sopenharmony_ci REGULATOR_ERROR_OVER_VOLTAGE_WARN | 40762306a36Sopenharmony_ci REGULATOR_ERROR_REGULATION_OUT | 40862306a36Sopenharmony_ci REGULATOR_ERROR_OVER_CURRENT | 40962306a36Sopenharmony_ci REGULATOR_ERROR_OVER_CURRENT_WARN | REGULATOR_ERROR_FAIL; 41062306a36Sopenharmony_ci void *irq_helper; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Register notifiers - can fail if IRQ is not given */ 41362306a36Sopenharmony_ci irq_helper = devm_regulator_irq_helper(dev, &max597x_notif, 41462306a36Sopenharmony_ci irq, 0, errs, NULL, 41562306a36Sopenharmony_ci &rdevs[0], num_switches); 41662306a36Sopenharmony_ci if (IS_ERR(irq_helper)) { 41762306a36Sopenharmony_ci if (PTR_ERR(irq_helper) == -EPROBE_DEFER) 41862306a36Sopenharmony_ci return -EPROBE_DEFER; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci dev_warn(dev, "IRQ disabled %pe\n", irq_helper); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int max597x_regulator_probe(struct platform_device *pdev) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct max5970_data *max597x; 42962306a36Sopenharmony_ci struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); 43062306a36Sopenharmony_ci struct max5970_regulator *data; 43162306a36Sopenharmony_ci struct i2c_client *i2c = to_i2c_client(pdev->dev.parent); 43262306a36Sopenharmony_ci struct regulator_config config = { }; 43362306a36Sopenharmony_ci struct regulator_dev *rdev; 43462306a36Sopenharmony_ci struct regulator_dev *rdevs[MAX5970_NUM_SWITCHES]; 43562306a36Sopenharmony_ci int num_switches; 43662306a36Sopenharmony_ci int ret, i; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!regmap) 43962306a36Sopenharmony_ci return -EPROBE_DEFER; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci max597x = devm_kzalloc(&i2c->dev, sizeof(struct max5970_data), GFP_KERNEL); 44262306a36Sopenharmony_ci if (!max597x) 44362306a36Sopenharmony_ci return -ENOMEM; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci i2c_set_clientdata(i2c, max597x); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (of_device_is_compatible(i2c->dev.of_node, "maxim,max5978")) 44862306a36Sopenharmony_ci max597x->num_switches = MAX5978_NUM_SWITCHES; 44962306a36Sopenharmony_ci else if (of_device_is_compatible(i2c->dev.of_node, "maxim,max5970")) 45062306a36Sopenharmony_ci max597x->num_switches = MAX5970_NUM_SWITCHES; 45162306a36Sopenharmony_ci else 45262306a36Sopenharmony_ci return -ENODEV; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci i2c_set_clientdata(i2c, max597x); 45562306a36Sopenharmony_ci num_switches = max597x->num_switches; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci for (i = 0; i < num_switches; i++) { 45862306a36Sopenharmony_ci data = 45962306a36Sopenharmony_ci devm_kzalloc(&i2c->dev, sizeof(struct max5970_regulator), 46062306a36Sopenharmony_ci GFP_KERNEL); 46162306a36Sopenharmony_ci if (!data) 46262306a36Sopenharmony_ci return -ENOMEM; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci data->num_switches = num_switches; 46562306a36Sopenharmony_ci data->regmap = regmap; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ret = max597x_adc_range(regmap, i, &max597x->irng[i], &max597x->mon_rng[i]); 46862306a36Sopenharmony_ci if (ret < 0) 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci data->irng = max597x->irng[i]; 47262306a36Sopenharmony_ci data->mon_rng = max597x->mon_rng[i]; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci config.dev = &i2c->dev; 47562306a36Sopenharmony_ci config.driver_data = (void *)data; 47662306a36Sopenharmony_ci config.regmap = data->regmap; 47762306a36Sopenharmony_ci rdev = devm_regulator_register(&i2c->dev, 47862306a36Sopenharmony_ci ®ulators[i], &config); 47962306a36Sopenharmony_ci if (IS_ERR(rdev)) { 48062306a36Sopenharmony_ci dev_err(&i2c->dev, "failed to register regulator %s\n", 48162306a36Sopenharmony_ci regulators[i].name); 48262306a36Sopenharmony_ci return PTR_ERR(rdev); 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci rdevs[i] = rdev; 48562306a36Sopenharmony_ci max597x->shunt_micro_ohms[i] = data->shunt_micro_ohms; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (i2c->irq) { 48962306a36Sopenharmony_ci ret = 49062306a36Sopenharmony_ci max597x_setup_irq(&i2c->dev, i2c->irq, rdevs, num_switches, 49162306a36Sopenharmony_ci data); 49262306a36Sopenharmony_ci if (ret) { 49362306a36Sopenharmony_ci dev_err(&i2c->dev, "IRQ setup failed"); 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return ret; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic struct platform_driver max597x_regulator_driver = { 50262306a36Sopenharmony_ci .driver = { 50362306a36Sopenharmony_ci .name = "max5970-regulator", 50462306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 50562306a36Sopenharmony_ci }, 50662306a36Sopenharmony_ci .probe = max597x_regulator_probe, 50762306a36Sopenharmony_ci}; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cimodule_platform_driver(max597x_regulator_driver); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ciMODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>"); 51362306a36Sopenharmony_ciMODULE_DESCRIPTION("MAX5970_hot-swap controller driver"); 51462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 515