10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7696SRichard.Bean@Sun.COM * Common Development and Distribution License (the "License"). 6*7696SRichard.Bean@Sun.COM * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*7696SRichard.Bean@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * tod driver module for Mostek M48T59 part 280Sstevel@tonic-gate */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <sys/types.h> 310Sstevel@tonic-gate #include <sys/param.h> 320Sstevel@tonic-gate #include <sys/sysmacros.h> 330Sstevel@tonic-gate #include <sys/systm.h> 340Sstevel@tonic-gate #include <sys/errno.h> 350Sstevel@tonic-gate #include <sys/modctl.h> 360Sstevel@tonic-gate #include <sys/autoconf.h> 370Sstevel@tonic-gate #include <sys/debug.h> 380Sstevel@tonic-gate #include <sys/clock.h> 390Sstevel@tonic-gate #include <sys/todmostek.h> 400Sstevel@tonic-gate #include <sys/reboot.h> 410Sstevel@tonic-gate #include <sys/cmn_err.h> 420Sstevel@tonic-gate #include <sys/cpuvar.h> 430Sstevel@tonic-gate 440Sstevel@tonic-gate static timestruc_t todm_get(void); 450Sstevel@tonic-gate static void todm_set(timestruc_t); 460Sstevel@tonic-gate static uint_t todm_set_watchdog_timer(uint_t); 470Sstevel@tonic-gate static uint_t todm_clear_watchdog_timer(void); 480Sstevel@tonic-gate static void todm_set_power_alarm(timestruc_t); 490Sstevel@tonic-gate static void todm_clear_power_alarm(void); 500Sstevel@tonic-gate static uint64_t todm_get_cpufrequency(void); 510Sstevel@tonic-gate 520Sstevel@tonic-gate static uchar_t watchdog_bits = 0; 530Sstevel@tonic-gate static uint_t watchdog_timeout; 540Sstevel@tonic-gate 550Sstevel@tonic-gate extern uint64_t find_cpufrequency(volatile uchar_t *); 560Sstevel@tonic-gate 570Sstevel@tonic-gate /* 580Sstevel@tonic-gate * Module linkage information for the kernel. 590Sstevel@tonic-gate */ 600Sstevel@tonic-gate static struct modlmisc modlmisc = { 61*7696SRichard.Bean@Sun.COM &mod_miscops, "tod module for Mostek M48T59" 620Sstevel@tonic-gate }; 630Sstevel@tonic-gate 640Sstevel@tonic-gate static struct modlinkage modlinkage = { 650Sstevel@tonic-gate MODREV_1, (void *)&modlmisc, NULL 660Sstevel@tonic-gate }; 670Sstevel@tonic-gate 680Sstevel@tonic-gate int 690Sstevel@tonic-gate _init(void) 700Sstevel@tonic-gate { 710Sstevel@tonic-gate if (strcmp(tod_module_name, "todmostek") == 0) { 720Sstevel@tonic-gate tod_ops.tod_get = todm_get; 730Sstevel@tonic-gate tod_ops.tod_set = todm_set; 740Sstevel@tonic-gate tod_ops.tod_set_watchdog_timer = todm_set_watchdog_timer; 750Sstevel@tonic-gate tod_ops.tod_clear_watchdog_timer = todm_clear_watchdog_timer; 760Sstevel@tonic-gate tod_ops.tod_set_power_alarm = todm_set_power_alarm; 770Sstevel@tonic-gate tod_ops.tod_clear_power_alarm = todm_clear_power_alarm; 780Sstevel@tonic-gate tod_ops.tod_get_cpufrequency = todm_get_cpufrequency; 790Sstevel@tonic-gate 800Sstevel@tonic-gate /* 810Sstevel@tonic-gate * check if hardware watchdog timer is available and user 820Sstevel@tonic-gate * enabled it. 830Sstevel@tonic-gate */ 840Sstevel@tonic-gate if (watchdog_enable) { 850Sstevel@tonic-gate if (!watchdog_available) { 860Sstevel@tonic-gate cmn_err(CE_WARN, "Hardware watchdog unavailable"); 870Sstevel@tonic-gate } else if (boothowto & RB_DEBUG) { 880Sstevel@tonic-gate cmn_err(CE_WARN, "Hardware watchdog disabled" 890Sstevel@tonic-gate " [debugger]"); 900Sstevel@tonic-gate } 910Sstevel@tonic-gate } 920Sstevel@tonic-gate } 930Sstevel@tonic-gate 940Sstevel@tonic-gate return (mod_install(&modlinkage)); 950Sstevel@tonic-gate } 960Sstevel@tonic-gate 970Sstevel@tonic-gate int 980Sstevel@tonic-gate _fini(void) 990Sstevel@tonic-gate { 1000Sstevel@tonic-gate if (strcmp(tod_module_name, "todmostek") == 0) 1010Sstevel@tonic-gate return (EBUSY); 1020Sstevel@tonic-gate else 1030Sstevel@tonic-gate return (mod_remove(&modlinkage)); 1040Sstevel@tonic-gate } 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate int 1070Sstevel@tonic-gate _info(struct modinfo *modinfop) 1080Sstevel@tonic-gate { 1090Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 1100Sstevel@tonic-gate } 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate /* 1130Sstevel@tonic-gate * Read the current time from the clock chip and convert to UNIX form. 1140Sstevel@tonic-gate * Assumes that the year in the clock chip is valid. 1150Sstevel@tonic-gate * Must be called with tod_lock held. 1160Sstevel@tonic-gate */ 1170Sstevel@tonic-gate static timestruc_t 1180Sstevel@tonic-gate todm_get(void) 1190Sstevel@tonic-gate { 1200Sstevel@tonic-gate timestruc_t ts; 1210Sstevel@tonic-gate #ifndef MPSAS 1220Sstevel@tonic-gate todinfo_t tod; 1230Sstevel@tonic-gate int s; 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate s = splhi(); 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate CLOCK->clk_ctrl |= CLK_CTRL_READ; 1300Sstevel@tonic-gate tod.tod_year = BCD_TO_BYTE(CLOCK->clk_year) + YRBASE; 1310Sstevel@tonic-gate tod.tod_month = BCD_TO_BYTE(CLOCK->clk_month & 0x1f); 1320Sstevel@tonic-gate tod.tod_day = BCD_TO_BYTE(CLOCK->clk_day & 0x3f); 1330Sstevel@tonic-gate tod.tod_dow = BCD_TO_BYTE(CLOCK->clk_weekday & 0x7); 1340Sstevel@tonic-gate tod.tod_hour = BCD_TO_BYTE(CLOCK->clk_hour & 0x3f); 1350Sstevel@tonic-gate tod.tod_min = BCD_TO_BYTE(CLOCK->clk_min & 0x7f); 1360Sstevel@tonic-gate tod.tod_sec = BCD_TO_BYTE(CLOCK->clk_sec & 0x7f); 1370Sstevel@tonic-gate CLOCK->clk_ctrl &= ~CLK_CTRL_READ; 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate splx(s); 1400Sstevel@tonic-gate 1410Sstevel@tonic-gate /* 1420Sstevel@tonic-gate * Apparently the m48t59 doesn't quite do what the spec sheet says. 1430Sstevel@tonic-gate * The spec says reading WRD will reset the timer but that doesn't work. 1440Sstevel@tonic-gate * So we need to reload timeout each time we want to reset the timer. 1450Sstevel@tonic-gate */ 1460Sstevel@tonic-gate CLOCK->clk_watchdog = watchdog_bits; 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate ts.tv_sec = tod_to_utc(tod); 1490Sstevel@tonic-gate ts.tv_nsec = 0; 1500Sstevel@tonic-gate #else 1510Sstevel@tonic-gate ts.tv_sec = 0; 1520Sstevel@tonic-gate ts.tv_nsec = 0; 1530Sstevel@tonic-gate #endif 1540Sstevel@tonic-gate return (ts); 1550Sstevel@tonic-gate } 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate /* 1580Sstevel@tonic-gate * Write the specified time into the clock chip. 1590Sstevel@tonic-gate * Must be called with tod_lock held. 1600Sstevel@tonic-gate */ 1610Sstevel@tonic-gate /* ARGSUSED */ 1620Sstevel@tonic-gate static void 1630Sstevel@tonic-gate todm_set(timestruc_t ts) 1640Sstevel@tonic-gate { 1650Sstevel@tonic-gate #ifndef MPSAS 1660Sstevel@tonic-gate todinfo_t tod = utc_to_tod(ts.tv_sec); 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate CLOCK->clk_ctrl |= CLK_CTRL_WRITE; /* allow writes */ 1710Sstevel@tonic-gate CLOCK->clk_year = BYTE_TO_BCD(tod.tod_year - YRBASE); 1720Sstevel@tonic-gate CLOCK->clk_month = BYTE_TO_BCD(tod.tod_month); 1730Sstevel@tonic-gate CLOCK->clk_day = BYTE_TO_BCD(tod.tod_day); 1740Sstevel@tonic-gate CLOCK->clk_weekday = BYTE_TO_BCD(tod.tod_dow); 1750Sstevel@tonic-gate CLOCK->clk_hour = BYTE_TO_BCD(tod.tod_hour); 1760Sstevel@tonic-gate CLOCK->clk_min = BYTE_TO_BCD(tod.tod_min); 1770Sstevel@tonic-gate CLOCK->clk_sec = BYTE_TO_BCD(tod.tod_sec); 1780Sstevel@tonic-gate CLOCK->clk_ctrl &= ~CLK_CTRL_WRITE; /* load values */ 1790Sstevel@tonic-gate #endif 1800Sstevel@tonic-gate } 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate /* 1840Sstevel@tonic-gate * Program the watchdog timer shadow register with the specified value. 1850Sstevel@tonic-gate * Setting the timer to zero value means no watchdog timeout. 1860Sstevel@tonic-gate */ 1870Sstevel@tonic-gate static uint_t 1880Sstevel@tonic-gate todm_set_watchdog_timer(uint_t timeoutval) 1890Sstevel@tonic-gate { 1900Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate if (watchdog_enable == 0 || watchdog_available == 0 || 1930Sstevel@tonic-gate (boothowto & RB_DEBUG)) 1940Sstevel@tonic-gate return (0); 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate watchdog_timeout = timeoutval; 1970Sstevel@tonic-gate watchdog_bits = CLK_WATCHDOG_BITS(timeoutval); 1980Sstevel@tonic-gate watchdog_activated = 1; 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate return (timeoutval); 2010Sstevel@tonic-gate } 2020Sstevel@tonic-gate 2030Sstevel@tonic-gate /* 2040Sstevel@tonic-gate * Clear the hardware timer register. Also zero out the watchdog timer 2050Sstevel@tonic-gate * shadow register. 2060Sstevel@tonic-gate */ 2070Sstevel@tonic-gate static uint_t 2080Sstevel@tonic-gate todm_clear_watchdog_timer(void) 2090Sstevel@tonic-gate { 2100Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate if (watchdog_activated == 0) 2130Sstevel@tonic-gate return (0); 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate #ifndef MPSAS 2160Sstevel@tonic-gate CLOCK->clk_watchdog = 0; 2170Sstevel@tonic-gate #endif /* MPSAS */ 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate watchdog_bits = 0; 2200Sstevel@tonic-gate watchdog_activated = 0; 2210Sstevel@tonic-gate return (watchdog_timeout); 2220Sstevel@tonic-gate } 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate /* 2250Sstevel@tonic-gate * program the tod registers for alarm to go off at the specified time 2260Sstevel@tonic-gate */ 2270Sstevel@tonic-gate static void 2280Sstevel@tonic-gate todm_set_power_alarm(timestruc_t ts) 2290Sstevel@tonic-gate { 2300Sstevel@tonic-gate #ifndef MPSAS 2310Sstevel@tonic-gate todinfo_t tod; 2320Sstevel@tonic-gate uchar_t c; 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 2350Sstevel@tonic-gate tod = utc_to_tod(ts.tv_sec); 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */ 2380Sstevel@tonic-gate #ifdef lint 2390Sstevel@tonic-gate CLOCK->clk_flags = c; 2400Sstevel@tonic-gate #endif 2410Sstevel@tonic-gate CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */ 2420Sstevel@tonic-gate 2430Sstevel@tonic-gate CLOCK->clk_day &= ~CLK_FREQT; /* keep Freqency Test bit cleared */ 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate CLOCK->clk_alm_day = BYTE_TO_BCD(tod.tod_day); 2460Sstevel@tonic-gate CLOCK->clk_alm_hours = BYTE_TO_BCD(tod.tod_hour); 2470Sstevel@tonic-gate CLOCK->clk_alm_mins = BYTE_TO_BCD(tod.tod_min); 2480Sstevel@tonic-gate CLOCK->clk_alm_secs = BYTE_TO_BCD(tod.tod_sec); 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate CLOCK->clk_interrupts |= CLK_ALARM_ENABLE; /* enable alarm intr */ 2510Sstevel@tonic-gate #endif /* MPSAS */ 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate /* 2550Sstevel@tonic-gate * clear alarm interrupt 2560Sstevel@tonic-gate */ 2570Sstevel@tonic-gate static void 2580Sstevel@tonic-gate todm_clear_power_alarm() 2590Sstevel@tonic-gate { 2600Sstevel@tonic-gate #ifndef MPSAS 2610Sstevel@tonic-gate uchar_t c; 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */ 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate #ifdef lint 2680Sstevel@tonic-gate CLOCK->clk_flags = c; 2690Sstevel@tonic-gate #endif 2700Sstevel@tonic-gate 2710Sstevel@tonic-gate CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */ 2720Sstevel@tonic-gate #endif /* MPSAS */ 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * Determine the cpu frequency by watching the TOD chip rollover twice. 2770Sstevel@tonic-gate * Cpu clock rate is determined by computing the ticks added (in tick register) 2780Sstevel@tonic-gate * during one second interval on TOD. 2790Sstevel@tonic-gate */ 2800Sstevel@tonic-gate uint64_t 2810Sstevel@tonic-gate todm_get_cpufrequency(void) 2820Sstevel@tonic-gate { 2830Sstevel@tonic-gate #ifndef MPSAS 2840Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate return (find_cpufrequency(&(TIMECHECK_CLOCK->clk_sec))); 2870Sstevel@tonic-gate #else 2880Sstevel@tonic-gate return (cpunodes[CPU->cpu_id].clock_freq); 2890Sstevel@tonic-gate #endif /* MPSAS */ 2900Sstevel@tonic-gate } 291