lkml.org 
[lkml]   [2010]   [Jul]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v2 3/3] x86/mrst: add vrtc driver which serves as a wall clock device
    Date
    From: Feng Tang <feng.tang@intel.com>

    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.

    Currently, vrtc init func will be called as arch_initcall() before
    xtime's init. Also move the sfi vrtc table parsing from mrst.c to
    vrtc.c

    There will be another general vrtc driver for rtc subsystem

    Signed-off-by: Feng Tang <feng.tang@intel.com>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: H. Peter Anvin <hpa@zytor.com>
    Cc: Ingo Molnar <mingo@elte.hu>
    Cc: Alan Cox <alan@linux.intel.com>
    ---
    arch/x86/include/asm/mrst.h | 2 -
    arch/x86/include/asm/vrtc.h | 24 ++++++
    arch/x86/kernel/Makefile | 2 +-
    arch/x86/kernel/mrst.c | 44 ------------
    arch/x86/kernel/vrtc.c | 164 +++++++++++++++++++++++++++++++++++++++++++
    5 files changed, 189 insertions(+), 47 deletions(-)
    create mode 100644 arch/x86/include/asm/vrtc.h
    create mode 100644 arch/x86/kernel/vrtc.c

    diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h
    index 451d30e..fa144f2 100644
    --- a/arch/x86/include/asm/mrst.h
    +++ b/arch/x86/include/asm/mrst.h
    @@ -11,9 +11,7 @@
    #ifndef _ASM_X86_MRST_H
    #define _ASM_X86_MRST_H
    extern int pci_mrst_init(void);
    -int __init sfi_parse_mrtc(struct sfi_table_header *table);

    #define SFI_MTMR_MAX_NUM 8
    -#define SFI_MRTC_MAX 8

    #endif /* _ASM_X86_MRST_H */
    diff --git a/arch/x86/include/asm/vrtc.h b/arch/x86/include/asm/vrtc.h
    new file mode 100644
    index 0000000..fcdfcde
    --- /dev/null
    +++ b/arch/x86/include/asm/vrtc.h
    @@ -0,0 +1,24 @@
    +#ifndef _MRST_VRTC_H
    +#define _MRST_VRTC_H
    +
    +#define SFI_MRTC_MAX 8
    +#define VRTC_IOLEN 1024
    +
    +#ifdef CONFIG_X86_MRST
    +extern unsigned char vrtc_reg_read(unsigned char reg);
    +extern void vrtc_reg_write(unsigned char val, unsigned char reg);
    +
    +extern struct sfi_rtc_table_entry sfi_mrtc_array[];
    +#else
    +static inline unsigned char vrtc_reg_read(unsigned char reg)
    +{
    + return 0xff;
    +}
    +
    +static inline void vrtc_reg_write(unsigned char val, unsigned char reg)
    +{
    + return;
    +}
    +#endif
    +
    +#endif
    diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
    index e77b220..f3fdcbe 100644
    --- a/arch/x86/kernel/Makefile
    +++ b/arch/x86/kernel/Makefile
    @@ -104,7 +104,7 @@ obj-$(CONFIG_SCx200) += scx200.o
    scx200-y += scx200_32.o

    obj-$(CONFIG_OLPC) += olpc.o
    -obj-$(CONFIG_X86_MRST) += mrst.o
    +obj-$(CONFIG_X86_MRST) += mrst.o vrtc.o

    microcode-y := microcode_core.o
    microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o
    diff --git a/arch/x86/kernel/mrst.c b/arch/x86/kernel/mrst.c
    index 8cd809c..cd599ad 100644
    --- a/arch/x86/kernel/mrst.c
    +++ b/arch/x86/kernel/mrst.c
    @@ -29,10 +29,6 @@ static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM];
    static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM];
    int sfi_mtimer_num;

    -struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX];
    -EXPORT_SYMBOL_GPL(sfi_mrtc_array);
    -int sfi_mrtc_num;
    -
    /* parse all the mtimer info to a static mtimer array */
    static int __init sfi_parse_mtmr(struct sfi_table_header *table)
    {
    @@ -106,41 +102,6 @@ void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr)
    }
    }

    -/* parse all the mrtc info to a global mrtc array */
    -int __init sfi_parse_mrtc(struct sfi_table_header *table)
    -{
    - struct sfi_table_simple *sb;
    - struct sfi_rtc_table_entry *pentry;
    - struct mpc_intsrc mp_irq;
    -
    - int totallen;
    -
    - sb = (struct sfi_table_simple *)table;
    - if (!sfi_mrtc_num) {
    - sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb,
    - struct sfi_rtc_table_entry);
    - pentry = (struct sfi_rtc_table_entry *)sb->pentry;
    - totallen = sfi_mrtc_num * sizeof(*pentry);
    - memcpy(sfi_mrtc_array, pentry, totallen);
    - }
    -
    - printk(KERN_INFO "SFI: RTC info (num = %d):\n", sfi_mrtc_num);
    - pentry = sfi_mrtc_array;
    - for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) {
    - printk(KERN_INFO "RTC[%d]: paddr = 0x%08x, irq = %d\n",
    - totallen, (u32)pentry->phys_addr, pentry->irq);
    - mp_irq.type = MP_IOAPIC;
    - mp_irq.irqtype = mp_INT;
    - mp_irq.irqflag = 0;
    - mp_irq.srcbus = 0;
    - mp_irq.srcbusirq = pentry->irq; /* IRQ */
    - mp_irq.dstapic = MP_APIC_ALL;
    - mp_irq.dstirq = pentry->irq;
    - mp_save_irq(&mp_irq);
    - }
    - return 0;
    -}
    -
    /*
    * the secondary clock in Moorestown can be APBT or LAPIC clock, default to
    * APBT but cmdline option can also override it.
    @@ -174,11 +135,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);
    -}
    -
    /*
    * if we use per cpu apb timer, the bootclock already setup. if we use lapic
    * timer and one apbt timer for broadcast, we need to set up lapic boot clock.
    diff --git a/arch/x86/kernel/vrtc.c b/arch/x86/kernel/vrtc.c
    new file mode 100644
    index 0000000..1f9071f
    --- /dev/null
    +++ b/arch/x86/kernel/vrtc.c
    @@ -0,0 +1,164 @@
    +/*
    + * vrtc.c: Driver for virtual RTC device on Intel MID platform
    + *
    + * (C) Copyright 2010 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.
    + *
    + */
    +
    +#include <linux/kernel.h>
    +#include <linux/init.h>
    +#include <linux/sfi.h>
    +
    +#include <asm/intel_scu_ipc.h>
    +#include <asm/time.h>
    +#include <asm/vrtc.h>
    +
    +static unsigned char __iomem *vrtc_virt_base;
    +struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX];
    +EXPORT_SYMBOL_GPL(sfi_mrtc_array);
    +static int sfi_mrtc_num;
    +
    +/* vRTC's registers range from 0x0 to 0xD */
    +unsigned char vrtc_reg_read(unsigned char reg)
    +{
    + if (unlikely(reg > 0xd || !vrtc_virt_base))
    + return 0xff;
    +
    + return __raw_readb(vrtc_virt_base + (reg << 2));
    +}
    +EXPORT_SYMBOL(vrtc_reg_read);
    +
    +void vrtc_reg_write(unsigned char val, unsigned char reg)
    +{
    + if (unlikely(reg > 0xd || !vrtc_virt_base))
    + return;
    +
    + __raw_writeb(val, vrtc_virt_base + (reg << 2));
    +}
    +EXPORT_SYMBOL(vrtc_reg_write);
    +
    +/*
    + * Both the get_time and set_time are protected by rtc_lock as shown
    + * in arch/x86/kernel/rtc.c
    + */
    +static unsigned long vrtc_get_time(void)
    +{
    + u8 sec, min, hour, mday, mon;
    + u32 year;
    +
    + while ((vrtc_reg_read(RTC_FREQ_SELECT) & RTC_UIP))
    + cpu_relax();
    +
    + sec = vrtc_reg_read(RTC_SECONDS);
    + min = vrtc_reg_read(RTC_MINUTES);
    + hour = vrtc_reg_read(RTC_HOURS);
    + mday = vrtc_reg_read(RTC_DAY_OF_MONTH);
    + mon = vrtc_reg_read(RTC_MONTH);
    + year = vrtc_reg_read(RTC_YEAR);
    +
    + /* vRTC YEAR reg contains the offset to 1960 */
    + year += 1960;
    +
    + pr_debug("vrtc time: %4d-%02d-%02d %02d:%02d:%02d\n",
    + year, mon, mday, hour, min, sec);
    +
    + return mktime(year, mon, mday, hour, min, sec);
    +}
    +
    +/* Only care about the minutes and seconds */
    +static int vrtc_set_mmss(unsigned long nowtime)
    +{
    + int real_sec, real_min;
    + int vrtc_min;
    + int ret = 0;
    +
    + vrtc_min = vrtc_reg_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_reg_write(real_sec, RTC_SECONDS);
    + vrtc_reg_write(real_min, RTC_MINUTES);
    +
    + /*
    + * Need send an IPC command to system controller to let it
    + * complete the time setting. This IPC API may sleep
    + */
    + spin_unlock(&rtc_lock);
    + ret = intel_scu_ipc_simple_command(0x1, 0xfa);
    + if (ret)
    + pr_warning("vrtc: fail to set time, %2d:%2d\n",
    + real_min, real_sec);
    + spin_lock(&rtc_lock);
    + return ret;
    +}
    +
    +/* parse all the mrtc info to a global mrtc array */
    +static int __init sfi_parse_mrtc(struct sfi_table_header *table)
    +{
    + struct sfi_table_simple *sb;
    + struct sfi_rtc_table_entry *pentry;
    + struct mpc_intsrc mp_irq;
    +
    + int totallen;
    +
    + sb = (struct sfi_table_simple *)table;
    + if (!sfi_mrtc_num) {
    + sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb,
    + struct sfi_rtc_table_entry);
    + pentry = (struct sfi_rtc_table_entry *)sb->pentry;
    + totallen = sfi_mrtc_num * sizeof(*pentry);
    + memcpy(sfi_mrtc_array, pentry, totallen);
    + }
    +
    + pr_info("SFI: RTC info (num = %d):\n", sfi_mrtc_num);
    + pentry = sfi_mrtc_array;
    + for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) {
    + pr_info("RTC[%d]: paddr = 0x%08x, irq = %d\n",
    + totallen, (u32)pentry->phys_addr, pentry->irq);
    + mp_irq.type = MP_IOAPIC;
    + mp_irq.irqtype = mp_INT;
    + mp_irq.irqflag = 0;
    + mp_irq.srcbus = 0;
    + mp_irq.srcbusirq = pentry->irq;
    + mp_irq.dstapic = MP_APIC_ALL;
    + mp_irq.dstirq = pentry->irq;
    + mp_save_irq(&mp_irq);
    + }
    + return 0;
    +}
    +
    +static int __init vrtc_init(void)
    +{
    + unsigned long rtc_paddr;
    +
    + sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc);
    + if (!sfi_mrtc_num)
    + return -1;
    +
    + rtc_paddr = sfi_mrtc_array[0].phys_addr;
    + vrtc_virt_base = ioremap(rtc_paddr, VRTC_IOLEN);
    + if (!vrtc_virt_base)
    + return -1;
    +
    + x86_platform.get_wallclock = vrtc_get_time;
    + x86_platform.set_wallclock = vrtc_set_mmss;
    +
    + pr_info("vrtc: successfully inited as a wall clock device.\n");
    + return 0;
    +}
    +arch_initcall(vrtc_init);
    --
    1.7.0.4


    \
     
     \ /
      Last update: 2010-07-15 10:07    [W:0.050 / U:452.892 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site