summaryrefslogtreecommitdiff
path: root/drivers/regulator/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator/core.c')
-rw-r--r--drivers/regulator/core.c316
1 files changed, 227 insertions, 89 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index e20e77e4c159..ca6caba8a191 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -33,17 +33,6 @@
#include "dummy.h"
#include "internal.h"
-#define rdev_crit(rdev, fmt, ...) \
- pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
-#define rdev_err(rdev, fmt, ...) \
- pr_err("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
-#define rdev_warn(rdev, fmt, ...) \
- pr_warn("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
-#define rdev_info(rdev, fmt, ...) \
- pr_info("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
-#define rdev_dbg(rdev, fmt, ...) \
- pr_debug("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
-
static DEFINE_WW_CLASS(regulator_ww_class);
static DEFINE_MUTEX(regulator_nesting_mutex);
static DEFINE_MUTEX(regulator_list_mutex);
@@ -117,6 +106,7 @@ const char *rdev_get_name(struct regulator_dev *rdev)
else
return "";
}
+EXPORT_SYMBOL_GPL(rdev_get_name);
static bool have_full_constraints(void)
{
@@ -591,8 +581,8 @@ regulator_get_suspend_state_check(struct regulator_dev *rdev, suspend_state_t st
return rstate;
}
-static ssize_t regulator_uV_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t microvolts_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
int uV;
@@ -605,16 +595,16 @@ static ssize_t regulator_uV_show(struct device *dev,
return uV;
return sprintf(buf, "%d\n", uV);
}
-static DEVICE_ATTR(microvolts, 0444, regulator_uV_show, NULL);
+static DEVICE_ATTR_RO(microvolts);
-static ssize_t regulator_uA_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t microamps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", _regulator_get_current_limit(rdev));
}
-static DEVICE_ATTR(microamps, 0444, regulator_uA_show, NULL);
+static DEVICE_ATTR_RO(microamps);
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -645,14 +635,14 @@ static ssize_t regulator_print_opmode(char *buf, int mode)
return sprintf(buf, "%s\n", regulator_opmode_to_str(mode));
}
-static ssize_t regulator_opmode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t opmode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return regulator_print_opmode(buf, _regulator_get_mode(rdev));
}
-static DEVICE_ATTR(opmode, 0444, regulator_opmode_show, NULL);
+static DEVICE_ATTR_RO(opmode);
static ssize_t regulator_print_state(char *buf, int state)
{
@@ -664,8 +654,8 @@ static ssize_t regulator_print_state(char *buf, int state)
return sprintf(buf, "unknown\n");
}
-static ssize_t regulator_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
ssize_t ret;
@@ -676,10 +666,10 @@ static ssize_t regulator_state_show(struct device *dev,
return ret;
}
-static DEVICE_ATTR(state, 0444, regulator_state_show, NULL);
+static DEVICE_ATTR_RO(state);
-static ssize_t regulator_status_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
int status;
@@ -723,10 +713,10 @@ static ssize_t regulator_status_show(struct device *dev,
return sprintf(buf, "%s\n", label);
}
-static DEVICE_ATTR(status, 0444, regulator_status_show, NULL);
+static DEVICE_ATTR_RO(status);
-static ssize_t regulator_min_uA_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t min_microamps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
@@ -735,10 +725,10 @@ static ssize_t regulator_min_uA_show(struct device *dev,
return sprintf(buf, "%d\n", rdev->constraints->min_uA);
}
-static DEVICE_ATTR(min_microamps, 0444, regulator_min_uA_show, NULL);
+static DEVICE_ATTR_RO(min_microamps);
-static ssize_t regulator_max_uA_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t max_microamps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
@@ -747,10 +737,10 @@ static ssize_t regulator_max_uA_show(struct device *dev,
return sprintf(buf, "%d\n", rdev->constraints->max_uA);
}
-static DEVICE_ATTR(max_microamps, 0444, regulator_max_uA_show, NULL);
+static DEVICE_ATTR_RO(max_microamps);
-static ssize_t regulator_min_uV_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t min_microvolts_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
@@ -759,10 +749,10 @@ static ssize_t regulator_min_uV_show(struct device *dev,
return sprintf(buf, "%d\n", rdev->constraints->min_uV);
}
-static DEVICE_ATTR(min_microvolts, 0444, regulator_min_uV_show, NULL);
+static DEVICE_ATTR_RO(min_microvolts);
-static ssize_t regulator_max_uV_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t max_microvolts_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
@@ -771,10 +761,10 @@ static ssize_t regulator_max_uV_show(struct device *dev,
return sprintf(buf, "%d\n", rdev->constraints->max_uV);
}
-static DEVICE_ATTR(max_microvolts, 0444, regulator_max_uV_show, NULL);
+static DEVICE_ATTR_RO(max_microvolts);
-static ssize_t regulator_total_uA_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t requested_microamps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
struct regulator *regulator;
@@ -788,7 +778,7 @@ static ssize_t regulator_total_uA_show(struct device *dev,
regulator_unlock(rdev);
return sprintf(buf, "%d\n", uA);
}
-static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL);
+static DEVICE_ATTR_RO(requested_microamps);
static ssize_t num_users_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -813,104 +803,95 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(type);
-static ssize_t regulator_suspend_mem_uV_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_mem_microvolts_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", rdev->constraints->state_mem.uV);
}
-static DEVICE_ATTR(suspend_mem_microvolts, 0444,
- regulator_suspend_mem_uV_show, NULL);
+static DEVICE_ATTR_RO(suspend_mem_microvolts);
-static ssize_t regulator_suspend_disk_uV_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_disk_microvolts_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", rdev->constraints->state_disk.uV);
}
-static DEVICE_ATTR(suspend_disk_microvolts, 0444,
- regulator_suspend_disk_uV_show, NULL);
+static DEVICE_ATTR_RO(suspend_disk_microvolts);
-static ssize_t regulator_suspend_standby_uV_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_standby_microvolts_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", rdev->constraints->state_standby.uV);
}
-static DEVICE_ATTR(suspend_standby_microvolts, 0444,
- regulator_suspend_standby_uV_show, NULL);
+static DEVICE_ATTR_RO(suspend_standby_microvolts);
-static ssize_t regulator_suspend_mem_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_mem_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return regulator_print_opmode(buf,
rdev->constraints->state_mem.mode);
}
-static DEVICE_ATTR(suspend_mem_mode, 0444,
- regulator_suspend_mem_mode_show, NULL);
+static DEVICE_ATTR_RO(suspend_mem_mode);
-static ssize_t regulator_suspend_disk_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_disk_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return regulator_print_opmode(buf,
rdev->constraints->state_disk.mode);
}
-static DEVICE_ATTR(suspend_disk_mode, 0444,
- regulator_suspend_disk_mode_show, NULL);
+static DEVICE_ATTR_RO(suspend_disk_mode);
-static ssize_t regulator_suspend_standby_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_standby_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return regulator_print_opmode(buf,
rdev->constraints->state_standby.mode);
}
-static DEVICE_ATTR(suspend_standby_mode, 0444,
- regulator_suspend_standby_mode_show, NULL);
+static DEVICE_ATTR_RO(suspend_standby_mode);
-static ssize_t regulator_suspend_mem_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_mem_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return regulator_print_state(buf,
rdev->constraints->state_mem.enabled);
}
-static DEVICE_ATTR(suspend_mem_state, 0444,
- regulator_suspend_mem_state_show, NULL);
+static DEVICE_ATTR_RO(suspend_mem_state);
-static ssize_t regulator_suspend_disk_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_disk_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return regulator_print_state(buf,
rdev->constraints->state_disk.enabled);
}
-static DEVICE_ATTR(suspend_disk_state, 0444,
- regulator_suspend_disk_state_show, NULL);
+static DEVICE_ATTR_RO(suspend_disk_state);
-static ssize_t regulator_suspend_standby_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t suspend_standby_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return regulator_print_state(buf,
rdev->constraints->state_standby.enabled);
}
-static DEVICE_ATTR(suspend_standby_state, 0444,
- regulator_suspend_standby_state_show, NULL);
+static DEVICE_ATTR_RO(suspend_standby_state);
-static ssize_t regulator_bypass_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t bypass_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
const char *report;
@@ -928,8 +909,7 @@ static ssize_t regulator_bypass_show(struct device *dev,
return sprintf(buf, "%s\n", report);
}
-static DEVICE_ATTR(bypass, 0444,
- regulator_bypass_show, NULL);
+static DEVICE_ATTR_RO(bypass);
/* Calculate the new optimum regulator operating mode based on the new total
* consumer load. All locks held by caller
@@ -1315,6 +1295,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
@@ -1400,9 +1426,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));
@@ -1410,6 +1454,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;
@@ -4111,6 +4211,29 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
}
EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel);
+int regulator_sync_voltage_rdev(struct regulator_dev *rdev)
+{
+ int ret;
+
+ regulator_lock(rdev);
+
+ if (!rdev->desc->ops->set_voltage &&
+ !rdev->desc->ops->set_voltage_sel) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* balance only, if regulator is coupled */
+ if (rdev->coupling_desc.n_coupled > 1)
+ ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON);
+ else
+ ret = -EOPNOTSUPP;
+
+out:
+ regulator_unlock(rdev);
+ return ret;
+}
+
/**
* regulator_sync_voltage - re-apply last regulator output voltage
* @regulator: regulator source
@@ -4386,22 +4509,36 @@ unsigned int regulator_get_mode(struct regulator *regulator)
}
EXPORT_SYMBOL_GPL(regulator_get_mode);
+static int rdev_get_cached_err_flags(struct regulator_dev *rdev)
+{
+ int ret = 0;
+
+ if (rdev->use_cached_err) {
+ spin_lock(&rdev->err_lock);
+ ret = rdev->cached_err;
+ spin_unlock(&rdev->err_lock);
+ }
+ return ret;
+}
+
static int _regulator_get_error_flags(struct regulator_dev *rdev,
unsigned int *flags)
{
- int ret;
+ int cached_flags, ret = 0;
regulator_lock(rdev);
- /* sanity check */
- if (!rdev->desc->ops->get_error_flags) {
+ cached_flags = rdev_get_cached_err_flags(rdev);
+
+ if (rdev->desc->ops->get_error_flags)
+ ret = rdev->desc->ops->get_error_flags(rdev, flags);
+ else if (!rdev->use_cached_err)
ret = -EINVAL;
- goto out;
- }
- ret = rdev->desc->ops->get_error_flags(rdev, flags);
-out:
+ *flags |= cached_flags;
+
regulator_unlock(rdev);
+
return ret;
}
@@ -5234,6 +5371,7 @@ regulator_register(const struct regulator_desc *regulator_desc,
goto rinse;
}
device_initialize(&rdev->dev);
+ spin_lock_init(&rdev->err_lock);
/*
* Duplicate the config so the driver could override it after