elan-mmcr.c revision 201223
1119452Sobrien/*- 2101225Sphk * ---------------------------------------------------------------------------- 3101225Sphk * "THE BEER-WARE LICENSE" (Revision 42): 4101225Sphk * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5101225Sphk * can do whatever you want with this stuff. If we meet some day, and you think 6101225Sphk * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7101225Sphk * ---------------------------------------------------------------------------- 8101225Sphk * 9101225Sphk * 10102934Sphk * The AMD Elan sc520 is a system-on-chip gadget which is used in embedded 11102934Sphk * kind of things, see www.soekris.com for instance, and it has a few quirks 12102934Sphk * we need to deal with. 13102934Sphk * Unfortunately we cannot identify the gadget by CPUID output because it 14102934Sphk * depends on strapping options and only the stepping field may be useful 15102934Sphk * and those are undocumented from AMDs side. 16102934Sphk * 17102934Sphk * So instead we recognize the on-chip host-PCI bridge and call back from 18102934Sphk * sys/i386/pci/pci_bus.c to here if we find it. 19109327Sphk * 20123015Sphk * #ifdef CPU_ELAN_PPS 21123015Sphk * The Elan has three general purpose counters, and when two of these 22123015Sphk * are used just right they can hardware timestamp external events with 23123015Sphk * approx 125 nsec resolution and +/- 125 nsec precision. 24123015Sphk * 25123015Sphk * Connect the signal to TMR1IN and a GPIO pin, and configure the GPIO pin 26123015Sphk * with a 'P' in sysctl machdep.elan_gpio_config. 27123015Sphk * 28123015Sphk * The rising edge of the signal will start timer 1 counting up from 29123015Sphk * zero, and when the timecounter polls for PPS, both counter 1 & 2 is 30123015Sphk * read, as well as the GPIO bit. If a rising edge has happened, the 31123015Sphk * contents of timer 1 which is how long time ago the edge happened, 32123015Sphk * is subtracted from timer 2 to give us a "true time stamp". 33123015Sphk * 34123015Sphk * Echoing the PPS signal on any GPIO pin is supported (set it to 'e' 35123015Sphk * or 'E' (inverted) in the sysctl) The echo signal should only be 36123015Sphk * used as a visual indication, not for calibration since it suffers 37123015Sphk * from 1/hz (or more) jitter which the timestamps are compensated for. 38123015Sphk * #endif CPU_ELAN_PPS 39101225Sphk */ 40102934Sphk 41115683Sobrien#include <sys/cdefs.h> 42115683Sobrien__FBSDID("$FreeBSD: head/sys/i386/i386/elan-mmcr.c 201223 2009-12-29 21:51:28Z rnoland $"); 43115683Sobrien 44111138Sphk#include "opt_cpu.h" 45101225Sphk#include <sys/param.h> 46101225Sphk#include <sys/systm.h> 47101225Sphk#include <sys/kernel.h> 48101225Sphk#include <sys/conf.h> 49102934Sphk#include <sys/sysctl.h> 50123015Sphk#include <sys/syslog.h> 51102934Sphk#include <sys/timetc.h> 52102934Sphk#include <sys/proc.h> 53103482Sphk#include <sys/uio.h> 54103482Sphk#include <sys/lock.h> 55103482Sphk#include <sys/mutex.h> 56103482Sphk#include <sys/malloc.h> 57109327Sphk#include <sys/sysctl.h> 58109327Sphk#include <sys/timepps.h> 59111647Sphk#include <sys/watchdog.h> 60101225Sphk 61121944Sphk#include <dev/led/led.h> 62101225Sphk#include <machine/md_var.h> 63124144Sphk#include <machine/elan_mmcr.h> 64148231Sphk#include <machine/pc/bios.h> 65101225Sphk 66102934Sphk#include <vm/vm.h> 67102934Sphk#include <vm/pmap.h> 68102934Sphk 69123015Sphkstatic char gpio_config[33]; 70123015Sphk 71124144Sphkstatic volatile uint16_t *mmcrptr; 72127039Sphkvolatile struct elan_mmcr *elan_mmcr; 73102934Sphk 74123015Sphk#ifdef CPU_ELAN_PPS 75109327Sphkstatic struct pps_state elan_pps; 76127039Sphkstatic volatile uint16_t *pps_ap[3]; 77127039Sphkstatic u_int pps_a, pps_d; 78127039Sphkstatic u_int echo_a, echo_d; 79123015Sphk#endif /* CPU_ELAN_PPS */ 80127039Sphk 81148231Sphk#ifdef CPU_SOEKRIS 82148231Sphk 83148231Sphkstatic struct bios_oem bios_soekris = { 84148231Sphk { 0xf0000, 0xf1000 }, 85148231Sphk { 86148231Sphk { "Soekris", 0, 8 }, /* Soekris Engineering. */ 87148231Sphk { "net4", 0, 8 }, /* net45xx */ 88148231Sphk { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ 89148231Sphk { NULL, 0, 0 }, 90148231Sphk } 91148231Sphk}; 92148231Sphk 93148231Sphk#endif 94148231Sphk 95127039Sphkstatic u_int led_cookie[32]; 96130585Sphkstatic struct cdev *led_dev[32]; 97109327Sphk 98109327Sphkstatic void 99123015Sphkgpio_led(void *cookie, int state) 100123015Sphk{ 101123015Sphk u_int u, v; 102123015Sphk 103123015Sphk u = *(int *)cookie; 104123015Sphk v = u & 0xffff; 105123015Sphk u >>= 16; 106123015Sphk if (!state) 107123015Sphk v ^= 0xc; 108124144Sphk mmcrptr[v / 2] = u; 109123015Sphk} 110123015Sphk 111123015Sphkstatic int 112123015Sphksysctl_machdep_elan_gpio_config(SYSCTL_HANDLER_ARGS) 113123015Sphk{ 114123015Sphk u_int u, v; 115123015Sphk int i, np, ne; 116123015Sphk int error; 117126762Sjb char buf[32]; 118126762Sjb char tmp[10]; 119123015Sphk 120123015Sphk error = SYSCTL_OUT(req, gpio_config, 33); 121123015Sphk if (error != 0 || req->newptr == NULL) 122123015Sphk return (error); 123123015Sphk if (req->newlen != 32) 124123015Sphk return (EINVAL); 125123015Sphk error = SYSCTL_IN(req, buf, 32); 126123015Sphk if (error != 0) 127123015Sphk return (error); 128123015Sphk /* Disallow any disabled pins and count pps and echo */ 129123015Sphk np = ne = 0; 130123015Sphk for (i = 0; i < 32; i++) { 131127801Sphk if (gpio_config[i] == '-' && buf[i] == '.') 132127801Sphk buf[i] = gpio_config[i]; 133127801Sphk if (gpio_config[i] == '-' && buf[i] != '-') 134123015Sphk return (EPERM); 135123015Sphk if (buf[i] == 'P') { 136123015Sphk np++; 137123015Sphk if (np > 1) 138123015Sphk return (EINVAL); 139123015Sphk } 140123015Sphk if (buf[i] == 'e' || buf[i] == 'E') { 141123015Sphk ne++; 142123015Sphk if (ne > 1) 143123015Sphk return (EINVAL); 144123015Sphk } 145123015Sphk if (buf[i] != 'L' && buf[i] != 'l' 146123015Sphk#ifdef CPU_ELAN_PPS 147123015Sphk && buf[i] != 'P' && buf[i] != 'E' && buf[i] != 'e' 148123015Sphk#endif /* CPU_ELAN_PPS */ 149123015Sphk && buf[i] != '.' && buf[i] != '-') 150123015Sphk return (EINVAL); 151123015Sphk } 152123015Sphk#ifdef CPU_ELAN_PPS 153123015Sphk if (np == 0) 154123015Sphk pps_a = pps_d = 0; 155123015Sphk if (ne == 0) 156123015Sphk echo_a = echo_d = 0; 157123015Sphk#endif 158123015Sphk for (i = 0; i < 32; i++) { 159123015Sphk u = 1 << (i & 0xf); 160123015Sphk if (i >= 16) 161123015Sphk v = 2; 162123015Sphk else 163123015Sphk v = 0; 164126762Sjb#ifdef CPU_SOEKRIS 165127801Sphk if (i == 9) 166127801Sphk ; 167127801Sphk else 168127801Sphk#endif 169123015Sphk if (buf[i] != 'l' && buf[i] != 'L' && led_dev[i] != NULL) { 170123015Sphk led_destroy(led_dev[i]); 171123015Sphk led_dev[i] = NULL; 172124144Sphk mmcrptr[(0xc2a + v) / 2] &= ~u; 173123015Sphk } 174123015Sphk switch (buf[i]) { 175123015Sphk#ifdef CPU_ELAN_PPS 176123015Sphk case 'P': 177123015Sphk pps_d = u; 178123015Sphk pps_a = 0xc30 + v; 179127039Sphk pps_ap[0] = &mmcrptr[pps_a / 2]; 180127039Sphk pps_ap[1] = &elan_mmcr->GPTMR2CNT; 181127039Sphk pps_ap[2] = &elan_mmcr->GPTMR1CNT; 182124144Sphk mmcrptr[(0xc2a + v) / 2] &= ~u; 183123015Sphk gpio_config[i] = buf[i]; 184123015Sphk break; 185123015Sphk case 'e': 186123015Sphk case 'E': 187123015Sphk echo_d = u; 188123015Sphk if (buf[i] == 'E') 189123015Sphk echo_a = 0xc34 + v; 190123015Sphk else 191123015Sphk echo_a = 0xc38 + v; 192124144Sphk mmcrptr[(0xc2a + v) / 2] |= u; 193123015Sphk gpio_config[i] = buf[i]; 194123015Sphk break; 195123015Sphk#endif /* CPU_ELAN_PPS */ 196123015Sphk case 'l': 197123015Sphk case 'L': 198123015Sphk if (buf[i] == 'L') 199123015Sphk led_cookie[i] = (0xc34 + v) | (u << 16); 200123015Sphk else 201123015Sphk led_cookie[i] = (0xc38 + v) | (u << 16); 202123015Sphk if (led_dev[i]) 203123015Sphk break; 204123015Sphk sprintf(tmp, "gpio%d", i); 205128677Sphk mmcrptr[(0xc2a + v) / 2] |= u; 206128677Sphk gpio_config[i] = buf[i]; 207123015Sphk led_dev[i] = 208123015Sphk led_create(gpio_led, &led_cookie[i], tmp); 209123015Sphk break; 210123015Sphk case '.': 211123015Sphk gpio_config[i] = buf[i]; 212123015Sphk break; 213123015Sphk case '-': 214123015Sphk default: 215123015Sphk break; 216123015Sphk } 217123015Sphk } 218123015Sphk return (0); 219123015Sphk} 220123015Sphk 221123015SphkSYSCTL_OID(_machdep, OID_AUTO, elan_gpio_config, CTLTYPE_STRING | CTLFLAG_RW, 222123015Sphk NULL, 0, sysctl_machdep_elan_gpio_config, "A", "Elan CPU GPIO pin config"); 223123015Sphk 224123015Sphk#ifdef CPU_ELAN_PPS 225123015Sphkstatic void 226109327Sphkelan_poll_pps(struct timecounter *tc) 227109327Sphk{ 228109327Sphk static int state; 229109327Sphk int i; 230127039Sphk uint16_t u, x, y, z; 231127039Sphk u_long eflags; 232109327Sphk 233123015Sphk /* 234127039Sphk * Grab the HW state as quickly and compactly as we can. Disable 235127039Sphk * interrupts to avoid measuring our interrupt service time on 236127039Sphk * hw with quality clock sources. 237127039Sphk */ 238127039Sphk eflags = read_eflags(); 239127039Sphk disable_intr(); 240127039Sphk x = *pps_ap[0]; /* state, must be first, see below */ 241127039Sphk y = *pps_ap[1]; /* timer2 */ 242127039Sphk z = *pps_ap[2]; /* timer1 */ 243127039Sphk write_eflags(eflags); 244127039Sphk 245127039Sphk /* 246123015Sphk * Order is important here. We need to check the state of the GPIO 247123015Sphk * pin first, in order to avoid reading timer 1 right before the 248123015Sphk * state change. Technically pps_a may be zero in which case we 249123015Sphk * harmlessly read the REVID register and the contents of pps_d is 250123015Sphk * of no concern. 251123015Sphk */ 252123015Sphk 253127039Sphk i = x & pps_d; 254123015Sphk 255123015Sphk /* If state did not change or we don't have a GPIO pin, return */ 256123015Sphk if (i == state || pps_a == 0) 257109327Sphk return; 258123015Sphk 259109327Sphk state = i; 260123015Sphk 261123015Sphk /* If the state is "low", flip the echo GPIO and return. */ 262123015Sphk if (!i) { 263123015Sphk if (echo_a) 264124144Sphk mmcrptr[(echo_a ^ 0xc) / 2] = echo_d; 265109327Sphk return; 266123015Sphk } 267123015Sphk 268127039Sphk /* 269127039Sphk * Subtract timer1 from timer2 to compensate for time from the 270127039Sphk * edge until we read the counters. 271127039Sphk */ 272127039Sphk u = y - z; 273127039Sphk 274109327Sphk pps_capture(&elan_pps); 275127039Sphk elan_pps.capcount = u; 276109327Sphk pps_event(&elan_pps, PPS_CAPTUREASSERT); 277123015Sphk 278123015Sphk /* Twiddle echo bit */ 279123015Sphk if (echo_a) 280124144Sphk mmcrptr[echo_a / 2] = echo_d; 281109327Sphk} 282123015Sphk#endif /* CPU_ELAN_PPS */ 283109327Sphk 284102935Sphkstatic unsigned 285102935Sphkelan_get_timecount(struct timecounter *tc) 286102935Sphk{ 287123015Sphk 288123015Sphk /* Read timer2, end of story */ 289124144Sphk return (elan_mmcr->GPTMR2CNT); 290102935Sphk} 291102935Sphk 292109327Sphk/* 293109327Sphk * The Elan CPU can be run from a number of clock frequencies, this 294109327Sphk * allows you to override the default 33.3 MHZ. 295109327Sphk */ 296123015Sphk#ifndef CPU_ELAN_XTAL 297123015Sphk#define CPU_ELAN_XTAL 33333333 298109327Sphk#endif 299109327Sphk 300102935Sphkstatic struct timecounter elan_timecounter = { 301102935Sphk elan_get_timecount, 302109327Sphk NULL, 303102935Sphk 0xffff, 304123015Sphk CPU_ELAN_XTAL / 4, 305119715Sphk "ELAN", 306119715Sphk 1000 307102935Sphk}; 308102935Sphk 309109327Sphkstatic int 310109327Sphksysctl_machdep_elan_freq(SYSCTL_HANDLER_ARGS) 311109327Sphk{ 312109327Sphk u_int f; 313109327Sphk int error; 314109327Sphk 315109327Sphk f = elan_timecounter.tc_frequency * 4; 316170289Sdwmalone error = sysctl_handle_int(oidp, &f, 0, req); 317109327Sphk if (error == 0 && req->newptr != NULL) 318109327Sphk elan_timecounter.tc_frequency = (f + 3) / 4; 319109327Sphk return (error); 320109327Sphk} 321109327Sphk 322109327SphkSYSCTL_PROC(_machdep, OID_AUTO, elan_freq, CTLTYPE_UINT | CTLFLAG_RW, 323109327Sphk 0, sizeof (u_int), sysctl_machdep_elan_freq, "IU", ""); 324109327Sphk 325123015Sphk/* 326123015Sphk * Positively identifying the Elan can only be done through the PCI id of 327123015Sphk * the host-bridge, this function is called from i386/pci/pci_bus.c. 328123015Sphk */ 329102934Sphkvoid 330102934Sphkinit_AMD_Elan_sc520(void) 331102934Sphk{ 332102934Sphk u_int new; 333102934Sphk int i; 334102934Sphk 335124144Sphk mmcrptr = pmap_mapdev(0xfffef000, 0x1000); 336124144Sphk elan_mmcr = (volatile struct elan_mmcr *)mmcrptr; 337102934Sphk 338102934Sphk /*- 339102934Sphk * The i8254 is driven with a nonstandard frequency which is 340102934Sphk * derived thusly: 341102934Sphk * f = 32768 * 45 * 25 / 31 = 1189161.29... 342123015Sphk * We use the sysctl to get the i8254 (timecounter etc) into whack. 343102934Sphk */ 344102934Sphk 345102934Sphk new = 1189161; 346102934Sphk i = kernel_sysctlbyname(&thread0, "machdep.i8254_freq", 347136419Sphk NULL, 0, &new, sizeof new, NULL, 0); 348123015Sphk if (bootverbose || 1) 349103168Ssam printf("sysctl machdep.i8254_freq=%d returns %d\n", new, i); 350102935Sphk 351102935Sphk /* Start GP timer #2 and use it as timecounter, hz permitting */ 352124144Sphk elan_mmcr->GPTMR2MAXCMPA = 0; 353124144Sphk elan_mmcr->GPTMR2CTL = 0xc001; 354109327Sphk 355123015Sphk#ifdef CPU_ELAN_PPS 356109327Sphk /* Set up GP timer #1 as pps counter */ 357124144Sphk elan_mmcr->CSPFS &= ~0x10; 358124144Sphk elan_mmcr->GPTMR1CTL = 0x8000 | 0x4000 | 0x10 | 0x1; 359124144Sphk elan_mmcr->GPTMR1MAXCMPA = 0x0; 360124144Sphk elan_mmcr->GPTMR1MAXCMPB = 0x0; 361109327Sphk elan_pps.ppscap |= PPS_CAPTUREASSERT; 362109327Sphk pps_init(&elan_pps); 363109327Sphk#endif 364102935Sphk tc_init(&elan_timecounter); 365102934Sphk} 366102934Sphk 367103482Sphkstatic void 368126370Sphkelan_watchdog(void *foo __unused, u_int spec, int *error) 369101225Sphk{ 370165260Sn_hibma u_int u, v, w; 371111647Sphk static u_int cur; 372111647Sphk 373126370Sphk u = spec & WD_INTERVAL; 374165260Sn_hibma if (u > 0 && u <= 35) { 375111647Sphk u = imax(u - 5, 24); 376111647Sphk v = 2 << (u - 24); 377111647Sphk v |= 0xc000; 378111647Sphk 379111647Sphk /* 380111647Sphk * There is a bug in some silicon which prevents us from 381111647Sphk * writing to the WDTMRCTL register if the GP echo mode is 382111647Sphk * enabled. GP echo mode on the other hand is desirable 383111647Sphk * for other reasons. Save and restore the GP echo mode 384111647Sphk * around our hardware tom-foolery. 385111647Sphk */ 386165260Sn_hibma w = elan_mmcr->GPECHO; 387124144Sphk elan_mmcr->GPECHO = 0; 388111647Sphk if (v != cur) { 389111647Sphk /* Clear the ENB bit */ 390124144Sphk elan_mmcr->WDTMRCTL = 0x3333; 391124144Sphk elan_mmcr->WDTMRCTL = 0xcccc; 392124144Sphk elan_mmcr->WDTMRCTL = 0; 393111647Sphk 394111647Sphk /* Set new value */ 395124144Sphk elan_mmcr->WDTMRCTL = 0x3333; 396124144Sphk elan_mmcr->WDTMRCTL = 0xcccc; 397124144Sphk elan_mmcr->WDTMRCTL = v; 398111647Sphk cur = v; 399111647Sphk } else { 400111647Sphk /* Just reset timer */ 401124144Sphk elan_mmcr->WDTMRCTL = 0xaaaa; 402124144Sphk elan_mmcr->WDTMRCTL = 0x5555; 403111647Sphk } 404165260Sn_hibma elan_mmcr->GPECHO = w; 405126370Sphk *error = 0; 406126370Sphk } else { 407165260Sn_hibma w = elan_mmcr->GPECHO; 408124144Sphk elan_mmcr->GPECHO = 0; 409124144Sphk elan_mmcr->WDTMRCTL = 0x3333; 410124144Sphk elan_mmcr->WDTMRCTL = 0xcccc; 411124144Sphk elan_mmcr->WDTMRCTL = 0x4080; 412165260Sn_hibma elan_mmcr->WDTMRCTL = w; /* XXX What does this statement do? */ 413165260Sn_hibma elan_mmcr->GPECHO = w; 414111647Sphk cur = 0; 415111647Sphk } 416111647Sphk} 417111647Sphk 418111647Sphkstatic int 419201223Srnolandelan_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 420201223Srnoland int nprot, vm_memattr_t *memattr) 421126370Sphk{ 422126370Sphk 423126370Sphk if (offset >= 0x1000) 424126370Sphk return (-1); 425126370Sphk *paddr = 0xfffef000; 426126370Sphk return (0); 427126370Sphk} 428126370Sphkstatic int 429130585Sphkelan_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr) 430101225Sphk{ 431109327Sphk int error; 432109327Sphk 433126370Sphk error = ENOIOCTL; 434123015Sphk 435123015Sphk#ifdef CPU_ELAN_PPS 436123015Sphk if (pps_a != 0) 437123015Sphk error = pps_ioctl(cmd, arg, &elan_pps); 438109327Sphk /* 439109327Sphk * We only want to incur the overhead of the PPS polling if we 440109327Sphk * are actually asked to timestamp. 441109327Sphk */ 442123015Sphk if (elan_pps.ppsparam.mode & PPS_CAPTUREASSERT) { 443109327Sphk elan_timecounter.tc_poll_pps = elan_poll_pps; 444123015Sphk } else { 445109327Sphk elan_timecounter.tc_poll_pps = NULL; 446123015Sphk } 447126370Sphk if (error != ENOIOCTL) 448109327Sphk return (error); 449123015Sphk#endif 450109327Sphk 451109327Sphk return(error); 452101225Sphk} 453101225Sphk 454126370Sphkstatic struct cdevsw elan_cdevsw = { 455126370Sphk .d_version = D_VERSION, 456126370Sphk .d_flags = D_NEEDGIANT, 457126370Sphk .d_ioctl = elan_ioctl, 458126370Sphk .d_mmap = elan_mmap, 459126370Sphk .d_name = "elan", 460126370Sphk}; 461126370Sphk 462126370Sphkstatic void 463126370Sphkelan_drvinit(void) 464126370Sphk{ 465126370Sphk 466148231Sphk#ifdef CPU_SOEKRIS 467148231Sphk#define BIOS_OEM_MAXLEN 72 468148231Sphk static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; 469148231Sphk#endif /* CPU_SOEKRIS */ 470148231Sphk 471126370Sphk /* If no elan found, just return */ 472126370Sphk if (mmcrptr == NULL) 473126370Sphk return; 474126370Sphk 475126370Sphk printf("Elan-mmcr driver: MMCR at %p.%s\n", 476126370Sphk mmcrptr, 477126370Sphk#ifdef CPU_ELAN_PPS 478126370Sphk " PPS support." 479126370Sphk#else 480126370Sphk "" 481126370Sphk#endif 482126370Sphk ); 483126370Sphk 484126370Sphk make_dev(&elan_cdevsw, 0, 485126370Sphk UID_ROOT, GID_WHEEL, 0600, "elan-mmcr"); 486126370Sphk 487126370Sphk#ifdef CPU_SOEKRIS 488148231Sphk if ( bios_oem_strings(&bios_soekris, bios_oem, BIOS_OEM_MAXLEN) > 0 ) 489148231Sphk printf("Elan-mmcr %s\n", bios_oem); 490148231Sphk 491126370Sphk /* Create the error LED on GPIO9 */ 492126370Sphk led_cookie[9] = 0x02000c34; 493126370Sphk led_dev[9] = led_create(gpio_led, &led_cookie[9], "error"); 494126370Sphk 495126370Sphk /* Disable the unavailable GPIO pins */ 496126370Sphk strcpy(gpio_config, "-----....--..--------..---------"); 497126370Sphk#else /* !CPU_SOEKRIS */ 498126370Sphk /* We don't know which pins are available so enable them all */ 499126370Sphk strcpy(gpio_config, "................................"); 500126370Sphk#endif /* CPU_SOEKRIS */ 501126370Sphk 502126370Sphk EVENTHANDLER_REGISTER(watchdog_list, elan_watchdog, NULL, 0); 503126370Sphk} 504126370Sphk 505126370SphkSYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE, elan_drvinit, NULL); 506126370Sphk 507