xref: /linux/drivers/rtc/rtc-cv1800.c (revision 7fdaba912981e6fe7585517ccf3b5da59e17592e)
1*d9f82683SJingbao Qiu // SPDX-License-Identifier: GPL-2.0-only
2*d9f82683SJingbao Qiu /*
3*d9f82683SJingbao Qiu  * rtc-cv1800.c: RTC driver for Sophgo cv1800 RTC
4*d9f82683SJingbao Qiu  *
5*d9f82683SJingbao Qiu  * Author: Jingbao Qiu <qiujingbao.dlmu@gmail.com>
6*d9f82683SJingbao Qiu  */
7*d9f82683SJingbao Qiu 
8*d9f82683SJingbao Qiu #include <linux/clk.h>
9*d9f82683SJingbao Qiu #include <linux/irq.h>
10*d9f82683SJingbao Qiu #include <linux/kernel.h>
11*d9f82683SJingbao Qiu #include <linux/mfd/syscon.h>
12*d9f82683SJingbao Qiu #include <linux/module.h>
13*d9f82683SJingbao Qiu #include <linux/of.h>
14*d9f82683SJingbao Qiu #include <linux/platform_device.h>
15*d9f82683SJingbao Qiu #include <linux/regmap.h>
16*d9f82683SJingbao Qiu #include <linux/rtc.h>
17*d9f82683SJingbao Qiu 
18*d9f82683SJingbao Qiu #define SEC_PULSE_GEN          0x1004
19*d9f82683SJingbao Qiu #define ALARM_TIME             0x1008
20*d9f82683SJingbao Qiu #define ALARM_ENABLE           0x100C
21*d9f82683SJingbao Qiu #define SET_SEC_CNTR_VAL       0x1010
22*d9f82683SJingbao Qiu #define SET_SEC_CNTR_TRIG      0x1014
23*d9f82683SJingbao Qiu #define SEC_CNTR_VAL           0x1018
24*d9f82683SJingbao Qiu 
25*d9f82683SJingbao Qiu /*
26*d9f82683SJingbao Qiu  * When in VDDBKUP domain, this MACRO register
27*d9f82683SJingbao Qiu  * does not power down
28*d9f82683SJingbao Qiu  */
29*d9f82683SJingbao Qiu #define MACRO_RO_T             0x14A8
30*d9f82683SJingbao Qiu #define MACRO_RG_SET_T         0x1498
31*d9f82683SJingbao Qiu 
32*d9f82683SJingbao Qiu #define ALARM_ENABLE_MASK      BIT(0)
33*d9f82683SJingbao Qiu #define SEL_SEC_PULSE          BIT(31)
34*d9f82683SJingbao Qiu 
35*d9f82683SJingbao Qiu struct cv1800_rtc_priv {
36*d9f82683SJingbao Qiu 	struct rtc_device *rtc_dev;
37*d9f82683SJingbao Qiu 	struct regmap *rtc_map;
38*d9f82683SJingbao Qiu 	struct clk *clk;
39*d9f82683SJingbao Qiu 	int irq;
40*d9f82683SJingbao Qiu };
41*d9f82683SJingbao Qiu 
cv1800_rtc_enabled(struct device * dev)42*d9f82683SJingbao Qiu static bool cv1800_rtc_enabled(struct device *dev)
43*d9f82683SJingbao Qiu {
44*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
45*d9f82683SJingbao Qiu 	u32 reg;
46*d9f82683SJingbao Qiu 
47*d9f82683SJingbao Qiu 	regmap_read(info->rtc_map, SEC_PULSE_GEN, &reg);
48*d9f82683SJingbao Qiu 
49*d9f82683SJingbao Qiu 	return (reg & SEL_SEC_PULSE) == 0;
50*d9f82683SJingbao Qiu }
51*d9f82683SJingbao Qiu 
cv1800_rtc_enable(struct device * dev)52*d9f82683SJingbao Qiu static void cv1800_rtc_enable(struct device *dev)
53*d9f82683SJingbao Qiu {
54*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
55*d9f82683SJingbao Qiu 
56*d9f82683SJingbao Qiu 	/* Sec pulse generated internally */
57*d9f82683SJingbao Qiu 	regmap_update_bits(info->rtc_map, SEC_PULSE_GEN, SEL_SEC_PULSE, 0);
58*d9f82683SJingbao Qiu }
59*d9f82683SJingbao Qiu 
cv1800_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)60*d9f82683SJingbao Qiu static int cv1800_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
61*d9f82683SJingbao Qiu {
62*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
63*d9f82683SJingbao Qiu 
64*d9f82683SJingbao Qiu 	regmap_write(info->rtc_map, ALARM_ENABLE, enabled);
65*d9f82683SJingbao Qiu 
66*d9f82683SJingbao Qiu 	return 0;
67*d9f82683SJingbao Qiu }
68*d9f82683SJingbao Qiu 
cv1800_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alrm)69*d9f82683SJingbao Qiu static int cv1800_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
70*d9f82683SJingbao Qiu {
71*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
72*d9f82683SJingbao Qiu 	unsigned long alarm_time;
73*d9f82683SJingbao Qiu 
74*d9f82683SJingbao Qiu 	alarm_time = rtc_tm_to_time64(&alrm->time);
75*d9f82683SJingbao Qiu 
76*d9f82683SJingbao Qiu 	cv1800_rtc_alarm_irq_enable(dev, 0);
77*d9f82683SJingbao Qiu 
78*d9f82683SJingbao Qiu 	regmap_write(info->rtc_map, ALARM_TIME, alarm_time);
79*d9f82683SJingbao Qiu 
80*d9f82683SJingbao Qiu 	cv1800_rtc_alarm_irq_enable(dev, alrm->enabled);
81*d9f82683SJingbao Qiu 
82*d9f82683SJingbao Qiu 	return 0;
83*d9f82683SJingbao Qiu }
84*d9f82683SJingbao Qiu 
cv1800_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alarm)85*d9f82683SJingbao Qiu static int cv1800_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
86*d9f82683SJingbao Qiu {
87*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
88*d9f82683SJingbao Qiu 	u32 enabled;
89*d9f82683SJingbao Qiu 	u32 time;
90*d9f82683SJingbao Qiu 
91*d9f82683SJingbao Qiu 	if (!cv1800_rtc_enabled(dev)) {
92*d9f82683SJingbao Qiu 		alarm->enabled = 0;
93*d9f82683SJingbao Qiu 		return 0;
94*d9f82683SJingbao Qiu 	}
95*d9f82683SJingbao Qiu 
96*d9f82683SJingbao Qiu 	regmap_read(info->rtc_map, ALARM_ENABLE, &enabled);
97*d9f82683SJingbao Qiu 
98*d9f82683SJingbao Qiu 	alarm->enabled = enabled & ALARM_ENABLE_MASK;
99*d9f82683SJingbao Qiu 
100*d9f82683SJingbao Qiu 	regmap_read(info->rtc_map, ALARM_TIME, &time);
101*d9f82683SJingbao Qiu 
102*d9f82683SJingbao Qiu 	rtc_time64_to_tm(time, &alarm->time);
103*d9f82683SJingbao Qiu 
104*d9f82683SJingbao Qiu 	return 0;
105*d9f82683SJingbao Qiu }
106*d9f82683SJingbao Qiu 
cv1800_rtc_read_time(struct device * dev,struct rtc_time * tm)107*d9f82683SJingbao Qiu static int cv1800_rtc_read_time(struct device *dev, struct rtc_time *tm)
108*d9f82683SJingbao Qiu {
109*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
110*d9f82683SJingbao Qiu 	u32 sec;
111*d9f82683SJingbao Qiu 
112*d9f82683SJingbao Qiu 	if (!cv1800_rtc_enabled(dev))
113*d9f82683SJingbao Qiu 		return -EINVAL;
114*d9f82683SJingbao Qiu 
115*d9f82683SJingbao Qiu 	regmap_read(info->rtc_map, SEC_CNTR_VAL, &sec);
116*d9f82683SJingbao Qiu 
117*d9f82683SJingbao Qiu 	rtc_time64_to_tm(sec, tm);
118*d9f82683SJingbao Qiu 
119*d9f82683SJingbao Qiu 	return 0;
120*d9f82683SJingbao Qiu }
121*d9f82683SJingbao Qiu 
cv1800_rtc_set_time(struct device * dev,struct rtc_time * tm)122*d9f82683SJingbao Qiu static int cv1800_rtc_set_time(struct device *dev, struct rtc_time *tm)
123*d9f82683SJingbao Qiu {
124*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
125*d9f82683SJingbao Qiu 	unsigned long sec;
126*d9f82683SJingbao Qiu 
127*d9f82683SJingbao Qiu 	sec = rtc_tm_to_time64(tm);
128*d9f82683SJingbao Qiu 
129*d9f82683SJingbao Qiu 	regmap_write(info->rtc_map, SET_SEC_CNTR_VAL, sec);
130*d9f82683SJingbao Qiu 	regmap_write(info->rtc_map, SET_SEC_CNTR_TRIG, 1);
131*d9f82683SJingbao Qiu 
132*d9f82683SJingbao Qiu 	regmap_write(info->rtc_map, MACRO_RG_SET_T, sec);
133*d9f82683SJingbao Qiu 
134*d9f82683SJingbao Qiu 	cv1800_rtc_enable(dev);
135*d9f82683SJingbao Qiu 
136*d9f82683SJingbao Qiu 	return 0;
137*d9f82683SJingbao Qiu }
138*d9f82683SJingbao Qiu 
cv1800_rtc_irq_handler(int irq,void * dev_id)139*d9f82683SJingbao Qiu static irqreturn_t cv1800_rtc_irq_handler(int irq, void *dev_id)
140*d9f82683SJingbao Qiu {
141*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *info = dev_id;
142*d9f82683SJingbao Qiu 
143*d9f82683SJingbao Qiu 	rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
144*d9f82683SJingbao Qiu 
145*d9f82683SJingbao Qiu 	regmap_write(info->rtc_map, ALARM_ENABLE, 0);
146*d9f82683SJingbao Qiu 
147*d9f82683SJingbao Qiu 	return IRQ_HANDLED;
148*d9f82683SJingbao Qiu }
149*d9f82683SJingbao Qiu 
150*d9f82683SJingbao Qiu static const struct rtc_class_ops cv1800_rtc_ops = {
151*d9f82683SJingbao Qiu 	.read_time = cv1800_rtc_read_time,
152*d9f82683SJingbao Qiu 	.set_time = cv1800_rtc_set_time,
153*d9f82683SJingbao Qiu 	.read_alarm = cv1800_rtc_read_alarm,
154*d9f82683SJingbao Qiu 	.set_alarm = cv1800_rtc_set_alarm,
155*d9f82683SJingbao Qiu 	.alarm_irq_enable = cv1800_rtc_alarm_irq_enable,
156*d9f82683SJingbao Qiu };
157*d9f82683SJingbao Qiu 
cv1800_rtc_probe(struct platform_device * pdev)158*d9f82683SJingbao Qiu static int cv1800_rtc_probe(struct platform_device *pdev)
159*d9f82683SJingbao Qiu {
160*d9f82683SJingbao Qiu 	struct cv1800_rtc_priv *rtc;
161*d9f82683SJingbao Qiu 	int ret;
162*d9f82683SJingbao Qiu 
163*d9f82683SJingbao Qiu 	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
164*d9f82683SJingbao Qiu 	if (!rtc)
165*d9f82683SJingbao Qiu 		return -ENOMEM;
166*d9f82683SJingbao Qiu 
167*d9f82683SJingbao Qiu 	rtc->rtc_map = device_node_to_regmap(pdev->dev.parent->of_node);
168*d9f82683SJingbao Qiu 	if (IS_ERR(rtc->rtc_map))
169*d9f82683SJingbao Qiu 		return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_map),
170*d9f82683SJingbao Qiu 				     "cannot get parent regmap\n");
171*d9f82683SJingbao Qiu 
172*d9f82683SJingbao Qiu 	rtc->irq = platform_get_irq(pdev, 0);
173*d9f82683SJingbao Qiu 	if (rtc->irq < 0)
174*d9f82683SJingbao Qiu 		return rtc->irq;
175*d9f82683SJingbao Qiu 
176*d9f82683SJingbao Qiu 	rtc->clk = devm_clk_get_enabled(pdev->dev.parent, "rtc");
177*d9f82683SJingbao Qiu 	if (IS_ERR(rtc->clk))
178*d9f82683SJingbao Qiu 		return dev_err_probe(&pdev->dev, PTR_ERR(rtc->clk),
179*d9f82683SJingbao Qiu 				     "rtc clk not found\n");
180*d9f82683SJingbao Qiu 
181*d9f82683SJingbao Qiu 	platform_set_drvdata(pdev, rtc);
182*d9f82683SJingbao Qiu 
183*d9f82683SJingbao Qiu 	device_init_wakeup(&pdev->dev, 1);
184*d9f82683SJingbao Qiu 
185*d9f82683SJingbao Qiu 	rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
186*d9f82683SJingbao Qiu 	if (IS_ERR(rtc->rtc_dev))
187*d9f82683SJingbao Qiu 		return PTR_ERR(rtc->rtc_dev);
188*d9f82683SJingbao Qiu 
189*d9f82683SJingbao Qiu 	rtc->rtc_dev->ops = &cv1800_rtc_ops;
190*d9f82683SJingbao Qiu 	rtc->rtc_dev->range_max = U32_MAX;
191*d9f82683SJingbao Qiu 
192*d9f82683SJingbao Qiu 	ret = devm_request_irq(&pdev->dev, rtc->irq, cv1800_rtc_irq_handler,
193*d9f82683SJingbao Qiu 			       IRQF_TRIGGER_HIGH, "rtc alarm", rtc);
194*d9f82683SJingbao Qiu 	if (ret)
195*d9f82683SJingbao Qiu 		return dev_err_probe(&pdev->dev, ret,
196*d9f82683SJingbao Qiu 				     "cannot register interrupt handler\n");
197*d9f82683SJingbao Qiu 
198*d9f82683SJingbao Qiu 	return devm_rtc_register_device(rtc->rtc_dev);
199*d9f82683SJingbao Qiu }
200*d9f82683SJingbao Qiu 
201*d9f82683SJingbao Qiu static const struct platform_device_id cv1800_rtc_id[] = {
202*d9f82683SJingbao Qiu 	{ .name = "cv1800b-rtc" },
203*d9f82683SJingbao Qiu 	{ /* sentinel */ },
204*d9f82683SJingbao Qiu };
205*d9f82683SJingbao Qiu MODULE_DEVICE_TABLE(platform, cv1800_rtc_id);
206*d9f82683SJingbao Qiu 
207*d9f82683SJingbao Qiu static struct platform_driver cv1800_rtc_driver = {
208*d9f82683SJingbao Qiu 	.driver = {
209*d9f82683SJingbao Qiu 		.name = "sophgo-cv1800-rtc",
210*d9f82683SJingbao Qiu 	},
211*d9f82683SJingbao Qiu 	.probe = cv1800_rtc_probe,
212*d9f82683SJingbao Qiu 	.id_table = cv1800_rtc_id,
213*d9f82683SJingbao Qiu };
214*d9f82683SJingbao Qiu 
215*d9f82683SJingbao Qiu module_platform_driver(cv1800_rtc_driver);
216*d9f82683SJingbao Qiu MODULE_AUTHOR("Jingbao Qiu");
217*d9f82683SJingbao Qiu MODULE_DESCRIPTION("Sophgo cv1800 RTC Driver");
218*d9f82683SJingbao Qiu MODULE_LICENSE("GPL");
219