From nobody Wed Jul 1 02:45:08 2026 Received: from azure-sdnproxy.icoremail.net (azure-sdnproxy.icoremail.net [52.229.168.213]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5EA7F3403F7; Tue, 30 Jun 2026 09:11:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=52.229.168.213 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782810702; cv=none; b=KlOWG5HiWaw1W9+DuxG9cFZXbVkyqXXwPEEZcIbSQAkCIG1B6odsW5B0O8INgDvUHktuS2etv0nQs2fNMef3xF3AfR+c/MIk2TyxA3VI7yqrQY5SuIGE77K5X0IdshmbF2RF5D+fzPc/oNcX1ghbRCbUSUZKfB5LsYwr8q3bL+Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782810702; c=relaxed/simple; bh=QNMwS0lMZsXdqbMsqFeYbmYpYB5amVx1FCR61nA8wR8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=AO3p/b810+luJwToh88oPTICxoKcs88fXMEXHx5eoBY4sn8eCmwUrL/KPZXDIO4GKpkgiZkiGBCfmrv4O/wjEtVr9k55AeWxpt0DudPx6KJhXLtQC2aeggKWvVWqAaSIMQvGjEj/uWwP19x2C6xbWBHuC477w/e7Ei/Wb0blYfY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com; spf=pass smtp.mailfrom=eswincomputing.com; arc=none smtp.client-ip=52.229.168.213 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=eswincomputing.com Received: from E0005152DT.eswin.cn (unknown [10.12.96.41]) by app2 (Coremail) with SMTP id TQJkCgCHnaA6iENqoSUwAA--.36520S2; Tue, 30 Jun 2026 17:11:32 +0800 (CST) From: dongxuyang@eswincomputing.com To: linux@roeck-us.net, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, linux-hwmon@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, p.zabel@pengutronix.de Cc: ningyu@eswincomputing.com, linmin@eswincomputing.com, pinkesh.vaghela@einfochips.com, luyulin@eswincomputing.com, dongxuyang@eswincomputing.com, Conor Dooley Subject: [PATCH v9 1/2] dt-bindings: hwmon: Add Eswin EIC7700 PVT sensor Date: Tue, 30 Jun 2026 17:11:22 +0800 Message-Id: <20260630091122.1462-1-dongxuyang@eswincomputing.com> X-Mailer: git-send-email 2.31.1.windows.1 In-Reply-To: <20260630091040.1407-1-dongxuyang@eswincomputing.com> References: <20260630091040.1407-1-dongxuyang@eswincomputing.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: TQJkCgCHnaA6iENqoSUwAA--.36520S2 X-Coremail-Antispam: 1UD129KBjvJXoWxAr45AF1rKry5Gw47JF15twb_yoW5Jw13pF s7CFyUGr1xXryxZ3y3tF109F1ftws5CFy7Arn2q3WrKr1Dtas0qw43KFy5ua48Cr1fXFW3 uFyaq34ay3WDA3DanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUBv14x267AKxVW8JVW5JwAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK02 1l84ACjcxK6xIIjxv20xvE14v26w1j6s0DM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4U JVWxJr1l84ACjcxK6I8E87Iv67AKxVW0oVCq3wA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_Gc CE3s1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E 2Ix0cI8IcVAFwI0_Jrv_JF1lYx0Ex4A2jsIE14v26r4j6F4UMcvjeVCFs4IE7xkEbVWUJV W8JwACjcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lFIxGxcIEc7CjxVA2 Y2ka0xkIwI1lw4CEc2x0rVAKj4xxMxkF7I0En4kS14v26r1q6r43MxkIecxEwVCm-wCF04 k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18 MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_Jw0_GFylIxkGc2Ij64vIr4 1lIxAIcVC0I7IYx2IY67AKxVWUJVWUCwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1l IxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4 A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x0JUDgA7UUUUU= X-CM-SenderInfo: pgrqw5xx1d0w46hv4xpqfrz1xxwl0woofrz/ Content-Type: text/plain; charset="utf-8" From: Huan He Add device tree binding documentation for ESWIN EIC7700 Voltage and Temperature sensor. The EIC7700 SoC integrates two PVT instances for monitoring SoC and DDR power domains respectively. Signed-off-by: Yulin Lu Signed-off-by: Huan He Reviewed-by: Conor Dooley Signed-off-by: Xuyang Dong --- .../bindings/hwmon/eswin,eic7700-pvt.yaml | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/eswin,eic7700-p= vt.yaml diff --git a/Documentation/devicetree/bindings/hwmon/eswin,eic7700-pvt.yaml= b/Documentation/devicetree/bindings/hwmon/eswin,eic7700-pvt.yaml new file mode 100644 index 000000000000..58ec8635dce3 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/eswin,eic7700-pvt.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/eswin,eic7700-pvt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ESWIN EIC7700 PVT Sensor + +maintainers: + - Yulin Lu + - Huan He + +description: + ESWIN EIC7700 SoC integrates embedded voltage and temperature sensors to + monitor the internal SoC environment. The system includes two PVT sensor + instances. The PVT0 monitors the main SoC power domain. The PVT1 sensor + monitors the DDR core power domain. + +allOf: + - $ref: /schemas/hwmon/hwmon-common.yaml# + +properties: + compatible: + const: eswin,eic7700-pvt + + reg: + maxItems: 1 + + clocks: + items: + - description: PVT enable clock + - description: APB bus clock + + clock-names: + items: + - const: enable + - const: apb + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + + '#thermal-sensor-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - resets + - '#thermal-sensor-cells' + +unevaluatedProperties: false + +examples: + - | + sensor@50b00000 { + compatible =3D "eswin,eic7700-pvt"; + reg =3D <0x50b00000 0x10000>; + clocks =3D <&clocks 244>, <&clocks 234>; + clock-names =3D "enable", "apb"; + interrupts =3D <349>; + interrupt-parent =3D <&plic>; + label =3D "pvt0"; + resets =3D <&reset 111>; + #thermal-sensor-cells =3D <0>; + }; +... --=20 2.34.1 From nobody Wed Jul 1 02:45:08 2026 Received: from zg8tmja5ljk3lje4mi4ymjia.icoremail.net (zg8tmja5ljk3lje4mi4ymjia.icoremail.net [209.97.182.222]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4415D3E9F9D; Tue, 30 Jun 2026 09:12:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.97.182.222 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782810759; cv=none; b=IwsJCOu0T8R7SlB92+VCYBap3Mlk/lJta34DdXsn0CgF8Rx0esBuvNCJw/Q80ZBhIEuP5r7IdchuXz9WtIlByrLxMOYDCagkIZscyllSuDC3T/F4+znRDZRO28NdHmIeKbXpw9xnqFN1agWYgL+57V0lHODrfxjPyG/cau4YgdQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782810759; c=relaxed/simple; bh=+z/fxfMTZswNAHzmi9sjVfkA7+Dw+AIVB2qXFtXoPnc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=Lvbve0pTz9B/r/ElHEIkMrRUyF7X85HZygF/4DLT/BhZaDC+0Li7rAodfMVfdhR+uQ6SB9g+95uG5fhWRPiE5HwbrMPUKQ/rM+EQX+dmPD4Zrq25NSi0rnZeOeBtHm/MgZ2oJzfT50f7qOh3iUNdnhEHPyMw9FsUuz7Wfk/wPT8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com; spf=pass smtp.mailfrom=eswincomputing.com; arc=none smtp.client-ip=209.97.182.222 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=eswincomputing.com Received: from E0005152DT.eswin.cn (unknown [10.12.96.41]) by app2 (Coremail) with SMTP id TQJkCgBXa550iENqtiUwAA--.52823S2; Tue, 30 Jun 2026 17:12:21 +0800 (CST) From: dongxuyang@eswincomputing.com To: linux@roeck-us.net, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, linux-hwmon@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, p.zabel@pengutronix.de Cc: ningyu@eswincomputing.com, linmin@eswincomputing.com, pinkesh.vaghela@einfochips.com, luyulin@eswincomputing.com, dongxuyang@eswincomputing.com Subject: [PATCH v9 2/2] hwmon: Add Eswin EIC7700 PVT sensor driver Date: Tue, 30 Jun 2026 17:12:20 +0800 Message-Id: <20260630091220.1608-1-dongxuyang@eswincomputing.com> X-Mailer: git-send-email 2.31.1.windows.1 In-Reply-To: <20260630091040.1407-1-dongxuyang@eswincomputing.com> References: <20260630091040.1407-1-dongxuyang@eswincomputing.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: TQJkCgBXa550iENqtiUwAA--.52823S2 X-Coremail-Antispam: 1UD129KBjvAXoW3ur4kWr4DXw4rGr4UXFy7Wrg_yoW8CF4ruo WfGFn3Zw18JrWfCrZxGF10qFyxXwn29w4rZ3WFkFsF9F17trn0gay7KwnxW3W3Kr1Ygr4j vrn3G34rZFW7t3Wfn29KB7ZKAUJUUUU5529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UjIYCTnIWjp_UUUYK7AC8VAFwI0_Gr0_Xr1l1xkIjI8I6I8E6xAIw20EY4v20xva j40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l8cAvFVAK0II2c7xJM28CjxkF64kEwVA0rcxSw2 x7M28EF7xvwVC0I7IYx2IY67AKxVWDJVCq3wA2z4x0Y4vE2Ix0cI8IcVCY1x0267AKxVW8 Jr0_Cr1UM28EF7xvwVC2z280aVAFwI0_GcCE3s1l84ACjcxK6I8E87Iv6xkF7I0E14v26r xl6s0DM2AIxVAIcxkEcVAq07x20xvEncxIr21l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj 6xIIjxv20xvE14v26r126r1DMcIj6I8E87Iv67AKxVW8JVWxJwAm72CE4IkC6x0Yz7v_Jr 0_Gr1lF7xvr2IYc2Ij64vIr41lF7I21c0EjII2zVCS5cI20VAGYxC7M4IIrI8v6xkF7I0E 8cxan2IY04v7M4kE6xkIj40Ew7xC0wCY1x0262kKe7AKxVWUtVW8ZwCY02Avz4vE-syl42 xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWU GwC20s026x8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r1q6r43MIIYrxkI7VAKI4 8JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4U MIIF0xvE42xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVWUJVW8JwCI42IY6I 8E87Iv6xkF7I0E14v26r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjfUeZ2-DUUUU X-CM-SenderInfo: pgrqw5xx1d0w46hv4xpqfrz1xxwl0woofrz/ From: Huan He Add support for ESWIN EIC7700 Voltage and Temperature sensor. The driver supports temperature and voltage monitoring with polynomial conversion, and provides sysfs interface for sensor data access. The PVT IP contains one temperature sensor and one voltage sensor. Signed-off-by: Yulin Lu Signed-off-by: Huan He Signed-off-by: Xuyang Dong --- drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/eic7700-pvt.c | 509 ++++++++++++++++++++++++++++++++++++ drivers/hwmon/eic7700-pvt.h | 99 +++++++ 4 files changed, 620 insertions(+) create mode 100644 drivers/hwmon/eic7700-pvt.c create mode 100644 drivers/hwmon/eic7700-pvt.h diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5c2d3ff5fce8..b25300157f62 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2067,6 +2067,17 @@ config SENSORS_DME1737 This driver can also be built as a module. If so, the module will be called dme1737. =20 +config SENSORS_EIC7700_PVT + tristate "Eswin EIC7700 Voltage, Temperature sensor driver" + depends on ARCH_ESWIN || COMPILE_TEST + select POLYNOMIAL + help + If you say yes here you get support for Eswin EIC7700 PVT sensor + embedded into the SoC. + + This driver can also be built as a module. If so, the module will be + called eic7700-pvt. + config SENSORS_EMC1403 tristate "SMSC EMC1403/23 thermal sensor" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 63effc0ab8d1..e49cfdda970c 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_SENSORS_DME1737) +=3D dme1737.o obj-$(CONFIG_SENSORS_DRIVETEMP) +=3D drivetemp.o obj-$(CONFIG_SENSORS_DS620) +=3D ds620.o obj-$(CONFIG_SENSORS_DS1621) +=3D ds1621.o +obj-$(CONFIG_SENSORS_EIC7700_PVT) +=3D eic7700-pvt.o obj-$(CONFIG_SENSORS_EMC1403) +=3D emc1403.o obj-$(CONFIG_SENSORS_EMC1812) +=3D emc1812.o obj-$(CONFIG_SENSORS_EMC2103) +=3D emc2103.o diff --git a/drivers/hwmon/eic7700-pvt.c b/drivers/hwmon/eic7700-pvt.c new file mode 100644 index 000000000000..d7403ab33f4e --- /dev/null +++ b/drivers/hwmon/eic7700-pvt.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ESWIN EIC7700 Voltage, Temperature sensor driver + * + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd. + * + * Authors: + * Yulin Lu + * Huan He + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "eic7700-pvt.h" + +static const struct pvt_sensor_info pvt_info[] =3D { + PVT_SENSOR_INFO(0, "Temperature", hwmon_temp, TEMP), + PVT_SENSOR_INFO(0, "Voltage", hwmon_in, VOLT), +}; + +static const char * const pvt_clk_names[PVT_CLK_NUM] =3D {"enable", "apb"}; + +/* + * The original translation formulae of the temperature (in degrees of Cel= sius) + * to PVT data and vice-versa are following: + * N =3D 6.0818e-8*(T^4) +1.2873e-5*(T^3) + 7.2244e-3*(T^2) + 3.6484*(T^1)= + + * 1.6198e2, + * T =3D -1.8439e-11*(N^4) + 8.0705e-8*(N^3) + -1.8501e-4*(N^2) + + * 3.2843e-1*(N^1) - 4.8690e1, + * where T =3D [-40, 125]C and N =3D [27, 771]. + * They must be accordingly altered to be suitable for the integer arithme= tics. + * The technique is called 'factor redistribution', which just makes sure = the + * multiplications and divisions are made so to have a result of the opera= tions + * within the integer numbers limit. In addition we need to translate the + * formulae to accept millidegrees of Celsius. Here what they look like af= ter + * the alterations: + * N =3D (60818e-20*(T^4) + 12873e-14*(T^3) + 72244e-9*(T^2) + 36484e-3*T + + * 16198e2) / 1e4, + * T =3D -18439e-12*(N^4) + 80705e-9*(N^3) - 185010e-6*(N^2) + 328430e-3*N= - + * 48690, + * where T =3D [-40000, 125000] mC and N =3D [27, 771]. + */ +static const struct polynomial poly_N_to_temp =3D { + .total_divider =3D 1, + .terms =3D { + {4, -18439, 1000, 1}, + {3, 80705, 1000, 1}, + {2, -185010, 1000, 1}, + {1, 328430, 1000, 1}, + {0, -48690, 1, 1} + } +}; + +/* + * Similar alterations are performed for the voltage conversion equations. + * The original formulae are: + * N =3D 1.3905e3*V - 5.7685e2, + * V =3D (N + 5.7685e2) / 1.3905e3, + * where V =3D [0.72, 0.88] V and N =3D [424, 646]. + * After the optimization they looks as follows: + * N =3D (13905e-3*V - 5768.5) / 10, + * V =3D (N * 10^5 / 13905 + 57685 * 10^3 / 13905) / 10. + * where V =3D [720, 880] mV and N =3D [424, 646]. + */ +static const struct polynomial poly_N_to_volt =3D { + .total_divider =3D 10, + .terms =3D { + {1, 100000, 13905, 1}, + {0, 57685000, 1, 13905} + } +}; + +static inline u32 eic7700_pvt_update(void __iomem *reg, u32 mask, u32 data) +{ + u32 old; + + old =3D readl_relaxed(reg); + writel((old & ~mask) | (data & mask), reg); + + return old & mask; +} + +static inline void eic7700_pvt_set_mode(struct pvt_hwmon *pvt, u32 mode) +{ + u32 old; + + mode =3D FIELD_PREP(PVT_MODE_MASK, mode); + + old =3D eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); + eic7700_pvt_update(pvt->regs + PVT_MODE, PVT_MODE_MASK, mode); + eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old); +} + +static inline void eic7700_pvt_set_trim(struct pvt_hwmon *pvt, u32 val) +{ + u32 old; + + old =3D eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); + writel(val, pvt->regs + PVT_TRIM); + eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old); +} + +static irqreturn_t eic7700_pvt_hard_isr(int irq, void *data) +{ + struct pvt_hwmon *pvt =3D data; + u32 stat, val; + int active; + + if (IS_ENABLED(CONFIG_PM)) { + active =3D pm_runtime_get_if_active(pvt->dev); + if (active <=3D 0) + return IRQ_NONE; + } + + stat =3D readl(pvt->regs + PVT_INT); + if (!(stat & PVT_INT_STAT)) { + if (IS_ENABLED(CONFIG_PM)) + pm_runtime_put(pvt->dev); + return IRQ_NONE; + } + + eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); + /* + * Read the data, update the cache and notify a waiter of this event. + */ + val =3D readl(pvt->regs + PVT_DATA); + WRITE_ONCE(pvt->data_cache, FIELD_GET(PVT_DATA_OUT, val)); + complete(&pvt->conversion); + + if (IS_ENABLED(CONFIG_PM)) + pm_runtime_put(pvt->dev); + + return IRQ_HANDLED; +} + +static int eic7700_pvt_read_data(struct pvt_hwmon *pvt, + enum pvt_sensor_type type, long *val) +{ + unsigned long timeout; + u32 data; + int ret; + + /* + * Wait for PVT conversion to complete and update the data cache. The + * data read procedure is following: set the requested PVT sensor mode, + * enable conversion, wait until conversion is finished, then disable + * conversion and IRQ, and read the cached data. + */ + reinit_completion(&pvt->conversion); + + eic7700_pvt_set_mode(pvt, pvt_info[type].mode); + eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, PVT_ENA_EN); + + /* + * Wait with timeout since in case if the sensor is suddenly powered + * down the request won't be completed and the caller will hang up on + * this procedure until the power is back up again. Multiply the + * timeout by the factor of two to prevent a false timeout. + */ + timeout =3D 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout)); + ret =3D wait_for_completion_timeout(&pvt->conversion, timeout); + + eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); + eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); + + if (!ret) + synchronize_irq(pvt->irq); + + data =3D READ_ONCE(pvt->data_cache); + + if (!ret) + return -ETIMEDOUT; + + if (type =3D=3D PVT_TEMP) + *val =3D polynomial_calc(&poly_N_to_temp, data); + else + *val =3D polynomial_calc(&poly_N_to_volt, data); + + return 0; +} + +static const struct hwmon_channel_info *pvt_channel_info[] =3D { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL), + NULL +}; + +static umode_t eic7700_pvt_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int ch) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + return 0444; + } + break; + default: + break; + } + + return 0; +} + +static int eic7700_pvt_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int ch, long *val) +{ + struct pvt_hwmon *pvt =3D dev_get_drvdata(dev); + int ret; + + ret =3D pm_runtime_get_sync(pvt->dev); + if (ret < 0) { + dev_err(pvt->dev, "Failed to resume PVT device: %d\n", ret); + pm_runtime_put_noidle(pvt->dev); + return ret; + } + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + ret =3D eic7700_pvt_read_data(pvt, ch, val); + break; + default: + ret =3D -EOPNOTSUPP; + } + break; + case hwmon_in: + if (attr =3D=3D hwmon_in_input) + ret =3D eic7700_pvt_read_data(pvt, PVT_VOLT + ch, val); + else + ret =3D -EOPNOTSUPP; + break; + default: + ret =3D -EOPNOTSUPP; + } + + pm_runtime_mark_last_busy(pvt->dev); + pm_runtime_put_autosuspend(pvt->dev); + return ret; +} + +static int eic7700_pvt_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int ch, const char **str) +{ + switch (type) { + case hwmon_temp: + if (attr =3D=3D hwmon_temp_label) { + *str =3D pvt_info[ch].label; + return 0; + } + break; + case hwmon_in: + if (attr =3D=3D hwmon_in_label) { + *str =3D pvt_info[PVT_VOLT + ch].label; + return 0; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +} + +static const struct hwmon_ops pvt_hwmon_ops =3D { + .is_visible =3D eic7700_pvt_hwmon_is_visible, + .read =3D eic7700_pvt_hwmon_read, + .read_string =3D eic7700_pvt_hwmon_read_string +}; + +static const struct hwmon_chip_info pvt_hwmon_info =3D { + .ops =3D &pvt_hwmon_ops, + .info =3D pvt_channel_info +}; + +static struct pvt_hwmon *eic7700_pvt_create_data(struct platform_device *p= dev) +{ + struct device *dev =3D &pdev->dev; + struct pvt_hwmon *pvt; + + pvt =3D devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); + if (!pvt) + return ERR_PTR(-ENOMEM); + + pvt->dev =3D dev; + init_completion(&pvt->conversion); + + return pvt; +} + +static int eic7700_pvt_init_iface(struct pvt_hwmon *pvt) +{ + /* + * Make sure controller are disabled so not to accidentally have ISR + * executed before the driver data is fully initialized. Clear the IRQ + * status as well. + */ + eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); + eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); + readl(pvt->regs + PVT_INT); + readl(pvt->regs + PVT_DATA); + + /* Setup default sensor mode and temperature trim. */ + eic7700_pvt_set_mode(pvt, pvt_info[PVT_TEMP].mode); + + /* + * Max conversion latency (~333 =C2=B5s) derived from PVT spec: + * maximum sampling rate =3D 3000 samples/sec. + */ + pvt->timeout =3D ns_to_ktime(PVT_TOUT_MIN); + + eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF); + + return 0; +} + +static int eic7700_pvt_request_irq(struct pvt_hwmon *pvt) +{ + struct platform_device *pdev =3D to_platform_device(pvt->dev); + int ret; + + pvt->irq =3D platform_get_irq(pdev, 0); + if (pvt->irq < 0) + return pvt->irq; + + ret =3D devm_request_threaded_irq(pvt->dev, pvt->irq, + eic7700_pvt_hard_isr, NULL, + IRQF_TRIGGER_HIGH, "pvt", pvt); + if (ret) { + dev_err(pvt->dev, "Couldn't request PVT IRQ\n"); + return ret; + } + + return 0; +} + +static int eic7700_pvt_create_hwmon(struct pvt_hwmon *pvt) +{ + pvt->hwmon =3D devm_hwmon_device_register_with_info(pvt->dev, "pvt", + pvt, &pvt_hwmon_info, + NULL); + if (IS_ERR(pvt->hwmon)) { + dev_err(pvt->dev, "Couldn't create hwmon device\n"); + return PTR_ERR(pvt->hwmon); + } + + return 0; +} + +static void eic7700_pvt_disable_pm_runtime(void *data) +{ + struct pvt_hwmon *pvt =3D data; + + pm_runtime_dont_use_autosuspend(pvt->dev); + pm_runtime_disable(pvt->dev); + + if (!pm_runtime_status_suspended(pvt->dev)) { + clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); + pm_runtime_set_suspended(pvt->dev); + } +} + +static int eic7700_pvt_probe(struct platform_device *pdev) +{ + struct reset_control *rst; + struct pvt_hwmon *pvt; + int i, ret; + + pvt =3D eic7700_pvt_create_data(pdev); + if (IS_ERR(pvt)) + return PTR_ERR(pvt); + + platform_set_drvdata(pdev, pvt); + + pvt->regs =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pvt->regs)) + return PTR_ERR(pvt->regs); + + for (i =3D 0; i < PVT_CLK_NUM; i++) + pvt->clks[i].id =3D pvt_clk_names[i]; + + ret =3D devm_clk_bulk_get(&pdev->dev, PVT_CLK_NUM, pvt->clks); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Couldn't get clock descriptors\n"); + + rst =3D devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(pvt->dev, PTR_ERR(rst), + "Couldn't get reset control\n"); + + ret =3D clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks); + if (ret) + return dev_err_probe(pvt->dev, ret, + "Failed to enable clocks\n"); + + ret =3D eic7700_pvt_init_iface(pvt); + if (ret) { + clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); + return ret; + } + + if (IS_ENABLED(CONFIG_PM)) + clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); + + pm_runtime_enable(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + + ret =3D devm_add_action_or_reset(pvt->dev, eic7700_pvt_disable_pm_runtime, + pvt); + if (ret) { + pm_runtime_put_noidle(&pdev->dev); + return dev_err_probe(&pdev->dev, ret, + "Can't register PM cleanup\n"); + } + + ret =3D eic7700_pvt_request_irq(pvt); + if (ret) + goto err_put_pm_runtime; + + ret =3D eic7700_pvt_create_hwmon(pvt); + if (ret) + goto err_put_pm_runtime; + + pm_runtime_put_autosuspend(&pdev->dev); + + return 0; + +err_put_pm_runtime: + pm_runtime_put_noidle(&pdev->dev); + return ret; +} + +static int __maybe_unused eic7700_pvt_runtime_resume(struct device *dev) +{ + struct pvt_hwmon *pvt =3D dev_get_drvdata(dev); + int ret; + + ret =3D clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks); + if (ret) { + dev_err(dev, "Failed to enable clocks: %d\n", ret); + return ret; + } + + eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF); + + return 0; +} + +static int __maybe_unused eic7700_pvt_runtime_suspend(struct device *dev) +{ + struct pvt_hwmon *pvt =3D dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); + + return 0; +} + +static const struct dev_pm_ops eic7700_pvt_pm_ops =3D { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(eic7700_pvt_runtime_suspend, eic7700_pvt_runtime_resume, + NULL) +}; + +static const struct of_device_id pvt_of_match[] =3D { + { .compatible =3D "eswin,eic7700-pvt"}, + { } +}; +MODULE_DEVICE_TABLE(of, pvt_of_match); + +static struct platform_driver pvt_driver =3D { + .probe =3D eic7700_pvt_probe, + .driver =3D { + .name =3D "eic7700-pvt", + .of_match_table =3D pvt_of_match, + .pm =3D pm_ptr(&eic7700_pvt_pm_ops), + }, +}; +module_platform_driver(pvt_driver); + +MODULE_AUTHOR("Yulin Lu "); +MODULE_AUTHOR("Huan He "); +MODULE_DESCRIPTION("Eswin eic7700 PVT driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/eic7700-pvt.h b/drivers/hwmon/eic7700-pvt.h new file mode 100644 index 000000000000..fb10f9e4e93a --- /dev/null +++ b/drivers/hwmon/eic7700-pvt.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ESWIN EIC7700 Voltage, Temperature sensor driver + * + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd. + */ +#ifndef __HWMON_EIC7700_PVT_H__ +#define __HWMON_EIC7700_PVT_H__ + +#include +#include +#include +#include + +/* ESWIN EIC7700 PVT registers and their bitfields */ +#define PVT_TRIM 0x04 +#define PVT_MODE 0x08 +#define PVT_MODE_MASK GENMASK(2, 0) +#define PVT_CTRL_MODE_TEMP 0x0 +#define PVT_CTRL_MODE_VOLT 0x4 +#define PVT_ENA 0x0c +#define PVT_ENA_EN BIT(0) +#define PVT_INT 0x10 +#define PVT_INT_STAT BIT(0) +#define PVT_INT_CLR BIT(1) +#define PVT_DATA 0x14 +#define PVT_DATA_OUT GENMASK(9, 0) + +/* + * PVT sensors-related limits and default values + * @PVT_TEMP_CHS: Number of temperature hwmon channels. + * @PVT_VOLT_CHS: Number of voltage hwmon channels. + * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value + * when one is determined for ESWIN EIC7700 SoC). + * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds. + */ +#define PVT_TEMP_CHS 1 +#define PVT_VOLT_CHS 1 +#define PVT_TRIM_DEF 0 +#define PVT_TOUT_MIN (NSEC_PER_SEC / 3000) + +/* + * enum pvt_sensor_type - ESWIN EIC7700 PVT sensor types (correspond to ea= ch PVT + * sampling mode) + * @PVT_TEMP: PVT Temperature sensor. + * @PVT_VOLT: PVT Voltage sensor. + */ +enum pvt_sensor_type { + PVT_TEMP =3D 0, + PVT_VOLT +}; + +#define PVT_CLK_NUM 2 + +/* + * struct pvt_sensor_info - ESWIN EIC7700 PVT sensor informational structu= re + * @channel: Sensor channel ID. + * @label: hwmon sensor label. + * @mode: PVT mode corresponding to the channel. + * @type: Sensor type. + */ +struct pvt_sensor_info { + int channel; + const char *label; + u32 mode; + enum hwmon_sensor_types type; +}; + +#define PVT_SENSOR_INFO(_ch, _label, _type, _mode) \ + { \ + .channel =3D _ch, \ + .label =3D _label, \ + .mode =3D PVT_CTRL_MODE_ ##_mode, \ + .type =3D _type, \ + } + +/* + * struct pvt_hwmon - Eswin EIC7700 PVT private data + * @dev: device structure of the PVT platform device. + * @hwmon: hwmon device structure. + * @regs: pointer to the Eswin EIC7700 PVT registers region. + * @irq: PVT events IRQ number. + * @clks: PVT clock descriptors. + * @data_cache: data cache in raw format. + * @conversion: data conversion completion. + * @timeout: conversion timeout. + */ +struct pvt_hwmon { + struct device *dev; + struct device *hwmon; + void __iomem *regs; + int irq; + struct clk_bulk_data clks[PVT_CLK_NUM]; + u32 data_cache; + struct completion conversion; + ktime_t timeout; +}; + +#endif /* __HWMON_EIC7700_PVT_H__ */ --=20 2.34.1