--- qemu-0.9.0/hw/versatilepb.c.orig 2007-06-06 21:45:40.000000000 +0200 +++ qemu-0.9.0/hw/versatilepb.c 2007-06-06 21:45:40.000000000 +0200 @@ -214,6 +214,9 @@ that includes hardware cursor support from the PL111. */ pl110_init(ds, 0x10120000, pic, 16, 1); + /* Add PL031 Real Time Clock. */ + pl031_init(0x101e8000,pic,10); + /* Memory map for Versatile/PB: */ /* 0x10000000 System registers. */ /* 0x10001000 PCI controller config registers. */ --- qemu-0.9.0/vl.h.orig 2007-06-06 21:45:40.000000000 +0200 +++ qemu-0.9.0/vl.h 2007-06-06 21:45:40.000000000 +0200 @@ -1307,6 +1307,9 @@ /* smc91c111.c */ void smc91c111_init(NICInfo *, uint32_t, void *, int); +/* pl031.c */ +void pl031_init(uint32_t base, void * pic, int irq); + /* pl110.c */ void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq, int); --- qemu-0.9.0/Makefile.target.orig 2007-06-06 21:45:40.000000000 +0200 +++ qemu-0.9.0/Makefile.target 2007-06-06 21:45:40.000000000 +0200 @@ -399,7 +399,7 @@ endif ifeq ($(TARGET_BASE_ARCH), arm) VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o -VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o +VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o pl031.o VL_OBJS+= versatile_pci.o VL_OBJS+= arm_gic.o realview.o arm_sysctl.o VL_OBJS+= arm-semi.o --- qemu-0.9.0/hw/pl031.c.orig 2007-06-06 21:56:59.000000000 +0200 +++ qemu-0.9.0/hw/pl031.c 2007-06-06 21:52:15.000000000 +0200 @@ -0,0 +1,480 @@ +/* + * ARM AMBA PrimeCell PL031 RTC + * + * Copyright (c) 2007 Politecnico di Torino, Italy. + * Initially written by Simone M.Basso , + * Gaspare Scherma + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * References to implement this emulated device were + * linux-2.6.22-rc3/drivers/rtc/{rtc-pl031.c,class.c,interface.c,rtc-lib.c} + * qemu-0.9.0/hw/{versatilepb.c,pl011.c,arm_timer.c,pl*.c} + * http://www.arm.com/pdfs/DDI0287B_arm926ejs_dev_chip_trm.pdf [pag. 222] + * + * PATCH's ChangeLog + * + * version_2 : fixed the problem with interrupts: it was a problem in + * linux 2.6.21.3 rtc-pl031.c driver. updated the code + * to have interrupt in next 24 hours working [2007/06/06] + * + * version_1 : the patch works, but crashes the kernel when an + * interrupt is triggered [2007/06/04] + */ + +#define PL031_DEBUG /* Print some debug messages */ + +#include"vl.h" + +#define RTC_DR 0x00 /* Data read register */ +#define RTC_MR 0x04 /* Match register */ +#define RTC_LR 0x08 /* Data load register */ +#define RTC_CR 0x0c /* Control register */ +#define RTC_IMSC 0x10 /* Interrupt mask and set register */ +#define RTC_RIS 0x14 /* Raw interrupt status register */ +#define RTC_MIS 0x18 /* Masked interrupt status register */ +#define RTC_ICR 0x1c /* Interrupt clear register */ + +struct pl031_state { + uint32_t base; /* Base I/O memory */ + void * pic; /* Reference to pic */ + int irq; /* Assigned IRQ */ + + uint32_t boot_time; + + struct { + uint32_t time; + uint32_t enabled; + uint32_t pending; + + QEMUTimer * _timer; + } alarm; +}; + +static const unsigned char pl031_id[] = { + 0x31, 0x10, 0x14, 0x00, /* Device ID */ + 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */ +}; + +static void pl031_interrupt(void * opaque) +{ + struct pl031_state * state = opaque; + + pic_set_irq_new(state->pic, state->irq, 1); +#ifdef PL031_DEBUG + fprintf(stderr, "qemu: pl031: tick!\n"); +#endif +} + +static inline uint32_t read_current_time(struct pl031_state * state, + int64_t *ticks) +{ + /* + * WARNING! state->boot_time is _approx_ the second since Epoch + * when the emulation was started. The vm_clock counts in ticks, + * as specified in vl.h, from the time when the vm was started. + * So, if we sum these two values, we _approx_ the needed time, + * which is ok since RTC usually have a resolution of one second. + * + * state->boot_time is saved during pl031_init(). + */ + int64_t myTicks = qemu_get_clock(vm_clock); + uint32_t cur_time = state->boot_time; + + cur_time += (uint32_t)(myTicks / ticks_per_sec); + if (ticks) + *ticks = myTicks; + return cur_time; +} + +static uint32_t pl031_read(void * opaque, target_phys_addr_t offset) +{ + struct pl031_state * state = opaque; + + offset -= state->base; + + if (offset >= 0xfe0 && offset < 0x1000) + return pl031_id[(offset - 0xfe0) >> 2]; + /* + * We don't need to (offset >> 2), as it happens with other pl*.c + * emulated devices: Infact the defines RTC_* were taken from the + * linux kernel drivers/rtc/rtc-pl031.c driver and their values + * don't need such shift. + */ + switch (offset) { + + case RTC_DR: + return read_current_time(state, NULL); + break; + + case RTC_MR: + return state->alarm.time; + break; + case RTC_IMSC: + return state->alarm.enabled; + break; + case RTC_RIS: + return state->alarm.pending; + break; + + case RTC_LR: + case RTC_CR: + case RTC_MIS: + case RTC_ICR: + fprintf(stderr, "qemu: pl031_read: Unexpected offset " + "0x%x\n", offset); + break; + + default: + cpu_abort (cpu_single_env, "pl031_read: Bad offset " + "0x%x\n", offset); + } + + return 0; +} + +static inline void write_current_time(struct pl031_state * state, + uint32_t value) +{ + /* + * XXX - We can store the offset between the virtual machine and + * the real one in a variable. But, how we can save such value + * to survive a machine restart? + */ + fprintf(stderr, "qemu: warning: pl031_write: can't change time\n"); +} + +static void rtc_time_to_tm(unsigned long time, struct tm *tm); +static int rtc_tm_to_time(struct tm *tm, unsigned long *time); + +/* + * WARNING! The Linux kernel, in driver rtc-pl031, sends us a time from a + * "strange" struct tm, which has the following parameters + * + * alarm.time.tm_mday = -1; + * alarm.time.tm_mon = -1; + * alarm.time.tm_year = -1; + * alarm.time.tm_wday = -1; + * alarm.time.tm_yday = -1; + * alarm.time.tm_isdst = -1; + * + * And has the right day, hour and minute. Such structure is then processed + * using the function that in this file is called ke_mktime(), which leads + * to the following value, as result, when day = hour = minute = 0; + * + * So, we subtract such value to the 32 bit number that we receive to have + * the right HHMMSS offset from Epoch. + */ +#define OFFSET 2051591296 + +static inline void save_alarm(struct pl031_state * state, uint32_t value) +{ + struct tm alarm, now; + + rtc_time_to_tm(read_current_time(state, NULL), &now); + value -= OFFSET; + rtc_time_to_tm(value, &alarm); + now.tm_hour = alarm.tm_hour; + now.tm_min = alarm.tm_min; + now.tm_sec = alarm.tm_sec; + + rtc_tm_to_time(&now, &state->alarm.time); +#ifdef PL031_DEBUG + fprintf(stderr,"qemu: pl031: alarm at %02d/%02d/%02d, %02d:%02d:%02d\n", + now.tm_mday, now.tm_mon +1, now.tm_year + 1900, + now.tm_hour, now.tm_min, now.tm_sec); +#endif +} + +static inline void enable_disable_alarm(struct pl031_state * state, + int disable) +{ + int64_t alarm; + uint32_t now; + + if (disable) { +#ifdef PL031_DEBUG + fprintf(stderr, "qemu: pl031: disabled alarm\n"); +#endif + qemu_del_timer(state->alarm._timer); + return; + } + + now = read_current_time(state, &alarm); +#ifdef PL031_DEBUG + fprintf(stderr, "qemu: pl031: current cpu ticks %lld\n", alarm); +#endif + alarm += ticks_per_sec * (state->alarm.time - now); +#ifdef PL031_DEBUG + fprintf(stderr, "qemu: pl031: alarm at cpu ticks %lld\n", alarm); +#endif + qemu_mod_timer(state->alarm._timer, alarm); + ++state->alarm.enabled; +} + +static void pl031_write(void * opaque, target_phys_addr_t offset, + uint32_t value) +{ + struct pl031_state * state = opaque; + + offset -= state->base; + + /* + * We don't need to (offset >> 2), as it happens with other pl*.c + * emulated devices: Infact the defines RTC_* were taken from the + * linux kernel drivers/rtc/rtc-pl031.c driver and their values + * don't need such shift. + */ + switch (offset) { + case RTC_LR: + write_current_time(state, value); + break; + + case RTC_MR: + save_alarm(state, value); + break; + case RTC_MIS: + enable_disable_alarm(state, value); + break; + + case RTC_ICR: + /* www.arm.com/pdfs/DDI0287B_arm926ejs_dev_chip_trm.pdf at + page 222 tells that "The interrupt is cleared by writing + any data value to the interrupt clear register RTCICR." */ + pic_set_irq_new(state->pic, state->irq, 0); + break; + + case RTC_DR: + case RTC_CR: + case RTC_IMSC: + case RTC_RIS: + fprintf(stderr, "qemu: pl031_write: Unexpected offset " + "0x%x\n", offset); + break; + + default: + cpu_abort (cpu_single_env, "pl031_write: Bad offset " + "0x%x\n", offset); + } +} + +static CPUWriteMemoryFunc * pl031_writefn[] = { + pl031_write, /* To access byte (index 0) */ + pl031_write, /* To access word (index 1) */ + pl031_write /* To access dword (index 2) */ +}; + +static CPUReadMemoryFunc * pl031_readfn[] = { + pl031_read, /* To access byte (index 0) */ + pl031_read, /* To access word (index 1) */ + pl031_read /* To access dword (index 2) */ +}; + +void pl031_init(uint32_t base, void * pic, int irq) +{ + int offset; + struct pl031_state * state; + + state = qemu_mallocz(sizeof (*state)); + if (!state) + goto err_no_mem; + + offset = cpu_register_io_memory(0, pl031_readfn, pl031_writefn, state); + if (offset == -1) + goto err_no_offset; + + cpu_register_physical_memory(base, 0x00000fff, offset); + + state->base = base; + state->pic = pic; + state->irq = irq; + /* + * Synchronize emulated RTC with host RTC. The precision of a second + * will suffice, so it's not important that we have booted some + * milliseconds ago: we just need an amount of seconds to add to + * the number of ticks since startup. + */ + state->boot_time = time(NULL); + + state->alarm._timer = qemu_new_timer(vm_clock, pl031_interrupt, state); + if (!state->alarm._timer) + goto err_no_mem; + return; + +err_no_mem: + cpu_abort(cpu_single_env, "pl031_init: Out of memory\n"); +err_no_offset: + cpu_abort(cpu_single_env, "pl031_init: Can't register I/O memory\n"); +} + +/* + * @from-file: linux-2.6.21.3/drivers/rtc/rtc-lib.c + * + * rtc and date/time utility functions + * + * Copyright (C) 2005-06 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c and other bits + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +static unsigned long +ke_mktime(const unsigned int year0, const unsigned int mon0, + const unsigned int day, const unsigned int hour, + const unsigned int min, const unsigned int sec); + +static const unsigned char rtc_days_in_month[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static const unsigned short rtc_ydays[2][13] = { + /* Normal years */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) + +/* + * The number of days in the month. + */ +static +int rtc_month_days(unsigned int month, unsigned int year) +{ + return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1); +} + +/* + * The number of days since January 1. (0 to 365) + */ +static +int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) +{ + return rtc_ydays[LEAP_YEAR(year)][month] + day-1; +} + +/* + * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. + */ +static +void rtc_time_to_tm(unsigned long time, struct tm *tm) +{ + register int days, month, year; + + days = time / 86400; + time -= days * 86400; + + /* day of the week, 1970-01-01 was a Thursday */ + tm->tm_wday = (days + 4) % 7; + + year = 1970 + days / 365; + days -= (year - 1970) * 365 + + LEAPS_THRU_END_OF(year - 1) + - LEAPS_THRU_END_OF(1970 - 1); + if (days < 0) { + year -= 1; + days += 365 + LEAP_YEAR(year); + } + tm->tm_year = year - 1900; + tm->tm_yday = days + 1; + + for (month = 0; month < 11; month++) { + int newdays; + + newdays = days - rtc_month_days(month, year); + if (newdays < 0) + break; + days = newdays; + } + tm->tm_mon = month; + tm->tm_mday = days + 1; + + tm->tm_hour = time / 3600; + time -= tm->tm_hour * 3600; + tm->tm_min = time / 60; + tm->tm_sec = time - tm->tm_min * 60; +} + +/* + * Does the rtc_time represent a valid date/time? + */ +static +int rtc_valid_tm(struct tm *tm) +{ + if (tm->tm_year < 70 + || ((unsigned)tm->tm_mon) >= 12 + || tm->tm_mday < 1 + || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) + || ((unsigned)tm->tm_hour) >= 24 + || ((unsigned)tm->tm_min) >= 60 + || ((unsigned)tm->tm_sec) >= 60) + return 1; + + return 0; +} + +/* + * Convert Gregorian date to seconds since 01-01-1970 00:00:00. + */ +static +int rtc_tm_to_time(struct tm *tm, unsigned long *time) +{ + *time = ke_mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + return 0; +} + +/* + * @from-file: linux-2.6.21.3/linux/kernel/time.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This file contains the interface functions for the various + * time related system calls: time, stime, gettimeofday, settimeofday, + * adjtime + */ + +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static unsigned long +ke_mktime(const unsigned int year0, const unsigned int mon0, + const unsigned int day, const unsigned int hour, + const unsigned int min, const unsigned int sec) +{ + unsigned int mon = mon0, year = year0; + + /* 1..12 -> 11,12,1..10 */ + if (0 >= (int) (mon -= 2)) { + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + + return ((((unsigned long) + (year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} +