📄 rtc.c
字号:
/* * rtc.c, RTC(has only timer function) routines for NEC VR4100 series. * * Copyright (C) 2003-2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> * * 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; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/init.h>#include <linux/irq.h>#include <linux/smp.h>#include <linux/types.h>#include <asm/io.h>#include <asm/time.h>#include <asm/vr41xx/vr41xx.h>static uint32_t rtc1_base;static uint32_t rtc2_base;static uint64_t previous_elapsedtime;static unsigned int remainder_per_sec;static unsigned int cycles_per_sec;static unsigned int cycles_per_jiffy;static unsigned long epoch_time;#define CYCLES_PER_JIFFY (CLOCK_TICK_RATE / HZ)#define REMAINDER_PER_SEC (CLOCK_TICK_RATE - (CYCLES_PER_JIFFY * HZ))#define CYCLES_PER_100USEC ((CLOCK_TICK_RATE + (10000 / 2)) / 10000)#define ETIMELREG_TYPE1 KSEG1ADDR(0x0b0000c0)#define TCLKLREG_TYPE1 KSEG1ADDR(0x0b0001c0)#define ETIMELREG_TYPE2 KSEG1ADDR(0x0f000100)#define TCLKLREG_TYPE2 KSEG1ADDR(0x0f000120)/* RTC 1 registers */#define ETIMELREG 0x00#define ETIMEMREG 0x02#define ETIMEHREG 0x04/* RFU */#define ECMPLREG 0x08#define ECMPMREG 0x0a#define ECMPHREG 0x0c/* RFU */#define RTCL1LREG 0x10#define RTCL1HREG 0x12#define RTCL1CNTLREG 0x14#define RTCL1CNTHREG 0x16#define RTCL2LREG 0x18#define RTCL2HREG 0x1a#define RTCL2CNTLREG 0x1c#define RTCL2CNTHREG 0x1e/* RTC 2 registers */#define TCLKLREG 0x00#define TCLKHREG 0x02#define TCLKCNTLREG 0x04#define TCLKCNTHREG 0x06/* RFU */#define RTCINTREG 0x1e #define TCLOCK_INT 0x08 #define RTCLONG2_INT 0x04 #define RTCLONG1_INT 0x02 #define ELAPSEDTIME_INT 0x01#define read_rtc1(offset) readw(rtc1_base + (offset))#define write_rtc1(val, offset) writew((val), rtc1_base + (offset))#define read_rtc2(offset) readw(rtc2_base + (offset))#define write_rtc2(val, offset) writew((val), rtc2_base + (offset))static inline uint64_t read_elapsedtime_counter(void){ uint64_t first, second; uint32_t first_mid, first_low; uint32_t second_mid, second_low; do { first_low = (uint32_t)read_rtc1(ETIMELREG); first_mid = (uint32_t)read_rtc1(ETIMEMREG); first = (uint64_t)read_rtc1(ETIMEHREG); second_low = (uint32_t)read_rtc1(ETIMELREG); second_mid = (uint32_t)read_rtc1(ETIMEMREG); second = (uint64_t)read_rtc1(ETIMEHREG); } while (first_low != second_low || first_mid != second_mid || first != second); return (first << 32) | (uint64_t)((first_mid << 16) | first_low);}static inline void write_elapsedtime_counter(uint64_t time){ write_rtc1((uint16_t)time, ETIMELREG); write_rtc1((uint16_t)(time >> 16), ETIMEMREG); write_rtc1((uint16_t)(time >> 32), ETIMEHREG);}static inline void write_elapsedtime_compare(uint64_t time){ write_rtc1((uint16_t)time, ECMPLREG); write_rtc1((uint16_t)(time >> 16), ECMPMREG); write_rtc1((uint16_t)(time >> 32), ECMPHREG);}void vr41xx_set_rtclong1_cycle(uint32_t cycles){ write_rtc1((uint16_t)cycles, RTCL1LREG); write_rtc1((uint16_t)(cycles >> 16), RTCL1HREG);}uint32_t vr41xx_read_rtclong1_counter(void){ uint32_t first_high, first_low; uint32_t second_high, second_low; do { first_low = (uint32_t)read_rtc1(RTCL1CNTLREG); first_high = (uint32_t)read_rtc1(RTCL1CNTHREG); second_low = (uint32_t)read_rtc1(RTCL1CNTLREG); second_high = (uint32_t)read_rtc1(RTCL1CNTHREG); } while (first_low != second_low || first_high != second_high); return (first_high << 16) | first_low;}void vr41xx_set_rtclong2_cycle(uint32_t cycles){ write_rtc1((uint16_t)cycles, RTCL2LREG); write_rtc1((uint16_t)(cycles >> 16), RTCL2HREG);}uint32_t vr41xx_read_rtclong2_counter(void){ uint32_t first_high, first_low; uint32_t second_high, second_low; do { first_low = (uint32_t)read_rtc1(RTCL2CNTLREG); first_high = (uint32_t)read_rtc1(RTCL2CNTHREG); second_low = (uint32_t)read_rtc1(RTCL2CNTLREG); second_high = (uint32_t)read_rtc1(RTCL2CNTHREG); } while (first_low != second_low || first_high != second_high); return (first_high << 16) | first_low;}void vr41xx_set_tclock_cycle(uint32_t cycles){ write_rtc2((uint16_t)cycles, TCLKLREG); write_rtc2((uint16_t)(cycles >> 16), TCLKHREG);}uint32_t vr41xx_read_tclock_counter(void){ uint32_t first_high, first_low; uint32_t second_high, second_low; do { first_low = (uint32_t)read_rtc2(TCLKCNTLREG); first_high = (uint32_t)read_rtc2(TCLKCNTHREG); second_low = (uint32_t)read_rtc2(TCLKCNTLREG); second_high = (uint32_t)read_rtc2(TCLKCNTHREG); } while (first_low != second_low || first_high != second_high); return (first_high << 16) | first_low;}static void vr41xx_timer_ack(void){ uint64_t cur; write_rtc2(ELAPSEDTIME_INT, RTCINTREG); previous_elapsedtime += (uint64_t)cycles_per_jiffy; cycles_per_sec += cycles_per_jiffy; if (cycles_per_sec >= CLOCK_TICK_RATE) { cycles_per_sec = 0; remainder_per_sec = REMAINDER_PER_SEC; } cycles_per_jiffy = 0; do { cycles_per_jiffy += CYCLES_PER_JIFFY; if (remainder_per_sec > 0) { cycles_per_jiffy++; remainder_per_sec--; } cur = read_elapsedtime_counter(); } while (cur >= previous_elapsedtime + (uint64_t)cycles_per_jiffy); write_elapsedtime_compare(previous_elapsedtime + (uint64_t)cycles_per_jiffy);}static void vr41xx_hpt_init(unsigned int count){}static unsigned int vr41xx_hpt_read(void){ uint64_t cur; cur = read_elapsedtime_counter(); return (unsigned int)cur;}static unsigned long vr41xx_gettimeoffset(void){ uint64_t cur; unsigned long gap; cur = read_elapsedtime_counter(); gap = (unsigned long)(cur - previous_elapsedtime); gap = gap / CYCLES_PER_100USEC * 100; /* usec */ return gap;}static unsigned long vr41xx_get_time(void){ uint64_t counts; counts = read_elapsedtime_counter(); counts >>= 15; return epoch_time + (unsigned long)counts;}static int vr41xx_set_time(unsigned long sec){ if (sec < epoch_time) return -EINVAL; sec -= epoch_time; write_elapsedtime_counter((uint64_t)sec << 15); return 0;}void vr41xx_set_epoch_time(unsigned long time){ epoch_time = time;}static void __init vr41xx_time_init(void){ switch (current_cpu_data.cputype) { case CPU_VR4111: case CPU_VR4121: rtc1_base = ETIMELREG_TYPE1; rtc2_base = TCLKLREG_TYPE1; break; case CPU_VR4122: case CPU_VR4131: case CPU_VR4133: rtc1_base = ETIMELREG_TYPE2; rtc2_base = TCLKLREG_TYPE2; break; default: panic("Unexpected CPU of NEC VR4100 series"); break; } mips_timer_ack = vr41xx_timer_ack; mips_hpt_init = vr41xx_hpt_init; mips_hpt_read = vr41xx_hpt_read; mips_hpt_frequency = CLOCK_TICK_RATE; if (epoch_time == 0) epoch_time = mktime(1970, 1, 1, 0, 0, 0); rtc_get_time = vr41xx_get_time; rtc_set_time = vr41xx_set_time;}static void __init vr41xx_timer_setup(struct irqaction *irq){ do_gettimeoffset = vr41xx_gettimeoffset; remainder_per_sec = REMAINDER_PER_SEC; cycles_per_jiffy = CYCLES_PER_JIFFY; if (remainder_per_sec > 0) { cycles_per_jiffy++; remainder_per_sec--; } previous_elapsedtime = read_elapsedtime_counter(); write_elapsedtime_compare(previous_elapsedtime + (uint64_t)cycles_per_jiffy); write_rtc2(ELAPSEDTIME_INT, RTCINTREG); setup_irq(ELAPSEDTIME_IRQ, irq);}static int __init vr41xx_rtc_init(void){ board_time_init = vr41xx_time_init; board_timer_setup = vr41xx_timer_setup; return 0;}early_initcall(vr41xx_rtc_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -