From patchwork Fri Apr 22 13:52:25 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Harald Geyer X-Patchwork-Id: 8912221 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B0DC19F1D3 for ; Fri, 22 Apr 2016 14:03:25 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0FABB2024F for ; Fri, 22 Apr 2016 14:03:23 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D2633201F5 for ; Fri, 22 Apr 2016 14:03:21 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1atbed-0005Lp-6t; Fri, 22 Apr 2016 14:01:55 +0000 Received: from h1.radempa.de ([176.9.142.194] helo=mail.cosmopool.net) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1atbeA-0004mJ-BT for linux-arm-kernel@lists.infradead.org; Fri, 22 Apr 2016 14:01:27 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.cosmopool.net (Postfix) with ESMTP id F1180901977; Fri, 22 Apr 2016 15:53:12 +0200 (CEST) Received: from mail.cosmopool.net ([127.0.0.1]) by localhost (mail.your-server.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id SEc0V8t46JVZ; Fri, 22 Apr 2016 15:53:09 +0200 (CEST) Received: from huygens.ccbib.org (unknown [10.0.10.106]) by mail.cosmopool.net (Postfix) with SMTP id 3B036901971; Fri, 22 Apr 2016 15:53:08 +0200 (CEST) Received: by huygens.ccbib.org (sSMTP sendmail emulation); Fri, 22 Apr 2016 13:53:08 +0000 From: Harald Geyer To: Jonathan Cameron , devicetree@vger.kernel.org, linux-iio@vger.kernel.org, Shawn Guo , Sascha Hauer Subject: [PATCH 1/3] iio: mxs-lradc: Add regulators for current sources Date: Fri, 22 Apr 2016 13:52:25 +0000 Message-Id: <1461333147-11873-2-git-send-email-harald@ccbib.org> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1461333147-11873-1-git-send-email-harald@ccbib.org> References: <1461333147-11873-1-git-send-email-harald@ccbib.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160422_070126_753582_B7EA691A X-CRM114-Status: GOOD ( 17.84 ) X-Spam-Score: -1.9 (-) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Stefan Wahren , Marek Vasut , linux-arm-kernel@lists.infradead.org, Harald Geyer MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-5.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 #include +#include +#include +#include + #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: