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 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 * 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 todm5819p_rmc_get(void) 138 { 139 int i; 140 int s; 141 timestruc_t ts; 142 struct rtc_t rtc; 143 144 ASSERT(MUTEX_HELD(&tod_lock)); 145 146 /* set the hw watchdog timer if it's been activated */ 147 if (watchdog_activated) { 148 int ret = 0; 149 ret = tod_ops.tod_set_watchdog_timer(0); 150 /* 151 * The empty set_watchdog routine returns a 0. So if a 152 * coded routine fails we will look for a -1 for a failure. 153 */ 154 if (ret == -1) 155 cmn_err(CE_WARN, "todm5819p: failed to set hardware " 156 "watchdog timer."); 157 } 158 159 /* 160 * Read current time from the tod. If the tod isn't accessible, wait and 161 * retry. 162 * Run critical in the time critical section to avoid being interrupted 163 */ 164 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) { 165 s = ddi_enter_critical(); 166 M5819P_ADDR_REG = RTC_A; 167 if (!(M5819P_DATA_REG & RTC_UIP)) { 168 read_rtc(&rtc); 169 ddi_exit_critical(s); 170 break; 171 } 172 ddi_exit_critical(s); 173 drv_usecwait(TODM5819_UIP_WAIT_USEC); 174 } 175 if (i == TODM5819_UIP_RETRY_THRESH) { 176 /* 177 * tod is inaccessible: just return current software time 178 */ 179 tod_status_set(TOD_GET_FAILED); 180 return (hrestime); 181 } 182 183 /* read was successful so ensure failure flag is clear */ 184 tod_status_clear(TOD_GET_FAILED); 185 186 ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc)); 187 ts.tv_nsec = 0; 188 return (ts); 189 } 190 191 static todinfo_t 192 rtc_to_tod(struct rtc_t *rtc) 193 { 194 todinfo_t tod; 195 196 /* 197 * tod_year is base 1900 so this code needs to adjust the true year 198 * retrieved from the rtc's century and year fields. 199 */ 200 tod.tod_year = rtc->rtc_year + (rtc->rtc_century * 100) - 1900; 201 tod.tod_month = rtc->rtc_mon; 202 tod.tod_day = rtc->rtc_dom; 203 tod.tod_dow = rtc->rtc_dow; 204 tod.tod_hour = rtc->rtc_hrs; 205 tod.tod_min = rtc->rtc_min; 206 tod.tod_sec = rtc->rtc_sec; 207 208 return (tod); 209 } 210 211 static void 212 read_rtc(struct rtc_t *rtc) 213 { 214 M5819P_ADDR_REG = RTC_SEC; 215 rtc->rtc_sec = M5819P_DATA_REG; 216 M5819P_ADDR_REG = RTC_ASEC; 217 rtc->rtc_asec = M5819P_DATA_REG; 218 M5819P_ADDR_REG = RTC_MIN; 219 rtc->rtc_min = M5819P_DATA_REG; 220 M5819P_ADDR_REG = RTC_AMIN; 221 rtc->rtc_amin = M5819P_DATA_REG; 222 M5819P_ADDR_REG = RTC_HRS; 223 rtc->rtc_hrs = M5819P_DATA_REG; 224 M5819P_ADDR_REG = RTC_AHRS; 225 rtc->rtc_ahrs = M5819P_DATA_REG; 226 M5819P_ADDR_REG = RTC_DOW; 227 rtc->rtc_dow = M5819P_DATA_REG; 228 M5819P_ADDR_REG = RTC_DOM; 229 rtc->rtc_dom = M5819P_DATA_REG; 230 M5819P_ADDR_REG = RTC_MON; 231 rtc->rtc_mon = M5819P_DATA_REG; 232 M5819P_ADDR_REG = RTC_YEAR; 233 rtc->rtc_year = M5819P_DATA_REG; 234 M5819P_ADDR_REG = RTC_CENTURY; 235 rtc->rtc_century = M5819P_DATA_REG; 236 237 /* Read date alarm */ 238 M5819P_ADDR_REG = RTC_ADOM_REG; 239 rtc->rtc_adom = (M5819P_DATA_REG) & RTC_ADOM; 240 } 241 242 /* 243 * Write the specified time into the clock chip. 244 * Must be called with tod_lock held. 245 */ 246 static void 247 todm5819p_rmc_set(timestruc_t ts) 248 { 249 struct rtc_t rtc; 250 todinfo_t tod = utc_to_tod(ts.tv_sec); 251 int year; 252 rmc_comm_msg_t request; 253 dp_set_date_time_t set_time_msg; 254 255 ASSERT(MUTEX_HELD(&tod_lock)); 256 257 /* tod_year is base 1900 so this code needs to adjust */ 258 year = 1900 + tod.tod_year; 259 rtc.rtc_year = year % 100; 260 rtc.rtc_century = year / 100; 261 rtc.rtc_mon = (uint8_t)tod.tod_month; 262 rtc.rtc_dom = (uint8_t)tod.tod_day; 263 rtc.rtc_dow = (uint8_t)tod.tod_dow; 264 rtc.rtc_hrs = (uint8_t)tod.tod_hour; 265 rtc.rtc_min = (uint8_t)tod.tod_min; 266 rtc.rtc_sec = (uint8_t)tod.tod_sec; 267 268 write_rtc_time(&rtc); 269 270 set_time_msg.year = year - 1900; 271 set_time_msg.month = tod.tod_month - 1; 272 set_time_msg.day = tod.tod_day; 273 set_time_msg.hour = tod.tod_hour; 274 set_time_msg.minute = tod.tod_min; 275 set_time_msg.second = tod.tod_sec; 276 277 request.msg_type = DP_SET_DATE_TIME; 278 request.msg_len = sizeof (set_time_msg); 279 request.msg_buf = (caddr_t)&set_time_msg; 280 281 (void) rmc_comm_request_nowait(&request, 0); 282 } 283 284 void 285 write_rtc_time(struct rtc_t *rtc) 286 { 287 uint8_t regb; 288 int i; 289 290 /* 291 * Freeze 292 */ 293 M5819P_ADDR_REG = RTC_B; 294 regb = M5819P_DATA_REG; 295 M5819P_DATA_REG = (regb | RTC_SET); 296 297 /* 298 * If an update cycle is in progress wait for the UIP flag to 299 * clear. If we write whilst UIP is still set there is a slight 300 * but real possibility of corrupting the RTC date and time 301 * registers. 302 * 303 * The expected wait is one internal cycle of the chip. We could 304 * simply spin but this may hang a CPU if we were to have a broken 305 * RTC chip where UIP is stuck, so we use a retry loop instead. 306 * No critical section is needed here as the UIP flag will not be 307 * re-asserted until we clear RTC_SET. 308 */ 309 M5819P_ADDR_REG = RTC_A; 310 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) { 311 if (!(M5819P_DATA_REG & RTC_UIP)) { 312 break; 313 } 314 drv_usecwait(TODM5819_UIP_WAIT_USEC); 315 } 316 if (i < TODM5819_UIP_RETRY_THRESH) { 317 M5819P_ADDR_REG = RTC_SEC; 318 M5819P_DATA_REG = rtc->rtc_sec; 319 M5819P_ADDR_REG = RTC_MIN; 320 M5819P_DATA_REG = rtc->rtc_min; 321 M5819P_ADDR_REG = RTC_HRS; 322 M5819P_DATA_REG = rtc->rtc_hrs; 323 M5819P_ADDR_REG = RTC_DOW; 324 M5819P_DATA_REG = rtc->rtc_dow; 325 M5819P_ADDR_REG = RTC_DOM; 326 M5819P_DATA_REG = rtc->rtc_dom; 327 M5819P_ADDR_REG = RTC_MON; 328 M5819P_DATA_REG = rtc->rtc_mon; 329 M5819P_ADDR_REG = RTC_YEAR; 330 M5819P_DATA_REG = rtc->rtc_year; 331 M5819P_ADDR_REG = RTC_CENTURY; 332 M5819P_DATA_REG = rtc->rtc_century; 333 } else { 334 cmn_err(CE_WARN, "todm5819p_rmc: Could not write the RTC\n"); 335 } 336 337 /* 338 * Unfreeze 339 */ 340 M5819P_ADDR_REG = RTC_B; 341 M5819P_DATA_REG = regb; 342 } 343 344 void 345 write_rtc_alarm(struct rtc_t *rtc) 346 { 347 M5819P_ADDR_REG = RTC_ASEC; 348 M5819P_DATA_REG = rtc->rtc_asec; 349 M5819P_ADDR_REG = RTC_AMIN; 350 M5819P_DATA_REG = rtc->rtc_amin; 351 M5819P_ADDR_REG = RTC_AHRS; 352 M5819P_DATA_REG = rtc->rtc_ahrs; 353 354 M5819P_ADDR_REG = RTC_ADOM_REG; 355 M5819P_DATA_REG = rtc->rtc_adom; 356 } 357 358 /* 359 * program the rtc registers for alarm to go off at the specified time 360 */ 361 static void 362 todm5819p_rmc_set_power_alarm(timestruc_t ts) 363 { 364 todinfo_t tod; 365 uint8_t regb; 366 struct rtc_t rtc; 367 368 ASSERT(MUTEX_HELD(&tod_lock)); 369 tod = utc_to_tod(ts.tv_sec); 370 371 /* 372 * disable alarms and clear AF flag by reading reg C 373 */ 374 M5819P_ADDR_REG = RTC_B; 375 regb = M5819P_DATA_REG; 376 M5819P_DATA_REG = regb & ~RTC_AIE; 377 M5819P_ADDR_REG = RTC_C; 378 (void) M5819P_DATA_REG; 379 380 rtc.rtc_asec = (uint8_t)tod.tod_sec; 381 rtc.rtc_amin = (uint8_t)tod.tod_min; 382 rtc.rtc_ahrs = (uint8_t)tod.tod_hour; 383 rtc.rtc_adom = (uint8_t)tod.tod_day; 384 385 /* 386 * Write alarm values and enable alarm 387 */ 388 write_rtc_alarm(&rtc); 389 390 M5819P_ADDR_REG = RTC_B; 391 M5819P_DATA_REG = regb | RTC_AIE; 392 } 393 394 /* 395 * clear alarm interrupt 396 */ 397 static void 398 todm5819p_rmc_clear_power_alarm(void) 399 { 400 uint8_t regb; 401 402 ASSERT(MUTEX_HELD(&tod_lock)); 403 404 M5819P_ADDR_REG = RTC_B; 405 regb = M5819P_DATA_REG; 406 M5819P_DATA_REG = regb & ~RTC_AIE; 407 } 408 409 /* 410 * Determine the cpu frequency by watching the TOD chip rollover twice. 411 * Cpu clock rate is determined by computing the ticks added (in tick register) 412 * during one second interval on TOD. 413 */ 414 uint64_t 415 todm5819p_rmc_get_cpufrequency(void) 416 { 417 ASSERT(MUTEX_HELD(&tod_lock)); 418 M5819P_ADDR_REG = RTC_SEC; 419 return (find_cpufrequency(v_rtc_data_reg)); 420 } 421 422 /*ARGSUSED*/ 423 static uint_t 424 todm5819p_rmc_set_watchdog_timer(uint_t timeoutval) 425 { 426 ASSERT(MUTEX_HELD(&tod_lock)); 427 return (0); 428 } 429 430 static uint_t 431 todm5819p_rmc_clear_watchdog_timer(void) 432 { 433 ASSERT(MUTEX_HELD(&tod_lock)); 434 return (0); 435 } 436