Blame view

drivers/rtc/rtc-mrst.c 12.8 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
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
  /*
   * 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>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/sfi.h>
  
  #include <asm-generic/rtc.h>
  #include <asm/intel_scu_ipc.h>
  #include <asm/mrst.h>
  #include <asm/mrst-vrtc.h>
  
  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 ...
64
65
66
67
68
69
70
71
72
73
  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/...
74
75
76
  /*
   * 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 ...
77
   * driver chose to use 1972 (1970 is UNIX time start point) as the base,
d3e1884bc   Feng Tang   x86, mrst: Add ex...
78
79
   * and does the translation at read/write time.
   *
57e6319dd   Feng Tang   vrtc: change its ...
80
   * Why not just use 1970 as the offset? it's because using 1972 will
d3e1884bc   Feng Tang   x86, mrst: Add ex...
81
   * make it consistent in leap year setting for both vrtc and low-level
57e6319dd   Feng Tang   vrtc: change its ...
82
83
84
85
   * 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/...
86
87
88
89
   */
  static int mrst_read_time(struct device *dev, struct rtc_time *time)
  {
  	unsigned long flags;
168202c7b   Feng Tang   mrst/vrtc: Avoid ...
90
  	if (vrtc_is_updating())
0146f2614   Feng Tang   rtc: Add drivers/...
91
92
93
94
95
96
97
98
99
100
  		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 ...
101
102
  	/* Adjust for the 1972/1900 */
  	time->tm_year += 72;
0146f2614   Feng Tang   rtc: Add drivers/...
103
  	time->tm_mon--;
57e6319dd   Feng Tang   vrtc: change its ...
104
  	return rtc_valid_tm(time);
0146f2614   Feng Tang   rtc: Add drivers/...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  }
  
  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 ...
120
  	if (yrs < 72 || yrs > 138)
0146f2614   Feng Tang   rtc: Add drivers/...
121
  		return -EINVAL;
57e6319dd   Feng Tang   vrtc: change its ...
122
  	yrs -= 72;
0146f2614   Feng Tang   rtc: Add drivers/...
123
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
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
237
238
239
240
241
242
243
244
245
246
  
  	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;
  
  	/* Basic alarms only support hour, minute, and seconds fields.
  	 * Some also support day and month, for alarms up to a year in
  	 * the future.
  	 */
  	t->time.tm_mday = -1;
  	t->time.tm_mon = -1;
  	t->time.tm_year = -1;
  
  	/* 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/...
247
  /* Currently, the vRTC doesn't support UIE ON/OFF */
16380c153   John Stultz   RTC: Convert rtc ...
248
  static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
0146f2614   Feng Tang   rtc: Add drivers/...
249
250
251
  {
  	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
  	unsigned long	flags;
0146f2614   Feng Tang   rtc: Add drivers/...
252
  	spin_lock_irqsave(&rtc_lock, flags);
16380c153   John Stultz   RTC: Convert rtc ...
253
  	if (enabled)
0146f2614   Feng Tang   rtc: Add drivers/...
254
  		mrst_irq_enable(mrst, RTC_AIE);
16380c153   John Stultz   RTC: Convert rtc ...
255
256
  	else
  		mrst_irq_disable(mrst, RTC_AIE);
0146f2614   Feng Tang   rtc: Add drivers/...
257
258
259
  	spin_unlock_irqrestore(&rtc_lock, flags);
  	return 0;
  }
0146f2614   Feng Tang   rtc: Add drivers/...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  
  #if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
  
  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);
  
  	return 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");
  }
  
  #else
  #define	mrst_procfs	NULL
  #endif
  
  static const struct rtc_class_ops mrst_rtc_ops = {
0146f2614   Feng Tang   rtc: Add drivers/...
290
291
292
293
294
  	.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 ...
295
  	.alarm_irq_enable = mrst_rtc_alarm_irq_enable,
0146f2614   Feng Tang   rtc: Add drivers/...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  };
  
  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;
  }
b3b896c73   Major Lee   rtc-mrst: Fix sec...
320
  static int __devinit
0146f2614   Feng Tang   rtc: Add drivers/...
321
322
323
324
325
326
327
328
329
330
331
  vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, int rtc_irq)
  {
  	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...
332
333
  	iomem = request_mem_region(iomem->start, resource_size(iomem),
  				   driver_name);
0146f2614   Feng Tang   rtc: Add drivers/...
334
335
336
337
338
339
340
341
  	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...
342
343
  	mrst_rtc.dev = dev;
  	dev_set_drvdata(dev, &mrst_rtc);
0146f2614   Feng Tang   rtc: Add drivers/...
344
345
346
347
348
349
350
  
  	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/...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  	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,
  				IRQF_DISABLED, dev_name(&mrst_rtc.rtc->dev),
  				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/...
378
379
  	rtc_device_unregister(mrst_rtc.rtc);
  cleanup0:
de97a21a2   Feng Tang   rtc, x86/mrst/vrt...
380
381
  	dev_set_drvdata(dev, NULL);
  	mrst_rtc.dev = NULL;
c258f9a0a   Julia Lawall   drivers/rtc/rtc-m...
382
  	release_mem_region(iomem->start, resource_size(iomem));
0146f2614   Feng Tang   rtc: Add drivers/...
383
384
385
386
387
388
389
390
391
392
393
  	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);
  }
b3b896c73   Major Lee   rtc-mrst: Fix sec...
394
  static void __devexit rtc_mrst_do_remove(struct device *dev)
0146f2614   Feng Tang   rtc: Add drivers/...
395
396
397
398
399
400
401
402
403
404
405
406
407
  {
  	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...
408
  	release_mem_region(iomem->start, resource_size(iomem));
0146f2614   Feng Tang   rtc: Add drivers/...
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
444
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
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
  	mrst->iomem = NULL;
  
  	mrst->dev = NULL;
  	dev_set_drvdata(dev, NULL);
  }
  
  #ifdef	CONFIG_PM
  static int mrst_suspend(struct device *dev, pm_message_t mesg)
  {
  	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)
  {
  	return mrst_suspend(dev, PMSG_HIBERNATE);
  }
  
  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;
  }
  
  #else
  #define	mrst_suspend	NULL
  #define	mrst_resume	NULL
  
  static inline int mrst_poweroff(struct device *dev)
  {
  	return -ENOSYS;
  }
  
  #endif
b3b896c73   Major Lee   rtc-mrst: Fix sec...
504
  static int __devinit vrtc_mrst_platform_probe(struct platform_device *pdev)
0146f2614   Feng Tang   rtc: Add drivers/...
505
506
507
508
509
  {
  	return vrtc_mrst_do_probe(&pdev->dev,
  			platform_get_resource(pdev, IORESOURCE_MEM, 0),
  			platform_get_irq(pdev, 0));
  }
b3b896c73   Major Lee   rtc-mrst: Fix sec...
510
  static int __devexit vrtc_mrst_platform_remove(struct platform_device *pdev)
0146f2614   Feng Tang   rtc: Add drivers/...
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  {
  	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,
b3b896c73   Major Lee   rtc-mrst: Fix sec...
528
  	.remove		= __devexit_p(vrtc_mrst_platform_remove),
0146f2614   Feng Tang   rtc: Add drivers/...
529
530
531
532
533
534
535
  	.shutdown	= vrtc_mrst_platform_shutdown,
  	.driver = {
  		.name		= (char *) driver_name,
  		.suspend	= mrst_suspend,
  		.resume		= mrst_resume,
  	}
  };
0c4eae665   Axel Lin   rtc: convert driv...
536
  module_platform_driver(vrtc_mrst_platform_driver);
0146f2614   Feng Tang   rtc: Add drivers/...
537
538
539
540
  
  MODULE_AUTHOR("Jacob Pan; Feng Tang");
  MODULE_DESCRIPTION("Driver for Moorestown virtual RTC");
  MODULE_LICENSE("GPL");