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 * $FreeBSD: releng/5.1/sys/i386/i386/elan-mmcr.c 112569 2003-03-25 00:07:06Z jake $
10 *
11 * The AMD Elan sc520 is a system-on-chip gadget which is used in embedded
12 * kind of things, see www.soekris.com for instance, and it has a few quirks
13 * we need to deal with.
14 * Unfortunately we cannot identify the gadget by CPUID output because it
15 * depends on strapping options and only the stepping field may be useful
16 * and those are undocumented from AMDs side.
17 *
18 * So instead we recognize the on-chip host-PCI bridge and call back from
19 * sys/i386/pci/pci_bus.c to here if we find it.
20 *
21 * #ifdef ELAN_PPS
22 * The Elan has three general purpose counters, which when used just right
23 * can hardware timestamp external events with approx 250 nanoseconds
24 * resolution _and_ precision. Connect the signal to TMR1IN and PIO7.
25 * (You can use any PIO pin, look for PIO7 to change this). Use the
26 * PPS-API on the /dev/elan-mmcr device.
27 * #endif ELAN_PPS
28 */
29
30 #include "opt_cpu.h"
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/conf.h>
35 #include <sys/sysctl.h>
36 #include <sys/timetc.h>
37 #include <sys/proc.h>
38 #include <sys/uio.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <sys/malloc.h>
42 #include <sys/sysctl.h>
43 #include <sys/timepps.h>
44 #include <sys/watchdog.h>
45
46 #include <machine/md_var.h>
47
48 #include <vm/vm.h>
49 #include <vm/pmap.h>
50
51 uint16_t *elan_mmcr;
52
53 /* Relating to the /dev/soekris-errled */
54 static struct mtx errled_mtx;
55 static char *errled;
56 static struct callout_handle errled_h = CALLOUT_HANDLE_INITIALIZER(&errled_h);
57 static void timeout_errled(void *);
58
59 #ifdef ELAN_PPS
60 /* Relating to the PPS-api */
61 static struct pps_state elan_pps;
62
63 static void
64 elan_poll_pps(struct timecounter *tc)
65 {
66 static int state;
67 int i;
68
69 /* XXX: This is PIO7, change to your preference */
70 i = elan_mmcr[0xc30 / 2] & 0x80;
71 if (i == state)
72 return;
73 state = i;
74 if (!state)
75 return;
76 pps_capture(&elan_pps);
77 elan_pps.capcount =
78 (elan_mmcr[0xc84 / 2] - elan_mmcr[0xc7c / 2]) & 0xffff;
79 pps_event(&elan_pps, PPS_CAPTUREASSERT);
80 }
81 #endif /* ELAN_PPS */
82
83 static unsigned
84 elan_get_timecount(struct timecounter *tc)
85 {
86 return (elan_mmcr[0xc84 / 2]);
87 }
88
89 /*
90 * The Elan CPU can be run from a number of clock frequencies, this
91 * allows you to override the default 33.3 MHZ.
92 */
93 #ifndef ELAN_XTAL
94 #define ELAN_XTAL 33333333
95 #endif
96
97 static struct timecounter elan_timecounter = {
98 elan_get_timecount,
99 NULL,
100 0xffff,
101 ELAN_XTAL / 4,
102 "ELAN"
103 };
104
105 static int
106 sysctl_machdep_elan_freq(SYSCTL_HANDLER_ARGS)
107 {
108 u_int f;
109 int error;
110
111 f = elan_timecounter.tc_frequency * 4;
112 error = sysctl_handle_int(oidp, &f, sizeof(f), req);
113 if (error == 0 && req->newptr != NULL)
114 elan_timecounter.tc_frequency = (f + 3) / 4;
115 return (error);
116 }
117
118 SYSCTL_PROC(_machdep, OID_AUTO, elan_freq, CTLTYPE_UINT | CTLFLAG_RW,
119 0, sizeof (u_int), sysctl_machdep_elan_freq, "IU", "");
120
121 void
122 init_AMD_Elan_sc520(void)
123 {
124 u_int new;
125 int i;
126
127 if (bootverbose)
128 printf("Doing h0h0magic for AMD Elan sc520\n");
129 elan_mmcr = pmap_mapdev(0xfffef000, 0x1000);
130
131 /*-
132 * The i8254 is driven with a nonstandard frequency which is
133 * derived thusly:
134 * f = 32768 * 45 * 25 / 31 = 1189161.29...
135 * We use the sysctl to get the timecounter etc into whack.
136 */
137
138 new = 1189161;
139 i = kernel_sysctlbyname(&thread0, "machdep.i8254_freq",
140 NULL, 0,
141 &new, sizeof new,
142 NULL);
143 if (bootverbose)
144 printf("sysctl machdep.i8254_freq=%d returns %d\n", new, i);
145
146 /* Start GP timer #2 and use it as timecounter, hz permitting */
147 elan_mmcr[0xc82 / 2] = 0xc001;
148
149 #ifdef ELAN_PPS
150 /* Set up GP timer #1 as pps counter */
151 elan_mmcr[0xc24 / 2] &= ~0x10;
152 elan_mmcr[0xc7a / 2] = 0x8000 | 0x4000 | 0x10 | 0x1;
153 elan_pps.ppscap |= PPS_CAPTUREASSERT;
154 pps_init(&elan_pps);
155 #endif
156
157 tc_init(&elan_timecounter);
158 }
159
160
161 /*
162 * Device driver initialization stuff
163 */
164
165 static d_write_t elan_write;
166 static d_ioctl_t elan_ioctl;
167 static d_mmap_t elan_mmap;
168
169 #define ELAN_MMCR 0
170 #define ELAN_ERRLED 1
171
172 #define CDEV_MAJOR 100 /* Share with xrpu */
173 static struct cdevsw elan_cdevsw = {
174 .d_open = nullopen,
175 .d_close = nullclose,
176 .d_write = elan_write,
177 .d_ioctl = elan_ioctl,
178 .d_mmap = elan_mmap,
179 .d_name = "elan",
180 .d_maj = CDEV_MAJOR,
181 };
182
183 static void
184 elan_drvinit(void)
185 {
186
187 if (elan_mmcr == NULL)
188 return;
189 printf("Elan-mmcr driver: MMCR at %p\n", elan_mmcr);
190 make_dev(&elan_cdevsw, ELAN_MMCR,
191 UID_ROOT, GID_WHEEL, 0600, "elan-mmcr");
192 make_dev(&elan_cdevsw, ELAN_ERRLED,
193 UID_ROOT, GID_WHEEL, 0600, "soekris-errled");
194 mtx_init(&errled_mtx, "Elan-errled", MTX_DEF, 0);
195 return;
196 }
197
198 SYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE+CDEV_MAJOR,elan_drvinit,NULL);
199
200 #define LED_ON() do {elan_mmcr[0xc34 / 2] = 0x200;} while(0)
201 #define LED_OFF() do {elan_mmcr[0xc38 / 2] = 0x200;} while(0)
202
203 static void
204 timeout_errled(void *p)
205 {
206 static enum {NOTHING, FLASH, DIGIT} mode;
207 static int count, cnt2, state;
208
209 mtx_lock(&errled_mtx);
210 if (p != NULL) {
211 mode = NOTHING;
212 /* Our instructions changed */
213 if (*errled == '1') { /* Turn LED on */
214 LED_ON();
215 } else if (*errled == '') { /* Turn LED off */
216 LED_OFF();
217 } else if (*errled == 'f') { /* Flash */
218 mode = FLASH;
219 cnt2 = 10;
220 if (errled[1] >= '1' && errled[1] <= '9')
221 cnt2 = errled[1] - '';
222 cnt2 = hz / cnt2;
223 LED_ON();
224 errled_h = timeout(timeout_errled, NULL, cnt2);
225 } else if (*errled == 'd') { /* Digit */
226 mode = DIGIT;
227 count = 0;
228 cnt2 = 0;
229 state = 0;
230 LED_OFF();
231 errled_h = timeout(timeout_errled, NULL, hz/10);
232 }
233 } else if (mode == FLASH) {
234 if (count)
235 LED_ON();
236 else
237 LED_OFF();
238 count = !count;
239 errled_h = timeout(timeout_errled, NULL, cnt2);
240 } else if (mode == DIGIT) {
241 if (cnt2 > 0) {
242 if (state) {
243 LED_OFF();
244 state = 0;
245 cnt2--;
246 } else {
247 LED_ON();
248 state = 1;
249 }
250 errled_h = timeout(timeout_errled, NULL, hz/5);
251 } else {
252 do
253 count++;
254 while (errled[count] != '\0' &&
255 (errled[count] < '' || errled[count] > '9'));
256 if (errled[count] == '\0') {
257 count = 0;
258 errled_h = timeout(timeout_errled, NULL, hz * 2);
259 } else {
260 cnt2 = errled[count] - '';
261 state = 0;
262 errled_h = timeout(timeout_errled, NULL, hz);
263 }
264 }
265 }
266 mtx_unlock(&errled_mtx);
267 return;
268 }
269
270 /*
271 * The write function is used for the error-LED.
272 */
273
274 static int
275 elan_write(dev_t dev, struct uio *uio, int ioflag)
276 {
277 int error;
278 char *s, *q;
279
280 if (minor(dev) != ELAN_ERRLED)
281 return (EOPNOTSUPP);
282
283 if (uio->uio_resid > 512)
284 return (EINVAL);
285 s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
286 if (s == NULL)
287 return (ENOMEM);
288 untimeout(timeout_errled, NULL, errled_h);
289 s[uio->uio_resid] = '\0';
290 error = uiomove(s, uio->uio_resid, uio);
291 if (error) {
292 free(s, M_DEVBUF);
293 return (error);
294 }
295 mtx_lock(&errled_mtx);
296 q = errled;
297 errled = s;
298 mtx_unlock(&errled_mtx);
299 if (q != NULL)
300 free(q, M_DEVBUF);
301 timeout_errled(errled);
302
303 return(0);
304 }
305
306 static int
307 elan_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
308 {
309
310 if (minor(dev) != ELAN_MMCR)
311 return (EOPNOTSUPP);
312 if (offset >= 0x1000)
313 return (-1);
314 *paddr = 0xfffef000;
315 return (0);
316 }
317
318 static int
319 elan_watchdog(u_int spec)
320 {
321 u_int u, v;
322 static u_int cur;
323
324 if (spec & ~__WD_LEGAL)
325 return (EINVAL);
326 switch (spec & (WD_ACTIVE|WD_PASSIVE)) {
327 case WD_ACTIVE:
328 u = spec & WD_INTERVAL;
329 if (u > 35)
330 return (EINVAL);
331 u = imax(u - 5, 24);
332 v = 2 << (u - 24);
333 v |= 0xc000;
334
335 /*
336 * There is a bug in some silicon which prevents us from
337 * writing to the WDTMRCTL register if the GP echo mode is
338 * enabled. GP echo mode on the other hand is desirable
339 * for other reasons. Save and restore the GP echo mode
340 * around our hardware tom-foolery.
341 */
342 u = elan_mmcr[0xc00 / 2];
343 elan_mmcr[0xc00 / 2] = 0;
344 if (v != cur) {
345 /* Clear the ENB bit */
346 elan_mmcr[0xcb0 / 2] = 0x3333;
347 elan_mmcr[0xcb0 / 2] = 0xcccc;
348 elan_mmcr[0xcb0 / 2] = 0;
349
350 /* Set new value */
351 elan_mmcr[0xcb0 / 2] = 0x3333;
352 elan_mmcr[0xcb0 / 2] = 0xcccc;
353 elan_mmcr[0xcb0 / 2] = v;
354 cur = v;
355 } else {
356 /* Just reset timer */
357 elan_mmcr[0xcb0 / 2] = 0xaaaa;
358 elan_mmcr[0xcb0 / 2] = 0x5555;
359 }
360 elan_mmcr[0xc00 / 2] = u;
361 return (0);
362 case WD_PASSIVE:
363 return (EOPNOTSUPP);
364 case 0:
365 u = elan_mmcr[0xc00 / 2];
366 elan_mmcr[0xc00 / 2] = 0;
367 elan_mmcr[0xcb0 / 2] = 0x3333;
368 elan_mmcr[0xcb0 / 2] = 0xcccc;
369 elan_mmcr[0xcb0 / 2] = 0x4080;
370 elan_mmcr[0xc00 / 2] = u;
371 cur = 0;
372 return (0);
373 default:
374 return (EINVAL);
375 }
376
377 }
378
379 static int
380 elan_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr)
381 {
382 int error;
383
384 error = ENOTTY;
385 #ifdef ELAN_PPS
386 error = pps_ioctl(cmd, arg, &elan_pps);
387 /*
388 * We only want to incur the overhead of the PPS polling if we
389 * are actually asked to timestamp.
390 */
391 if (elan_pps.ppsparam.mode & PPS_CAPTUREASSERT)
392 elan_timecounter.tc_poll_pps = elan_poll_pps;
393 else
394 elan_timecounter.tc_poll_pps = NULL;
395 if (error != ENOTTY)
396 return (error);
397 #endif /* ELAN_PPS */
398
399 if (cmd == WDIOCPATPAT)
400 return elan_watchdog(*((u_int*)arg));
401
402 /* Other future ioctl handling here */
403 return(error);
404 }
405
Cache object: 6e83d3013f186fbde408564a05d3be06
|