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