1119608Sphk/*-
2126349Sphk * Copyright (c) 2003-2004 Poul-Henning Kamp
3119608Sphk * All rights reserved.
4119608Sphk *
5119608Sphk * Redistribution and use in source and binary forms, with or without
6119608Sphk * modification, are permitted provided that the following conditions
7119608Sphk * are met:
8119608Sphk * 1. Redistributions of source code must retain the above copyright
9119608Sphk *    notice, this list of conditions and the following disclaimer.
10119608Sphk * 2. Redistributions in binary form must reproduce the above copyright
11119608Sphk *    notice, this list of conditions and the following disclaimer in the
12119608Sphk *    documentation and/or other materials provided with the distribution.
13119608Sphk *
14119608Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15119608Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16119608Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17119608Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18119608Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19119608Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20119608Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21119608Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22119608Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23119608Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24119608Sphk * SUCH DAMAGE.
25119608Sphk */
26119608Sphk
27119608Sphk#include <sys/cdefs.h>
28119608Sphk__FBSDID("$FreeBSD: releng/10.3/sys/i386/i386/geode.c 208111 2010-05-15 10:31:11Z phk $");
29119608Sphk
30119608Sphk#include <sys/param.h>
31119608Sphk#include <sys/systm.h>
32119608Sphk#include <sys/timetc.h>
33119608Sphk#include <sys/bus.h>
34119608Sphk#include <sys/kernel.h>
35130041Sphk#include <sys/module.h>
36126387Sphk#include <sys/watchdog.h>
37126349Sphk#include <dev/pci/pcireg.h>
38119608Sphk#include <dev/pci/pcivar.h>
39130041Sphk#include <dev/led/led.h>
40130041Sphk#include <machine/pc/bios.h>
41119608Sphk
42148231Sphkstatic struct bios_oem bios_soekris = {
43176153Sphk    { 0xf0000, 0xf1000 },
44176153Sphk    {
45176153Sphk	{ "Soekris", 0, 8 },		/* Soekris Engineering. */
46176153Sphk	{ "net4", 0, 8 },		/* net45xx */
47176153Sphk	{ "comBIOS", 0, 54 },		/* comBIOS ver. 1.26a  20040819 ... */
48176153Sphk	{ NULL, 0, 0 },
49176153Sphk    }
50148231Sphk};
51148231Sphk
52172216Sphkstatic struct bios_oem bios_soekris_55 = {
53176153Sphk    { 0xf0000, 0xf1000 },
54176153Sphk    {
55176153Sphk	{ "Soekris", 0, 8 },		/* Soekris Engineering. */
56176153Sphk	{ "net5", 0, 8 },		/* net5xxx */
57176153Sphk	{ "comBIOS", 0, 54 },		/* comBIOS ver. 1.26a  20040819 ... */
58176153Sphk	{ NULL, 0, 0 },
59176153Sphk    }
60172216Sphk};
61172216Sphk
62148231Sphkstatic struct bios_oem bios_pcengines = {
63176153Sphk    { 0xf9000, 0xfa000 },
64176153Sphk    {
65176153Sphk	{ "PC Engines WRAP", 0, 28 },	/* PC Engines WRAP.1C v1.03 */
66176153Sphk	{ "tinyBIOS", 0, 28 },		/* tinyBIOS V1.4a (C)1997-2003 */
67176153Sphk	{ NULL, 0, 0 },
68176153Sphk    }
69148231Sphk};
70148231Sphk
71176153Sphkstatic struct bios_oem bios_pcengines_55 = {
72176153Sphk    { 0xf9000, 0xfa000 },
73176153Sphk    {
74176153Sphk	{ "PC Engines ALIX", 0, 28 },	/* PC Engines ALIX */
75176153Sphk	{ "tinyBIOS", 0, 28 },		/* tinyBIOS V1.4a (C)1997-2005 */
76176153Sphk	{ NULL, 0, 0 },
77176153Sphk    }
78176153Sphk};
79176153Sphk
80148231Sphkstatic struct bios_oem bios_advantech = {
81176153Sphk    { 0xfe000, 0xff000 },
82176153Sphk    {
83176153Sphk	{ "**** PCM-582", 5, 33 },	/* PCM-5823 BIOS V1.12 ... */
84176153Sphk	{ "GXm-Cx5530",	-11, 35 },	/* 06/07/2002-GXm-Cx5530... */
85176153Sphk	{ NULL, 0, 0 },
86176153Sphk    }
87148231Sphk};
88148231Sphk
89119608Sphkstatic unsigned	cba;
90126349Sphkstatic unsigned	gpio;
91119608Sphkstatic unsigned	geode_counter;
92119608Sphk
93130585Sphkstatic struct cdev *led1, *led2, *led3;
94130041Sphkstatic int 	led1b, led2b, led3b;
95126349Sphk
96126349Sphkstatic void
97130041Sphkled_func(void *ptr, int onoff)
98126349Sphk{
99126349Sphk	uint32_t u;
100130041Sphk	int bit;
101126349Sphk
102130041Sphk	bit = *(int *)ptr;
103130041Sphk	if (bit < 0) {
104130041Sphk		bit = -bit;
105130041Sphk		onoff = !onoff;
106130041Sphk	}
107130041Sphk
108126349Sphk	u = inl(gpio + 4);
109126349Sphk	if (onoff)
110130041Sphk		u |= 1 << bit;
111126349Sphk	else
112130041Sphk		u &= ~(1 << bit);
113126349Sphk	outl(gpio, u);
114126349Sphk}
115126349Sphk
116172216Sphkstatic void
117172216Sphkcs5536_led_func(void *ptr, int onoff)
118172216Sphk{
119172216Sphk	int bit;
120172216Sphk	uint16_t a;
121126349Sphk
122172216Sphk	bit = *(int *)ptr;
123172216Sphk	if (bit < 0) {
124172216Sphk		bit = -bit;
125172216Sphk		onoff = !onoff;
126172216Sphk	}
127172216Sphk
128172216Sphk	a = rdmsr(0x5140000c);
129176153Sphk	if (bit >= 16) {
130176153Sphk		a += 0x80;
131176153Sphk		bit -= 16;
132176153Sphk	}
133176153Sphk
134172216Sphk	if (onoff)
135172216Sphk		outl(a, 1 << bit);
136172216Sphk	else
137172216Sphk		outl(a, 1 << (bit + 16));
138172216Sphk}
139172216Sphk
140172216Sphk
141119608Sphkstatic unsigned
142119608Sphkgeode_get_timecount(struct timecounter *tc)
143119608Sphk{
144119608Sphk	return (inl(geode_counter));
145119608Sphk}
146119608Sphk
147119608Sphkstatic struct timecounter geode_timecounter = {
148119608Sphk	geode_get_timecount,
149119608Sphk	NULL,
150119608Sphk	0xffffffff,
151119608Sphk	27000000,
152119608Sphk	"Geode",
153119608Sphk	1000
154119608Sphk};
155119608Sphk
156155534Sphkstatic uint64_t
157155534Sphkgeode_cputicks(void)
158155534Sphk{
159155534Sphk	unsigned c;
160155534Sphk	static unsigned last;
161155534Sphk	static uint64_t offset;
162155534Sphk
163155534Sphk	c = inl(geode_counter);
164155534Sphk	if (c < last)
165155534Sphk		offset += (1LL << 32);
166155534Sphk	last = c;
167155534Sphk	return (offset | c);
168155534Sphk}
169155534Sphk
170126387Sphk/*
171126387Sphk * The GEODE watchdog runs from a 32kHz frequency.  One period of that is
172126387Sphk * 31250 nanoseconds which we round down to 2^14 nanoseconds.  The watchdog
173126387Sphk * consists of a power-of-two prescaler and a 16 bit counter, so the math
174126387Sphk * is quite simple.  The max timeout is 14 + 16 + 13 = 2^43 nsec ~= 2h26m.
175126387Sphk */
176126387Sphkstatic void
177126387Sphkgeode_watchdog(void *foo __unused, u_int cmd, int *error)
178126387Sphk{
179126387Sphk	u_int u, p, r;
180126387Sphk
181126387Sphk	u = cmd & WD_INTERVAL;
182167950Sn_hibma	if (u >= 14 && u <= 43) {
183126387Sphk		u -= 14;
184126387Sphk		if (u > 16) {
185126387Sphk			p = u - 16;
186126387Sphk			u -= p;
187126387Sphk		} else {
188126387Sphk			p = 0;
189126387Sphk		}
190126387Sphk		if (u == 16)
191126387Sphk			u = (1 << u) - 1;
192126387Sphk		else
193126387Sphk			u = 1 << u;
194126387Sphk		r = inw(cba + 2) & 0xff00;
195126387Sphk		outw(cba + 2, p | 0xf0 | r);
196126387Sphk		outw(cba, u);
197126387Sphk		*error = 0;
198126387Sphk	} else {
199126387Sphk		outw(cba, 0);
200126387Sphk	}
201126387Sphk}
202126387Sphk
203148231Sphk/*
204172216Sphk * We run MFGPT0 off the 32kHz frequency and prescale by 16384 giving a
205172216Sphk * period of half a second.
206172216Sphk * Range becomes 2^30 (= 1 sec) to 2^44 (almost 5 hours)
207172216Sphk */
208172216Sphkstatic void
209172216Sphkcs5536_watchdog(void *foo __unused, u_int cmd, int *error)
210172216Sphk{
211208111Sphk	u_int u, p, s;
212172216Sphk	uint16_t a;
213172216Sphk	uint32_t m;
214172216Sphk
215172216Sphk	a = rdmsr(0x5140000d);
216172216Sphk
217172216Sphk	u = cmd & WD_INTERVAL;
218172216Sphk	if (u >= 30 && u <= 44) {
219172216Sphk		p = 1 << (u - 29);
220172216Sphk
221172216Sphk		/* Set up MFGPT0, 32khz, prescaler 16k, C2 event */
222172216Sphk		outw(a + 6, 0x030e);
223172216Sphk		/* set comparator 2 */
224172216Sphk		outw(a + 2, p);
225172216Sphk		/* reset counter */
226172216Sphk		outw(a + 4, 0);
227172216Sphk		/* Arm reset mechanism */
228208111Sphk		m = rdmsr(0x51400029);
229172216Sphk		m |= (1 << 24);
230172216Sphk		wrmsr(0x51400029, m);
231172216Sphk		/* Start counter */
232172216Sphk		outw(a + 6, 0x8000);
233172216Sphk
234172216Sphk		*error = 0;
235208111Sphk	} else {
236208111Sphk		/*
237208111Sphk		 * MFGPT_SETUP is write-once
238208111Sphk		 * Check if the counter has been setup
239208111Sphk		 */
240208111Sphk		s = inw(a + 6);
241208111Sphk		if (s & (1 << 12)) {
242208111Sphk			/* Stop and reset counter */
243208111Sphk			outw(a + 6, 0);
244208111Sphk			outw(a + 4, 0);
245208111Sphk		}
246172216Sphk	}
247172216Sphk}
248172216Sphk
249172216Sphk/*
250148231Sphk * The Advantech PCM-582x watchdog expects 0x1 at I/O port 0x0443
251148231Sphk * every 1.6 secs +/- 30%. Writing 0x0 disables the watchdog
252148231Sphk * NB: reading the I/O port enables the timer as well
253148231Sphk */
254148231Sphkstatic void
255148231Sphkadvantech_watchdog(void *foo __unused, u_int cmd, int *error)
256148231Sphk{
257167950Sn_hibma	u_int u;
258167950Sn_hibma
259167950Sn_hibma	u = cmd & WD_INTERVAL;
260167950Sn_hibma	if (u > 0 && u <= WD_TO_1SEC) {
261156335Sphk		outb(0x0443, 1);
262156335Sphk		*error = 0;
263156335Sphk	} else {
264156335Sphk		outb(0x0443, 0);
265156335Sphk	}
266148231Sphk}
267148231Sphk
268119608Sphkstatic int
269119608Sphkgeode_probe(device_t self)
270119608Sphk{
271148231Sphk#define BIOS_OEM_MAXLEN 80
272148231Sphk	static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0";
273119608Sphk
274172216Sphk	switch (pci_get_devid(self)) {
275172216Sphk	case 0x0515100b:
276126349Sphk		if (geode_counter == 0) {
277126349Sphk			/*
278126349Sphk			 * The address of the CBA is written to this register
279126349Sphk			 * by the bios, see p161 in data sheet.
280126349Sphk			 */
281126349Sphk			cba = pci_read_config(self, 0x64, 4);
282176153Sphk			if (bootverbose)
283176153Sphk				printf("Geode CBA@ 0x%x\n", cba);
284126349Sphk			geode_counter = cba + 0x08;
285126349Sphk			outl(cba + 0x0d, 2);
286176153Sphk			if (bootverbose)
287176153Sphk				printf("Geode rev: %02x %02x\n",
288176153Sphk					inb(cba + 0x3c), inb(cba + 0x3d));
289126349Sphk			tc_init(&geode_timecounter);
290126387Sphk			EVENTHANDLER_REGISTER(watchdog_list, geode_watchdog,
291126387Sphk			    NULL, 0);
292155534Sphk			set_cputicker(geode_cputicks, 27000000, 0);
293126349Sphk		}
294172216Sphk		break;
295172216Sphk	case 0x0510100b:
296126349Sphk		gpio = pci_read_config(self, PCIR_BAR(0), 4);
297126349Sphk		gpio &= ~0x1f;
298176153Sphk		if (bootverbose)
299176153Sphk			printf("Geode GPIO@ = %x\n", gpio);
300176153Sphk		if (bios_oem_strings(&bios_soekris,
301176153Sphk		    bios_oem, sizeof bios_oem) > 0 ) {
302130041Sphk			led1b = 20;
303130041Sphk			led1 = led_create(led_func, &led1b, "error");
304176153Sphk		} else if (bios_oem_strings(&bios_pcengines,
305176153Sphk		    bios_oem, sizeof bios_oem) > 0 ) {
306130041Sphk			led1b = -2;
307130041Sphk			led2b = -3;
308130041Sphk			led3b = -18;
309130041Sphk			led1 = led_create(led_func, &led1b, "led1");
310130041Sphk			led2 = led_create(led_func, &led2b, "led2");
311130041Sphk			led3 = led_create(led_func, &led3b, "led3");
312130041Sphk			/*
313148231Sphk		 	* Turn on first LED so we don't make
314148231Sphk			* people think their box just died.
315148231Sphk		 	*/
316130041Sphk			led_func(&led1b, 1);
317130041Sphk		}
318176153Sphk		if (*bios_oem)
319148231Sphk			printf("Geode %s\n", bios_oem);
320172216Sphk		break;
321172216Sphk	case 0x01011078:
322176153Sphk		if (bios_oem_strings(&bios_advantech,
323176153Sphk		    bios_oem, sizeof bios_oem) > 0 ) {
324148231Sphk			printf("Geode %s\n", bios_oem);
325148231Sphk			EVENTHANDLER_REGISTER(watchdog_list, advantech_watchdog,
326148231Sphk			    NULL, 0);
327148231Sphk		}
328172216Sphk		break;
329172216Sphk	case 0x20801022:
330176153Sphk		if (bios_oem_strings(&bios_soekris_55,
331176153Sphk		    bios_oem, sizeof bios_oem) > 0 ) {
332172216Sphk			led1b = 6;
333172216Sphk			led1 = led_create(cs5536_led_func, &led1b, "error");
334176153Sphk		} else if (bios_oem_strings(&bios_pcengines_55,
335176153Sphk		    bios_oem, sizeof bios_oem) > 0 ) {
336176153Sphk			led1b = -6;
337176153Sphk			led2b = -25;
338176153Sphk			led3b = -27;
339176153Sphk			led1 = led_create(cs5536_led_func, &led1b, "led1");
340176153Sphk			led2 = led_create(cs5536_led_func, &led2b, "led2");
341176153Sphk			led3 = led_create(cs5536_led_func, &led3b, "led3");
342176153Sphk			/*
343176153Sphk		 	* Turn on first LED so we don't make
344176153Sphk			* people think their box just died.
345176153Sphk		 	*/
346176153Sphk			cs5536_led_func(&led1b, 1);
347172216Sphk		}
348176153Sphk		if (*bios_oem)
349176153Sphk			printf("Geode LX: %s\n", bios_oem);
350176153Sphk		if (bootverbose)
351176153Sphk			printf("MFGPT bar: %jx\n", rdmsr(0x5140000d));
352176153Sphk		EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, NULL, 0);
353172216Sphk		break;
354126349Sphk	}
355119608Sphk	return (ENXIO);
356119608Sphk}
357119608Sphk
358119608Sphkstatic int
359119608Sphkgeode_attach(device_t self)
360119608Sphk{
361119608Sphk
362119608Sphk	return(ENODEV);
363119608Sphk}
364119608Sphk
365119608Sphkstatic device_method_t geode_methods[] = {
366119608Sphk	/* Device interface */
367119608Sphk	DEVMETHOD(device_probe,		geode_probe),
368119608Sphk	DEVMETHOD(device_attach,	geode_attach),
369119608Sphk	DEVMETHOD(device_suspend,	bus_generic_suspend),
370119608Sphk	DEVMETHOD(device_resume,	bus_generic_resume),
371119608Sphk	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
372119608Sphk	{0, 0}
373119608Sphk};
374119608Sphk
375119608Sphkstatic driver_t geode_driver = {
376119608Sphk	"geode",
377119608Sphk	geode_methods,
378119608Sphk	0,
379119608Sphk};
380119608Sphk
381119608Sphkstatic devclass_t geode_devclass;
382119608Sphk
383119608SphkDRIVER_MODULE(geode, pci, geode_driver, geode_devclass, 0, 0);
384