elan-mmcr.c revision 130585
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 130585 2004-06-16 09:47:26Z 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; 75static volatile uint16_t *pps_ap[3]; 76static u_int pps_a, pps_d; 77static u_int echo_a, echo_d; 78#endif /* CPU_ELAN_PPS */ 79 80static u_int led_cookie[32]; 81static struct cdev *led_dev[32]; 82 83static void 84gpio_led(void *cookie, int state) 85{ 86 u_int u, v; 87 88 u = *(int *)cookie; 89 v = u & 0xffff; 90 u >>= 16; 91 if (!state) 92 v ^= 0xc; 93 mmcrptr[v / 2] = u; 94} 95 96static int 97sysctl_machdep_elan_gpio_config(SYSCTL_HANDLER_ARGS) 98{ 99 u_int u, v; 100 int i, np, ne; 101 int error; 102 char buf[32]; 103#ifdef CPU_SOEKRIS 104 char tmp[10]; 105#endif 106 107 error = SYSCTL_OUT(req, gpio_config, 33); 108 if (error != 0 || req->newptr == NULL) 109 return (error); 110 if (req->newlen != 32) 111 return (EINVAL); 112 error = SYSCTL_IN(req, buf, 32); 113 if (error != 0) 114 return (error); 115 /* Disallow any disabled pins and count pps and echo */ 116 np = ne = 0; 117 for (i = 0; i < 32; i++) { 118 if (gpio_config[i] == '-' && buf[i] == '.') 119 buf[i] = gpio_config[i]; 120 if (gpio_config[i] == '-' && buf[i] != '-') 121 return (EPERM); 122 if (buf[i] == 'P') { 123 np++; 124 if (np > 1) 125 return (EINVAL); 126 } 127 if (buf[i] == 'e' || buf[i] == 'E') { 128 ne++; 129 if (ne > 1) 130 return (EINVAL); 131 } 132 if (buf[i] != 'L' && buf[i] != 'l' 133#ifdef CPU_ELAN_PPS 134 && buf[i] != 'P' && buf[i] != 'E' && buf[i] != 'e' 135#endif /* CPU_ELAN_PPS */ 136 && buf[i] != '.' && buf[i] != '-') 137 return (EINVAL); 138 } 139#ifdef CPU_ELAN_PPS 140 if (np == 0) 141 pps_a = pps_d = 0; 142 if (ne == 0) 143 echo_a = echo_d = 0; 144#endif 145 for (i = 0; i < 32; i++) { 146 u = 1 << (i & 0xf); 147 if (i >= 16) 148 v = 2; 149 else 150 v = 0; 151#ifdef CPU_SOEKRIS 152 if (i == 9) 153 ; 154 else 155#endif 156 if (buf[i] != 'l' && buf[i] != 'L' && led_dev[i] != NULL) { 157 led_destroy(led_dev[i]); 158 led_dev[i] = NULL; 159 mmcrptr[(0xc2a + v) / 2] &= ~u; 160 } 161 switch (buf[i]) { 162#ifdef CPU_ELAN_PPS 163 case 'P': 164 pps_d = u; 165 pps_a = 0xc30 + v; 166 pps_ap[0] = &mmcrptr[pps_a / 2]; 167 pps_ap[1] = &elan_mmcr->GPTMR2CNT; 168 pps_ap[2] = &elan_mmcr->GPTMR1CNT; 169 mmcrptr[(0xc2a + v) / 2] &= ~u; 170 gpio_config[i] = buf[i]; 171 break; 172 case 'e': 173 case 'E': 174 echo_d = u; 175 if (buf[i] == 'E') 176 echo_a = 0xc34 + v; 177 else 178 echo_a = 0xc38 + v; 179 mmcrptr[(0xc2a + v) / 2] |= u; 180 gpio_config[i] = buf[i]; 181 break; 182#endif /* CPU_ELAN_PPS */ 183 case 'l': 184 case 'L': 185 if (buf[i] == 'L') 186 led_cookie[i] = (0xc34 + v) | (u << 16); 187 else 188 led_cookie[i] = (0xc38 + v) | (u << 16); 189 if (led_dev[i]) 190 break; 191 sprintf(tmp, "gpio%d", i); 192 mmcrptr[(0xc2a + v) / 2] |= u; 193 gpio_config[i] = buf[i]; 194 led_dev[i] = 195 led_create(gpio_led, &led_cookie[i], tmp); 196 break; 197 case '.': 198 gpio_config[i] = buf[i]; 199 break; 200 case '-': 201 default: 202 break; 203 } 204 } 205 return (0); 206} 207 208SYSCTL_OID(_machdep, OID_AUTO, elan_gpio_config, CTLTYPE_STRING | CTLFLAG_RW, 209 NULL, 0, sysctl_machdep_elan_gpio_config, "A", "Elan CPU GPIO pin config"); 210 211#ifdef CPU_ELAN_PPS 212static void 213elan_poll_pps(struct timecounter *tc) 214{ 215 static int state; 216 int i; 217 uint16_t u, x, y, z; 218 u_long eflags; 219 220 /* 221 * Grab the HW state as quickly and compactly as we can. Disable 222 * interrupts to avoid measuring our interrupt service time on 223 * hw with quality clock sources. 224 */ 225 eflags = read_eflags(); 226 disable_intr(); 227 x = *pps_ap[0]; /* state, must be first, see below */ 228 y = *pps_ap[1]; /* timer2 */ 229 z = *pps_ap[2]; /* timer1 */ 230 write_eflags(eflags); 231 232 /* 233 * Order is important here. We need to check the state of the GPIO 234 * pin first, in order to avoid reading timer 1 right before the 235 * state change. Technically pps_a may be zero in which case we 236 * harmlessly read the REVID register and the contents of pps_d is 237 * of no concern. 238 */ 239 240 i = x & pps_d; 241 242 /* If state did not change or we don't have a GPIO pin, return */ 243 if (i == state || pps_a == 0) 244 return; 245 246 state = i; 247 248 /* If the state is "low", flip the echo GPIO and return. */ 249 if (!i) { 250 if (echo_a) 251 mmcrptr[(echo_a ^ 0xc) / 2] = echo_d; 252 return; 253 } 254 255 /* 256 * Subtract timer1 from timer2 to compensate for time from the 257 * edge until we read the counters. 258 */ 259 u = y - z; 260 261 pps_capture(&elan_pps); 262 elan_pps.capcount = u; 263 pps_event(&elan_pps, PPS_CAPTUREASSERT); 264 265 /* Twiddle echo bit */ 266 if (echo_a) 267 mmcrptr[echo_a / 2] = echo_d; 268} 269#endif /* CPU_ELAN_PPS */ 270 271static unsigned 272elan_get_timecount(struct timecounter *tc) 273{ 274 275 /* Read timer2, end of story */ 276 return (elan_mmcr->GPTMR2CNT); 277} 278 279/* 280 * The Elan CPU can be run from a number of clock frequencies, this 281 * allows you to override the default 33.3 MHZ. 282 */ 283#ifndef CPU_ELAN_XTAL 284#define CPU_ELAN_XTAL 33333333 285#endif 286 287static struct timecounter elan_timecounter = { 288 elan_get_timecount, 289 NULL, 290 0xffff, 291 CPU_ELAN_XTAL / 4, 292 "ELAN", 293 1000 294}; 295 296static int 297sysctl_machdep_elan_freq(SYSCTL_HANDLER_ARGS) 298{ 299 u_int f; 300 int error; 301 302 f = elan_timecounter.tc_frequency * 4; 303 error = sysctl_handle_int(oidp, &f, sizeof(f), req); 304 if (error == 0 && req->newptr != NULL) 305 elan_timecounter.tc_frequency = (f + 3) / 4; 306 return (error); 307} 308 309SYSCTL_PROC(_machdep, OID_AUTO, elan_freq, CTLTYPE_UINT | CTLFLAG_RW, 310 0, sizeof (u_int), sysctl_machdep_elan_freq, "IU", ""); 311 312/* 313 * Positively identifying the Elan can only be done through the PCI id of 314 * the host-bridge, this function is called from i386/pci/pci_bus.c. 315 */ 316void 317init_AMD_Elan_sc520(void) 318{ 319 u_int new; 320 int i; 321 322 mmcrptr = pmap_mapdev(0xfffef000, 0x1000); 323 elan_mmcr = (volatile struct elan_mmcr *)mmcrptr; 324 325 /*- 326 * The i8254 is driven with a nonstandard frequency which is 327 * derived thusly: 328 * f = 32768 * 45 * 25 / 31 = 1189161.29... 329 * We use the sysctl to get the i8254 (timecounter etc) into whack. 330 */ 331 332 new = 1189161; 333 i = kernel_sysctlbyname(&thread0, "machdep.i8254_freq", 334 NULL, 0, &new, sizeof new, NULL); 335 if (bootverbose || 1) 336 printf("sysctl machdep.i8254_freq=%d returns %d\n", new, i); 337 338 /* Start GP timer #2 and use it as timecounter, hz permitting */ 339 elan_mmcr->GPTMR2MAXCMPA = 0; 340 elan_mmcr->GPTMR2CTL = 0xc001; 341 342#ifdef CPU_ELAN_PPS 343 /* Set up GP timer #1 as pps counter */ 344 elan_mmcr->CSPFS &= ~0x10; 345 elan_mmcr->GPTMR1CTL = 0x8000 | 0x4000 | 0x10 | 0x1; 346 elan_mmcr->GPTMR1MAXCMPA = 0x0; 347 elan_mmcr->GPTMR1MAXCMPB = 0x0; 348 elan_pps.ppscap |= PPS_CAPTUREASSERT; 349 pps_init(&elan_pps); 350#endif 351 tc_init(&elan_timecounter); 352} 353 354static void 355elan_watchdog(void *foo __unused, u_int spec, int *error) 356{ 357 u_int u, v; 358 static u_int cur; 359 360 u = spec & WD_INTERVAL; 361 if (spec && u <= 35) { 362 u = imax(u - 5, 24); 363 v = 2 << (u - 24); 364 v |= 0xc000; 365 366 /* 367 * There is a bug in some silicon which prevents us from 368 * writing to the WDTMRCTL register if the GP echo mode is 369 * enabled. GP echo mode on the other hand is desirable 370 * for other reasons. Save and restore the GP echo mode 371 * around our hardware tom-foolery. 372 */ 373 u = elan_mmcr->GPECHO; 374 elan_mmcr->GPECHO = 0; 375 if (v != cur) { 376 /* Clear the ENB bit */ 377 elan_mmcr->WDTMRCTL = 0x3333; 378 elan_mmcr->WDTMRCTL = 0xcccc; 379 elan_mmcr->WDTMRCTL = 0; 380 381 /* Set new value */ 382 elan_mmcr->WDTMRCTL = 0x3333; 383 elan_mmcr->WDTMRCTL = 0xcccc; 384 elan_mmcr->WDTMRCTL = v; 385 cur = v; 386 } else { 387 /* Just reset timer */ 388 elan_mmcr->WDTMRCTL = 0xaaaa; 389 elan_mmcr->WDTMRCTL = 0x5555; 390 } 391 elan_mmcr->GPECHO = u; 392 *error = 0; 393 return; 394 } else { 395 u = elan_mmcr->GPECHO; 396 elan_mmcr->GPECHO = 0; 397 elan_mmcr->WDTMRCTL = 0x3333; 398 elan_mmcr->WDTMRCTL = 0xcccc; 399 elan_mmcr->WDTMRCTL = 0x4080; 400 elan_mmcr->WDTMRCTL = u; 401 elan_mmcr->GPECHO = u; 402 cur = 0; 403 return; 404 } 405} 406 407static int 408elan_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 409{ 410 411 if (offset >= 0x1000) 412 return (-1); 413 *paddr = 0xfffef000; 414 return (0); 415} 416static int 417elan_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr) 418{ 419 int error; 420 421 error = ENOIOCTL; 422 423#ifdef CPU_ELAN_PPS 424 if (pps_a != 0) 425 error = pps_ioctl(cmd, arg, &elan_pps); 426 /* 427 * We only want to incur the overhead of the PPS polling if we 428 * are actually asked to timestamp. 429 */ 430 if (elan_pps.ppsparam.mode & PPS_CAPTUREASSERT) { 431 elan_timecounter.tc_poll_pps = elan_poll_pps; 432 } else { 433 elan_timecounter.tc_poll_pps = NULL; 434 } 435 if (error != ENOIOCTL) 436 return (error); 437#endif 438 439 return(error); 440} 441 442static struct cdevsw elan_cdevsw = { 443 .d_version = D_VERSION, 444 .d_flags = D_NEEDGIANT, 445 .d_ioctl = elan_ioctl, 446 .d_mmap = elan_mmap, 447 .d_name = "elan", 448}; 449 450static void 451elan_drvinit(void) 452{ 453 454 /* If no elan found, just return */ 455 if (mmcrptr == NULL) 456 return; 457 458 printf("Elan-mmcr driver: MMCR at %p.%s\n", 459 mmcrptr, 460#ifdef CPU_ELAN_PPS 461 " PPS support." 462#else 463 "" 464#endif 465 ); 466 467 make_dev(&elan_cdevsw, 0, 468 UID_ROOT, GID_WHEEL, 0600, "elan-mmcr"); 469 470#ifdef CPU_SOEKRIS 471 /* Create the error LED on GPIO9 */ 472 led_cookie[9] = 0x02000c34; 473 led_dev[9] = led_create(gpio_led, &led_cookie[9], "error"); 474 475 /* Disable the unavailable GPIO pins */ 476 strcpy(gpio_config, "-----....--..--------..---------"); 477#else /* !CPU_SOEKRIS */ 478 /* We don't know which pins are available so enable them all */ 479 strcpy(gpio_config, "................................"); 480#endif /* CPU_SOEKRIS */ 481 482 EVENTHANDLER_REGISTER(watchdog_list, elan_watchdog, NULL, 0); 483} 484 485SYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE, elan_drvinit, NULL); 486 487