xref: /openbmc/linux/drivers/rtc/rtc-pl031.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28ae6e163SDeepak Saxena /*
38ae6e163SDeepak Saxena  * drivers/rtc/rtc-pl031.c
48ae6e163SDeepak Saxena  *
58ae6e163SDeepak Saxena  * Real Time Clock interface for ARM AMBA PrimeCell 031 RTC
68ae6e163SDeepak Saxena  *
78ae6e163SDeepak Saxena  * Author: Deepak Saxena <dsaxena@plexity.net>
88ae6e163SDeepak Saxena  *
98ae6e163SDeepak Saxena  * Copyright 2006 (c) MontaVista Software, Inc.
108ae6e163SDeepak Saxena  *
11c72881e8SLinus Walleij  * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
12c72881e8SLinus Walleij  * Copyright 2010 (c) ST-Ericsson AB
138ae6e163SDeepak Saxena  */
148ae6e163SDeepak Saxena #include <linux/module.h>
158ae6e163SDeepak Saxena #include <linux/rtc.h>
168ae6e163SDeepak Saxena #include <linux/init.h>
178ae6e163SDeepak Saxena #include <linux/interrupt.h>
188ae6e163SDeepak Saxena #include <linux/amba/bus.h>
192dba8518SRussell King #include <linux/io.h>
20c72881e8SLinus Walleij #include <linux/bcd.h>
21c72881e8SLinus Walleij #include <linux/delay.h>
22eff6dd41SSudeep Holla #include <linux/pm_wakeirq.h>
235a0e3ad6STejun Heo #include <linux/slab.h>
248ae6e163SDeepak Saxena 
258ae6e163SDeepak Saxena /*
268ae6e163SDeepak Saxena  * Register definitions
278ae6e163SDeepak Saxena  */
288ae6e163SDeepak Saxena #define	RTC_DR		0x00	/* Data read register */
298ae6e163SDeepak Saxena #define	RTC_MR		0x04	/* Match register */
308ae6e163SDeepak Saxena #define	RTC_LR		0x08	/* Data load register */
318ae6e163SDeepak Saxena #define	RTC_CR		0x0c	/* Control register */
328ae6e163SDeepak Saxena #define	RTC_IMSC	0x10	/* Interrupt mask and set register */
338ae6e163SDeepak Saxena #define	RTC_RIS		0x14	/* Raw interrupt status register */
348ae6e163SDeepak Saxena #define	RTC_MIS		0x18	/* Masked interrupt status register */
358ae6e163SDeepak Saxena #define	RTC_ICR		0x1c	/* Interrupt clear register */
36c72881e8SLinus Walleij /* ST variants have additional timer functionality */
37c72881e8SLinus Walleij #define RTC_TDR		0x20	/* Timer data read register */
38c72881e8SLinus Walleij #define RTC_TLR		0x24	/* Timer data load register */
39c72881e8SLinus Walleij #define RTC_TCR		0x28	/* Timer control register */
40c72881e8SLinus Walleij #define RTC_YDR		0x30	/* Year data read register */
41c72881e8SLinus Walleij #define RTC_YMR		0x34	/* Year match register */
42c72881e8SLinus Walleij #define RTC_YLR		0x38	/* Year data load register */
43c72881e8SLinus Walleij 
44e7e034e1SHaojian Zhuang #define RTC_CR_EN	(1 << 0)	/* counter enable bit */
45c72881e8SLinus Walleij #define RTC_CR_CWEN	(1 << 26)	/* Clockwatch enable bit */
46c72881e8SLinus Walleij 
47c72881e8SLinus Walleij #define RTC_TCR_EN	(1 << 1) /* Periodic timer enable bit */
48c72881e8SLinus Walleij 
49c72881e8SLinus Walleij /* Common bit definitions for Interrupt status and control registers */
50c72881e8SLinus Walleij #define RTC_BIT_AI	(1 << 0) /* Alarm interrupt bit */
51c72881e8SLinus Walleij #define RTC_BIT_PI	(1 << 1) /* Periodic interrupt bit. ST variants only. */
52c72881e8SLinus Walleij 
53c72881e8SLinus Walleij /* Common bit definations for ST v2 for reading/writing time */
54c72881e8SLinus Walleij #define RTC_SEC_SHIFT 0
55c72881e8SLinus Walleij #define RTC_SEC_MASK (0x3F << RTC_SEC_SHIFT) /* Second [0-59] */
56c72881e8SLinus Walleij #define RTC_MIN_SHIFT 6
57c72881e8SLinus Walleij #define RTC_MIN_MASK (0x3F << RTC_MIN_SHIFT) /* Minute [0-59] */
58c72881e8SLinus Walleij #define RTC_HOUR_SHIFT 12
59c72881e8SLinus Walleij #define RTC_HOUR_MASK (0x1F << RTC_HOUR_SHIFT) /* Hour [0-23] */
60c72881e8SLinus Walleij #define RTC_WDAY_SHIFT 17
61c72881e8SLinus Walleij #define RTC_WDAY_MASK (0x7 << RTC_WDAY_SHIFT) /* Day of Week [1-7] 1=Sunday */
62c72881e8SLinus Walleij #define RTC_MDAY_SHIFT 20
63c72881e8SLinus Walleij #define RTC_MDAY_MASK (0x1F << RTC_MDAY_SHIFT) /* Day of Month [1-31] */
64c72881e8SLinus Walleij #define RTC_MON_SHIFT 25
65c72881e8SLinus Walleij #define RTC_MON_MASK (0xF << RTC_MON_SHIFT) /* Month [1-12] 1=January */
66c72881e8SLinus Walleij 
67c72881e8SLinus Walleij #define RTC_TIMER_FREQ 32768
688ae6e163SDeepak Saxena 
69aff05ed5SLinus Walleij /**
70aff05ed5SLinus Walleij  * struct pl031_vendor_data - per-vendor variations
71aff05ed5SLinus Walleij  * @ops: the vendor-specific operations used on this silicon version
721bb457fcSLinus Walleij  * @clockwatch: if this is an ST Microelectronics silicon version with a
731bb457fcSLinus Walleij  *	clockwatch function
741bb457fcSLinus Walleij  * @st_weekday: if this is an ST Microelectronics silicon version that need
751bb457fcSLinus Walleij  *	the weekday fix
76559a6fc0SMattias Wallin  * @irqflags: special IRQ flags per variant
77aff05ed5SLinus Walleij  */
78aff05ed5SLinus Walleij struct pl031_vendor_data {
79aff05ed5SLinus Walleij 	struct rtc_class_ops ops;
801bb457fcSLinus Walleij 	bool clockwatch;
811bb457fcSLinus Walleij 	bool st_weekday;
82559a6fc0SMattias Wallin 	unsigned long irqflags;
8303f2a0e4SAlexandre Belloni 	time64_t range_min;
8403f2a0e4SAlexandre Belloni 	timeu64_t range_max;
85aff05ed5SLinus Walleij };
86aff05ed5SLinus Walleij 
878ae6e163SDeepak Saxena struct pl031_local {
88aff05ed5SLinus Walleij 	struct pl031_vendor_data *vendor;
898ae6e163SDeepak Saxena 	struct rtc_device *rtc;
908ae6e163SDeepak Saxena 	void __iomem *base;
918ae6e163SDeepak Saxena };
928ae6e163SDeepak Saxena 
pl031_alarm_irq_enable(struct device * dev,unsigned int enabled)93c72881e8SLinus Walleij static int pl031_alarm_irq_enable(struct device *dev,
94c72881e8SLinus Walleij 	unsigned int enabled)
95c72881e8SLinus Walleij {
96c72881e8SLinus Walleij 	struct pl031_local *ldata = dev_get_drvdata(dev);
97c72881e8SLinus Walleij 	unsigned long imsc;
98c72881e8SLinus Walleij 
99c72881e8SLinus Walleij 	/* Clear any pending alarm interrupts. */
100c72881e8SLinus Walleij 	writel(RTC_BIT_AI, ldata->base + RTC_ICR);
101c72881e8SLinus Walleij 
102c72881e8SLinus Walleij 	imsc = readl(ldata->base + RTC_IMSC);
103c72881e8SLinus Walleij 
104c72881e8SLinus Walleij 	if (enabled == 1)
105c72881e8SLinus Walleij 		writel(imsc | RTC_BIT_AI, ldata->base + RTC_IMSC);
106c72881e8SLinus Walleij 	else
107c72881e8SLinus Walleij 		writel(imsc & ~RTC_BIT_AI, ldata->base + RTC_IMSC);
108c72881e8SLinus Walleij 
109c72881e8SLinus Walleij 	return 0;
110c72881e8SLinus Walleij }
111c72881e8SLinus Walleij 
112c72881e8SLinus Walleij /*
113c72881e8SLinus Walleij  * Convert Gregorian date to ST v2 RTC format.
114c72881e8SLinus Walleij  */
pl031_stv2_tm_to_time(struct device * dev,struct rtc_time * tm,unsigned long * st_time,unsigned long * bcd_year)115c72881e8SLinus Walleij static int pl031_stv2_tm_to_time(struct device *dev,
116c72881e8SLinus Walleij 				 struct rtc_time *tm, unsigned long *st_time,
117c72881e8SLinus Walleij 	unsigned long *bcd_year)
118c72881e8SLinus Walleij {
119c72881e8SLinus Walleij 	int year = tm->tm_year + 1900;
120c72881e8SLinus Walleij 	int wday = tm->tm_wday;
121c72881e8SLinus Walleij 
122c72881e8SLinus Walleij 	/* wday masking is not working in hardware so wday must be valid */
123c72881e8SLinus Walleij 	if (wday < -1 || wday > 6) {
124c72881e8SLinus Walleij 		dev_err(dev, "invalid wday value %d\n", tm->tm_wday);
125c72881e8SLinus Walleij 		return -EINVAL;
126c72881e8SLinus Walleij 	} else if (wday == -1) {
127c72881e8SLinus Walleij 		/* wday is not provided, calculate it here */
128c72881e8SLinus Walleij 		struct rtc_time calc_tm;
129c72881e8SLinus Walleij 
130c8ff5841SAlexandre Belloni 		rtc_time64_to_tm(rtc_tm_to_time64(tm), &calc_tm);
131c72881e8SLinus Walleij 		wday = calc_tm.tm_wday;
132c72881e8SLinus Walleij 	}
133c72881e8SLinus Walleij 
134c72881e8SLinus Walleij 	*bcd_year = (bin2bcd(year % 100) | bin2bcd(year / 100) << 8);
135c72881e8SLinus Walleij 
136c72881e8SLinus Walleij 	*st_time = ((tm->tm_mon + 1) << RTC_MON_SHIFT)
137c72881e8SLinus Walleij 			|	(tm->tm_mday << RTC_MDAY_SHIFT)
138c72881e8SLinus Walleij 			|	((wday + 1) << RTC_WDAY_SHIFT)
139c72881e8SLinus Walleij 			|	(tm->tm_hour << RTC_HOUR_SHIFT)
140c72881e8SLinus Walleij 			|	(tm->tm_min << RTC_MIN_SHIFT)
141c72881e8SLinus Walleij 			|	(tm->tm_sec << RTC_SEC_SHIFT);
142c72881e8SLinus Walleij 
143c72881e8SLinus Walleij 	return 0;
144c72881e8SLinus Walleij }
145c72881e8SLinus Walleij 
146c72881e8SLinus Walleij /*
147c72881e8SLinus Walleij  * Convert ST v2 RTC format to Gregorian date.
148c72881e8SLinus Walleij  */
pl031_stv2_time_to_tm(unsigned long st_time,unsigned long bcd_year,struct rtc_time * tm)149c72881e8SLinus Walleij static int pl031_stv2_time_to_tm(unsigned long st_time, unsigned long bcd_year,
150c72881e8SLinus Walleij 	struct rtc_time *tm)
151c72881e8SLinus Walleij {
152c72881e8SLinus Walleij 	tm->tm_year = bcd2bin(bcd_year) + (bcd2bin(bcd_year >> 8) * 100);
153c72881e8SLinus Walleij 	tm->tm_mon  = ((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1;
154c72881e8SLinus Walleij 	tm->tm_mday = ((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT);
155c72881e8SLinus Walleij 	tm->tm_wday = ((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1;
156c72881e8SLinus Walleij 	tm->tm_hour = ((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT);
157c72881e8SLinus Walleij 	tm->tm_min  = ((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT);
158c72881e8SLinus Walleij 	tm->tm_sec  = ((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT);
159c72881e8SLinus Walleij 
160c72881e8SLinus Walleij 	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
161c72881e8SLinus Walleij 	tm->tm_year -= 1900;
162c72881e8SLinus Walleij 
163c72881e8SLinus Walleij 	return 0;
164c72881e8SLinus Walleij }
165c72881e8SLinus Walleij 
pl031_stv2_read_time(struct device * dev,struct rtc_time * tm)166c72881e8SLinus Walleij static int pl031_stv2_read_time(struct device *dev, struct rtc_time *tm)
167c72881e8SLinus Walleij {
168c72881e8SLinus Walleij 	struct pl031_local *ldata = dev_get_drvdata(dev);
169c72881e8SLinus Walleij 
170c72881e8SLinus Walleij 	pl031_stv2_time_to_tm(readl(ldata->base + RTC_DR),
171c72881e8SLinus Walleij 			readl(ldata->base + RTC_YDR), tm);
172c72881e8SLinus Walleij 
173c72881e8SLinus Walleij 	return 0;
174c72881e8SLinus Walleij }
175c72881e8SLinus Walleij 
pl031_stv2_set_time(struct device * dev,struct rtc_time * tm)176c72881e8SLinus Walleij static int pl031_stv2_set_time(struct device *dev, struct rtc_time *tm)
177c72881e8SLinus Walleij {
178c72881e8SLinus Walleij 	unsigned long time;
179c72881e8SLinus Walleij 	unsigned long bcd_year;
180c72881e8SLinus Walleij 	struct pl031_local *ldata = dev_get_drvdata(dev);
181c72881e8SLinus Walleij 	int ret;
182c72881e8SLinus Walleij 
183c72881e8SLinus Walleij 	ret = pl031_stv2_tm_to_time(dev, tm, &time, &bcd_year);
184c72881e8SLinus Walleij 	if (ret == 0) {
185c72881e8SLinus Walleij 		writel(bcd_year, ldata->base + RTC_YLR);
186c72881e8SLinus Walleij 		writel(time, ldata->base + RTC_LR);
187c72881e8SLinus Walleij 	}
188c72881e8SLinus Walleij 
189c72881e8SLinus Walleij 	return ret;
190c72881e8SLinus Walleij }
191c72881e8SLinus Walleij 
pl031_stv2_read_alarm(struct device * dev,struct rtc_wkalrm * alarm)192c72881e8SLinus Walleij static int pl031_stv2_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
193c72881e8SLinus Walleij {
194c72881e8SLinus Walleij 	struct pl031_local *ldata = dev_get_drvdata(dev);
195c72881e8SLinus Walleij 	int ret;
196c72881e8SLinus Walleij 
197c72881e8SLinus Walleij 	ret = pl031_stv2_time_to_tm(readl(ldata->base + RTC_MR),
198c72881e8SLinus Walleij 			readl(ldata->base + RTC_YMR), &alarm->time);
199c72881e8SLinus Walleij 
200c72881e8SLinus Walleij 	alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
201c72881e8SLinus Walleij 	alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
202c72881e8SLinus Walleij 
203c72881e8SLinus Walleij 	return ret;
204c72881e8SLinus Walleij }
205c72881e8SLinus Walleij 
pl031_stv2_set_alarm(struct device * dev,struct rtc_wkalrm * alarm)206c72881e8SLinus Walleij static int pl031_stv2_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
207c72881e8SLinus Walleij {
208c72881e8SLinus Walleij 	struct pl031_local *ldata = dev_get_drvdata(dev);
209c72881e8SLinus Walleij 	unsigned long time;
210c72881e8SLinus Walleij 	unsigned long bcd_year;
211c72881e8SLinus Walleij 	int ret;
212c72881e8SLinus Walleij 
213c72881e8SLinus Walleij 	ret = pl031_stv2_tm_to_time(dev, &alarm->time,
214c72881e8SLinus Walleij 				    &time, &bcd_year);
215c72881e8SLinus Walleij 	if (ret == 0) {
216c72881e8SLinus Walleij 		writel(bcd_year, ldata->base + RTC_YMR);
217c72881e8SLinus Walleij 		writel(time, ldata->base + RTC_MR);
218c72881e8SLinus Walleij 
219c72881e8SLinus Walleij 		pl031_alarm_irq_enable(dev, alarm->enabled);
220c72881e8SLinus Walleij 	}
221c72881e8SLinus Walleij 
222c72881e8SLinus Walleij 	return ret;
223c72881e8SLinus Walleij }
224c72881e8SLinus Walleij 
pl031_interrupt(int irq,void * dev_id)2257d12e780SDavid Howells static irqreturn_t pl031_interrupt(int irq, void *dev_id)
2268ae6e163SDeepak Saxena {
227c72881e8SLinus Walleij 	struct pl031_local *ldata = dev_id;
228c72881e8SLinus Walleij 	unsigned long rtcmis;
229c72881e8SLinus Walleij 	unsigned long events = 0;
2308ae6e163SDeepak Saxena 
231c72881e8SLinus Walleij 	rtcmis = readl(ldata->base + RTC_MIS);
232ac2dee59SRajkumar Kasirajan 	if (rtcmis & RTC_BIT_AI) {
233ac2dee59SRajkumar Kasirajan 		writel(RTC_BIT_AI, ldata->base + RTC_ICR);
234c72881e8SLinus Walleij 		events |= (RTC_AF | RTC_IRQF);
235c72881e8SLinus Walleij 		rtc_update_irq(ldata->rtc, 1, events);
2368ae6e163SDeepak Saxena 
2378ae6e163SDeepak Saxena 		return IRQ_HANDLED;
2388ae6e163SDeepak Saxena 	}
2398ae6e163SDeepak Saxena 
240c72881e8SLinus Walleij 	return IRQ_NONE;
2418ae6e163SDeepak Saxena }
2428ae6e163SDeepak Saxena 
pl031_read_time(struct device * dev,struct rtc_time * tm)2438ae6e163SDeepak Saxena static int pl031_read_time(struct device *dev, struct rtc_time *tm)
2448ae6e163SDeepak Saxena {
2458ae6e163SDeepak Saxena 	struct pl031_local *ldata = dev_get_drvdata(dev);
2468ae6e163SDeepak Saxena 
247c8ff5841SAlexandre Belloni 	rtc_time64_to_tm(readl(ldata->base + RTC_DR), tm);
2488ae6e163SDeepak Saxena 
2498ae6e163SDeepak Saxena 	return 0;
2508ae6e163SDeepak Saxena }
2518ae6e163SDeepak Saxena 
pl031_set_time(struct device * dev,struct rtc_time * tm)2528ae6e163SDeepak Saxena static int pl031_set_time(struct device *dev, struct rtc_time *tm)
2538ae6e163SDeepak Saxena {
2548ae6e163SDeepak Saxena 	struct pl031_local *ldata = dev_get_drvdata(dev);
2558ae6e163SDeepak Saxena 
256c8ff5841SAlexandre Belloni 	writel(rtc_tm_to_time64(tm), ldata->base + RTC_LR);
257c72881e8SLinus Walleij 
258c8ff5841SAlexandre Belloni 	return 0;
2598ae6e163SDeepak Saxena }
2608ae6e163SDeepak Saxena 
pl031_read_alarm(struct device * dev,struct rtc_wkalrm * alarm)2618ae6e163SDeepak Saxena static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
2628ae6e163SDeepak Saxena {
2638ae6e163SDeepak Saxena 	struct pl031_local *ldata = dev_get_drvdata(dev);
2648ae6e163SDeepak Saxena 
265c8ff5841SAlexandre Belloni 	rtc_time64_to_tm(readl(ldata->base + RTC_MR), &alarm->time);
266c72881e8SLinus Walleij 
267c72881e8SLinus Walleij 	alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
268c72881e8SLinus Walleij 	alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
2698ae6e163SDeepak Saxena 
2708ae6e163SDeepak Saxena 	return 0;
2718ae6e163SDeepak Saxena }
2728ae6e163SDeepak Saxena 
pl031_set_alarm(struct device * dev,struct rtc_wkalrm * alarm)2738ae6e163SDeepak Saxena static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
2748ae6e163SDeepak Saxena {
2758ae6e163SDeepak Saxena 	struct pl031_local *ldata = dev_get_drvdata(dev);
2768ae6e163SDeepak Saxena 
277c8ff5841SAlexandre Belloni 	writel(rtc_tm_to_time64(&alarm->time), ldata->base + RTC_MR);
2784df2ef85SSudeep Holla 	pl031_alarm_irq_enable(dev, alarm->enabled);
279c72881e8SLinus Walleij 
280c8ff5841SAlexandre Belloni 	return 0;
281c72881e8SLinus Walleij }
282c72881e8SLinus Walleij 
pl031_remove(struct amba_device * adev)2833fd269e7SUwe Kleine-König static void pl031_remove(struct amba_device *adev)
2848ae6e163SDeepak Saxena {
2858ae6e163SDeepak Saxena 	struct pl031_local *ldata = dev_get_drvdata(&adev->dev);
2868ae6e163SDeepak Saxena 
287eff6dd41SSudeep Holla 	dev_pm_clear_wake_irq(&adev->dev);
288eff6dd41SSudeep Holla 	device_init_wakeup(&adev->dev, false);
2895b64a296SRussell King 	if (adev->irq[0])
290cac29af6SLars-Peter Clausen 		free_irq(adev->irq[0], ldata);
2912dba8518SRussell King 	amba_release_regions(adev);
2928ae6e163SDeepak Saxena }
2938ae6e163SDeepak Saxena 
pl031_probe(struct amba_device * adev,const struct amba_id * id)294aa25afadSRussell King static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
2958ae6e163SDeepak Saxena {
2968ae6e163SDeepak Saxena 	int ret;
2978ae6e163SDeepak Saxena 	struct pl031_local *ldata;
298aff05ed5SLinus Walleij 	struct pl031_vendor_data *vendor = id->data;
299b86f581fSRussell King 	struct rtc_class_ops *ops;
300e7e034e1SHaojian Zhuang 	unsigned long time, data;
3018ae6e163SDeepak Saxena 
3022dba8518SRussell King 	ret = amba_request_regions(adev, NULL);
3032dba8518SRussell King 	if (ret)
3042dba8518SRussell King 		goto err_req;
3058ae6e163SDeepak Saxena 
306273c868eSRussell King 	ldata = devm_kzalloc(&adev->dev, sizeof(struct pl031_local),
307273c868eSRussell King 			     GFP_KERNEL);
308b86f581fSRussell King 	ops = devm_kmemdup(&adev->dev, &vendor->ops, sizeof(vendor->ops),
309b86f581fSRussell King 			   GFP_KERNEL);
310b86f581fSRussell King 	if (!ldata || !ops) {
3118ae6e163SDeepak Saxena 		ret = -ENOMEM;
3128ae6e163SDeepak Saxena 		goto out;
3138ae6e163SDeepak Saxena 	}
3148ae6e163SDeepak Saxena 
315b86f581fSRussell King 	ldata->vendor = vendor;
316273c868eSRussell King 	ldata->base = devm_ioremap(&adev->dev, adev->res.start,
317273c868eSRussell King 				   resource_size(&adev->res));
3188ae6e163SDeepak Saxena 	if (!ldata->base) {
3198ae6e163SDeepak Saxena 		ret = -ENOMEM;
320273c868eSRussell King 		goto out;
3218ae6e163SDeepak Saxena 	}
3228ae6e163SDeepak Saxena 
3232dba8518SRussell King 	amba_set_drvdata(adev, ldata);
3242dba8518SRussell King 
3251bb457fcSLinus Walleij 	dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev));
3261bb457fcSLinus Walleij 	dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev));
327c72881e8SLinus Walleij 
328e7e034e1SHaojian Zhuang 	data = readl(ldata->base + RTC_CR);
329c72881e8SLinus Walleij 	/* Enable the clockwatch on ST Variants */
3301bb457fcSLinus Walleij 	if (vendor->clockwatch)
331e7e034e1SHaojian Zhuang 		data |= RTC_CR_CWEN;
3323399cfb5SLinus Walleij 	else
3333399cfb5SLinus Walleij 		data |= RTC_CR_EN;
3343399cfb5SLinus Walleij 	writel(data, ldata->base + RTC_CR);
335c72881e8SLinus Walleij 
336c0a5f4a0SRajkumar Kasirajan 	/*
337c0a5f4a0SRajkumar Kasirajan 	 * On ST PL031 variants, the RTC reset value does not provide correct
338c0a5f4a0SRajkumar Kasirajan 	 * weekday for 2000-01-01. Correct the erroneous sunday to saturday.
339c0a5f4a0SRajkumar Kasirajan 	 */
3401bb457fcSLinus Walleij 	if (vendor->st_weekday) {
341c0a5f4a0SRajkumar Kasirajan 		if (readl(ldata->base + RTC_YDR) == 0x2000) {
342c0a5f4a0SRajkumar Kasirajan 			time = readl(ldata->base + RTC_DR);
343c0a5f4a0SRajkumar Kasirajan 			if ((time &
344c0a5f4a0SRajkumar Kasirajan 			     (RTC_MON_MASK | RTC_MDAY_MASK | RTC_WDAY_MASK))
345c0a5f4a0SRajkumar Kasirajan 			    == 0x02120000) {
346c0a5f4a0SRajkumar Kasirajan 				time = time | (0x7 << RTC_WDAY_SHIFT);
347c0a5f4a0SRajkumar Kasirajan 				writel(0x2000, ldata->base + RTC_YLR);
348c0a5f4a0SRajkumar Kasirajan 				writel(time, ldata->base + RTC_LR);
349c0a5f4a0SRajkumar Kasirajan 			}
350c0a5f4a0SRajkumar Kasirajan 		}
351c0a5f4a0SRajkumar Kasirajan 	}
352c0a5f4a0SRajkumar Kasirajan 
353eff6dd41SSudeep Holla 	device_init_wakeup(&adev->dev, true);
354b7aff107SAlexandre Belloni 	ldata->rtc = devm_rtc_allocate_device(&adev->dev);
3551eab0feaSZheng Liang 	if (IS_ERR(ldata->rtc)) {
3561eab0feaSZheng Liang 		ret = PTR_ERR(ldata->rtc);
3571eab0feaSZheng Liang 		goto out;
3581eab0feaSZheng Liang 	}
359b7aff107SAlexandre Belloni 
360*ea6af39fSAli Pouladi 	if (!adev->irq[0])
361*ea6af39fSAli Pouladi 		clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features);
362*ea6af39fSAli Pouladi 
363b7aff107SAlexandre Belloni 	ldata->rtc->ops = ops;
36403f2a0e4SAlexandre Belloni 	ldata->rtc->range_min = vendor->range_min;
36503f2a0e4SAlexandre Belloni 	ldata->rtc->range_max = vendor->range_max;
366b7aff107SAlexandre Belloni 
367fdcfd854SBartosz Golaszewski 	ret = devm_rtc_register_device(ldata->rtc);
368b7aff107SAlexandre Belloni 	if (ret)
369273c868eSRussell King 		goto out;
3708ae6e163SDeepak Saxena 
3715b64a296SRussell King 	if (adev->irq[0]) {
3725b64a296SRussell King 		ret = request_irq(adev->irq[0], pl031_interrupt,
3735b64a296SRussell King 				  vendor->irqflags, "rtc-pl031", ldata);
3745b64a296SRussell King 		if (ret)
375b7aff107SAlexandre Belloni 			goto out;
376eff6dd41SSudeep Holla 		dev_pm_set_wake_irq(&adev->dev, adev->irq[0]);
3775b64a296SRussell King 	}
3788ae6e163SDeepak Saxena 	return 0;
3798ae6e163SDeepak Saxena 
3808ae6e163SDeepak Saxena out:
3812dba8518SRussell King 	amba_release_regions(adev);
3822dba8518SRussell King err_req:
383c72881e8SLinus Walleij 
3848ae6e163SDeepak Saxena 	return ret;
3858ae6e163SDeepak Saxena }
3868ae6e163SDeepak Saxena 
387c72881e8SLinus Walleij /* Operations for the original ARM version */
388aff05ed5SLinus Walleij static struct pl031_vendor_data arm_pl031 = {
389aff05ed5SLinus Walleij 	.ops = {
390c72881e8SLinus Walleij 		.read_time = pl031_read_time,
391c72881e8SLinus Walleij 		.set_time = pl031_set_time,
392c72881e8SLinus Walleij 		.read_alarm = pl031_read_alarm,
393c72881e8SLinus Walleij 		.set_alarm = pl031_set_alarm,
394c72881e8SLinus Walleij 		.alarm_irq_enable = pl031_alarm_irq_enable,
395aff05ed5SLinus Walleij 	},
39603f2a0e4SAlexandre Belloni 	.range_max = U32_MAX,
397c72881e8SLinus Walleij };
398c72881e8SLinus Walleij 
399c72881e8SLinus Walleij /* The First ST derivative */
400aff05ed5SLinus Walleij static struct pl031_vendor_data stv1_pl031 = {
401aff05ed5SLinus Walleij 	.ops = {
402c72881e8SLinus Walleij 		.read_time = pl031_read_time,
403c72881e8SLinus Walleij 		.set_time = pl031_set_time,
404c72881e8SLinus Walleij 		.read_alarm = pl031_read_alarm,
405c72881e8SLinus Walleij 		.set_alarm = pl031_set_alarm,
406c72881e8SLinus Walleij 		.alarm_irq_enable = pl031_alarm_irq_enable,
407aff05ed5SLinus Walleij 	},
4081bb457fcSLinus Walleij 	.clockwatch = true,
4091bb457fcSLinus Walleij 	.st_weekday = true,
41003f2a0e4SAlexandre Belloni 	.range_max = U32_MAX,
411c72881e8SLinus Walleij };
412c72881e8SLinus Walleij 
413c72881e8SLinus Walleij /* And the second ST derivative */
414aff05ed5SLinus Walleij static struct pl031_vendor_data stv2_pl031 = {
415aff05ed5SLinus Walleij 	.ops = {
416c72881e8SLinus Walleij 		.read_time = pl031_stv2_read_time,
417c72881e8SLinus Walleij 		.set_time = pl031_stv2_set_time,
418c72881e8SLinus Walleij 		.read_alarm = pl031_stv2_read_alarm,
419c72881e8SLinus Walleij 		.set_alarm = pl031_stv2_set_alarm,
420c72881e8SLinus Walleij 		.alarm_irq_enable = pl031_alarm_irq_enable,
421aff05ed5SLinus Walleij 	},
4221bb457fcSLinus Walleij 	.clockwatch = true,
4231bb457fcSLinus Walleij 	.st_weekday = true,
424559a6fc0SMattias Wallin 	/*
425559a6fc0SMattias Wallin 	 * This variant shares the IRQ with another block and must not
426559a6fc0SMattias Wallin 	 * suspend that IRQ line.
427eff6dd41SSudeep Holla 	 * TODO check if it shares with IRQF_NO_SUSPEND user, else we can
428eff6dd41SSudeep Holla 	 * remove IRQF_COND_SUSPEND
429559a6fc0SMattias Wallin 	 */
430eff6dd41SSudeep Holla 	.irqflags = IRQF_SHARED | IRQF_COND_SUSPEND,
43103f2a0e4SAlexandre Belloni 	.range_min = RTC_TIMESTAMP_BEGIN_0000,
43203f2a0e4SAlexandre Belloni 	.range_max = RTC_TIMESTAMP_END_9999,
433c72881e8SLinus Walleij };
434c72881e8SLinus Walleij 
435eb508b36SRussell King static const struct amba_id pl031_ids[] = {
4368ae6e163SDeepak Saxena 	{
4378ae6e163SDeepak Saxena 		.id = 0x00041031,
4382934d6a8SLinus Walleij 		.mask = 0x000fffff,
439aff05ed5SLinus Walleij 		.data = &arm_pl031,
440c72881e8SLinus Walleij 	},
441c72881e8SLinus Walleij 	/* ST Micro variants */
442c72881e8SLinus Walleij 	{
443c72881e8SLinus Walleij 		.id = 0x00180031,
444c72881e8SLinus Walleij 		.mask = 0x00ffffff,
445aff05ed5SLinus Walleij 		.data = &stv1_pl031,
446c72881e8SLinus Walleij 	},
447c72881e8SLinus Walleij 	{
448c72881e8SLinus Walleij 		.id = 0x00280031,
449c72881e8SLinus Walleij 		.mask = 0x00ffffff,
450aff05ed5SLinus Walleij 		.data = &stv2_pl031,
4512934d6a8SLinus Walleij 	},
4528ae6e163SDeepak Saxena 	{0, 0},
4538ae6e163SDeepak Saxena };
4548ae6e163SDeepak Saxena 
455f5feac2aSDave Martin MODULE_DEVICE_TABLE(amba, pl031_ids);
456f5feac2aSDave Martin 
4578ae6e163SDeepak Saxena static struct amba_driver pl031_driver = {
4588ae6e163SDeepak Saxena 	.drv = {
4598ae6e163SDeepak Saxena 		.name = "rtc-pl031",
4608ae6e163SDeepak Saxena 	},
4618ae6e163SDeepak Saxena 	.id_table = pl031_ids,
4628ae6e163SDeepak Saxena 	.probe = pl031_probe,
4638ae6e163SDeepak Saxena 	.remove = pl031_remove,
4648ae6e163SDeepak Saxena };
4658ae6e163SDeepak Saxena 
4669e5ed094Sviresh kumar module_amba_driver(pl031_driver);
4678ae6e163SDeepak Saxena 
46827675ef0SLeo Yan MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
4698ae6e163SDeepak Saxena MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver");
4708ae6e163SDeepak Saxena MODULE_LICENSE("GPL");
471