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 /* 28 * tod driver module for ALI M5819P part 29 */ 30 31 #include <sys/types.h> 32 #include <sys/conf.h> 33 #include <sys/kmem.h> 34 #include <sys/open.h> 35 #include <sys/ddi.h> 36 #include <sys/sunddi.h> 37 38 #include <sys/todm5819p.h> 39 #include <sys/rmc_comm_dp.h> 40 #include <sys/rmc_comm_drvintf.h> 41 #include <sys/modctl.h> 42 #include <sys/stat.h> 43 #include <sys/clock.h> 44 #include <sys/reboot.h> 45 #include <sys/machsystm.h> 46 47 static timestruc_t todm5819p_rmc_get(void); 48 static void todm5819p_rmc_set(timestruc_t); 49 static uint_t todm5819p_rmc_set_watchdog_timer(uint_t); 50 static uint_t todm5819p_rmc_clear_watchdog_timer(void); 51 static void todm5819p_rmc_set_power_alarm(timestruc_t); 52 static void todm5819p_rmc_clear_power_alarm(void); 53 static uint64_t todm5819p_rmc_get_cpufrequency(void); 54 55 extern uint64_t find_cpufrequency(volatile uint8_t *); 56 57 /* 58 * External variables 59 */ 60 extern int watchdog_enable; 61 extern int watchdog_available; 62 extern int boothowto; 63 64 /* 65 * Global variables 66 */ 67 int m5819p_debug_flags; 68 69 /* 70 * Module linkage information for the kernel. 71 */ 72 static struct modlmisc modlmisc = { 73 &mod_miscops, "tod module for ALI M5819P" 74 }; 75 76 static struct modlinkage modlinkage = { 77 MODREV_1, (void *)&modlmisc, NULL 78 }; 79 80 static todinfo_t rtc_to_tod(struct rtc_t *); 81 static void read_rtc(struct rtc_t *); 82 static void write_rtc_time(struct rtc_t *); 83 static void write_rtc_alarm(struct rtc_t *); 84 85 86 int 87 _init(void) 88 { 89 if (strcmp(tod_module_name, "todm5819p_rmc") == 0) { 90 M5819P_ADDR_REG = RTC_B; 91 M5819P_DATA_REG = (RTC_DM | RTC_HM); 92 93 tod_ops.tod_get = todm5819p_rmc_get; 94 tod_ops.tod_set = todm5819p_rmc_set; 95 96 tod_ops.tod_set_watchdog_timer = 97 todm5819p_rmc_set_watchdog_timer; 98 tod_ops.tod_clear_watchdog_timer = 99 todm5819p_rmc_clear_watchdog_timer; 100 tod_ops.tod_set_power_alarm = todm5819p_rmc_set_power_alarm; 101 tod_ops.tod_clear_power_alarm = todm5819p_rmc_clear_power_alarm; 102 tod_ops.tod_get_cpufrequency = todm5819p_rmc_get_cpufrequency; 103 if (boothowto & RB_DEBUG) { 104 cmn_err(CE_WARN, "todm5819p_rmc: kernel debugger " 105 "detected: hardware watchdog disabled"); 106 } 107 } 108 109 return (mod_install(&modlinkage)); 110 } 111 112 int 113 _fini(void) 114 { 115 if (strcmp(tod_module_name, "todm5819p_rmc") == 0) 116 return (EBUSY); 117 118 return (mod_remove(&modlinkage)); 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 * todm5819p_rmc is normally called once a second, from the clock thread. 133 * It may also be infrequently called from other contexts (eg. ddi framework), 134 * in which case our counting to NBAD_READ_LIMIT may be a few seconds short 135 * of the desired 15-minute timeframe; this slight inaccuracy is acceptable. 136 */ 137 #define NBAD_READ_LIMIT (900) /* 15 minutes, in seconds */ 138 /* 139 * Read the current time from the clock chip and convert to UNIX form. 140 * Checks the century, but otherwise assumes that the values in the clock 141 * chip are valid. 142 * Must be called with tod_lock held. 143 */ 144 static timestruc_t 145 todm5819p_rmc_get(void) 146 { 147 int i; 148 int s; 149 timestruc_t ts; 150 struct rtc_t rtc; 151 static int nbad_reads = 0; 152 153 ASSERT(MUTEX_HELD(&tod_lock)); 154 155 /* set the hw watchdog timer if it's been activated */ 156 if (watchdog_activated) { 157 int ret = 0; 158 ret = tod_ops.tod_set_watchdog_timer(0); 159 /* 160 * The empty set_watchdog routine returns a 0. So if a 161 * coded routine fails we will look for a -1 for a failure. 162 */ 163 if (ret == -1) 164 cmn_err(CE_WARN, "todm5819p: failed to set hardware " 165 "watchdog timer."); 166 } 167 168 /* 169 * Read current time from the tod. If the tod isn't accessible, wait and 170 * retry. 171 * Run critical in the time critical section to avoid being interrupted 172 */ 173 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) { 174 s = ddi_enter_critical(); 175 M5819P_ADDR_REG = RTC_A; 176 if (!(M5819P_DATA_REG & RTC_UIP)) { 177 read_rtc(&rtc); 178 ddi_exit_critical(s); 179 break; 180 } 181 ddi_exit_critical(s); 182 drv_usecwait(TODM5819_UIP_WAIT_USEC); 183 } 184 if (i == TODM5819_UIP_RETRY_THRESH) { 185 /* 186 * tod is inaccessible: just return current software time 187 */ 188 tod_fault_reset(); 189 return (hrestime); 190 } 191 192 DPRINTF("todm5819p_rmc_get: century=%d year=%d dom=%d hrs=%d\n", 193 (int)rtc.rtc_century, (int)rtc.rtc_year, (int)rtc.rtc_dom, 194 (int)rtc.rtc_hrs); 195 196 /* detect and correct invalid century register data */ 197 if (rtc.rtc_century < 19) { 198 DPRINTF( 199 "todm5819p_rmc_get: century invalid (%d), returning 20\n", 200 (int)rtc.rtc_century); 201 rtc.rtc_century = 20; 202 if (++nbad_reads == NBAD_READ_LIMIT) { 203 nbad_reads = 0; 204 cmn_err(CE_WARN, "todm5819p: realtime clock century " 205 "register appears to be defective."); 206 } 207 } 208 209 ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc)); 210 ts.tv_nsec = 0; 211 return (ts); 212 } 213 214 static todinfo_t 215 rtc_to_tod(struct rtc_t *rtc) 216 { 217 todinfo_t tod; 218 219 /* 220 * tod_year is base 1900 so this code needs to adjust the true year 221 * retrieved from the rtc's century and year fields. 222 */ 223 tod.tod_year = rtc->rtc_year + (rtc->rtc_century * 100) - 1900; 224 tod.tod_month = rtc->rtc_mon; 225 tod.tod_day = rtc->rtc_dom; 226 tod.tod_dow = rtc->rtc_dow; 227 tod.tod_hour = rtc->rtc_hrs; 228 tod.tod_min = rtc->rtc_min; 229 tod.tod_sec = rtc->rtc_sec; 230 231 return (tod); 232 } 233 234 static void 235 read_rtc(struct rtc_t *rtc) 236 { 237 M5819P_ADDR_REG = RTC_SEC; 238 rtc->rtc_sec = M5819P_DATA_REG; 239 M5819P_ADDR_REG = RTC_ASEC; 240 rtc->rtc_asec = M5819P_DATA_REG; 241 M5819P_ADDR_REG = RTC_MIN; 242 rtc->rtc_min = M5819P_DATA_REG; 243 M5819P_ADDR_REG = RTC_AMIN; 244 rtc->rtc_amin = M5819P_DATA_REG; 245 M5819P_ADDR_REG = RTC_HRS; 246 rtc->rtc_hrs = M5819P_DATA_REG; 247 M5819P_ADDR_REG = RTC_AHRS; 248 rtc->rtc_ahrs = M5819P_DATA_REG; 249 M5819P_ADDR_REG = RTC_DOW; 250 rtc->rtc_dow = M5819P_DATA_REG; 251 M5819P_ADDR_REG = RTC_DOM; 252 rtc->rtc_dom = M5819P_DATA_REG; 253 M5819P_ADDR_REG = RTC_MON; 254 rtc->rtc_mon = M5819P_DATA_REG; 255 M5819P_ADDR_REG = RTC_YEAR; 256 rtc->rtc_year = M5819P_DATA_REG; 257 M5819P_ADDR_REG = RTC_CENTURY; 258 rtc->rtc_century = M5819P_DATA_REG; 259 260 /* Read date alarm */ 261 M5819P_ADDR_REG = RTC_ADOM_REG; 262 rtc->rtc_adom = (M5819P_DATA_REG) & RTC_ADOM; 263 } 264 265 /* 266 * Write the specified time into the clock chip. 267 * Must be called with tod_lock held. 268 */ 269 static void 270 todm5819p_rmc_set(timestruc_t ts) 271 { 272 struct rtc_t rtc; 273 todinfo_t tod = utc_to_tod(ts.tv_sec); 274 int year; 275 rmc_comm_msg_t request; 276 dp_set_date_time_t set_time_msg; 277 278 ASSERT(MUTEX_HELD(&tod_lock)); 279 280 /* tod_year is base 1900 so this code needs to adjust */ 281 year = 1900 + tod.tod_year; 282 rtc.rtc_year = year % 100; 283 rtc.rtc_century = year / 100; 284 rtc.rtc_mon = (uint8_t)tod.tod_month; 285 rtc.rtc_dom = (uint8_t)tod.tod_day; 286 rtc.rtc_dow = (uint8_t)tod.tod_dow; 287 rtc.rtc_hrs = (uint8_t)tod.tod_hour; 288 rtc.rtc_min = (uint8_t)tod.tod_min; 289 rtc.rtc_sec = (uint8_t)tod.tod_sec; 290 291 DPRINTF("todm5819p_rmc_set: century=%d year=%d dom=%d hrs=%d\n", 292 (int)rtc.rtc_century, (int)rtc.rtc_year, (int)rtc.rtc_dom, 293 (int)rtc.rtc_hrs); 294 295 write_rtc_time(&rtc); 296 297 set_time_msg.year = year - 1900; 298 set_time_msg.month = tod.tod_month - 1; 299 set_time_msg.day = tod.tod_day; 300 set_time_msg.hour = tod.tod_hour; 301 set_time_msg.minute = tod.tod_min; 302 set_time_msg.second = tod.tod_sec; 303 304 request.msg_type = DP_SET_DATE_TIME; 305 request.msg_len = sizeof (set_time_msg); 306 request.msg_buf = (caddr_t)&set_time_msg; 307 308 (void) rmc_comm_request_nowait(&request, 0); 309 } 310 311 void 312 write_rtc_time(struct rtc_t *rtc) 313 { 314 uint8_t regb; 315 316 /* 317 * Freeze 318 */ 319 M5819P_ADDR_REG = RTC_B; 320 regb = M5819P_DATA_REG; 321 M5819P_DATA_REG = (regb | RTC_SET); 322 323 M5819P_ADDR_REG = RTC_SEC; 324 M5819P_DATA_REG = rtc->rtc_sec; 325 M5819P_ADDR_REG = RTC_MIN; 326 M5819P_DATA_REG = rtc->rtc_min; 327 M5819P_ADDR_REG = RTC_HRS; 328 M5819P_DATA_REG = rtc->rtc_hrs; 329 M5819P_ADDR_REG = RTC_DOW; 330 M5819P_DATA_REG = rtc->rtc_dow; 331 M5819P_ADDR_REG = RTC_DOM; 332 M5819P_DATA_REG = rtc->rtc_dom; 333 M5819P_ADDR_REG = RTC_MON; 334 M5819P_DATA_REG = rtc->rtc_mon; 335 M5819P_ADDR_REG = RTC_YEAR; 336 M5819P_DATA_REG = rtc->rtc_year; 337 M5819P_ADDR_REG = RTC_CENTURY; 338 M5819P_DATA_REG = rtc->rtc_century; 339 340 /* 341 * Unfreeze 342 */ 343 M5819P_ADDR_REG = RTC_B; 344 M5819P_DATA_REG = regb; 345 } 346 347 void 348 write_rtc_alarm(struct rtc_t *rtc) 349 { 350 M5819P_ADDR_REG = RTC_ASEC; 351 M5819P_DATA_REG = rtc->rtc_asec; 352 M5819P_ADDR_REG = RTC_AMIN; 353 M5819P_DATA_REG = rtc->rtc_amin; 354 M5819P_ADDR_REG = RTC_AHRS; 355 M5819P_DATA_REG = rtc->rtc_ahrs; 356 357 M5819P_ADDR_REG = RTC_ADOM_REG; 358 M5819P_DATA_REG = rtc->rtc_adom; 359 } 360 361 /* 362 * program the rtc registers for alarm to go off at the specified time 363 */ 364 static void 365 todm5819p_rmc_set_power_alarm(timestruc_t ts) 366 { 367 todinfo_t tod; 368 uint8_t regb; 369 struct rtc_t rtc; 370 371 ASSERT(MUTEX_HELD(&tod_lock)); 372 tod = utc_to_tod(ts.tv_sec); 373 374 /* 375 * disable alarms and clear AF flag by reading reg C 376 */ 377 M5819P_ADDR_REG = RTC_B; 378 regb = M5819P_DATA_REG; 379 M5819P_DATA_REG = regb & ~RTC_AIE; 380 M5819P_ADDR_REG = RTC_C; 381 (void) M5819P_DATA_REG; 382 383 rtc.rtc_asec = (uint8_t)tod.tod_sec; 384 rtc.rtc_amin = (uint8_t)tod.tod_min; 385 rtc.rtc_ahrs = (uint8_t)tod.tod_hour; 386 rtc.rtc_adom = (uint8_t)tod.tod_day; 387 388 /* 389 * Write alarm values and enable alarm 390 */ 391 write_rtc_alarm(&rtc); 392 393 M5819P_ADDR_REG = RTC_B; 394 M5819P_DATA_REG = regb | RTC_AIE; 395 } 396 397 /* 398 * clear alarm interrupt 399 */ 400 static void 401 todm5819p_rmc_clear_power_alarm(void) 402 { 403 uint8_t regb; 404 405 ASSERT(MUTEX_HELD(&tod_lock)); 406 407 M5819P_ADDR_REG = RTC_B; 408 regb = M5819P_DATA_REG; 409 M5819P_DATA_REG = regb & ~RTC_AIE; 410 } 411 412 /* 413 * Determine the cpu frequency by watching the TOD chip rollover twice. 414 * Cpu clock rate is determined by computing the ticks added (in tick register) 415 * during one second interval on TOD. 416 */ 417 uint64_t 418 todm5819p_rmc_get_cpufrequency(void) 419 { 420 ASSERT(MUTEX_HELD(&tod_lock)); 421 M5819P_ADDR_REG = RTC_SEC; 422 return (find_cpufrequency(v_rtc_data_reg)); 423 } 424 425 /*ARGSUSED*/ 426 static uint_t 427 todm5819p_rmc_set_watchdog_timer(uint_t timeoutval) 428 { 429 ASSERT(MUTEX_HELD(&tod_lock)); 430 return (0); 431 } 432 433 static uint_t 434 todm5819p_rmc_clear_watchdog_timer(void) 435 { 436 ASSERT(MUTEX_HELD(&tod_lock)); 437 return (0); 438 } 439