From patchwork Fri Dec 22 23:19:13 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Stevanovic X-Patchwork-Id: 10131249 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 E305C6038F for ; Fri, 22 Dec 2017 23:20:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D2413297D5 for ; Fri, 22 Dec 2017 23:20:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C56852A231; Fri, 22 Dec 2017 23:20:15 +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=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FROM,RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable 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 11138297D5 for ; Fri, 22 Dec 2017 23:20:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756410AbdLVXUB (ORCPT ); Fri, 22 Dec 2017 18:20:01 -0500 Received: from mail-wr0-f193.google.com ([209.85.128.193]:37739 "EHLO mail-wr0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757173AbdLVXTx (ORCPT ); Fri, 22 Dec 2017 18:19:53 -0500 Received: by mail-wr0-f193.google.com with SMTP id f8so18321500wre.4; Fri, 22 Dec 2017 15:19:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=9FZLX+98hcb258CWakn47e0p+jjE52C9YtEPtuG+qMY=; b=FGuZiaxCl19f/JYaP0GHHmmXWLg6t3g+5QihnmnJpS6OPFNFIW7SoSS5cXUwHRISiF MWY211ZcRFdtEgu99UDFT5/KwD4a2ok7TwEl0CIJMf9aaJ942BmkCPhilSeyc46QEIG8 loHmpac9ZEY8Wn5zzsXMmWsnW5S0Izu1m8vEZtCHifl/NNeY45g8rl7wc71dUZeK3JAG bfy6mdZlhK4VR4dYJ7O51IapuQ4WpoUG1vseu/H0HE/MLnW60OD4fAdfyux3jK4bWgOZ Jz38T5XaKkCanH93FcOAzfWxu4MsWV5AIrqhsoBoNJybYSHnFg+Wx3EhCLQhQh+TgAue Byfg== 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; bh=9FZLX+98hcb258CWakn47e0p+jjE52C9YtEPtuG+qMY=; b=pt0lmE5fKQt1pxBpaCxtTCv7lepUH/CS8T8Gn5CDGdbU8Tg8eJFy5CrtRq9cBxhbVC bEYZgkGrOrzpIvxH0FHVNFnS6g+nKKONmBc515cOI8FBDroWkgQiRJBawA1BSGs+lDBh JhNF+2o+2g3kBT/6UeDgYNwlPiAqAicdnlFoSPa07W0Edv2YwsVlmJC+CJS7LUMKe81a +HvRwJ4wJZkzgYgZZ/S8Ds0DOdDyqRxR8bCyIQ0PaCBmPzSo0y+5PNTpsn9W4vah5+R1 W3PJfVFvmVH44JcisUkfzoXsO8b8w8bPSYLmZPWC1YFrI3YrO441tjFi9wwPq8dLr4l8 gLIA== X-Gm-Message-State: AKGB3mLPokXz0WHLI0YpBXgKy+H0r71ibeVwhSAhBFDn1Ns6xaxWZ0sC ifNVDAr38JyQKdRfvXR2vlc= X-Google-Smtp-Source: ACJfBosBO45+9f1eSoKaKrwl7mP9rMvX752SZ2mFUY91GBquE9flaH4oQ9WT/gaF9lMNKlK+lVV6Rw== X-Received: by 10.223.199.203 with SMTP id y11mr17650105wrg.37.1513984791042; Fri, 22 Dec 2017 15:19:51 -0800 (PST) Received: from localhost.localdomain (LFbn-1-8169-84.w90-112.abo.wanadoo.fr. [90.112.78.84]) by smtp.gmail.com with ESMTPSA id i17sm6681144wrf.71.2017.12.22.15.19.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 22 Dec 2017 15:19:50 -0800 (PST) From: Milan Stevanovic To: jic23@kernel.org Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, milan.o.stevanovic@gmail.com Subject: [PATCH] iio: adc: add driver for ti adc081s/adc101s/adc121s Date: Sat, 23 Dec 2017 00:19:13 +0100 Message-Id: <1513984753-3040-1-git-send-email-milan.o.stevanovic@gmail.com> X-Mailer: git-send-email 2.7.4 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 Signed-off-by: Milan Stevanovic --- drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-adc081s.c | 239 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 drivers/iio/adc/ti-adc081s.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index ef86296..208238f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -701,6 +701,16 @@ config TI_ADC081C This driver can also be built as a module. If so, the module will be called ti-adc081c. +config TI_ADC081S + tristate "Texas Instruments ADC081S/ADC101S/ADC121S family" + depends on SPI + help + If you say yes here you get support for Texas Instruments ADC081S, + ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc081s. + config TI_ADC0832 tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 9572c10..020fecf 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o obj-$(CONFIG_STM32_ADC) += stm32-adc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o +obj-$(CONFIG_TI_ADC081S) += ti-adc081s.o obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o diff --git a/drivers/iio/adc/ti-adc081s.c b/drivers/iio/adc/ti-adc081s.c new file mode 100644 index 0000000..be9cdb7 --- /dev/null +++ b/drivers/iio/adc/ti-adc081s.c @@ -0,0 +1,239 @@ +/* + * TI ADC081S/ADC101S/ADC121S 8/10/12-bit ADC driver + * + * + * 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. + * + * Datasheets: + * http://www.ti.com/lit/ds/symlink/adc081s021.pdf + * http://www.ti.com/lit/ds/symlink/adc101s021.pdf + * http://www.ti.com/lit/ds/symlink/adc121s021.pdf + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +struct adc081s { + struct spi_device *spi; + struct regulator *reg; + struct mutex lock; + + /* 8, 10 or 12 */ + int bits; +}; + +static int adc081s_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc081s *adc = iio_priv(iio); + int ret; + __be16 buf; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + ret = spi_read(adc->spi, (void *) &buf, 2); + mutex_unlock(&adc->lock); + if (ret) + return ret; + *value = (be16_to_cpu(buf) & 0x0FFF) >> (12 - adc->bits); + *value = sign_extend32(*value, channel->scan_type.realbits - 1); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *value = regulator_get_voltage(adc->reg); + if (*value < 0) + return *value; + + /* convert regulator output voltage to mV */ + *value /= 1000; + *shift = adc->bits; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + break; + } + + return -EINVAL; +} + +#define ADCxx1S_CHAN(_bits) { \ + .type = IIO_VOLTAGE, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = 16, \ + .shift = 12 - (_bits), \ + .endianness = IIO_CPU, \ + }, \ +} + +#define DEFINE_ADCxx1S_CHANNELS(_name, _bits) \ + static const struct iio_chan_spec _name ## _channels[] = { \ + ADCxx1S_CHAN((_bits)), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ + }; \ + +#define ADC081S_NUM_CHANNELS 2 + +struct adcxx1s_model { + const struct iio_chan_spec *channels; + int bits; +}; + +#define ADCxx1S_MODEL(_name, _bits) \ + { \ + .channels = _name ## _channels, \ + .bits = (_bits), \ + } + +DEFINE_ADCxx1S_CHANNELS(adc081s, 8); +DEFINE_ADCxx1S_CHANNELS(adc101s, 10); +DEFINE_ADCxx1S_CHANNELS(adc121s, 12); + +/* Model ids are indexes in _models array */ +enum adcxx1s_model_id { + ADC081S = 0, + ADC101S = 1, + ADC121S = 2, +}; + +static struct adcxx1s_model adcxx1s_models[] = { + ADCxx1S_MODEL(adc081s, 8), + ADCxx1S_MODEL(adc101s, 10), + ADCxx1S_MODEL(adc121s, 12), +}; + +static const struct iio_info adc081s_info = { + .read_raw = adc081s_read_raw, + .driver_module = THIS_MODULE, +}; + +static int adc081s_probe(struct spi_device *spi) +{ + struct iio_dev *iio; + struct adc081s *adc; + struct adcxx1s_model *model; + int err; + + if (ACPI_COMPANION(&spi->dev)) { + const struct acpi_device_id *ad_id; + + ad_id = acpi_match_device(spi->dev.driver->acpi_match_table, + &spi->dev); + if (!ad_id) + return -ENODEV; + model = &adcxx1s_models[ad_id->driver_data]; + } else { + model = &adcxx1s_models[spi_get_device_id(spi)->driver_data]; + } + + iio = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!iio) + return -ENOMEM; + + adc = iio_priv(iio); + adc->spi = spi; + adc->bits = model->bits; + mutex_init(&adc->lock); + + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) + return PTR_ERR(adc->reg); + + err = regulator_enable(adc->reg); + if (err < 0) + return err; + + iio->dev.parent = &spi->dev; + iio->dev.of_node = spi->dev.of_node; + iio->name = dev_name(&spi->dev); + iio->modes = INDIO_DIRECT_MODE; + iio->info = &adc081s_info; + + iio->channels = model->channels; + iio->num_channels = ADC081S_NUM_CHANNELS; + + err = iio_device_register(iio); + if (err < 0) + goto err_regulator_disable; + + spi_set_drvdata(spi, iio); + + return 0; + +err_regulator_disable: + regulator_disable(adc->reg); + + return err; +} + +static int adc081s_remove(struct spi_device *spi) +{ + struct iio_dev *iio = spi_get_drvdata(spi); + struct adc081s *adc = iio_priv(iio); + + iio_device_unregister(iio); + regulator_disable(adc->reg); + + return 0; +} + +static const struct spi_device_id adc081s_id[] = { + { "adc081s", ADC081S }, + { "adc101s", ADC101S }, + { "adc121s", ADC121S }, + { } +}; +MODULE_DEVICE_TABLE(spi, adc081s_id); + +#ifdef CONFIG_OF +static const struct of_device_id adc081s_of_match[] = { + { .compatible = "ti,adc081s" }, + { .compatible = "ti,adc101s" }, + { .compatible = "ti,adc121s" }, + { } +}; +MODULE_DEVICE_TABLE(of, adc081s_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id adc081s_acpi_match[] = { + { "ADC081S", ADC081S }, + { "ADC101S", ADC101S }, + { "ADC121S", ADC121S }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc081s_acpi_match); +#endif + +static struct spi_driver adc081s_driver = { + .driver = { + .name = "adc081s", + .of_match_table = of_match_ptr(adc081s_of_match), + .acpi_match_table = ACPI_PTR(adc081s_acpi_match), + }, + .probe = adc081s_probe, + .remove = adc081s_remove, + .id_table = adc081s_id, +}; +module_spi_driver(adc081s_driver); + +MODULE_AUTHOR("Milan Stevanovic "); +MODULE_DESCRIPTION("Texas Instruments ADC081S/ADC101S/ADC121S driver"); +MODULE_LICENSE("GPL v2");