elan-mmcr.c revision 124144
1/*- 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * 10 * The AMD Elan sc520 is a system-on-chip gadget which is used in embedded 11 * kind of things, see www.soekris.com for instance, and it has a few quirks 12 * we need to deal with. 13 * Unfortunately we cannot identify the gadget by CPUID output because it 14 * depends on strapping options and only the stepping field may be useful 15 * and those are undocumented from AMDs side. 16 * 17 * So instead we recognize the on-chip host-PCI bridge and call back from 18 * sys/i386/pci/pci_bus.c to here if we find it. 19 * 20 * #ifdef CPU_ELAN_PPS 21 * The Elan has three general purpose counters, and when two of these 22 * are used just right they can hardware timestamp external events with 23 * approx 125 nsec resolution and +/- 125 nsec precision. 24 * 25 * Connect the signal to TMR1IN and a GPIO pin, and configure the GPIO pin 26 * with a 'P' in sysctl machdep.elan_gpio_config. 27 * 28 * The rising edge of the signal will start timer 1 counting up from 29 * zero, and when the timecounter polls for PPS, both counter 1 & 2 is 30 * read, as well as the GPIO bit. If a rising edge has happened, the 31 * contents of timer 1 which is how long time ago the edge happened, 32 * is subtracted from timer 2 to give us a "true time stamp". 33 * 34 * Echoing the PPS signal on any GPIO pin is supported (set it to 'e' 35 * or 'E' (inverted) in the sysctl) The echo signal should only be 36 * used as a visual indication, not for calibration since it suffers 37 * from 1/hz (or more) jitter which the timestamps are compensated for. 38 * #endif CPU_ELAN_PPS 39 */ 40 41#include <sys/cdefs.h> 42__FBSDID("$FreeBSD: head/sys/i386/i386/elan-mmcr.c 124144 2004-01-05 12:00:59Z phk $"); 43 44#include "opt_cpu.h" 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/kernel.h> 48#include <sys/conf.h> 49#include <sys/sysctl.h> 50#include <sys/syslog.h> 51#include <sys/timetc.h> 52#include <sys/proc.h> 53#include <sys/uio.h> 54#include <sys/lock.h> 55#include <sys/mutex.h> 56#include <sys/malloc.h> 57#include <sys/sysctl.h> 58#include <sys/timepps.h> 59#include <sys/watchdog.h> 60 61#include <dev/led/led.h> 62#include <machine/md_var.h> 63#include <machine/elan_mmcr.h> 64 65#include <vm/vm.h> 66#include <vm/pmap.h> 67 68static char gpio_config[33]; 69 70static volatile uint16_t *mmcrptr; 71volatile struct elan_mmcr * elan_mmcr; 72 73#ifdef CPU_ELAN_PPS 74static struct pps_state elan_pps; 75u_int pps_a, pps_d; 76u_int echo_a, echo_d; 77#endif /* CPU_ELAN_PPS */ 78u_int led_cookie[32]; 79dev_t led_dev[32]; 80 81static void 82gpio_led(void *cookie, int state) 83{ 84 u_int u, v; 85 86 u = *(int *)cookie; 87 v = u & 0xffff; 88 u >>= 16; 89 if (!state) 90 v ^= 0xc; 91 mmcrptr[v / 2] = u; 92} 93 94static int 95sysctl_machdep_elan_gpio_config(SYSCTL_HANDLER_ARGS) 96{ 97 u_int u, v; 98 int i, np, ne; 99 int error; 100 char buf[32], tmp[10]; 101 102 error = SYSCTL_OUT(req, gpio_config, 33); 103 if (error != 0 || req->newptr == NULL) 104 return (error); 105 if (req->newlen != 32) 106 return (EINVAL); 107 error = SYSCTL_IN(req, buf, 32); 108 if (error != 0) 109 return (error); 110 /* Disallow any disabled pins and count pps and echo */ 111 np = ne = 0; 112 for (i = 0; i < 32; i++) { 113 if (gpio_config[i] == '-' && (buf[i] != '-' && buf[i] != '.')) 114 return (EPERM); 115 if (buf[i] == 'P') { 116 np++; 117 if (np > 1) 118 return (EINVAL); 119 } 120 if (buf[i] == 'e' || buf[i] == 'E') { 121 ne++; 122 if (ne > 1) 123 return (EINVAL); 124 } 125 if (buf[i] != 'L' && buf[i] != 'l' 126#ifdef CPU_ELAN_PPS 127 && buf[i] != 'P' && buf[i] != 'E' && buf[i] != 'e' 128#endif /* CPU_ELAN_PPS */ 129 && buf[i] != '.' && buf[i] != '-') 130 return (EINVAL); 131 } 132#ifdef CPU_ELAN_PPS 133 if (np == 0) 134 pps_a = pps_d = 0; 135 if (ne == 0) 136 echo_a = echo_d = 0; 137#endif 138 for (i = 0; i < 32; i++) { 139 u = 1 << (i & 0xf); 140 if (i >= 16) 141 v = 2; 142 else 143 v = 0; 144 if (buf[i] != 'l' && buf[i] != 'L' && led_dev[i] != NULL) { 145 led_destroy(led_dev[i]); 146 led_dev[i] = NULL; 147 mmcrptr[(0xc2a + v) / 2] &= ~u; 148 } 149 switch (buf[i]) { 150#ifdef CPU_ELAN_PPS 151 case 'P': 152 pps_d = u; 153 pps_a = 0xc30 + v; 154 mmcrptr[(0xc2a + v) / 2] &= ~u; 155 gpio_config[i] = buf[i]; 156 break; 157 case 'e': 158 case 'E': 159 echo_d = u; 160 if (buf[i] == 'E') 161 echo_a = 0xc34 + v; 162 else 163 echo_a = 0xc38 + v; 164 mmcrptr[(0xc2a + v) / 2] |= u; 165 gpio_config[i] = buf[i]; 166 break; 167#endif /* CPU_ELAN_PPS */ 168 case 'l': 169 case 'L': 170 if (buf[i] == 'L') 171 led_cookie[i] = (0xc34 + v) | (u << 16); 172 else 173 led_cookie[i] = (0xc38 + v) | (u << 16); 174 if (led_dev[i]) 175 break; 176 sprintf(tmp, "gpio%d", i); 177 led_dev[i] = 178 led_create(gpio_led, &led_cookie[i], tmp); 179 mmcrptr[(0xc2a + v) / 2] |= u; 180 gpio_config[i] = buf[i]; 181 break; 182 case '.': 183 gpio_config[i] = buf[i]; 184 break; 185 case '-': 186 default: 187 break; 188 } 189 } 190 return (0); 191} 192 193SYSCTL_OID(_machdep, OID_AUTO, elan_gpio_config, CTLTYPE_STRING | CTLFLAG_RW, 194 NULL, 0, sysctl_machdep_elan_gpio_config, "A", "Elan CPU GPIO pin config"); 195 196#ifdef CPU_ELAN_PPS 197static void 198elan_poll_pps(struct timecounter *tc) 199{ 200 static int state; 201 int i; 202 u_int u; 203 204 /* 205 * Order is important here. We need to check the state of the GPIO 206 * pin first, in order to avoid reading timer 1 right before the 207 * state change. Technically pps_a may be zero in which case we 208 * harmlessly read the REVID register and the contents of pps_d is 209 * of no concern. 210 */ 211 i = mmcrptr[pps_a / 2] & pps_d; 212 213 /* 214 * Subtract timer1 from timer2 to compensate for time from the 215 * edge until now. 216 */ 217 u = elan_mmcr->GPTMR2CNT - elan_mmcr->GPTMR1CNT; 218 219 /* If state did not change or we don't have a GPIO pin, return */ 220 if (i == state || pps_a == 0) 221 return; 222 223 state = i; 224 225 /* If the state is "low", flip the echo GPIO and return. */ 226 if (!i) { 227 if (echo_a) 228 mmcrptr[(echo_a ^ 0xc) / 2] = echo_d; 229 return; 230 } 231 232 /* State is "high", record the pps data */ 233 pps_capture(&elan_pps); 234 elan_pps.capcount = u & 0xffff; 235 pps_event(&elan_pps, PPS_CAPTUREASSERT); 236 237 /* Twiddle echo bit */ 238 if (echo_a) 239 mmcrptr[echo_a / 2] = echo_d; 240} 241#endif /* CPU_ELAN_PPS */ 242 243static unsigned 244elan_get_timecount(struct timecounter *tc) 245{ 246 247 /* Read timer2, end of story */ 248 return (elan_mmcr->GPTMR2CNT); 249} 250 251/* 252 * The Elan CPU can be run from a number of clock frequencies, this 253 * allows you to override the default 33.3 MHZ. 254 */ 255#ifndef CPU_ELAN_XTAL 256#define CPU_ELAN_XTAL 33333333 257#endif 258 259static struct timecounter elan_timecounter = { 260 elan_get_timecount, 261 NULL, 262 0xffff, 263 CPU_ELAN_XTAL / 4, 264 "ELAN", 265 1000 266}; 267 268static int 269sysctl_machdep_elan_freq(SYSCTL_HANDLER_ARGS) 270{ 271 u_int f; 272 int error; 273 274 f = elan_timecounter.tc_frequency * 4; 275 error = sysctl_handle_int(oidp, &f, sizeof(f), req); 276 if (error == 0 && req->newptr != NULL) 277 elan_timecounter.tc_frequency = (f + 3) / 4; 278 return (error); 279} 280 281SYSCTL_PROC(_machdep, OID_AUTO, elan_freq, CTLTYPE_UINT | CTLFLAG_RW, 282 0, sizeof (u_int), sysctl_machdep_elan_freq, "IU", ""); 283 284/* 285 * Positively identifying the Elan can only be done through the PCI id of 286 * the host-bridge, this function is called from i386/pci/pci_bus.c. 287 */ 288void 289init_AMD_Elan_sc520(void) 290{ 291 u_int new; 292 int i; 293 294 mmcrptr = pmap_mapdev(0xfffef000, 0x1000); 295 elan_mmcr = (volatile struct elan_mmcr *)mmcrptr; 296 297 /*- 298 * The i8254 is driven with a nonstandard frequency which is 299 * derived thusly: 300 * f = 32768 * 45 * 25 / 31 = 1189161.29... 301 * We use the sysctl to get the i8254 (timecounter etc) into whack. 302 */ 303 304 new = 1189161; 305 i = kernel_sysctlbyname(&thread0, "machdep.i8254_freq", 306 NULL, 0, &new, sizeof new, NULL); 307 if (bootverbose || 1) 308 printf("sysctl machdep.i8254_freq=%d returns %d\n", new, i); 309 310 /* Start GP timer #2 and use it as timecounter, hz permitting */ 311 elan_mmcr->GPTMR2MAXCMPA = 0; 312 elan_mmcr->GPTMR2CTL = 0xc001; 313 314#ifdef CPU_ELAN_PPS 315 /* Set up GP timer #1 as pps counter */ 316 elan_mmcr->CSPFS &= ~0x10; 317 elan_mmcr->GPTMR1CTL = 0x8000 | 0x4000 | 0x10 | 0x1; 318 elan_mmcr->GPTMR1MAXCMPA = 0x0; 319 elan_mmcr->GPTMR1MAXCMPB = 0x0; 320 elan_pps.ppscap |= PPS_CAPTUREASSERT; 321 pps_init(&elan_pps); 322#endif 323 tc_init(&elan_timecounter); 324} 325 326static d_ioctl_t elan_ioctl; 327static d_mmap_t elan_mmap; 328 329static struct cdevsw elan_cdevsw = { 330 .d_ioctl = elan_ioctl, 331 .d_mmap = elan_mmap, 332 .d_name = "elan", 333}; 334 335static void 336elan_drvinit(void) 337{ 338 339 /* If no elan found, just return */ 340 if (mmcrptr == NULL) 341 return; 342 343 printf("Elan-mmcr driver: MMCR at %p.%s\n", 344 mmcrptr, 345#ifdef CPU_ELAN_PPS 346 " PPS support." 347#else 348 "" 349#endif 350 ); 351 352 make_dev(&elan_cdevsw, 0, 353 UID_ROOT, GID_WHEEL, 0600, "elan-mmcr"); 354 355#ifdef CPU_SOEKRIS 356 /* Create the error LED on GPIO9 */ 357 led_cookie[9] = 0x02000c34; 358 led_dev[9] = led_create(gpio_led, &led_cookie[9], "error"); 359 360 /* Disable the unavailable GPIO pins */ 361 strcpy(gpio_config, "-----....--..--------..---------"); 362#else /* !CPU_SOEKRIS */ 363 /* We don't know which pins are available so enable them all */ 364 strcpy(gpio_config, "................................"); 365#endif /* CPU_SOEKRIS */ 366} 367 368SYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE, elan_drvinit, NULL); 369 370static int 371elan_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 372{ 373 374 if (offset >= 0x1000) 375 return (-1); 376 *paddr = 0xfffef000; 377 return (0); 378} 379 380static int 381elan_watchdog(u_int spec) 382{ 383 u_int u, v; 384 static u_int cur; 385 386 if (spec & ~__WD_LEGAL) 387 return (EINVAL); 388 switch (spec & (WD_ACTIVE|WD_PASSIVE)) { 389 case WD_ACTIVE: 390 u = spec & WD_INTERVAL; 391 if (u > 35) 392 return (EINVAL); 393 u = imax(u - 5, 24); 394 v = 2 << (u - 24); 395 v |= 0xc000; 396 397 /* 398 * There is a bug in some silicon which prevents us from 399 * writing to the WDTMRCTL register if the GP echo mode is 400 * enabled. GP echo mode on the other hand is desirable 401 * for other reasons. Save and restore the GP echo mode 402 * around our hardware tom-foolery. 403 */ 404 u = elan_mmcr->GPECHO; 405 elan_mmcr->GPECHO = 0; 406 if (v != cur) { 407 /* Clear the ENB bit */ 408 elan_mmcr->WDTMRCTL = 0x3333; 409 elan_mmcr->WDTMRCTL = 0xcccc; 410 elan_mmcr->WDTMRCTL = 0; 411 412 /* Set new value */ 413 elan_mmcr->WDTMRCTL = 0x3333; 414 elan_mmcr->WDTMRCTL = 0xcccc; 415 elan_mmcr->WDTMRCTL = v; 416 cur = v; 417 } else { 418 /* Just reset timer */ 419 elan_mmcr->WDTMRCTL = 0xaaaa; 420 elan_mmcr->WDTMRCTL = 0x5555; 421 } 422 elan_mmcr->GPECHO = u; 423 return (0); 424 case WD_PASSIVE: 425 return (EOPNOTSUPP); 426 case 0: 427 u = elan_mmcr->GPECHO; 428 elan_mmcr->GPECHO = 0; 429 elan_mmcr->WDTMRCTL = 0x3333; 430 elan_mmcr->WDTMRCTL = 0xcccc; 431 elan_mmcr->WDTMRCTL = 0x4080; 432 elan_mmcr->WDTMRCTL = u; 433 elan_mmcr->GPECHO = u; 434 cur = 0; 435 return (0); 436 default: 437 return (EINVAL); 438 } 439 440} 441 442static int 443elan_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr) 444{ 445 int error; 446 447 error = ENOTTY; 448 449#ifdef CPU_ELAN_PPS 450 if (pps_a != 0) 451 error = pps_ioctl(cmd, arg, &elan_pps); 452 /* 453 * We only want to incur the overhead of the PPS polling if we 454 * are actually asked to timestamp. 455 */ 456 if (elan_pps.ppsparam.mode & PPS_CAPTUREASSERT) { 457 elan_timecounter.tc_poll_pps = elan_poll_pps; 458 } else { 459 elan_timecounter.tc_poll_pps = NULL; 460 } 461 if (error != ENOTTY) 462 return (error); 463#endif 464 465 if (cmd == WDIOCPATPAT) 466 return elan_watchdog(*((u_int*)arg)); 467 468 /* Other future ioctl handling here */ 469 return(error); 470} 471 472