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