Blame view

drivers/rtc/rtc-mrst.c 12 KB
b886d83c5   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
0146f2614   Feng Tang   rtc: Add drivers/...
2
3
4
5
6
7
8
  /*
   * rtc-mrst.c: Driver for Moorestown virtual RTC
   *
   * (C) Copyright 2009 Intel Corporation
   * Author: Jacob Pan (jacob.jun.pan@intel.com)
   *	   Feng Tang (feng.tang@intel.com)
   *
0146f2614   Feng Tang   rtc: Add drivers/...
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   * Note:
   * VRTC is emulated by system controller firmware, the real HW
   * RTC is located in the PMIC device. SCU FW shadows PMIC RTC
   * in a memory mapped IO space that is visible to the host IA
   * processor.
   *
   * This driver is based upon drivers/rtc/rtc-cmos.c
   */
  
  /*
   * Note:
   *  * vRTC only supports binary mode and 24H mode
   *  * vRTC only support PIE and AIE, no UIE, and its PIE only happens
   *    at 23:59:59pm everyday, no support for adjustable frequency
   *  * Alarm function is also limited to hr/min/sec.
   */
  
  #include <linux/mod_devicetable.h>
  #include <linux/platform_device.h>
  #include <linux/interrupt.h>
  #include <linux/spinlock.h>
  #include <linux/kernel.h>
463a86304   Arnd Bergmann   char/genrtc: x86:...
31
  #include <linux/mc146818rtc.h>
0146f2614   Feng Tang   rtc: Add drivers/...
32
33
34
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/sfi.h>
0146f2614   Feng Tang   rtc: Add drivers/...
35
  #include <asm/intel_scu_ipc.h>
05454c26e   Kuppuswamy Sathyanarayanan   intel_mid: Rename...
36
37
  #include <asm/intel-mid.h>
  #include <asm/intel_mid_vrtc.h>
0146f2614   Feng Tang   rtc: Add drivers/...
38
39
40
41
42
  
  struct mrst_rtc {
  	struct rtc_device	*rtc;
  	struct device		*dev;
  	int			irq;
0146f2614   Feng Tang   rtc: Add drivers/...
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  
  	u8			enabled_wake;
  	u8			suspend_ctrl;
  };
  
  static const char driver_name[] = "rtc_mrst";
  
  #define	RTC_IRQMASK	(RTC_PF | RTC_AF)
  
  static inline int is_intr(u8 rtc_intr)
  {
  	if (!(rtc_intr & RTC_IRQF))
  		return 0;
  	return rtc_intr & RTC_IRQMASK;
  }
168202c7b   Feng Tang   mrst/vrtc: Avoid ...
58
59
60
61
62
63
64
65
66
67
  static inline unsigned char vrtc_is_updating(void)
  {
  	unsigned char uip;
  	unsigned long flags;
  
  	spin_lock_irqsave(&rtc_lock, flags);
  	uip = (vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP);
  	spin_unlock_irqrestore(&rtc_lock, flags);
  	return uip;
  }
0146f2614   Feng Tang   rtc: Add drivers/...
68
69
70
  /*
   * rtc_time's year contains the increment over 1900, but vRTC's YEAR
   * register can't be programmed to value larger than 0x64, so vRTC
57e6319dd   Feng Tang   vrtc: change its ...
71
   * driver chose to use 1972 (1970 is UNIX time start point) as the base,
d3e1884bc   Feng Tang   x86, mrst: Add ex...
72
73
   * and does the translation at read/write time.
   *
57e6319dd   Feng Tang   vrtc: change its ...
74
   * Why not just use 1970 as the offset? it's because using 1972 will
d3e1884bc   Feng Tang   x86, mrst: Add ex...
75
   * make it consistent in leap year setting for both vrtc and low-level
57e6319dd   Feng Tang   vrtc: change its ...
76
77
78
79
   * physical rtc devices. Then why not use 1960 as the offset? If we use
   * 1960, for a device's first use, its YEAR register is 0 and the system
   * year will be parsed as 1960 which is not a valid UNIX time and will
   * cause many applications to fail mysteriously.
0146f2614   Feng Tang   rtc: Add drivers/...
80
81
82
83
   */
  static int mrst_read_time(struct device *dev, struct rtc_time *time)
  {
  	unsigned long flags;
168202c7b   Feng Tang   mrst/vrtc: Avoid ...
84
  	if (vrtc_is_updating())
c6b5eb8dd   Jia-Ju Bai   rtc: rtc-mrst: Re...
85
  		msleep(20);
0146f2614   Feng Tang   rtc: Add drivers/...
86
87
88
89
90
91
92
93
94
  
  	spin_lock_irqsave(&rtc_lock, flags);
  	time->tm_sec = vrtc_cmos_read(RTC_SECONDS);
  	time->tm_min = vrtc_cmos_read(RTC_MINUTES);
  	time->tm_hour = vrtc_cmos_read(RTC_HOURS);
  	time->tm_mday = vrtc_cmos_read(RTC_DAY_OF_MONTH);
  	time->tm_mon = vrtc_cmos_read(RTC_MONTH);
  	time->tm_year = vrtc_cmos_read(RTC_YEAR);
  	spin_unlock_irqrestore(&rtc_lock, flags);
57e6319dd   Feng Tang   vrtc: change its ...
95
96
  	/* Adjust for the 1972/1900 */
  	time->tm_year += 72;
0146f2614   Feng Tang   rtc: Add drivers/...
97
  	time->tm_mon--;
22652ba72   Alexandre Belloni   rtc: stop validat...
98
  	return 0;
0146f2614   Feng Tang   rtc: Add drivers/...
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  }
  
  static int mrst_set_time(struct device *dev, struct rtc_time *time)
  {
  	int ret;
  	unsigned long flags;
  	unsigned char mon, day, hrs, min, sec;
  	unsigned int yrs;
  
  	yrs = time->tm_year;
  	mon = time->tm_mon + 1;   /* tm_mon starts at zero */
  	day = time->tm_mday;
  	hrs = time->tm_hour;
  	min = time->tm_min;
  	sec = time->tm_sec;
f441f98f5   Alexandre Belloni   rtc: mrst: remove...
114
  	if (yrs < 72 || yrs > 172)
0146f2614   Feng Tang   rtc: Add drivers/...
115
  		return -EINVAL;
57e6319dd   Feng Tang   vrtc: change its ...
116
  	yrs -= 72;
0146f2614   Feng Tang   rtc: Add drivers/...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  
  	spin_lock_irqsave(&rtc_lock, flags);
  
  	vrtc_cmos_write(yrs, RTC_YEAR);
  	vrtc_cmos_write(mon, RTC_MONTH);
  	vrtc_cmos_write(day, RTC_DAY_OF_MONTH);
  	vrtc_cmos_write(hrs, RTC_HOURS);
  	vrtc_cmos_write(min, RTC_MINUTES);
  	vrtc_cmos_write(sec, RTC_SECONDS);
  
  	spin_unlock_irqrestore(&rtc_lock, flags);
  
  	ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME);
  	return ret;
  }
  
  static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t)
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
  	unsigned char rtc_control;
  
  	if (mrst->irq <= 0)
  		return -EIO;
0146f2614   Feng Tang   rtc: Add drivers/...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
  	/* vRTC only supports binary mode */
  	spin_lock_irq(&rtc_lock);
  	t->time.tm_sec = vrtc_cmos_read(RTC_SECONDS_ALARM);
  	t->time.tm_min = vrtc_cmos_read(RTC_MINUTES_ALARM);
  	t->time.tm_hour = vrtc_cmos_read(RTC_HOURS_ALARM);
  
  	rtc_control = vrtc_cmos_read(RTC_CONTROL);
  	spin_unlock_irq(&rtc_lock);
  
  	t->enabled = !!(rtc_control & RTC_AIE);
  	t->pending = 0;
  
  	return 0;
  }
  
  static void mrst_checkintr(struct mrst_rtc *mrst, unsigned char rtc_control)
  {
  	unsigned char	rtc_intr;
  
  	/*
  	 * NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
  	 * allegedly some older rtcs need that to handle irqs properly
  	 */
  	rtc_intr = vrtc_cmos_read(RTC_INTR_FLAGS);
  	rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
  	if (is_intr(rtc_intr))
  		rtc_update_irq(mrst->rtc, 1, rtc_intr);
  }
  
  static void mrst_irq_enable(struct mrst_rtc *mrst, unsigned char mask)
  {
  	unsigned char	rtc_control;
  
  	/*
  	 * Flush any pending IRQ status, notably for update irqs,
  	 * before we enable new IRQs
  	 */
  	rtc_control = vrtc_cmos_read(RTC_CONTROL);
  	mrst_checkintr(mrst, rtc_control);
  
  	rtc_control |= mask;
  	vrtc_cmos_write(rtc_control, RTC_CONTROL);
  
  	mrst_checkintr(mrst, rtc_control);
  }
  
  static void mrst_irq_disable(struct mrst_rtc *mrst, unsigned char mask)
  {
  	unsigned char	rtc_control;
  
  	rtc_control = vrtc_cmos_read(RTC_CONTROL);
  	rtc_control &= ~mask;
  	vrtc_cmos_write(rtc_control, RTC_CONTROL);
  	mrst_checkintr(mrst, rtc_control);
  }
  
  static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
  	unsigned char hrs, min, sec;
  	int ret = 0;
  
  	if (!mrst->irq)
  		return -EIO;
  
  	hrs = t->time.tm_hour;
  	min = t->time.tm_min;
  	sec = t->time.tm_sec;
  
  	spin_lock_irq(&rtc_lock);
  	/* Next rtc irq must not be from previous alarm setting */
  	mrst_irq_disable(mrst, RTC_AIE);
  
  	/* Update alarm */
  	vrtc_cmos_write(hrs, RTC_HOURS_ALARM);
  	vrtc_cmos_write(min, RTC_MINUTES_ALARM);
  	vrtc_cmos_write(sec, RTC_SECONDS_ALARM);
  
  	spin_unlock_irq(&rtc_lock);
  
  	ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM);
  	if (ret)
  		return ret;
  
  	spin_lock_irq(&rtc_lock);
  	if (t->enabled)
  		mrst_irq_enable(mrst, RTC_AIE);
  
  	spin_unlock_irq(&rtc_lock);
  
  	return 0;
  }
0146f2614   Feng Tang   rtc: Add drivers/...
232
  /* Currently, the vRTC doesn't support UIE ON/OFF */
16380c153   John Stultz   RTC: Convert rtc ...
233
  static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
0146f2614   Feng Tang   rtc: Add drivers/...
234
235
236
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
  	unsigned long	flags;
0146f2614   Feng Tang   rtc: Add drivers/...
237
  	spin_lock_irqsave(&rtc_lock, flags);
16380c153   John Stultz   RTC: Convert rtc ...
238
  	if (enabled)
0146f2614   Feng Tang   rtc: Add drivers/...
239
  		mrst_irq_enable(mrst, RTC_AIE);
16380c153   John Stultz   RTC: Convert rtc ...
240
241
  	else
  		mrst_irq_disable(mrst, RTC_AIE);
0146f2614   Feng Tang   rtc: Add drivers/...
242
243
244
  	spin_unlock_irqrestore(&rtc_lock, flags);
  	return 0;
  }
0146f2614   Feng Tang   rtc: Add drivers/...
245

6fca3fc51   Javier Martinez Canillas   rtc: Use IS_ENABL...
246
  #if IS_ENABLED(CONFIG_RTC_INTF_PROC)
0146f2614   Feng Tang   rtc: Add drivers/...
247
248
249
  
  static int mrst_procfs(struct device *dev, struct seq_file *seq)
  {
de96bc399   YueHaibing   rtc: mrst: remove...
250
  	unsigned char	rtc_control;
0146f2614   Feng Tang   rtc: Add drivers/...
251
252
253
  
  	spin_lock_irq(&rtc_lock);
  	rtc_control = vrtc_cmos_read(RTC_CONTROL);
0146f2614   Feng Tang   rtc: Add drivers/...
254
  	spin_unlock_irq(&rtc_lock);
4395eb1f1   Joe Perches   rtc: remove use o...
255
256
257
258
259
260
261
262
263
264
265
266
267
  	seq_printf(seq,
  		   "periodic_IRQ\t: %s
  "
  		   "alarm\t\t: %s
  "
  		   "BCD\t\t: no
  "
  		   "periodic_freq\t: daily (not adjustable)
  ",
  		   (rtc_control & RTC_PIE) ? "on" : "off",
  		   (rtc_control & RTC_AIE) ? "on" : "off");
  
  	return 0;
0146f2614   Feng Tang   rtc: Add drivers/...
268
269
270
271
272
273
274
  }
  
  #else
  #define	mrst_procfs	NULL
  #endif
  
  static const struct rtc_class_ops mrst_rtc_ops = {
0146f2614   Feng Tang   rtc: Add drivers/...
275
276
277
278
279
  	.read_time	= mrst_read_time,
  	.set_time	= mrst_set_time,
  	.read_alarm	= mrst_read_alarm,
  	.set_alarm	= mrst_set_alarm,
  	.proc		= mrst_procfs,
16380c153   John Stultz   RTC: Convert rtc ...
280
  	.alarm_irq_enable = mrst_rtc_alarm_irq_enable,
0146f2614   Feng Tang   rtc: Add drivers/...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
  };
  
  static struct mrst_rtc	mrst_rtc;
  
  /*
   * When vRTC IRQ is captured by SCU FW, FW will clear the AIE bit in
   * Reg B, so no need for this driver to clear it
   */
  static irqreturn_t mrst_rtc_irq(int irq, void *p)
  {
  	u8 irqstat;
  
  	spin_lock(&rtc_lock);
  	/* This read will clear all IRQ flags inside Reg C */
  	irqstat = vrtc_cmos_read(RTC_INTR_FLAGS);
  	spin_unlock(&rtc_lock);
  
  	irqstat &= RTC_IRQMASK | RTC_IRQF;
  	if (is_intr(irqstat)) {
  		rtc_update_irq(p, 1, irqstat);
  		return IRQ_HANDLED;
  	}
  	return IRQ_NONE;
  }
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
305
306
  static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem,
  			      int rtc_irq)
0146f2614   Feng Tang   rtc: Add drivers/...
307
308
309
310
311
312
313
314
315
316
  {
  	int retval = 0;
  	unsigned char rtc_control;
  
  	/* There can be only one ... */
  	if (mrst_rtc.dev)
  		return -EBUSY;
  
  	if (!iomem)
  		return -ENODEV;
32b41f93d   Alexandre Belloni   rtc: mrst: switch...
317
318
  	iomem = devm_request_mem_region(dev, iomem->start, resource_size(iomem),
  					driver_name);
0146f2614   Feng Tang   rtc: Add drivers/...
319
320
321
322
323
324
325
  	if (!iomem) {
  		dev_dbg(dev, "i/o mem already in use.
  ");
  		return -EBUSY;
  	}
  
  	mrst_rtc.irq = rtc_irq;
de97a21a2   Feng Tang   rtc, x86/mrst/vrt...
326
327
  	mrst_rtc.dev = dev;
  	dev_set_drvdata(dev, &mrst_rtc);
0146f2614   Feng Tang   rtc: Add drivers/...
328

32b41f93d   Alexandre Belloni   rtc: mrst: switch...
329
330
331
332
333
  	mrst_rtc.rtc = devm_rtc_allocate_device(dev);
  	if (IS_ERR(mrst_rtc.rtc))
  		return PTR_ERR(mrst_rtc.rtc);
  
  	mrst_rtc.rtc->ops = &mrst_rtc_ops;
0146f2614   Feng Tang   rtc: Add drivers/...
334

0146f2614   Feng Tang   rtc: Add drivers/...
335
336
337
338
339
340
341
342
343
344
345
346
  	rename_region(iomem, dev_name(&mrst_rtc.rtc->dev));
  
  	spin_lock_irq(&rtc_lock);
  	mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE);
  	rtc_control = vrtc_cmos_read(RTC_CONTROL);
  	spin_unlock_irq(&rtc_lock);
  
  	if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))
  		dev_dbg(dev, "TODO: support more than 24-hr BCD mode
  ");
  
  	if (rtc_irq) {
32b41f93d   Alexandre Belloni   rtc: mrst: switch...
347
348
349
  		retval = devm_request_irq(dev, rtc_irq, mrst_rtc_irq,
  					  0, dev_name(&mrst_rtc.rtc->dev),
  					  mrst_rtc.rtc);
0146f2614   Feng Tang   rtc: Add drivers/...
350
351
352
353
  		if (retval < 0) {
  			dev_dbg(dev, "IRQ %d is already in use, err %d
  ",
  				rtc_irq, retval);
32b41f93d   Alexandre Belloni   rtc: mrst: switch...
354
  			goto cleanup0;
0146f2614   Feng Tang   rtc: Add drivers/...
355
356
  		}
  	}
32b41f93d   Alexandre Belloni   rtc: mrst: switch...
357
358
  
  	retval = rtc_register_device(mrst_rtc.rtc);
ca95ef7c9   Dan Carpenter   rtc: mrst: fix er...
359
  	if (retval)
32b41f93d   Alexandre Belloni   rtc: mrst: switch...
360
  		goto cleanup0;
32b41f93d   Alexandre Belloni   rtc: mrst: switch...
361

0146f2614   Feng Tang   rtc: Add drivers/...
362
363
364
  	dev_dbg(dev, "initialised
  ");
  	return 0;
0146f2614   Feng Tang   rtc: Add drivers/...
365
  cleanup0:
de97a21a2   Feng Tang   rtc, x86/mrst/vrt...
366
  	mrst_rtc.dev = NULL;
0146f2614   Feng Tang   rtc: Add drivers/...
367
368
369
370
371
372
373
374
375
376
377
  	dev_err(dev, "rtc-mrst: unable to initialise
  ");
  	return retval;
  }
  
  static void rtc_mrst_do_shutdown(void)
  {
  	spin_lock_irq(&rtc_lock);
  	mrst_irq_disable(&mrst_rtc, RTC_IRQMASK);
  	spin_unlock_irq(&rtc_lock);
  }
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
378
  static void rtc_mrst_do_remove(struct device *dev)
0146f2614   Feng Tang   rtc: Add drivers/...
379
380
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
0146f2614   Feng Tang   rtc: Add drivers/...
381
382
  
  	rtc_mrst_do_shutdown();
0146f2614   Feng Tang   rtc: Add drivers/...
383
  	mrst->rtc = NULL;
0146f2614   Feng Tang   rtc: Add drivers/...
384
  	mrst->dev = NULL;
0146f2614   Feng Tang   rtc: Add drivers/...
385
  }
ddd2a30d4   Lars-Peter Clausen   drivers/rtc/rtc-m...
386
387
  #ifdef CONFIG_PM_SLEEP
  static int mrst_suspend(struct device *dev)
0146f2614   Feng Tang   rtc: Add drivers/...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
  	unsigned char	tmp;
  
  	/* Only the alarm might be a wakeup event source */
  	spin_lock_irq(&rtc_lock);
  	mrst->suspend_ctrl = tmp = vrtc_cmos_read(RTC_CONTROL);
  	if (tmp & (RTC_PIE | RTC_AIE)) {
  		unsigned char	mask;
  
  		if (device_may_wakeup(dev))
  			mask = RTC_IRQMASK & ~RTC_AIE;
  		else
  			mask = RTC_IRQMASK;
  		tmp &= ~mask;
  		vrtc_cmos_write(tmp, RTC_CONTROL);
  
  		mrst_checkintr(mrst, tmp);
  	}
  	spin_unlock_irq(&rtc_lock);
  
  	if (tmp & RTC_AIE) {
  		mrst->enabled_wake = 1;
  		enable_irq_wake(mrst->irq);
  	}
  
  	dev_dbg(&mrst_rtc.rtc->dev, "suspend%s, ctrl %02x
  ",
  			(tmp & RTC_AIE) ? ", alarm may wake" : "",
  			tmp);
  
  	return 0;
  }
  
  /*
   * We want RTC alarms to wake us from the deep power saving state
   */
  static inline int mrst_poweroff(struct device *dev)
  {
ddd2a30d4   Lars-Peter Clausen   drivers/rtc/rtc-m...
427
  	return mrst_suspend(dev);
0146f2614   Feng Tang   rtc: Add drivers/...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  }
  
  static int mrst_resume(struct device *dev)
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
  	unsigned char tmp = mrst->suspend_ctrl;
  
  	/* Re-enable any irqs previously active */
  	if (tmp & RTC_IRQMASK) {
  		unsigned char	mask;
  
  		if (mrst->enabled_wake) {
  			disable_irq_wake(mrst->irq);
  			mrst->enabled_wake = 0;
  		}
  
  		spin_lock_irq(&rtc_lock);
  		do {
  			vrtc_cmos_write(tmp, RTC_CONTROL);
  
  			mask = vrtc_cmos_read(RTC_INTR_FLAGS);
  			mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
  			if (!is_intr(mask))
  				break;
  
  			rtc_update_irq(mrst->rtc, 1, mask);
  			tmp &= ~RTC_AIE;
  		} while (mask & RTC_AIE);
  		spin_unlock_irq(&rtc_lock);
  	}
  
  	dev_dbg(&mrst_rtc.rtc->dev, "resume, ctrl %02x
  ", tmp);
  
  	return 0;
  }
ddd2a30d4   Lars-Peter Clausen   drivers/rtc/rtc-m...
464
465
  static SIMPLE_DEV_PM_OPS(mrst_pm_ops, mrst_suspend, mrst_resume);
  #define MRST_PM_OPS (&mrst_pm_ops)
0146f2614   Feng Tang   rtc: Add drivers/...
466
  #else
ddd2a30d4   Lars-Peter Clausen   drivers/rtc/rtc-m...
467
  #define MRST_PM_OPS NULL
0146f2614   Feng Tang   rtc: Add drivers/...
468
469
470
471
472
473
474
  
  static inline int mrst_poweroff(struct device *dev)
  {
  	return -ENOSYS;
  }
  
  #endif
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
475
  static int vrtc_mrst_platform_probe(struct platform_device *pdev)
0146f2614   Feng Tang   rtc: Add drivers/...
476
477
478
479
480
  {
  	return vrtc_mrst_do_probe(&pdev->dev,
  			platform_get_resource(pdev, IORESOURCE_MEM, 0),
  			platform_get_irq(pdev, 0));
  }
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
481
  static int vrtc_mrst_platform_remove(struct platform_device *pdev)
0146f2614   Feng Tang   rtc: Add drivers/...
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  {
  	rtc_mrst_do_remove(&pdev->dev);
  	return 0;
  }
  
  static void vrtc_mrst_platform_shutdown(struct platform_device *pdev)
  {
  	if (system_state == SYSTEM_POWER_OFF && !mrst_poweroff(&pdev->dev))
  		return;
  
  	rtc_mrst_do_shutdown();
  }
  
  MODULE_ALIAS("platform:vrtc_mrst");
  
  static struct platform_driver vrtc_mrst_platform_driver = {
  	.probe		= vrtc_mrst_platform_probe,
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
499
  	.remove		= vrtc_mrst_platform_remove,
0146f2614   Feng Tang   rtc: Add drivers/...
500
501
  	.shutdown	= vrtc_mrst_platform_shutdown,
  	.driver = {
ddd2a30d4   Lars-Peter Clausen   drivers/rtc/rtc-m...
502
503
  		.name	= driver_name,
  		.pm	= MRST_PM_OPS,
0146f2614   Feng Tang   rtc: Add drivers/...
504
505
  	}
  };
0c4eae665   Axel Lin   rtc: convert driv...
506
  module_platform_driver(vrtc_mrst_platform_driver);
0146f2614   Feng Tang   rtc: Add drivers/...
507
508
509
510
  
  MODULE_AUTHOR("Jacob Pan; Feng Tang");
  MODULE_DESCRIPTION("Driver for Moorestown virtual RTC");
  MODULE_LICENSE("GPL");