From patchwork Mon Oct 22 21:02:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Couret Charles-Antoine X-Patchwork-Id: 10652555 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0C20E17D4 for ; Mon, 22 Oct 2018 21:03:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6F21828E22 for ; Mon, 22 Oct 2018 21:03:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 630292900B; Mon, 22 Oct 2018 21:03:03 +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=-7.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 32A9028E22 for ; Mon, 22 Oct 2018 21:03:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725752AbeJWFXK (ORCPT ); Tue, 23 Oct 2018 01:23:10 -0400 Received: from mail-ed1-f68.google.com ([209.85.208.68]:46735 "EHLO mail-ed1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726885AbeJWFXK (ORCPT ); Tue, 23 Oct 2018 01:23:10 -0400 Received: by mail-ed1-f68.google.com with SMTP id v22-v6so2829227edq.13 for ; Mon, 22 Oct 2018 14:02:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=essensium-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=UDOfib5ZMln5kMLJSHZgmXYWP4f3EmJcB1OU1ZmDqnA=; b=ONAcNO4+5kDGs3IPrX3helfBqy94nLQGYqY3sI+8H3FZu4eFl/KkFm/+gkfDfpVfC6 abmDmjX89Z37jV+nFzOTUrCG9CGQKHBY+1iDoPxwnV1bP9Kmlxga161OKwohXB06pojH 8XybJVw+sY76CK0pMiqOHir6v67ifHZwJnWPMG7b6N7cRNkomrRPxlwiyaDX10B5QpIA BN9+SGcTpNxgXvTWkEjjLPs/yntOrQ2IY7TYx9TsNRNBfhXKHapTQK9s+O6bliYnEfe1 fO1OGoC0GUvMJP/M+py/vgc+YIDrwkMPso7qQkXI6DNQ9c6aprXvku71ACD5YRmo+Kn3 krkw== 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:mime-version :content-transfer-encoding; bh=UDOfib5ZMln5kMLJSHZgmXYWP4f3EmJcB1OU1ZmDqnA=; b=s4l7z4xTWqZN8LZ1Ea7mG+2A0C5xwv6X7emUSxy9P/czfeAv/U1yAG+wg6wj7Sjvmz DJlRZsrx0V0XIw77sLJwSrUtLNkpUlBmi8lVJ/GmOFppj/y3C18ZfsXkj3wZLV7LtVVz rBe/AZW4k1iDkieB7KaJSnwGU+iJkS2cNNkEjtOaOchZ04NfNfswzbeutsiE12MVUmGB fP1AOMC8tX6njy1rl85YqhWZepq87vfVnkigqk0a3haQcnN/MuzEgQnL6bfRlNHckOpr GGDXIG8ol+N0CWwrP+kAtpsDdBoAKwmW4sGM9qUAXQC+azJHkMKXlzcF/VHD39RLXL97 4OWA== X-Gm-Message-State: ABuFfog8zUWNu0IujS7K1H5SEdDifkT3csvIwNkg4yM0ucKn07peAnW3 s+YRPQl6nNKdXeMFp4R0PmdBpLmRUkI= X-Google-Smtp-Source: ACcGV63y05+5N5n0wV2LcvA/PyjvYzEDWqBG65IbWFLmyvOJKB4zmvDs6mj908QS+5Nn7H3sChZ3og== X-Received: by 2002:a50:8ee4:: with SMTP id x33-v6mr13929003edx.201.1540242177736; Mon, 22 Oct 2018 14:02:57 -0700 (PDT) Received: from localhost.localdomain ([2a02:a03f:4423:3600:9626:d5b7:4a40:9655]) by smtp.gmail.com with ESMTPSA id o20-v6sm1513088ejs.30.2018.10.22.14.02.56 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 22 Oct 2018 14:02:57 -0700 (PDT) From: Charles-Antoine Couret To: linux-iio@vger.kernel.org Cc: Charles-Antoine Couret Subject: [PATCH V3 1/2] iio:adc:ad7949: Add AD7949 ADC driver family Date: Mon, 22 Oct 2018 23:02:42 +0200 Message-Id: <20181022210243.837-1-charles-antoine.couret@essensium.com> X-Mailer: git-send-email 2.19.1 MIME-Version: 1.0 Sender: linux-iio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Compatible with AD7682 and AD7689 chips. It is a Analog Devices ADC driver 14/16 bits 4/8 channels with SPI protocol Datasheet of the device: http://www.analog.com/media/en/technical-documentation/data-sheets/AD7949.pdf Signed-off-by: Charles-Antoine Couret --- V1 to V2: * Use GENMASK * Little refactoring to define SPI message parameters * Add ____cacheline_aligned for SPI buffer * Comment required delays * Clean switch statements * Init SPI before iio V2 to V3: * Merge message init functions into spi_message_init_with_transfers call * Add vendor prefix for device tree * Remove SPI settings overrinding, use info from device tree only drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7949.c | 347 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 358 insertions(+) create mode 100644 drivers/iio/adc/ad7949.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 4a754921fb6f..42e66efff6c0 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -116,6 +116,16 @@ config AD7923 To compile this driver as a module, choose M here: the module will be called ad7923. +config AD7949 + tristate "Analog Devices AD7949 and similar ADCs driver" + depends on SPI + help + Say yes here to build support for Analog Devices + AD7949, AD7682, AD7689 8 Channel ADCs. + + To compile this driver as a module, choose M here: the + module will be called ad7949. + config AD799X tristate "Analog Devices AD799x ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 03db7b578f9c..88804c867aa9 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7766) += ad7766.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o +obj-$(CONFIG_AD7949) += ad7949.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o obj-$(CONFIG_AT91_ADC) += at91_adc.o diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c new file mode 100644 index 000000000000..5f93ab0ebac2 --- /dev/null +++ b/drivers/iio/adc/ad7949.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ad7949.c - Analog Devices ADC driver 14/16 bits 4/8 channels + * + * Copyright (C) 2018 CMC NV + * + * http://www.analog.com/media/en/technical-documentation/data-sheets/AD7949.pdf + */ + +#include +#include +#include +#include +#include + +#define AD7949_MASK_CHANNEL_SEL GENMASK(9, 7) +#define AD7949_MASK_TOTAL GENMASK(13, 0) +#define AD7949_OFFSET_CHANNEL_SEL 7 +#define AD7949_CFG_READ_BACK 0x1 +#define AD7949_CFG_REG_SIZE_BITS 14 + +enum { + ID_AD7949 = 0, + ID_AD7682, + ID_AD7689, +}; + +struct ad7949_adc_spec { + u8 num_channels; + u8 resolution; +}; + +static const struct ad7949_adc_spec ad7949_adc_spec[] = { + [ID_AD7949] = { .num_channels = 8, .resolution = 14 }, + [ID_AD7682] = { .num_channels = 4, .resolution = 16 }, + [ID_AD7689] = { .num_channels = 8, .resolution = 16 }, +}; + +/** + * struct ad7949_adc_chip - AD ADC chip + * @lock: protects write sequences + * @vref: regulator generating Vref + * @iio_dev: reference to iio structure + * @spi: reference to spi structure + * @resolution: resolution of the chip + * @cfg: copy of the configuration register + * @current_channel: current channel in use + * @buffer: buffer to send / receive data to / from device + */ +struct ad7949_adc_chip { + struct mutex lock; + struct regulator *vref; + struct iio_dev *indio_dev; + struct spi_device *spi; + u8 resolution; + u16 cfg; + unsigned int current_channel; + u32 buffer ____cacheline_aligned; +}; + +static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc) +{ + if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK)) + return true; + + return false; +} + +static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc) +{ + int ret = ad7949_adc->resolution; + + if (ad7949_spi_cfg_is_read_back(ad7949_adc)) + ret += AD7949_CFG_REG_SIZE_BITS; + + return ret; +} + +static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, + u16 mask) +{ + int ret; + int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS; + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &ad7949_adc->buffer, + .len = 4, + .bits_per_word = bits_per_word, + }, + }; + + ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask); + ad7949_adc->buffer = ad7949_adc->cfg << shift; + spi_message_init_with_transfers(&msg, tx, 1); + ret = spi_sync(ad7949_adc->spi, &msg); + + /* + * This delay is to avoid a new request before the required time to + * send a new command to the device + */ + udelay(2); + return ret; +} + +static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, + unsigned int channel) +{ + int ret; + int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int mask = GENMASK(ad7949_adc->resolution, 0); + struct spi_message msg; + struct spi_transfer tx[] = { + { + .rx_buf = &ad7949_adc->buffer, + .len = 4, + .bits_per_word = bits_per_word, + }, + }; + + ret = ad7949_spi_write_cfg(ad7949_adc, + channel << AD7949_OFFSET_CHANNEL_SEL, + AD7949_MASK_CHANNEL_SEL); + if (ret) + return ret; + + ad7949_adc->buffer = 0; + spi_message_init_with_transfers(&msg, tx, 1); + ret = spi_sync(ad7949_adc->spi, &msg); + if (ret) + return ret; + + /* + * This delay is to avoid a new request before the required time to + * send a new command to the device + */ + udelay(2); + + ad7949_adc->current_channel = channel; + + if (ad7949_spi_cfg_is_read_back(ad7949_adc)) + *val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask; + else + *val = ad7949_adc->buffer & mask; + + return 0; +} + +#define AD7949_ADC_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec ad7949_adc_channels[] = { + AD7949_ADC_CHANNEL(0), + AD7949_ADC_CHANNEL(1), + AD7949_ADC_CHANNEL(2), + AD7949_ADC_CHANNEL(3), + AD7949_ADC_CHANNEL(4), + AD7949_ADC_CHANNEL(5), + AD7949_ADC_CHANNEL(6), + AD7949_ADC_CHANNEL(7), +}; + +static int ad7949_spi_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); + int ret; + + if (!val) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ad7949_adc->lock); + ret = ad7949_spi_read_channel(ad7949_adc, val, chan->channel); + mutex_unlock(&ad7949_adc->lock); + + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(ad7949_adc->vref); + if (ret < 0) + return ret; + + *val = ret / 5000; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad7949_spi_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); + int ret = 0; + + if (readval) + *readval = ad7949_adc->cfg; + else + ret = ad7949_spi_write_cfg(ad7949_adc, + writeval & AD7949_MASK_TOTAL, AD7949_MASK_TOTAL); + + return ret; +} + +static const struct iio_info ad7949_spi_info = { + .read_raw = ad7949_spi_read_raw, + .debugfs_reg_access = ad7949_spi_reg_access, +}; + +static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) +{ + int ret; + int val; + + /* Sequencer disabled, CFG readback disabled, IN0 as default channel */ + ad7949_adc->current_channel = 0; + ret = ad7949_spi_write_cfg(ad7949_adc, 0x3C79, AD7949_MASK_TOTAL); + + /* Do two dummy conversions to apply the first configuration setting. + * Required only after the start up of the device. + */ + ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel); + ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel); + + return ret; +} + +static int ad7949_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct ad7949_adc_spec *spec; + struct ad7949_adc_chip *ad7949_adc; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc)); + if (!indio_dev) { + dev_err(dev, "can not allocate iio device\n"); + return -ENOMEM; + } + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = dev->of_node; + indio_dev->info = &ad7949_spi_info; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7949_adc_channels; + spi_set_drvdata(spi, indio_dev); + + ad7949_adc = iio_priv(indio_dev); + ad7949_adc->indio_dev = indio_dev; + ad7949_adc->spi = spi; + + spec = &ad7949_adc_spec[spi_get_device_id(spi)->driver_data]; + indio_dev->num_channels = spec->num_channels; + ad7949_adc->resolution = spec->resolution; + + ad7949_adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(ad7949_adc->vref)) { + dev_err(dev, "fail to request regulator\n"); + return PTR_ERR(ad7949_adc->vref); + } + + ret = regulator_enable(ad7949_adc->vref); + if (ret < 0) { + dev_err(dev, "fail to enable regulator\n"); + goto err_regulator; + } + + mutex_init(&ad7949_adc->lock); + + ret = ad7949_spi_init(ad7949_adc); + if (ret) { + dev_err(dev, "enable to init this device: %d\n", ret); + goto err; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "fail to register iio device: %d\n", ret); + goto err; + } + + return 0; + +err: + mutex_destroy(&ad7949_adc->lock); + regulator_disable(ad7949_adc->vref); +err_regulator: + return ret; +} + +static int ad7949_spi_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + mutex_destroy(&ad7949_adc->lock); + regulator_disable(ad7949_adc->vref); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id ad7949_spi_of_id[] = { + { .compatible = "adi,ad7949" }, + { .compatible = "adi,ad7682" }, + { .compatible = "adi,ad7689" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7949_spi_of_id); +#endif + +static const struct spi_device_id ad7949_spi_id[] = { + { "ad7949", ID_AD7949 }, + { "ad7682", ID_AD7682 }, + { "ad7689", ID_AD7689 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7949_spi_id); + +static struct spi_driver ad7949_spi_driver = { + .driver = { + .name = "ad7949", + .of_match_table = of_match_ptr(ad7949_spi_of_id), + }, + .probe = ad7949_spi_probe, + .remove = ad7949_spi_remove, + .id_table = ad7949_spi_id, +}; +module_spi_driver(ad7949_spi_driver); + +MODULE_AUTHOR("Charles-Antoine Couret "); +MODULE_DESCRIPTION("Analog Devices 14/16-bit 8-channel ADC driver"); +MODULE_LICENSE("GPL v2");