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 2009 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, 87 "Hardware watchdog unavailable"); 88 } else if (boothowto & RB_DEBUG) { 89 cmn_err(CE_WARN, "Hardware watchdog disabled" 90 " [debugger]"); 91 } 92 } 93 } 94 95 return (mod_install(&modlinkage)); 96 } 97 98 int 99 _fini(void) 100 { 101 if (strcmp(tod_module_name, "todmostek") == 0) 102 return (EBUSY); 103 else 104 return (mod_remove(&modlinkage)); 105 } 106 107 int 108 _info(struct modinfo *modinfop) 109 { 110 return (mod_info(&modlinkage, modinfop)); 111 } 112 113 /* 114 * Read the current time from the clock chip and convert to UNIX form. 115 * Assumes that the year in the clock chip is valid. 116 * Must be called with tod_lock held. 117 */ 118 static timestruc_t 119 todm_get(void) 120 { 121 timestruc_t ts; 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 return (ts); 151 } 152 153 /* 154 * Write the specified time into the clock chip. 155 * Must be called with tod_lock held. 156 */ 157 /* ARGSUSED */ 158 static void 159 todm_set(timestruc_t ts) 160 { 161 todinfo_t tod = utc_to_tod(ts.tv_sec); 162 163 ASSERT(MUTEX_HELD(&tod_lock)); 164 165 CLOCK->clk_ctrl |= CLK_CTRL_WRITE; /* allow writes */ 166 CLOCK->clk_year = BYTE_TO_BCD(tod.tod_year - YRBASE); 167 CLOCK->clk_month = BYTE_TO_BCD(tod.tod_month); 168 CLOCK->clk_day = BYTE_TO_BCD(tod.tod_day); 169 CLOCK->clk_weekday = BYTE_TO_BCD(tod.tod_dow); 170 CLOCK->clk_hour = BYTE_TO_BCD(tod.tod_hour); 171 CLOCK->clk_min = BYTE_TO_BCD(tod.tod_min); 172 CLOCK->clk_sec = BYTE_TO_BCD(tod.tod_sec); 173 CLOCK->clk_ctrl &= ~CLK_CTRL_WRITE; /* load values */ 174 } 175 176 177 /* 178 * Program the watchdog timer shadow register with the specified value. 179 * Setting the timer to zero value means no watchdog timeout. 180 */ 181 static uint_t 182 todm_set_watchdog_timer(uint_t timeoutval) 183 { 184 ASSERT(MUTEX_HELD(&tod_lock)); 185 186 if (watchdog_enable == 0 || watchdog_available == 0 || 187 (boothowto & RB_DEBUG)) 188 return (0); 189 190 watchdog_timeout = timeoutval; 191 watchdog_bits = CLK_WATCHDOG_BITS(timeoutval); 192 watchdog_activated = 1; 193 194 return (timeoutval); 195 } 196 197 /* 198 * Clear the hardware timer register. Also zero out the watchdog timer 199 * shadow register. 200 */ 201 static uint_t 202 todm_clear_watchdog_timer(void) 203 { 204 ASSERT(MUTEX_HELD(&tod_lock)); 205 206 if (watchdog_activated == 0) 207 return (0); 208 209 CLOCK->clk_watchdog = 0; 210 211 watchdog_bits = 0; 212 watchdog_activated = 0; 213 return (watchdog_timeout); 214 } 215 216 /* 217 * program the tod registers for alarm to go off at the specified time 218 */ 219 static void 220 todm_set_power_alarm(timestruc_t ts) 221 { 222 todinfo_t tod; 223 uchar_t c; 224 225 ASSERT(MUTEX_HELD(&tod_lock)); 226 tod = utc_to_tod(ts.tv_sec); 227 228 c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */ 229 #ifdef lint 230 CLOCK->clk_flags = c; 231 #endif 232 CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */ 233 234 CLOCK->clk_day &= ~CLK_FREQT; /* keep Freqency Test bit cleared */ 235 236 CLOCK->clk_alm_day = BYTE_TO_BCD(tod.tod_day); 237 CLOCK->clk_alm_hours = BYTE_TO_BCD(tod.tod_hour); 238 CLOCK->clk_alm_mins = BYTE_TO_BCD(tod.tod_min); 239 CLOCK->clk_alm_secs = BYTE_TO_BCD(tod.tod_sec); 240 241 CLOCK->clk_interrupts |= CLK_ALARM_ENABLE; /* enable alarm intr */ 242 } 243 244 /* 245 * clear alarm interrupt 246 */ 247 static void 248 todm_clear_power_alarm() 249 { 250 uchar_t c; 251 252 ASSERT(MUTEX_HELD(&tod_lock)); 253 254 c = CLOCK->clk_flags; /* clear alarm intr flag by reading the reg */ 255 256 #ifdef lint 257 CLOCK->clk_flags = c; 258 #endif 259 260 CLOCK->clk_interrupts &= ~CLK_ALARM_ENABLE; /* disable alarm intr */ 261 } 262 263 /* 264 * Determine the cpu frequency by watching the TOD chip rollover twice. 265 * Cpu clock rate is determined by computing the ticks added (in tick register) 266 * during one second interval on TOD. 267 */ 268 uint64_t 269 todm_get_cpufrequency(void) 270 { 271 ASSERT(MUTEX_HELD(&tod_lock)); 272 273 return (find_cpufrequency(&(TIMECHECK_CLOCK->clk_sec))); 274 } 275