geode.c revision 331722
1/*-
2 * Copyright (c) 2003-2004 Poul-Henning Kamp
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/sys/i386/i386/geode.c 331722 2018-03-29 02:50:57Z eadler $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/timetc.h>
33#include <sys/bus.h>
34#include <sys/kernel.h>
35#include <sys/module.h>
36#include <sys/watchdog.h>
37#include <dev/pci/pcireg.h>
38#include <dev/pci/pcivar.h>
39#include <dev/led/led.h>
40#include <machine/pc/bios.h>
41
42static struct bios_oem bios_soekris = {
43    { 0xf0000, 0xf1000 },
44    {
45	{ "Soekris", 0, 8 },		/* Soekris Engineering. */
46	{ "net4", 0, 8 },		/* net45xx */
47	{ "comBIOS", 0, 54 },		/* comBIOS ver. 1.26a  20040819 ... */
48	{ NULL, 0, 0 },
49    }
50};
51
52static struct bios_oem bios_soekris_55 = {
53    { 0xf0000, 0xf1000 },
54    {
55	{ "Soekris", 0, 8 },		/* Soekris Engineering. */
56	{ "net5", 0, 8 },		/* net5xxx */
57	{ "comBIOS", 0, 54 },		/* comBIOS ver. 1.26a  20040819 ... */
58	{ NULL, 0, 0 },
59    }
60};
61
62static struct bios_oem bios_pcengines = {
63    { 0xf9000, 0xfa000 },
64    {
65	{ "PC Engines WRAP", 0, 28 },	/* PC Engines WRAP.1C v1.03 */
66	{ "tinyBIOS", 0, 28 },		/* tinyBIOS V1.4a (C)1997-2003 */
67	{ NULL, 0, 0 },
68    }
69};
70
71static struct bios_oem bios_pcengines_55 = {
72    { 0xf9000, 0xfa000 },
73    {
74	{ "PC Engines ALIX", 0, 28 },	/* PC Engines ALIX */
75	{ "tinyBIOS", 0, 28 },		/* tinyBIOS V1.4a (C)1997-2005 */
76	{ NULL, 0, 0 },
77    }
78};
79
80static struct bios_oem bios_advantech = {
81    { 0xfe000, 0xff000 },
82    {
83	{ "**** PCM-582", 5, 33 },	/* PCM-5823 BIOS V1.12 ... */
84	{ "GXm-Cx5530",	-11, 35 },	/* 06/07/2002-GXm-Cx5530... */
85	{ NULL, 0, 0 },
86    }
87};
88
89static unsigned	cba;
90static unsigned	gpio;
91static unsigned	geode_counter;
92
93static struct cdev *led1, *led2, *led3;
94static int 	led1b, led2b, led3b;
95
96static void
97led_func(void *ptr, int onoff)
98{
99	uint32_t u;
100	int bit;
101
102	bit = *(int *)ptr;
103	if (bit < 0) {
104		bit = -bit;
105		onoff = !onoff;
106	}
107
108	u = inl(gpio + 4);
109	if (onoff)
110		u |= 1 << bit;
111	else
112		u &= ~(1 << bit);
113	outl(gpio, u);
114}
115
116static void
117cs5536_led_func(void *ptr, int onoff)
118{
119	int bit;
120	uint16_t a;
121
122	bit = *(int *)ptr;
123	if (bit < 0) {
124		bit = -bit;
125		onoff = !onoff;
126	}
127
128	a = rdmsr(0x5140000c);
129	if (bit >= 16) {
130		a += 0x80;
131		bit -= 16;
132	}
133
134	if (onoff)
135		outl(a, 1 << bit);
136	else
137		outl(a, 1 << (bit + 16));
138}
139
140
141static unsigned
142geode_get_timecount(struct timecounter *tc)
143{
144	return (inl(geode_counter));
145}
146
147static struct timecounter geode_timecounter = {
148	geode_get_timecount,
149	NULL,
150	0xffffffff,
151	27000000,
152	"Geode",
153	1000
154};
155
156static uint64_t
157geode_cputicks(void)
158{
159	unsigned c;
160	static unsigned last;
161	static uint64_t offset;
162
163	c = inl(geode_counter);
164	if (c < last)
165		offset += (1LL << 32);
166	last = c;
167	return (offset | c);
168}
169
170/*
171 * The GEODE watchdog runs from a 32kHz frequency.  One period of that is
172 * 31250 nanoseconds which we round down to 2^14 nanoseconds.  The watchdog
173 * consists of a power-of-two prescaler and a 16 bit counter, so the math
174 * is quite simple.  The max timeout is 14 + 16 + 13 = 2^43 nsec ~= 2h26m.
175 */
176static void
177geode_watchdog(void *foo __unused, u_int cmd, int *error)
178{
179	u_int u, p, r;
180
181	u = cmd & WD_INTERVAL;
182	if (u >= 14 && u <= 43) {
183		u -= 14;
184		if (u > 16) {
185			p = u - 16;
186			u -= p;
187		} else {
188			p = 0;
189		}
190		if (u == 16)
191			u = (1 << u) - 1;
192		else
193			u = 1 << u;
194		r = inw(cba + 2) & 0xff00;
195		outw(cba + 2, p | 0xf0 | r);
196		outw(cba, u);
197		*error = 0;
198	} else {
199		outw(cba, 0);
200	}
201}
202
203/*
204 * We run MFGPT0 off the 32kHz frequency and prescale by 16384 giving a
205 * period of half a second.
206 * Range becomes 2^30 (= 1 sec) to 2^44 (almost 5 hours)
207 */
208static void
209cs5536_watchdog(void *foo __unused, u_int cmd, int *error)
210{
211	u_int u, p, s;
212	uint16_t a;
213	uint32_t m;
214
215	a = rdmsr(0x5140000d);
216
217	u = cmd & WD_INTERVAL;
218	if (u >= 30 && u <= 44) {
219		p = 1 << (u - 29);
220
221		/* Set up MFGPT0, 32khz, prescaler 16k, C2 event */
222		outw(a + 6, 0x030e);
223		/* set comparator 2 */
224		outw(a + 2, p);
225		/* reset counter */
226		outw(a + 4, 0);
227		/* Arm reset mechanism */
228		m = rdmsr(0x51400029);
229		m |= (1 << 24);
230		wrmsr(0x51400029, m);
231		/* Start counter */
232		outw(a + 6, 0x8000);
233
234		*error = 0;
235	} else {
236		/*
237		 * MFGPT_SETUP is write-once
238		 * Check if the counter has been setup
239		 */
240		s = inw(a + 6);
241		if (s & (1 << 12)) {
242			/* Stop and reset counter */
243			outw(a + 6, 0);
244			outw(a + 4, 0);
245		}
246	}
247}
248
249/*
250 * The Advantech PCM-582x watchdog expects 0x1 at I/O port 0x0443
251 * every 1.6 secs +/- 30%. Writing 0x0 disables the watchdog
252 * NB: reading the I/O port enables the timer as well
253 */
254static void
255advantech_watchdog(void *foo __unused, u_int cmd, int *error)
256{
257	u_int u;
258
259	u = cmd & WD_INTERVAL;
260	if (u > 0 && u <= WD_TO_1SEC) {
261		outb(0x0443, 1);
262		*error = 0;
263	} else {
264		outb(0x0443, 0);
265	}
266}
267
268static int
269geode_probe(device_t self)
270{
271#define BIOS_OEM_MAXLEN 80
272	static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0";
273
274	switch (pci_get_devid(self)) {
275	case 0x0515100b:
276		if (geode_counter == 0) {
277			/*
278			 * The address of the CBA is written to this register
279			 * by the bios, see p161 in data sheet.
280			 */
281			cba = pci_read_config(self, 0x64, 4);
282			if (bootverbose)
283				printf("Geode CBA@ 0x%x\n", cba);
284			geode_counter = cba + 0x08;
285			outl(cba + 0x0d, 2);
286			if (bootverbose)
287				printf("Geode rev: %02x %02x\n",
288					inb(cba + 0x3c), inb(cba + 0x3d));
289			tc_init(&geode_timecounter);
290			EVENTHANDLER_REGISTER(watchdog_list, geode_watchdog,
291			    NULL, 0);
292			set_cputicker(geode_cputicks, 27000000, 0);
293		}
294		break;
295	case 0x0510100b:
296		gpio = pci_read_config(self, PCIR_BAR(0), 4);
297		gpio &= ~0x1f;
298		if (bootverbose)
299			printf("Geode GPIO@ = %x\n", gpio);
300		if (bios_oem_strings(&bios_soekris,
301		    bios_oem, sizeof bios_oem) > 0 ) {
302			led1b = 20;
303			led1 = led_create(led_func, &led1b, "error");
304		} else if (bios_oem_strings(&bios_pcengines,
305		    bios_oem, sizeof bios_oem) > 0 ) {
306			led1b = -2;
307			led2b = -3;
308			led3b = -18;
309			led1 = led_create(led_func, &led1b, "led1");
310			led2 = led_create(led_func, &led2b, "led2");
311			led3 = led_create(led_func, &led3b, "led3");
312			/*
313		 	* Turn on first LED so we don't make
314			* people think their box just died.
315		 	*/
316			led_func(&led1b, 1);
317		}
318		if (*bios_oem)
319			printf("Geode %s\n", bios_oem);
320		break;
321	case 0x01011078:
322		if (bios_oem_strings(&bios_advantech,
323		    bios_oem, sizeof bios_oem) > 0 ) {
324			printf("Geode %s\n", bios_oem);
325			EVENTHANDLER_REGISTER(watchdog_list, advantech_watchdog,
326			    NULL, 0);
327		}
328		break;
329	case 0x20801022:
330		if (bios_oem_strings(&bios_soekris_55,
331		    bios_oem, sizeof bios_oem) > 0 ) {
332			led1b = 6;
333			led1 = led_create(cs5536_led_func, &led1b, "error");
334		} else if (bios_oem_strings(&bios_pcengines_55,
335		    bios_oem, sizeof bios_oem) > 0 ) {
336			led1b = -6;
337			led2b = -25;
338			led3b = -27;
339			led1 = led_create(cs5536_led_func, &led1b, "led1");
340			led2 = led_create(cs5536_led_func, &led2b, "led2");
341			led3 = led_create(cs5536_led_func, &led3b, "led3");
342			/*
343		 	* Turn on first LED so we don't make
344			* people think their box just died.
345		 	*/
346			cs5536_led_func(&led1b, 1);
347		}
348		if (*bios_oem)
349			printf("Geode LX: %s\n", bios_oem);
350		if (bootverbose)
351			printf("MFGPT bar: %jx\n", rdmsr(0x5140000d));
352		EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, NULL, 0);
353		break;
354	}
355	return (ENXIO);
356}
357
358static int
359geode_attach(device_t self)
360{
361
362	return(ENODEV);
363}
364
365static device_method_t geode_methods[] = {
366	/* Device interface */
367	DEVMETHOD(device_probe,		geode_probe),
368	DEVMETHOD(device_attach,	geode_attach),
369	DEVMETHOD(device_suspend,	bus_generic_suspend),
370	DEVMETHOD(device_resume,	bus_generic_resume),
371	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
372	{0, 0}
373};
374
375static driver_t geode_driver = {
376	"geode",
377	geode_methods,
378	0,
379};
380
381static devclass_t geode_devclass;
382
383DRIVER_MODULE(geode, pci, geode_driver, geode_devclass, 0, 0);
384