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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/conf.h> 28 #include <sys/kmem.h> 29 #include <sys/open.h> 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 33 #include <sys/todm5819.h> 34 #include <sys/modctl.h> 35 #include <sys/stat.h> 36 #include <sys/clock.h> 37 #include <sys/reboot.h> 38 #include <sys/machsystm.h> 39 #include <sys/poll.h> 40 #include <sys/pbio.h> 41 #include <sys/lom_priv.h> 42 43 #define WDOG_ON 1 44 #define WDOG_OFF 0 45 46 static timestruc_t todbl_get(void); 47 static void todbl_set(timestruc_t); 48 static uint_t todbl_set_watchdog_timer(uint_t); 49 static uint_t todbl_clear_watchdog_timer(void); 50 static void todbl_set_power_alarm(timestruc_t); 51 static void todbl_clear_power_alarm(void); 52 static uint64_t todbl_get_cpufrequency(void); 53 54 static todinfo_t rtc_to_tod(struct rtc_t *); 55 static uint_t read_rtc(struct rtc_t *); 56 static void write_rtc_time(struct rtc_t *); 57 static uint_t configure_wdog(uint8_t new_state); 58 59 extern uint64_t find_cpufrequency(volatile uint8_t *); 60 61 /* 62 * External variables 63 */ 64 extern int watchdog_enable; 65 extern int watchdog_available; 66 extern int watchdog_activated; 67 extern uint_t watchdog_timeout_seconds; 68 extern int boothowto; 69 extern void (*bsc_drv_func_ptr)(struct bscv_idi_info *); 70 71 /* 72 * Global variables 73 */ 74 int m5819_debug_flags; 75 uint8_t wdog_reset_on_timeout = 1; 76 static clock_t last_pat_lbt; 77 78 79 static struct modlmisc modlmisc = { 80 &mod_miscops, "todblade module", 81 }; 82 83 static struct modlinkage modlinkage = { 84 MODREV_1, &modlmisc, NULL 85 }; 86 87 88 int 89 _init(void) 90 { 91 if (strcmp(tod_module_name, "todblade") == 0) { 92 RTC_PUT8(RTC_B, (RTC_DM | RTC_HM)); 93 94 tod_ops.tod_get = todbl_get; 95 tod_ops.tod_set = todbl_set; 96 tod_ops.tod_set_watchdog_timer = todbl_set_watchdog_timer; 97 tod_ops.tod_clear_watchdog_timer = todbl_clear_watchdog_timer; 98 tod_ops.tod_set_power_alarm = todbl_set_power_alarm; 99 tod_ops.tod_clear_power_alarm = todbl_clear_power_alarm; 100 tod_ops.tod_get_cpufrequency = todbl_get_cpufrequency; 101 102 if (watchdog_enable && (boothowto & RB_DEBUG)) { 103 watchdog_available = 0; 104 cmn_err(CE_WARN, "todblade: kernel debugger " 105 "detected: hardware watchdog disabled"); 106 } 107 } 108 return (mod_install(&modlinkage)); 109 } 110 111 int 112 _fini(void) 113 { 114 if (strcmp(tod_module_name, "todblade") == 0) { 115 return (EBUSY); 116 } else { 117 return (mod_remove(&modlinkage)); 118 } 119 } 120 121 /* 122 * The loadable-module _info(9E) entry point 123 */ 124 int 125 _info(struct modinfo *modinfop) 126 { 127 return (mod_info(&modlinkage, modinfop)); 128 } 129 130 131 /* 132 * Read the current time from the clock chip and convert to UNIX form. 133 * Assumes that the year in the clock chip is valid. 134 * Must be called with tod_lock held. 135 */ 136 static timestruc_t 137 todbl_get(void) 138 { 139 int i; 140 timestruc_t ts; 141 struct rtc_t rtc; 142 struct bscv_idi_info bscv_info; 143 144 ASSERT(MUTEX_HELD(&tod_lock)); 145 146 /* 147 * We must check that the value of watchdog enable hasnt changed 148 * as its a user knob for turning it on and off 149 */ 150 if (watchdog_available) { 151 if (watchdog_activated && !watchdog_enable) { 152 (void) configure_wdog(WDOG_OFF); 153 } else if (!watchdog_activated && watchdog_enable) { 154 (void) configure_wdog(WDOG_ON); 155 } else if (watchdog_activated && 156 (ddi_get_lbolt() - last_pat_lbt) >= SEC_TO_TICK(1)) { 157 /* 158 * PAT THE WATCHDOG!! 159 * We dont want to accelerate the pat frequency 160 * when userland calls to the TOD_GET_DATE ioctl 161 * pass through here. 162 */ 163 bscv_info.type = BSCV_IDI_WDOG_PAT; 164 bscv_info.data = NULL; 165 bscv_info.size = 0; 166 if (bsc_drv_func_ptr != NULL) { 167 (*bsc_drv_func_ptr)(&bscv_info); 168 last_pat_lbt = ddi_get_lbolt(); 169 } 170 } 171 } 172 173 /* 174 * Read from the tod, and if it isnt accessible wait 175 * before retrying. 176 */ 177 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) { 178 if (read_rtc(&rtc)) 179 break; 180 drv_usecwait(TODM5819_UIP_WAIT_USEC); 181 } 182 if (i == TODM5819_UIP_RETRY_THRESH) { 183 /* 184 * We couldn't read from the TOD. 185 */ 186 tod_status_set(TOD_GET_FAILED); 187 return (hrestime); 188 } 189 190 DPRINTF("todbl_get: century=%d year=%d dom=%d hrs=%d\n", 191 rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs); 192 193 /* read was successful so ensure failure flag is clear */ 194 tod_status_clear(TOD_GET_FAILED); 195 196 ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc)); 197 ts.tv_nsec = 0; 198 return (ts); 199 } 200 201 static todinfo_t 202 rtc_to_tod(struct rtc_t *rtc) 203 { 204 todinfo_t tod; 205 206 /* 207 * tod_year is base 1900 so this code needs to adjust the true 208 * year retrieved from the rtc's century and year fields. 209 */ 210 tod.tod_year = rtc->rtc_year + (rtc->rtc_century * 100) - 1900; 211 tod.tod_month = rtc->rtc_mon; 212 tod.tod_day = rtc->rtc_dom; 213 tod.tod_dow = rtc->rtc_dow; 214 tod.tod_hour = rtc->rtc_hrs; 215 tod.tod_min = rtc->rtc_min; 216 tod.tod_sec = rtc->rtc_sec; 217 218 return (tod); 219 } 220 221 222 static uint_t 223 read_rtc(struct rtc_t *rtc) 224 { 225 int s; 226 uint_t rtc_readable = 0; 227 228 s = splhi(); 229 /* 230 * If UIP bit is not set we have at least 274us 231 * to read the values. 232 */ 233 if (!(RTC_GET8(RTC_A) & RTC_UIP)) { 234 rtc_readable = 1; 235 236 rtc->rtc_sec = RTC_GET8(RTC_SEC); 237 rtc->rtc_asec = RTC_GET8(RTC_ASEC); 238 rtc->rtc_min = RTC_GET8(RTC_MIN); 239 rtc->rtc_amin = RTC_GET8(RTC_AMIN); 240 241 rtc->rtc_hrs = RTC_GET8(RTC_HRS); 242 rtc->rtc_ahrs = RTC_GET8(RTC_AHRS); 243 rtc->rtc_dow = RTC_GET8(RTC_DOW); 244 rtc->rtc_dom = RTC_GET8(RTC_DOM); 245 rtc->rtc_adom = RTC_GET8(RTC_D) & 0x3f; 246 247 rtc->rtc_mon = RTC_GET8(RTC_MON); 248 rtc->rtc_year = RTC_GET8(RTC_YEAR); 249 rtc->rtc_century = RTC_GET8(RTC_CENTURY); 250 rtc->rtc_amon = 0; 251 252 /* Clear wakeup data */ 253 rtc->apc_wdwr = 0; 254 rtc->apc_wdmr = 0; 255 rtc->apc_wmr = 0; 256 rtc->apc_wyr = 0; 257 rtc->apc_wcr = 0; 258 } 259 260 splx(s); 261 return (rtc_readable); 262 } 263 264 /* 265 * Write the specified time into the clock chip. 266 * Must be called with tod_lock held. 267 */ 268 static void 269 todbl_set(timestruc_t ts) 270 { 271 struct rtc_t rtc; 272 todinfo_t tod = utc_to_tod(ts.tv_sec); 273 struct bscv_idi_info bscv_info; 274 int year; 275 276 ASSERT(MUTEX_HELD(&tod_lock)); 277 278 /* tod_year is base 1900 so this code needs to adjust */ 279 year = 1900 + tod.tod_year; 280 rtc.rtc_year = year % 100; 281 rtc.rtc_century = year / 100; 282 rtc.rtc_mon = (uint8_t)tod.tod_month; 283 rtc.rtc_dom = (uint8_t)tod.tod_day; 284 rtc.rtc_dow = (uint8_t)tod.tod_dow; 285 rtc.rtc_hrs = (uint8_t)tod.tod_hour; 286 rtc.rtc_min = (uint8_t)tod.tod_min; 287 rtc.rtc_sec = (uint8_t)tod.tod_sec; 288 DPRINTF("todbl_set: century=%d year=%d dom=%d hrs=%d\n", 289 rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs); 290 291 write_rtc_time(&rtc); 292 293 /* 294 * Because of a generic solaris problem where calls to stime() 295 * starve calls to tod_get(), we need to check to see when the 296 * watchdog was last patted and pat it if necessary. 297 */ 298 if (watchdog_activated && 299 (ddi_get_lbolt() - last_pat_lbt) >= SEC_TO_TICK(1)) { 300 /* 301 * Pat the watchdog! 302 */ 303 bscv_info.type = BSCV_IDI_WDOG_PAT; 304 bscv_info.data = NULL; 305 bscv_info.size = 0; 306 if (bsc_drv_func_ptr != NULL) { 307 (*bsc_drv_func_ptr)(&bscv_info); 308 last_pat_lbt = ddi_get_lbolt(); 309 } 310 } 311 } 312 313 static void 314 write_rtc_time(struct rtc_t *rtc) 315 { 316 uint8_t regb; 317 int i; 318 319 /* 320 * Freeze 321 */ 322 regb = RTC_GET8(RTC_B); 323 RTC_PUT8(RTC_B, (regb | RTC_SET)); 324 325 /* 326 * If an update is in progress wait for the UIP flag to clear. 327 * If we write whilst UIP is still set there is a slight but real 328 * possibility of corrupting the RTC date and time registers. 329 * 330 * The expected wait is one internal cycle of the chip. We could 331 * simply spin but this may hang a CPU if we were to have a broken 332 * RTC chip where UIP is stuck, so we use a retry loop instead. 333 * No critical section is needed here as the UIP flag will not be 334 * re-asserted until we clear RTC_SET. 335 */ 336 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) { 337 if (!(RTC_GET8(RTC_A) & RTC_UIP)) { 338 break; 339 } 340 drv_usecwait(TODM5819_UIP_WAIT_USEC); 341 } 342 if (i < TODM5819_UIP_RETRY_THRESH) { 343 RTC_PUT8(RTC_SEC, (rtc->rtc_sec)); 344 RTC_PUT8(RTC_ASEC, (rtc->rtc_asec)); 345 RTC_PUT8(RTC_MIN, (rtc->rtc_min)); 346 RTC_PUT8(RTC_AMIN, (rtc->rtc_amin)); 347 348 RTC_PUT8(RTC_HRS, (rtc->rtc_hrs)); 349 RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs)); 350 RTC_PUT8(RTC_DOW, (rtc->rtc_dow)); 351 RTC_PUT8(RTC_DOM, (rtc->rtc_dom)); 352 353 RTC_PUT8(RTC_MON, (rtc->rtc_mon)); 354 RTC_PUT8(RTC_YEAR, (rtc->rtc_year)); 355 RTC_PUT8(RTC_CENTURY, (rtc->rtc_century)); 356 } else { 357 cmn_err(CE_WARN, "todblade: Could not write the RTC\n"); 358 } 359 360 /* 361 * Unfreeze 362 */ 363 RTC_PUT8(RTC_B, regb); 364 } 365 366 367 368 /* 369 * The TOD alarm functionality is not supported on our platform 370 * as the interrupt is not wired, so do nothing. 371 */ 372 /*ARGSUSED*/ 373 static void 374 todbl_set_power_alarm(timestruc_t ts) 375 { 376 ASSERT(MUTEX_HELD(&tod_lock)); 377 } 378 379 /* 380 * clear alarm interrupt 381 */ 382 static void 383 todbl_clear_power_alarm(void) 384 { 385 ASSERT(MUTEX_HELD(&tod_lock)); 386 } 387 388 /* 389 * Determine the cpu frequency by watching the TOD chip rollover twice. 390 * Cpu clock rate is determined by computing the ticks added (in tick register) 391 * during one second interval on TOD. 392 */ 393 uint64_t 394 todbl_get_cpufrequency(void) 395 { 396 ASSERT(MUTEX_HELD(&tod_lock)); 397 M5819_ADDR_REG = RTC_SEC; 398 return (find_cpufrequency(v_rtc_data_reg)); 399 } 400 401 402 static uint_t 403 todbl_set_watchdog_timer(uint_t timeoutval) 404 { 405 /* 406 * We get started during kernel intilaisation only 407 * if watchdog_enable is set. 408 */ 409 ASSERT(MUTEX_HELD(&tod_lock)); 410 411 if (watchdog_available && (!watchdog_activated || 412 (watchdog_activated && (timeoutval != watchdog_timeout_seconds)))) { 413 watchdog_timeout_seconds = timeoutval; 414 if (configure_wdog(WDOG_ON)) 415 return (watchdog_timeout_seconds); 416 } 417 return (0); 418 } 419 420 static uint_t 421 todbl_clear_watchdog_timer(void) 422 { 423 /* 424 * The core kernel will call us here to disable the wdog when: 425 * 1. we're panicing 426 * 2. we're entering debug 427 * 3. we're rebooting 428 */ 429 ASSERT(MUTEX_HELD(&tod_lock)); 430 431 if (watchdog_available && watchdog_activated) { 432 watchdog_enable = 0; 433 if (!configure_wdog(WDOG_OFF)) 434 return (0); 435 } 436 return (watchdog_timeout_seconds); 437 } 438 439 static uint_t 440 configure_wdog(uint8_t new_state) 441 { 442 bscv_wdog_t wdog_cmd; 443 struct bscv_idi_info bscv_info; 444 445 if (new_state == WDOG_ON || new_state == WDOG_OFF) { 446 447 wdog_cmd.enable_wdog = new_state; 448 wdog_cmd.wdog_timeout_s = watchdog_timeout_seconds; 449 wdog_cmd.reset_system_on_timeout = wdog_reset_on_timeout; 450 bscv_info.type = BSCV_IDI_WDOG_CFG; 451 bscv_info.data = &wdog_cmd; 452 bscv_info.size = sizeof (wdog_cmd); 453 454 if (bsc_drv_func_ptr != NULL) { 455 watchdog_activated = new_state; 456 (*bsc_drv_func_ptr)(&bscv_info); 457 return (1); 458 } 459 } 460 return (0); 461 462 } 463