1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * tod driver module for Mostek M48T59 part 31 */ 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/sysmacros.h> 36 #include <sys/systm.h> 37 #include <sys/errno.h> 38 #include <sys/modctl.h> 39 #include <sys/autoconf.h> 40 #include <sys/debug.h> 41 #include <sys/clock.h> 42 #include <sys/todmostek.h> 43 #include <sys/reboot.h> 44 #include <sys/cmn_err.h> 45 #include <sys/cpuvar.h> 46 47 static timestruc_t todm_get(void); 48 static void todm_set(timestruc_t); 49 static uint_t todm_set_watchdog_timer(uint_t); 50 static uint_t todm_clear_watchdog_timer(void); 51 static void todm_set_power_alarm(timestruc_t); 52 static void todm_clear_power_alarm(void); 53 static uint64_t todm_get_cpufrequency(void); 54 55 static uchar_t watchdog_bits = 0; 56 static uint_t watchdog_timeout; 57 58 extern uint64_t find_cpufrequency(volatile uchar_t *); 59 60 /* 61 * Module linkage information for the kernel. 62 */ 63 static struct modlmisc modlmisc = { 64 &mod_miscops, "tod module for Mostek M48T59 %I%" 65 }; 66 67 static struct modlinkage modlinkage = { 68 MODREV_1, (void *)&modlmisc, NULL 69 }; 70 71 int 72 _init(void) 73 { 74 if (strcmp(tod_module_name, "todmostek") == 0) { 75 tod_ops.tod_get = todm_get; 76 tod_ops.tod_set = todm_set; 77 tod_ops.tod_set_watchdog_timer = todm_set_watchdog_timer; 78 tod_ops.tod_clear_watchdog_timer = todm_clear_watchdog_timer; 79 tod_ops.tod_set_power_alarm = todm_set_power_alarm; 80 tod_ops.tod_clear_power_alarm = todm_clear_power_alarm; 81 tod_ops.tod_get_cpufrequency = todm_get_cpufrequency; 82 83 /* 84 * check if hardware watchdog timer is available and user 85 * enabled it. 86 */ 87 if (watchdog_enable) { 88 if (!watchdog_available) { 89 cmn_err(CE_WARN, "Hardware watchdog unavailable"); 90 } else if (boothowto & RB_DEBUG) { 91 cmn_err(CE_WARN, "Hardware watchdog disabled" 92 " [debugger]"); 93 } 94 } 95 } 96 97 return (mod_install(&modlinkage)); 98 } 99 100 int 101 _fini(void) 102 { 103 if (strcmp(tod_module_name, "todmostek") == 0) 104 return (EBUSY); 105 else 106 return (mod_remove(&modlinkage)); 107 } 108 109 int 110 _info(struct modinfo *modinfop) 111 { 112 return (mod_info(&modlinkage, modinfop)); 113 } 114 115 /* 116 * Read the current time from the clock chip and convert to UNIX form. 117 * Assumes that the year in the clock chip is valid. 118 * Must be called with tod_lock held. 119 */ 120 static timestruc_t 121 todm_get(void) 122 { 123 timestruc_t ts; 124 #ifndef MPSAS 125 todinfo_t tod; 126 int s; 127 128 ASSERT(MUTEX_HELD(&tod_lock)); 129 130 s = splhi(); 131 132 CLOCK->clk_ctrl |= CLK_CTRL_READ; 133 tod.tod_year = BCD_TO_BYTE(CLOCK->clk_year) + YRBASE; 134 tod.tod_month = BCD_TO_BYTE(CLOCK->clk_month & 0x1f); 135 tod.tod_day = BCD_TO_BYTE(CLOCK->clk_day & 0x3f); 136 tod.tod_dow = BCD_TO_BYTE(CLOCK->clk_weekday & 0x7); 137 tod.tod_hour = BCD_TO_BYTE(CLOCK->clk_hour & 0x3f); 138 tod.tod_min = BCD_TO_BYTE(CLOCK->clk_min & 0x7f); 139 tod.tod_sec = BCD_TO_BYTE(CLOCK->clk_sec & 0x7f); 140 CLOCK->clk_ctrl &= ~CLK_CTRL_READ; 141 142 splx(s); 143 144 /* 145 * Apparently the m48t59 doesn't quite do what the spec sheet says. 146 * The spec says reading WRD will reset the timer but that doesn't work. 147 * So we need to reload timeout each time we want to reset the timer. 148 */ 149 CLOCK->clk_watchdog = watchdog_bits; 150 151 ts.tv_sec = tod_to_utc(tod); 152 ts.tv_nsec = 0; 153 #else 154 ts.tv_sec = 0; 155 ts.tv_nsec = 0; 156 #endif 157 return (ts); 158 } 159 160 /* 161 * Write the specified time into the clock chip. 162 * Must be called with tod_lock held. 163 */ 164 /* ARGSUSED */ 165 static void 166 todm_set(timestruc_t ts) 167 { 168 #ifndef MPSAS 169 todinfo_t tod = utc_to_tod(ts.tv_sec); 170 171 ASSERT(MUTEX_HELD(&tod_lock)); 172 173 CLOCK->clk_ctrl |= CLK_CTRL_WRITE; /* allow writes */ 174 CLOCK->clk_year = BYTE_TO_BCD(tod.tod_year - YRBASE); 175 CLOCK->clk_month = BYTE_TO_BCD(tod.tod_month); 176 CLOCK->clk_day = BYTE_TO_BCD(tod.tod_day); 177 CLOCK->clk_weekday = BYTE_TO_BCD(tod.tod_dow); 178 CLOCK->clk_hour = BYTE_TO_BCD(tod.tod_hour); 179 CLOCK->clk_min = BYTE_TO_BCD(tod.tod_min); 180 CLOCK->clk_sec = BYTE_TO_BCD(tod.tod_sec); 181 CLOCK->clk_ctrl &= ~CLK_CTRL_WRITE; /* load values */ 182 #endif 183 } 184 185 186 /* 187 * Program the watchdog timer shadow register with the specified value. 188 * Setting the timer to zero value means no watchdog timeout. 189 */ 190 static uint_t 191 todm_set_watchdog_timer(uint_t timeoutval) 192 { 193 ASSERT(MUTEX_HELD(&tod_lock)); 194 195 if (watchdog_enable == 0 || watchdog_available == 0 || 196 (boothowto & RB_DEBUG)) 197 return (0); 198 199 watchdog_timeout = timeoutval; 200 watchdog_bits = CLK_WATCHDOG_BITS(timeoutval); 201 watchdog_activated = 1; 202 203 return (timeoutval); 204 } 205 206 /* 207 * Clear the hardware timer register. Also zero out the watchdog timer 208 * shadow register. 209 */ 210 static uint_t 211 todm_clear_watchdog_timer(void) 212 { 213 ASSERT(MUTEX_HELD(&tod_lock)); 214 215 if (watchdog_activated == 0) 216 return (0); 217 218 #ifndef MPSAS 219 CLOCK->clk_watchdog = 0; 220 #endif /* MPSAS */ 221 222 watchdog_bits = 0; 223 watchdog_activated = 0; 224 return (watchdog_timeout); 225 } 226 227 /* 228 * program the tod registers for alarm to go off at the specified time 229 */ 230 static void 231 todm_set_power_alarm(timestruc_t ts) 232 { 233 #ifndef MPSAS 234 todinfo_t tod; 235 uchar_t c; 236 237 ASSERT(MUTEX_HELD(&tod_lock)); 238 tod = utc_to_tod(ts.tv_sec); 239 240 c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */ 241 #ifdef lint 242 CLOCK->clk_flags = c; 243 #endif 244 CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */ 245 246 CLOCK->clk_day &= ~CLK_FREQT; /* keep Freqency Test bit cleared */ 247 248 CLOCK->clk_alm_day = BYTE_TO_BCD(tod.tod_day); 249 CLOCK->clk_alm_hours = BYTE_TO_BCD(tod.tod_hour); 250 CLOCK->clk_alm_mins = BYTE_TO_BCD(tod.tod_min); 251 CLOCK->clk_alm_secs = BYTE_TO_BCD(tod.tod_sec); 252 253 CLOCK->clk_interrupts |= CLK_ALARM_ENABLE; /* enable alarm intr */ 254 #endif /* MPSAS */ 255 } 256 257 /* 258 * clear alarm interrupt 259 */ 260 static void 261 todm_clear_power_alarm() 262 { 263 #ifndef MPSAS 264 uchar_t c; 265 266 ASSERT(MUTEX_HELD(&tod_lock)); 267 268 c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */ 269 270 #ifdef lint 271 CLOCK->clk_flags = c; 272 #endif 273 274 CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */ 275 #endif /* MPSAS */ 276 } 277 278 /* 279 * Determine the cpu frequency by watching the TOD chip rollover twice. 280 * Cpu clock rate is determined by computing the ticks added (in tick register) 281 * during one second interval on TOD. 282 */ 283 uint64_t 284 todm_get_cpufrequency(void) 285 { 286 #ifndef MPSAS 287 ASSERT(MUTEX_HELD(&tod_lock)); 288 289 return (find_cpufrequency(&(TIMECHECK_CLOCK->clk_sec))); 290 #else 291 return (cpunodes[CPU->cpu_id].clock_freq); 292 #endif /* MPSAS */ 293 } 294