]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/i386/i386/geode.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / i386 / i386 / geode.c
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$");
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
42 static 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
52 static 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
62 static 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
71 static struct bios_oem bios_advantech = {
72         { 0xfe000, 0xff000 },
73         {
74                 { "**** PCM-582", 5, 33 },      /* PCM-5823 BIOS V1.12 ... */
75                 { "GXm-Cx5530", -11, 35 },      /* 06/07/2002-GXm-Cx5530... */
76                 { NULL, 0, 0 },
77         }
78 };
79
80 static unsigned cba;
81 static unsigned gpio;
82 static unsigned geode_counter;
83
84 static struct cdev *led1, *led2, *led3;
85 static int      led1b, led2b, led3b;
86
87 static void
88 led_func(void *ptr, int onoff)
89 {
90         uint32_t u;
91         int bit;
92
93         bit = *(int *)ptr;
94         if (bit < 0) {
95                 bit = -bit;
96                 onoff = !onoff;
97         }
98
99         u = inl(gpio + 4);
100         if (onoff)
101                 u |= 1 << bit;
102         else
103                 u &= ~(1 << bit);
104         outl(gpio, u);
105 }
106
107 static void
108 cs5536_led_func(void *ptr, int onoff)
109 {
110         int bit;
111         uint16_t a;
112
113         bit = *(int *)ptr;
114         if (bit < 0) {
115                 bit = -bit;
116                 onoff = !onoff;
117         }
118
119         a = rdmsr(0x5140000c);
120         if (onoff)
121                 outl(a, 1 << bit);
122         else
123                 outl(a, 1 << (bit + 16));
124 }
125
126
127 static unsigned
128 geode_get_timecount(struct timecounter *tc)
129 {
130         return (inl(geode_counter));
131 }
132
133 static struct timecounter geode_timecounter = {
134         geode_get_timecount,
135         NULL,
136         0xffffffff,
137         27000000,
138         "Geode",
139         1000
140 };
141
142 static uint64_t
143 geode_cputicks(void)
144 {
145         unsigned c;
146         static unsigned last;
147         static uint64_t offset;
148
149         c = inl(geode_counter);
150         if (c < last)
151                 offset += (1LL << 32);
152         last = c;
153         return (offset | c);
154 }
155
156 /*
157  * The GEODE watchdog runs from a 32kHz frequency.  One period of that is
158  * 31250 nanoseconds which we round down to 2^14 nanoseconds.  The watchdog
159  * consists of a power-of-two prescaler and a 16 bit counter, so the math
160  * is quite simple.  The max timeout is 14 + 16 + 13 = 2^43 nsec ~= 2h26m.
161  */
162 static void
163 geode_watchdog(void *foo __unused, u_int cmd, int *error)
164 {
165         u_int u, p, r;
166
167         u = cmd & WD_INTERVAL;
168         if (u >= 14 && u <= 43) {
169                 u -= 14;
170                 if (u > 16) {
171                         p = u - 16;
172                         u -= p;
173                 } else {
174                         p = 0;
175                 }
176                 if (u == 16)
177                         u = (1 << u) - 1;
178                 else
179                         u = 1 << u;
180                 r = inw(cba + 2) & 0xff00;
181                 outw(cba + 2, p | 0xf0 | r);
182                 outw(cba, u);
183                 *error = 0;
184         } else {
185                 outw(cba, 0);
186         }
187 }
188
189 /*
190  * We run MFGPT0 off the 32kHz frequency and prescale by 16384 giving a
191  * period of half a second.
192  * Range becomes 2^30 (= 1 sec) to 2^44 (almost 5 hours)
193  */
194 static void
195 cs5536_watchdog(void *foo __unused, u_int cmd, int *error)
196 {
197         u_int u, p;
198         uint16_t a;
199         uint32_t m;
200
201         a = rdmsr(0x5140000d);
202         m = rdmsr(0x51400029);
203         m &= ~(1 << 24);
204         wrmsr(0x51400029, m);
205
206         u = cmd & WD_INTERVAL;
207         if (u >= 30 && u <= 44) {
208                 p = 1 << (u - 29);
209
210                 /* Set up MFGPT0, 32khz, prescaler 16k, C2 event */
211                 outw(a + 6, 0x030e);
212                 /* set comparator 2 */
213                 outw(a + 2, p);
214                 /* reset counter */
215                 outw(a + 4, 0);
216                 /* Arm reset mechanism */
217                 m |= (1 << 24);
218                 wrmsr(0x51400029, m);
219                 /* Start counter */
220                 outw(a + 6, 0x8000);
221
222                 *error = 0;
223         }
224 }
225
226 /*
227  * The Advantech PCM-582x watchdog expects 0x1 at I/O port 0x0443
228  * every 1.6 secs +/- 30%. Writing 0x0 disables the watchdog
229  * NB: reading the I/O port enables the timer as well
230  */
231 static void
232 advantech_watchdog(void *foo __unused, u_int cmd, int *error)
233 {
234         u_int u;
235
236         u = cmd & WD_INTERVAL;
237         if (u > 0 && u <= WD_TO_1SEC) {
238                 outb(0x0443, 1);
239                 *error = 0;
240         } else {
241                 outb(0x0443, 0);
242         }
243 }
244
245 static int
246 geode_probe(device_t self)
247 {
248 #define BIOS_OEM_MAXLEN 80
249         static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0";
250
251         switch (pci_get_devid(self)) {
252         case 0x0515100b:
253                 if (geode_counter == 0) {
254                         /*
255                          * The address of the CBA is written to this register
256                          * by the bios, see p161 in data sheet.
257                          */
258                         cba = pci_read_config(self, 0x64, 4);
259                         printf("Geode CBA@ 0x%x\n", cba);
260                         geode_counter = cba + 0x08;
261                         outl(cba + 0x0d, 2);
262                         printf("Geode rev: %02x %02x\n",
263                                 inb(cba + 0x3c), inb(cba + 0x3d));
264                         tc_init(&geode_timecounter);
265                         EVENTHANDLER_REGISTER(watchdog_list, geode_watchdog,
266                             NULL, 0);
267                         set_cputicker(geode_cputicks, 27000000, 0);
268                 }
269                 break;
270         case 0x0510100b:
271                 gpio = pci_read_config(self, PCIR_BAR(0), 4);
272                 gpio &= ~0x1f;
273                 printf("Geode GPIO@ = %x\n", gpio);
274                 if ( bios_oem_strings(&bios_soekris,
275                                         bios_oem, BIOS_OEM_MAXLEN) > 0 ) {
276                         led1b = 20;
277                         led1 = led_create(led_func, &led1b, "error");
278                 } else if ( bios_oem_strings(&bios_pcengines,
279                                         bios_oem, BIOS_OEM_MAXLEN) > 0 ) {
280                         led1b = -2;
281                         led2b = -3;
282                         led3b = -18;
283                         led1 = led_create(led_func, &led1b, "led1");
284                         led2 = led_create(led_func, &led2b, "led2");
285                         led3 = led_create(led_func, &led3b, "led3");
286                         /*
287                         * Turn on first LED so we don't make
288                         * people think their box just died.
289                         */
290                         led_func(&led1b, 1);
291                 }
292                 if ( strlen(bios_oem) )
293                         printf("Geode %s\n", bios_oem);
294                 break;
295         case 0x01011078:
296                 if ( bios_oem_strings(&bios_advantech,
297                                 bios_oem, BIOS_OEM_MAXLEN) > 0 ) {
298                         printf("Geode %s\n", bios_oem);
299                         EVENTHANDLER_REGISTER(watchdog_list, advantech_watchdog,
300                             NULL, 0);
301                 }
302                 break;
303         case 0x20801022:
304                 if ( bios_oem_strings(&bios_soekris_55,
305                     bios_oem, BIOS_OEM_MAXLEN) > 0 ) {
306                         printf("Geode LX: %s\n", bios_oem);
307                         led1b = 6;
308                         led1 = led_create(cs5536_led_func, &led1b, "error");
309                 }
310                 printf("MFGPT bar: %jx\n", rdmsr(0x5140000d));
311                 EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog,
312                     NULL, 0);
313                 break;
314         }
315         return (ENXIO);
316 }
317
318 static int
319 geode_attach(device_t self)
320 {
321
322         return(ENODEV);
323 }
324
325 static device_method_t geode_methods[] = {
326         /* Device interface */
327         DEVMETHOD(device_probe,         geode_probe),
328         DEVMETHOD(device_attach,        geode_attach),
329         DEVMETHOD(device_suspend,       bus_generic_suspend),
330         DEVMETHOD(device_resume,        bus_generic_resume),
331         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
332         {0, 0}
333 };
334  
335 static driver_t geode_driver = {
336         "geode",
337         geode_methods,
338         0,
339 };
340
341 static devclass_t geode_devclass;
342
343 DRIVER_MODULE(geode, pci, geode_driver, geode_devclass, 0, 0);