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