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