Blame view

drivers/rtc/rtc-stk17ta8.c 10.5 KB
029641151   Thomas Hommel   rtc: add support ...
1
2
3
  /*
   * A RTC driver for the Simtek STK17TA8
   *
ab4364d31   Martyn Welch   stk17ta8: renamin...
4
   * By Thomas Hommel <thomas.hommel@ge.com>
029641151   Thomas Hommel   rtc: add support ...
5
6
7
8
9
10
11
12
13
14
15
16
   *
   * Based on the DS1553 driver from
   * Atsushi Nemoto <anemo@mba.ocn.ne.jp>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  
  #include <linux/bcd.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/gfp.h>
029641151   Thomas Hommel   rtc: add support ...
18
19
20
21
22
23
  #include <linux/delay.h>
  #include <linux/jiffies.h>
  #include <linux/interrupt.h>
  #include <linux/rtc.h>
  #include <linux/platform_device.h>
  #include <linux/io.h>
2113852b2   Paul Gortmaker   rtc: Add module.h...
24
  #include <linux/module.h>
029641151   Thomas Hommel   rtc: add support ...
25

029641151   Thomas Hommel   rtc: add support ...
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  #define RTC_REG_SIZE		0x20000
  #define RTC_OFFSET		0x1fff0
  
  #define RTC_FLAGS		(RTC_OFFSET + 0)
  #define RTC_CENTURY		(RTC_OFFSET + 1)
  #define RTC_SECONDS_ALARM	(RTC_OFFSET + 2)
  #define RTC_MINUTES_ALARM	(RTC_OFFSET + 3)
  #define RTC_HOURS_ALARM		(RTC_OFFSET + 4)
  #define RTC_DATE_ALARM		(RTC_OFFSET + 5)
  #define RTC_INTERRUPTS		(RTC_OFFSET + 6)
  #define RTC_WATCHDOG		(RTC_OFFSET + 7)
  #define RTC_CALIBRATION		(RTC_OFFSET + 8)
  #define RTC_SECONDS		(RTC_OFFSET + 9)
  #define RTC_MINUTES		(RTC_OFFSET + 10)
  #define RTC_HOURS		(RTC_OFFSET + 11)
  #define RTC_DAY			(RTC_OFFSET + 12)
  #define RTC_DATE		(RTC_OFFSET + 13)
  #define RTC_MONTH		(RTC_OFFSET + 14)
  #define RTC_YEAR		(RTC_OFFSET + 15)
  
  #define RTC_SECONDS_MASK	0x7f
  #define RTC_DAY_MASK		0x07
  #define RTC_CAL_MASK		0x3f
  
  /* Bits in the Calibration register */
  #define RTC_STOP		0x80
  
  /* Bits in the Flags register */
  #define RTC_FLAGS_AF		0x40
  #define RTC_FLAGS_PF		0x20
  #define RTC_WRITE		0x02
  #define RTC_READ		0x01
  
  /* Bits in the Interrupts register */
  #define RTC_INTS_AIE		0x40
  
  struct rtc_plat_data {
  	struct rtc_device *rtc;
  	void __iomem *ioaddr;
029641151   Thomas Hommel   rtc: add support ...
65
66
67
68
69
70
71
  	unsigned long last_jiffies;
  	int irq;
  	unsigned int irqen;
  	int alrm_sec;
  	int alrm_min;
  	int alrm_hour;
  	int alrm_mday;
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
72
  	spinlock_t lock;
029641151   Thomas Hommel   rtc: add support ...
73
74
75
76
77
78
79
80
81
82
83
  };
  
  static int stk17ta8_rtc_set_time(struct device *dev, struct rtc_time *tm)
  {
  	struct platform_device *pdev = to_platform_device(dev);
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
  	void __iomem *ioaddr = pdata->ioaddr;
  	u8 flags;
  
  	flags = readb(pdata->ioaddr + RTC_FLAGS);
  	writeb(flags | RTC_WRITE, pdata->ioaddr + RTC_FLAGS);
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
84
85
86
87
88
89
90
91
  	writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR);
  	writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH);
  	writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY);
  	writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE);
  	writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS);
  	writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES);
  	writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS);
  	writeb(bin2bcd((tm->tm_year + 1900) / 100), ioaddr + RTC_CENTURY);
029641151   Thomas Hommel   rtc: add support ...
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  
  	writeb(flags & ~RTC_WRITE, pdata->ioaddr + RTC_FLAGS);
  	return 0;
  }
  
  static int stk17ta8_rtc_read_time(struct device *dev, struct rtc_time *tm)
  {
  	struct platform_device *pdev = to_platform_device(dev);
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
  	void __iomem *ioaddr = pdata->ioaddr;
  	unsigned int year, month, day, hour, minute, second, week;
  	unsigned int century;
  	u8 flags;
  
  	/* give enough time to update RTC in case of continuous read */
  	if (pdata->last_jiffies == jiffies)
  		msleep(1);
  	pdata->last_jiffies = jiffies;
  
  	flags = readb(pdata->ioaddr + RTC_FLAGS);
  	writeb(flags | RTC_READ, ioaddr + RTC_FLAGS);
  	second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK;
  	minute = readb(ioaddr + RTC_MINUTES);
  	hour = readb(ioaddr + RTC_HOURS);
  	day = readb(ioaddr + RTC_DATE);
  	week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK;
  	month = readb(ioaddr + RTC_MONTH);
  	year = readb(ioaddr + RTC_YEAR);
  	century = readb(ioaddr + RTC_CENTURY);
  	writeb(flags & ~RTC_READ, ioaddr + RTC_FLAGS);
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
122
123
124
125
126
127
  	tm->tm_sec = bcd2bin(second);
  	tm->tm_min = bcd2bin(minute);
  	tm->tm_hour = bcd2bin(hour);
  	tm->tm_mday = bcd2bin(day);
  	tm->tm_wday = bcd2bin(week);
  	tm->tm_mon = bcd2bin(month) - 1;
029641151   Thomas Hommel   rtc: add support ...
128
  	/* year is 1900 + tm->tm_year */
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
129
  	tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900;
029641151   Thomas Hommel   rtc: add support ...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  
  	if (rtc_valid_tm(tm) < 0) {
  		dev_err(dev, "retrieved date/time is not valid.
  ");
  		rtc_time_to_tm(0, tm);
  	}
  	return 0;
  }
  
  static void stk17ta8_rtc_update_alarm(struct rtc_plat_data *pdata)
  {
  	void __iomem *ioaddr = pdata->ioaddr;
  	unsigned long irqflags;
  	u8 flags;
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
144
  	spin_lock_irqsave(&pdata->lock, irqflags);
029641151   Thomas Hommel   rtc: add support ...
145
146
147
148
149
  
  	flags = readb(ioaddr + RTC_FLAGS);
  	writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS);
  
  	writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
150
  	       0x80 : bin2bcd(pdata->alrm_mday),
029641151   Thomas Hommel   rtc: add support ...
151
152
  	       ioaddr + RTC_DATE_ALARM);
  	writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ?
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
153
  	       0x80 : bin2bcd(pdata->alrm_hour),
029641151   Thomas Hommel   rtc: add support ...
154
155
  	       ioaddr + RTC_HOURS_ALARM);
  	writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ?
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
156
  	       0x80 : bin2bcd(pdata->alrm_min),
029641151   Thomas Hommel   rtc: add support ...
157
158
  	       ioaddr + RTC_MINUTES_ALARM);
  	writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ?
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
159
  	       0x80 : bin2bcd(pdata->alrm_sec),
029641151   Thomas Hommel   rtc: add support ...
160
161
162
163
  	       ioaddr + RTC_SECONDS_ALARM);
  	writeb(pdata->irqen ? RTC_INTS_AIE : 0, ioaddr + RTC_INTERRUPTS);
  	readb(ioaddr + RTC_FLAGS);	/* clear interrupts */
  	writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS);
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
164
  	spin_unlock_irqrestore(&pdata->lock, irqflags);
029641151   Thomas Hommel   rtc: add support ...
165
166
167
168
169
170
  }
  
  static int stk17ta8_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  {
  	struct platform_device *pdev = to_platform_device(dev);
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
2fac6674d   Anton Vorontsov   rtc: bunch of dri...
171
  	if (pdata->irq <= 0)
029641151   Thomas Hommel   rtc: add support ...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  		return -EINVAL;
  	pdata->alrm_mday = alrm->time.tm_mday;
  	pdata->alrm_hour = alrm->time.tm_hour;
  	pdata->alrm_min = alrm->time.tm_min;
  	pdata->alrm_sec = alrm->time.tm_sec;
  	if (alrm->enabled)
  		pdata->irqen |= RTC_AF;
  	stk17ta8_rtc_update_alarm(pdata);
  	return 0;
  }
  
  static int stk17ta8_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
  {
  	struct platform_device *pdev = to_platform_device(dev);
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
2fac6674d   Anton Vorontsov   rtc: bunch of dri...
187
  	if (pdata->irq <= 0)
029641151   Thomas Hommel   rtc: add support ...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
  		return -EINVAL;
  	alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday;
  	alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour;
  	alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min;
  	alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec;
  	alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0;
  	return 0;
  }
  
  static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id)
  {
  	struct platform_device *pdev = dev_id;
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
  	void __iomem *ioaddr = pdata->ioaddr;
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
202
  	unsigned long events = 0;
029641151   Thomas Hommel   rtc: add support ...
203

3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
204
  	spin_lock(&pdata->lock);
029641151   Thomas Hommel   rtc: add support ...
205
  	/* read and clear interrupt */
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
206
207
208
209
210
211
  	if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) {
  		events = RTC_IRQF;
  		if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
  			events |= RTC_UF;
  		else
  			events |= RTC_AF;
0d71915d2   Alexander Shiyan   rtc: treewide: re...
212
  		rtc_update_irq(pdata->rtc, 1, events);
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
213
214
215
  	}
  	spin_unlock(&pdata->lock);
  	return events ? IRQ_HANDLED : IRQ_NONE;
029641151   Thomas Hommel   rtc: add support ...
216
  }
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
217
218
  static int stk17ta8_rtc_alarm_irq_enable(struct device *dev,
  	unsigned int enabled)
029641151   Thomas Hommel   rtc: add support ...
219
220
221
  {
  	struct platform_device *pdev = to_platform_device(dev);
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
2fac6674d   Anton Vorontsov   rtc: bunch of dri...
222
  	if (pdata->irq <= 0)
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
223
224
  		return -EINVAL;
  	if (enabled)
029641151   Thomas Hommel   rtc: add support ...
225
  		pdata->irqen |= RTC_AF;
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
226
227
228
  	else
  		pdata->irqen &= ~RTC_AF;
  	stk17ta8_rtc_update_alarm(pdata);
029641151   Thomas Hommel   rtc: add support ...
229
230
231
232
  	return 0;
  }
  
  static const struct rtc_class_ops stk17ta8_rtc_ops = {
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
233
234
235
236
237
  	.read_time		= stk17ta8_rtc_read_time,
  	.set_time		= stk17ta8_rtc_set_time,
  	.read_alarm		= stk17ta8_rtc_read_alarm,
  	.set_alarm		= stk17ta8_rtc_set_alarm,
  	.alarm_irq_enable	= stk17ta8_rtc_alarm_irq_enable,
029641151   Thomas Hommel   rtc: add support ...
238
  };
2c3c8bea6   Chris Wright   sysfs: add struct...
239
  static ssize_t stk17ta8_nvram_read(struct file *filp, struct kobject *kobj,
c98dbe59a   Al Viro   fix missing argum...
240
  				 struct bin_attribute *attr, char *buf,
029641151   Thomas Hommel   rtc: add support ...
241
242
  				 loff_t pos, size_t size)
  {
50e49bee3   Atsushi Nemoto   rtc: do not use c...
243
244
  	struct device *dev = container_of(kobj, struct device, kobj);
  	struct platform_device *pdev = to_platform_device(dev);
029641151   Thomas Hommel   rtc: add support ...
245
246
247
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
  	void __iomem *ioaddr = pdata->ioaddr;
  	ssize_t count;
ecc663c3d   Vladimir Zapolskiy   rtc: stk17ta8: cl...
248
  	for (count = 0; count < size; count++)
029641151   Thomas Hommel   rtc: add support ...
249
250
251
  		*buf++ = readb(ioaddr + pos++);
  	return count;
  }
2c3c8bea6   Chris Wright   sysfs: add struct...
252
  static ssize_t stk17ta8_nvram_write(struct file *filp, struct kobject *kobj,
c98dbe59a   Al Viro   fix missing argum...
253
  				  struct bin_attribute *attr, char *buf,
029641151   Thomas Hommel   rtc: add support ...
254
255
  				  loff_t pos, size_t size)
  {
50e49bee3   Atsushi Nemoto   rtc: do not use c...
256
257
  	struct device *dev = container_of(kobj, struct device, kobj);
  	struct platform_device *pdev = to_platform_device(dev);
029641151   Thomas Hommel   rtc: add support ...
258
259
260
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
  	void __iomem *ioaddr = pdata->ioaddr;
  	ssize_t count;
ecc663c3d   Vladimir Zapolskiy   rtc: stk17ta8: cl...
261
  	for (count = 0; count < size; count++)
029641151   Thomas Hommel   rtc: add support ...
262
263
264
265
266
267
268
  		writeb(*buf++, ioaddr + pos++);
  	return count;
  }
  
  static struct bin_attribute stk17ta8_nvram_attr = {
  	.attr = {
  		.name = "nvram",
a4b1d50e6   David Brownell   RTCs: handle NVRA...
269
  		.mode = S_IRUGO | S_IWUSR,
029641151   Thomas Hommel   rtc: add support ...
270
271
272
273
274
  	},
  	.size = RTC_OFFSET,
  	.read = stk17ta8_nvram_read,
  	.write = stk17ta8_nvram_write,
  };
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
275
  static int stk17ta8_rtc_probe(struct platform_device *pdev)
029641151   Thomas Hommel   rtc: add support ...
276
  {
029641151   Thomas Hommel   rtc: add support ...
277
278
279
280
  	struct resource *res;
  	unsigned int cal;
  	unsigned int flags;
  	struct rtc_plat_data *pdata;
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
281
  	void __iomem *ioaddr;
029641151   Thomas Hommel   rtc: add support ...
282
  	int ret = 0;
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
283
  	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
029641151   Thomas Hommel   rtc: add support ...
284
285
  	if (!pdata)
  		return -ENOMEM;
7c1d69ee1   Julia Lawall   rtc: simplify dev...
286
287
288
289
290
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	ioaddr = devm_ioremap_resource(&pdev->dev, res);
  	if (IS_ERR(ioaddr))
  		return PTR_ERR(ioaddr);
029641151   Thomas Hommel   rtc: add support ...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  	pdata->ioaddr = ioaddr;
  	pdata->irq = platform_get_irq(pdev, 0);
  
  	/* turn RTC on if it was not on */
  	cal = readb(ioaddr + RTC_CALIBRATION);
  	if (cal & RTC_STOP) {
  		cal &= RTC_CAL_MASK;
  		flags = readb(ioaddr + RTC_FLAGS);
  		writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS);
  		writeb(cal, ioaddr + RTC_CALIBRATION);
  		writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS);
  	}
  	if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_PF)
  		dev_warn(&pdev->dev, "voltage-low detected.
  ");
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
306
307
308
  	spin_lock_init(&pdata->lock);
  	pdata->last_jiffies = jiffies;
  	platform_set_drvdata(pdev, pdata);
2fac6674d   Anton Vorontsov   rtc: bunch of dri...
309
  	if (pdata->irq > 0) {
029641151   Thomas Hommel   rtc: add support ...
310
  		writeb(0, ioaddr + RTC_INTERRUPTS);
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
311
312
  		if (devm_request_irq(&pdev->dev, pdata->irq,
  				stk17ta8_rtc_interrupt,
2f6e5f945   Yong Zhang   drivers/rtc: remo...
313
  				IRQF_SHARED,
029641151   Thomas Hommel   rtc: add support ...
314
315
316
  				pdev->name, pdev) < 0) {
  			dev_warn(&pdev->dev, "interrupt not available.
  ");
2fac6674d   Anton Vorontsov   rtc: bunch of dri...
317
  			pdata->irq = 0;
029641151   Thomas Hommel   rtc: add support ...
318
319
  		}
  	}
9c6813d7e   Jingoo Han   rtc: rtc-stk17ta8...
320
  	pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
029641151   Thomas Hommel   rtc: add support ...
321
  				  &stk17ta8_rtc_ops, THIS_MODULE);
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
322
323
  	if (IS_ERR(pdata->rtc))
  		return PTR_ERR(pdata->rtc);
b74d2caa6   Alessandro Zummo   rtc: fix driver d...
324

029641151   Thomas Hommel   rtc: add support ...
325
  	ret = sysfs_create_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
9c6813d7e   Jingoo Han   rtc: rtc-stk17ta8...
326

029641151   Thomas Hommel   rtc: add support ...
327
328
  	return ret;
  }
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
329
  static int stk17ta8_rtc_remove(struct platform_device *pdev)
029641151   Thomas Hommel   rtc: add support ...
330
331
332
333
  {
  	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
  
  	sysfs_remove_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
3151520d8   Atsushi Nemoto   rtc-stk17ta8: fix...
334
  	if (pdata->irq > 0)
029641151   Thomas Hommel   rtc: add support ...
335
  		writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
029641151   Thomas Hommel   rtc: add support ...
336
337
  	return 0;
  }
ad28a07bc   Kay Sievers   rtc: fix platform...
338
339
  /* work with hotplug and coldplug */
  MODULE_ALIAS("platform:stk17ta8");
029641151   Thomas Hommel   rtc: add support ...
340
341
  static struct platform_driver stk17ta8_rtc_driver = {
  	.probe		= stk17ta8_rtc_probe,
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
342
  	.remove		= stk17ta8_rtc_remove,
029641151   Thomas Hommel   rtc: add support ...
343
344
  	.driver		= {
  		.name	= "stk17ta8",
029641151   Thomas Hommel   rtc: add support ...
345
346
  	},
  };
0c4eae665   Axel Lin   rtc: convert driv...
347
  module_platform_driver(stk17ta8_rtc_driver);
029641151   Thomas Hommel   rtc: add support ...
348

ab4364d31   Martyn Welch   stk17ta8: renamin...
349
  MODULE_AUTHOR("Thomas Hommel <thomas.hommel@ge.com>");
029641151   Thomas Hommel   rtc: add support ...
350
351
  MODULE_DESCRIPTION("Simtek STK17TA8 RTC driver");
  MODULE_LICENSE("GPL");