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