mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
regulator: add property parsing and callbacks to set protection limits
Add DT property parsing code and setting callback for regulator over/under voltage, over-current and temperature error limits. Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> Link: https://lore.kernel.org/r/e7b8007ba9eae7076178bf3363fb942ccb1cc9a5.1622628334.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
7111c6d1b3
commit
89a6a5e56c
@ -1305,6 +1305,52 @@ static int machine_constraints_current(struct regulator_dev *rdev,
|
||||
|
||||
static int _regulator_do_enable(struct regulator_dev *rdev);
|
||||
|
||||
static int notif_set_limit(struct regulator_dev *rdev,
|
||||
int (*set)(struct regulator_dev *, int, int, bool),
|
||||
int limit, int severity)
|
||||
{
|
||||
bool enable;
|
||||
|
||||
if (limit == REGULATOR_NOTIF_LIMIT_DISABLE) {
|
||||
enable = false;
|
||||
limit = 0;
|
||||
} else {
|
||||
enable = true;
|
||||
}
|
||||
|
||||
if (limit == REGULATOR_NOTIF_LIMIT_ENABLE)
|
||||
limit = 0;
|
||||
|
||||
return set(rdev, limit, severity, enable);
|
||||
}
|
||||
|
||||
static int handle_notify_limits(struct regulator_dev *rdev,
|
||||
int (*set)(struct regulator_dev *, int, int, bool),
|
||||
struct notification_limit *limits)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!set)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (limits->prot)
|
||||
ret = notif_set_limit(rdev, set, limits->prot,
|
||||
REGULATOR_SEVERITY_PROT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (limits->err)
|
||||
ret = notif_set_limit(rdev, set, limits->err,
|
||||
REGULATOR_SEVERITY_ERR);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (limits->warn)
|
||||
ret = notif_set_limit(rdev, set, limits->warn,
|
||||
REGULATOR_SEVERITY_WARN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* set_machine_constraints - sets regulator constraints
|
||||
* @rdev: regulator source
|
||||
@ -1390,9 +1436,27 @@ static int set_machine_constraints(struct regulator_dev *rdev)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Existing logic does not warn if over_current_protection is given as
|
||||
* a constraint but driver does not support that. I think we should
|
||||
* warn about this type of issues as it is possible someone changes
|
||||
* PMIC on board to another type - and the another PMIC's driver does
|
||||
* not support setting protection. Board composer may happily believe
|
||||
* the DT limits are respected - especially if the new PMIC HW also
|
||||
* supports protection but the driver does not. I won't change the logic
|
||||
* without hearing more experienced opinion on this though.
|
||||
*
|
||||
* If warning is seen as a good idea then we can merge handling the
|
||||
* over-curret protection and detection and get rid of this special
|
||||
* handling.
|
||||
*/
|
||||
if (rdev->constraints->over_current_protection
|
||||
&& ops->set_over_current_protection) {
|
||||
ret = ops->set_over_current_protection(rdev);
|
||||
int lim = rdev->constraints->over_curr_limits.prot;
|
||||
|
||||
ret = ops->set_over_current_protection(rdev, lim,
|
||||
REGULATOR_SEVERITY_PROT,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
rdev_err(rdev, "failed to set over current protection: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
@ -1400,6 +1464,62 @@ static int set_machine_constraints(struct regulator_dev *rdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (rdev->constraints->over_current_detection)
|
||||
ret = handle_notify_limits(rdev,
|
||||
ops->set_over_current_protection,
|
||||
&rdev->constraints->over_curr_limits);
|
||||
if (ret) {
|
||||
if (ret != -EOPNOTSUPP) {
|
||||
rdev_err(rdev, "failed to set over current limits: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
rdev_warn(rdev,
|
||||
"IC does not support requested over-current limits\n");
|
||||
}
|
||||
|
||||
if (rdev->constraints->over_voltage_detection)
|
||||
ret = handle_notify_limits(rdev,
|
||||
ops->set_over_voltage_protection,
|
||||
&rdev->constraints->over_voltage_limits);
|
||||
if (ret) {
|
||||
if (ret != -EOPNOTSUPP) {
|
||||
rdev_err(rdev, "failed to set over voltage limits %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
rdev_warn(rdev,
|
||||
"IC does not support requested over voltage limits\n");
|
||||
}
|
||||
|
||||
if (rdev->constraints->under_voltage_detection)
|
||||
ret = handle_notify_limits(rdev,
|
||||
ops->set_under_voltage_protection,
|
||||
&rdev->constraints->under_voltage_limits);
|
||||
if (ret) {
|
||||
if (ret != -EOPNOTSUPP) {
|
||||
rdev_err(rdev, "failed to set under voltage limits %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
rdev_warn(rdev,
|
||||
"IC does not support requested under voltage limits\n");
|
||||
}
|
||||
|
||||
if (rdev->constraints->over_temp_detection)
|
||||
ret = handle_notify_limits(rdev,
|
||||
ops->set_thermal_protection,
|
||||
&rdev->constraints->temp_limits);
|
||||
if (ret) {
|
||||
if (ret != -EOPNOTSUPP) {
|
||||
rdev_err(rdev, "failed to set temperature limits %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
rdev_warn(rdev,
|
||||
"IC does not support requested temperature limits\n");
|
||||
}
|
||||
|
||||
if (rdev->constraints->active_discharge && ops->set_active_discharge) {
|
||||
bool ad_state = (rdev->constraints->active_discharge ==
|
||||
REGULATOR_ACTIVE_DISCHARGE_ENABLE) ? true : false;
|
||||
|
@ -21,6 +21,62 @@ static const char *const regulator_states[PM_SUSPEND_MAX + 1] = {
|
||||
[PM_SUSPEND_MAX] = "regulator-state-disk",
|
||||
};
|
||||
|
||||
static void fill_limit(int *limit, int val)
|
||||
{
|
||||
if (val)
|
||||
if (val == 1)
|
||||
*limit = REGULATOR_NOTIF_LIMIT_ENABLE;
|
||||
else
|
||||
*limit = val;
|
||||
else
|
||||
*limit = REGULATOR_NOTIF_LIMIT_DISABLE;
|
||||
}
|
||||
|
||||
static void of_get_regulator_prot_limits(struct device_node *np,
|
||||
struct regulation_constraints *constraints)
|
||||
{
|
||||
u32 pval;
|
||||
int i;
|
||||
static const char *const props[] = {
|
||||
"regulator-oc-%s-microamp",
|
||||
"regulator-ov-%s-microvolt",
|
||||
"regulator-temp-%s-kelvin",
|
||||
"regulator-uv-%s-microvolt",
|
||||
};
|
||||
struct notification_limit *limits[] = {
|
||||
&constraints->over_curr_limits,
|
||||
&constraints->over_voltage_limits,
|
||||
&constraints->temp_limits,
|
||||
&constraints->under_voltage_limits,
|
||||
};
|
||||
bool set[4] = {0};
|
||||
|
||||
/* Protection limits: */
|
||||
for (i = 0; i < ARRAY_SIZE(props); i++) {
|
||||
char prop[255];
|
||||
bool found;
|
||||
int j;
|
||||
static const char *const lvl[] = {
|
||||
"protection", "error", "warn"
|
||||
};
|
||||
int *l[] = {
|
||||
&limits[i]->prot, &limits[i]->err, &limits[i]->warn,
|
||||
};
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(lvl); j++) {
|
||||
snprintf(prop, 255, props[i], lvl[j]);
|
||||
found = !of_property_read_u32(np, prop, &pval);
|
||||
if (found)
|
||||
fill_limit(l[j], pval);
|
||||
set[i] |= found;
|
||||
}
|
||||
}
|
||||
constraints->over_current_detection = set[0];
|
||||
constraints->over_voltage_detection = set[1];
|
||||
constraints->over_temp_detection = set[2];
|
||||
constraints->under_voltage_detection = set[3];
|
||||
}
|
||||
|
||||
static int of_get_regulation_constraints(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct regulator_init_data **init_data,
|
||||
@ -188,6 +244,8 @@ static int of_get_regulation_constraints(struct device *dev,
|
||||
constraints->over_current_protection = of_property_read_bool(np,
|
||||
"regulator-over-current-protection");
|
||||
|
||||
of_get_regulator_prot_limits(np, constraints);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
|
||||
switch (i) {
|
||||
case PM_SUSPEND_MEM:
|
||||
|
@ -307,13 +307,21 @@ end:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int qcom_labibb_set_ocp(struct regulator_dev *rdev)
|
||||
static int qcom_labibb_set_ocp(struct regulator_dev *rdev, int lim,
|
||||
int severity, bool enable)
|
||||
{
|
||||
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
|
||||
char *ocp_irq_name;
|
||||
u32 irq_flags = IRQF_ONESHOT;
|
||||
int irq_trig_low, ret;
|
||||
|
||||
/*
|
||||
* labibb supports only protection - and does not support setting
|
||||
* limit. Furthermore, we don't support disabling protection.
|
||||
*/
|
||||
if (lim || severity != REGULATOR_SEVERITY_PROT || !enable)
|
||||
return -EINVAL;
|
||||
|
||||
/* If there is no OCP interrupt, there's nothing to set */
|
||||
if (vreg->ocp_irq <= 0)
|
||||
return -EINVAL;
|
||||
|
@ -595,11 +595,15 @@ static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
|
||||
return regulator_enable_regmap(rdev);
|
||||
}
|
||||
|
||||
static int spmi_regulator_vs_ocp(struct regulator_dev *rdev)
|
||||
static int spmi_regulator_vs_ocp(struct regulator_dev *rdev, int lim_uA,
|
||||
int severity, bool enable)
|
||||
{
|
||||
struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
|
||||
u8 reg = SPMI_VS_OCP_OVERRIDE;
|
||||
|
||||
if (lim_uA || !enable || severity != REGULATOR_SEVERITY_PROT)
|
||||
return -EINVAL;
|
||||
|
||||
return spmi_vreg_write(vreg, SPMI_VS_REG_OCP, ®, 1);
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,8 @@ struct stpmic1_regulator_cfg {
|
||||
|
||||
static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode);
|
||||
static unsigned int stpmic1_get_mode(struct regulator_dev *rdev);
|
||||
static int stpmic1_set_icc(struct regulator_dev *rdev);
|
||||
static int stpmic1_set_icc(struct regulator_dev *rdev, int lim, int severity,
|
||||
bool enable);
|
||||
static unsigned int stpmic1_map_mode(unsigned int mode);
|
||||
|
||||
enum {
|
||||
@ -491,11 +492,26 @@ static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode)
|
||||
STPMIC1_BUCK_MODE_LP, value);
|
||||
}
|
||||
|
||||
static int stpmic1_set_icc(struct regulator_dev *rdev)
|
||||
static int stpmic1_set_icc(struct regulator_dev *rdev, int lim, int severity,
|
||||
bool enable)
|
||||
{
|
||||
struct stpmic1_regulator_cfg *cfg = rdev_get_drvdata(rdev);
|
||||
struct regmap *regmap = rdev_get_regmap(rdev);
|
||||
|
||||
/*
|
||||
* The code seems like one bit in a register controls whether OCP is
|
||||
* enabled. So we might be able to turn it off here is if that
|
||||
* was requested. I won't support this because I don't have the HW.
|
||||
* Feel free to try and implement if you have the HW and need kernel
|
||||
* to disable this.
|
||||
*
|
||||
* Also, I don't know if limit can be configured or if we support
|
||||
* error/warning instead of protect. So I just keep existing logic
|
||||
* and assume no.
|
||||
*/
|
||||
if (lim || severity != REGULATOR_SEVERITY_PROT || !enable)
|
||||
return -EINVAL;
|
||||
|
||||
/* enable switch off in case of over current */
|
||||
return regmap_update_bits(regmap, cfg->icc_reg, cfg->icc_mask,
|
||||
cfg->icc_mask);
|
||||
|
@ -40,6 +40,15 @@ enum regulator_status {
|
||||
REGULATOR_STATUS_UNDEFINED,
|
||||
};
|
||||
|
||||
enum regulator_detection_severity {
|
||||
/* Hardware shut down voltage outputs if condition is detected */
|
||||
REGULATOR_SEVERITY_PROT,
|
||||
/* Hardware is probably damaged/inoperable */
|
||||
REGULATOR_SEVERITY_ERR,
|
||||
/* Hardware is still recoverable but recovery action must be taken */
|
||||
REGULATOR_SEVERITY_WARN,
|
||||
};
|
||||
|
||||
/* Initialize struct linear_range for regulators */
|
||||
#define REGULATOR_LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV) \
|
||||
{ \
|
||||
@ -78,8 +87,25 @@ enum regulator_status {
|
||||
* @get_current_limit: Get the configured limit for a current-limited regulator.
|
||||
* @set_input_current_limit: Configure an input limit.
|
||||
*
|
||||
* @set_over_current_protection: Support capability of automatically shutting
|
||||
* down when detecting an over current event.
|
||||
* @set_over_current_protection: Support enabling of and setting limits for over
|
||||
* current situation detection. Detection can be configured for three
|
||||
* levels of severity.
|
||||
* REGULATOR_SEVERITY_PROT should automatically shut down the regulator(s).
|
||||
* REGULATOR_SEVERITY_ERR should indicate that over-current situation is
|
||||
* caused by an unrecoverable error but HW does not perform
|
||||
* automatic shut down.
|
||||
* REGULATOR_SEVERITY_WARN should indicate situation where hardware is
|
||||
* still believed to not be damaged but that a board sepcific
|
||||
* recovery action is needed. If lim_uA is 0 the limit should not
|
||||
* be changed but the detection should just be enabled/disabled as
|
||||
* is requested.
|
||||
* @set_over_voltage_protection: Support enabling of and setting limits for over
|
||||
* voltage situation detection. Detection can be configured for same
|
||||
* severities as over current protection.
|
||||
* @set_under_voltage_protection: Support enabling of and setting limits for
|
||||
* under situation detection.
|
||||
* @set_thermal_protection: Support enabling of and setting limits for over
|
||||
* temperature situation detection.
|
||||
*
|
||||
* @set_active_discharge: Set active discharge enable/disable of regulators.
|
||||
*
|
||||
@ -143,8 +169,15 @@ struct regulator_ops {
|
||||
int (*get_current_limit) (struct regulator_dev *);
|
||||
|
||||
int (*set_input_current_limit) (struct regulator_dev *, int lim_uA);
|
||||
int (*set_over_current_protection) (struct regulator_dev *);
|
||||
int (*set_active_discharge) (struct regulator_dev *, bool enable);
|
||||
int (*set_over_current_protection)(struct regulator_dev *, int lim_uA,
|
||||
int severity, bool enable);
|
||||
int (*set_over_voltage_protection)(struct regulator_dev *, int lim_uV,
|
||||
int severity, bool enable);
|
||||
int (*set_under_voltage_protection)(struct regulator_dev *, int lim_uV,
|
||||
int severity, bool enable);
|
||||
int (*set_thermal_protection)(struct regulator_dev *, int lim,
|
||||
int severity, bool enable);
|
||||
int (*set_active_discharge)(struct regulator_dev *, bool enable);
|
||||
|
||||
/* enable/disable regulator */
|
||||
int (*enable) (struct regulator_dev *);
|
||||
|
@ -83,6 +83,14 @@ struct regulator_state {
|
||||
bool changeable;
|
||||
};
|
||||
|
||||
#define REGULATOR_NOTIF_LIMIT_DISABLE -1
|
||||
#define REGULATOR_NOTIF_LIMIT_ENABLE -2
|
||||
struct notification_limit {
|
||||
int prot;
|
||||
int err;
|
||||
int warn;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct regulation_constraints - regulator operating constraints.
|
||||
*
|
||||
@ -100,6 +108,11 @@ struct regulator_state {
|
||||
* @ilim_uA: Maximum input current.
|
||||
* @system_load: Load that isn't captured by any consumer requests.
|
||||
*
|
||||
* @over_curr_limits: Limits for acting on over current.
|
||||
* @over_voltage_limits: Limits for acting on over voltage.
|
||||
* @under_voltage_limits: Limits for acting on under voltage.
|
||||
* @temp_limits: Limits for acting on over temperature.
|
||||
|
||||
* @max_spread: Max possible spread between coupled regulators
|
||||
* @max_uV_step: Max possible step change in voltage
|
||||
* @valid_modes_mask: Mask of modes which may be configured by consumers.
|
||||
@ -116,6 +129,11 @@ struct regulator_state {
|
||||
* @pull_down: Enable pull down when regulator is disabled.
|
||||
* @over_current_protection: Auto disable on over current event.
|
||||
*
|
||||
* @over_current_detection: Configure over current limits.
|
||||
* @over_voltage_detection: Configure over voltage limits.
|
||||
* @under_voltage_detection: Configure under voltage limits.
|
||||
* @over_temp_detection: Configure over temperature limits.
|
||||
*
|
||||
* @input_uV: Input voltage for regulator when supplied by another regulator.
|
||||
*
|
||||
* @state_disk: State for regulator when system is suspended in disk mode.
|
||||
@ -172,6 +190,10 @@ struct regulation_constraints {
|
||||
struct regulator_state state_disk;
|
||||
struct regulator_state state_mem;
|
||||
struct regulator_state state_standby;
|
||||
struct notification_limit over_curr_limits;
|
||||
struct notification_limit over_voltage_limits;
|
||||
struct notification_limit under_voltage_limits;
|
||||
struct notification_limit temp_limits;
|
||||
suspend_state_t initial_state; /* suspend state to set at init */
|
||||
|
||||
/* mode to set on startup */
|
||||
@ -193,6 +215,10 @@ struct regulation_constraints {
|
||||
unsigned soft_start:1; /* ramp voltage slowly */
|
||||
unsigned pull_down:1; /* pull down resistor when regulator off */
|
||||
unsigned over_current_protection:1; /* auto disable on over current */
|
||||
unsigned over_current_detection:1; /* notify on over current */
|
||||
unsigned over_voltage_detection:1; /* notify on over voltage */
|
||||
unsigned under_voltage_detection:1; /* notify on under voltage */
|
||||
unsigned over_temp_detection:1; /* notify on over temperature */
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user