Devicetree
 help / color / mirror / Atom feed
From: Alexandre Belloni <alexandre.belloni@bootlin.com>
To: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
Cc: Bjorn Andersson <andersson@kernel.org>,
	Konrad Dybcio <konradybcio@kernel.org>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	linux-kernel@vger.kernel.org, linux-rtc@vger.kernel.org,
	linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org,
	Raj Aryan <raryan@qti.qualcomm.com>
Subject: Re: [PATCH 2/2] rtc: qcom: rtc-glink: Add PMIC GLINK RTC driver and extend pmic_glink
Date: Fri, 7 Aug 2026 22:58:44 +0200	[thread overview]
Message-ID: <2026080720584407a4d82d@mail.local> (raw)
In-Reply-To: <20260807-rtc-glink-cleanup-v1-2-7801e4d69c4f@oss.qualcomm.com>

On 07/08/2026 02:26:31+0530, Kamal Wadhwa wrote:
> Add a new RTC driver that communicates with the PMIC firmware over
> the GLINK transport layer. The driver supports reading time and
> alarm functionality via GET_PROPERTY and SET_PROPERTY opcodes.
> 
> Time is read via GET_PROPERTY (opcode 0x62) using QCOM_RTC_GLINK_TIME
> property (0x00). Alarm set and enable are handled via SET_PROPERTY
> (opcode 0x63) using QCOM_RTC_GLINK_ALARM and QCOM_RTC_GLINK_ALARM_ENABLE
> properties respectively.
> 
> GET_PROPERTY and SET_PROPERTY responses share the same firmware message
> layout, with the firmware echoing back the property value. Both opcodes
> are handled by a common callback path accordingly.
> 
> Also extend pmic_glink to register the RTC as a PMIC GLINK client and
> add PMIC GLINK RTC client support for glymur.
> 
> Assisted-by: Claude:claude-sonnet-5
> Signed-off-by: Raj Aryan <raryan@qti.qualcomm.com>
> Signed-off-by: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
> ---
>  MAINTAINERS                         |   7 +
>  drivers/rtc/Kconfig                 |  10 +
>  drivers/rtc/Makefile                |   1 +
>  drivers/rtc/rtc-qcom-glink.c        | 690 ++++++++++++++++++++++++++++++++++++
>  drivers/soc/qcom/pmic_glink.c       |  22 +-
>  include/linux/soc/qcom/pmic_glink.h |   1 +
>  6 files changed, 730 insertions(+), 1 deletion(-)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5f178a5ff026..752cf667b8ad 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22505,6 +22505,13 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml
>  F:	drivers/power/supply/qcom_smbx.c
>  
> +QUALCOMM PMIC GLINK RTC DRIVER
> +M:	Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
> +L:	linux-rtc@vger.kernel.org
> +L:	linux-arm-msm@vger.kernel.org
> +S:	Maintained
> +F:	drivers/rtc/rtc-qcom-glink.c
> +
>  QUALCOMM PPE DRIVER
>  M:	Luo Jie <jie.luo@oss.qualcomm.com>
>  L:	netdev@vger.kernel.org
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 01def8231873..3ac39704b2e1 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1836,6 +1836,16 @@ config RTC_DRV_PM8XXX
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called rtc-pm8xxx.
>  
> +config RTC_DRV_QCOM_GLINK
> +	tristate "Qualcomm PMIC GLINK RTC"
> +	depends on QCOM_PMIC_GLINK
> +	help
> +	  Say Y here to enable support for the RTC exposed by the PMIC
> +	  over the GLINK transport on Qualcomm platforms.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called rtc-qcom-glink.
> +
>  config RTC_DRV_TEGRA
>  	tristate "NVIDIA Tegra Internal RTC driver"
>  	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 0347645b021f..8215a1fc29fb 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -73,6 +73,7 @@ obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o
>  obj-$(CONFIG_RTC_DRV_FSL_FTM_ALARM)	+= rtc-fsl-ftm-alarm.o
>  obj-$(CONFIG_RTC_DRV_FTRTC010)	+= rtc-ftrtc010.o
>  obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
> +obj-$(CONFIG_RTC_DRV_QCOM_GLINK) += rtc-qcom-glink.o
>  obj-$(CONFIG_RTC_DRV_GOLDFISH)	+= rtc-goldfish.o
>  obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
>  obj-$(CONFIG_RTC_DRV_HYM8563)	+= rtc-hym8563.o
> diff --git a/drivers/rtc/rtc-qcom-glink.c b/drivers/rtc/rtc-qcom-glink.c
> new file mode 100644
> index 000000000000..611f06a3cc5c
> --- /dev/null
> +++ b/drivers/rtc/rtc-qcom-glink.c
> @@ -0,0 +1,690 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +#include <linux/auxiliary_bus.h>
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/property.h>
> +#include <linux/rtc.h>
> +#include <linux/slab.h>
> +#include <linux/soc/qcom/pdr.h>
> +#include <linux/soc/qcom/pmic_glink.h>
> +#include <linux/timekeeping.h>
> +#include <linux/workqueue.h>
> +
> +#define RTC_GLINK_SET_PROPERTY		0x63
> +#define RTC_GLINK_GET_PROPERTY		0x65
> +#define RTC_GLINK_GET_RTC_TICKS		0x67
> +#define RTC_GLINK_GET_REAL_TIME		0x68
> +#define RTC_GLINK_SET_REAL_TIME		0x69
> +#define RTC_GLINK_ALARM_EXPIRED		0x6A
> +
> +enum qcom_rtc_glink_properties {
> +	QCOM_RTC_GLINK_TIME = 0,
> +	QCOM_RTC_GLINK_ALARM_TIME,
> +	QCOM_RTC_GLINK_ALARM_ENABLE,
> +};
> +
> +struct qcom_rtc_glink_msg {
> +	struct pmic_glink_hdr	hdr;
> +	__le32			property;
> +	__le32			value;
> +};
> +
> +struct qcom_rtc_glink_generic_req {
> +	struct pmic_glink_hdr	hdr;
> +};
> +
> +struct qcom_rtc_glink_status_resp {
> +	struct pmic_glink_hdr	hdr;
> +	__le32			return_status;
> +};
> +
> +struct qcom_rtc_glink_ticks_resp {
> +	struct pmic_glink_hdr	hdr;
> +	__le32			return_status;
> +	__le32			rtc_ticks;
> +};
> +
> +struct qcom_rtc_glink_real_time_resp {
> +	struct pmic_glink_hdr	hdr;
> +	__le32			return_status;
> +	__le32			real_time_data[4];
> +};
> +
> +struct qcom_rtc_glink_set_real_time_req {
> +	struct pmic_glink_hdr	hdr;
> +	__le32			real_time_data[4];
> +};
> +
> +struct qcom_rtc_glink {
> +	struct device		*dev;
> +	struct pmic_glink_client *client;
> +	struct rtc_device	*rtc;
> +
> +	/* Serializes requests: only one may be in flight at a time */
> +	struct mutex		lock;

This mutex is useless as all the RTC operations are already serialized.
Simply rtc_lock/rtc_unlock in qcom_rtc_glink_alarm_work.

> +	struct completion	ack;
> +	struct work_struct	alarm_work;
> +
> +	/* Protects service_up and request_pending across callback contexts */
> +	spinlock_t		state_lock;
> +	bool			service_up;
> +	bool			request_pending;
> +
> +	int			error;
> +	u32			resp_value;
> +	u32			offset;
> +	struct rtc_time		resp_tm;
> +	bool			resp_valid;
> +	bool			allow_set_time;
> +};
> +
> +static int qcom_rtc_glink_request(struct qcom_rtc_glink *rtc_glink,
> +				  void *data, size_t len)
> +{
> +	unsigned long flags;
> +	unsigned long left;
> +	int ret;
> +
> +	/*
> +	 * Reinit the completion before request_pending becomes visible to
> +	 * qcom_rtc_glink_pdr_tify(), so a concurrent SSR down-transition
> +	 * can't complete() a stale completion that gets wiped out by
> +	 * reinit_completion() right after.
> +	 */
> +	reinit_completion(&rtc_glink->ack);
> +	rtc_glink->error = 0;
> +	rtc_glink->resp_valid = false;
> +
> +	spin_lock_irqsave(&rtc_glink->state_lock, flags);
> +	if (!rtc_glink->service_up) {
> +		spin_unlock_irqrestore(&rtc_glink->state_lock, flags);
> +		return -ECONNRESET;
> +	}
> +	rtc_glink->request_pending = true;
> +	spin_unlock_irqrestore(&rtc_glink->state_lock, flags);
> +
> +	ret = pmic_glink_send(rtc_glink->client, data, len);
> +	if (ret < 0) {
> +		spin_lock_irqsave(&rtc_glink->state_lock, flags);
> +		rtc_glink->request_pending = false;
> +		spin_unlock_irqrestore(&rtc_glink->state_lock, flags);
> +		return ret;
> +	}
> +
> +	left = wait_for_completion_timeout(&rtc_glink->ack, HZ);
> +	spin_lock_irqsave(&rtc_glink->state_lock, flags);
> +	rtc_glink->request_pending = false;
> +	spin_unlock_irqrestore(&rtc_glink->state_lock, flags);
> +	if (!left)
> +		return -ETIMEDOUT;
> +
> +	return rtc_glink->error;
> +}
> +
> +static int qcom_rtc_glink_set_property(struct qcom_rtc_glink *rtc_glink,
> +				       u32 property, u32 value)
> +{
> +	struct qcom_rtc_glink_msg msg = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_RTC),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(RTC_GLINK_SET_PROPERTY),
> +		.property = cpu_to_le32(property),
> +		.value = cpu_to_le32(value),
> +	};
> +	int ret;
> +
> +	dev_dbg(rtc_glink->dev,
> +		"TX opcode=0x%x property=%u value=%u\n",
> +		RTC_GLINK_SET_PROPERTY, property, value);
> +	mutex_lock(&rtc_glink->lock);
> +	ret = qcom_rtc_glink_request(rtc_glink, &msg, sizeof(msg));
> +	mutex_unlock(&rtc_glink->lock);
> +	return ret;
> +}
> +
> +static int qcom_rtc_glink_get_property(struct qcom_rtc_glink *rtc_glink,
> +				       u32 property, u32 *value)
> +{
> +	struct qcom_rtc_glink_msg msg = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_RTC),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(RTC_GLINK_GET_PROPERTY),
> +		.property = cpu_to_le32(property),
> +	};
> +	int ret;
> +
> +	dev_dbg(rtc_glink->dev,
> +		"TX opcode=0x%x property=%u\n",
> +		RTC_GLINK_GET_PROPERTY, property);
> +	mutex_lock(&rtc_glink->lock);
> +	ret = qcom_rtc_glink_request(rtc_glink, &msg, sizeof(msg));
> +	if (!ret) {
> +		if (!rtc_glink->resp_valid)
> +			ret = -EIO;
> +		else
> +			*value = rtc_glink->resp_value;
> +	}
> +	mutex_unlock(&rtc_glink->lock);
> +	return ret;
> +}
> +
> +static int qcom_rtc_glink_get_ticks(struct qcom_rtc_glink *rtc_glink,
> +				    u32 *ticks)
> +{
> +	struct qcom_rtc_glink_generic_req msg = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_RTC),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(RTC_GLINK_GET_RTC_TICKS),
> +	};
> +	int ret;
> +
> +	dev_dbg(rtc_glink->dev, "TX opcode=0x%x\n", RTC_GLINK_GET_RTC_TICKS);
> +	mutex_lock(&rtc_glink->lock);
> +	ret = qcom_rtc_glink_request(rtc_glink, &msg, sizeof(msg));
> +	if (!ret) {
> +		if (!rtc_glink->resp_valid)
> +			ret = -EIO;
> +		else
> +			*ticks = rtc_glink->resp_value;
> +	}
> +	mutex_unlock(&rtc_glink->lock);
> +	return ret;
> +}
> +
> +static int qcom_rtc_glink_get_real_time(struct qcom_rtc_glink *rtc_glink,
> +					struct rtc_time *tm)
> +{
> +	struct qcom_rtc_glink_generic_req msg = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_RTC),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(RTC_GLINK_GET_REAL_TIME),
> +	};
> +	int ret;
> +
> +	dev_dbg(rtc_glink->dev, "TX opcode=0x%x\n", RTC_GLINK_GET_REAL_TIME);
> +	mutex_lock(&rtc_glink->lock);
> +	ret = qcom_rtc_glink_request(rtc_glink, &msg, sizeof(msg));
> +	if (!ret) {
> +		if (!rtc_glink->resp_valid)
> +			ret = -EIO;
> +		else
> +			*tm = rtc_glink->resp_tm;
> +	}
> +	mutex_unlock(&rtc_glink->lock);
> +	return ret;
> +}
> +
> +static int qcom_rtc_glink_set_real_time(struct qcom_rtc_glink *rtc_glink,
> +					struct rtc_time *tm)
> +{
> +	struct qcom_rtc_glink_set_real_time_req msg = {};
> +	u32 w0, w1;
> +	int ret;
> +
> +	w0 = ((tm->tm_year + 1900) & 0xffff) |
> +	     (((tm->tm_mon + 1) & 0xff) << 16) |
> +	     ((tm->tm_mday & 0xff) << 24);
> +	w1 = (tm->tm_hour & 0xff) |
> +	     ((tm->tm_min & 0xff) << 8) |
> +	     ((tm->tm_sec & 0xff) << 16) |
> +	     (1U << 24);
> +	msg.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_RTC);
> +	msg.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP);
> +	msg.hdr.opcode = cpu_to_le32(RTC_GLINK_SET_REAL_TIME);
> +	msg.real_time_data[0] = cpu_to_le32(w0);
> +	msg.real_time_data[1] = cpu_to_le32(w1);
> +	msg.real_time_data[2] = 0;
> +	msg.real_time_data[3] = 0;
> +	dev_dbg(rtc_glink->dev,
> +		"TX opcode=0x%x data=%08x %08x\n",
> +		RTC_GLINK_SET_REAL_TIME, w0, w1);
> +	mutex_lock(&rtc_glink->lock);
> +	ret = qcom_rtc_glink_request(rtc_glink, &msg, sizeof(msg));
> +	mutex_unlock(&rtc_glink->lock);
> +	return ret;
> +}
> +
> +static int qcom_rtc_glink_get_time(struct qcom_rtc_glink *rtc_glink,
> +				   struct rtc_time *time)
> +{
> +	u32 ticks;
> +	int ret;
> +
> +	ret = qcom_rtc_glink_get_real_time(rtc_glink, time);
> +	if (!ret)
> +		return 0;
> +	dev_warn(rtc_glink->dev, "0x68 failed (%d), falling back\n", ret);

This message is useless, either fail or fallback silently

> +
> +	ret = qcom_rtc_glink_get_ticks(rtc_glink, &ticks);
> +	if (!ret) {
> +		rtc_time64_to_tm((time64_t)ticks + rtc_glink->offset, time);
> +		return 0;
> +	}
> +
> +	ret = qcom_rtc_glink_get_property(rtc_glink, QCOM_RTC_GLINK_TIME,
> +					  &ticks);
> +	if (!ret && ticks != U32_MAX) {
> +		rtc_time64_to_tm((time64_t)ticks + rtc_glink->offset, time);
> +		return 0;
> +	}
> +
> +	dev_err(rtc_glink->dev, "all time sources failed\n");

Ditto

> +	return -EIO;
> +}
> +
> +static int qcom_rtc_glink_set_time(struct qcom_rtc_glink *rtc_glink,
> +				   struct rtc_time *time)
> +{
> +	time64_t t = rtc_tm_to_time64(time);
> +	u32 ticks;
> +	int ret;
> +
> +	if (!rtc_glink->allow_set_time)
> +		return -EOPNOTSUPP;
> +
> +	ret = qcom_rtc_glink_set_real_time(rtc_glink, time);
> +	if (ret)
> +		dev_warn(rtc_glink->dev, "0x69 failed (%d)\n", ret);

Ditto

> +
> +	ret = qcom_rtc_glink_set_property(rtc_glink, QCOM_RTC_GLINK_TIME, (u32)t);
> +	if (ret)
> +		return ret;
> +
> +	ret = qcom_rtc_glink_get_ticks(rtc_glink, &ticks);
> +	if (!ret && ticks != U32_MAX) {
> +		rtc_glink->offset = (u32)t - ticks;
> +	} else {
> +		dev_warn(rtc_glink->dev, "no tick source for offset\n");

This is also probably useless. No one is going to read those messages or
do anything about them.

> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int qcom_rtc_glink_set_alarm_en(struct qcom_rtc_glink *rtc_glink,
> +				       int enabled)
> +{
> +	return qcom_rtc_glink_set_property(rtc_glink,
> +					   QCOM_RTC_GLINK_ALARM_ENABLE,
> +					   enabled);
> +}
> +
> +static int qcom_rtc_glink_set_alarm(struct qcom_rtc_glink *rtc_glink,
> +				    struct rtc_wkalrm *alarm)
> +{
> +	time64_t alarm_t = rtc_tm_to_time64(&alarm->time);
> +	struct rtc_time now_tm;
> +	time64_t now_real, secs_until_alarm;
> +	u32 fw_current, fw_alarm;
> +	int ret;
> +
> +	ret = qcom_rtc_glink_get_time(rtc_glink, &now_tm);
> +	if (ret)
> +		return ret;
> +	now_real = rtc_tm_to_time64(&now_tm);
> +	secs_until_alarm = alarm_t - now_real;
> +	dev_dbg(rtc_glink->dev,
> +		"set_alarm: now_real=%lld alarm_t=%lld delta=%lld\n",
> +		now_real, alarm_t, secs_until_alarm);
> +	if (secs_until_alarm < 0)
> +		return -EINVAL;
> +	if (secs_until_alarm > U32_MAX)
> +		return -ERANGE;
> +
> +	ret = qcom_rtc_glink_get_property(rtc_glink, QCOM_RTC_GLINK_TIME,
> +					  &fw_current);
> +	if (ret)
> +		return ret;
> +	if (fw_current == U32_MAX)
> +		fw_current = 0;
> +	fw_alarm = fw_current + (u32)secs_until_alarm;
> +	dev_dbg(rtc_glink->dev,
> +		"set_alarm: fw_current=%u fw_alarm=%u\n",
> +		fw_current, fw_alarm);
> +
> +	ret = qcom_rtc_glink_set_alarm_en(rtc_glink, 0);
> +	if (ret)
> +		return ret;
> +
> +	ret = qcom_rtc_glink_set_property(rtc_glink,
> +					  QCOM_RTC_GLINK_ALARM_TIME, fw_alarm);
> +	if (ret)
> +		return ret;
> +
> +	return qcom_rtc_glink_set_alarm_en(rtc_glink, alarm->enabled);
> +}
> +
> +static int qcom_rtc_glink_get_alarm(struct qcom_rtc_glink *rtc_glink,
> +				    struct rtc_wkalrm *alarm)
> +{
> +	u32 fw_alarm_time, fw_current, alarm_en;
> +	struct rtc_time now_tm;
> +	time64_t now_real, secs_until_alarm, alarm_real;
> +	int ret;
> +
> +	ret = qcom_rtc_glink_get_property(rtc_glink,
> +					  QCOM_RTC_GLINK_ALARM_TIME,
> +					  &fw_alarm_time);
> +	if (ret)
> +		return ret;
> +
> +	ret = qcom_rtc_glink_get_property(rtc_glink, QCOM_RTC_GLINK_TIME,
> +					  &fw_current);
> +	if (ret)
> +		return ret;
> +
> +	ret = qcom_rtc_glink_get_property(rtc_glink,
> +					  QCOM_RTC_GLINK_ALARM_ENABLE,
> +					  &alarm_en);
> +	if (ret)
> +		return ret;
> +	if (fw_current == U32_MAX)
> +		fw_current = 0;
> +
> +	ret = qcom_rtc_glink_get_time(rtc_glink, &now_tm);
> +	if (ret)
> +		return ret;
> +	now_real = rtc_tm_to_time64(&now_tm);
> +	secs_until_alarm = (time64_t)fw_alarm_time - (time64_t)fw_current;
> +	alarm_real = now_real + secs_until_alarm;
> +	dev_dbg(rtc_glink->dev,
> +		"get_alarm: now_real=%lld fw_current=%u fw_alarm=%u delta=%lld\n",
> +		now_real, fw_current, fw_alarm_time, secs_until_alarm);
> +	rtc_time64_to_tm(alarm_real, &alarm->time);
> +	alarm->enabled = !!alarm_en;
> +	return 0;
> +}
> +
> +static void qcom_rtc_glink_alarm_work(struct work_struct *work)
> +{
> +	struct qcom_rtc_glink *rtc_glink =
> +		container_of(work, struct qcom_rtc_glink, alarm_work);
> +	int ret;
> +
> +	ret = qcom_rtc_glink_set_alarm_en(rtc_glink, 0);
> +	if (ret)
> +		dev_err(rtc_glink->dev,
> +			"failed to disable alarm after expiry (%d)\n", ret);

So what is the user supposed to do with this message? Also, do you
actually have to disable the alarm? I guess it is not going to fire again.

> +	rtc_update_irq(rtc_glink->rtc, 1, RTC_IRQF | RTC_AF);
> +}
> +
> +static void qcom_rtc_glink_callback(const void *data, size_t len, void *priv)
> +{
> +	struct qcom_rtc_glink *rtc_glink = priv;
> +	const struct pmic_glink_hdr *hdr = data;
> +	unsigned long flags;
> +	bool pending;
> +
> +	if (len < sizeof(*hdr))
> +		return;
> +
> +	dev_dbg(rtc_glink->dev,
> +		"RX opcode=0x%x type=0x%x owner=0x%x len=%zu\n",
> +		le32_to_cpu(hdr->opcode), le32_to_cpu(hdr->type),
> +		le32_to_cpu(hdr->owner), len);
> +
> +	if (le32_to_cpu(hdr->opcode) == RTC_GLINK_ALARM_EXPIRED) {
> +		dev_info(rtc_glink->dev, "alarm expired\n");

This is just noise

> +		schedule_work(&rtc_glink->alarm_work);
> +		return;
> +	}
> +
> +	/*
> +	 * Hold state_lock across the pending check and the response fields
> +	 * below: qcom_rtc_glink_pdr_notify() writes the same error/resp_*
> +	 * fields under this lock when it force-completes a request on an
> +	 * SSR down-transition, and the two must not race. Dropping the
> +	 * response here if !pending also covers requests that already
> +	 * timed out or were aborted; qcom_rtc_glink_request() will call
> +	 * reinit_completion() again before this response could be mistaken
> +	 * for a later request's.
> +	 */
> +	spin_lock_irqsave(&rtc_glink->state_lock, flags);
> +	pending = rtc_glink->request_pending;
> +
> +	if (!pending) {
> +		spin_unlock_irqrestore(&rtc_glink->state_lock, flags);
> +		dev_dbg(rtc_glink->dev,
> +			"dropping stale response opcode=0x%x\n",
> +			le32_to_cpu(hdr->opcode));
> +		return;
> +	}
> +
> +	switch (le32_to_cpu(hdr->opcode)) {
> +	case RTC_GLINK_GET_PROPERTY:
> +		fallthrough;
> +	case RTC_GLINK_SET_PROPERTY: {
> +		const struct qcom_rtc_glink_msg *msg = data;
> +
> +		if (len < sizeof(struct pmic_glink_hdr) + sizeof(__le32)) {
> +			rtc_glink->error = -EINVAL;
> +			break;
> +		}
> +		rtc_glink->resp_value = le32_to_cpu(msg->property);
> +		rtc_glink->resp_valid = true;
> +		rtc_glink->error = 0;
> +		dev_dbg(rtc_glink->dev, "ACK opcode=0x%x resp_value=%u\n",
> +			le32_to_cpu(hdr->opcode), rtc_glink->resp_value);
> +		break;
> +	}
> +
> +	case RTC_GLINK_GET_RTC_TICKS: {
> +		const struct qcom_rtc_glink_ticks_resp *resp = data;
> +
> +		if (len < sizeof(*resp)) {
> +			rtc_glink->error = -EINVAL;
> +			break;
> +		}
> +		if (le32_to_cpu(resp->return_status)) {
> +			rtc_glink->error = -EIO;
> +			break;
> +		}
> +		rtc_glink->resp_value = le32_to_cpu(resp->rtc_ticks);
> +		rtc_glink->resp_valid = true;
> +		rtc_glink->error = 0;
> +		dev_dbg(rtc_glink->dev, "ACK opcode=0x%x ticks=%u\n",
> +			RTC_GLINK_GET_RTC_TICKS, rtc_glink->resp_value);
> +		break;
> +	}
> +
> +	case RTC_GLINK_GET_REAL_TIME: {
> +		const struct qcom_rtc_glink_real_time_resp *resp = data;
> +		u32 w0, w1;
> +
> +		if (len < sizeof(*resp)) {
> +			rtc_glink->error = -EINVAL;
> +			break;
> +		}
> +		if (le32_to_cpu(resp->return_status)) {
> +			rtc_glink->error = -EIO;
> +			break;
> +		}
> +		w0 = le32_to_cpu(resp->real_time_data[0]);
> +		w1 = le32_to_cpu(resp->real_time_data[1]);
> +		rtc_glink->resp_tm.tm_year = (w0 & 0xffff) - 1900;
> +		rtc_glink->resp_tm.tm_mon = ((w0 >> 16) & 0xff) - 1;
> +		rtc_glink->resp_tm.tm_mday = (w0 >> 24) & 0xff;
> +		rtc_glink->resp_tm.tm_hour = w1 & 0xff;
> +		rtc_glink->resp_tm.tm_min = (w1 >> 8) & 0xff;
> +		rtc_glink->resp_tm.tm_sec = (w1 >> 16) & 0xff;
> +		rtc_glink->resp_valid = true;
> +		rtc_glink->error = 0;
> +		dev_dbg(rtc_glink->dev,
> +			"ACK opcode=0x%x %04d-%02d-%02d %02d:%02d:%02d\n",
> +			RTC_GLINK_GET_REAL_TIME,
> +			rtc_glink->resp_tm.tm_year + 1900,
> +			rtc_glink->resp_tm.tm_mon + 1,
> +			rtc_glink->resp_tm.tm_mday,
> +			rtc_glink->resp_tm.tm_hour,
> +			rtc_glink->resp_tm.tm_min,
> +			rtc_glink->resp_tm.tm_sec);
> +		break;
> +	}
> +
> +	case RTC_GLINK_SET_REAL_TIME: {
> +		const struct qcom_rtc_glink_status_resp *resp = data;
> +
> +		if (len < sizeof(*resp)) {
> +			rtc_glink->error = -EINVAL;
> +			break;
> +		}
> +		rtc_glink->error = le32_to_cpu(resp->return_status) ? -EIO : 0;
> +		dev_dbg(rtc_glink->dev, "ACK opcode=0x%x status=%u\n",
> +			RTC_GLINK_SET_REAL_TIME,
> +			le32_to_cpu(resp->return_status));
> +		break;
> +	}
> +
> +	default:
> +		spin_unlock_irqrestore(&rtc_glink->state_lock, flags);
> +		dev_warn(rtc_glink->dev,
> +			 "unhandled RX opcode=0x%x len=%zu\n",
> +			 le32_to_cpu(hdr->opcode), len);

Useless message

> +		return;
> +	}
> +
> +	spin_unlock_irqrestore(&rtc_glink->state_lock, flags);
> +	complete(&rtc_glink->ack);
> +}
> +
> +static void qcom_rtc_glink_pdr_notify(void *priv, int state)
> +{
> +	struct qcom_rtc_glink *rtc_glink = priv;
> +	unsigned long flags;
> +	bool up = (state == SERVREG_SERVICE_STATE_UP);
> +
> +	spin_lock_irqsave(&rtc_glink->state_lock, flags);
> +	rtc_glink->service_up = up;
> +	if (!up && rtc_glink->request_pending) {
> +		rtc_glink->error = -ECONNRESET;
> +		rtc_glink->resp_valid = false;
> +		complete(&rtc_glink->ack);
> +	}
> +	spin_unlock_irqrestore(&rtc_glink->state_lock, flags);
> +}
> +
> +static int qcom_rtc_glink_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	return qcom_rtc_glink_get_time(dev_get_drvdata(dev), tm);
> +}
> +
> +static int qcom_rtc_glink_set_time_dev(struct device *dev, struct rtc_time *tm)
> +{
> +	return qcom_rtc_glink_set_time(dev_get_drvdata(dev), tm);
> +}
> +
> +static int qcom_rtc_glink_read_alarm(struct device *dev,
> +				     struct rtc_wkalrm *alrm)
> +{
> +	return qcom_rtc_glink_get_alarm(dev_get_drvdata(dev), alrm);
> +}
> +
> +static int qcom_rtc_glink_set_alarm_dev(struct device *dev,
> +					struct rtc_wkalrm *alrm)
> +{
> +	return qcom_rtc_glink_set_alarm(dev_get_drvdata(dev), alrm);
> +}
> +
> +static int qcom_rtc_glink_alarm_irq_enable(struct device *dev,
> +					   unsigned int enabled)
> +{
> +	return qcom_rtc_glink_set_alarm_en(dev_get_drvdata(dev), enabled);
> +}
> +
> +static const struct rtc_class_ops qcom_rtc_glink_rtc_ops = {
> +	.read_time = qcom_rtc_glink_read_time,
> +	.set_time = qcom_rtc_glink_set_time_dev,
> +	.read_alarm = qcom_rtc_glink_read_alarm,
> +	.set_alarm = qcom_rtc_glink_set_alarm_dev,
> +	.alarm_irq_enable = qcom_rtc_glink_alarm_irq_enable,
> +};
> +
> +static const struct of_device_id qcom_rtc_glink_of_variants[] = {
> +	{ .compatible = "qcom,glymur-pmic-glink" },
> +	{}
> +};
> +
> +static int qcom_rtc_glink_probe(struct auxiliary_device *adev,
> +				const struct auxiliary_device_id *id)
> +{
> +	struct device *dev = &adev->dev;
> +	struct qcom_rtc_glink *rtc_glink;
> +
> +	if (!of_match_device(qcom_rtc_glink_of_variants, dev->parent))
> +		return -ENXIO;
> +
> +	rtc_glink = devm_kzalloc(dev, sizeof(*rtc_glink), GFP_KERNEL);
> +	if (!rtc_glink)
> +		return -ENOMEM;
> +
> +	rtc_glink->dev            = dev;
> +	rtc_glink->allow_set_time = device_property_read_bool(dev->parent,
> +							      "allow-set-time");
> +
> +	mutex_init(&rtc_glink->lock);
> +	spin_lock_init(&rtc_glink->state_lock);
> +	init_completion(&rtc_glink->ack);
> +	INIT_WORK(&rtc_glink->alarm_work, qcom_rtc_glink_alarm_work);
> +	dev_set_drvdata(dev, rtc_glink);
> +
> +	rtc_glink->client = devm_pmic_glink_client_alloc(dev,
> +							 PMIC_GLINK_OWNER_RTC,
> +							 qcom_rtc_glink_callback,
> +							 qcom_rtc_glink_pdr_notify,
> +							 rtc_glink);
> +	if (IS_ERR(rtc_glink->client))
> +		return dev_err_probe(dev, PTR_ERR(rtc_glink->client),
> +				     "failed to allocate glink client\n");
> +
> +	pmic_glink_client_register(rtc_glink->client);
> +
> +	device_init_wakeup(dev, true);
> +
> +	rtc_glink->rtc = devm_rtc_allocate_device(dev);
> +	if (IS_ERR(rtc_glink->rtc))
> +		return dev_err_probe(dev, PTR_ERR(rtc_glink->rtc),
> +				     "failed to allocate RTC device\n");
> +

Useless message, there is no failure path without a message in
devm_rtc_allocate_device.


> +	rtc_glink->rtc->ops      = &qcom_rtc_glink_rtc_ops;
> +	rtc_glink->rtc->range_min = 0;
> +	rtc_glink->rtc->range_max = U32_MAX;
> +
> +	return devm_rtc_register_device(rtc_glink->rtc);
> +}
> +
> +static void qcom_rtc_glink_remove(struct auxiliary_device *adev)
> +{
> +	struct qcom_rtc_glink *rtc_glink = dev_get_drvdata(&adev->dev);
> +	/*
> +	 * Nothing left to arm alarm_work after this returns: the glink
> +	 * client (and its callback/pdr_notify) is torn down by devm after
> +	 * .remove() returns, and rtc_update_irq() below still targets a
> +	 * live devm-managed rtc device.
> +	 */
> +	cancel_work_sync(&rtc_glink->alarm_work);
> +}
> +
> +static const struct auxiliary_device_id qcom_rtc_glink_id_table[] = {
> +	{ .name = "pmic_glink.rtc-glink" },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(auxiliary, qcom_rtc_glink_id_table);
> +
> +static struct auxiliary_driver qcom_rtc_glink_driver = {
> +	.name = "qcom_pmic_rtc_glink",
> +	.id_table = qcom_rtc_glink_id_table,
> +	.probe = qcom_rtc_glink_probe,
> +	.remove = qcom_rtc_glink_remove,
> +};
> +
> +module_auxiliary_driver(qcom_rtc_glink_driver);
> +MODULE_DESCRIPTION("Qualcomm PMIC GLINK RTC driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c
> index 3042261578aa..7ee70a31ba15 100644
> --- a/drivers/soc/qcom/pmic_glink.c
> +++ b/drivers/soc/qcom/pmic_glink.c
> @@ -21,6 +21,7 @@ enum {
>  	PMIC_GLINK_CLIENT_BATT = 0,
>  	PMIC_GLINK_CLIENT_ALTMODE,
>  	PMIC_GLINK_CLIENT_UCSI,
> +	PMIC_GLINK_CLIENT_RTC,
>  };
>  
>  struct pmic_glink_data {
> @@ -40,6 +41,7 @@ struct pmic_glink {
>  	struct auxiliary_device altmode_aux;
>  	struct auxiliary_device ps_aux;
>  	struct auxiliary_device ucsi_aux;
> +	struct auxiliary_device rtc_aux;
>  
>  	/* serializing client_state and pdr_state updates */
>  	struct mutex state_lock;
> @@ -341,6 +343,12 @@ static int pmic_glink_probe(struct platform_device *pdev)
>  			goto out_release_altmode_aux;
>  	}
>  
> +	if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_RTC)) {
> +		ret = pmic_glink_add_aux_device(pg, &pg->rtc_aux, "rtc-glink");
> +		if (ret)
> +			goto out_release_ps_aux;
> +	}
> +
>  	if (pg->data->charger_pdr_service_name && pg->data->charger_pdr_service_path) {
>  		service = pdr_add_lookup(pg->pdr, pg->data->charger_pdr_service_name,
>  					 pg->data->charger_pdr_service_path);
> @@ -358,6 +366,9 @@ static int pmic_glink_probe(struct platform_device *pdev)
>  	return 0;
>  
>  out_release_aux_devices:
> +	if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_RTC))
> +		pmic_glink_del_aux_device(pg, &pg->rtc_aux);
> +out_release_ps_aux:
>  	if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_BATT))
>  		pmic_glink_del_aux_device(pg, &pg->ps_aux);
>  out_release_altmode_aux:
> @@ -384,11 +395,20 @@ static void pmic_glink_remove(struct platform_device *pdev)
>  		pmic_glink_del_aux_device(pg, &pg->altmode_aux);
>  	if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI))
>  		pmic_glink_del_aux_device(pg, &pg->ucsi_aux);
> +	if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_RTC))
> +		pmic_glink_del_aux_device(pg, &pg->rtc_aux);
>  
>  	guard(mutex)(&__pmic_glink_lock);
>  	__pmic_glink = NULL;
>  }
>  
> +static const struct pmic_glink_data pmic_glink_glymur_data = {
> +	.client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |
> +		       BIT(PMIC_GLINK_CLIENT_ALTMODE) |
> +		       BIT(PMIC_GLINK_CLIENT_UCSI) |
> +		       BIT(PMIC_GLINK_CLIENT_RTC),
> +};
> +
>  static const struct pmic_glink_data pmic_glink_adsp_data = {
>  	.client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |
>  		       BIT(PMIC_GLINK_CLIENT_ALTMODE) |
> @@ -404,7 +424,7 @@ static const struct pmic_glink_data pmic_glink_soccp_data = {
>  };
>  
>  static const struct of_device_id pmic_glink_of_match[] = {
> -	{ .compatible = "qcom,glymur-pmic-glink", .data = &pmic_glink_soccp_data },
> +	{ .compatible = "qcom,glymur-pmic-glink", .data = &pmic_glink_glymur_data },
>  	{ .compatible = "qcom,kaanapali-pmic-glink", .data = &pmic_glink_soccp_data },
>  	{ .compatible = "qcom,pmic-glink", .data = &pmic_glink_adsp_data },
>  	{}
> diff --git a/include/linux/soc/qcom/pmic_glink.h b/include/linux/soc/qcom/pmic_glink.h
> index 7cddf1027752..11031b06b810 100644
> --- a/include/linux/soc/qcom/pmic_glink.h
> +++ b/include/linux/soc/qcom/pmic_glink.h
> @@ -11,6 +11,7 @@ struct pmic_glink_client;
>  #define PMIC_GLINK_OWNER_BATTMGR	32778
>  #define PMIC_GLINK_OWNER_USBC		32779
>  #define PMIC_GLINK_OWNER_USBC_PAN	32780
> +#define PMIC_GLINK_OWNER_RTC		32784
>  
>  #define PMIC_GLINK_REQ_RESP		1
>  #define PMIC_GLINK_NOTIFY		2
> 
> -- 
> 2.43.0
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

      parent reply	other threads:[~2026-08-07 20:58 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-06 20:56 [PATCH 0/2] rtc: Add Qualcomm PMIC RTC-GLINK support for Glymur Kamal Wadhwa
2026-08-06 20:56 ` [PATCH 1/2] dt-bindings: soc: qcom: qcom,pmic-glink: Document RTC client properties Kamal Wadhwa
2026-08-06 21:02   ` sashiko-bot
2026-08-07  0:02   ` Bjorn Andersson
2026-08-06 20:56 ` [PATCH 2/2] rtc: qcom: rtc-glink: Add PMIC GLINK RTC driver and extend pmic_glink Kamal Wadhwa
2026-08-06 21:09   ` sashiko-bot
2026-08-06 23:53   ` Bjorn Andersson
2026-08-07 20:58   ` Alexandre Belloni [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2026080720584407a4d82d@mail.local \
    --to=alexandre.belloni@bootlin.com \
    --cc=andersson@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=kamal.wadhwa@oss.qualcomm.com \
    --cc=konradybcio@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rtc@vger.kernel.org \
    --cc=raryan@qti.qualcomm.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox