From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (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 D24003B3BF2 for ; Mon, 27 Jul 2026 15:43:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785167010; cv=none; b=FnLYsZSB5LW9D+TqQp/NgTqNYXq1TCT+41jFHeXRCreijA5TystWsKazVf4hYtLHNOfxV9V8JHJ88qBjX9IBXzwTLY1fVbaA2JVWqZVdJCWh5lOJr59XwfApOwPLwXoErREWoO7H2elmNwKOWvWUAWovC7IK2vJGR1eXxeHVSKk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785167010; c=relaxed/simple; bh=BrJNowDL1ubUecYSYIeyOW/Legj2+eO/bvW/7AS1fvA=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=br8G5jIZ37iY/m+WT1EU/he0V2i9RGX26rJQLn4aRJQErucAlDK+w5fYEWRzz8EXrA22utbhU3Ryj051L6ypp3M09Lzem25oO00/YTY7T67jaT0Fdz0rZN+mSv+Lrs2HSCJkrYPABDGyJhg7F/8AG8pdB6UVAtOIGZsIcfHMj9o= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=bNlUg+rJ; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=ZRtN2+sU; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="bNlUg+rJ"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="ZRtN2+sU" Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66RDlv1N3787534 for ; Mon, 27 Jul 2026 15:43:27 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= udZKWsoy/KjGJ3Mh1stcIlBeBKiPkG5JHOG+aGyrSMk=; b=bNlUg+rJXEmCb0Q6 Up9J/AMqPnyjyDhyI//og4/n3ygMUHrWIs/p10m92/FiJRoe3uP8F6cZB+zFLH2j 2a3J+wCaNoeryoDKyf6QOcipcDB5ohpIwhUCGrtm9mesmo6sobEP0c9xe/TH4637 wldgdDfEX4gHbp/GTRVK7bMwTtCEgnyCZghQB9RRXJHspf2U4+DYTBNftegbdQ61 f9vapAZrVHuPYRB2bhs6sPFDKyJkKua5pHHUaVKVe5n3/7NAJ+V2JCqQd2oz90v5 ua8Yom3zU14oyqTYrkesqbZiF9LLBlE7vMGX9PmsHmUnaOhNXHfyMgNqWDe/KDIA fxSMcg== Received: from mail-yw1-f199.google.com (mail-yw1-f199.google.com [209.85.128.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fp3v2hraq-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Mon, 27 Jul 2026 15:43:27 +0000 (GMT) Received: by mail-yw1-f199.google.com with SMTP id 00721157ae682-81f0e988059so51504817b3.1 for ; Mon, 27 Jul 2026 08:43:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1785167007; x=1785771807; darn=vger.kernel.org; h=content-transfer-encoding:content-type:in-reply-to:from :content-language:references:cc:to:subject:user-agent:mime-version :date:message-id:from:to:cc:subject:date:message-id:reply-to :content-type; bh=udZKWsoy/KjGJ3Mh1stcIlBeBKiPkG5JHOG+aGyrSMk=; b=ZRtN2+sUb7takrNTG0L/wIp3ptQSJBkNkgowyaAVMZSVljBkxuEV8hJQx+FE1HDykr a/LkRZmVl/k1uvjMx6Al4pC2kPsGvdDjKQHcRfNN17EYD9sM723GUtb2l3deTOz/Ah/a xexXPaTfhQzFGfCB1YN4qNMbssu458NV1FdOlSMMMLGdKkCNKzngLKN6/jNd56mpc92+ IaPD0WMaXnyZ6wr+vugRNdfpL4e7vWn+ZQ4DpzXxnZJBYU6IcK4iIeLK7vQ0UMQzX5/V 4hCzcliB7oE16ymE1oMkcUS96NZxIOpH6h/wxvfpnLn6JTF16wTZ0+nfbzhKj+9yWGo0 YdWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785167007; x=1785771807; h=content-transfer-encoding:content-type:in-reply-to:from :content-language:references:cc:to:subject:user-agent:mime-version :date:message-id:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=udZKWsoy/KjGJ3Mh1stcIlBeBKiPkG5JHOG+aGyrSMk=; b=c0BCmY3aDMBie0e2GrREAXsKu6UlWEY7FesYElLuDDEdw+3OOtqkfd+7sgH91I+FWs AkhbzAVjXb1wCiXjPNDPUMRM9A0CoV79uuzj7Jdgkar+zzifziwh1jHmR//MRsoA/bNb HwSAV5u1mb29Ac/YIcRm/cG3bdKhGKzwh2Ry+HPU71RKgSTyC4XsiOI6elj5HQMTOpx9 V4NGxRyIU/oKbsuyZPuAd0sLX037hcnu7msG9dCqrNfg1wVUFKZvmZMbOPfDNTwe8tuV u83MpKlek2ADMfEStZjt5dMl2OcVCl6S3H4zQBT6lrhK4rUo0BJmWVwPrWWbiZLw+/T4 ASUw== X-Forwarded-Encrypted: i=1; AHgh+RpLPkokVCPzdM68I+0fEUIinLnICNmmyWTvWjzXbR311ts9CBDjJDt5XEacp+od3yRSnVCHq9ySaistQ3I=@vger.kernel.org X-Gm-Message-State: AOJu0YzCkMcNpMv5FZaRb+dzD1jzjUPzQ0EeEfZf4nOKdJ6MA7g37fS1 qGdm0dBu6oISYPDA/0cXr7WCBuK00tEFnheO9WU5M/+XTji2FN4TSW596l9yKieu2CzCphkbRRY e5fUAqYFK5mAjjxWhqpda0a25gnTwmI4iFQx6C0HS6IOiEOG+AWQFs2towrftPT9Un+k= X-Gm-Gg: AR+sD135kQ+hvvU/WUnoD87cvWhPB9XqaNOvefOO+rj9JLQz0+VmyEgdk/CG2HxFecw FRWWB84w84K8gsoEdcHqf1HvOQMWRk1MAGqA1ipf8wFyC/MLFk5Y30cW5orvNgQI78YTOO1/32j vSXPg6cM9232CPmW/8MSCUEFRRUd4X7DsuWQSBNr4JFc4VhqqD/pi+0GryXhujj4iaix6VMLP6G Qpkm7Vm4aBkaXPvnJVILSKgmMri5uEbwrpXXlZGkKpYeYhgQM/vU3rvBN2lEIneZ+QX3EnsDDJY U72sfwEJQIfv0tcoxYxiJ64cKp2JTGXVLXwp7h3Lt1jqpWWffhZ5rCeMgIRaNIrGOnMSCy2tvdw gt49ZbxuGeQ5xyPrVuCet33T/r/ZSrUFTL3La8TnSUeekucqo0NP+aWx7mGDZAV5l X-Received: by 2002:a05:690c:6287:b0:81d:26f9:bc85 with SMTP id 00721157ae682-81f69dea8b2mr31502767b3.45.1785167006790; Mon, 27 Jul 2026 08:43:26 -0700 (PDT) X-Received: by 2002:a05:690c:6287:b0:81d:26f9:bc85 with SMTP id 00721157ae682-81f69dea8b2mr31502477b3.45.1785167006227; Mon, 27 Jul 2026 08:43:26 -0700 (PDT) Received: from ?IPV6:2a01:cde0:108:24ed:6c20:c6e6:2fc0:aab7? ([2a01:cde0:108:24ed:6c20:c6e6:2fc0:aab7]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81f6592ecd4sm33445157b3.47.2026.07.27.08.43.24 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 27 Jul 2026 08:43:25 -0700 (PDT) Message-ID: Date: Mon, 27 Jul 2026 17:43:22 +0200 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v4 3/3] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring To: Jishnu Prakash , Jonathan Cameron , David Lechner , =?UTF-8?Q?Nuno_S=C3=A1?= , Andy Shevchenko , Amit Kucheria , Thara Gopinath , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-arm-msm@vger.kernel.org, linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Kamal Wadhwa , David Collins , Anjelique Melendez , Neil Armstrong , Stephan Gerhold References: <20260722-gen3_adc_tm-v4-0-011981f756c8@oss.qualcomm.com> <20260722-gen3_adc_tm-v4-3-011981f756c8@oss.qualcomm.com> Content-Language: en-US From: Daniel Lezcano In-Reply-To: <20260722-gen3_adc_tm-v4-3-011981f756c8@oss.qualcomm.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Proofpoint-Spam-Info: AW1haW4tMjYwNzI3MDE0NyBTYWx0ZWRfX6dKSQm6v4Ify BAlAibxm7uTRzksLqLwHPFJbJfKnUDVKL7XVA2G7PK6ew15B3T1sO+K5/VH/xmeKwqRrAwcRYk4 9UGgQgewp8mZ0tFNXo+zLQuFRklqf8I= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzI3MDE0NyBTYWx0ZWRfXx4AJrMWotY/r V3N+mI3roDEmiJUbsW9n8vb6utiIPLen9sPOzZZO5Np8sbHc3ygrlhJ9ZlOR/1gb0xhKzwNNKID arA8yd8Wsk7xVcM2U3hljj2WfloKoAVud5p0wi0tx3DZ9uUNwur+6mjqc3eKQ+kNNgliyxUzaQ7 YC8wCxho6nBcO39Xi4jTtr3H/h0DtFkRFChRS1nAMMPHEgdJG6DPbW1+mWU0wGhx2ZClVnZqrUr xN5mI0PnTi5nYD0MEIUzlYMXw/MrW9G1/L2Xsuje27yWkO3Z3DAEAintCCTCZYEwQhjfJCj86n3 Br8rs7u9dfgKyKBFjJbuxAqbRKEtafiyBJljyUST2l0FRJpTIVFeeB8jLShNk1VH4yfRSuQ7Njh fHC0ipL9cmOLUF+RCtv4QiN+MRQ1EnhnZNxQRVWeRbcnt2dM2Aum5YSzW2Uef7HP92EsxxMaHqz w712EHZABBijczVZq0Q== X-Proofpoint-GUID: uXzJTa1Y_aw-Z4RuSpVC7i3tAtfcCFgo X-Authority-Analysis: v=2.4 cv=KJ9qylFo c=1 sm=1 tr=0 ts=6a677c9f cx=c_pps a=72HoHk1woDtn7btP4rdmlg==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yx91gb_oNiZeI1HMLzn7:22 a=EUspDBNiAAAA:8 a=k6uiJ9FFFaPGw3kp3twA:9 a=QEXdDO2ut3YA:10 a=kA6IBgd4cpdPkAWqgNAz:22 X-Proofpoint-ORIG-GUID: uXzJTa1Y_aw-Z4RuSpVC7i3tAtfcCFgo X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-27_04,2026-07-24_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 impostorscore=0 malwarescore=0 clxscore=1015 lowpriorityscore=0 phishscore=0 priorityscore=1501 suspectscore=0 bulkscore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607270147 Hi, Andy ? Are fine with these changes ? On 7/22/26 13:26, Jishnu Prakash wrote: > Add support for ADC_TM part of PMIC5 Gen3 in an auxiliary driver > under the Gen3 ADC driver. Its functionality is similar to that of > PMIC5 Gen2 ADC_TM, which implements the threshold setting and > interrupt generating functions, used to support thermal trip points. > > In Gen3 ADC, the register interface is implemented on one or more SDAM > (Shared Direct Access Memory) peripherals instead of dedicated ADC > peripherals. Each ADC SDAM has eight channels which can be configured > for either immediate reads (main ADC driver's functionality) or ADC_TM > reads. By convention, the first channel of the first ADC SDAM is reserved > for all immediate reads and remaining channels across all SDAMs are used > for ADC_TM functionality. > > On the first SDAM, the interrupt line and configuration registers are > shared between the main ADC and auxiliary ADC_TM drivers. Access to the > registers is protected through a mutex shared between the drivers. The > ADC_TM driver accesses this mutex and some other functions shared from > the main driver (like adc5_gen3_get_scaled_reading() for immediate channel > reads in the .get_temp() callback) through APIs exported into a shared > namespace. > > Signed-off-by: Jishnu Prakash > --- > drivers/thermal/qcom/Kconfig | 9 + > drivers/thermal/qcom/Makefile | 1 + > drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c | 434 ++++++++++++++++++++++++++ > 3 files changed, 444 insertions(+) > > diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig > index a6bb01082ec6..1acb11e4ac80 100644 > --- a/drivers/thermal/qcom/Kconfig > +++ b/drivers/thermal/qcom/Kconfig > @@ -21,6 +21,15 @@ config QCOM_SPMI_ADC_TM5 > Thermal client sets threshold temperature for both warm and cool and > gets updated when a threshold is reached. > > +config QCOM_SPMI_ADC_TM5_GEN3 > + tristate "Qualcomm SPMI PMIC Thermal Monitor ADC5 Gen3" > + depends on QCOM_SPMI_ADC5_GEN3 > + help > + This enables the auxiliary thermal driver for the ADC5 Gen3 thermal > + monitoring device. It shows up as a thermal zone with multiple trip points. > + Thermal client sets threshold temperature for both warm and cool and > + gets updated when a threshold is reached. > + > config QCOM_SPMI_TEMP_ALARM > tristate "Qualcomm SPMI PMIC Temperature Alarm" > depends on OF && SPMI && IIO > diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile > index 0fa2512042e7..828d9e7bc797 100644 > --- a/drivers/thermal/qcom/Makefile > +++ b/drivers/thermal/qcom/Makefile > @@ -4,5 +4,6 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o > qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \ > tsens-8960.o > obj-$(CONFIG_QCOM_SPMI_ADC_TM5) += qcom-spmi-adc-tm5.o > +obj-$(CONFIG_QCOM_SPMI_ADC_TM5_GEN3) += qcom-spmi-adc-tm5-gen3.o > obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o > obj-$(CONFIG_QCOM_LMH) += lmh.o > diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c > new file mode 100644 > index 000000000000..9cf552d36868 > --- /dev/null > +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c > @@ -0,0 +1,434 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "../thermal_hwmon.h" > + > +#define ADC_TM5_GEN3_CONFIG_REGS 12 > + > +struct device; > +struct adc_tm5_gen3_chip; > + > +/** > + * struct adc_tm5_gen3_channel_props - ADC_TM channel structure > + * @common_props: structure with common ADC channel properties. > + * @chip: ADC TM device. > + * @tzd: pointer to thermal device corresponding to TM channel. > + * @sdam_index: SDAM on which this TM channel lies. > + * @timer: time period of recurring TM measurement. > + * @tm_chan_index: TM channel number used. > + * @high_thr_en: TM high threshold crossing detection enabled. > + * @low_thr_en: TM low threshold crossing detection enabled. > + */ > +struct adc_tm5_gen3_channel_props { > + struct adc5_channel_common_prop common_props; > + struct adc_tm5_gen3_chip *chip; > + struct thermal_zone_device *tzd; > + unsigned int sdam_index; > + unsigned int timer; > + unsigned int tm_chan_index; > + bool high_thr_en; > + bool low_thr_en; > +}; > + > +/** > + * struct adc_tm5_gen3_chip - ADC Thermal Monitoring device structure > + * @dev_data: Top-level ADC device data. > + * @chan_props: Array of ADC_TM channel structures. > + * @dev: SPMI ADC5 Gen3 device. > + * @nchannels: number of TM channels allocated > + */ > +struct adc_tm5_gen3_chip { > + struct adc5_device_data *dev_data; > + struct adc_tm5_gen3_channel_props *chan_props; > + struct device *dev; > + unsigned int nchannels; > +}; > + > +DEFINE_GUARD(adc5_gen3, struct adc_tm5_gen3_chip *, > + adc5_gen3_mutex_lock(_T->dev), adc5_gen3_mutex_unlock(_T->dev)) > + > +static int get_sdam_from_irq(struct adc_tm5_gen3_chip *adc_tm5, int irq) > +{ > + for (int i = 0; i < adc_tm5->dev_data->num_sdams; i++) { > + if (adc_tm5->dev_data->base[i].irq == irq) > + return i; > + } > + return -ENOENT; > +} > + > +static irqreturn_t adctm5_gen3_isr(int irq, void *dev_id) > +{ > + struct adc_tm5_gen3_chip *adc_tm5 = dev_id; > + int ret, sdam_num; > + u8 tm_status[2]; > + u8 status, val; > + > + sdam_num = get_sdam_from_irq(adc_tm5, irq); > + if (sdam_num < 0) > + return IRQ_NONE; > + > + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_STATUS1, > + &status, sizeof(status)); > + if (ret) > + return IRQ_NONE; > + > + if (status & ADC5_GEN3_STATUS1_CONV_FAULT) { > + val = ADC5_GEN3_CONV_ERR_CLR_REQ; > + adc5_gen3_status_clear(adc_tm5->dev_data, sdam_num, > + ADC5_GEN3_CONV_ERR_CLR, &val, 1); > + return IRQ_HANDLED; > + } > + > + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_TM_HIGH_STS, > + tm_status, sizeof(tm_status)); > + if (ret) > + return IRQ_NONE; > + > + if (tm_status[0] || tm_status[1]) > + return IRQ_WAKE_THREAD; > + > + return IRQ_NONE; > +} > + > +static irqreturn_t adctm5_gen3_isr_thread(int irq, void *dev_id) > +{ > + struct adc_tm5_gen3_chip *adc_tm5 = dev_id; > + u8 tm_status[2]; > + int sdam_index; > + > + sdam_index = get_sdam_from_irq(adc_tm5, irq); > + if (sdam_index < 0) > + return IRQ_NONE; > + > + scoped_guard(adc5_gen3, adc_tm5) { > + int ret; > + > + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_index, ADC5_GEN3_TM_HIGH_STS, > + tm_status, sizeof(tm_status)); > + if (ret) > + return IRQ_NONE; > + > + ret = adc5_gen3_status_clear(adc_tm5->dev_data, sdam_index, > + ADC5_GEN3_TM_HIGH_STS_CLR, tm_status, > + sizeof(tm_status)); > + if (ret) > + return IRQ_NONE; > + } > + > + for (int i = 0; i < adc_tm5->nchannels; i++) { > + struct adc_tm5_gen3_channel_props *chan_prop = &adc_tm5->chan_props[i]; > + int offset = chan_prop->tm_chan_index; > + bool upper_set, lower_set; > + > + if (chan_prop->sdam_index != sdam_index) > + continue; > + > + upper_set = ((tm_status[0] & BIT(offset)) && chan_prop->high_thr_en); > + lower_set = ((tm_status[1] & BIT(offset)) && chan_prop->low_thr_en); > + > + if (!(upper_set || lower_set)) > + continue; > + > + thermal_zone_device_update(chan_prop->tzd, THERMAL_TRIP_VIOLATED); > + } > + > + return IRQ_HANDLED; > +} > + > +static int adc_tm5_gen3_get_temp(struct thermal_zone_device *tz, int *temp) > +{ > + struct adc_tm5_gen3_channel_props *prop = thermal_zone_device_priv(tz); > + struct adc_tm5_gen3_chip *adc_tm5; > + > + if (!prop || !prop->chip) > + return -EINVAL; > + > + adc_tm5 = prop->chip; > + > + return adc5_gen3_get_scaled_reading(adc_tm5->dev, &prop->common_props, temp); > +} > + > +static int adc_tm5_gen3_disable_channel(struct adc_tm5_gen3_channel_props *prop) > +{ > + struct adc_tm5_gen3_chip *adc_tm5 = prop->chip; > + int ret; > + u8 val; > + > + prop->high_thr_en = false; > + prop->low_thr_en = false; > + > + ret = adc5_gen3_poll_wait_hs(adc_tm5->dev_data, prop->sdam_index); > + if (ret) > + return ret; > + > + val = BIT(prop->tm_chan_index); > + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, > + ADC5_GEN3_TM_HIGH_STS_CLR, &val, sizeof(val)); > + if (ret) > + return ret; > + > + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, > + ADC5_GEN3_TM_LOW_STS_CLR, &val, sizeof(val)); > + if (ret) > + return ret; > + > + val = MEAS_INT_DISABLE; > + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, > + ADC5_GEN3_TIMER_SEL, &val, sizeof(val)); > + if (ret) > + return ret; > + > + /* To indicate there is an actual conversion request */ > + val = ADC5_GEN3_CHAN_CONV_REQ | prop->tm_chan_index; > + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, > + ADC5_GEN3_PERPH_CH, &val, sizeof(val)); > + if (ret) > + return ret; > + > + val = ADC5_GEN3_CONV_REQ_REQ; > + return adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, > + ADC5_GEN3_CONV_REQ, &val, sizeof(val)); > +} > + > +static int adc_tm5_gen3_configure(struct adc_tm5_gen3_channel_props *prop, > + int low_temp, int high_temp) > +{ > + struct adc_tm5_gen3_chip *adc_tm5 = prop->chip; > + u8 buf[ADC_TM5_GEN3_CONFIG_REGS]; > + u8 conv_req; > + u16 adc_code; > + int ret; > + > + ret = adc5_gen3_poll_wait_hs(adc_tm5->dev_data, prop->sdam_index); > + if (ret < 0) > + return ret; > + > + ret = adc5_gen3_read(adc_tm5->dev_data, prop->sdam_index, > + ADC5_GEN3_SID, buf, sizeof(buf)); > + if (ret < 0) > + return ret; > + > + /* Write SID */ > + buf[0] = FIELD_PREP(ADC5_GEN3_SID_MASK, prop->common_props.sid); > + > + /* Select TM channel and indicate there is an actual conversion request */ > + buf[1] = ADC5_GEN3_CHAN_CONV_REQ | prop->tm_chan_index; > + > + buf[2] = prop->timer; > + > + /* Digital param selection */ > + adc5_gen3_update_dig_param(&prop->common_props, &buf[3]); > + > + /* Update fast average sample value */ > + buf[4] = FIELD_PREP(ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK, > + prop->common_props.avg_samples) | ADC5_GEN3_FAST_AVG_CTL_EN; > + > + /* Select ADC channel */ > + buf[5] = prop->common_props.channel; > + > + /* Select HW settle delay for channel */ > + buf[6] = FIELD_PREP(ADC5_GEN3_HW_SETTLE_DELAY_MASK, > + prop->common_props.hw_settle_time_us); > + > + buf[7] = 0; > + > + /* High temperature corresponds to low voltage threshold */ > + prop->low_thr_en = (high_temp != INT_MAX); > + if (prop->low_thr_en) { > + adc_code = qcom_adc_tm5_gen2_temp_res_scale(high_temp); > + put_unaligned_le16(adc_code, &buf[8]); > + buf[7] |= ADC5_GEN3_LOW_THR_INT_EN; > + } > + > + /* Low temperature corresponds to high voltage threshold */ > + prop->high_thr_en = (low_temp != -INT_MAX); > + if (prop->high_thr_en) { > + adc_code = qcom_adc_tm5_gen2_temp_res_scale(low_temp); > + put_unaligned_le16(adc_code, &buf[10]); > + buf[7] |= ADC5_GEN3_HIGH_THR_INT_EN; > + } > + > + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, ADC5_GEN3_SID, > + buf, sizeof(buf)); > + if (ret < 0) > + return ret; > + > + conv_req = ADC5_GEN3_CONV_REQ_REQ; > + return adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, > + ADC5_GEN3_CONV_REQ, &conv_req, sizeof(conv_req)); > +} > + > +static int adc_tm5_gen3_set_trip_temp(struct thermal_zone_device *tz, > + int low_temp, int high_temp) > +{ > + struct adc_tm5_gen3_channel_props *prop = thermal_zone_device_priv(tz); > + struct adc_tm5_gen3_chip *adc_tm5; > + > + if (!prop || !prop->chip) > + return -EINVAL; > + > + adc_tm5 = prop->chip; > + > + dev_dbg(adc_tm5->dev, "channel:%s, low_temp(mdegC):%d, high_temp(mdegC):%d\n", > + prop->common_props.label, low_temp, high_temp); > + > + guard(adc5_gen3)(adc_tm5); > + > + return adc_tm5_gen3_configure(prop, low_temp, high_temp); > +} > + > +static const struct thermal_zone_device_ops adc_tm_ops = { > + .get_temp = adc_tm5_gen3_get_temp, > + .set_trips = adc_tm5_gen3_set_trip_temp, > +}; > + > +static int adc_tm5_register_tzd(struct adc_tm5_gen3_chip *adc_tm5) > +{ > + struct thermal_zone_device *tzd; > + unsigned int channel; > + int ret; > + > + for (int i = 0; i < adc_tm5->nchannels; i++) { > + channel = ADC5_GEN3_V_CHAN(adc_tm5->chan_props[i].common_props); > + tzd = devm_thermal_of_zone_register(adc_tm5->dev, channel, > + &adc_tm5->chan_props[i], > + &adc_tm_ops); > + if (IS_ERR(tzd)) { > + if (PTR_ERR(tzd) == -ENODEV) { > + dev_dbg(adc_tm5->dev, > + "thermal sensor on channel %d is not used\n", > + channel); > + continue; > + } > + return PTR_ERR(tzd); > + } > + adc_tm5->chan_props[i].tzd = tzd; > + ret = devm_thermal_add_hwmon_sysfs(adc_tm5->dev, tzd); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static void adc5_gen3_disable(void *data) > +{ > + struct adc_tm5_gen3_chip *adc_tm5 = data; > + > + guard(adc5_gen3)(adc_tm5); > + > + /* Disable all available TM channels */ > + for (int i = 0; i < adc_tm5->nchannels; i++) > + adc_tm5_gen3_disable_channel(&adc_tm5->chan_props[i]); > +} > + > +static int adc_tm5_probe(struct auxiliary_device *aux_dev, > + const struct auxiliary_device_id *id) > +{ > + struct adc_tm5_gen3_chip *adc_tm5; > + struct tm5_aux_dev_wrapper *aux_dev_wrapper; > + struct device *dev = &aux_dev->dev; > + int ret; > + > + adc_tm5 = devm_kzalloc(dev, sizeof(*adc_tm5), GFP_KERNEL); > + if (!adc_tm5) > + return -ENOMEM; > + > + aux_dev_wrapper = container_of(aux_dev, struct tm5_aux_dev_wrapper, aux_dev); > + > + adc_tm5->dev = dev; > + adc_tm5->dev_data = aux_dev_wrapper->dev_data; > + adc_tm5->nchannels = aux_dev_wrapper->n_tm_channels; > + adc_tm5->chan_props = devm_kcalloc(dev, aux_dev_wrapper->n_tm_channels, > + sizeof(*adc_tm5->chan_props), GFP_KERNEL); > + if (!adc_tm5->chan_props) > + return -ENOMEM; > + > + for (int i = 0; i < adc_tm5->nchannels; i++) { > + /* > + * Since the first channel of the first SDAM is reserved for > + * immediate ADC conversions, TM channel count must start from > + * the channel just after it. The variable tm_count is used to > + * calculate SDAM and TM channel index on that SDAM correctly > + * for each TM channel. > + */ > + int tm_count = i + 1; > + > + adc_tm5->chan_props[i].common_props = aux_dev_wrapper->tm_props[i]; > + adc_tm5->chan_props[i].timer = MEAS_INT_1S; > + adc_tm5->chan_props[i].sdam_index = tm_count / 8; > + adc_tm5->chan_props[i].tm_chan_index = tm_count % 8; > + adc_tm5->chan_props[i].chip = adc_tm5; > + } > + > + /* > + * ADC_TM channels are enabled in the loop in adc_tm5_register_tzd() as > + * part of the set_trips calls during thermal zone registration. This > + * action is to disable them all in case of probe failure. > + */ > + ret = devm_add_action(dev, adc5_gen3_disable, adc_tm5); > + if (ret) > + return ret; > + > + ret = adc_tm5_register_tzd(adc_tm5); > + if (ret) > + return ret; > + > + for (int i = 0; i < adc_tm5->dev_data->num_sdams; i++) { > + u32 irq_flags = IRQF_ONESHOT; > + > + /* > + * First SDAM's interrupt is shared between main ADC driver and > + * auxiliary TM driver, so its flags must include IRQF_SHARED. > + * This is not needed for other SDAMs as they will be used only > + * for TM functionality. > + */ > + if (i == 0) > + irq_flags |= IRQF_SHARED; > + > + ret = devm_request_threaded_irq(dev, > + adc_tm5->dev_data->base[i].irq, > + adctm5_gen3_isr, > + adctm5_gen3_isr_thread, > + irq_flags, > + adc_tm5->dev_data->base[i].irq_name, > + adc_tm5); > + if (ret < 0) > + return ret; > + } > + > + return 0; > +} > + > +static const struct auxiliary_device_id adctm5_auxiliary_id_table[] = { > + { .name = "qcom_spmi_adc5_gen3.adc5_tm_gen3" }, > + { } > +}; > +MODULE_DEVICE_TABLE(auxiliary, adctm5_auxiliary_id_table); > + > +static struct auxiliary_driver adctm5gen3_auxiliary_driver = { > + .id_table = adctm5_auxiliary_id_table, > + .probe = adc_tm5_probe, > +}; > +module_auxiliary_driver(adctm5gen3_auxiliary_driver); > + > +MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver"); > +MODULE_LICENSE("GPL"); > +MODULE_IMPORT_NS("QCOM_SPMI_ADC5_GEN3"); >