Blame view

drivers/rtc/rtc-mv.c 8.45 KB
b67436152   Alexandre Belloni   rtc: mv: convert ...
1
  // SPDX-License-Identifier: GPL-2.0
defb45147   Saeed Bishara   rtc: driver for M...
2
3
  /*
   * Driver for the RTC in Marvell SoCs.
defb45147   Saeed Bishara   rtc: driver for M...
4
5
6
7
8
9
   */
  
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/rtc.h>
  #include <linux/bcd.h>
fe5613360   Alexandre Belloni   rtc: mv: use BIT()
10
  #include <linux/bitops.h>
defb45147   Saeed Bishara   rtc: driver for M...
11
12
  #include <linux/io.h>
  #include <linux/platform_device.h>
ea983ede1   Jason Cooper   ARM: kirkwood: rt...
13
  #include <linux/of.h>
9d1d4f9ea   Nicolas Pitre   [ARM] Kirkwood: f...
14
  #include <linux/delay.h>
89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
15
  #include <linux/clk.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/gfp.h>
2113852b2   Paul Gortmaker   rtc: Add module.h...
17
  #include <linux/module.h>
defb45147   Saeed Bishara   rtc: driver for M...
18
19
20
21
22
23
24
  
  
  #define RTC_TIME_REG_OFFS	0
  #define RTC_SECONDS_OFFS	0
  #define RTC_MINUTES_OFFS	8
  #define RTC_HOURS_OFFS		16
  #define RTC_WDAY_OFFS		24
fe5613360   Alexandre Belloni   rtc: mv: use BIT()
25
  #define RTC_HOURS_12H_MODE	BIT(22) /* 12 hour mode */
defb45147   Saeed Bishara   rtc: driver for M...
26
27
28
29
30
  
  #define RTC_DATE_REG_OFFS	4
  #define RTC_MDAY_OFFS		0
  #define RTC_MONTH_OFFS		8
  #define RTC_YEAR_OFFS		16
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
31
32
  #define RTC_ALARM_TIME_REG_OFFS	8
  #define RTC_ALARM_DATE_REG_OFFS	0xc
fe5613360   Alexandre Belloni   rtc: mv: use BIT()
33
  #define RTC_ALARM_VALID		BIT(7)
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
34
35
36
  
  #define RTC_ALARM_INTERRUPT_MASK_REG_OFFS	0x10
  #define RTC_ALARM_INTERRUPT_CASUE_REG_OFFS	0x14
defb45147   Saeed Bishara   rtc: driver for M...
37
38
39
40
  
  struct rtc_plat_data {
  	struct rtc_device *rtc;
  	void __iomem *ioaddr;
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
41
  	int		irq;
89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
42
  	struct clk	*clk;
defb45147   Saeed Bishara   rtc: driver for M...
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  };
  
  static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm)
  {
  	struct rtc_plat_data *pdata = dev_get_drvdata(dev);
  	void __iomem *ioaddr = pdata->ioaddr;
  	u32	rtc_reg;
  
  	rtc_reg = (bin2bcd(tm->tm_sec) << RTC_SECONDS_OFFS) |
  		(bin2bcd(tm->tm_min) << RTC_MINUTES_OFFS) |
  		(bin2bcd(tm->tm_hour) << RTC_HOURS_OFFS) |
  		(bin2bcd(tm->tm_wday) << RTC_WDAY_OFFS);
  	writel(rtc_reg, ioaddr + RTC_TIME_REG_OFFS);
  
  	rtc_reg = (bin2bcd(tm->tm_mday) << RTC_MDAY_OFFS) |
  		(bin2bcd(tm->tm_mon + 1) << RTC_MONTH_OFFS) |
b46c5815b   Alexandre Belloni   rtc: mv: add range
59
  		(bin2bcd(tm->tm_year - 100) << RTC_YEAR_OFFS);
defb45147   Saeed Bishara   rtc: driver for M...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  	writel(rtc_reg, ioaddr + RTC_DATE_REG_OFFS);
  
  	return 0;
  }
  
  static int mv_rtc_read_time(struct device *dev, struct rtc_time *tm)
  {
  	struct rtc_plat_data *pdata = dev_get_drvdata(dev);
  	void __iomem *ioaddr = pdata->ioaddr;
  	u32	rtc_time, rtc_date;
  	unsigned int year, month, day, hour, minute, second, wday;
  
  	rtc_time = readl(ioaddr + RTC_TIME_REG_OFFS);
  	rtc_date = readl(ioaddr + RTC_DATE_REG_OFFS);
  
  	second = rtc_time & 0x7f;
  	minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f;
4042a1475   Alexandre Belloni   rtc: mv: correct ...
77
  	hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hour mode */
defb45147   Saeed Bishara   rtc: driver for M...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  	wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7;
  
  	day = rtc_date & 0x3f;
  	month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f;
  	year = (rtc_date >> RTC_YEAR_OFFS) & 0xff;
  
  	tm->tm_sec = bcd2bin(second);
  	tm->tm_min = bcd2bin(minute);
  	tm->tm_hour = bcd2bin(hour);
  	tm->tm_mday = bcd2bin(day);
  	tm->tm_wday = bcd2bin(wday);
  	tm->tm_mon = bcd2bin(month) - 1;
  	/* hw counts from year 2000, but tm_year is relative to 1900 */
  	tm->tm_year = bcd2bin(year) + 100;
22652ba72   Alexandre Belloni   rtc: stop validat...
92
  	return 0;
defb45147   Saeed Bishara   rtc: driver for M...
93
  }
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
94
95
96
97
98
99
100
101
102
103
104
105
  static int mv_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
  	struct rtc_plat_data *pdata = dev_get_drvdata(dev);
  	void __iomem *ioaddr = pdata->ioaddr;
  	u32	rtc_time, rtc_date;
  	unsigned int year, month, day, hour, minute, second, wday;
  
  	rtc_time = readl(ioaddr + RTC_ALARM_TIME_REG_OFFS);
  	rtc_date = readl(ioaddr + RTC_ALARM_DATE_REG_OFFS);
  
  	second = rtc_time & 0x7f;
  	minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f;
4042a1475   Alexandre Belloni   rtc: mv: correct ...
106
  	hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hour mode */
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  	wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7;
  
  	day = rtc_date & 0x3f;
  	month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f;
  	year = (rtc_date >> RTC_YEAR_OFFS) & 0xff;
  
  	alm->time.tm_sec = bcd2bin(second);
  	alm->time.tm_min = bcd2bin(minute);
  	alm->time.tm_hour = bcd2bin(hour);
  	alm->time.tm_mday = bcd2bin(day);
  	alm->time.tm_wday = bcd2bin(wday);
  	alm->time.tm_mon = bcd2bin(month) - 1;
  	/* hw counts from year 2000, but tm_year is relative to 1900 */
  	alm->time.tm_year = bcd2bin(year) + 100;
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
121
  	alm->enabled = !!readl(ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
959e8b77b   Alexandre Belloni   rtc: mv: let the ...
122
123
  
  	return rtc_valid_tm(&alm->time);
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  }
  
  static int mv_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
  	struct rtc_plat_data *pdata = dev_get_drvdata(dev);
  	void __iomem *ioaddr = pdata->ioaddr;
  	u32 rtc_reg = 0;
  
  	if (alm->time.tm_sec >= 0)
  		rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_sec))
  			<< RTC_SECONDS_OFFS;
  	if (alm->time.tm_min >= 0)
  		rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_min))
  			<< RTC_MINUTES_OFFS;
  	if (alm->time.tm_hour >= 0)
  		rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_hour))
  			<< RTC_HOURS_OFFS;
  
  	writel(rtc_reg, ioaddr + RTC_ALARM_TIME_REG_OFFS);
  
  	if (alm->time.tm_mday >= 0)
  		rtc_reg = (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mday))
  			<< RTC_MDAY_OFFS;
  	else
  		rtc_reg = 0;
  
  	if (alm->time.tm_mon >= 0)
  		rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mon + 1))
  			<< RTC_MONTH_OFFS;
  
  	if (alm->time.tm_year >= 0)
b46c5815b   Alexandre Belloni   rtc: mv: add range
155
  		rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_year - 100))
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
156
157
158
159
160
161
162
163
164
  			<< RTC_YEAR_OFFS;
  
  	writel(rtc_reg, ioaddr + RTC_ALARM_DATE_REG_OFFS);
  	writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS);
  	writel(alm->enabled ? 1 : 0,
  	       ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
  
  	return 0;
  }
16380c153   John Stultz   RTC: Convert rtc ...
165
  static int mv_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
166
  {
85368bb9d   Wolfram Sang   rtc: simplify get...
167
  	struct rtc_plat_data *pdata = dev_get_drvdata(dev);
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
168
169
170
  	void __iomem *ioaddr = pdata->ioaddr;
  
  	if (pdata->irq < 0)
16380c153   John Stultz   RTC: Convert rtc ...
171
172
173
  		return -EINVAL; /* fall back into rtc-dev's emulation */
  
  	if (enabled)
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
174
  		writel(1, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
16380c153   John Stultz   RTC: Convert rtc ...
175
176
  	else
  		writel(0, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  	return 0;
  }
  
  static irqreturn_t mv_rtc_interrupt(int irq, void *data)
  {
  	struct rtc_plat_data *pdata = data;
  	void __iomem *ioaddr = pdata->ioaddr;
  
  	/* alarm irq? */
  	if (!readl(ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS))
  		return IRQ_NONE;
  
  	/* clear interrupt */
  	writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS);
  	rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
  	return IRQ_HANDLED;
  }
defb45147   Saeed Bishara   rtc: driver for M...
194
195
196
197
  static const struct rtc_class_ops mv_rtc_ops = {
  	.read_time	= mv_rtc_read_time,
  	.set_time	= mv_rtc_set_time,
  };
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
198
199
200
201
202
  static const struct rtc_class_ops mv_rtc_alarm_ops = {
  	.read_time	= mv_rtc_read_time,
  	.set_time	= mv_rtc_set_time,
  	.read_alarm	= mv_rtc_read_alarm,
  	.set_alarm	= mv_rtc_set_alarm,
16380c153   John Stultz   RTC: Convert rtc ...
203
  	.alarm_irq_enable = mv_rtc_alarm_irq_enable,
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
204
  };
b69af4373   Jingoo Han   rtc: rtc-mv: add ...
205
  static int __init mv_rtc_probe(struct platform_device *pdev)
defb45147   Saeed Bishara   rtc: driver for M...
206
  {
defb45147   Saeed Bishara   rtc: driver for M...
207
  	struct rtc_plat_data *pdata;
defb45147   Saeed Bishara   rtc: driver for M...
208
  	u32 rtc_time;
89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
209
  	int ret = 0;
defb45147   Saeed Bishara   rtc: driver for M...
210

defb45147   Saeed Bishara   rtc: driver for M...
211
212
213
  	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
  	if (!pdata)
  		return -ENOMEM;
09ef18bcd   YueHaibing   rtc: use devm_pla...
214
  	pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0);
7c1d69ee1   Julia Lawall   rtc: simplify dev...
215
216
  	if (IS_ERR(pdata->ioaddr))
  		return PTR_ERR(pdata->ioaddr);
defb45147   Saeed Bishara   rtc: driver for M...
217

89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
218
219
220
221
  	pdata->clk = devm_clk_get(&pdev->dev, NULL);
  	/* Not all SoCs require a clock.*/
  	if (!IS_ERR(pdata->clk))
  		clk_prepare_enable(pdata->clk);
4042a1475   Alexandre Belloni   rtc: mv: correct ...
222
  	/* make sure the 24 hour mode is enabled */
defb45147   Saeed Bishara   rtc: driver for M...
223
224
  	rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS);
  	if (rtc_time & RTC_HOURS_12H_MODE) {
4042a1475   Alexandre Belloni   rtc: mv: correct ...
225
226
  		dev_err(&pdev->dev, "12 Hour mode is enabled but not supported.
  ");
89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
227
228
  		ret = -EINVAL;
  		goto out;
defb45147   Saeed Bishara   rtc: driver for M...
229
  	}
9d1d4f9ea   Nicolas Pitre   [ARM] Kirkwood: f...
230
231
232
233
234
235
236
  	/* make sure it is actually functional */
  	if (rtc_time == 0x01000000) {
  		ssleep(1);
  		rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS);
  		if (rtc_time == 0x01000000) {
  			dev_err(&pdev->dev, "internal RTC not ticking
  ");
89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
237
238
  			ret = -ENODEV;
  			goto out;
9d1d4f9ea   Nicolas Pitre   [ARM] Kirkwood: f...
239
240
  		}
  	}
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
241
  	pdata->irq = platform_get_irq(pdev, 0);
defb45147   Saeed Bishara   rtc: driver for M...
242
  	platform_set_drvdata(pdev, pdata);
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
243

5b25a71b6   Alexandre Belloni   rtc: mv: convert ...
244
  	pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
245
246
247
248
  	if (IS_ERR(pdata->rtc)) {
  		ret = PTR_ERR(pdata->rtc);
  		goto out;
  	}
defb45147   Saeed Bishara   rtc: driver for M...
249

aeedacaea   Saeed Bishara   rtc-mv: add suppo...
250
251
252
  	if (pdata->irq >= 0) {
  		writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
  		if (devm_request_irq(&pdev->dev, pdata->irq, mv_rtc_interrupt,
2f6e5f945   Yong Zhang   drivers/rtc: remo...
253
  				     IRQF_SHARED,
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
254
255
256
257
258
259
  				     pdev->name, pdata) < 0) {
  			dev_warn(&pdev->dev, "interrupt not available.
  ");
  			pdata->irq = -1;
  		}
  	}
5b25a71b6   Alexandre Belloni   rtc: mv: convert ...
260
261
262
263
264
265
  	if (pdata->irq >= 0) {
  		device_init_wakeup(&pdev->dev, 1);
  		pdata->rtc->ops = &mv_rtc_alarm_ops;
  	} else {
  		pdata->rtc->ops = &mv_rtc_ops;
  	}
b46c5815b   Alexandre Belloni   rtc: mv: add range
266
267
  	pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
  	pdata->rtc->range_max = RTC_TIMESTAMP_END_2099;
5b25a71b6   Alexandre Belloni   rtc: mv: convert ...
268
269
270
  	ret = rtc_register_device(pdata->rtc);
  	if (!ret)
  		return 0;
89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
271
272
273
274
275
  out:
  	if (!IS_ERR(pdata->clk))
  		clk_disable_unprepare(pdata->clk);
  
  	return ret;
defb45147   Saeed Bishara   rtc: driver for M...
276
277
278
279
280
  }
  
  static int __exit mv_rtc_remove(struct platform_device *pdev)
  {
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
aeedacaea   Saeed Bishara   rtc-mv: add suppo...
281
282
  	if (pdata->irq >= 0)
  		device_init_wakeup(&pdev->dev, 0);
89c58c198   Andrew Lunn   rtc: rtc-mv: Add ...
283
284
  	if (!IS_ERR(pdata->clk))
  		clk_disable_unprepare(pdata->clk);
defb45147   Saeed Bishara   rtc: driver for M...
285
286
  	return 0;
  }
ea983ede1   Jason Cooper   ARM: kirkwood: rt...
287
  #ifdef CONFIG_OF
4621bc56c   Jingoo Han   rtc: rtc-mv: make...
288
  static const struct of_device_id rtc_mv_of_match_table[] = {
778435045   Andrew Lunn   ARM: Kirkwood: Re...
289
  	{ .compatible = "marvell,orion-rtc", },
ea983ede1   Jason Cooper   ARM: kirkwood: rt...
290
291
  	{}
  };
73798d5c4   Javier Martinez Canillas   rtc: Fix module a...
292
  MODULE_DEVICE_TABLE(of, rtc_mv_of_match_table);
ea983ede1   Jason Cooper   ARM: kirkwood: rt...
293
  #endif
defb45147   Saeed Bishara   rtc: driver for M...
294
295
296
297
  static struct platform_driver mv_rtc_driver = {
  	.remove		= __exit_p(mv_rtc_remove),
  	.driver		= {
  		.name	= "rtc-mv",
ea983ede1   Jason Cooper   ARM: kirkwood: rt...
298
  		.of_match_table = of_match_ptr(rtc_mv_of_match_table),
defb45147   Saeed Bishara   rtc: driver for M...
299
300
  	},
  };
1963f7cd1   Jingoo Han   rtc: rtc-mv: use ...
301
  module_platform_driver_probe(mv_rtc_driver, mv_rtc_probe);
defb45147   Saeed Bishara   rtc: driver for M...
302
303
304
305
306
  
  MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>");
  MODULE_DESCRIPTION("Marvell RTC driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:rtc-mv");