From patchwork Tue Jun 29 14:08:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Pierre CASTELLAN X-Patchwork-Id: 12350029 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C7F5FC11F66 for ; Tue, 29 Jun 2021 14:08:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A93ED61DB4 for ; Tue, 29 Jun 2021 14:08:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232790AbhF2OKz (ORCPT ); Tue, 29 Jun 2021 10:10:55 -0400 Received: from mail-db8eur05on2092.outbound.protection.outlook.com ([40.107.20.92]:1058 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S234309AbhF2OKw (ORCPT ); Tue, 29 Jun 2021 10:10:52 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=AG+hc1cL3/eOcfT2KUszAVBZfO6+bBWIKY/uYo8Mn+thGnb13AWsFt4BwZSGhLcfP2Q6nm//M9yT5X7e3AXvI0bnBDl5iO74lVeU7FNq0a6DQTsDr6ArjNCZY2GC1YMHDl+YA43duJQLjfc0GginPtdFo3giwzDM40EF02MxtBYLTR5MINihfnJXaXwkAGDiJHRps3ML1FYaGTBkKpLICppSGlHrmFROGMttKU3udFjZo86x28HEqOgQy/BTbTXaqTd2jx3wP8ViK71mRqMSRTwwT451rq7x4QB1/upLI8lqBvvlwMnPwS2tgndUxtVr9kw5j9BKNJ3fRPguLL+05w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WCH3ng6zvJFDMwvaoggxq+SQ4UeOZpoVwTKzgQSbfbM=; b=hmACxAlncOxHyvkHjhXNlRCxqvNUY6I4kWFi4FzVPiDa9VdW63AXTav1rCJnve4xmnjYAThkMyKc0bMLgCdQgDvz/Qxu+0usWwvmyLH6nvzA52sUbZl22wdgUACDwoWa9NchTIkDt5BJTlerXy+W7vhz0Zl9UhY7Yq/m/mIJ5uPlWVVgEb4hKbokv6kHs6nVf+BJhUZ4lws8UGwnEqgl7n6qxec603XU9Kp+k12dVzJIyp1Jqn4PArSPAHM52KxIsaVBO3i/+Nr8yfZGx5XXznXKPXyEMzcmqGtVt6BFohCH1ILb015kWmCWPTenWUHO5r8Q+n+7yMgo566wgSL66g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=non.se.com; dmarc=pass action=none header.from=non.se.com; dkim=pass header.d=non.se.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=non.se.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WCH3ng6zvJFDMwvaoggxq+SQ4UeOZpoVwTKzgQSbfbM=; b=MKB9WlZmLJG/qBCk0y2Pl0HXUhtXrUiSPc20nbnymxnRF8suk9igyZE1cKQ0znUV9v1IfCyFbuP4kjfHVH3KRmPtboIX75mp37XO03xZHVqu5shTqPi4RBWMiI/vrIHS/ksgqFB9FsVAtlqw9pn9qPAYGcPOV6sb0bWniZks7OUb6SVN6W4thjpcXgEP6/KELq4SdBKSV6TCzqtI1NE52d08ZDaWkFd+tOIZfV14zGGkeDVhHnVd0ifP+H7yHAnv0gvzibAHNSyMa/IbfHQx10SSbbWzaOtg2ohzb5LVIUU1zNpDbQ1FxCM1ibcT2qEP+pi75Uv2Uqfv7ml/CdZCSQ== Received: from DB6PR04MB2967.eurprd04.prod.outlook.com (2603:10a6:6:a::10) by DB7PR04MB4524.eurprd04.prod.outlook.com (2603:10a6:5:3b::28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4242.21; Tue, 29 Jun 2021 14:08:19 +0000 Received: from DB6PR04MB2967.eurprd04.prod.outlook.com ([fe80::bd7b:73a0:7819:6152]) by DB6PR04MB2967.eurprd04.prod.outlook.com ([fe80::bd7b:73a0:7819:6152%6]) with mapi id 15.20.4264.026; Tue, 29 Jun 2021 14:08:19 +0000 From: Pierre CASTELLAN To: Jonathan Cameron , Peter Meerwald-Stadler CC: Milan Stevanovic , "linux-iio@vger.kernel.org" , Tomasz Motyl , FREDERIC LOREAUD , Jonathan Cameron , Hartmut Knaack , Lars-Peter Clausen , "linux-kernel@vger.kernel.org" Subject: [PATCH v2] iio:adc: Add RZ/N1D ADC driver Thread-Topic: [PATCH v2] iio:adc: Add RZ/N1D ADC driver Thread-Index: Adds74qZWK7TAdIbQAy+IJqbSSmyaA== Date: Tue, 29 Jun 2021 14:08:19 +0000 Message-ID: Accept-Language: en-US Content-Language: fr-FR X-MS-Has-Attach: X-MS-TNEF-Correlator: msip_labels: MSIP_Label_23f93e5f-d3c2-49a7-ba94-15405423c204_Enabled=true; MSIP_Label_23f93e5f-d3c2-49a7-ba94-15405423c204_SetDate=2021-06-29T14:08:17Z; MSIP_Label_23f93e5f-d3c2-49a7-ba94-15405423c204_Method=Standard; MSIP_Label_23f93e5f-d3c2-49a7-ba94-15405423c204_Name=SE Internal; MSIP_Label_23f93e5f-d3c2-49a7-ba94-15405423c204_SiteId=6e51e1ad-c54b-4b39-b598-0ffe9ae68fef; MSIP_Label_23f93e5f-d3c2-49a7-ba94-15405423c204_ActionId=b0294f6d-2ece-45c2-9fae-316971dccd29; MSIP_Label_23f93e5f-d3c2-49a7-ba94-15405423c204_ContentBits=2 authentication-results: kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=none action=none header.from=non.se.com; x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 7882d3ad-d30e-4cf5-134e-08d93b075946 x-ms-traffictypediagnostic: DB7PR04MB4524: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:5516; x-ms-exchange-senderadcheck: 1 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: y0WxLrc3g2OApFaHr4q4XmI4tdst9COw5mbe17KheTyQ82JVNE9MF6d04LsFxsRNW3ro+JGoJ4prXynGR7hF5aD9DyqAmDrJZHGq25L/U7CCFXUIr6xPdu+RJgIueG36io8RO0CbQYuDgXbFd32OaACbyw1O9B3sSkjda+EpYLbaBFcnZpxeP3FrAIC9A/nAP+1Zk4hszMVwI3wZmpL4Fopc7ASk4GAzZeuzxSbC0sCc1qVVPWad7mNVF6pibBIhB0p+76v6YzBRw43ah5U8Fkp7f9mqgtd7dCODtRr6oICoh34J/IW6tZhuQOr6eLV473MfSciLMOhIQkjTR2rCHJEFM18LIVbz/GQZzHWxfzJd5ZozL0LczvVhJ7Z3MV6PphVtqrlrxvdBIQWz5d96P2fbxKbriPptQhWH6asL5FPTETuOWtAoWYUwxWUTLJmaxqdzWsslX9ehRVdeqq849CW2CWRgZBvpwLPIicUR5B0OSAhvgBqzt3EfsXewGMu9bvxQGb65HMxaCryTyiQO7VM4ohUyzBGUn/+T38yOuuxFwivUYlSJ/fcPAkeav3AwcF+xXiPPsg2MUu+g+UwGSyNHANffRZIQNzdHItei3dSF1PJiQbUBi3XlfXYT+4+Wg/OuM1ceMyWqpGS0jn/2xV4e489NpxF4UPirxAxDOUnFOyNehEi4ebK1VT/rz3JcTTEOjwjCBCXiqVn8Eoh6KBp9RTddyoyP4tUyr8L2QxLWGty9zyklIlPjARl4g5OkVslCFd1V1nlPSOudkVeITg== x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DB6PR04MB2967.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(4636009)(39860400002)(136003)(346002)(396003)(366004)(376002)(66446008)(66476007)(55016002)(64756008)(66556008)(2906002)(54906003)(66946007)(9686003)(86362001)(966005)(76116006)(71200400001)(7696005)(55236004)(8676002)(6506007)(5660300002)(26005)(66574015)(52536014)(122000001)(45080400002)(38100700002)(186003)(33656002)(30864003)(83380400001)(316002)(110136005)(4326008)(8936002)(478600001)(559001)(579004);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?q?0fyMlV2C9Zhk0gWyJzh9Pib?= =?iso-8859-1?q?ZG3P6WQWh5fKizyX8/VcWw7OlhfM2XJl4gdY+GWFyco+Pf0PiaWJj0lNykmG?= =?iso-8859-1?q?VKnFbmRakSYk9KuFuRBltf6vAN3dynQpNk1tBtL2/1Zl92jaXcfUKRcwMVV4?= =?iso-8859-1?q?WHv0HF9Yy35Al+ulV+aEjrTR+qRcLGZwvaoSndcUOKOCcQvquOE5eNT6uN57?= =?iso-8859-1?q?uLUXqlS0YO4/4lu9eDhCSCQ3PRH04YzQlY9eiNQdI8G6mBT64xHHlrnXJcBb?= =?iso-8859-1?q?bHF/+iLjUleNJctXyTsNMvOQEErNE2ufSsKf05BedOQ8YDrC/qeLwekdWSA0?= =?iso-8859-1?q?ZAtAVeHfvMiUO88Uo59g5AGbHHt9DI5ieuCfH+FN9gPyn9rJvuhedoXnuSa6?= =?iso-8859-1?q?AJUolbQsFFErO2/mAAwoElP+rseVsPDc07fT2Ha0J5/7HO6mWwf2wgg09vrT?= =?iso-8859-1?q?K7IZefrz5xN78l4gPcNLaq/z9J4+8BkkGM12I8dhUbhC0eYISw3SRf9mOzTC?= =?iso-8859-1?q?SseaLBxHNGwV9abpyRABJm1RKEpn4A7avgNysLwEtAHyisw4+USatEUh44Up?= =?iso-8859-1?q?nGfpCIr6M7f7dWNV5jvYnFyQbWE8FID2brl9T6PPBNttohYCJnpGG/NbnUqb?= =?iso-8859-1?q?OpH+vYOGHwW/k+IagScAMVhSROzqXEghWQbyVXktNsq1oHzO+jXDiUF105QG?= =?iso-8859-1?q?BMYuV4NgsQ4vS4q72LTtSB8RBzdSjUKaSBIPbEdLPaiyiAVTs8hap1bsJ9cl?= =?iso-8859-1?q?ID8lYcwjwpjDjNvWKVmaJFLS0WYjXRZYrWs54Ks1R+aRdUT/YXnR/+SF8GTM?= =?iso-8859-1?q?X+d2h9z13KNfHC4naGOuPG/GBNFTMl7wy+j8pU1uRNkkXpX2HnDd3WbkQz52?= =?iso-8859-1?q?Siu+W3HPKWu6mHu7Aaautr0E5THUvvI98VyR+r6wy5QxbB8gsg1tW9Sk/YCr?= =?iso-8859-1?q?6yN6aNqBPkRVWEZuoyjj1bfi9UzF6KdOcg+AAomeYmSoKqUouS0Y4NzBS3FB?= =?iso-8859-1?q?9IkAkPCMZZNkyhjWPpRn3yxltJ9yz2csBcLor1ujtHywkhoK8ysIESBVR7/S?= =?iso-8859-1?q?gs+EbXQheW3XoloqEUk2s4/fiQ8f6cKxnQ46I2xmI/Wl2MLH2LyecaGHK+As?= =?iso-8859-1?q?tLclTmYJacS+hXeTTU1QCrEv5eyRxsbZTchCZi9Io3riDAPaYDgvUXaCW37s?= =?iso-8859-1?q?OzeABPZuboQBEY/kKHMKZ1nYSJe3MF5xWYwfHHbIe1QejFFOnyIv6tPHLCTF?= =?iso-8859-1?q?1ZOGd+ubBfZ12KfdZbsqSDNbH76WBDogBzgcARk5QojjeLDhsjhHz5oPPADw?= =?iso-8859-1?q?Jb5ZfZpGHgFhEtLTfa2jqwH3tEQfrj1ndTlHpa1LUHPikbimInqxNtFkPBSI?= =?iso-8859-1?q?a?= MIME-Version: 1.0 X-OriginatorOrg: non.se.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DB6PR04MB2967.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 7882d3ad-d30e-4cf5-134e-08d93b075946 X-MS-Exchange-CrossTenant-originalarrivaltime: 29 Jun 2021 14:08:19.6761 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 6e51e1ad-c54b-4b39-b598-0ffe9ae68fef X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: xN4hdEn+7nNiy+9u24PiQAHaCn0h3L6oiCHLKmvdSGp37wbGt89kw/QRkB8tPq+0gYr28Vx40CrxkqjTbzAQ7w== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB7PR04MB4524 Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org This is ADC driver that can be found in the Renesas RZ/N1D SoC https://www.renesas.com/us/en/document/man/rzn1d-group-rzn1s-group-rzn1l-group-users-manual-peripherals-0?language=en&r=1054561 ADC Core Features - Up to 2 units - Resolution 12-bit - Sampling rate from 0.0625 MSPS to 1 MSPS - Successive approximation - Maximal conversion time 21 ADC_CLK - Analog inputs 8 channels per core (5 standard channels + 3 channels with sample/hold) - Each channel has his own input trigger to start the conversion, the triggers are managed by the ADC Controller - Power down mode - ADC clock frequency from 4 MHz to 20 MHz Signed-off-by: Tomasz Kazimierz Motyl Signed-off-by: Frederic Loreaud Signed-off-by: Pierre Castellan --- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/r9a06g032-adc.c | 352 ++++++++++++++++++++++++++++++++ 3 files changed, 363 insertions(+) create mode 100644 drivers/iio/adc/r9a06g032-adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index f0af3a42f53c..c6ab22aa2000 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -746,6 +746,16 @@ config ROCKCHIP_SARADC To compile this driver as a module, choose M here: the module will be called rockchip_saradc. +config R9A06G032_ADC + depends on ARCH_R9A06G032 || COMPILE_TEST + tristate "Renesas ADC driver" + help + Say yes here to build support for the ADCs found in SoCs from + Renesas. + + To compile this driver as a module, choose M here: the + module will be called rzn1_adc. + config SC27XX_ADC tristate "Spreadtrum SC27xx series PMICs ADC" depends on MFD_SC27XX_PMIC || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ef9cc485fb67..84c2ccae4317 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o +obj-$(CONFIG_R9A06G032_ADC) += r9a06g032-adc.o obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o obj-$(CONFIG_SPEAR_ADC) += spear_adc.o obj-$(CONFIG_STX104) += stx104.o diff --git a/drivers/iio/adc/r9a06g032-adc.c b/drivers/iio/adc/r9a06g032-adc.c new file mode 100644 index 000000000000..6c41ad74c868 --- /dev/null +++ b/drivers/iio/adc/r9a06g032-adc.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Driver for Renesas R9A06G032 SoC built-in ADC + * + * Authors: + * Tomasz Kazimierz Motyl + * Frédéric Loreaud + * Pierre Castellan + * + * Copyright (C) 2021 Schneider-Electric + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* force conversion register */ +#define R9A06G032_ADC_SET_FORCE_REG_OFFSET (13 * sizeof(u32)) +/* configuration register */ +#define R9A06G032_ADC_CONFIG_REG_OFFSET (16 * sizeof(u32)) +/* configuration register's power down bit */ +#define R9A06G032_ADC_CONFIG_POWER_DOWN_BIT BIT(3) + +/* virtual channels 0..8 control registers */ +#define R9A06G032_ADC_VIRTUAL_CHANNNELS_REGS_OFFSET (48 * sizeof(u32)) +/* control registers' virtual channels' bits */ +#define R9A06G032_ADC_VIRTUAL_CHANNEL_ADC1_SELECTION_MASK GENMASK(2, 0) +/* control registers' enable bit */ +#define R9A06G032_ADC_VIRTUAL_CHANNEL_ADC1_ENABLE_BIT BIT(15) +/* ADC 1 virtual channels conversion data register */ +#define R9A06G032_ADC_VIRTUAL_CHANNEL_ADC1_CONVERSION_DATA_REGS_OFFSET (64 * sizeof(u32)) +/* read data register's bits */ +#define R9A06G032_ADC_READ_DATA_VALUE_MASK GENMASK(11, 0) +/* read data register's update bit */ +#define R9A06G032_ADC_READ_DATA_UPDATE_BIT BIT(31) + +#define R9A06G032_ADC_CHANNEL(index) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 0, \ + .endianness = IIO_BE, \ + }, \ + .extend_name = NULL, \ +} + +/* R9A06G032 ADC has 8 channels */ +#define R9A06G032_ADC_NUM_CHANNELS 8 + +static struct iio_chan_spec r9a06g032_adc_channels[R9A06G032_ADC_NUM_CHANNELS] = { + R9A06G032_ADC_CHANNEL(0), + R9A06G032_ADC_CHANNEL(1), + R9A06G032_ADC_CHANNEL(2), + R9A06G032_ADC_CHANNEL(3), + R9A06G032_ADC_CHANNEL(4), + R9A06G032_ADC_CHANNEL(5), + R9A06G032_ADC_CHANNEL(6), + R9A06G032_ADC_CHANNEL(7), +}; + +/* Device's private data */ +struct r9a06g032_adc { + struct device *dev; + + struct mutex lock; /* channels readings must be done sequentially */ + struct completion complete; + void __iomem *registers; + resource_size_t phys_base; + struct clk *clk; + uint64_t conv_delay; +}; + +static inline uint32_t r9a06g032_adc_read32(struct r9a06g032_adc *const adc, + const uint32_t reg_off) +{ + void __iomem *addr = adc->registers + reg_off; + + return ioread32(addr); +} + +static inline void r9a06g032_adc_write32(struct r9a06g032_adc *const adc, + const uint32_t reg_off, + const uint32_t val) +{ + iowrite32(val, adc->registers + reg_off); +} + +static bool r9a06g032_adc_interrupt_status(struct r9a06g032_adc *const adc, + const int virtual_channel) +{ + bool ret; + /* interrupt 0 status register has a 0 offset in register table */ + uint32_t status = r9a06g032_adc_read32(adc, 0); + + ret = status & BIT(virtual_channel); + + return ret; +} + +static int r9a06g032_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct r9a06g032_adc *adc = iio_priv(indio_dev); + int virtual_channel = chan->channel; + uint32_t virtual_channel_control_offset = R9A06G032_ADC_VIRTUAL_CHANNNELS_REGS_OFFSET + 4 * virtual_channel; + uint32_t virtual_channel_control = R9A06G032_ADC_VIRTUAL_CHANNEL_ADC1_ENABLE_BIT | (R9A06G032_ADC_VIRTUAL_CHANNEL_ADC1_SELECTION_MASK & virtual_channel); + uint32_t read_data = 0; + int ret = 0; + + if ((virtual_channel < 0) + || (virtual_channel > R9A06G032_ADC_NUM_CHANNELS)) { + dev_err(adc->dev, + "Invalid channel index (%i)\n", virtual_channel); + return -EINVAL; + } + mutex_lock(&adc->lock); + + /* disable power down mode, disable DMA and Sample & Hold mode */ + r9a06g032_adc_write32(adc, R9A06G032_ADC_CONFIG_REG_OFFSET, + 0x00000000); + + /* map virtual to physical channels 1:1 */ + r9a06g032_adc_write32(adc, virtual_channel_control_offset, + virtual_channel_control); + + /* force conversion on vc[chan_idx] channel */ + r9a06g032_adc_write32(adc, R9A06G032_ADC_SET_FORCE_REG_OFFSET, + BIT(virtual_channel)); + + /* Wait for maximum conversion duration of 21 ADC clock periods */ + ndelay(adc->conv_delay); + + read_data = r9a06g032_adc_read32(adc, + (R9A06G032_ADC_VIRTUAL_CHANNEL_ADC1_CONVERSION_DATA_REGS_OFFSET + + 4 * virtual_channel)); + + if ((read_data & R9A06G032_ADC_READ_DATA_UPDATE_BIT) == 0 + || (r9a06g032_adc_interrupt_status(adc, + virtual_channel) == false)) { + ret = -EINVAL; /* error reading input value */ + goto exit_point; + } + + *val = read_data & R9A06G032_ADC_READ_DATA_VALUE_MASK; + + /* enable power down mode, keep DMA and Sample & Hold mode disabled */ + r9a06g032_adc_write32(adc, R9A06G032_ADC_CONFIG_REG_OFFSET, + R9A06G032_ADC_CONFIG_POWER_DOWN_BIT); + ret = IIO_VAL_INT; + +exit_point: + mutex_unlock(&adc->lock); + return ret; +} +static int r9a06g032_adc_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + strcpy(label, indio_dev->channels->extend_name); + return 0; +} + +static const struct iio_info r9a06g032_adc_info = { + .read_raw = &r9a06g032_adc_read_raw, + .read_label = &r9a06g032_adc_read_label, +}; + +static int r9a06g032_adc_parse_channels_of(struct r9a06g032_adc *adc, + struct device_node *dn, + const int num_channels) +{ + int ret; + struct device_node *channels; + struct device_node *channel; + struct iio_chan_spec *chanp; + + channels = of_get_child_by_name(dn, "channels"); + + if (channels == NULL) + return -EINVAL; + + for_each_available_child_of_node(channels, channel) { + + uint32_t reg = 0; + + ret = of_property_read_u32(channel, "reg", ®); + + if (ret != 0) + return ret; + + if (reg >= num_channels) { + dev_err(adc->dev, "wrong reg child node value %i\n", + reg); + return -EINVAL; + } + chanp = &r9a06g032_adc_channels[reg]; + chanp->extend_name = of_get_property(channel, "label", NULL); + } + return 0; +} + +static int r9a06g032_adc_setup_channel_names(struct r9a06g032_adc *adc, + struct iio_dev *const indio_dev) +{ + struct device_node *dn = indio_dev->dev.of_node; + int ret = r9a06g032_adc_parse_channels_of(adc, dn, + indio_dev->num_channels); + + if (ret < 0) + dev_warn(adc->dev, "unable to parse channels!\n"); + + return ret; +} + +static int r9a06g032_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct r9a06g032_adc *adc; + struct resource *res; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + + if (indio_dev == NULL) { + dev_err(&pdev->dev, "Failed to allocate memory for an IIO device!\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, indio_dev); + + adc = iio_priv(indio_dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* + * Initialize private physical address for + * R9A06G032 secure monitor calls + */ + adc->phys_base = res->start; + adc->registers = devm_ioremap_resource(dev, res); + + if (IS_ERR(adc->registers)) { + dev_err(dev, "Unable to acquire memory mapping for the registers!\n"); + return PTR_ERR(adc->registers); + } + platform_set_drvdata(pdev, indio_dev); + adc->dev = dev; + + /* Enabling clock for the ADC */ + adc->clk = devm_clk_get(&pdev->dev, "r9a06g032_adc_clk"); + + if (IS_ERR(adc->clk)) { + dev_err(dev, "Failed to get the clock!\n"); + ret = PTR_ERR(adc->clk); + } + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(dev, "Could not prepare or enable the clock!\n"); + return ret; + } + dev_info(dev, "ADC clock rate: %lu Hz\n", clk_get_rate(adc->clk)); + + /* + * Estimate the max delay in ns, + * 20 clock tics + 1 clock tic for safety + */ + adc->conv_delay = div_u64(21000000000, clk_get_rate(adc->clk)); + dev_info(dev, "ADC conversion delay : %llu ns\n", adc->conv_delay); + + mutex_init(&adc->lock); + init_completion(&adc->complete); + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &r9a06g032_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = r9a06g032_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(r9a06g032_adc_channels); + + if (r9a06g032_adc_setup_channel_names(adc, indio_dev) < 0) + dev_warn(dev, + "Invalid channels information - using defaults.\n"); + + ret = iio_device_register(indio_dev); + if (ret) { + clk_disable(adc->clk); + dev_err(dev, "Failed to register IIO device %s': %d\n", + indio_dev->name, ret); + return ret; + } + return 0; +} + +static int r9a06g032_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static const struct platform_device_id r9a06g032_adc_ids[] = { + { .name = "r9a06g032-adc", }, + { }, +}; + +MODULE_DEVICE_TABLE(platform, r9a06g032_adc_ids); + +static const struct of_device_id r9a06g032_adc_dt_ids[] = { + { .compatible = "r9a06g032-adc", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, r9a06g032_adc_dt_ids); + +static struct platform_driver r9a06g032_adc_driver = { + .driver = { + .name = "r9a06g032-adc", + .of_match_table = of_match_ptr(r9a06g032_adc_dt_ids), + }, + .probe = r9a06g032_adc_probe, + .remove = r9a06g032_adc_remove, + .id_table = r9a06g032_adc_ids, +}; + +module_platform_driver(r9a06g032_adc_driver); + +MODULE_DESCRIPTION("R9A06G032 ADC Driver for LCES2"); +MODULE_AUTHOR("Tomasz Kazimierz Motyl "); +MODULE_LICENSE("GPL");