Received: by 2002:a05:6358:3188:b0:123:57c1:9b43 with SMTP id q8csp1048313rwd; Thu, 8 Jun 2023 11:14:28 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5juIpQy8ONErIdZ512Im3QyyHgKVAiI695yn2NBdiwhLgRVHt6xq/RMBDMnLXjdOq797ip X-Received: by 2002:a05:6a20:6a28:b0:114:788f:ae3f with SMTP id p40-20020a056a206a2800b00114788fae3fmr5389165pzk.57.1686248067942; Thu, 08 Jun 2023 11:14:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686248067; cv=none; d=google.com; s=arc-20160816; b=ARXwd5iP+6cklZ22UL4s9+TBPvdV/jL5B0MaDDR0mvoQdZGjrGVzQlbof2mUR734dY p7I2Eq2XKeYjQkXVFHp3P1BP6U0DboK5pOO3HzI/YEz+BHFOu733s5FV0BK9xYNhfYny tjpHHZ0KA1ZRO96umGgalaJVijR7tyMt4WRZo7uxah0zt/7swwRxsO9jzGkHqx4J/wKV EluBjxzZp12G27CWMHZRSTXYSFJoy7Rxh9Hm143JLlIipFTOl9YE4jP5bcQ4bGPelrOe 3cUl4elIbOWPgVeqv7hxBs7F0G/AbEV6SX9GptpGU3x5+Qn9XjE+njhRiTj/qF1WkDjD MRSA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:to:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:from; bh=J3kJSAEyvB4E5J73oOjL5KelYC5m1yZVCfCwtyzL2Q4=; b=VByYqlcXLv9YniV2okmGelRFfIgz02ZQIsXVBLQHV9ENqWkQcKXMMsjukSA8GIXdN6 0F4l805aZm9IbH/+08bowEt+ttDcpU84AtVIm2FL6F+kLEglYv3XDjf9ct8xyNiB5q/5 zx2JmwHz7XI7CSph8HQCh4/xmI5sCJzoDcRnuhzHhWqj422uGG+oTwIX0PF0x3h5NkY3 fOwhdnZwWH6hBxDBiz89JPcdJ7rszcsDSR7HnSRQKAFJkRK+GFDCWMQLpnsPlDoJzCfk hUeqr9CG8CSS6ERc8cU3w20G2wYlfpvLId730DXT/kpxFL3ojDWVX/CnB1ThuNaFFsoV lWcg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=analog.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id p4-20020a625b04000000b00653a59e83b3si1184209pfb.70.2023.06.08.11.14.14; Thu, 08 Jun 2023 11:14:27 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=analog.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235466AbjFHRqc (ORCPT + 99 others); Thu, 8 Jun 2023 13:46:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57988 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235563AbjFHRq3 (ORCPT ); Thu, 8 Jun 2023 13:46:29 -0400 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2D9D52D4B; Thu, 8 Jun 2023 10:46:28 -0700 (PDT) Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 358HIa39032685; Thu, 8 Jun 2023 13:46:17 -0400 Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 3r2abwfb12-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 08 Jun 2023 13:46:17 -0400 Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 358HkFoR041771 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Thu, 8 Jun 2023 13:46:15 -0400 Received: from ASHBMBX9.ad.analog.com (10.64.17.10) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.14; Thu, 8 Jun 2023 13:46:14 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server id 15.2.986.14 via Frontend Transport; Thu, 8 Jun 2023 13:46:14 -0400 Received: from kimedia-VirtualBox.ad.analog.com (KPALLER2-L02.ad.analog.com [10.116.242.24]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 358HjaKk003381; Thu, 8 Jun 2023 13:46:08 -0400 From: Kim Seer Paller CC: Kim Seer Paller , Lars-Peter Clausen , , Subject: [PATCH v3 2/2] iio: adc: max14001: New driver Date: Fri, 9 Jun 2023 01:44:13 +0800 Message-ID: <20230608174413.39959-3-kimseer.paller@analog.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230608174413.39959-1-kimseer.paller@analog.com> References: <20230608174413.39959-1-kimseer.paller@analog.com> MIME-Version: 1.0 Content-Transfer-Encoding: 7BIT Content-Type: text/plain; charset=US-ASCII X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: ECI0n448gMQ3U2HMCetpUVmtJfxPkEdn X-Proofpoint-GUID: ECI0n448gMQ3U2HMCetpUVmtJfxPkEdn X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-08_12,2023-06-08_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 lowpriorityscore=0 malwarescore=0 phishscore=0 impostorscore=0 suspectscore=0 priorityscore=1501 mlxscore=0 bulkscore=0 clxscore=1015 mlxlogscore=999 spamscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2305260000 definitions=main-2306080156 X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The MAX14001 is configurable, isolated 10-bit ADCs for multi-range binary inputs. Signed-off-by: Kim Seer Paller --- MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/max14001.c | 337 +++++++++++++++++++++++++++++++++++++ 4 files changed, 349 insertions(+) create mode 100644 drivers/iio/adc/max14001.c diff --git a/MAINTAINERS b/MAINTAINERS index b8ad615d011f..8c85d0b2d60d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12666,6 +12666,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/dac/adi,max14001.yaml +F: drivers/iio/adc/max14001.c MAXBOTIX ULTRASONIC RANGER IIO DRIVER M: Andreas Klinger diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index eb2b09ef5d5b..e599d166e98d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -706,6 +706,16 @@ config MAX11410 To compile this driver as a module, choose M here: the module will be called max11410. +config MAX14001 + tristate "Analog Devices MAX14001 ADC driver" + depends on SPI + help + Say yes here to build support for Analog Devices MAX14001 Configurable, + Isolated 10-bit ADCs for Multi-Range Binary Inputs. + + To compile this driver as a module, choose M here: the module will be + called max14001. + config MAX1241 tristate "Maxim max1241 ADC driver" depends on SPI_MASTER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index e07e4a3e6237..9f8964258f03 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_MAX11100) += max11100.o obj-$(CONFIG_MAX1118) += max1118.o obj-$(CONFIG_MAX11205) += max11205.o obj-$(CONFIG_MAX11410) += max11410.o +obj-$(CONFIG_MAX14001) += max14001.o obj-$(CONFIG_MAX1241) += max1241.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MAX9611) += max9611.o diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c new file mode 100644 index 000000000000..b04aa4ba5508 --- /dev/null +++ b/drivers/iio/adc/max14001.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Analog Devices MAX14001 ADC driver + * + * Copyright 2023 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* MAX14001 Registers Address */ +#define MAX14001_ADC 0x00 +#define MAX14001_FADC 0x01 +#define MAX14001_FLAGS 0x02 +#define MAX14001_FLTEN 0x03 +#define MAX14001_THL 0x04 +#define MAX14001_THU 0x05 +#define MAX14001_INRR 0x06 +#define MAX14001_INRT 0x07 +#define MAX14001_INRP 0x08 +#define MAX14001_CFG 0x09 +#define MAX14001_ENBL 0x0A +#define MAX14001_ACT 0x0B +#define MAX14001_WEN 0x0C + +#define MAX14001_VERIFICATION_REG(x) ((x) + 0x10) + +#define MAX14001_CFG_EXRF BIT(5) + +#define MAX14001_ADDR_MASK GENMASK(15, 11) +#define MAX14001_DATA_MASK GENMASK(9, 0) +#define MAX14001_FILTER_MASK GENMASK(3, 2) + +#define MAX14001_SET_WRITE_BIT BIT(10) +#define MAX14001_WRITE_WEN 0x294 + +struct max14001_state { + struct spi_device *spi; + /* + * lock protect agains multiple concurrent accesses, RMW sequence, and + * SPI transfer + */ + struct mutex lock; + struct regmap *regmap; + int vref_mv; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 spi_tx_buffer __aligned(IIO_DMA_MINALIGN); + __be16 spi_rx_buffer; +}; + +static int max14001_read(void *context, unsigned int reg_addr, unsigned int *data) +{ + struct max14001_state *st = context; + int ret; + + struct spi_transfer xfers[] = { + { + .tx_buf = &st->spi_tx_buffer, + .len = 2, + .cs_change = 1, + }, { + .rx_buf = &st->spi_rx_buffer, + .len = 2, + }, + }; + + st->spi_tx_buffer = bitrev16(cpu_to_be16(FIELD_PREP(MAX14001_ADDR_MASK, + reg_addr))); + + ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); + if (ret) + return ret; + + *data = bitrev16(be16_to_cpu(st->spi_rx_buffer)) & MAX14001_DATA_MASK; + + return 0; +} + +static int max14001_write(void *context, unsigned int reg_addr, unsigned int data) +{ + struct max14001_state *st = context; + + st->spi_tx_buffer = bitrev16(cpu_to_be16( + FIELD_PREP(MAX14001_ADDR_MASK, reg_addr) | + FIELD_PREP(MAX14001_SET_WRITE_BIT, 1) | + FIELD_PREP(MAX14001_DATA_MASK, data))); + + return spi_write(st->spi, &st->spi_tx_buffer, sizeof(st->spi_tx_buffer)); +} + +static const struct regmap_config max14001_regmap_config = { + .reg_read = max14001_read, + .reg_write = max14001_write, +}; + +static int max14001_write_verification_reg(struct max14001_state *st, + unsigned int reg_addr) +{ + unsigned int reg_data; + int ret; + + ret = max14001_read(st, reg_addr, ®_data); + if (ret) + return ret; + + return max14001_write(st, MAX14001_VERIFICATION_REG(reg_addr), reg_data); +} + +static int max14001_reg_update(struct max14001_state *st, + unsigned int reg_addr, + unsigned int mask, + unsigned int val) +{ + int ret; + unsigned int reg_data; + + /* Enable SPI Registers Write */ + ret = max14001_write(st, MAX14001_WEN, MAX14001_WRITE_WEN); + if (ret) + return ret; + + ret = max14001_read(st, reg_addr, ®_data); + if (ret) + return ret; + + reg_data = FIELD_PREP(mask, val); + + ret = max14001_write(st, reg_addr, reg_data); + if (ret) + return ret; + + /* Write Verification Register */ + ret = max14001_write_verification_reg(st, reg_addr); + if (ret) + return ret; + + /* Disable SPI Registers Write */ + return max14001_write(st, MAX14001_WEN, 0); +} + +static int max14001_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max14001_state *st = iio_priv(indio_dev); + unsigned int data; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + ret = max14001_read(st, MAX14001_ADC, &data); + mutex_unlock(&st->lock); + if (ret < 0) + return ret; + + *val = data; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_mv; + *val2 = 10; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static const struct iio_info max14001_info = { + .read_raw = max14001_read_raw, +}; + +static const struct iio_chan_spec max14001_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + } +}; + +static void max14001_regulator_disable(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + +static int max14001_init(struct max14001_state *st) +{ + int ret; + + /* Enable SPI Registers Write */ + ret = max14001_write(st, MAX14001_WEN, MAX14001_WRITE_WEN); + if (ret) + return ret; + + /* + * Reads all registers and writes the values back to their appropriate + * verification registers to clear the Memory Validation fault. + */ + ret = max14001_write_verification_reg(st, MAX14001_FLTEN); + if (ret) + return ret; + + ret = max14001_write_verification_reg(st, MAX14001_THL); + if (ret) + return ret; + + ret = max14001_write_verification_reg(st, MAX14001_THU); + if (ret) + return ret; + + ret = max14001_write_verification_reg(st, MAX14001_INRR); + if (ret) + return ret; + + ret = max14001_write_verification_reg(st, MAX14001_INRT); + if (ret) + return ret; + + ret = max14001_write_verification_reg(st, MAX14001_INRP); + if (ret) + return ret; + + ret = max14001_write_verification_reg(st, MAX14001_CFG); + if (ret) + return ret; + + ret = max14001_write_verification_reg(st, MAX14001_ENBL); + if (ret) + return ret; + + /* Disable SPI Registers Write */ + return max14001_write(st, MAX14001_WEN, 0); +} + +static int max14001_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct max14001_state *st; + struct regulator *vref; + struct device *dev = &spi->dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->regmap = devm_regmap_init(dev, NULL, st, &max14001_regmap_config); + + indio_dev->name = "max14001"; + indio_dev->info = &max14001_info; + indio_dev->channels = max14001_channels; + indio_dev->num_channels = ARRAY_SIZE(max14001_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = max14001_init(st); + if (ret) + return ret; + + vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(vref)) { + if (PTR_ERR(vref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(vref), + "Failed to get vref regulator"); + + /* internal reference */ + st->vref_mv = 1250; + } else { + ret = regulator_enable(vref); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable vref regulators\n"); + + ret = devm_add_action_or_reset(dev, max14001_regulator_disable, + vref); + if (ret) + return ret; + + /* select external voltage reference source for the ADC */ + ret = max14001_reg_update(st, MAX14001_CFG, + MAX14001_CFG_EXRF, 1); + + ret = regulator_get_voltage(vref); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get vref\n"); + + st->vref_mv = ret / 1000; + } + + mutex_init(&st->lock); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id max14001_of_match[] = { + { .compatible = "adi,max14001" }, + { } +}; +MODULE_DEVICE_TABLE(of, max14001_of_match); + +static struct spi_driver max14001_driver = { + .driver = { + .name = "max14001", + .of_match_table = max14001_of_match, + }, + .probe = max14001_probe, +}; +module_spi_driver(max14001_driver); + +MODULE_AUTHOR("Kim Seer Paller"); +MODULE_DESCRIPTION("MAX14001 ADC driver"); +MODULE_LICENSE("GPL"); -- 2.34.1