xref: /openbmc/linux/drivers/rtc/rtc-pic32.c (revision c3d12a10)
1e91b94fdSAlexandre Belloni // SPDX-License-Identifier: GPL-2.0+
251aa905cSJoshua Henderson /*
351aa905cSJoshua Henderson  * PIC32 RTC driver
451aa905cSJoshua Henderson  *
551aa905cSJoshua Henderson  * Joshua Henderson <joshua.henderson@microchip.com>
651aa905cSJoshua Henderson  * Copyright (C) 2016 Microchip Technology Inc.  All rights reserved.
751aa905cSJoshua Henderson  *
851aa905cSJoshua Henderson  */
951aa905cSJoshua Henderson #include <linux/init.h>
1051aa905cSJoshua Henderson #include <linux/module.h>
1151aa905cSJoshua Henderson #include <linux/of.h>
1251aa905cSJoshua Henderson #include <linux/platform_device.h>
1351aa905cSJoshua Henderson #include <linux/io.h>
1451aa905cSJoshua Henderson #include <linux/slab.h>
1551aa905cSJoshua Henderson #include <linux/clk.h>
1651aa905cSJoshua Henderson #include <linux/rtc.h>
1751aa905cSJoshua Henderson #include <linux/bcd.h>
1851aa905cSJoshua Henderson 
1951aa905cSJoshua Henderson #include <asm/mach-pic32/pic32.h>
2051aa905cSJoshua Henderson 
2151aa905cSJoshua Henderson #define PIC32_RTCCON		0x00
2251aa905cSJoshua Henderson #define PIC32_RTCCON_ON		BIT(15)
2351aa905cSJoshua Henderson #define PIC32_RTCCON_SIDL	BIT(13)
2451aa905cSJoshua Henderson #define PIC32_RTCCON_RTCCLKSEL	(3 << 9)
2551aa905cSJoshua Henderson #define PIC32_RTCCON_RTCCLKON	BIT(6)
2651aa905cSJoshua Henderson #define PIC32_RTCCON_RTCWREN	BIT(3)
2751aa905cSJoshua Henderson #define PIC32_RTCCON_RTCSYNC	BIT(2)
2851aa905cSJoshua Henderson #define PIC32_RTCCON_HALFSEC	BIT(1)
2951aa905cSJoshua Henderson #define PIC32_RTCCON_RTCOE	BIT(0)
3051aa905cSJoshua Henderson 
3151aa905cSJoshua Henderson #define PIC32_RTCALRM		0x10
3251aa905cSJoshua Henderson #define PIC32_RTCALRM_ALRMEN	BIT(15)
3351aa905cSJoshua Henderson #define PIC32_RTCALRM_CHIME	BIT(14)
3451aa905cSJoshua Henderson #define PIC32_RTCALRM_PIV	BIT(13)
3551aa905cSJoshua Henderson #define PIC32_RTCALRM_ALARMSYNC	BIT(12)
3651aa905cSJoshua Henderson #define PIC32_RTCALRM_AMASK	0x0F00
3751aa905cSJoshua Henderson #define PIC32_RTCALRM_ARPT	0xFF
3851aa905cSJoshua Henderson 
3951aa905cSJoshua Henderson #define PIC32_RTCHOUR		0x23
4051aa905cSJoshua Henderson #define PIC32_RTCMIN		0x22
4151aa905cSJoshua Henderson #define PIC32_RTCSEC		0x21
4251aa905cSJoshua Henderson #define PIC32_RTCYEAR		0x33
4351aa905cSJoshua Henderson #define PIC32_RTCMON		0x32
4451aa905cSJoshua Henderson #define PIC32_RTCDAY		0x31
4551aa905cSJoshua Henderson 
4651aa905cSJoshua Henderson #define PIC32_ALRMTIME		0x40
4751aa905cSJoshua Henderson #define PIC32_ALRMDATE		0x50
4851aa905cSJoshua Henderson 
4951aa905cSJoshua Henderson #define PIC32_ALRMHOUR		0x43
5051aa905cSJoshua Henderson #define PIC32_ALRMMIN		0x42
5151aa905cSJoshua Henderson #define PIC32_ALRMSEC		0x41
5251aa905cSJoshua Henderson #define PIC32_ALRMYEAR		0x53
5351aa905cSJoshua Henderson #define PIC32_ALRMMON		0x52
5451aa905cSJoshua Henderson #define PIC32_ALRMDAY		0x51
5551aa905cSJoshua Henderson 
5651aa905cSJoshua Henderson struct pic32_rtc_dev {
5751aa905cSJoshua Henderson 	struct rtc_device	*rtc;
5851aa905cSJoshua Henderson 	void __iomem		*reg_base;
5951aa905cSJoshua Henderson 	struct clk		*clk;
6051aa905cSJoshua Henderson 	spinlock_t		alarm_lock;
6151aa905cSJoshua Henderson 	int			alarm_irq;
6251aa905cSJoshua Henderson 	bool			alarm_clk_enabled;
6351aa905cSJoshua Henderson };
6451aa905cSJoshua Henderson 
pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev * pdata,bool enable)6551aa905cSJoshua Henderson static void pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev *pdata,
6651aa905cSJoshua Henderson 				       bool enable)
6751aa905cSJoshua Henderson {
6851aa905cSJoshua Henderson 	unsigned long flags;
6951aa905cSJoshua Henderson 
7051aa905cSJoshua Henderson 	spin_lock_irqsave(&pdata->alarm_lock, flags);
7151aa905cSJoshua Henderson 	if (enable) {
7251aa905cSJoshua Henderson 		if (!pdata->alarm_clk_enabled) {
7351aa905cSJoshua Henderson 			clk_enable(pdata->clk);
7451aa905cSJoshua Henderson 			pdata->alarm_clk_enabled = true;
7551aa905cSJoshua Henderson 		}
7651aa905cSJoshua Henderson 	} else {
7751aa905cSJoshua Henderson 		if (pdata->alarm_clk_enabled) {
7851aa905cSJoshua Henderson 			clk_disable(pdata->clk);
7951aa905cSJoshua Henderson 			pdata->alarm_clk_enabled = false;
8051aa905cSJoshua Henderson 		}
8151aa905cSJoshua Henderson 	}
8251aa905cSJoshua Henderson 	spin_unlock_irqrestore(&pdata->alarm_lock, flags);
8351aa905cSJoshua Henderson }
8451aa905cSJoshua Henderson 
pic32_rtc_alarmirq(int irq,void * id)8551aa905cSJoshua Henderson static irqreturn_t pic32_rtc_alarmirq(int irq, void *id)
8651aa905cSJoshua Henderson {
8751aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = (struct pic32_rtc_dev *)id;
8851aa905cSJoshua Henderson 
8951aa905cSJoshua Henderson 	clk_enable(pdata->clk);
9051aa905cSJoshua Henderson 	rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF);
9151aa905cSJoshua Henderson 	clk_disable(pdata->clk);
9251aa905cSJoshua Henderson 
9351aa905cSJoshua Henderson 	pic32_rtc_alarm_clk_enable(pdata, false);
9451aa905cSJoshua Henderson 
9551aa905cSJoshua Henderson 	return IRQ_HANDLED;
9651aa905cSJoshua Henderson }
9751aa905cSJoshua Henderson 
pic32_rtc_setaie(struct device * dev,unsigned int enabled)9851aa905cSJoshua Henderson static int pic32_rtc_setaie(struct device *dev, unsigned int enabled)
9951aa905cSJoshua Henderson {
10051aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
10151aa905cSJoshua Henderson 	void __iomem *base = pdata->reg_base;
10251aa905cSJoshua Henderson 
10351aa905cSJoshua Henderson 	clk_enable(pdata->clk);
10451aa905cSJoshua Henderson 
10551aa905cSJoshua Henderson 	writel(PIC32_RTCALRM_ALRMEN,
10651aa905cSJoshua Henderson 	       base + (enabled ? PIC32_SET(PIC32_RTCALRM) :
10751aa905cSJoshua Henderson 		       PIC32_CLR(PIC32_RTCALRM)));
10851aa905cSJoshua Henderson 
10951aa905cSJoshua Henderson 	clk_disable(pdata->clk);
11051aa905cSJoshua Henderson 
11151aa905cSJoshua Henderson 	pic32_rtc_alarm_clk_enable(pdata, enabled);
11251aa905cSJoshua Henderson 
11351aa905cSJoshua Henderson 	return 0;
11451aa905cSJoshua Henderson }
11551aa905cSJoshua Henderson 
pic32_rtc_setfreq(struct device * dev,int freq)11651aa905cSJoshua Henderson static int pic32_rtc_setfreq(struct device *dev, int freq)
11751aa905cSJoshua Henderson {
11851aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
11951aa905cSJoshua Henderson 	void __iomem *base = pdata->reg_base;
12051aa905cSJoshua Henderson 
12151aa905cSJoshua Henderson 	clk_enable(pdata->clk);
12251aa905cSJoshua Henderson 
12351aa905cSJoshua Henderson 	writel(PIC32_RTCALRM_AMASK, base + PIC32_CLR(PIC32_RTCALRM));
12451aa905cSJoshua Henderson 	writel(freq << 8, base + PIC32_SET(PIC32_RTCALRM));
12551aa905cSJoshua Henderson 	writel(PIC32_RTCALRM_CHIME, base + PIC32_SET(PIC32_RTCALRM));
12651aa905cSJoshua Henderson 
12751aa905cSJoshua Henderson 	clk_disable(pdata->clk);
12851aa905cSJoshua Henderson 
12951aa905cSJoshua Henderson 	return 0;
13051aa905cSJoshua Henderson }
13151aa905cSJoshua Henderson 
pic32_rtc_gettime(struct device * dev,struct rtc_time * rtc_tm)13251aa905cSJoshua Henderson static int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
13351aa905cSJoshua Henderson {
13451aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
13551aa905cSJoshua Henderson 	void __iomem *base = pdata->reg_base;
13651aa905cSJoshua Henderson 	unsigned int tries = 0;
13751aa905cSJoshua Henderson 
13851aa905cSJoshua Henderson 	clk_enable(pdata->clk);
13951aa905cSJoshua Henderson 
14051aa905cSJoshua Henderson 	do {
14151aa905cSJoshua Henderson 		rtc_tm->tm_hour = readb(base + PIC32_RTCHOUR);
14251aa905cSJoshua Henderson 		rtc_tm->tm_min = readb(base + PIC32_RTCMIN);
14351aa905cSJoshua Henderson 		rtc_tm->tm_mon  = readb(base + PIC32_RTCMON);
14451aa905cSJoshua Henderson 		rtc_tm->tm_mday = readb(base + PIC32_RTCDAY);
14551aa905cSJoshua Henderson 		rtc_tm->tm_year = readb(base + PIC32_RTCYEAR);
14651aa905cSJoshua Henderson 		rtc_tm->tm_sec  = readb(base + PIC32_RTCSEC);
14751aa905cSJoshua Henderson 
14851aa905cSJoshua Henderson 		/*
14951aa905cSJoshua Henderson 		 * The only way to work out whether the system was mid-update
15051aa905cSJoshua Henderson 		 * when we read it is to check the second counter, and if it
15151aa905cSJoshua Henderson 		 * is zero, then we re-try the entire read.
15251aa905cSJoshua Henderson 		 */
15351aa905cSJoshua Henderson 		tries += 1;
15451aa905cSJoshua Henderson 	} while (rtc_tm->tm_sec == 0 && tries < 2);
15551aa905cSJoshua Henderson 
15651aa905cSJoshua Henderson 	rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
15751aa905cSJoshua Henderson 	rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
15851aa905cSJoshua Henderson 	rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
15951aa905cSJoshua Henderson 	rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
16051aa905cSJoshua Henderson 	rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon) - 1;
16151aa905cSJoshua Henderson 	rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
16251aa905cSJoshua Henderson 
16351aa905cSJoshua Henderson 	rtc_tm->tm_year += 100;
16451aa905cSJoshua Henderson 
16593206f93SAndy Shevchenko 	dev_dbg(dev, "read time %ptR\n", rtc_tm);
16651aa905cSJoshua Henderson 
16751aa905cSJoshua Henderson 	clk_disable(pdata->clk);
16822652ba7SAlexandre Belloni 	return 0;
16951aa905cSJoshua Henderson }
17051aa905cSJoshua Henderson 
pic32_rtc_settime(struct device * dev,struct rtc_time * tm)17151aa905cSJoshua Henderson static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm)
17251aa905cSJoshua Henderson {
17351aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
17451aa905cSJoshua Henderson 	void __iomem *base = pdata->reg_base;
17551aa905cSJoshua Henderson 
17693206f93SAndy Shevchenko 	dev_dbg(dev, "set time %ptR\n", tm);
17751aa905cSJoshua Henderson 
17851aa905cSJoshua Henderson 	clk_enable(pdata->clk);
17951aa905cSJoshua Henderson 	writeb(bin2bcd(tm->tm_sec),  base + PIC32_RTCSEC);
18051aa905cSJoshua Henderson 	writeb(bin2bcd(tm->tm_min),  base + PIC32_RTCMIN);
18151aa905cSJoshua Henderson 	writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR);
18251aa905cSJoshua Henderson 	writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY);
18351aa905cSJoshua Henderson 	writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON);
184c145e5f4SAlexandre Belloni 	writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR);
18551aa905cSJoshua Henderson 	clk_disable(pdata->clk);
18651aa905cSJoshua Henderson 
18751aa905cSJoshua Henderson 	return 0;
18851aa905cSJoshua Henderson }
18951aa905cSJoshua Henderson 
pic32_rtc_getalarm(struct device * dev,struct rtc_wkalrm * alrm)19051aa905cSJoshua Henderson static int pic32_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
19151aa905cSJoshua Henderson {
19251aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
19351aa905cSJoshua Henderson 	struct rtc_time *alm_tm = &alrm->time;
19451aa905cSJoshua Henderson 	void __iomem *base = pdata->reg_base;
19551aa905cSJoshua Henderson 	unsigned int alm_en;
19651aa905cSJoshua Henderson 
19751aa905cSJoshua Henderson 	clk_enable(pdata->clk);
19851aa905cSJoshua Henderson 	alm_tm->tm_sec  = readb(base + PIC32_ALRMSEC);
19951aa905cSJoshua Henderson 	alm_tm->tm_min  = readb(base + PIC32_ALRMMIN);
20051aa905cSJoshua Henderson 	alm_tm->tm_hour = readb(base + PIC32_ALRMHOUR);
20151aa905cSJoshua Henderson 	alm_tm->tm_mon  = readb(base + PIC32_ALRMMON);
20251aa905cSJoshua Henderson 	alm_tm->tm_mday = readb(base + PIC32_ALRMDAY);
20351aa905cSJoshua Henderson 	alm_tm->tm_year = readb(base + PIC32_ALRMYEAR);
20451aa905cSJoshua Henderson 
20551aa905cSJoshua Henderson 	alm_en = readb(base + PIC32_RTCALRM);
20651aa905cSJoshua Henderson 
20751aa905cSJoshua Henderson 	alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0;
20851aa905cSJoshua Henderson 
20993206f93SAndy Shevchenko 	dev_dbg(dev, "getalarm: %d, %ptR\n", alm_en, alm_tm);
21051aa905cSJoshua Henderson 
21151aa905cSJoshua Henderson 	alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
21251aa905cSJoshua Henderson 	alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
21351aa905cSJoshua Henderson 	alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
21451aa905cSJoshua Henderson 	alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
21551aa905cSJoshua Henderson 	alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1;
21651aa905cSJoshua Henderson 	alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
21751aa905cSJoshua Henderson 
21851aa905cSJoshua Henderson 	clk_disable(pdata->clk);
21951aa905cSJoshua Henderson 	return 0;
22051aa905cSJoshua Henderson }
22151aa905cSJoshua Henderson 
pic32_rtc_setalarm(struct device * dev,struct rtc_wkalrm * alrm)22251aa905cSJoshua Henderson static int pic32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
22351aa905cSJoshua Henderson {
22451aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
22551aa905cSJoshua Henderson 	struct rtc_time *tm = &alrm->time;
22651aa905cSJoshua Henderson 	void __iomem *base = pdata->reg_base;
22751aa905cSJoshua Henderson 
22851aa905cSJoshua Henderson 	clk_enable(pdata->clk);
22993206f93SAndy Shevchenko 	dev_dbg(dev, "setalarm: %d, %ptR\n", alrm->enabled, tm);
23051aa905cSJoshua Henderson 
23151aa905cSJoshua Henderson 	writel(0x00, base + PIC32_ALRMTIME);
23251aa905cSJoshua Henderson 	writel(0x00, base + PIC32_ALRMDATE);
23351aa905cSJoshua Henderson 
23451aa905cSJoshua Henderson 	pic32_rtc_setaie(dev, alrm->enabled);
23551aa905cSJoshua Henderson 
23651aa905cSJoshua Henderson 	clk_disable(pdata->clk);
23751aa905cSJoshua Henderson 	return 0;
23851aa905cSJoshua Henderson }
23951aa905cSJoshua Henderson 
pic32_rtc_proc(struct device * dev,struct seq_file * seq)24051aa905cSJoshua Henderson static int pic32_rtc_proc(struct device *dev, struct seq_file *seq)
24151aa905cSJoshua Henderson {
24251aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
24351aa905cSJoshua Henderson 	void __iomem *base = pdata->reg_base;
24451aa905cSJoshua Henderson 	unsigned int repeat;
24551aa905cSJoshua Henderson 
24651aa905cSJoshua Henderson 	clk_enable(pdata->clk);
24751aa905cSJoshua Henderson 
24851aa905cSJoshua Henderson 	repeat = readw(base + PIC32_RTCALRM);
24951aa905cSJoshua Henderson 	repeat &= PIC32_RTCALRM_ARPT;
25051aa905cSJoshua Henderson 	seq_printf(seq, "periodic_IRQ\t: %s\n", repeat  ? "yes" : "no");
25151aa905cSJoshua Henderson 
25251aa905cSJoshua Henderson 	clk_disable(pdata->clk);
25351aa905cSJoshua Henderson 	return 0;
25451aa905cSJoshua Henderson }
25551aa905cSJoshua Henderson 
25651aa905cSJoshua Henderson static const struct rtc_class_ops pic32_rtcops = {
25751aa905cSJoshua Henderson 	.read_time	  = pic32_rtc_gettime,
25851aa905cSJoshua Henderson 	.set_time	  = pic32_rtc_settime,
25951aa905cSJoshua Henderson 	.read_alarm	  = pic32_rtc_getalarm,
26051aa905cSJoshua Henderson 	.set_alarm	  = pic32_rtc_setalarm,
26151aa905cSJoshua Henderson 	.proc		  = pic32_rtc_proc,
26251aa905cSJoshua Henderson 	.alarm_irq_enable = pic32_rtc_setaie,
26351aa905cSJoshua Henderson };
26451aa905cSJoshua Henderson 
pic32_rtc_enable(struct pic32_rtc_dev * pdata,int en)26551aa905cSJoshua Henderson static void pic32_rtc_enable(struct pic32_rtc_dev *pdata, int en)
26651aa905cSJoshua Henderson {
26751aa905cSJoshua Henderson 	void __iomem *base = pdata->reg_base;
26851aa905cSJoshua Henderson 
26951aa905cSJoshua Henderson 	if (!base)
27051aa905cSJoshua Henderson 		return;
27151aa905cSJoshua Henderson 
27251aa905cSJoshua Henderson 	clk_enable(pdata->clk);
27351aa905cSJoshua Henderson 	if (!en) {
27451aa905cSJoshua Henderson 		writel(PIC32_RTCCON_ON, base + PIC32_CLR(PIC32_RTCCON));
27551aa905cSJoshua Henderson 	} else {
27651aa905cSJoshua Henderson 		pic32_syskey_unlock();
27751aa905cSJoshua Henderson 
27851aa905cSJoshua Henderson 		writel(PIC32_RTCCON_RTCWREN, base + PIC32_SET(PIC32_RTCCON));
27951aa905cSJoshua Henderson 		writel(3 << 9, base + PIC32_CLR(PIC32_RTCCON));
28051aa905cSJoshua Henderson 
28151aa905cSJoshua Henderson 		if (!(readl(base + PIC32_RTCCON) & PIC32_RTCCON_ON))
28251aa905cSJoshua Henderson 			writel(PIC32_RTCCON_ON, base + PIC32_SET(PIC32_RTCCON));
28351aa905cSJoshua Henderson 	}
28451aa905cSJoshua Henderson 	clk_disable(pdata->clk);
28551aa905cSJoshua Henderson }
28651aa905cSJoshua Henderson 
pic32_rtc_remove(struct platform_device * pdev)287*c3d12a10SUwe Kleine-König static void pic32_rtc_remove(struct platform_device *pdev)
28851aa905cSJoshua Henderson {
28951aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata = platform_get_drvdata(pdev);
29051aa905cSJoshua Henderson 
29151aa905cSJoshua Henderson 	pic32_rtc_setaie(&pdev->dev, 0);
29251aa905cSJoshua Henderson 	clk_unprepare(pdata->clk);
29351aa905cSJoshua Henderson 	pdata->clk = NULL;
29451aa905cSJoshua Henderson }
29551aa905cSJoshua Henderson 
pic32_rtc_probe(struct platform_device * pdev)29651aa905cSJoshua Henderson static int pic32_rtc_probe(struct platform_device *pdev)
29751aa905cSJoshua Henderson {
29851aa905cSJoshua Henderson 	struct pic32_rtc_dev *pdata;
29951aa905cSJoshua Henderson 	int ret;
30051aa905cSJoshua Henderson 
30151aa905cSJoshua Henderson 	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
30251aa905cSJoshua Henderson 	if (!pdata)
30351aa905cSJoshua Henderson 		return -ENOMEM;
30451aa905cSJoshua Henderson 
30551aa905cSJoshua Henderson 	platform_set_drvdata(pdev, pdata);
30651aa905cSJoshua Henderson 
30751aa905cSJoshua Henderson 	pdata->alarm_irq = platform_get_irq(pdev, 0);
308faac9102SStephen Boyd 	if (pdata->alarm_irq < 0)
30951aa905cSJoshua Henderson 		return pdata->alarm_irq;
31051aa905cSJoshua Henderson 
31109ef18bcSYueHaibing 	pdata->reg_base = devm_platform_ioremap_resource(pdev, 0);
31251aa905cSJoshua Henderson 	if (IS_ERR(pdata->reg_base))
31351aa905cSJoshua Henderson 		return PTR_ERR(pdata->reg_base);
31451aa905cSJoshua Henderson 
31551aa905cSJoshua Henderson 	pdata->clk = devm_clk_get(&pdev->dev, NULL);
31651aa905cSJoshua Henderson 	if (IS_ERR(pdata->clk)) {
31751aa905cSJoshua Henderson 		dev_err(&pdev->dev, "failed to find rtc clock source\n");
31851aa905cSJoshua Henderson 		ret = PTR_ERR(pdata->clk);
31951aa905cSJoshua Henderson 		pdata->clk = NULL;
32051aa905cSJoshua Henderson 		return ret;
32151aa905cSJoshua Henderson 	}
32251aa905cSJoshua Henderson 
32351aa905cSJoshua Henderson 	spin_lock_init(&pdata->alarm_lock);
32451aa905cSJoshua Henderson 
32590cd5c88SGaosheng Cui 	pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
32690cd5c88SGaosheng Cui 	if (IS_ERR(pdata->rtc))
32790cd5c88SGaosheng Cui 		return PTR_ERR(pdata->rtc);
32890cd5c88SGaosheng Cui 
32951aa905cSJoshua Henderson 	clk_prepare_enable(pdata->clk);
33051aa905cSJoshua Henderson 
33151aa905cSJoshua Henderson 	pic32_rtc_enable(pdata, 1);
33251aa905cSJoshua Henderson 
33351aa905cSJoshua Henderson 	device_init_wakeup(&pdev->dev, 1);
33451aa905cSJoshua Henderson 
3356515e23bSAlexandre Belloni 	pdata->rtc->ops = &pic32_rtcops;
336c145e5f4SAlexandre Belloni 	pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
337c145e5f4SAlexandre Belloni 	pdata->rtc->range_max = RTC_TIMESTAMP_END_2099;
3386515e23bSAlexandre Belloni 
339fdcfd854SBartosz Golaszewski 	ret = devm_rtc_register_device(pdata->rtc);
3406515e23bSAlexandre Belloni 	if (ret)
34151aa905cSJoshua Henderson 		goto err_nortc;
34251aa905cSJoshua Henderson 
34351aa905cSJoshua Henderson 	pdata->rtc->max_user_freq = 128;
34451aa905cSJoshua Henderson 
34551aa905cSJoshua Henderson 	pic32_rtc_setfreq(&pdev->dev, 1);
34651aa905cSJoshua Henderson 	ret = devm_request_irq(&pdev->dev, pdata->alarm_irq,
34751aa905cSJoshua Henderson 			       pic32_rtc_alarmirq, 0,
34851aa905cSJoshua Henderson 			       dev_name(&pdev->dev), pdata);
34951aa905cSJoshua Henderson 	if (ret) {
35051aa905cSJoshua Henderson 		dev_err(&pdev->dev,
35151aa905cSJoshua Henderson 			"IRQ %d error %d\n", pdata->alarm_irq, ret);
35251aa905cSJoshua Henderson 		goto err_nortc;
35351aa905cSJoshua Henderson 	}
35451aa905cSJoshua Henderson 
35551aa905cSJoshua Henderson 	clk_disable(pdata->clk);
35651aa905cSJoshua Henderson 
35751aa905cSJoshua Henderson 	return 0;
35851aa905cSJoshua Henderson 
35951aa905cSJoshua Henderson err_nortc:
36051aa905cSJoshua Henderson 	pic32_rtc_enable(pdata, 0);
36151aa905cSJoshua Henderson 	clk_disable_unprepare(pdata->clk);
36251aa905cSJoshua Henderson 
36351aa905cSJoshua Henderson 	return ret;
36451aa905cSJoshua Henderson }
36551aa905cSJoshua Henderson 
36651aa905cSJoshua Henderson static const struct of_device_id pic32_rtc_dt_ids[] = {
36751aa905cSJoshua Henderson 	{ .compatible = "microchip,pic32mzda-rtc" },
36851aa905cSJoshua Henderson 	{ /* sentinel */ }
36951aa905cSJoshua Henderson };
37051aa905cSJoshua Henderson MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids);
37151aa905cSJoshua Henderson 
37251aa905cSJoshua Henderson static struct platform_driver pic32_rtc_driver = {
37351aa905cSJoshua Henderson 	.probe		= pic32_rtc_probe,
374*c3d12a10SUwe Kleine-König 	.remove_new	= pic32_rtc_remove,
37551aa905cSJoshua Henderson 	.driver		= {
37651aa905cSJoshua Henderson 		.name	= "pic32-rtc",
37751aa905cSJoshua Henderson 		.of_match_table	= of_match_ptr(pic32_rtc_dt_ids),
37851aa905cSJoshua Henderson 	},
37951aa905cSJoshua Henderson };
38051aa905cSJoshua Henderson module_platform_driver(pic32_rtc_driver);
38151aa905cSJoshua Henderson 
38251aa905cSJoshua Henderson MODULE_DESCRIPTION("Microchip PIC32 RTC Driver");
38351aa905cSJoshua Henderson MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");
38451aa905cSJoshua Henderson MODULE_LICENSE("GPL");
385