From patchwork Mon Jul 24 20:51:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Sverdlin X-Patchwork-Id: 9860561 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 246E6601A1 for ; Mon, 24 Jul 2017 21:08:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 161D3281F9 for ; Mon, 24 Jul 2017 21:08:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0AA2528385; Mon, 24 Jul 2017 21:08:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, DKIM_VALID, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3A8F3281F9 for ; Mon, 24 Jul 2017 21:08:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=Ztf/OQY1j9jrh+jjRZlXpJGNtXAgwxmJu5YqX2icSeQ=; b=oZ0jwOvUJMDWqCibqD/x1Ph0H3 lupZ0dL4WHIAhgLwxXLSCBzLAW8t+SOpfsd44+aHYfsbVM369YblOU+la7TDoPevKd/m/TjamViOf qDrInB4Q5lKyv1Anb7+3XvT58+aC4Qhht0j4NZLdQb/qpF2s4E+V5cCUnEiQRL/ND/25GAwxYAVW8 UuzLXHM8+1DZI2JnhSr7VUJKutQTFaslryhI+W893vF1tZD/gdO0/GMWq9QMKp2DbriXNK6olU9tI mv2LwJpGIf+LZvEoqvU6MHXWuuKDA8HmL8iAX6SulWpjRoOnHyLdjjeS0kC8+Sj6aXM/6hKxDiVua LOStiP5Q==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dZkau-0002Vd-JE; Mon, 24 Jul 2017 21:08:48 +0000 Received: from casper.infradead.org ([2001:8b0:10b:1236::1]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dZkaf-000239-Iz for linux-arm-kernel@bombadil.infradead.org; Mon, 24 Jul 2017 21:08:33 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Sender:Reply-To:MIME-Version:Content-Type: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=IWSFgKtCd/htXqPvBLOM685wPKazHAmxsqremIbSnyE=; b=aWpMQgywhM4ZbzgmiSGhN4uLw CDgsi9hlqrbQmbRKs7AjQHauulI2K9g8Aj8qFDdMAxCvIk2t6xBMUd0sJ3DKD9wrQSYjAdyJ/akLn ovQu2nHjIjfFRpE+uCrgNHnVciyMzGOZeZcs6xCYMusQemMaNhfSfPyWT0E0O8hyTXsKCBKNFUPn8 KpMzVlxyMRz/isAyBjOV/Yfk4PE+NFTbOALQw4aJCp6hKFLnpcIi+AKJ+6JH3hq/YQPPXus1m9Syn xP+PFFMnA1l4r7tdCekJGWV33Mw2cIBk1gv6IeTfXuV8Kv+YRqK9NdKPoY0NsMCGTp8VIy0YIU7nB i5zCBVDUw==; Received: from mail-lf0-x244.google.com ([2a00:1450:4010:c07::244]) by casper.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dZkLm-0004ep-W8 for linux-arm-kernel@lists.infradead.org; Mon, 24 Jul 2017 20:53:13 +0000 Received: by mail-lf0-x244.google.com with SMTP id x16so3696258lfb.4 for ; Mon, 24 Jul 2017 13:52:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=IWSFgKtCd/htXqPvBLOM685wPKazHAmxsqremIbSnyE=; b=ez3nobvmzhWvRb37TWO5jQJyp+ZWkw3o+yjbl0TrGc+7PYe3qlZOP4Mf3MehWlu3I/ vmngN23FGaFEIZLIDyJhvbCg3Q48CuY8Hzo8AyV8OxTMt51AS0Uwwytzk1okaJFaRVGf qeGlkU+8uTppSUzO8JxfFhUCr1mNQyFe1qzCUSCns33+uFxEKnYeiGrEpj+sob4O6jMR 46cLtR24sRZCsV8FvEhqOk2MPv67/oOYcTUa2NCU0qYBIcgQvw6uKnhc41wqNJ/pGni+ NemuiSyqqs/rvM1yCghh8VtpxDIOG1U214cpOKFtkVcSUjbBt0R7ob8cUY45F50piK+k ra3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=IWSFgKtCd/htXqPvBLOM685wPKazHAmxsqremIbSnyE=; b=koRMBxvVNSG3LUqQBqNbra8UdLfEBkeEWJlAAAB6sD+aawUUG9Mg3UEuK3ZNNcBynZ r+Rv7EJade9/Jby7DYpNNEeAUc09KI75mj5S5+0L/mXTgdDEzOrVaaOkLr1CC9OnyWnn zpyWRZ4biGDZSPy8kmRHjLI98ooCXmkvYm6gzZAGg87D65Q+rh6O/i1h9kyGv6bwh+cG gj055iMWxnZ849s0m6vG7ZITusa4elHu4gTd0octhEe80kcMXT6kvM92JR98Cn0XI8If r9W3rv41CpaEESoOlXmLOcZNZUSDS9b5Ao/Sv7vHA2wuStiXj/OPl2cZNZ0Ai5/sJbkP 0zvw== X-Gm-Message-State: AIVw112RQCYe5B80M17l3BZSLh6AWpBO4QOqPybNA88Mdzf+4ReI91BJ ev+HcoujhFZlOQ== X-Received: by 10.25.199.205 with SMTP id x196mr5935681lff.124.1500929568421; Mon, 24 Jul 2017 13:52:48 -0700 (PDT) Received: from giga1.localdomain ([195.245.52.204]) by smtp.gmail.com with ESMTPSA id g24sm2460559lfh.83.2017.07.24.13.52.46 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 24 Jul 2017 13:52:47 -0700 (PDT) From: Alexander Sverdlin To: linux-iio@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH v6] iio: adc: New driver for Cirrus Logic EP93xx ADC Date: Mon, 24 Jul 2017 22:51:45 +0200 Message-Id: <20170724205145.3898-1-alexander.sverdlin@gmail.com> X-Mailer: git-send-email 2.13.2 In-Reply-To: <20170611151837.35fc64a4@kernel.org> References: <20170611151837.35fc64a4@kernel.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170724_215311_206022_11D40186 X-CRM114-Status: GOOD ( 31.82 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Lars-Peter Clausen , Ryan Mallon , Hartley Sweeten , Peter Meerwald-Stadler , Hartmut Knaack , Alexander Sverdlin , Jonathan Cameron MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP New driver adding support for ADC found on Cirrus Logic EP93xx series of SoCs. Board specific code must take care to create plaform device with all necessary resources. Signed-off-by: Alexander Sverdlin Acked-by: Jonathan Cameron --- Changelog v6: Separated from mach-ep93xx/ patches (will come via arm-soc tree) Rebased to v4.13-rc2 v5: Rebased to ea094f3c830a v4: Spelling improvements sign_extend32() usage v3: Rebased to the current master sizeof(struct foo) -> sizeof(*bar) v2: Added Documentation/iio/ep93xx_adc.txt Removed extend_name Added timeout to the status polling loop Used iio_device_register() instead of devm_iio_device_register() Diffstat Documentation/iio/ep93xx_adc.txt | 29 +++++ drivers/iio/adc/Kconfig | 11 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ep93xx_adc.c | 255 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 296 insertions(+) create mode 100644 Documentation/iio/ep93xx_adc.txt create mode 100644 drivers/iio/adc/ep93xx_adc.c diff --git a/Documentation/iio/ep93xx_adc.txt b/Documentation/iio/ep93xx_adc.txt new file mode 100644 index 000000000000..23053e7817bd --- /dev/null +++ b/Documentation/iio/ep93xx_adc.txt @@ -0,0 +1,29 @@ +Cirrus Logic EP93xx ADC driver. + +1. Overview + +The driver is intended to work on both low-end (EP9301, EP9302) devices with +5-channel ADC and high-end (EP9307, EP9312, EP9315) devices with 10-channel +touchscreen/ADC module. + +2. Channel numbering + +Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets. +EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is +not defined. So the last three are numbered randomly, let's say. + +Assuming ep93xx_adc is IIO device0, you'd find the following entries under +/sys/bus/iio/devices/iio:device0/: + + +-----------------+---------------+ + | sysfs entry | ball/pin name | + +-----------------+---------------+ + | in_voltage0_raw | YM | + | in_voltage1_raw | SXP | + | in_voltage2_raw | SXM | + | in_voltage3_raw | SYP | + | in_voltage4_raw | SYM | + | in_voltage5_raw | XP | + | in_voltage6_raw | XM | + | in_voltage7_raw | YP | + +-----------------+---------------+ diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 614fa41559b1..5c1ab02ef730 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -249,6 +249,17 @@ config ENVELOPE_DETECTOR To compile this driver as a module, choose M here: the module will be called envelope-detector. +config EP93XX_ADC + tristate "Cirrus Logic EP93XX ADC driver" + depends on ARCH_EP93XX + help + Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic. + It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this + case driver will reduce its CPU usage by 90% in some use cases. + + To compile this driver as a module, choose M here: the module will be + called ep93xx_adc. + config EXYNOS_ADC tristate "Exynos ADC driver support" depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index b546736a5541..2d266eaf352c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o +obj-$(CONFIG_EP93XX_ADC) += ep93xx_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o obj-$(CONFIG_HI8435) += hi8435.o diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c new file mode 100644 index 000000000000..a179ac476c6d --- /dev/null +++ b/drivers/iio/adc/ep93xx_adc.c @@ -0,0 +1,255 @@ +/* + * Driver for ADC module on the Cirrus Logic EP93xx series of SoCs + * + * Copyright (C) 2015 Alexander Sverdlin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The driver uses polling to get the conversion status. According to EP93xx + * datasheets, reading ADCResult register starts the conversion, but user is also + * responsible for ensuring that delay between adjacent conversion triggers is + * long enough so that maximum allowed conversion rate is not exceeded. This + * basically renders IRQ mode unusable. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This code could benefit from real HR Timers, but jiffy granularity would + * lower ADC conversion rate down to CONFIG_HZ, so we fallback to busy wait + * in such case. + * + * HR Timers-based version loads CPU only up to 10% during back to back ADC + * conversion, while busy wait-based version consumes whole CPU power. + */ +#ifdef CONFIG_HIGH_RES_TIMERS +#define ep93xx_adc_delay(usmin, usmax) usleep_range(usmin, usmax) +#else +#define ep93xx_adc_delay(usmin, usmax) udelay(usmin) +#endif + +#define EP93XX_ADC_RESULT 0x08 +#define EP93XX_ADC_SDR BIT(31) +#define EP93XX_ADC_SWITCH 0x18 +#define EP93XX_ADC_SW_LOCK 0x20 + +struct ep93xx_adc_priv { + struct clk *clk; + void __iomem *base; + int lastch; + struct mutex lock; +}; + +#define EP93XX_ADC_CH(index, dname, swcfg) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .address = swcfg, \ + .datasheet_name = dname, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +/* + * Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets. + * EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is + * not defined. So the last three are numbered randomly, let's say. + */ +static const struct iio_chan_spec ep93xx_adc_channels[8] = { + EP93XX_ADC_CH(0, "YM", 0x608), + EP93XX_ADC_CH(1, "SXP", 0x680), + EP93XX_ADC_CH(2, "SXM", 0x640), + EP93XX_ADC_CH(3, "SYP", 0x620), + EP93XX_ADC_CH(4, "SYM", 0x610), + EP93XX_ADC_CH(5, "XP", 0x601), + EP93XX_ADC_CH(6, "XM", 0x602), + EP93XX_ADC_CH(7, "YP", 0x604), +}; + +static int ep93xx_read_raw(struct iio_dev *iiodev, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct ep93xx_adc_priv *priv = iio_priv(iiodev); + unsigned long timeout; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&priv->lock); + if (priv->lastch != channel->channel) { + priv->lastch = channel->channel; + /* + * Switch register is software-locked, unlocking must be + * immediately followed by write + */ + local_irq_disable(); + writel_relaxed(0xAA, priv->base + EP93XX_ADC_SW_LOCK); + writel_relaxed(channel->address, + priv->base + EP93XX_ADC_SWITCH); + local_irq_enable(); + /* + * Settling delay depends on module clock and could be + * 2ms or 500us + */ + ep93xx_adc_delay(2000, 2000); + } + /* Start the conversion, eventually discarding old result */ + readl_relaxed(priv->base + EP93XX_ADC_RESULT); + /* Ensure maximum conversion rate is not exceeded */ + ep93xx_adc_delay(DIV_ROUND_UP(1000000, 925), + DIV_ROUND_UP(1000000, 925)); + /* At this point conversion must be completed, but anyway... */ + ret = IIO_VAL_INT; + timeout = jiffies + msecs_to_jiffies(1) + 1; + while (1) { + u32 t; + + t = readl_relaxed(priv->base + EP93XX_ADC_RESULT); + if (t & EP93XX_ADC_SDR) { + *value = sign_extend32(t, 15); + break; + } + + if (time_after(jiffies, timeout)) { + dev_err(&iiodev->dev, "Conversion timeout\n"); + ret = -ETIMEDOUT; + break; + } + + cpu_relax(); + } + mutex_unlock(&priv->lock); + return ret; + + case IIO_CHAN_INFO_OFFSET: + /* According to datasheet, range is -25000..25000 */ + *value = 25000; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* Typical supply voltage is 3.3v */ + *value = (1ULL << 32) * 3300 / 50000; + *shift = 32; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info ep93xx_adc_info = { + .driver_module = THIS_MODULE, + .read_raw = ep93xx_read_raw, +}; + +static int ep93xx_adc_probe(struct platform_device *pdev) +{ + int ret; + struct iio_dev *iiodev; + struct ep93xx_adc_priv *priv; + struct clk *pclk; + struct resource *res; + + iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!iiodev) + return -ENOMEM; + priv = iio_priv(iiodev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot obtain memory resource\n"); + return -ENXIO; + } + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(&pdev->dev, "Cannot map memory resource\n"); + return PTR_ERR(priv->base); + } + + iiodev->dev.parent = &pdev->dev; + iiodev->name = dev_name(&pdev->dev); + iiodev->modes = INDIO_DIRECT_MODE; + iiodev->info = &ep93xx_adc_info; + iiodev->num_channels = ARRAY_SIZE(ep93xx_adc_channels); + iiodev->channels = ep93xx_adc_channels; + + priv->lastch = -1; + mutex_init(&priv->lock); + + platform_set_drvdata(pdev, iiodev); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "Cannot obtain clock\n"); + return PTR_ERR(priv->clk); + } + + pclk = clk_get_parent(priv->clk); + if (!pclk) { + dev_warn(&pdev->dev, "Cannot obtain parent clock\n"); + } else { + /* + * This is actually a place for improvement: + * EP93xx ADC supports two clock divisors -- 4 and 16, + * resulting in conversion rates 3750 and 925 samples per second + * with 500us or 2ms settling time respectively. + * One might find this interesting enough to be configurable. + */ + ret = clk_set_rate(priv->clk, clk_get_rate(pclk) / 16); + if (ret) + dev_warn(&pdev->dev, "Cannot set clock rate\n"); + /* + * We can tolerate rate setting failure because the module should + * work in any case. + */ + } + + ret = clk_enable(priv->clk); + if (ret) { + dev_err(&pdev->dev, "Cannot enable clock\n"); + return ret; + } + + ret = iio_device_register(iiodev); + if (ret) + clk_disable(priv->clk); + + return ret; +} + +static int ep93xx_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *iiodev = platform_get_drvdata(pdev); + struct ep93xx_adc_priv *priv = iio_priv(iiodev); + + iio_device_unregister(iiodev); + clk_disable(priv->clk); + + return 0; +} + +static struct platform_driver ep93xx_adc_driver = { + .driver = { + .name = "ep93xx-adc", + }, + .probe = ep93xx_adc_probe, + .remove = ep93xx_adc_remove, +}; +module_platform_driver(ep93xx_adc_driver); + +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_DESCRIPTION("Cirrus Logic EP93XX ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-adc");