Skip to content

Commit

Permalink
x86: mrst: Add vrtc driver which serves as a wall clock device
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Feng Tang authored and Thomas Gleixner committed Nov 11, 2010
1 parent cfb505a commit 7309282
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 7 deletions.
4 changes: 4 additions & 0 deletions arch/x86/include/asm/fixmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ enum fixed_addresses {
FIX_TEXT_POKE1, /* reserve 2 pages for text_poke() */
FIX_TEXT_POKE0, /* first page is last, because allocation is backward */
__end_of_permanent_fixed_addresses,

#ifdef CONFIG_X86_MRST
FIX_LNW_VRTC,
#endif
/*
* 256 temporary boot-time mappings, used by early_ioremap(),
* before ioremap() is functional.
Expand Down
9 changes: 9 additions & 0 deletions arch/x86/include/asm/mrst-vrtc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef _MRST_VRTC_H
#define _MRST_VRTC_H

extern unsigned char vrtc_cmos_read(unsigned char reg);
extern void vrtc_cmos_write(unsigned char val, unsigned char reg);
extern unsigned long vrtc_get_time(void);
extern int vrtc_set_mmss(unsigned long nowtime);

#endif
10 changes: 9 additions & 1 deletion arch/x86/include/asm/mrst.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
#include <linux/sfi.h>

extern int pci_mrst_init(void);
int __init sfi_parse_mrtc(struct sfi_table_header *table);
extern int __init sfi_parse_mrtc(struct sfi_table_header *table);
extern int sfi_mrtc_num;
extern struct sfi_rtc_table_entry sfi_mrtc_array[];

/*
* Medfield is the follow-up of Moorestown, it combines two chip solution into
Expand Down Expand Up @@ -54,4 +56,10 @@ extern void hsu_early_console_init(void);
extern void intel_scu_devices_create(void);
extern void intel_scu_devices_destroy(void);

/* VRTC timer */
#define MRST_VRTC_MAP_SZ (1024)
/*#define MRST_VRTC_PGOFFSET (0xc00) */

extern void mrst_rtc_init(void);

#endif /* _ASM_X86_MRST_H */
1 change: 1 addition & 0 deletions arch/x86/platform/mrst/Makefile
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
obj-$(CONFIG_X86_MRST) += mrst.o
obj-$(CONFIG_X86_MRST) += vrtc.o
6 changes: 0 additions & 6 deletions arch/x86/platform/mrst/mrst.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include <asm/apb_timer.h>
#include <asm/reboot.h>


/*
* the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock,
* cmdline option x86_mrst_timer can be used to override the configuration
Expand Down Expand Up @@ -242,11 +241,6 @@ void __init mrst_time_init(void)
apbt_time_init();
}

void __init mrst_rtc_init(void)
{
sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc);
}

void __cpuinit mrst_arch_setup(void)
{
if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27)
Expand Down
120 changes: 120 additions & 0 deletions arch/x86/platform/mrst/vrtc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* vrtc.c: Driver for virtual RTC device on Intel MID platform
*
* (C) Copyright 2009 Intel Corporation
*
* 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 on RTC CMOS driver.
*/

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sfi.h>

#include <asm/mrst.h>
#include <asm/mrst-vrtc.h>
#include <asm/time.h>
#include <asm/fixmap.h>

static unsigned char __iomem *vrtc_virt_base;

unsigned char vrtc_cmos_read(unsigned char reg)
{
unsigned char retval;

/* vRTC's registers range from 0x0 to 0xD */
if (reg > 0xd || !vrtc_virt_base)
return 0xff;

lock_cmos_prefix(reg);
retval = __raw_readb(vrtc_virt_base + (reg << 2));
lock_cmos_suffix(reg);
return retval;
}
EXPORT_SYMBOL_GPL(vrtc_cmos_read);

void vrtc_cmos_write(unsigned char val, unsigned char reg)
{
if (reg > 0xd || !vrtc_virt_base)
return;

lock_cmos_prefix(reg);
__raw_writeb(val, vrtc_virt_base + (reg << 2));
lock_cmos_suffix(reg);
}
EXPORT_SYMBOL_GPL(vrtc_cmos_write);

unsigned long vrtc_get_time(void)
{
u8 sec, min, hour, mday, mon;
u32 year;

while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP))
cpu_relax();

sec = vrtc_cmos_read(RTC_SECONDS);
min = vrtc_cmos_read(RTC_MINUTES);
hour = vrtc_cmos_read(RTC_HOURS);
mday = vrtc_cmos_read(RTC_DAY_OF_MONTH);
mon = vrtc_cmos_read(RTC_MONTH);
year = vrtc_cmos_read(RTC_YEAR);

/* vRTC YEAR reg contains the offset to 1960 */
year += 1960;

printk(KERN_INFO "vRTC: sec: %d min: %d hour: %d day: %d "
"mon: %d year: %d\n", sec, min, hour, mday, mon, year);

return mktime(year, mon, mday, hour, min, sec);
}

/* Only care about the minutes and seconds */
int vrtc_set_mmss(unsigned long nowtime)
{
int real_sec, real_min;
int vrtc_min;

vrtc_min = vrtc_cmos_read(RTC_MINUTES);

real_sec = nowtime % 60;
real_min = nowtime / 60;
if (((abs(real_min - vrtc_min) + 15)/30) & 1)
real_min += 30;
real_min %= 60;

vrtc_cmos_write(real_sec, RTC_SECONDS);
vrtc_cmos_write(real_min, RTC_MINUTES);
return 0;
}

void __init mrst_rtc_init(void)
{
unsigned long rtc_paddr;
void __iomem *virt_base;

sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc);
if (!sfi_mrtc_num)
return;

rtc_paddr = sfi_mrtc_array[0].phys_addr;

/* vRTC's register address may not be page aligned */
set_fixmap_nocache(FIX_LNW_VRTC, rtc_paddr);

virt_base = (void __iomem *)__fix_to_virt(FIX_LNW_VRTC);
virt_base += rtc_paddr & ~PAGE_MASK;
vrtc_virt_base = virt_base;

x86_platform.get_wallclock = vrtc_get_time;
x86_platform.set_wallclock = vrtc_set_mmss;
}

0 comments on commit 7309282

Please sign in to comment.