brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · 4f85e0c Raw
385 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * PIC32 RTC driver4 *5 * Joshua Henderson <joshua.henderson@microchip.com>6 * Copyright (C) 2016 Microchip Technology Inc.  All rights reserved.7 *8 */9#include <linux/init.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/platform_device.h>13#include <linux/io.h>14#include <linux/slab.h>15#include <linux/clk.h>16#include <linux/rtc.h>17#include <linux/bcd.h>18 19#include <asm/mach-pic32/pic32.h>20 21#define PIC32_RTCCON		0x0022#define PIC32_RTCCON_ON		BIT(15)23#define PIC32_RTCCON_SIDL	BIT(13)24#define PIC32_RTCCON_RTCCLKSEL	(3 << 9)25#define PIC32_RTCCON_RTCCLKON	BIT(6)26#define PIC32_RTCCON_RTCWREN	BIT(3)27#define PIC32_RTCCON_RTCSYNC	BIT(2)28#define PIC32_RTCCON_HALFSEC	BIT(1)29#define PIC32_RTCCON_RTCOE	BIT(0)30 31#define PIC32_RTCALRM		0x1032#define PIC32_RTCALRM_ALRMEN	BIT(15)33#define PIC32_RTCALRM_CHIME	BIT(14)34#define PIC32_RTCALRM_PIV	BIT(13)35#define PIC32_RTCALRM_ALARMSYNC	BIT(12)36#define PIC32_RTCALRM_AMASK	0x0F0037#define PIC32_RTCALRM_ARPT	0xFF38 39#define PIC32_RTCHOUR		0x2340#define PIC32_RTCMIN		0x2241#define PIC32_RTCSEC		0x2142#define PIC32_RTCYEAR		0x3343#define PIC32_RTCMON		0x3244#define PIC32_RTCDAY		0x3145 46#define PIC32_ALRMTIME		0x4047#define PIC32_ALRMDATE		0x5048 49#define PIC32_ALRMHOUR		0x4350#define PIC32_ALRMMIN		0x4251#define PIC32_ALRMSEC		0x4152#define PIC32_ALRMYEAR		0x5353#define PIC32_ALRMMON		0x5254#define PIC32_ALRMDAY		0x5155 56struct pic32_rtc_dev {57	struct rtc_device	*rtc;58	void __iomem		*reg_base;59	struct clk		*clk;60	spinlock_t		alarm_lock;61	int			alarm_irq;62	bool			alarm_clk_enabled;63};64 65static void pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev *pdata,66				       bool enable)67{68	unsigned long flags;69 70	spin_lock_irqsave(&pdata->alarm_lock, flags);71	if (enable) {72		if (!pdata->alarm_clk_enabled) {73			clk_enable(pdata->clk);74			pdata->alarm_clk_enabled = true;75		}76	} else {77		if (pdata->alarm_clk_enabled) {78			clk_disable(pdata->clk);79			pdata->alarm_clk_enabled = false;80		}81	}82	spin_unlock_irqrestore(&pdata->alarm_lock, flags);83}84 85static irqreturn_t pic32_rtc_alarmirq(int irq, void *id)86{87	struct pic32_rtc_dev *pdata = (struct pic32_rtc_dev *)id;88 89	clk_enable(pdata->clk);90	rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF);91	clk_disable(pdata->clk);92 93	pic32_rtc_alarm_clk_enable(pdata, false);94 95	return IRQ_HANDLED;96}97 98static int pic32_rtc_setaie(struct device *dev, unsigned int enabled)99{100	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);101	void __iomem *base = pdata->reg_base;102 103	clk_enable(pdata->clk);104 105	writel(PIC32_RTCALRM_ALRMEN,106	       base + (enabled ? PIC32_SET(PIC32_RTCALRM) :107		       PIC32_CLR(PIC32_RTCALRM)));108 109	clk_disable(pdata->clk);110 111	pic32_rtc_alarm_clk_enable(pdata, enabled);112 113	return 0;114}115 116static int pic32_rtc_setfreq(struct device *dev, int freq)117{118	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);119	void __iomem *base = pdata->reg_base;120 121	clk_enable(pdata->clk);122 123	writel(PIC32_RTCALRM_AMASK, base + PIC32_CLR(PIC32_RTCALRM));124	writel(freq << 8, base + PIC32_SET(PIC32_RTCALRM));125	writel(PIC32_RTCALRM_CHIME, base + PIC32_SET(PIC32_RTCALRM));126 127	clk_disable(pdata->clk);128 129	return 0;130}131 132static int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)133{134	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);135	void __iomem *base = pdata->reg_base;136	unsigned int tries = 0;137 138	clk_enable(pdata->clk);139 140	do {141		rtc_tm->tm_hour = readb(base + PIC32_RTCHOUR);142		rtc_tm->tm_min = readb(base + PIC32_RTCMIN);143		rtc_tm->tm_mon  = readb(base + PIC32_RTCMON);144		rtc_tm->tm_mday = readb(base + PIC32_RTCDAY);145		rtc_tm->tm_year = readb(base + PIC32_RTCYEAR);146		rtc_tm->tm_sec  = readb(base + PIC32_RTCSEC);147 148		/*149		 * The only way to work out whether the system was mid-update150		 * when we read it is to check the second counter, and if it151		 * is zero, then we re-try the entire read.152		 */153		tries += 1;154	} while (rtc_tm->tm_sec == 0 && tries < 2);155 156	rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);157	rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);158	rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);159	rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);160	rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon) - 1;161	rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);162 163	rtc_tm->tm_year += 100;164 165	dev_dbg(dev, "read time %ptR\n", rtc_tm);166 167	clk_disable(pdata->clk);168	return 0;169}170 171static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm)172{173	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);174	void __iomem *base = pdata->reg_base;175 176	dev_dbg(dev, "set time %ptR\n", tm);177 178	clk_enable(pdata->clk);179	writeb(bin2bcd(tm->tm_sec),  base + PIC32_RTCSEC);180	writeb(bin2bcd(tm->tm_min),  base + PIC32_RTCMIN);181	writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR);182	writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY);183	writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON);184	writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR);185	clk_disable(pdata->clk);186 187	return 0;188}189 190static int pic32_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)191{192	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);193	struct rtc_time *alm_tm = &alrm->time;194	void __iomem *base = pdata->reg_base;195	unsigned int alm_en;196 197	clk_enable(pdata->clk);198	alm_tm->tm_sec  = readb(base + PIC32_ALRMSEC);199	alm_tm->tm_min  = readb(base + PIC32_ALRMMIN);200	alm_tm->tm_hour = readb(base + PIC32_ALRMHOUR);201	alm_tm->tm_mon  = readb(base + PIC32_ALRMMON);202	alm_tm->tm_mday = readb(base + PIC32_ALRMDAY);203	alm_tm->tm_year = readb(base + PIC32_ALRMYEAR);204 205	alm_en = readb(base + PIC32_RTCALRM);206 207	alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0;208 209	dev_dbg(dev, "getalarm: %d, %ptR\n", alm_en, alm_tm);210 211	alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);212	alm_tm->tm_min = bcd2bin(alm_tm->tm_min);213	alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);214	alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);215	alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1;216	alm_tm->tm_year = bcd2bin(alm_tm->tm_year);217 218	clk_disable(pdata->clk);219	return 0;220}221 222static int pic32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)223{224	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);225	struct rtc_time *tm = &alrm->time;226	void __iomem *base = pdata->reg_base;227 228	clk_enable(pdata->clk);229	dev_dbg(dev, "setalarm: %d, %ptR\n", alrm->enabled, tm);230 231	writel(0x00, base + PIC32_ALRMTIME);232	writel(0x00, base + PIC32_ALRMDATE);233 234	pic32_rtc_setaie(dev, alrm->enabled);235 236	clk_disable(pdata->clk);237	return 0;238}239 240static int pic32_rtc_proc(struct device *dev, struct seq_file *seq)241{242	struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);243	void __iomem *base = pdata->reg_base;244	unsigned int repeat;245 246	clk_enable(pdata->clk);247 248	repeat = readw(base + PIC32_RTCALRM);249	repeat &= PIC32_RTCALRM_ARPT;250	seq_printf(seq, "periodic_IRQ\t: %s\n", repeat  ? "yes" : "no");251 252	clk_disable(pdata->clk);253	return 0;254}255 256static const struct rtc_class_ops pic32_rtcops = {257	.read_time	  = pic32_rtc_gettime,258	.set_time	  = pic32_rtc_settime,259	.read_alarm	  = pic32_rtc_getalarm,260	.set_alarm	  = pic32_rtc_setalarm,261	.proc		  = pic32_rtc_proc,262	.alarm_irq_enable = pic32_rtc_setaie,263};264 265static void pic32_rtc_enable(struct pic32_rtc_dev *pdata, int en)266{267	void __iomem *base = pdata->reg_base;268 269	if (!base)270		return;271 272	clk_enable(pdata->clk);273	if (!en) {274		writel(PIC32_RTCCON_ON, base + PIC32_CLR(PIC32_RTCCON));275	} else {276		pic32_syskey_unlock();277 278		writel(PIC32_RTCCON_RTCWREN, base + PIC32_SET(PIC32_RTCCON));279		writel(3 << 9, base + PIC32_CLR(PIC32_RTCCON));280 281		if (!(readl(base + PIC32_RTCCON) & PIC32_RTCCON_ON))282			writel(PIC32_RTCCON_ON, base + PIC32_SET(PIC32_RTCCON));283	}284	clk_disable(pdata->clk);285}286 287static void pic32_rtc_remove(struct platform_device *pdev)288{289	struct pic32_rtc_dev *pdata = platform_get_drvdata(pdev);290 291	pic32_rtc_setaie(&pdev->dev, 0);292	clk_unprepare(pdata->clk);293	pdata->clk = NULL;294}295 296static int pic32_rtc_probe(struct platform_device *pdev)297{298	struct pic32_rtc_dev *pdata;299	int ret;300 301	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);302	if (!pdata)303		return -ENOMEM;304 305	platform_set_drvdata(pdev, pdata);306 307	pdata->alarm_irq = platform_get_irq(pdev, 0);308	if (pdata->alarm_irq < 0)309		return pdata->alarm_irq;310 311	pdata->reg_base = devm_platform_ioremap_resource(pdev, 0);312	if (IS_ERR(pdata->reg_base))313		return PTR_ERR(pdata->reg_base);314 315	pdata->clk = devm_clk_get(&pdev->dev, NULL);316	if (IS_ERR(pdata->clk)) {317		dev_err(&pdev->dev, "failed to find rtc clock source\n");318		ret = PTR_ERR(pdata->clk);319		pdata->clk = NULL;320		return ret;321	}322 323	spin_lock_init(&pdata->alarm_lock);324 325	pdata->rtc = devm_rtc_allocate_device(&pdev->dev);326	if (IS_ERR(pdata->rtc))327		return PTR_ERR(pdata->rtc);328 329	clk_prepare_enable(pdata->clk);330 331	pic32_rtc_enable(pdata, 1);332 333	device_init_wakeup(&pdev->dev, 1);334 335	pdata->rtc->ops = &pic32_rtcops;336	pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;337	pdata->rtc->range_max = RTC_TIMESTAMP_END_2099;338 339	ret = devm_rtc_register_device(pdata->rtc);340	if (ret)341		goto err_nortc;342 343	pdata->rtc->max_user_freq = 128;344 345	pic32_rtc_setfreq(&pdev->dev, 1);346	ret = devm_request_irq(&pdev->dev, pdata->alarm_irq,347			       pic32_rtc_alarmirq, 0,348			       dev_name(&pdev->dev), pdata);349	if (ret) {350		dev_err(&pdev->dev,351			"IRQ %d error %d\n", pdata->alarm_irq, ret);352		goto err_nortc;353	}354 355	clk_disable(pdata->clk);356 357	return 0;358 359err_nortc:360	pic32_rtc_enable(pdata, 0);361	clk_disable_unprepare(pdata->clk);362 363	return ret;364}365 366static const struct of_device_id pic32_rtc_dt_ids[] = {367	{ .compatible = "microchip,pic32mzda-rtc" },368	{ /* sentinel */ }369};370MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids);371 372static struct platform_driver pic32_rtc_driver = {373	.probe		= pic32_rtc_probe,374	.remove_new	= pic32_rtc_remove,375	.driver		= {376		.name	= "pic32-rtc",377		.of_match_table	= of_match_ptr(pic32_rtc_dt_ids),378	},379};380module_platform_driver(pic32_rtc_driver);381 382MODULE_DESCRIPTION("Microchip PIC32 RTC Driver");383MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");384MODULE_LICENSE("GPL");385