The hardware has two current sources ISRC0 and ISRC1 to allow measuring resistors without additional circuitry. This commit makes them available as regulators. Tested on an imx233-olinuxino board. Signed-off-by: Harald Geyer <harald@xxxxxxxxx> --- The current regulator API doesn't fit this type of device very well: Typically consumers will want to set a defined current, ie. min_uA == max_uA, but they can't without help from configuration data, because the valid values aren't reported by the API for current regulators. I have been thinking about extending the API, but currently AFAIK no such consumers exist and most users, like myself, will force the regulator to a defined value in devicetree anyway. .../bindings/staging/iio/adc/mxs-lradc.txt | 29 ++++ drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/mxs-lradc.c | 152 +++++++++++++++++++++ 3 files changed, 182 insertions(+) diff --git a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt index 555fb11..983952c 100644 --- a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt +++ b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt @@ -19,6 +19,15 @@ Optional properties: - fsl,settling: delay between plate switch to next sample. Allowed value is 1 ... 2047. It counts at 2 kHz and its default is 10 (= 5 ms) +- ISRC0: A node describing the regulator of internal current source 0 +- ISRC1: A node describing the regulator of internal current source 1 + +Required properties for the ISRCx sub-nodes: +- regulator-max-microamp: See standard regulator binding documentation. + Valid values are from 0 to 300 in steps of 20. + +Optional properties for the ISRCx sub-nodes: +Any standard regulator properties that apply to current regulators. Example for i.MX23 SoC: @@ -31,6 +40,16 @@ Example for i.MX23 SoC: fsl,ave-ctrl = <4>; fsl,ave-delay = <2>; fsl,settling = <10>; + + isrc_lradc0: ISRC0 { + regulator-max-microamp = <300>; + }; + + isrc_lradc1: ISRC1 { + regulator-max-microamp = <120>; + regulator-min-microamp = <120>; + regulator-always-on; + }; }; Example for i.MX28 SoC: @@ -44,4 +63,14 @@ Example for i.MX28 SoC: fsl,ave-ctrl = <4>; fsl,ave-delay = <2>; fsl,settling = <10>; + + isrc_lradc0: ISRC0 { + regulator-max-microamp = <300>; + }; + + isrc_lradc6: ISRC1 { + regulator-max-microamp = <120>; + regulator-min-microamp = <120>; + regulator-always-on; + }; }; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 5937030..1968d1c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -319,6 +319,7 @@ config MXS_LRADC tristate "Freescale i.MX23/i.MX28 LRADC" depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM depends on INPUT + depends on REGULATOR select STMP_DEVICE select IIO_BUFFER select IIO_TRIGGERED_BUFFER diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c index 33051b8..f22f339 100644 --- a/drivers/iio/adc/mxs-lradc.c +++ b/drivers/iio/adc/mxs-lradc.c @@ -40,6 +40,10 @@ #include <linux/iio/triggered_buffer.h> #include <linux/iio/sysfs.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + #define DRIVER_NAME "mxs-lradc" #define LRADC_MAX_DELAY_CHANS 4 @@ -261,6 +265,9 @@ struct mxs_lradc { unsigned over_sample_delay; /* time in clocks to wait after the plates where switched */ unsigned settling_delay; + + struct regulator_desc isrc0; + struct regulator_desc isrc1; }; #define LRADC_CTRL0 0x00 @@ -305,6 +312,11 @@ struct mxs_lradc { #define LRADC_CTRL2 0x20 #define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24 #define LRADC_CTRL2_TEMPSENSE_PWD BIT(15) +#define LRADC_CTRL2_TEMP_SENSOR_IENABLE1 BIT(9) +#define LRADC_CTRL2_TEMP_SENSOR_IENABLE0 BIT(8) +#define LRADC_CTRL2_TEMP_ISRC1_OFFSET 4 +#define LRADC_CTRL2_TEMP_ISRC0_OFFSET 0 +#define LRADC_CTRL2_TEMP_ISRC_MASK 0x0f #define LRADC_STATUS 0x40 #define LRADC_STATUS_TOUCH_DETECT_RAW BIT(0) @@ -1383,6 +1395,109 @@ static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = { .validate_scan_mask = &mxs_lradc_validate_scan_mask, }; +static int mxs_lradc_regulator_is_enabled(struct regulator_dev *dev) +{ + struct mxs_lradc *lradc = rdev_get_drvdata(dev); + int reg = readl(lradc->base + LRADC_CTRL2); + + if (dev->desc == &lradc->isrc0) + return reg & LRADC_CTRL2_TEMP_SENSOR_IENABLE0; + else if (dev->desc == &lradc->isrc1) + return reg & LRADC_CTRL2_TEMP_SENSOR_IENABLE1; + + /* This should never happen */ + return -ENODEV; +} + +#define LRADC_REGVALUE2uA(regval, offset) \ + (20 * ((regval >> offset) & LRADC_CTRL2_TEMP_ISRC_MASK)) + +static int mxs_lradc_regulator_get_current_limit(struct regulator_dev *dev) +{ + struct mxs_lradc *lradc = rdev_get_drvdata(dev); + int reg = readl(lradc->base + LRADC_CTRL2); + + if (dev->desc == &lradc->isrc0) + return LRADC_REGVALUE2uA(reg, LRADC_CTRL2_TEMP_ISRC0_OFFSET); + else if (dev->desc == &lradc->isrc1) + return LRADC_REGVALUE2uA(reg, LRADC_CTRL2_TEMP_ISRC1_OFFSET); + + /* This should never happen */ + return -ENODEV; +} + +static int mxs_lradc_regulator_enable(struct regulator_dev *dev) +{ + struct mxs_lradc *lradc = rdev_get_drvdata(dev); + + if (dev->desc == &lradc->isrc0) + mxs_lradc_reg_set(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0, + LRADC_CTRL2); + else if (dev->desc == &lradc->isrc1) + mxs_lradc_reg_set(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE1, + LRADC_CTRL2); + else + /* This should never happen */ + return -ENODEV; + + return 0; +} + +static int mxs_lradc_regulator_disable(struct regulator_dev *dev) +{ + struct mxs_lradc *lradc = rdev_get_drvdata(dev); + + if (dev->desc == &lradc->isrc0) + mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0, + LRADC_CTRL2); + else if (dev->desc == &lradc->isrc1) + mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE1, + LRADC_CTRL2); + else + /* This should never happen */ + return -ENODEV; + + return 0; +} + +static int mxs_lradc_regulator_set_current_limit(struct regulator_dev *dev, + int min_uA, int max_uA) +{ + struct mxs_lradc *lradc = rdev_get_drvdata(dev); + int offset, value; + + if (dev->desc == &lradc->isrc0) + offset = LRADC_CTRL2_TEMP_ISRC0_OFFSET; + else if (dev->desc == &lradc->isrc1) + offset = LRADC_CTRL2_TEMP_ISRC1_OFFSET; + else + /* This should never happen */ + return -ENODEV; + + value = min_uA / 20; + if (min_uA % 20) + value++; + if (value * 20 > max_uA) + return -EINVAL; + if (value & ~LRADC_CTRL2_TEMP_ISRC_MASK) + /* This should never happen */ + return -EPERM; + + mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_ISRC_MASK << offset, + LRADC_CTRL2); + mxs_lradc_reg_set(lradc, value << offset, LRADC_CTRL2); + + return 0; +} + +static struct regulator_ops mxs_lradc_regulator_current_ops = { + .enable = mxs_lradc_regulator_enable, + .is_enabled = mxs_lradc_regulator_is_enabled, + .disable = mxs_lradc_regulator_disable, + .get_current_limit = mxs_lradc_regulator_get_current_limit, + .set_current_limit = mxs_lradc_regulator_set_current_limit, +}; + /* * Driver initialization */ @@ -1519,6 +1634,10 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc) for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++) mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i)); + + mxs_lradc_reg_clear(lradc, LRADC_CTRL2_TEMP_SENSOR_IENABLE0 | + LRADC_CTRL2_TEMP_SENSOR_IENABLE1, + LRADC_CTRL2); } static const struct of_device_id mxs_lradc_dt_ids[] = { @@ -1592,6 +1711,32 @@ static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc, return 0; } +static void mxs_lradc_reg_helper(struct device_node *np, const char *name, + struct regulator_config *conf, + struct regulator_desc *desc) +{ + struct regulator_dev *ret; + + conf->of_node = of_get_child_by_name(np, name); + if (!conf->of_node) + return; + + desc->name = name; + desc->owner = THIS_MODULE; + desc->type = REGULATOR_CURRENT; + desc->ops = &mxs_lradc_regulator_current_ops; + + conf->init_data = of_get_regulator_init_data(conf->dev, conf->of_node, + desc); + ret = devm_regulator_register(conf->dev, desc, conf); + if (IS_ERR(ret)) + /* Just pretend the regulator isn't there */ + dev_err(conf->dev, "Failed to register regulator %s: %ld\n", + desc->name, PTR_ERR(ret)); + + of_node_put(conf->of_node); +} + static int mxs_lradc_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -1603,6 +1748,7 @@ static int mxs_lradc_probe(struct platform_device *pdev) struct mxs_lradc *lradc; struct iio_dev *iio; struct resource *iores; + struct regulator_config regconf; int ret = 0, touch_ret; int i, s; u64 scale_uv; @@ -1727,6 +1873,12 @@ static int mxs_lradc_probe(struct platform_device *pdev) goto err_ts; } + /* Setup regulator devices for current source. */ + regconf.dev = dev; + regconf.driver_data = lradc; + mxs_lradc_reg_helper(node, "ISRC0", ®conf, &lradc->isrc0); + mxs_lradc_reg_helper(node, "ISRC1", ®conf, &lradc->isrc1); + return 0; err_ts: -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html