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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 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/todm5819.h> 40 #include <sys/modctl.h> 41 #include <sys/stat.h> 42 #include <sys/clock.h> 43 #include <sys/reboot.h> 44 #include <sys/machsystm.h> 45 #include <sys/poll.h> 46 #include <sys/pbio.h> 47 48 static timestruc_t todm5819_get(void); 49 static void todm5819_set(timestruc_t); 50 static uint_t todm5819_set_watchdog_timer(uint_t); 51 static uint_t todm5819_clear_watchdog_timer(void); 52 static void todm5819_set_power_alarm(timestruc_t); 53 static void todm5819_clear_power_alarm(void); 54 static uint64_t todm5819_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 m5819_debug_flags; 69 70 static todinfo_t rtc_to_tod(struct rtc_t *); 71 static uint_t read_rtc(struct rtc_t *); 72 static void write_rtc_time(struct rtc_t *); 73 static void write_rtc_alarm(struct rtc_t *); 74 75 76 static struct modlmisc modlmisc = { 77 &mod_miscops, "tod module for ALI M5819", 78 }; 79 80 static struct modlinkage modlinkage = { 81 MODREV_1, &modlmisc, NULL 82 }; 83 84 85 int 86 _init(void) 87 { 88 if (strcmp(tod_module_name, "todm5819") == 0 || 89 strcmp(tod_module_name, "m5819") == 0) { 90 RTC_PUT8(RTC_B, (RTC_DM | RTC_HM)); 91 92 tod_ops.tod_get = todm5819_get; 93 tod_ops.tod_set = todm5819_set; 94 tod_ops.tod_set_watchdog_timer = 95 todm5819_set_watchdog_timer; 96 tod_ops.tod_clear_watchdog_timer = 97 todm5819_clear_watchdog_timer; 98 tod_ops.tod_set_power_alarm = todm5819_set_power_alarm; 99 tod_ops.tod_clear_power_alarm = todm5819_clear_power_alarm; 100 tod_ops.tod_get_cpufrequency = todm5819_get_cpufrequency; 101 102 /* 103 * check if hardware watchdog timer is available and user 104 * enabled it. 105 */ 106 if (watchdog_enable) { 107 if (!watchdog_available) { 108 cmn_err(CE_WARN, "m5819: Hardware watchdog " 109 "unavailable"); 110 } else if (boothowto & RB_DEBUG) { 111 cmn_err(CE_WARN, "m5819: Hardware watchdog " 112 "disabled [debugger]"); 113 } 114 } 115 } 116 117 return (mod_install(&modlinkage)); 118 } 119 120 int 121 _fini(void) 122 { 123 if (strcmp(tod_module_name, "m5819") == 0 || 124 strcmp(tod_module_name, "todm5819") == 0) { 125 return (EBUSY); 126 } else { 127 return (mod_remove(&modlinkage)); 128 } 129 } 130 131 /* 132 * The loadable-module _info(9E) entry point 133 */ 134 int 135 _info(struct modinfo *modinfop) 136 { 137 return (mod_info(&modlinkage, modinfop)); 138 } 139 140 141 /* 142 * Read the current time from the clock chip and convert to UNIX form. 143 * Assumes that the year in the clock chip is valid. 144 * Must be called with tod_lock held. 145 */ 146 static timestruc_t 147 todm5819_get(void) 148 { 149 int i; 150 timestruc_t ts; 151 struct rtc_t rtc; 152 153 ASSERT(MUTEX_HELD(&tod_lock)); 154 155 /* 156 * Read from the tod, and if it isnt accessible wait 157 * before retrying. 158 */ 159 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) { 160 if (read_rtc(&rtc)) 161 break; 162 drv_usecwait(TODM5819_UIP_WAIT_USEC); 163 } 164 if (i == TODM5819_UIP_RETRY_THRESH) { 165 /* 166 * We couldnt read from the tod 167 */ 168 tod_fault_reset(); 169 return (hrestime); 170 } 171 172 DPRINTF("todm5819_get: century=%d year=%d dom=%d hrs=%d\n", 173 rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs); 174 175 ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc)); 176 ts.tv_nsec = 0; 177 return (ts); 178 } 179 180 static todinfo_t 181 rtc_to_tod(struct rtc_t *rtc) 182 { 183 todinfo_t tod; 184 185 /* 186 * tod_year is base 1900 so this code needs to adjust the true 187 * year retrieved from the rtc's century and year fields. 188 */ 189 tod.tod_year = rtc->rtc_year + (rtc->rtc_century * 100) - 1900; 190 tod.tod_month = rtc->rtc_mon; 191 tod.tod_day = rtc->rtc_dom; 192 tod.tod_dow = rtc->rtc_dow; 193 tod.tod_hour = rtc->rtc_hrs; 194 tod.tod_min = rtc->rtc_min; 195 tod.tod_sec = rtc->rtc_sec; 196 197 return (tod); 198 } 199 200 uint_t 201 read_rtc(struct rtc_t *rtc) 202 { 203 int s; 204 uint_t rtc_readable = 0; 205 206 s = splhi(); 207 /* 208 * If UIP bit is not set we have at least 274us 209 * to read the values. Otherwise we have up to 210 * 336us to wait before we can read it 211 */ 212 if (!(RTC_GET8(RTC_A) & RTC_UIP)) { 213 rtc_readable = 1; 214 215 rtc->rtc_sec = RTC_GET8(RTC_SEC); 216 rtc->rtc_asec = RTC_GET8(RTC_ASEC); 217 rtc->rtc_min = RTC_GET8(RTC_MIN); 218 rtc->rtc_amin = RTC_GET8(RTC_AMIN); 219 220 rtc->rtc_hrs = RTC_GET8(RTC_HRS); 221 rtc->rtc_ahrs = RTC_GET8(RTC_AHRS); 222 rtc->rtc_dow = RTC_GET8(RTC_DOW); 223 rtc->rtc_dom = RTC_GET8(RTC_DOM); 224 rtc->rtc_adom = RTC_GET8(RTC_D) & 0x3f; 225 226 rtc->rtc_mon = RTC_GET8(RTC_MON); 227 rtc->rtc_year = RTC_GET8(RTC_YEAR); 228 rtc->rtc_century = RTC_GET8(RTC_CENTURY); 229 rtc->rtc_amon = 0; 230 231 /* Clear wakeup data */ 232 rtc->apc_wdwr = 0; 233 rtc->apc_wdmr = 0; 234 rtc->apc_wmr = 0; 235 rtc->apc_wyr = 0; 236 rtc->apc_wcr = 0; 237 } 238 splx(s); 239 return (rtc_readable); 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 todm5819_set(timestruc_t ts) 248 { 249 struct rtc_t rtc; 250 todinfo_t tod = utc_to_tod(ts.tv_sec); 251 int year; 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 DPRINTF("todm5819_set: century=%d year=%d dom=%d hrs=%d\n", 266 rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs); 267 268 write_rtc_time(&rtc); 269 } 270 271 void 272 write_rtc_time(struct rtc_t *rtc) 273 { 274 uint8_t regb; 275 276 /* 277 * Freeze 278 */ 279 regb = RTC_GET8(RTC_B); 280 RTC_PUT8(RTC_B, (regb | RTC_SET)); 281 282 RTC_PUT8(RTC_SEC, (rtc->rtc_sec)); 283 RTC_PUT8(RTC_ASEC, (rtc->rtc_asec)); 284 RTC_PUT8(RTC_MIN, (rtc->rtc_min)); 285 RTC_PUT8(RTC_AMIN, (rtc->rtc_amin)); 286 287 RTC_PUT8(RTC_HRS, (rtc->rtc_hrs)); 288 RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs)); 289 RTC_PUT8(RTC_DOW, (rtc->rtc_dow)); 290 RTC_PUT8(RTC_DOM, (rtc->rtc_dom)); 291 292 RTC_PUT8(RTC_MON, (rtc->rtc_mon)); 293 RTC_PUT8(RTC_YEAR, (rtc->rtc_year)); 294 RTC_PUT8(RTC_CENTURY, (rtc->rtc_century)); 295 296 /* 297 * Unfreeze 298 */ 299 RTC_PUT8(RTC_B, regb); 300 } 301 302 303 void 304 write_rtc_alarm(struct rtc_t *rtc) 305 { 306 RTC_PUT8(RTC_ASEC, (rtc->rtc_asec)); 307 RTC_PUT8(RTC_AMIN, (rtc->rtc_amin)); 308 RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs)); 309 RTC_PUT8(RTC_D, (rtc->rtc_adom)); 310 } 311 312 /* 313 * program the rtc registers for alarm to go off at the specified time 314 */ 315 static void 316 todm5819_set_power_alarm(timestruc_t ts) 317 { 318 todinfo_t tod; 319 uint8_t regb; 320 struct rtc_t rtc; 321 322 ASSERT(MUTEX_HELD(&tod_lock)); 323 tod = utc_to_tod(ts.tv_sec); 324 325 /* 326 * disable alarms 327 */ 328 regb = RTC_GET8(RTC_B); 329 RTC_PUT8(RTC_B, (regb & ~RTC_AIE)); 330 331 332 rtc.rtc_asec = (uint8_t)tod.tod_sec; 333 rtc.rtc_amin = (uint8_t)tod.tod_min; 334 rtc.rtc_ahrs = (uint8_t)tod.tod_hour; 335 rtc.rtc_adom = (uint8_t)tod.tod_day; 336 337 write_rtc_alarm(&rtc); 338 /* 339 * Enable alarm. 340 */ 341 RTC_PUT8(RTC_B, (regb | RTC_AIE)); 342 } 343 344 /* 345 * clear alarm interrupt 346 */ 347 static void 348 todm5819_clear_power_alarm(void) 349 { 350 uint8_t regb; 351 ASSERT(MUTEX_HELD(&tod_lock)); 352 353 regb = RTC_GET8(RTC_B); 354 RTC_PUT8(RTC_B, (regb & ~RTC_AIE)); 355 } 356 357 /* 358 * Determine the cpu frequency by watching the TOD chip rollover twice. 359 * Cpu clock rate is determined by computing the ticks added (in tick register) 360 * during one second interval on TOD. 361 */ 362 uint64_t 363 todm5819_get_cpufrequency(void) 364 { 365 ASSERT(MUTEX_HELD(&tod_lock)); 366 M5819_ADDR_REG = RTC_SEC; 367 return (find_cpufrequency(v_rtc_data_reg)); 368 } 369 370 371 /*ARGSUSED*/ 372 static uint_t 373 todm5819_set_watchdog_timer(uint_t timeoutval) 374 { 375 ASSERT(MUTEX_HELD(&tod_lock)); 376 return (0); 377 } 378 379 static uint_t 380 todm5819_clear_watchdog_timer(void) 381 { 382 ASSERT(MUTEX_HELD(&tod_lock)); 383 return (0); 384 } 385