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 42 static timestruc_t todm5819_get(void); 43 static void todm5819_set(timestruc_t); 44 static uint_t todm5819_set_watchdog_timer(uint_t); 45 static uint_t todm5819_clear_watchdog_timer(void); 46 static void todm5819_set_power_alarm(timestruc_t); 47 static void todm5819_clear_power_alarm(void); 48 static uint64_t todm5819_get_cpufrequency(void); 49 50 extern uint64_t find_cpufrequency(volatile uint8_t *); 51 52 /* 53 * External variables 54 */ 55 extern int watchdog_enable; 56 extern int watchdog_available; 57 extern int boothowto; 58 59 /* 60 * Global variables 61 */ 62 int m5819_debug_flags; 63 64 static todinfo_t rtc_to_tod(struct rtc_t *); 65 static uint_t read_rtc(struct rtc_t *); 66 static void write_rtc_time(struct rtc_t *); 67 static void write_rtc_alarm(struct rtc_t *); 68 69 70 static struct modlmisc modlmisc = { 71 &mod_miscops, "tod module for ALI M5819", 72 }; 73 74 static struct modlinkage modlinkage = { 75 MODREV_1, &modlmisc, NULL 76 }; 77 78 79 int 80 _init(void) 81 { 82 if (strcmp(tod_module_name, "todm5819") == 0 || 83 strcmp(tod_module_name, "m5819") == 0) { 84 RTC_PUT8(RTC_B, (RTC_DM | RTC_HM)); 85 86 tod_ops.tod_get = todm5819_get; 87 tod_ops.tod_set = todm5819_set; 88 tod_ops.tod_set_watchdog_timer = todm5819_set_watchdog_timer; 89 tod_ops.tod_clear_watchdog_timer = 90 todm5819_clear_watchdog_timer; 91 tod_ops.tod_set_power_alarm = todm5819_set_power_alarm; 92 tod_ops.tod_clear_power_alarm = todm5819_clear_power_alarm; 93 tod_ops.tod_get_cpufrequency = todm5819_get_cpufrequency; 94 95 /* 96 * check if hardware watchdog timer is available and user 97 * enabled it. 98 */ 99 if (watchdog_enable) { 100 if (!watchdog_available) { 101 cmn_err(CE_WARN, "m5819: Hardware watchdog " 102 "unavailable"); 103 } else if (boothowto & RB_DEBUG) { 104 cmn_err(CE_WARN, "m5819: Hardware watchdog " 105 "disabled [debugger]"); 106 } 107 } 108 } 109 110 return (mod_install(&modlinkage)); 111 } 112 113 int 114 _fini(void) 115 { 116 if (strcmp(tod_module_name, "m5819") == 0 || 117 strcmp(tod_module_name, "todm5819") == 0) { 118 return (EBUSY); 119 } else { 120 return (mod_remove(&modlinkage)); 121 } 122 } 123 124 /* 125 * The loadable-module _info(9E) entry point 126 */ 127 int 128 _info(struct modinfo *modinfop) 129 { 130 return (mod_info(&modlinkage, modinfop)); 131 } 132 133 134 /* 135 * Read the current time from the clock chip and convert to UNIX form. 136 * Assumes that the year in the clock chip is valid. 137 * Must be called with tod_lock held. 138 */ 139 static timestruc_t 140 todm5819_get(void) 141 { 142 int i; 143 timestruc_t ts; 144 struct rtc_t rtc; 145 146 ASSERT(MUTEX_HELD(&tod_lock)); 147 148 /* 149 * Read from the tod, and if it isnt accessible wait 150 * before retrying. 151 */ 152 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) { 153 if (read_rtc(&rtc)) 154 break; 155 drv_usecwait(TODM5819_UIP_WAIT_USEC); 156 } 157 if (i == TODM5819_UIP_RETRY_THRESH) { 158 /* 159 * We couldn't read from the TOD. 160 */ 161 tod_status_set(TOD_GET_FAILED); 162 return (hrestime); 163 } 164 165 DPRINTF("todm5819_get: century=%d year=%d dom=%d hrs=%d\n", 166 rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs); 167 168 /* read was successful so ensure failure flag is clear */ 169 tod_status_clear(TOD_GET_FAILED); 170 171 ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc)); 172 ts.tv_nsec = 0; 173 return (ts); 174 } 175 176 static todinfo_t 177 rtc_to_tod(struct rtc_t *rtc) 178 { 179 todinfo_t tod; 180 181 /* 182 * tod_year is base 1900 so this code needs to adjust the true 183 * year retrieved from the rtc's century and year fields. 184 */ 185 tod.tod_year = rtc->rtc_year + (rtc->rtc_century * 100) - 1900; 186 tod.tod_month = rtc->rtc_mon; 187 tod.tod_day = rtc->rtc_dom; 188 tod.tod_dow = rtc->rtc_dow; 189 tod.tod_hour = rtc->rtc_hrs; 190 tod.tod_min = rtc->rtc_min; 191 tod.tod_sec = rtc->rtc_sec; 192 193 return (tod); 194 } 195 196 uint_t 197 read_rtc(struct rtc_t *rtc) 198 { 199 int s; 200 uint_t rtc_readable = 0; 201 202 s = splhi(); 203 /* 204 * If UIP bit is not set we have at least 274us 205 * to read the values. Otherwise we have up to 206 * 336us to wait before we can read it 207 */ 208 if (!(RTC_GET8(RTC_A) & RTC_UIP)) { 209 rtc_readable = 1; 210 211 rtc->rtc_sec = RTC_GET8(RTC_SEC); 212 rtc->rtc_asec = RTC_GET8(RTC_ASEC); 213 rtc->rtc_min = RTC_GET8(RTC_MIN); 214 rtc->rtc_amin = RTC_GET8(RTC_AMIN); 215 216 rtc->rtc_hrs = RTC_GET8(RTC_HRS); 217 rtc->rtc_ahrs = RTC_GET8(RTC_AHRS); 218 rtc->rtc_dow = RTC_GET8(RTC_DOW); 219 rtc->rtc_dom = RTC_GET8(RTC_DOM); 220 rtc->rtc_adom = RTC_GET8(RTC_D) & 0x3f; 221 222 rtc->rtc_mon = RTC_GET8(RTC_MON); 223 rtc->rtc_year = RTC_GET8(RTC_YEAR); 224 rtc->rtc_century = RTC_GET8(RTC_CENTURY); 225 rtc->rtc_amon = 0; 226 227 /* Clear wakeup data */ 228 rtc->apc_wdwr = 0; 229 rtc->apc_wdmr = 0; 230 rtc->apc_wmr = 0; 231 rtc->apc_wyr = 0; 232 rtc->apc_wcr = 0; 233 } 234 splx(s); 235 return (rtc_readable); 236 } 237 238 /* 239 * Write the specified time into the clock chip. 240 * Must be called with tod_lock held. 241 */ 242 static void 243 todm5819_set(timestruc_t ts) 244 { 245 struct rtc_t rtc; 246 todinfo_t tod = utc_to_tod(ts.tv_sec); 247 int year; 248 249 ASSERT(MUTEX_HELD(&tod_lock)); 250 251 /* tod_year is base 1900 so this code needs to adjust */ 252 year = 1900 + tod.tod_year; 253 rtc.rtc_year = year % 100; 254 rtc.rtc_century = year / 100; 255 rtc.rtc_mon = (uint8_t)tod.tod_month; 256 rtc.rtc_dom = (uint8_t)tod.tod_day; 257 rtc.rtc_dow = (uint8_t)tod.tod_dow; 258 rtc.rtc_hrs = (uint8_t)tod.tod_hour; 259 rtc.rtc_min = (uint8_t)tod.tod_min; 260 rtc.rtc_sec = (uint8_t)tod.tod_sec; 261 DPRINTF("todm5819_set: century=%d year=%d dom=%d hrs=%d\n", 262 rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs); 263 264 write_rtc_time(&rtc); 265 } 266 267 void 268 write_rtc_time(struct rtc_t *rtc) 269 { 270 uint8_t regb; 271 int i; 272 273 /* 274 * Freeze 275 */ 276 regb = RTC_GET8(RTC_B); 277 RTC_PUT8(RTC_B, (regb | RTC_SET)); 278 279 /* 280 * If an update is in progress wait for the UIP flag to clear. 281 * If we write whilst UIP is still set there is a slight but real 282 * possibility of corrupting the RTC date and time registers. 283 * 284 * The expected wait is one internal cycle of the chip. We could 285 * simply spin but this may hang a CPU if we were to have a broken 286 * RTC chip where UIP is stuck, so we use a retry loop instead. 287 * No critical section is needed here as the UIP flag will not be 288 * re-asserted until we clear RTC_SET. 289 */ 290 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) { 291 if (!(RTC_GET8(RTC_A) & RTC_UIP)) { 292 break; 293 } 294 drv_usecwait(TODM5819_UIP_WAIT_USEC); 295 } 296 if (i < TODM5819_UIP_RETRY_THRESH) { 297 RTC_PUT8(RTC_SEC, (rtc->rtc_sec)); 298 RTC_PUT8(RTC_ASEC, (rtc->rtc_asec)); 299 RTC_PUT8(RTC_MIN, (rtc->rtc_min)); 300 RTC_PUT8(RTC_AMIN, (rtc->rtc_amin)); 301 302 RTC_PUT8(RTC_HRS, (rtc->rtc_hrs)); 303 RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs)); 304 RTC_PUT8(RTC_DOW, (rtc->rtc_dow)); 305 RTC_PUT8(RTC_DOM, (rtc->rtc_dom)); 306 307 RTC_PUT8(RTC_MON, (rtc->rtc_mon)); 308 RTC_PUT8(RTC_YEAR, (rtc->rtc_year)); 309 RTC_PUT8(RTC_CENTURY, (rtc->rtc_century)); 310 } else { 311 cmn_err(CE_WARN, "todm5819: Could not write the RTC\n"); 312 } 313 314 /* 315 * Unfreeze 316 */ 317 RTC_PUT8(RTC_B, regb); 318 } 319 320 321 void 322 write_rtc_alarm(struct rtc_t *rtc) 323 { 324 RTC_PUT8(RTC_ASEC, (rtc->rtc_asec)); 325 RTC_PUT8(RTC_AMIN, (rtc->rtc_amin)); 326 RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs)); 327 RTC_PUT8(RTC_D, (rtc->rtc_adom)); 328 } 329 330 /* 331 * program the rtc registers for alarm to go off at the specified time 332 */ 333 static void 334 todm5819_set_power_alarm(timestruc_t ts) 335 { 336 todinfo_t tod; 337 uint8_t regb; 338 struct rtc_t rtc; 339 340 ASSERT(MUTEX_HELD(&tod_lock)); 341 tod = utc_to_tod(ts.tv_sec); 342 343 /* 344 * disable alarms 345 */ 346 regb = RTC_GET8(RTC_B); 347 RTC_PUT8(RTC_B, (regb & ~RTC_AIE)); 348 349 350 rtc.rtc_asec = (uint8_t)tod.tod_sec; 351 rtc.rtc_amin = (uint8_t)tod.tod_min; 352 rtc.rtc_ahrs = (uint8_t)tod.tod_hour; 353 rtc.rtc_adom = (uint8_t)tod.tod_day; 354 355 write_rtc_alarm(&rtc); 356 /* 357 * Enable alarm. 358 */ 359 RTC_PUT8(RTC_B, (regb | RTC_AIE)); 360 } 361 362 /* 363 * clear alarm interrupt 364 */ 365 static void 366 todm5819_clear_power_alarm(void) 367 { 368 uint8_t regb; 369 ASSERT(MUTEX_HELD(&tod_lock)); 370 371 regb = RTC_GET8(RTC_B); 372 RTC_PUT8(RTC_B, (regb & ~RTC_AIE)); 373 } 374 375 /* 376 * Determine the cpu frequency by watching the TOD chip rollover twice. 377 * Cpu clock rate is determined by computing the ticks added (in tick register) 378 * during one second interval on TOD. 379 */ 380 uint64_t 381 todm5819_get_cpufrequency(void) 382 { 383 ASSERT(MUTEX_HELD(&tod_lock)); 384 M5819_ADDR_REG = RTC_SEC; 385 return (find_cpufrequency(v_rtc_data_reg)); 386 } 387 388 389 /*ARGSUSED*/ 390 static uint_t 391 todm5819_set_watchdog_timer(uint_t timeoutval) 392 { 393 ASSERT(MUTEX_HELD(&tod_lock)); 394 return (0); 395 } 396 397 static uint_t 398 todm5819_clear_watchdog_timer(void) 399 { 400 ASSERT(MUTEX_HELD(&tod_lock)); 401 return (0); 402 } 403