File Index
Files and directories
Sign in
FILE

virtio_rtc_class.c

File preview with syntax highlighting.

linux-6.19.14/drivers/virtio/virtio_rtc_class.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * virtio_rtc RTC class driver
  4 *
  5 * Copyright (C) 2023 OpenSynergy GmbH
  6 * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
  7 */
  8
  9#include <linux/math64.h>
 10#include <linux/overflow.h>
 11#include <linux/rtc.h>
 12#include <linux/time64.h>
 13
 14#include <uapi/linux/virtio_rtc.h>
 15
 16#include "virtio_rtc_internal.h"
 17
 18/**
 19 * struct viortc_class - RTC class wrapper
 20 * @viortc: virtio_rtc device data
 21 * @rtc: RTC device
 22 * @vio_clk_id: virtio_rtc clock id
 23 * @stopped: Whether RTC ops are disallowed. Access protected by rtc_lock().
 24 */
 25struct viortc_class {
 26	struct viortc_dev *viortc;
 27	struct rtc_device *rtc;
 28	u16 vio_clk_id;
 29	bool stopped;
 30};
 31
 32/**
 33 * viortc_class_get_locked() - get RTC class wrapper, if ops allowed
 34 * @dev: virtio device
 35 *
 36 * Gets the RTC class wrapper from the virtio device, if it is available and
 37 * ops are allowed.
 38 *
 39 * Context: Caller must hold rtc_lock().
 40 * Return: RTC class wrapper if available and ops allowed, ERR_PTR otherwise.
 41 */
 42static struct viortc_class *viortc_class_get_locked(struct device *dev)
 43{
 44	struct viortc_class *viortc_class;
 45
 46	viortc_class = viortc_class_from_dev(dev);
 47	if (IS_ERR(viortc_class))
 48		return viortc_class;
 49
 50	if (viortc_class->stopped)
 51		return ERR_PTR(-EBUSY);
 52
 53	return viortc_class;
 54}
 55
 56/**
 57 * viortc_class_read_time() - RTC class op read_time
 58 * @dev: virtio device
 59 * @tm: read time
 60 *
 61 * Context: Process context.
 62 * Return: Zero on success, negative error code otherwise.
 63 */
 64static int viortc_class_read_time(struct device *dev, struct rtc_time *tm)
 65{
 66	struct viortc_class *viortc_class;
 67	time64_t sec;
 68	int ret;
 69	u64 ns;
 70
 71	viortc_class = viortc_class_get_locked(dev);
 72	if (IS_ERR(viortc_class))
 73		return PTR_ERR(viortc_class);
 74
 75	ret = viortc_read(viortc_class->viortc, viortc_class->vio_clk_id, &ns);
 76	if (ret)
 77		return ret;
 78
 79	sec = div_u64(ns, NSEC_PER_SEC);
 80
 81	rtc_time64_to_tm(sec, tm);
 82
 83	return 0;
 84}
 85
 86/**
 87 * viortc_class_read_alarm() - RTC class op read_alarm
 88 * @dev: virtio device
 89 * @alrm: alarm read out
 90 *
 91 * Context: Process context.
 92 * Return: Zero on success, negative error code otherwise.
 93 */
 94static int viortc_class_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 95{
 96	struct viortc_class *viortc_class;
 97	time64_t alarm_time_sec;
 98	u64 alarm_time_ns;
 99	bool enabled;
100	int ret;
101
102	viortc_class = viortc_class_get_locked(dev);
103	if (IS_ERR(viortc_class))
104		return PTR_ERR(viortc_class);
105
106	ret = viortc_read_alarm(viortc_class->viortc, viortc_class->vio_clk_id,
107				&alarm_time_ns, &enabled);
108	if (ret)
109		return ret;
110
111	alarm_time_sec = div_u64(alarm_time_ns, NSEC_PER_SEC);
112	rtc_time64_to_tm(alarm_time_sec, &alrm->time);
113
114	alrm->enabled = enabled;
115
116	return 0;
117}
118
119/**
120 * viortc_class_set_alarm() - RTC class op set_alarm
121 * @dev: virtio device
122 * @alrm: alarm to set
123 *
124 * Context: Process context.
125 * Return: Zero on success, negative error code otherwise.
126 */
127static int viortc_class_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
128{
129	struct viortc_class *viortc_class;
130	time64_t alarm_time_sec;
131	u64 alarm_time_ns;
132
133	viortc_class = viortc_class_get_locked(dev);
134	if (IS_ERR(viortc_class))
135		return PTR_ERR(viortc_class);
136
137	alarm_time_sec = rtc_tm_to_time64(&alrm->time);
138
139	if (alarm_time_sec < 0)
140		return -EINVAL;
141
142	if (check_mul_overflow((u64)alarm_time_sec, (u64)NSEC_PER_SEC,
143			       &alarm_time_ns))
144		return -EINVAL;
145
146	return viortc_set_alarm(viortc_class->viortc, viortc_class->vio_clk_id,
147				alarm_time_ns, alrm->enabled);
148}
149
150/**
151 * viortc_class_alarm_irq_enable() - RTC class op alarm_irq_enable
152 * @dev: virtio device
153 * @enabled: enable or disable alarm IRQ
154 *
155 * Context: Process context.
156 * Return: Zero on success, negative error code otherwise.
157 */
158static int viortc_class_alarm_irq_enable(struct device *dev,
159					 unsigned int enabled)
160{
161	struct viortc_class *viortc_class;
162
163	viortc_class = viortc_class_get_locked(dev);
164	if (IS_ERR(viortc_class))
165		return PTR_ERR(viortc_class);
166
167	return viortc_set_alarm_enabled(viortc_class->viortc,
168					viortc_class->vio_clk_id, enabled);
169}
170
171static const struct rtc_class_ops viortc_class_ops = {
172	.read_time = viortc_class_read_time,
173	.read_alarm = viortc_class_read_alarm,
174	.set_alarm = viortc_class_set_alarm,
175	.alarm_irq_enable = viortc_class_alarm_irq_enable,
176};
177
178/**
179 * viortc_class_alarm() - propagate alarm notification as alarm interrupt
180 * @viortc_class: RTC class wrapper
181 * @vio_clk_id: virtio_rtc clock id
182 *
183 * Context: Any context.
184 */
185void viortc_class_alarm(struct viortc_class *viortc_class, u16 vio_clk_id)
186{
187	if (vio_clk_id != viortc_class->vio_clk_id) {
188		dev_warn_ratelimited(&viortc_class->rtc->dev,
189				     "ignoring alarm for clock id %d, expected id %d\n",
190				     vio_clk_id, viortc_class->vio_clk_id);
191		return;
192	}
193
194	rtc_update_irq(viortc_class->rtc, 1, RTC_AF | RTC_IRQF);
195}
196
197/**
198 * viortc_class_stop() - disallow RTC class ops
199 * @viortc_class: RTC class wrapper
200 *
201 * Context: Process context. Caller must NOT hold rtc_lock().
202 */
203void viortc_class_stop(struct viortc_class *viortc_class)
204{
205	rtc_lock(viortc_class->rtc);
206
207	viortc_class->stopped = true;
208
209	rtc_unlock(viortc_class->rtc);
210}
211
212/**
213 * viortc_class_register() - register RTC class device
214 * @viortc_class: RTC class wrapper
215 *
216 * Context: Process context.
217 * Return: Zero on success, negative error code otherwise.
218 */
219int viortc_class_register(struct viortc_class *viortc_class)
220{
221	return devm_rtc_register_device(viortc_class->rtc);
222}
223
224/**
225 * viortc_class_init() - init RTC class wrapper and device
226 * @viortc: device data
227 * @vio_clk_id: virtio_rtc clock id
228 * @have_alarm: have alarm feature
229 * @parent_dev: virtio device
230 *
231 * Context: Process context.
232 * Return: RTC class wrapper on success, ERR_PTR otherwise.
233 */
234struct viortc_class *viortc_class_init(struct viortc_dev *viortc,
235				       u16 vio_clk_id, bool have_alarm,
236				       struct device *parent_dev)
237{
238	struct viortc_class *viortc_class;
239	struct rtc_device *rtc;
240
241	viortc_class =
242		devm_kzalloc(parent_dev, sizeof(*viortc_class), GFP_KERNEL);
243	if (!viortc_class)
244		return ERR_PTR(-ENOMEM);
245
246	rtc = devm_rtc_allocate_device(parent_dev);
247	if (IS_ERR(rtc))
248		return ERR_CAST(rtc);
249
250	viortc_class->viortc = viortc;
251	viortc_class->rtc = rtc;
252	viortc_class->vio_clk_id = vio_clk_id;
253
254	if (!have_alarm)
255		clear_bit(RTC_FEATURE_ALARM, rtc->features);
256	clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
257
258	rtc->ops = &viortc_class_ops;
259	rtc->range_max = div_u64(U64_MAX, NSEC_PER_SEC);
260
261	return viortc_class;
262}
~/ Read-only file preview.