brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · cfcfc28 Raw
305 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * RTC driver for Nuvoton MA35D14 *5 * Copyright (C) 2023 Nuvoton Technology Corp.6 */7 8#include <linux/bcd.h>9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/init.h>12#include <linux/io.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/rtc.h>17 18/* MA35D1 RTC Control Registers */19#define MA35_REG_RTC_INIT	0x0020#define MA35_REG_RTC_SINFASTS	0x0421#define MA35_REG_RTC_FREQADJ	0x0822#define MA35_REG_RTC_TIME	0x0c23#define MA35_REG_RTC_CAL	0x1024#define MA35_REG_RTC_CLKFMT	0x1425#define MA35_REG_RTC_WEEKDAY	0x1826#define MA35_REG_RTC_TALM	0x1c27#define MA35_REG_RTC_CALM	0x2028#define MA35_REG_RTC_LEAPYEAR	0x2429#define MA35_REG_RTC_INTEN	0x2830#define MA35_REG_RTC_INTSTS	0x2c31 32/* register MA35_REG_RTC_INIT */33#define RTC_INIT_ACTIVE		BIT(0)34#define RTC_INIT_MAGIC_CODE	0xa5eb135735 36/* register MA35_REG_RTC_CLKFMT */37#define RTC_CLKFMT_24HEN	BIT(0)38#define RTC_CLKFMT_DCOMPEN	BIT(16)39 40/* register MA35_REG_RTC_INTEN */41#define RTC_INTEN_ALMIEN	BIT(0)42#define RTC_INTEN_UIEN		BIT(1)43#define RTC_INTEN_CLKFIEN	BIT(24)44#define RTC_INTEN_CLKSTIEN	BIT(25)45 46/* register MA35_REG_RTC_INTSTS */47#define RTC_INTSTS_ALMIF	BIT(0)48#define RTC_INTSTS_UIF		BIT(1)49#define RTC_INTSTS_CLKFIF	BIT(24)50#define RTC_INTSTS_CLKSTIF	BIT(25)51 52#define RTC_INIT_TIMEOUT	25053 54struct ma35_rtc {55	int irq_num;56	void __iomem *rtc_reg;57	struct rtc_device *rtcdev;58};59 60static u32 rtc_reg_read(struct ma35_rtc *p, u32 offset)61{62	return __raw_readl(p->rtc_reg + offset);63}64 65static inline void rtc_reg_write(struct ma35_rtc *p, u32 offset, u32 value)66{67	__raw_writel(value, p->rtc_reg + offset);68}69 70static irqreturn_t ma35d1_rtc_interrupt(int irq, void *data)71{72	struct ma35_rtc *rtc = (struct ma35_rtc *)data;73	unsigned long events = 0, rtc_irq;74 75	rtc_irq = rtc_reg_read(rtc, MA35_REG_RTC_INTSTS);76 77	if (rtc_irq & RTC_INTSTS_ALMIF) {78		rtc_reg_write(rtc, MA35_REG_RTC_INTSTS, RTC_INTSTS_ALMIF);79		events |= RTC_AF | RTC_IRQF;80	}81 82	rtc_update_irq(rtc->rtcdev, 1, events);83 84	return IRQ_HANDLED;85}86 87static int ma35d1_rtc_init(struct ma35_rtc *rtc, u32 ms_timeout)88{89	const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);90 91	do {92		if (rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE)93			return 0;94 95		rtc_reg_write(rtc, MA35_REG_RTC_INIT, RTC_INIT_MAGIC_CODE);96 97		mdelay(1);98 99	} while (time_before(jiffies, timeout));100 101	return -ETIMEDOUT;102}103 104static int ma35d1_alarm_irq_enable(struct device *dev, u32 enabled)105{106	struct ma35_rtc *rtc = dev_get_drvdata(dev);107	u32 reg_ien;108 109	reg_ien = rtc_reg_read(rtc, MA35_REG_RTC_INTEN);110 111	if (enabled)112		rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien | RTC_INTEN_ALMIEN);113	else114		rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien & ~RTC_INTEN_ALMIEN);115 116	return 0;117}118 119static int ma35d1_rtc_read_time(struct device *dev, struct rtc_time *tm)120{121	struct ma35_rtc *rtc = dev_get_drvdata(dev);122	u32 time, cal, wday;123 124	do {125		time = rtc_reg_read(rtc, MA35_REG_RTC_TIME);126		cal  = rtc_reg_read(rtc, MA35_REG_RTC_CAL);127		wday = rtc_reg_read(rtc, MA35_REG_RTC_WEEKDAY);128	} while (time != rtc_reg_read(rtc, MA35_REG_RTC_TIME) ||129		 cal != rtc_reg_read(rtc, MA35_REG_RTC_CAL));130 131	tm->tm_mday = bcd2bin(cal >> 0);132	tm->tm_wday = wday;133	tm->tm_mon = bcd2bin(cal >> 8);134	tm->tm_mon = tm->tm_mon - 1;135	tm->tm_year = bcd2bin(cal >> 16) + 100;136 137	tm->tm_sec = bcd2bin(time >> 0);138	tm->tm_min = bcd2bin(time >> 8);139	tm->tm_hour = bcd2bin(time >> 16);140 141	return rtc_valid_tm(tm);142}143 144static int ma35d1_rtc_set_time(struct device *dev, struct rtc_time *tm)145{146	struct ma35_rtc *rtc = dev_get_drvdata(dev);147	u32 val;148 149	val = bin2bcd(tm->tm_mday) << 0 | bin2bcd(tm->tm_mon + 1) << 8 |150	      bin2bcd(tm->tm_year - 100) << 16;151	rtc_reg_write(rtc, MA35_REG_RTC_CAL, val);152 153	val = bin2bcd(tm->tm_sec) << 0 | bin2bcd(tm->tm_min) << 8 |154	      bin2bcd(tm->tm_hour) << 16;155	rtc_reg_write(rtc, MA35_REG_RTC_TIME, val);156 157	val = tm->tm_wday;158	rtc_reg_write(rtc, MA35_REG_RTC_WEEKDAY, val);159 160	return 0;161}162 163static int ma35d1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)164{165	struct ma35_rtc *rtc = dev_get_drvdata(dev);166	u32 talm, calm;167 168	talm = rtc_reg_read(rtc, MA35_REG_RTC_TALM);169	calm = rtc_reg_read(rtc, MA35_REG_RTC_CALM);170 171	alrm->time.tm_mday = bcd2bin(calm >> 0);172	alrm->time.tm_mon = bcd2bin(calm >> 8);173	alrm->time.tm_mon = alrm->time.tm_mon - 1;174 175	alrm->time.tm_year = bcd2bin(calm >> 16) + 100;176 177	alrm->time.tm_sec = bcd2bin(talm >> 0);178	alrm->time.tm_min = bcd2bin(talm >> 8);179	alrm->time.tm_hour = bcd2bin(talm >> 16);180 181	return rtc_valid_tm(&alrm->time);182}183 184static int ma35d1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)185{186	struct ma35_rtc *rtc = dev_get_drvdata(dev);187	unsigned long val;188 189	val = bin2bcd(alrm->time.tm_mday) << 0 | bin2bcd(alrm->time.tm_mon + 1) << 8 |190	      bin2bcd(alrm->time.tm_year - 100) << 16;191	rtc_reg_write(rtc, MA35_REG_RTC_CALM, val);192 193	val = bin2bcd(alrm->time.tm_sec) << 0 | bin2bcd(alrm->time.tm_min) << 8 |194	      bin2bcd(alrm->time.tm_hour) << 16;195	rtc_reg_write(rtc, MA35_REG_RTC_TALM, val);196 197	ma35d1_alarm_irq_enable(dev, alrm->enabled);198 199	return 0;200}201 202static const struct rtc_class_ops ma35d1_rtc_ops = {203	.read_time = ma35d1_rtc_read_time,204	.set_time = ma35d1_rtc_set_time,205	.read_alarm = ma35d1_rtc_read_alarm,206	.set_alarm = ma35d1_rtc_set_alarm,207	.alarm_irq_enable = ma35d1_alarm_irq_enable,208};209 210static int ma35d1_rtc_probe(struct platform_device *pdev)211{212	struct ma35_rtc *rtc;213	struct clk *clk;214	int ret;215 216	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);217	if (!rtc)218		return -ENOMEM;219 220	rtc->rtc_reg = devm_platform_ioremap_resource(pdev, 0);221	if (IS_ERR(rtc->rtc_reg))222		return PTR_ERR(rtc->rtc_reg);223 224	clk = of_clk_get(pdev->dev.of_node, 0);225	if (IS_ERR(clk))226		return dev_err_probe(&pdev->dev, PTR_ERR(clk), "failed to find rtc clock\n");227 228	ret = clk_prepare_enable(clk);229	if (ret)230		return ret;231 232	if (!(rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE)) {233		ret = ma35d1_rtc_init(rtc, RTC_INIT_TIMEOUT);234		if (ret)235			return dev_err_probe(&pdev->dev, ret, "rtc init failed\n");236	}237 238	rtc->irq_num = platform_get_irq(pdev, 0);239 240	ret = devm_request_irq(&pdev->dev, rtc->irq_num, ma35d1_rtc_interrupt,241			       IRQF_NO_SUSPEND, "ma35d1rtc", rtc);242	if (ret)243		return dev_err_probe(&pdev->dev, ret, "Failed to request rtc irq\n");244 245	platform_set_drvdata(pdev, rtc);246 247	device_init_wakeup(&pdev->dev, true);248 249	rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev);250	if (IS_ERR(rtc->rtcdev))251		return PTR_ERR(rtc->rtcdev);252 253	rtc->rtcdev->ops = &ma35d1_rtc_ops;254	rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000;255	rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099;256 257	ret = devm_rtc_register_device(rtc->rtcdev);258	if (ret)259		return dev_err_probe(&pdev->dev, ret, "Failed to register rtc device\n");260 261	return 0;262}263 264static int ma35d1_rtc_suspend(struct platform_device *pdev, pm_message_t state)265{266	struct ma35_rtc *rtc = platform_get_drvdata(pdev);267 268	if (device_may_wakeup(&pdev->dev))269		enable_irq_wake(rtc->irq_num);270 271	return 0;272}273 274static int ma35d1_rtc_resume(struct platform_device *pdev)275{276	struct ma35_rtc *rtc = platform_get_drvdata(pdev);277 278	if (device_may_wakeup(&pdev->dev))279		disable_irq_wake(rtc->irq_num);280 281	return 0;282}283 284static const struct of_device_id ma35d1_rtc_of_match[] = {285	{ .compatible = "nuvoton,ma35d1-rtc", },286	{},287};288MODULE_DEVICE_TABLE(of, ma35d1_rtc_of_match);289 290static struct platform_driver ma35d1_rtc_driver = {291	.suspend    = ma35d1_rtc_suspend,292	.resume     = ma35d1_rtc_resume,293	.probe      = ma35d1_rtc_probe,294	.driver		= {295		.name	= "rtc-ma35d1",296		.of_match_table = ma35d1_rtc_of_match,297	},298};299 300module_platform_driver(ma35d1_rtc_driver);301 302MODULE_AUTHOR("Ming-Jen Chen <mjchen@nuvoton.com>");303MODULE_DESCRIPTION("MA35D1 RTC driver");304MODULE_LICENSE("GPL");305