From patchwork Wed Jun 18 17:37:49 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Marcelo Schmitt X-Patchwork-Id: 897746 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C99B62F49E1; Wed, 18 Jun 2025 17:38:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268307; cv=none; b=C31bpSQhkcpQyTyqbF6uKZTFmvEoq2wlQUdk9wrPZQKzmlxhZojTS3RQQdqNvW9DQ2B3GC6vNcwVqifQbeFmsmCskOwgUPtl0tglSVDjpexwNLsVYLDPielhJj/VKTwpRAAkt+AJ7el9rxSE1ur4t6GHOhxZSaQ9kkGnwEj5Jqw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268307; c=relaxed/simple; bh=mjZYWaRkcRdjarRU3mciTYZiQMkNMt8qhA2kEYBByug=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=m+Pgede25emd0dnDB+qEK2C+7Q7FspMBYWJJD9AcnxEAQYVuh5sliBEIIqZJmXBb3zMmwR0b3vvAzpqrGSi4TLBUshO/Zca2IA2wZ9eiFXVEwToIbUWvreemhPZFSebzh7F7AlnuXBZvAKRmL50M+SMnC6e8yoeEtdnJOwjpwWM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=1Z83l+zA; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="1Z83l+zA" Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IG2GcP027608; Wed, 18 Jun 2025 13:38:09 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=yIzda 7vTpc+HHdRa8iOboc63iEN2HWlM5y0ZFmHhelI=; b=1Z83l+zAbId7mLaSjdPVQ YeUaokilvDoN20PzVlcAZqGggmJBlCN7Jedtj6X8cBAu7xnBPf/nVH7eTHWFhFWS MXfB50UimLRnauIel3lH+kyCVxlVT/D38JKusLbsb2mhs/jXDu/TrrpAqY26ZOdH YgRj6NB9Bw+TFiEHcCJnQ0ExewcBi21u7V/FIFU4HsZ/+3jenGvtTWJNYBkd53eV rY77BtiLBcuDBPcSxkTLCLVE0L+WhEKejU/kLQhMZRbanqZY0pYBuUIrby/hzBp4 jcSh1ZH8mdg3s8PrnQl9lIjkYUGn2wHre3d/AzXbkkPdKj40bgVZM+x8kYVmXfb+ Q== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfxcwdd1-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:38:08 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 55IHc7R9016190 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:38:07 -0400 Received: from ASHBCASHYB5.ad.analog.com (10.64.17.133) 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.1748.10; Wed, 18 Jun 2025 13:38:07 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB5.ad.analog.com (10.64.17.133) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:38:06 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:38:06 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHbo42007486; Wed, 18 Jun 2025 13:37:53 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 08/12] iio: adc: ad4170: Add clock provider support Date: Wed, 18 Jun 2025 14:37:49 -0300 Message-ID: <46c3b9da0270e748db1676f176156093237980e7.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: cCnAUvFOtcjnVCAQ4WtswMq30Z20FPS0 X-Proofpoint-GUID: cCnAUvFOtcjnVCAQ4WtswMq30Z20FPS0 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfXwHrF68xtBU1g sNNh5yUUGSTtz0rTk9/seIp405drmnOk3jI3spbK4qmABWjgRjNBQLsgpfB5c1xOlWzfeX5snzE 7ha12xaNKEStaHNcShQfBR2LHfglQZVOcfE/kPB/YD0LGp9fbj2QR8iPpw3yhLGtMIF7KNq5fF4 u0dLUD2jDU49i2QKHyCckBF/fuPfy+/Dke/QCGvTTkwkoiBgJI9vT28ovLC7ectQgc4TyqVjn4P lYuX13xqukZgHj7+FywDMaxc74wM0Cj6sJ20V9eNFl/5LgF6owS+BqylYlYHrFLWLsJ7QQEVYHg iAD9Wgr3w+0gEQad+jClTCq44NQr7gyTo/ZfqGnyz9n8NXCs49pnkaTzCP7g3QdBiUKTFrw2wjZ SWA3eZ7n6eKPVuL2dzy70FFR2uYUTEkKJhigFdWAsgvqRQ9xqGSMr1yYjaID2ZREmyn6jYCM X-Authority-Analysis: v=2.4 cv=Jb28rVKV c=1 sm=1 tr=0 ts=6852f980 cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=IkcTkHD0fZMA:10 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=FbnkXhljGIpD8s1dgEMA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 mlxlogscore=999 clxscore=1011 mlxscore=0 priorityscore=1501 lowpriorityscore=0 phishscore=0 adultscore=0 suspectscore=0 spamscore=0 impostorscore=0 bulkscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 The AD4170 chip can use an externally supplied clock at the XTAL2 pin, or an external crystal connected to the XTAL1 and XTAL2 pins. Alternatively, the AD4170 can provide its 16 MHz internal clock at the XTAL2 pin. In addition, the chip has a programmable clock divider that allows dividing the external or internal clock frequency, however, control for that is not provided in this patch. Extend the AD4170 driver so it effectively uses the provided external clock, if any, or supplies its own clock as a clock provider. Reviewed-by: Nuno Sá Signed-off-by: Marcelo Schmitt --- Change log v5 -> v6 - Now using device_property_present() to check #clock-cells presence. drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ad4170.c | 147 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index b12dcc04c894..32e5177ceebe 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -91,6 +91,7 @@ config AD4170 select REGMAP_SPI select IIO_BUFFER select IIO_TRIGGERED_BUFFER + depends on COMMON_CLK help Say yes here to build support for Analog Devices AD4170 SPI analog to digital converters (ADC). diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 2acd4316b079..21921844f5e6 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -53,6 +55,7 @@ #define AD4170_CONFIG_A_REG 0x00 #define AD4170_DATA_24B_REG 0x1E #define AD4170_PIN_MUXING_REG 0x69 +#define AD4170_CLOCK_CTRL_REG 0x6B #define AD4170_ADC_CTRL_REG 0x71 #define AD4170_CHAN_EN_REG 0x79 #define AD4170_CHAN_SETUP_REG(x) (0x81 + 4 * (x)) @@ -73,6 +76,9 @@ /* AD4170_PIN_MUXING_REG */ #define AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK GENMASK(5, 4) +/* AD4170_CLOCK_CTRL_REG */ +#define AD4170_CLOCK_CTRL_CLOCKSEL_MSK GENMASK(1, 0) + /* AD4170_ADC_CTRL_REG */ #define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK BIT(7) #define AD4170_ADC_CTRL_CONT_READ_MSK GENMASK(5, 4) @@ -100,6 +106,12 @@ /* AD4170 register constants */ +/* AD4170_CLOCK_CTRL_REG constants */ +#define AD4170_CLOCK_CTRL_CLOCKSEL_INT 0x0 +#define AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT 0x1 +#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT 0x2 +#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT_XTAL 0x3 + /* AD4170_CHAN_MAP_REG constants */ #define AD4170_CHAN_MAP_AIN(x) (x) #define AD4170_CHAN_MAP_TEMP_SENSOR 17 @@ -147,6 +159,8 @@ /* Internal and external clock properties */ #define AD4170_INT_CLOCK_16MHZ (16 * HZ_PER_MHZ) +#define AD4170_EXT_CLOCK_MHZ_MIN (1 * HZ_PER_MHZ) +#define AD4170_EXT_CLOCK_MHZ_MAX (17 * HZ_PER_MHZ) #define AD4170_NUM_PGA_OPTIONS 10 @@ -164,6 +178,7 @@ static const unsigned int ad4170_reg_size[] = { [AD4170_CONFIG_A_REG] = 1, [AD4170_DATA_24B_REG] = 3, [AD4170_PIN_MUXING_REG] = 2, + [AD4170_CLOCK_CTRL_REG] = 2, [AD4170_ADC_CTRL_REG] = 2, [AD4170_CHAN_EN_REG] = 2, /* @@ -236,6 +251,10 @@ enum ad4170_regulator { AD4170_MAX_SUP, }; +static const char *const ad4170_clk_sel[] = { + "ext-clk", "xtal", +}; + enum ad4170_int_pin_sel { AD4170_INT_PIN_SDO, AD4170_INT_PIN_DIG_AUX1, @@ -338,6 +357,8 @@ struct ad4170_state { struct completion completion; unsigned int pins_fn[AD4170_NUM_ANALOG_PINS]; u32 int_pin_sel; + struct clk_hw int_clk_hw; + unsigned int clock_ctrl; /* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines. @@ -1616,13 +1637,137 @@ static int ad4170_parse_channels(struct iio_dev *indio_dev) return 0; } +static struct ad4170_state *clk_hw_to_ad4170(struct clk_hw *hw) +{ + return container_of(hw, struct ad4170_state, int_clk_hw); +} + +static unsigned long ad4170_sel_clk(struct ad4170_state *st, + unsigned int clk_sel) +{ + st->clock_ctrl &= ~AD4170_CLOCK_CTRL_CLOCKSEL_MSK; + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, clk_sel); + return regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); +} + +static unsigned long ad4170_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD4170_INT_CLOCK_16MHZ; +} + +static int ad4170_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + u32 clk_sel; + + clk_sel = FIELD_GET(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, st->clock_ctrl); + return clk_sel == AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT; +} + +static int ad4170_clk_output_prepare(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + + return ad4170_sel_clk(st, AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT); +} + +static void ad4170_clk_output_unprepare(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + + ad4170_sel_clk(st, AD4170_CLOCK_CTRL_CLOCKSEL_INT); +} + +static const struct clk_ops ad4170_int_clk_ops = { + .recalc_rate = ad4170_clk_recalc_rate, + .is_enabled = ad4170_clk_output_is_enabled, + .prepare = ad4170_clk_output_prepare, + .unprepare = ad4170_clk_output_unprepare, +}; + +static int ad4170_register_clk_provider(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct clk_init_data init = {}; + int ret; + + if (device_property_read_string(dev, "clock-output-names", &init.name)) { + init.name = devm_kasprintf(dev, GFP_KERNEL, "%pfw", + dev_fwnode(dev)); + if (!init.name) + return -ENOMEM; + } + + init.ops = &ad4170_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad4170_clock_select(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + struct clk *ext_clk; + int ret; + + ext_clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(ext_clk)) + return dev_err_probe(dev, PTR_ERR(ext_clk), + "Failed to get external clock\n"); + + if (!ext_clk) { + /* Use internal clock reference */ + st->mclk_hz = AD4170_INT_CLOCK_16MHZ; + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT); + + if (!device_property_present(&st->spi->dev, "#clock-cells")) + return 0; + + return ad4170_register_clk_provider(indio_dev); + } + + /* Read optional clock-names prop to specify the external clock type */ + ret = device_property_match_property_string(dev, "clock-names", + ad4170_clk_sel, + ARRAY_SIZE(ad4170_clk_sel)); + + ret = ret < 0 ? 0 : ret; /* Default to external clock if no clock-names */ + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_CLOCK_CTRL_CLOCKSEL_EXT + ret); + + st->mclk_hz = clk_get_rate(ext_clk); + if (st->mclk_hz < AD4170_EXT_CLOCK_MHZ_MIN || + st->mclk_hz > AD4170_EXT_CLOCK_MHZ_MAX) { + return dev_err_probe(dev, -EINVAL, + "Invalid external clock frequency %u\n", + st->mclk_hz); + } + + return 0; +} + static int ad4170_parse_firmware(struct iio_dev *indio_dev) { struct ad4170_state *st = iio_priv(indio_dev); struct device *dev = &st->spi->dev; int reg_data, ret; - st->mclk_hz = AD4170_INT_CLOCK_16MHZ; + ret = ad4170_clock_select(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup device clock\n"); + + ret = regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); + if (ret) + return ret; /* On power on, device defaults to using SDO pin for data ready signal */ st->int_pin_sel = AD4170_INT_PIN_SDO;