xref: /openbmc/linux/drivers/rtc/rtc-goldfish.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
12be7f1b5SAlexandre Belloni // SPDX-License-Identifier: GPL-2.0
2f22d9cdcSMiodrag Dinic /* drivers/rtc/rtc-goldfish.c
3f22d9cdcSMiodrag Dinic  *
4f22d9cdcSMiodrag Dinic  * Copyright (C) 2007 Google, Inc.
5f22d9cdcSMiodrag Dinic  * Copyright (C) 2017 Imagination Technologies Ltd.
6f22d9cdcSMiodrag Dinic  */
7f22d9cdcSMiodrag Dinic 
8bd01386eSAlexandre Belloni #include <linux/io.h>
9f22d9cdcSMiodrag Dinic #include <linux/module.h>
106a6ec8c1SAlexandre Belloni #include <linux/of.h>
11f22d9cdcSMiodrag Dinic #include <linux/platform_device.h>
12f22d9cdcSMiodrag Dinic #include <linux/rtc.h>
133378c7f4SLaurent Vivier #include <linux/goldfish.h>
14*c92e7ef1SLaurent Vivier #include <clocksource/timer-goldfish.h>
15f22d9cdcSMiodrag Dinic 
16f22d9cdcSMiodrag Dinic struct goldfish_rtc {
17f22d9cdcSMiodrag Dinic 	void __iomem *base;
18f22d9cdcSMiodrag Dinic 	int irq;
19f22d9cdcSMiodrag Dinic 	struct rtc_device *rtc;
20f22d9cdcSMiodrag Dinic };
21f22d9cdcSMiodrag Dinic 
goldfish_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alrm)22f22d9cdcSMiodrag Dinic static int goldfish_rtc_read_alarm(struct device *dev,
23f22d9cdcSMiodrag Dinic 				   struct rtc_wkalrm *alrm)
24f22d9cdcSMiodrag Dinic {
25f22d9cdcSMiodrag Dinic 	u64 rtc_alarm;
26f22d9cdcSMiodrag Dinic 	u64 rtc_alarm_low;
27f22d9cdcSMiodrag Dinic 	u64 rtc_alarm_high;
28f22d9cdcSMiodrag Dinic 	void __iomem *base;
29f22d9cdcSMiodrag Dinic 	struct goldfish_rtc *rtcdrv;
30f22d9cdcSMiodrag Dinic 
31f22d9cdcSMiodrag Dinic 	rtcdrv = dev_get_drvdata(dev);
32f22d9cdcSMiodrag Dinic 	base = rtcdrv->base;
33f22d9cdcSMiodrag Dinic 
343378c7f4SLaurent Vivier 	rtc_alarm_low = gf_ioread32(base + TIMER_ALARM_LOW);
353378c7f4SLaurent Vivier 	rtc_alarm_high = gf_ioread32(base + TIMER_ALARM_HIGH);
36f22d9cdcSMiodrag Dinic 	rtc_alarm = (rtc_alarm_high << 32) | rtc_alarm_low;
37f22d9cdcSMiodrag Dinic 
38f22d9cdcSMiodrag Dinic 	do_div(rtc_alarm, NSEC_PER_SEC);
39f22d9cdcSMiodrag Dinic 	memset(alrm, 0, sizeof(struct rtc_wkalrm));
40f22d9cdcSMiodrag Dinic 
41b509306dSAlexandre Belloni 	rtc_time64_to_tm(rtc_alarm, &alrm->time);
42f22d9cdcSMiodrag Dinic 
433378c7f4SLaurent Vivier 	if (gf_ioread32(base + TIMER_ALARM_STATUS))
44f22d9cdcSMiodrag Dinic 		alrm->enabled = 1;
45f22d9cdcSMiodrag Dinic 	else
46f22d9cdcSMiodrag Dinic 		alrm->enabled = 0;
47f22d9cdcSMiodrag Dinic 
48f22d9cdcSMiodrag Dinic 	return 0;
49f22d9cdcSMiodrag Dinic }
50f22d9cdcSMiodrag Dinic 
goldfish_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alrm)51f22d9cdcSMiodrag Dinic static int goldfish_rtc_set_alarm(struct device *dev,
52f22d9cdcSMiodrag Dinic 				  struct rtc_wkalrm *alrm)
53f22d9cdcSMiodrag Dinic {
54f22d9cdcSMiodrag Dinic 	struct goldfish_rtc *rtcdrv;
55f22d9cdcSMiodrag Dinic 	u64 rtc_alarm64;
56f22d9cdcSMiodrag Dinic 	u64 rtc_status_reg;
57f22d9cdcSMiodrag Dinic 	void __iomem *base;
58f22d9cdcSMiodrag Dinic 
59f22d9cdcSMiodrag Dinic 	rtcdrv = dev_get_drvdata(dev);
60f22d9cdcSMiodrag Dinic 	base = rtcdrv->base;
61f22d9cdcSMiodrag Dinic 
62f22d9cdcSMiodrag Dinic 	if (alrm->enabled) {
63b509306dSAlexandre Belloni 		rtc_alarm64 = rtc_tm_to_time64(&alrm->time) * NSEC_PER_SEC;
643378c7f4SLaurent Vivier 		gf_iowrite32((rtc_alarm64 >> 32), base + TIMER_ALARM_HIGH);
653378c7f4SLaurent Vivier 		gf_iowrite32(rtc_alarm64, base + TIMER_ALARM_LOW);
663378c7f4SLaurent Vivier 		gf_iowrite32(1, base + TIMER_IRQ_ENABLED);
67f22d9cdcSMiodrag Dinic 	} else {
68f22d9cdcSMiodrag Dinic 		/*
69f22d9cdcSMiodrag Dinic 		 * if this function was called with enabled=0
70f22d9cdcSMiodrag Dinic 		 * then it could mean that the application is
71f22d9cdcSMiodrag Dinic 		 * trying to cancel an ongoing alarm
72f22d9cdcSMiodrag Dinic 		 */
733378c7f4SLaurent Vivier 		rtc_status_reg = gf_ioread32(base + TIMER_ALARM_STATUS);
74f22d9cdcSMiodrag Dinic 		if (rtc_status_reg)
753378c7f4SLaurent Vivier 			gf_iowrite32(1, base + TIMER_CLEAR_ALARM);
76f22d9cdcSMiodrag Dinic 	}
77f22d9cdcSMiodrag Dinic 
78b509306dSAlexandre Belloni 	return 0;
79f22d9cdcSMiodrag Dinic }
80f22d9cdcSMiodrag Dinic 
goldfish_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)81f22d9cdcSMiodrag Dinic static int goldfish_rtc_alarm_irq_enable(struct device *dev,
82f22d9cdcSMiodrag Dinic 					 unsigned int enabled)
83f22d9cdcSMiodrag Dinic {
84f22d9cdcSMiodrag Dinic 	void __iomem *base;
85f22d9cdcSMiodrag Dinic 	struct goldfish_rtc *rtcdrv;
86f22d9cdcSMiodrag Dinic 
87f22d9cdcSMiodrag Dinic 	rtcdrv = dev_get_drvdata(dev);
88f22d9cdcSMiodrag Dinic 	base = rtcdrv->base;
89f22d9cdcSMiodrag Dinic 
90f22d9cdcSMiodrag Dinic 	if (enabled)
913378c7f4SLaurent Vivier 		gf_iowrite32(1, base + TIMER_IRQ_ENABLED);
92f22d9cdcSMiodrag Dinic 	else
933378c7f4SLaurent Vivier 		gf_iowrite32(0, base + TIMER_IRQ_ENABLED);
94f22d9cdcSMiodrag Dinic 
95f22d9cdcSMiodrag Dinic 	return 0;
96f22d9cdcSMiodrag Dinic }
97f22d9cdcSMiodrag Dinic 
goldfish_rtc_interrupt(int irq,void * dev_id)98f22d9cdcSMiodrag Dinic static irqreturn_t goldfish_rtc_interrupt(int irq, void *dev_id)
99f22d9cdcSMiodrag Dinic {
100f22d9cdcSMiodrag Dinic 	struct goldfish_rtc *rtcdrv = dev_id;
101f22d9cdcSMiodrag Dinic 	void __iomem *base = rtcdrv->base;
102f22d9cdcSMiodrag Dinic 
1033378c7f4SLaurent Vivier 	gf_iowrite32(1, base + TIMER_CLEAR_INTERRUPT);
104f22d9cdcSMiodrag Dinic 
105f22d9cdcSMiodrag Dinic 	rtc_update_irq(rtcdrv->rtc, 1, RTC_IRQF | RTC_AF);
106f22d9cdcSMiodrag Dinic 
107f22d9cdcSMiodrag Dinic 	return IRQ_HANDLED;
108f22d9cdcSMiodrag Dinic }
109f22d9cdcSMiodrag Dinic 
goldfish_rtc_read_time(struct device * dev,struct rtc_time * tm)110f22d9cdcSMiodrag Dinic static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm)
111f22d9cdcSMiodrag Dinic {
112f22d9cdcSMiodrag Dinic 	struct goldfish_rtc *rtcdrv;
113f22d9cdcSMiodrag Dinic 	void __iomem *base;
114f22d9cdcSMiodrag Dinic 	u64 time_high;
115f22d9cdcSMiodrag Dinic 	u64 time_low;
116f22d9cdcSMiodrag Dinic 	u64 time;
117f22d9cdcSMiodrag Dinic 
118f22d9cdcSMiodrag Dinic 	rtcdrv = dev_get_drvdata(dev);
119f22d9cdcSMiodrag Dinic 	base = rtcdrv->base;
120f22d9cdcSMiodrag Dinic 
1213378c7f4SLaurent Vivier 	time_low = gf_ioread32(base + TIMER_TIME_LOW);
1223378c7f4SLaurent Vivier 	time_high = gf_ioread32(base + TIMER_TIME_HIGH);
123f22d9cdcSMiodrag Dinic 	time = (time_high << 32) | time_low;
124f22d9cdcSMiodrag Dinic 
125f22d9cdcSMiodrag Dinic 	do_div(time, NSEC_PER_SEC);
126f22d9cdcSMiodrag Dinic 
127b509306dSAlexandre Belloni 	rtc_time64_to_tm(time, tm);
128f22d9cdcSMiodrag Dinic 
129f22d9cdcSMiodrag Dinic 	return 0;
130f22d9cdcSMiodrag Dinic }
131f22d9cdcSMiodrag Dinic 
goldfish_rtc_set_time(struct device * dev,struct rtc_time * tm)132f22d9cdcSMiodrag Dinic static int goldfish_rtc_set_time(struct device *dev, struct rtc_time *tm)
133f22d9cdcSMiodrag Dinic {
134f22d9cdcSMiodrag Dinic 	struct goldfish_rtc *rtcdrv;
135f22d9cdcSMiodrag Dinic 	void __iomem *base;
136f22d9cdcSMiodrag Dinic 	u64 now64;
137f22d9cdcSMiodrag Dinic 
138f22d9cdcSMiodrag Dinic 	rtcdrv = dev_get_drvdata(dev);
139f22d9cdcSMiodrag Dinic 	base = rtcdrv->base;
140f22d9cdcSMiodrag Dinic 
141b509306dSAlexandre Belloni 	now64 = rtc_tm_to_time64(tm) * NSEC_PER_SEC;
1423378c7f4SLaurent Vivier 	gf_iowrite32((now64 >> 32), base + TIMER_TIME_HIGH);
1433378c7f4SLaurent Vivier 	gf_iowrite32(now64, base + TIMER_TIME_LOW);
144f22d9cdcSMiodrag Dinic 
145b509306dSAlexandre Belloni 	return 0;
146f22d9cdcSMiodrag Dinic }
147f22d9cdcSMiodrag Dinic 
148f22d9cdcSMiodrag Dinic static const struct rtc_class_ops goldfish_rtc_ops = {
149f22d9cdcSMiodrag Dinic 	.read_time	= goldfish_rtc_read_time,
150f22d9cdcSMiodrag Dinic 	.set_time	= goldfish_rtc_set_time,
151f22d9cdcSMiodrag Dinic 	.read_alarm	= goldfish_rtc_read_alarm,
152f22d9cdcSMiodrag Dinic 	.set_alarm	= goldfish_rtc_set_alarm,
153f22d9cdcSMiodrag Dinic 	.alarm_irq_enable = goldfish_rtc_alarm_irq_enable
154f22d9cdcSMiodrag Dinic };
155f22d9cdcSMiodrag Dinic 
goldfish_rtc_probe(struct platform_device * pdev)156f22d9cdcSMiodrag Dinic static int goldfish_rtc_probe(struct platform_device *pdev)
157f22d9cdcSMiodrag Dinic {
158f22d9cdcSMiodrag Dinic 	struct goldfish_rtc *rtcdrv;
159f22d9cdcSMiodrag Dinic 	int err;
160f22d9cdcSMiodrag Dinic 
161f22d9cdcSMiodrag Dinic 	rtcdrv = devm_kzalloc(&pdev->dev, sizeof(*rtcdrv), GFP_KERNEL);
162f22d9cdcSMiodrag Dinic 	if (!rtcdrv)
163f22d9cdcSMiodrag Dinic 		return -ENOMEM;
164f22d9cdcSMiodrag Dinic 
165f22d9cdcSMiodrag Dinic 	platform_set_drvdata(pdev, rtcdrv);
16689576bebSMarkus Elfring 	rtcdrv->base = devm_platform_ioremap_resource(pdev, 0);
167f22d9cdcSMiodrag Dinic 	if (IS_ERR(rtcdrv->base))
168f4c29a09STiezhu Yang 		return PTR_ERR(rtcdrv->base);
169f22d9cdcSMiodrag Dinic 
170f22d9cdcSMiodrag Dinic 	rtcdrv->irq = platform_get_irq(pdev, 0);
171f22d9cdcSMiodrag Dinic 	if (rtcdrv->irq < 0)
172f22d9cdcSMiodrag Dinic 		return -ENODEV;
173f22d9cdcSMiodrag Dinic 
174409b84e3SAlexandre Belloni 	rtcdrv->rtc = devm_rtc_allocate_device(&pdev->dev);
175f22d9cdcSMiodrag Dinic 	if (IS_ERR(rtcdrv->rtc))
176f22d9cdcSMiodrag Dinic 		return PTR_ERR(rtcdrv->rtc);
177f22d9cdcSMiodrag Dinic 
178409b84e3SAlexandre Belloni 	rtcdrv->rtc->ops = &goldfish_rtc_ops;
1795e2954fdSAlexandre Belloni 	rtcdrv->rtc->range_max = U64_MAX / NSEC_PER_SEC;
180409b84e3SAlexandre Belloni 
181f22d9cdcSMiodrag Dinic 	err = devm_request_irq(&pdev->dev, rtcdrv->irq,
182f22d9cdcSMiodrag Dinic 			       goldfish_rtc_interrupt,
183f22d9cdcSMiodrag Dinic 			       0, pdev->name, rtcdrv);
184f22d9cdcSMiodrag Dinic 	if (err)
185f22d9cdcSMiodrag Dinic 		return err;
186f22d9cdcSMiodrag Dinic 
187fdcfd854SBartosz Golaszewski 	return devm_rtc_register_device(rtcdrv->rtc);
188f22d9cdcSMiodrag Dinic }
189f22d9cdcSMiodrag Dinic 
190f22d9cdcSMiodrag Dinic static const struct of_device_id goldfish_rtc_of_match[] = {
191f22d9cdcSMiodrag Dinic 	{ .compatible = "google,goldfish-rtc", },
192f22d9cdcSMiodrag Dinic 	{},
193f22d9cdcSMiodrag Dinic };
194f22d9cdcSMiodrag Dinic MODULE_DEVICE_TABLE(of, goldfish_rtc_of_match);
195f22d9cdcSMiodrag Dinic 
196f22d9cdcSMiodrag Dinic static struct platform_driver goldfish_rtc = {
197f22d9cdcSMiodrag Dinic 	.probe = goldfish_rtc_probe,
198f22d9cdcSMiodrag Dinic 	.driver = {
199f22d9cdcSMiodrag Dinic 		.name = "goldfish_rtc",
200f22d9cdcSMiodrag Dinic 		.of_match_table = goldfish_rtc_of_match,
201f22d9cdcSMiodrag Dinic 	}
202f22d9cdcSMiodrag Dinic };
203f22d9cdcSMiodrag Dinic 
204f22d9cdcSMiodrag Dinic module_platform_driver(goldfish_rtc);
20582d632b8SJames Hogan 
20682d632b8SJames Hogan MODULE_LICENSE("GPL v2");
207