elan-mmcr.c revision 109327
1101225Sphk/* 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 * $FreeBSD: head/sys/i386/i386/elan-mmcr.c 109327 2003-01-15 20:15:33Z phk $ 10101225Sphk * 11102934Sphk * The AMD Elan sc520 is a system-on-chip gadget which is used in embedded 12102934Sphk * kind of things, see www.soekris.com for instance, and it has a few quirks 13102934Sphk * we need to deal with. 14102934Sphk * Unfortunately we cannot identify the gadget by CPUID output because it 15102934Sphk * depends on strapping options and only the stepping field may be useful 16102934Sphk * and those are undocumented from AMDs side. 17102934Sphk * 18102934Sphk * So instead we recognize the on-chip host-PCI bridge and call back from 19102934Sphk * sys/i386/pci/pci_bus.c to here if we find it. 20109327Sphk * 21109327Sphk * #ifdef ELAN_PPS 22109327Sphk * The Elan has three general purpose counters, which when used just right 23109327Sphk * can hardware timestamp external events with approx 250 nanoseconds 24109327Sphk * resolution _and_ precision. Connect the signal to TMR1IN and PIO7. 25109327Sphk * (You can use any PIO pin, look for PIO7 to change this). Use the 26109327Sphk * PPS-API on the /dev/elan-mmcr device. 27109327Sphk * #endif ELAN_PPS 28101225Sphk */ 29102934Sphk 30101225Sphk#include <sys/param.h> 31101225Sphk#include <sys/systm.h> 32101225Sphk#include <sys/kernel.h> 33101225Sphk#include <sys/conf.h> 34102934Sphk#include <sys/sysctl.h> 35102934Sphk#include <sys/timetc.h> 36102934Sphk#include <sys/proc.h> 37103482Sphk#include <sys/uio.h> 38103482Sphk#include <sys/lock.h> 39103482Sphk#include <sys/mutex.h> 40103482Sphk#include <sys/malloc.h> 41109327Sphk#include <sys/sysctl.h> 42109327Sphk#include <sys/timepps.h> 43101225Sphk 44101225Sphk#include <machine/md_var.h> 45101225Sphk 46102934Sphk#include <vm/vm.h> 47102934Sphk#include <vm/pmap.h> 48102934Sphk 49102934Sphkuint16_t *elan_mmcr; 50102934Sphk 51103482Sphk/* Relating to the /dev/soekris-errled */ 52103482Sphkstatic struct mtx errled_mtx; 53103482Sphkstatic char *errled; 54103482Sphkstatic struct callout_handle errled_h = CALLOUT_HANDLE_INITIALIZER(&errled_h); 55103482Sphkstatic void timeout_errled(void *); 56102935Sphk 57109327Sphk#ifdef ELAN_PPS 58109327Sphk/* Relating to the PPS-api */ 59109327Sphkstatic struct pps_state elan_pps; 60109327Sphk 61109327Sphkstatic void 62109327Sphkelan_poll_pps(struct timecounter *tc) 63109327Sphk{ 64109327Sphk static int state; 65109327Sphk int i; 66109327Sphk 67109327Sphk /* XXX: This is PIO7, change to your preference */ 68109327Sphk i = elan_mmcr[0xc30 / 2] & 0x80; 69109327Sphk if (i == state) 70109327Sphk return; 71109327Sphk state = i; 72109327Sphk if (!state) 73109327Sphk return; 74109327Sphk pps_capture(&elan_pps); 75109327Sphk elan_pps.capcount = 76109327Sphk (elan_mmcr[0xc84 / 2] - elan_mmcr[0xc7c / 2]) & 0xffff; 77109327Sphk pps_event(&elan_pps, PPS_CAPTUREASSERT); 78109327Sphk} 79109327Sphk#endif /* ELAN_PPS */ 80109327Sphk 81102935Sphkstatic unsigned 82102935Sphkelan_get_timecount(struct timecounter *tc) 83102935Sphk{ 84102935Sphk return (elan_mmcr[0xc84 / 2]); 85102935Sphk} 86102935Sphk 87109327Sphk/* 88109327Sphk * The Elan CPU can be run from a number of clock frequencies, this 89109327Sphk * allows you to override the default 33.3 MHZ. 90109327Sphk */ 91109327Sphk#ifndef ELAN_XTAL 92109327Sphk#define ELAN_XTAL 33333333 93109327Sphk#endif 94109327Sphk 95102935Sphkstatic struct timecounter elan_timecounter = { 96102935Sphk elan_get_timecount, 97109327Sphk NULL, 98102935Sphk 0xffff, 99109327Sphk ELAN_XTAL / 4, 100102935Sphk "ELAN" 101102935Sphk}; 102102935Sphk 103109327Sphkstatic int 104109327Sphksysctl_machdep_elan_freq(SYSCTL_HANDLER_ARGS) 105109327Sphk{ 106109327Sphk u_int f; 107109327Sphk int error; 108109327Sphk 109109327Sphk f = elan_timecounter.tc_frequency * 4; 110109327Sphk error = sysctl_handle_int(oidp, &f, sizeof(f), req); 111109327Sphk if (error == 0 && req->newptr != NULL) 112109327Sphk elan_timecounter.tc_frequency = (f + 3) / 4; 113109327Sphk return (error); 114109327Sphk} 115109327Sphk 116109327SphkSYSCTL_PROC(_machdep, OID_AUTO, elan_freq, CTLTYPE_UINT | CTLFLAG_RW, 117109327Sphk 0, sizeof (u_int), sysctl_machdep_elan_freq, "IU", ""); 118109327Sphk 119102934Sphkvoid 120102934Sphkinit_AMD_Elan_sc520(void) 121102934Sphk{ 122102934Sphk u_int new; 123102934Sphk int i; 124102934Sphk 125103168Ssam if (bootverbose) 126103168Ssam printf("Doing h0h0magic for AMD Elan sc520\n"); 127102934Sphk elan_mmcr = pmap_mapdev(0xfffef000, 0x1000); 128102934Sphk 129102934Sphk /*- 130102934Sphk * The i8254 is driven with a nonstandard frequency which is 131102934Sphk * derived thusly: 132102934Sphk * f = 32768 * 45 * 25 / 31 = 1189161.29... 133102934Sphk * We use the sysctl to get the timecounter etc into whack. 134102934Sphk */ 135102934Sphk 136102934Sphk new = 1189161; 137102934Sphk i = kernel_sysctlbyname(&thread0, "machdep.i8254_freq", 138102934Sphk NULL, 0, 139102934Sphk &new, sizeof new, 140102934Sphk NULL); 141103168Ssam if (bootverbose) 142103168Ssam printf("sysctl machdep.i8254_freq=%d returns %d\n", new, i); 143102935Sphk 144102935Sphk /* Start GP timer #2 and use it as timecounter, hz permitting */ 145102935Sphk elan_mmcr[0xc82 / 2] = 0xc001; 146109327Sphk 147109327Sphk#ifdef ELAN_PPS 148109327Sphk /* Set up GP timer #1 as pps counter */ 149109327Sphk elan_mmcr[0xc24 / 2] &= ~0x10; 150109327Sphk elan_mmcr[0xc7a / 2] = 0x8000 | 0x4000 | 0x10 | 0x1; 151109327Sphk elan_pps.ppscap |= PPS_CAPTUREASSERT; 152109327Sphk pps_init(&elan_pps); 153109327Sphk#endif 154109327Sphk 155102935Sphk tc_init(&elan_timecounter); 156102934Sphk} 157102934Sphk 158102934Sphk 159101225Sphk/* 160101225Sphk * Device driver initialization stuff 161101225Sphk */ 162101225Sphk 163103482Sphkstatic d_write_t elan_write; 164101225Sphkstatic d_ioctl_t elan_ioctl; 165101225Sphkstatic d_mmap_t elan_mmap; 166101225Sphk 167103482Sphk#define ELAN_MMCR 0 168103482Sphk#define ELAN_ERRLED 1 169103482Sphk 170101225Sphk#define CDEV_MAJOR 100 /* Share with xrpu */ 171101225Sphkstatic struct cdevsw elan_cdevsw = { 172103482Sphk /* open */ nullopen, 173103482Sphk /* close */ nullclose, 174101225Sphk /* read */ noread, 175103482Sphk /* write */ elan_write, 176101225Sphk /* ioctl */ elan_ioctl, 177101225Sphk /* poll */ nopoll, 178101225Sphk /* mmap */ elan_mmap, 179101225Sphk /* strategy */ nostrategy, 180101225Sphk /* name */ "elan", 181101225Sphk /* maj */ CDEV_MAJOR, 182101225Sphk /* dump */ nodump, 183101225Sphk /* psize */ nopsize, 184101225Sphk /* flags */ 0, 185101225Sphk}; 186101225Sphk 187103482Sphkstatic void 188103482Sphkelan_drvinit(void) 189101225Sphk{ 190103482Sphk 191103482Sphk if (elan_mmcr == NULL) 192103482Sphk return; 193103482Sphk printf("Elan-mmcr driver: MMCR at %p\n", elan_mmcr); 194103482Sphk make_dev(&elan_cdevsw, ELAN_MMCR, 195103482Sphk UID_ROOT, GID_WHEEL, 0600, "elan-mmcr"); 196103482Sphk make_dev(&elan_cdevsw, ELAN_ERRLED, 197103482Sphk UID_ROOT, GID_WHEEL, 0600, "soekris-errled"); 198103482Sphk mtx_init(&errled_mtx, "Elan-errled", MTX_DEF, 0); 199103482Sphk return; 200101225Sphk} 201101225Sphk 202103482SphkSYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE+CDEV_MAJOR,elan_drvinit,NULL); 203103482Sphk 204103482Sphk#define LED_ON() do {elan_mmcr[0xc34 / 2] = 0x200;} while(0) 205103482Sphk#define LED_OFF() do {elan_mmcr[0xc38 / 2] = 0x200;} while(0) 206103482Sphk 207103482Sphkstatic void 208103482Sphktimeout_errled(void *p) 209103482Sphk{ 210103482Sphk static enum {NOTHING, FLASH, DIGIT} mode; 211103482Sphk static int count, cnt2, state; 212103482Sphk 213103482Sphk mtx_lock(&errled_mtx); 214103482Sphk if (p != NULL) { 215103482Sphk mode = NOTHING; 216103482Sphk /* Our instructions changed */ 217103482Sphk if (*errled == '1') { /* Turn LED on */ 218103482Sphk LED_ON(); 219103482Sphk } else if (*errled == '0') { /* Turn LED off */ 220103482Sphk LED_OFF(); 221103482Sphk } else if (*errled == 'f') { /* Flash */ 222103482Sphk mode = FLASH; 223103482Sphk cnt2 = 10; 224103482Sphk if (errled[1] >= '1' && errled[1] <= '9') 225103482Sphk cnt2 = errled[1] - '0'; 226103482Sphk cnt2 = hz / cnt2; 227103482Sphk LED_ON(); 228103482Sphk errled_h = timeout(timeout_errled, NULL, cnt2); 229103482Sphk } else if (*errled == 'd') { /* Digit */ 230103482Sphk mode = DIGIT; 231103482Sphk count = 0; 232103482Sphk cnt2 = 0; 233103482Sphk state = 0; 234103482Sphk LED_OFF(); 235103482Sphk errled_h = timeout(timeout_errled, NULL, hz/10); 236103482Sphk } 237103482Sphk } else if (mode == FLASH) { 238103482Sphk if (count) 239103482Sphk LED_ON(); 240103482Sphk else 241103482Sphk LED_OFF(); 242103482Sphk count = !count; 243103482Sphk errled_h = timeout(timeout_errled, NULL, cnt2); 244103482Sphk } else if (mode == DIGIT) { 245103482Sphk if (cnt2 > 0) { 246103482Sphk if (state) { 247103482Sphk LED_OFF(); 248103482Sphk state = 0; 249103482Sphk cnt2--; 250103482Sphk } else { 251103482Sphk LED_ON(); 252103482Sphk state = 1; 253103482Sphk } 254103482Sphk errled_h = timeout(timeout_errled, NULL, hz/5); 255103482Sphk } else { 256103482Sphk do 257103482Sphk count++; 258103482Sphk while (errled[count] != '\0' && 259103482Sphk (errled[count] < '0' || errled[count] > '9')); 260103482Sphk if (errled[count] == '\0') { 261103482Sphk count = 0; 262103482Sphk errled_h = timeout(timeout_errled, NULL, hz * 2); 263103482Sphk } else { 264103482Sphk cnt2 = errled[count] - '0'; 265103482Sphk state = 0; 266103482Sphk errled_h = timeout(timeout_errled, NULL, hz); 267103482Sphk } 268103482Sphk } 269103482Sphk } 270103482Sphk mtx_unlock(&errled_mtx); 271103482Sphk return; 272103482Sphk} 273103482Sphk 274103482Sphk/* 275103482Sphk * The write function is used for the error-LED. 276103482Sphk */ 277103482Sphk 278101225Sphkstatic int 279103482Sphkelan_write(dev_t dev, struct uio *uio, int ioflag) 280103482Sphk{ 281103482Sphk int error; 282103482Sphk char *s, *q; 283103482Sphk 284103482Sphk if (minor(dev) != ELAN_ERRLED) 285103482Sphk return (EOPNOTSUPP); 286103482Sphk 287103482Sphk if (uio->uio_resid > 512) 288103482Sphk return (EINVAL); 289103482Sphk s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK); 290103482Sphk if (s == NULL) 291103482Sphk return (ENOMEM); 292103482Sphk untimeout(timeout_errled, NULL, errled_h); 293103482Sphk s[uio->uio_resid] = '\0'; 294103482Sphk error = uiomove(s, uio->uio_resid, uio); 295103482Sphk if (error) { 296103482Sphk free(s, M_DEVBUF); 297103482Sphk return (error); 298103482Sphk } 299103482Sphk mtx_lock(&errled_mtx); 300103482Sphk q = errled; 301103482Sphk errled = s; 302103482Sphk mtx_unlock(&errled_mtx); 303103482Sphk if (q != NULL) 304103482Sphk free(q, M_DEVBUF); 305103482Sphk timeout_errled(errled); 306103482Sphk 307103482Sphk return(0); 308101225Sphk} 309101225Sphk 310101225Sphkstatic int 311101225Sphkelan_mmap(dev_t dev, vm_offset_t offset, int nprot) 312101225Sphk{ 313103482Sphk 314103482Sphk if (minor(dev) != ELAN_MMCR) 315103482Sphk return (EOPNOTSUPP); 316101225Sphk if (offset >= 0x1000) 317101225Sphk return (-1); 318101225Sphk return (i386_btop(0xfffef000)); 319101225Sphk} 320101225Sphk 321101225Sphkstatic int 322101225Sphkelan_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr) 323101225Sphk{ 324109327Sphk int error; 325109327Sphk 326109327Sphk error = ENOTTY; 327109327Sphk#ifdef ELAN_PPS 328109327Sphk error = pps_ioctl(cmd, arg, &elan_pps); 329109327Sphk /* 330109327Sphk * We only want to incur the overhead of the PPS polling if we 331109327Sphk * are actually asked to timestamp. 332109327Sphk */ 333109327Sphk if (elan_pps.ppsparam.mode & PPS_CAPTUREASSERT) 334109327Sphk elan_timecounter.tc_poll_pps = elan_poll_pps; 335109327Sphk else 336109327Sphk elan_timecounter.tc_poll_pps = NULL; 337109327Sphk if (error != ENOTTY) 338109327Sphk return (error); 339109327Sphk#endif /* ELAN_PPS */ 340109327Sphk 341109327Sphk /* Other future ioctl handling here */ 342109327Sphk return(error); 343101225Sphk} 344101225Sphk 345