Commit 04eca28c authored by Tuomas Tynkkynen's avatar Tuomas Tynkkynen Committed by Mark Brown

regulator: Add helpers for low-level register access

Add helper functions that allow regulator consumers to obtain low-level
details about the regulator hardware, like the voltage selector register
address and such. These details can be useful when configuring hardware
or firmware that want to do low-level access to regulators, with no
involvement from the kernel.

The use-case for Tegra is a voltage-controlled oscillator clocksource
which has control logic to change the supply voltage via I2C to achieve
a desired output clock rate.
Signed-off-by: default avatarTuomas Tynkkynen <ttynkkynen@nvidia.com>
Signed-off-by: default avatarMark Brown <broonie@linaro.org>
parent 7171511e
......@@ -180,3 +180,38 @@ int regulator_unregister_notifier(struct regulator *regulator,
Regulators use the kernel notifier framework to send event to their interested
consumers.
7. Regulator Direct Register Access
===================================
Some kinds of power management hardware or firmware are designed such that
they need to do low-level hardware access to regulators, with no involvement
from the kernel. Examples of such devices are:
- clocksource with a voltage-controlled oscillator and control logic to change
the supply voltage over I2C to achieve a desired output clock rate
- thermal management firmware that can issue an arbitrary I2C transaction to
perform system poweroff during overtemperature conditions
To set up such a device/firmware, various parameters like I2C address of the
regulator, addresses of various regulator registers etc. need to be configured
to it. The regulator framework provides the following helpers for querying
these details.
Bus-specific details, like I2C addresses or transfer rates are handled by the
regmap framework. To get the regulator's regmap (if supported), use :-
struct regmap *regulator_get_regmap(struct regulator *regulator);
To obtain the hardware register offset and bitmask for the regulator's voltage
selector register, use :-
int regulator_get_hardware_vsel_register(struct regulator *regulator,
unsigned *vsel_reg,
unsigned *vsel_mask);
To convert a regulator framework voltage selector code (used by
regulator_list_voltage) to a hardware-specific voltage selector that can be
directly written to the voltage selector register, use :-
int regulator_list_hardware_vsel(struct regulator *regulator,
unsigned selector);
......@@ -2221,6 +2221,77 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector)
}
EXPORT_SYMBOL_GPL(regulator_list_voltage);
/**
* regulator_get_regmap - get the regulator's register map
* @regulator: regulator source
*
* Returns the register map for the given regulator, or an ERR_PTR value
* if the regulator doesn't use regmap.
*/
struct regmap *regulator_get_regmap(struct regulator *regulator)
{
struct regmap *map = regulator->rdev->regmap;
return map ? map : ERR_PTR(-EOPNOTSUPP);
}
/**
* regulator_get_hardware_vsel_register - get the HW voltage selector register
* @regulator: regulator source
* @vsel_reg: voltage selector register, output parameter
* @vsel_mask: mask for voltage selector bitfield, output parameter
*
* Returns the hardware register offset and bitmask used for setting the
* regulator voltage. This might be useful when configuring voltage-scaling
* hardware or firmware that can make I2C requests behind the kernel's back,
* for example.
*
* On success, the output parameters @vsel_reg and @vsel_mask are filled in
* and 0 is returned, otherwise a negative errno is returned.
*/
int regulator_get_hardware_vsel_register(struct regulator *regulator,
unsigned *vsel_reg,
unsigned *vsel_mask)
{
struct regulator_dev *rdev = regulator->rdev;
struct regulator_ops *ops = rdev->desc->ops;
if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
return -EOPNOTSUPP;
*vsel_reg = rdev->desc->vsel_reg;
*vsel_mask = rdev->desc->vsel_mask;
return 0;
}
EXPORT_SYMBOL_GPL(regulator_get_hardware_vsel_register);
/**
* regulator_list_hardware_vsel - get the HW-specific register value for a selector
* @regulator: regulator source
* @selector: identify voltage to list
*
* Converts the selector to a hardware-specific voltage selector that can be
* directly written to the regulator registers. The address of the voltage
* register can be determined by calling @regulator_get_hardware_vsel_register.
*
* On error a negative errno is returned.
*/
int regulator_list_hardware_vsel(struct regulator *regulator,
unsigned selector)
{
struct regulator_dev *rdev = regulator->rdev;
struct regulator_ops *ops = rdev->desc->ops;
if (selector >= rdev->desc->n_voltages)
return -EINVAL;
if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
return -EOPNOTSUPP;
return selector;
}
EXPORT_SYMBOL_GPL(regulator_list_hardware_vsel);
/**
* regulator_get_linear_step - return the voltage step size between VSEL values
* @regulator: regulator source
......
......@@ -37,6 +37,7 @@
struct device;
struct notifier_block;
struct regmap;
/*
* Regulator operating modes.
......@@ -215,6 +216,13 @@ int regulator_set_optimum_mode(struct regulator *regulator, int load_uA);
int regulator_allow_bypass(struct regulator *regulator, bool allow);
struct regmap *regulator_get_regmap(struct regulator *regulator);
int regulator_get_hardware_vsel_register(struct regulator *regulator,
unsigned *vsel_reg,
unsigned *vsel_mask);
int regulator_list_hardware_vsel(struct regulator *regulator,
unsigned selector);
/* regulator notifier block */
int regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb);
......@@ -452,6 +460,24 @@ static inline int regulator_allow_bypass(struct regulator *regulator,
return 0;
}
struct regmap *regulator_get_regmap(struct regulator *regulator)
{
return ERR_PTR(-EOPNOTSUPP);
}
int regulator_get_hardware_vsel_register(struct regulator *regulator,
unsigned *vsel_reg,
unsigned *vsel_mask)
{
return -EOPNOTSUPP;
}
int regulator_list_hardware_vsel(struct regulator *regulator,
unsigned selector)
{
return -EOPNOTSUPP;
}
static inline int regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment