Blame view

drivers/rtc/rtc-mrst.c 12.5 KB
0146f2614   Feng Tang   rtc: Add drivers/...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  /*
   * 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)
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; version 2
   * of the License.
   *
   * 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:...
35
  #include <linux/mc146818rtc.h>
0146f2614   Feng Tang   rtc: Add drivers/...
36
37
38
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/sfi.h>
0146f2614   Feng Tang   rtc: Add drivers/...
39
  #include <asm/intel_scu_ipc.h>
05454c26e   Kuppuswamy Sathyanarayanan   intel_mid: Rename...
40
41
  #include <asm/intel-mid.h>
  #include <asm/intel_mid_vrtc.h>
0146f2614   Feng Tang   rtc: Add drivers/...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  
  struct mrst_rtc {
  	struct rtc_device	*rtc;
  	struct device		*dev;
  	int			irq;
  	struct resource		*iomem;
  
  	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 ...
63
64
65
66
67
68
69
70
71
72
  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/...
73
74
75
  /*
   * 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 ...
76
   * driver chose to use 1972 (1970 is UNIX time start point) as the base,
d3e1884bc   Feng Tang   x86, mrst: Add ex...
77
78
   * and does the translation at read/write time.
   *
57e6319dd   Feng Tang   vrtc: change its ...
79
   * Why not just use 1970 as the offset? it's because using 1972 will
d3e1884bc   Feng Tang   x86, mrst: Add ex...
80
   * make it consistent in leap year setting for both vrtc and low-level
57e6319dd   Feng Tang   vrtc: change its ...
81
82
83
84
   * 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/...
85
86
87
88
   */
  static int mrst_read_time(struct device *dev, struct rtc_time *time)
  {
  	unsigned long flags;
168202c7b   Feng Tang   mrst/vrtc: Avoid ...
89
  	if (vrtc_is_updating())
0146f2614   Feng Tang   rtc: Add drivers/...
90
91
92
93
94
95
96
97
98
99
  		mdelay(20);
  
  	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 ...
100
101
  	/* Adjust for the 1972/1900 */
  	time->tm_year += 72;
0146f2614   Feng Tang   rtc: Add drivers/...
102
  	time->tm_mon--;
57e6319dd   Feng Tang   vrtc: change its ...
103
  	return rtc_valid_tm(time);
0146f2614   Feng Tang   rtc: Add drivers/...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  }
  
  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;
57e6319dd   Feng Tang   vrtc: change its ...
119
  	if (yrs < 72 || yrs > 138)
0146f2614   Feng Tang   rtc: Add drivers/...
120
  		return -EINVAL;
57e6319dd   Feng Tang   vrtc: change its ...
121
  	yrs -= 72;
0146f2614   Feng Tang   rtc: Add drivers/...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  
  	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/...
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
232
233
234
235
236
  	/* 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/...
237
  /* Currently, the vRTC doesn't support UIE ON/OFF */
16380c153   John Stultz   RTC: Convert rtc ...
238
  static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
0146f2614   Feng Tang   rtc: Add drivers/...
239
240
241
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
  	unsigned long	flags;
0146f2614   Feng Tang   rtc: Add drivers/...
242
  	spin_lock_irqsave(&rtc_lock, flags);
16380c153   John Stultz   RTC: Convert rtc ...
243
  	if (enabled)
0146f2614   Feng Tang   rtc: Add drivers/...
244
  		mrst_irq_enable(mrst, RTC_AIE);
16380c153   John Stultz   RTC: Convert rtc ...
245
246
  	else
  		mrst_irq_disable(mrst, RTC_AIE);
0146f2614   Feng Tang   rtc: Add drivers/...
247
248
249
  	spin_unlock_irqrestore(&rtc_lock, flags);
  	return 0;
  }
0146f2614   Feng Tang   rtc: Add drivers/...
250

6fca3fc51   Javier Martinez Canillas   rtc: Use IS_ENABL...
251
  #if IS_ENABLED(CONFIG_RTC_INTF_PROC)
0146f2614   Feng Tang   rtc: Add drivers/...
252
253
254
255
256
257
258
259
260
  
  static int mrst_procfs(struct device *dev, struct seq_file *seq)
  {
  	unsigned char	rtc_control, valid;
  
  	spin_lock_irq(&rtc_lock);
  	rtc_control = vrtc_cmos_read(RTC_CONTROL);
  	valid = vrtc_cmos_read(RTC_VALID);
  	spin_unlock_irq(&rtc_lock);
4395eb1f1   Joe Perches   rtc: remove use o...
261
262
263
264
265
266
267
268
269
270
271
272
273
  	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/...
274
275
276
277
278
279
280
  }
  
  #else
  #define	mrst_procfs	NULL
  #endif
  
  static const struct rtc_class_ops mrst_rtc_ops = {
0146f2614   Feng Tang   rtc: Add drivers/...
281
282
283
284
285
  	.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 ...
286
  	.alarm_irq_enable = mrst_rtc_alarm_irq_enable,
0146f2614   Feng Tang   rtc: Add drivers/...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  };
  
  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...
311
312
  static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem,
  			      int rtc_irq)
0146f2614   Feng Tang   rtc: Add drivers/...
313
314
315
316
317
318
319
320
321
322
  {
  	int retval = 0;
  	unsigned char rtc_control;
  
  	/* There can be only one ... */
  	if (mrst_rtc.dev)
  		return -EBUSY;
  
  	if (!iomem)
  		return -ENODEV;
28f65c11f   Joe Perches   treewide: Convert...
323
324
  	iomem = request_mem_region(iomem->start, resource_size(iomem),
  				   driver_name);
0146f2614   Feng Tang   rtc: Add drivers/...
325
326
327
328
329
330
331
332
  	if (!iomem) {
  		dev_dbg(dev, "i/o mem already in use.
  ");
  		return -EBUSY;
  	}
  
  	mrst_rtc.irq = rtc_irq;
  	mrst_rtc.iomem = iomem;
de97a21a2   Feng Tang   rtc, x86/mrst/vrt...
333
334
  	mrst_rtc.dev = dev;
  	dev_set_drvdata(dev, &mrst_rtc);
0146f2614   Feng Tang   rtc: Add drivers/...
335
336
337
338
339
340
341
  
  	mrst_rtc.rtc = rtc_device_register(driver_name, dev,
  				&mrst_rtc_ops, THIS_MODULE);
  	if (IS_ERR(mrst_rtc.rtc)) {
  		retval = PTR_ERR(mrst_rtc.rtc);
  		goto cleanup0;
  	}
0146f2614   Feng Tang   rtc: Add drivers/...
342
343
344
345
346
347
348
349
350
351
352
353
354
  	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) {
  		retval = request_irq(rtc_irq, mrst_rtc_irq,
2f6e5f945   Yong Zhang   drivers/rtc: remo...
355
  				0, dev_name(&mrst_rtc.rtc->dev),
0146f2614   Feng Tang   rtc: Add drivers/...
356
357
358
359
360
361
362
363
364
365
366
367
368
  				mrst_rtc.rtc);
  		if (retval < 0) {
  			dev_dbg(dev, "IRQ %d is already in use, err %d
  ",
  				rtc_irq, retval);
  			goto cleanup1;
  		}
  	}
  	dev_dbg(dev, "initialised
  ");
  	return 0;
  
  cleanup1:
0146f2614   Feng Tang   rtc: Add drivers/...
369
370
  	rtc_device_unregister(mrst_rtc.rtc);
  cleanup0:
de97a21a2   Feng Tang   rtc, x86/mrst/vrt...
371
  	mrst_rtc.dev = NULL;
c258f9a0a   Julia Lawall   drivers/rtc/rtc-m...
372
  	release_mem_region(iomem->start, resource_size(iomem));
0146f2614   Feng Tang   rtc: Add drivers/...
373
374
375
376
377
378
379
380
381
382
383
  	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...
384
  static void rtc_mrst_do_remove(struct device *dev)
0146f2614   Feng Tang   rtc: Add drivers/...
385
386
387
388
389
390
391
392
393
394
395
396
397
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
  	struct resource *iomem;
  
  	rtc_mrst_do_shutdown();
  
  	if (mrst->irq)
  		free_irq(mrst->irq, mrst->rtc);
  
  	rtc_device_unregister(mrst->rtc);
  	mrst->rtc = NULL;
  
  	iomem = mrst->iomem;
c258f9a0a   Julia Lawall   drivers/rtc/rtc-m...
398
  	release_mem_region(iomem->start, resource_size(iomem));
0146f2614   Feng Tang   rtc: Add drivers/...
399
400
401
  	mrst->iomem = NULL;
  
  	mrst->dev = NULL;
0146f2614   Feng Tang   rtc: Add drivers/...
402
  }
ddd2a30d4   Lars-Peter Clausen   drivers/rtc/rtc-m...
403
404
  #ifdef CONFIG_PM_SLEEP
  static int mrst_suspend(struct device *dev)
0146f2614   Feng Tang   rtc: Add drivers/...
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  {
  	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...
444
  	return mrst_suspend(dev);
0146f2614   Feng Tang   rtc: Add drivers/...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  }
  
  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...
481
482
  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/...
483
  #else
ddd2a30d4   Lars-Peter Clausen   drivers/rtc/rtc-m...
484
  #define MRST_PM_OPS NULL
0146f2614   Feng Tang   rtc: Add drivers/...
485
486
487
488
489
490
491
  
  static inline int mrst_poweroff(struct device *dev)
  {
  	return -ENOSYS;
  }
  
  #endif
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
492
  static int vrtc_mrst_platform_probe(struct platform_device *pdev)
0146f2614   Feng Tang   rtc: Add drivers/...
493
494
495
496
497
  {
  	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...
498
  static int vrtc_mrst_platform_remove(struct platform_device *pdev)
0146f2614   Feng Tang   rtc: Add drivers/...
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
  {
  	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...
516
  	.remove		= vrtc_mrst_platform_remove,
0146f2614   Feng Tang   rtc: Add drivers/...
517
518
  	.shutdown	= vrtc_mrst_platform_shutdown,
  	.driver = {
ddd2a30d4   Lars-Peter Clausen   drivers/rtc/rtc-m...
519
520
  		.name	= driver_name,
  		.pm	= MRST_PM_OPS,
0146f2614   Feng Tang   rtc: Add drivers/...
521
522
  	}
  };
0c4eae665   Axel Lin   rtc: convert driv...
523
  module_platform_driver(vrtc_mrst_platform_driver);
0146f2614   Feng Tang   rtc: Add drivers/...
524
525
526
527
  
  MODULE_AUTHOR("Jacob Pan; Feng Tang");
  MODULE_DESCRIPTION("Driver for Moorestown virtual RTC");
  MODULE_LICENSE("GPL");