1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD$ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD$"); 31221828Sgrehan 32221828Sgrehan#include <sys/types.h> 33221828Sgrehan#include <sys/time.h> 34221828Sgrehan 35221828Sgrehan#include <stdio.h> 36253181Sgrehan#include <string.h> 37221828Sgrehan#include <time.h> 38221828Sgrehan#include <assert.h> 39221828Sgrehan 40253181Sgrehan#include <machine/vmm.h> 41253181Sgrehan#include <vmmapi.h> 42253181Sgrehan 43261265Sjhb#include "acpi.h" 44221828Sgrehan#include "inout.h" 45261265Sjhb#include "pci_lpc.h" 46253181Sgrehan#include "rtc.h" 47221828Sgrehan 48221828Sgrehan#define IO_RTC 0x70 49221828Sgrehan 50221828Sgrehan#define RTC_SEC 0x00 /* seconds */ 51255689Sgrehan#define RTC_SEC_ALARM 0x01 52221828Sgrehan#define RTC_MIN 0x02 53255689Sgrehan#define RTC_MIN_ALARM 0x03 54221828Sgrehan#define RTC_HRS 0x04 55255689Sgrehan#define RTC_HRS_ALARM 0x05 56221828Sgrehan#define RTC_WDAY 0x06 57221828Sgrehan#define RTC_DAY 0x07 58221828Sgrehan#define RTC_MONTH 0x08 59221828Sgrehan#define RTC_YEAR 0x09 60221828Sgrehan#define RTC_CENTURY 0x32 /* current century */ 61221828Sgrehan 62221828Sgrehan#define RTC_STATUSA 0xA 63221828Sgrehan#define RTCSA_TUP 0x80 /* time update, don't look now */ 64221828Sgrehan 65221828Sgrehan#define RTC_STATUSB 0xB 66221828Sgrehan#define RTCSB_DST 0x01 67221828Sgrehan#define RTCSB_24HR 0x02 68221828Sgrehan#define RTCSB_BIN 0x04 /* 0 = BCD, 1 = Binary */ 69221828Sgrehan#define RTCSB_PINTR 0x40 /* 1 = enable periodic clock interrupt */ 70221828Sgrehan#define RTCSB_HALT 0x80 /* stop clock updates */ 71221828Sgrehan 72221828Sgrehan#define RTC_INTR 0x0c /* status register C (R) interrupt source */ 73221828Sgrehan 74221828Sgrehan#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ 75221828Sgrehan#define RTCSD_PWR 0x80 /* clock power OK */ 76221828Sgrehan 77253181Sgrehan#define RTC_NVRAM_START 0x0e 78253181Sgrehan#define RTC_NVRAM_END 0x7f 79253181Sgrehan#define RTC_NVRAM_SZ (128 - RTC_NVRAM_START) 80253181Sgrehan#define nvoff(x) ((x) - RTC_NVRAM_START) 81253181Sgrehan 82221828Sgrehan#define RTC_DIAG 0x0e 83221828Sgrehan#define RTC_RSTCODE 0x0f 84222105Sgrehan#define RTC_EQUIPMENT 0x14 85253181Sgrehan#define RTC_LMEM_LSB 0x34 86253181Sgrehan#define RTC_LMEM_MSB 0x35 87253181Sgrehan#define RTC_HMEM_LSB 0x5b 88253181Sgrehan#define RTC_HMEM_SB 0x5c 89253181Sgrehan#define RTC_HMEM_MSB 0x5d 90222105Sgrehan 91253181Sgrehan#define m_64KB (64*1024) 92253181Sgrehan#define m_16MB (16*1024*1024) 93253181Sgrehan#define m_4GB (4ULL*1024*1024*1024) 94253181Sgrehan 95221828Sgrehanstatic int addr; 96221828Sgrehan 97253181Sgrehanstatic uint8_t rtc_nvram[RTC_NVRAM_SZ]; 98253181Sgrehan 99221828Sgrehan/* XXX initialize these to default values as they would be from BIOS */ 100253181Sgrehanstatic uint8_t status_a, status_b; 101221828Sgrehan 102255689Sgrehanstatic struct { 103255689Sgrehan uint8_t hours; 104255689Sgrehan uint8_t mins; 105255689Sgrehan uint8_t secs; 106255689Sgrehan} rtc_alarm; 107255689Sgrehan 108221828Sgrehanstatic u_char const bin2bcd_data[] = { 109221828Sgrehan 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 110221828Sgrehan 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 111221828Sgrehan 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 112221828Sgrehan 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 113221828Sgrehan 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 114221828Sgrehan 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 115221828Sgrehan 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 116221828Sgrehan 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 117221828Sgrehan 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 118221828Sgrehan 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 119221828Sgrehan}; 120221828Sgrehan#define bin2bcd(bin) (bin2bcd_data[bin]) 121221828Sgrehan 122221828Sgrehan#define rtcout(val) ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val))) 123221828Sgrehan 124221828Sgrehanstatic void 125221828Sgrehantimevalfix(struct timeval *t1) 126221828Sgrehan{ 127221828Sgrehan 128221828Sgrehan if (t1->tv_usec < 0) { 129221828Sgrehan t1->tv_sec--; 130221828Sgrehan t1->tv_usec += 1000000; 131221828Sgrehan } 132221828Sgrehan if (t1->tv_usec >= 1000000) { 133221828Sgrehan t1->tv_sec++; 134221828Sgrehan t1->tv_usec -= 1000000; 135221828Sgrehan } 136221828Sgrehan} 137221828Sgrehan 138221828Sgrehanstatic void 139221828Sgrehantimevalsub(struct timeval *t1, const struct timeval *t2) 140221828Sgrehan{ 141221828Sgrehan 142221828Sgrehan t1->tv_sec -= t2->tv_sec; 143221828Sgrehan t1->tv_usec -= t2->tv_usec; 144221828Sgrehan timevalfix(t1); 145221828Sgrehan} 146221828Sgrehan 147221828Sgrehanstatic int 148221828Sgrehanrtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 149221828Sgrehan uint32_t *eax, void *arg) 150221828Sgrehan{ 151221828Sgrehan if (bytes != 1) 152221828Sgrehan return (-1); 153221828Sgrehan 154245920Sgrehan if (in) { 155245920Sgrehan /* straight read of this register will return 0xFF */ 156245920Sgrehan *eax = 0xff; 157245920Sgrehan return (0); 158245920Sgrehan } 159245920Sgrehan 160245920Sgrehan switch (*eax & 0x7f) { 161221828Sgrehan case RTC_SEC: 162255689Sgrehan case RTC_SEC_ALARM: 163221828Sgrehan case RTC_MIN: 164255689Sgrehan case RTC_MIN_ALARM: 165221828Sgrehan case RTC_HRS: 166255689Sgrehan case RTC_HRS_ALARM: 167221828Sgrehan case RTC_WDAY: 168221828Sgrehan case RTC_DAY: 169221828Sgrehan case RTC_MONTH: 170221828Sgrehan case RTC_YEAR: 171221828Sgrehan case RTC_STATUSA: 172221828Sgrehan case RTC_STATUSB: 173221828Sgrehan case RTC_INTR: 174221828Sgrehan case RTC_STATUSD: 175253181Sgrehan case RTC_NVRAM_START ... RTC_NVRAM_END: 176221828Sgrehan break; 177221828Sgrehan default: 178221828Sgrehan return (-1); 179221828Sgrehan } 180221828Sgrehan 181245920Sgrehan addr = *eax & 0x7f; 182221828Sgrehan return (0); 183221828Sgrehan} 184221828Sgrehan 185221828Sgrehanstatic int 186221828Sgrehanrtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 187221828Sgrehan uint32_t *eax, void *arg) 188221828Sgrehan{ 189221828Sgrehan int hour; 190221828Sgrehan time_t t; 191221828Sgrehan struct timeval cur, delta; 192221828Sgrehan 193221828Sgrehan static struct timeval last; 194221828Sgrehan static struct tm tm; 195221828Sgrehan 196221828Sgrehan if (bytes != 1) 197221828Sgrehan return (-1); 198221828Sgrehan 199221828Sgrehan gettimeofday(&cur, NULL); 200221828Sgrehan 201221828Sgrehan /* 202221828Sgrehan * Increment the cached time only once per second so we can guarantee 203221828Sgrehan * that the guest has at least one second to read the hour:min:sec 204221828Sgrehan * separately and still get a coherent view of the time. 205221828Sgrehan */ 206221828Sgrehan delta = cur; 207221828Sgrehan timevalsub(&delta, &last); 208221828Sgrehan if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) { 209221828Sgrehan t = cur.tv_sec; 210221828Sgrehan localtime_r(&t, &tm); 211221828Sgrehan last = cur; 212221828Sgrehan } 213221828Sgrehan 214221828Sgrehan if (in) { 215221828Sgrehan switch (addr) { 216255689Sgrehan case RTC_SEC_ALARM: 217255689Sgrehan *eax = rtc_alarm.secs; 218255689Sgrehan break; 219255689Sgrehan case RTC_MIN_ALARM: 220255689Sgrehan *eax = rtc_alarm.mins; 221255689Sgrehan break; 222255689Sgrehan case RTC_HRS_ALARM: 223255689Sgrehan *eax = rtc_alarm.hours; 224255689Sgrehan break; 225221828Sgrehan case RTC_SEC: 226221828Sgrehan *eax = rtcout(tm.tm_sec); 227221828Sgrehan return (0); 228221828Sgrehan case RTC_MIN: 229221828Sgrehan *eax = rtcout(tm.tm_min); 230221828Sgrehan return (0); 231221828Sgrehan case RTC_HRS: 232221828Sgrehan if (status_b & RTCSB_24HR) 233221828Sgrehan hour = tm.tm_hour; 234221828Sgrehan else 235221828Sgrehan hour = (tm.tm_hour % 12) + 1; 236221828Sgrehan 237221828Sgrehan *eax = rtcout(hour); 238221828Sgrehan 239221828Sgrehan /* 240221828Sgrehan * If we are representing time in the 12-hour format 241221828Sgrehan * then set the MSB to indicate PM. 242221828Sgrehan */ 243221828Sgrehan if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12) 244221828Sgrehan *eax |= 0x80; 245221828Sgrehan 246221828Sgrehan return (0); 247221828Sgrehan case RTC_WDAY: 248221828Sgrehan *eax = rtcout(tm.tm_wday + 1); 249221828Sgrehan return (0); 250221828Sgrehan case RTC_DAY: 251221828Sgrehan *eax = rtcout(tm.tm_mday); 252221828Sgrehan return (0); 253221828Sgrehan case RTC_MONTH: 254221828Sgrehan *eax = rtcout(tm.tm_mon + 1); 255221828Sgrehan return (0); 256221828Sgrehan case RTC_YEAR: 257221828Sgrehan *eax = rtcout(tm.tm_year % 100); 258221828Sgrehan return (0); 259221828Sgrehan case RTC_STATUSA: 260221828Sgrehan *eax = status_a; 261221828Sgrehan return (0); 262245920Sgrehan case RTC_STATUSB: 263245920Sgrehan *eax = status_b; 264245920Sgrehan return (0); 265221828Sgrehan case RTC_INTR: 266221828Sgrehan *eax = 0; 267221828Sgrehan return (0); 268221828Sgrehan case RTC_STATUSD: 269221828Sgrehan *eax = RTCSD_PWR; 270221828Sgrehan return (0); 271253181Sgrehan case RTC_NVRAM_START ... RTC_NVRAM_END: 272253181Sgrehan *eax = rtc_nvram[addr - RTC_NVRAM_START]; 273221828Sgrehan return (0); 274221828Sgrehan default: 275221828Sgrehan return (-1); 276221828Sgrehan } 277221828Sgrehan } 278221828Sgrehan 279221828Sgrehan switch (addr) { 280221828Sgrehan case RTC_STATUSA: 281221828Sgrehan status_a = *eax & ~RTCSA_TUP; 282221828Sgrehan break; 283221828Sgrehan case RTC_STATUSB: 284221828Sgrehan /* XXX not implemented yet XXX */ 285221828Sgrehan if (*eax & RTCSB_PINTR) 286221828Sgrehan return (-1); 287221828Sgrehan status_b = *eax; 288221828Sgrehan break; 289245920Sgrehan case RTC_STATUSD: 290245920Sgrehan /* ignore write */ 291245920Sgrehan break; 292255689Sgrehan case RTC_SEC_ALARM: 293255689Sgrehan rtc_alarm.secs = *eax; 294255689Sgrehan break; 295255689Sgrehan case RTC_MIN_ALARM: 296255689Sgrehan rtc_alarm.mins = *eax; 297255689Sgrehan break; 298255689Sgrehan case RTC_HRS_ALARM: 299255689Sgrehan rtc_alarm.hours = *eax; 300255689Sgrehan break; 301221828Sgrehan case RTC_SEC: 302221828Sgrehan case RTC_MIN: 303221828Sgrehan case RTC_HRS: 304221828Sgrehan case RTC_WDAY: 305221828Sgrehan case RTC_DAY: 306221828Sgrehan case RTC_MONTH: 307221828Sgrehan case RTC_YEAR: 308221828Sgrehan /* 309221828Sgrehan * Ignore writes to the time of day registers 310221828Sgrehan */ 311221828Sgrehan break; 312253181Sgrehan case RTC_NVRAM_START ... RTC_NVRAM_END: 313253181Sgrehan rtc_nvram[addr - RTC_NVRAM_START] = *eax; 314253181Sgrehan break; 315221828Sgrehan default: 316221828Sgrehan return (-1); 317221828Sgrehan } 318221828Sgrehan return (0); 319221828Sgrehan} 320221828Sgrehan 321253181Sgrehanvoid 322253181Sgrehanrtc_init(struct vmctx *ctx) 323253181Sgrehan{ 324253181Sgrehan struct timeval cur; 325253181Sgrehan struct tm tm; 326253181Sgrehan size_t himem; 327253181Sgrehan size_t lomem; 328253181Sgrehan int err; 329253181Sgrehan 330253181Sgrehan err = gettimeofday(&cur, NULL); 331253181Sgrehan assert(err == 0); 332253181Sgrehan (void) localtime_r(&cur.tv_sec, &tm); 333253181Sgrehan 334253181Sgrehan memset(rtc_nvram, 0, sizeof(rtc_nvram)); 335253181Sgrehan 336256755Sgrehan rtc_nvram[nvoff(RTC_CENTURY)] = bin2bcd((tm.tm_year + 1900) / 100); 337253181Sgrehan 338253181Sgrehan /* XXX init diag/reset code/equipment/checksum ? */ 339253181Sgrehan 340253181Sgrehan /* 341253181Sgrehan * Report guest memory size in nvram cells as required by UEFI. 342253181Sgrehan * Little-endian encoding. 343253181Sgrehan * 0x34/0x35 - 64KB chunks above 16MB, below 4GB 344253181Sgrehan * 0x5b/0x5c/0x5d - 64KB chunks above 4GB 345253181Sgrehan */ 346270074Sgrehan lomem = (vm_get_lowmem_size(ctx) - m_16MB) / m_64KB; 347253181Sgrehan rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem; 348253181Sgrehan rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8; 349253181Sgrehan 350270074Sgrehan himem = vm_get_highmem_size(ctx) / m_64KB; 351270074Sgrehan rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem; 352270074Sgrehan rtc_nvram[nvoff(RTC_HMEM_SB)] = himem >> 8; 353270074Sgrehan rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16; 354253181Sgrehan} 355253181Sgrehan 356245920SgrehanINOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler); 357221828SgrehanINOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler); 358261265Sjhb 359261265Sjhbstatic void 360261265Sjhbrtc_dsdt(void) 361261265Sjhb{ 362261265Sjhb 363261265Sjhb dsdt_line(""); 364261265Sjhb dsdt_line("Device (RTC)"); 365261265Sjhb dsdt_line("{"); 366261265Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0B00\"))"); 367261265Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 368261265Sjhb dsdt_line(" {"); 369261265Sjhb dsdt_indent(2); 370261265Sjhb dsdt_fixed_ioport(IO_RTC, 2); 371261265Sjhb dsdt_fixed_irq(8); 372261265Sjhb dsdt_unindent(2); 373261265Sjhb dsdt_line(" })"); 374261265Sjhb dsdt_line("}"); 375261265Sjhb} 376261265SjhbLPC_DSDT(rtc_dsdt); 377261265Sjhb 378261265SjhbSYSRES_IO(0x72, 6); 379