x86: mrst: Add vrtc driver which serves as a wall clock device

Moorestown platform doesn't have a m146818 RTC device like traditional
x86 PC, but a firmware emulated virtual RTC device(vrtc), which provides
some basic RTC functions like get/set time. vrtc serves as the only
wall clock device on Moorestown platform.

[ tglx: Changed the exports to _GPL ]

Signed-off-by: Feng Tang <feng.tang@intel.com>
Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
LKML-Reference: <20101110172837.3311.40483.stgit@localhost.localdomain>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by

Feng Tang and committed by
Thomas Gleixner
7309282c cfb505a7

+143 -7
+4
arch/x86/include/asm/fixmap.h
··· 117 117 FIX_TEXT_POKE1, /* reserve 2 pages for text_poke() */ 118 118 FIX_TEXT_POKE0, /* first page is last, because allocation is backward */ 119 119 __end_of_permanent_fixed_addresses, 120 + 121 + #ifdef CONFIG_X86_MRST 122 + FIX_LNW_VRTC, 123 + #endif 120 124 /* 121 125 * 256 temporary boot-time mappings, used by early_ioremap(), 122 126 * before ioremap() is functional.
+9
arch/x86/include/asm/mrst-vrtc.h
··· 1 + #ifndef _MRST_VRTC_H 2 + #define _MRST_VRTC_H 3 + 4 + extern unsigned char vrtc_cmos_read(unsigned char reg); 5 + extern void vrtc_cmos_write(unsigned char val, unsigned char reg); 6 + extern unsigned long vrtc_get_time(void); 7 + extern int vrtc_set_mmss(unsigned long nowtime); 8 + 9 + #endif
+9 -1
arch/x86/include/asm/mrst.h
··· 14 14 #include <linux/sfi.h> 15 15 16 16 extern int pci_mrst_init(void); 17 - int __init sfi_parse_mrtc(struct sfi_table_header *table); 17 + extern int __init sfi_parse_mrtc(struct sfi_table_header *table); 18 + extern int sfi_mrtc_num; 19 + extern struct sfi_rtc_table_entry sfi_mrtc_array[]; 18 20 19 21 /* 20 22 * Medfield is the follow-up of Moorestown, it combines two chip solution into ··· 55 53 56 54 extern void intel_scu_devices_create(void); 57 55 extern void intel_scu_devices_destroy(void); 56 + 57 + /* VRTC timer */ 58 + #define MRST_VRTC_MAP_SZ (1024) 59 + /*#define MRST_VRTC_PGOFFSET (0xc00) */ 60 + 61 + extern void mrst_rtc_init(void); 58 62 59 63 #endif /* _ASM_X86_MRST_H */
+1
arch/x86/platform/mrst/Makefile
··· 1 1 obj-$(CONFIG_X86_MRST) += mrst.o 2 + obj-$(CONFIG_X86_MRST) += vrtc.o
-6
arch/x86/platform/mrst/mrst.c
··· 37 37 #include <asm/apb_timer.h> 38 38 #include <asm/reboot.h> 39 39 40 - 41 40 /* 42 41 * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, 43 42 * cmdline option x86_mrst_timer can be used to override the configuration ··· 239 240 /* we need at least one APB timer */ 240 241 pre_init_apic_IRQ0(); 241 242 apbt_time_init(); 242 - } 243 - 244 - void __init mrst_rtc_init(void) 245 - { 246 - sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); 247 243 } 248 244 249 245 void __cpuinit mrst_arch_setup(void)
+120
arch/x86/platform/mrst/vrtc.c
··· 1 + /* 2 + * vrtc.c: Driver for virtual RTC device on Intel MID platform 3 + * 4 + * (C) Copyright 2009 Intel Corporation 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; version 2 9 + * of the License. 10 + * 11 + * Note: 12 + * VRTC is emulated by system controller firmware, the real HW 13 + * RTC is located in the PMIC device. SCU FW shadows PMIC RTC 14 + * in a memory mapped IO space that is visible to the host IA 15 + * processor. 16 + * 17 + * This driver is based on RTC CMOS driver. 18 + */ 19 + 20 + #include <linux/kernel.h> 21 + #include <linux/init.h> 22 + #include <linux/sfi.h> 23 + 24 + #include <asm/mrst.h> 25 + #include <asm/mrst-vrtc.h> 26 + #include <asm/time.h> 27 + #include <asm/fixmap.h> 28 + 29 + static unsigned char __iomem *vrtc_virt_base; 30 + 31 + unsigned char vrtc_cmos_read(unsigned char reg) 32 + { 33 + unsigned char retval; 34 + 35 + /* vRTC's registers range from 0x0 to 0xD */ 36 + if (reg > 0xd || !vrtc_virt_base) 37 + return 0xff; 38 + 39 + lock_cmos_prefix(reg); 40 + retval = __raw_readb(vrtc_virt_base + (reg << 2)); 41 + lock_cmos_suffix(reg); 42 + return retval; 43 + } 44 + EXPORT_SYMBOL_GPL(vrtc_cmos_read); 45 + 46 + void vrtc_cmos_write(unsigned char val, unsigned char reg) 47 + { 48 + if (reg > 0xd || !vrtc_virt_base) 49 + return; 50 + 51 + lock_cmos_prefix(reg); 52 + __raw_writeb(val, vrtc_virt_base + (reg << 2)); 53 + lock_cmos_suffix(reg); 54 + } 55 + EXPORT_SYMBOL_GPL(vrtc_cmos_write); 56 + 57 + unsigned long vrtc_get_time(void) 58 + { 59 + u8 sec, min, hour, mday, mon; 60 + u32 year; 61 + 62 + while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) 63 + cpu_relax(); 64 + 65 + sec = vrtc_cmos_read(RTC_SECONDS); 66 + min = vrtc_cmos_read(RTC_MINUTES); 67 + hour = vrtc_cmos_read(RTC_HOURS); 68 + mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); 69 + mon = vrtc_cmos_read(RTC_MONTH); 70 + year = vrtc_cmos_read(RTC_YEAR); 71 + 72 + /* vRTC YEAR reg contains the offset to 1960 */ 73 + year += 1960; 74 + 75 + printk(KERN_INFO "vRTC: sec: %d min: %d hour: %d day: %d " 76 + "mon: %d year: %d\n", sec, min, hour, mday, mon, year); 77 + 78 + return mktime(year, mon, mday, hour, min, sec); 79 + } 80 + 81 + /* Only care about the minutes and seconds */ 82 + int vrtc_set_mmss(unsigned long nowtime) 83 + { 84 + int real_sec, real_min; 85 + int vrtc_min; 86 + 87 + vrtc_min = vrtc_cmos_read(RTC_MINUTES); 88 + 89 + real_sec = nowtime % 60; 90 + real_min = nowtime / 60; 91 + if (((abs(real_min - vrtc_min) + 15)/30) & 1) 92 + real_min += 30; 93 + real_min %= 60; 94 + 95 + vrtc_cmos_write(real_sec, RTC_SECONDS); 96 + vrtc_cmos_write(real_min, RTC_MINUTES); 97 + return 0; 98 + } 99 + 100 + void __init mrst_rtc_init(void) 101 + { 102 + unsigned long rtc_paddr; 103 + void __iomem *virt_base; 104 + 105 + sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); 106 + if (!sfi_mrtc_num) 107 + return; 108 + 109 + rtc_paddr = sfi_mrtc_array[0].phys_addr; 110 + 111 + /* vRTC's register address may not be page aligned */ 112 + set_fixmap_nocache(FIX_LNW_VRTC, rtc_paddr); 113 + 114 + virt_base = (void __iomem *)__fix_to_virt(FIX_LNW_VRTC); 115 + virt_base += rtc_paddr & ~PAGE_MASK; 116 + vrtc_virt_base = virt_base; 117 + 118 + x86_platform.get_wallclock = vrtc_get_time; 119 + x86_platform.set_wallclock = vrtc_set_mmss; 120 + }