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