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.0/sys/i386/i386/elan-mmcr.c 103482 2002-09-17 11:47:38Z phk $
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
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/conf.h>
26 #include <sys/sysctl.h>
27 #include <sys/timetc.h>
28 #include <sys/proc.h>
29 #include <sys/uio.h>
30 #include <sys/lock.h>
31 #include <sys/mutex.h>
32 #include <sys/malloc.h>
33
34 #include <machine/md_var.h>
35
36 #include <vm/vm.h>
37 #include <vm/pmap.h>
38
39 uint16_t *elan_mmcr;
40
41 /* Relating to the /dev/soekris-errled */
42 static struct mtx errled_mtx;
43 static char *errled;
44 static struct callout_handle errled_h = CALLOUT_HANDLE_INITIALIZER(&errled_h);
45 static void timeout_errled(void *);
46
47 static unsigned
48 elan_get_timecount(struct timecounter *tc)
49 {
50 return (elan_mmcr[0xc84 / 2]);
51 }
52
53 static struct timecounter elan_timecounter = {
54 elan_get_timecount,
55 0,
56 0xffff,
57 33333333 / 4,
58 "ELAN"
59 };
60
61 void
62 init_AMD_Elan_sc520(void)
63 {
64 u_int new;
65 int i;
66
67 if (bootverbose)
68 printf("Doing h0h0magic for AMD Elan sc520\n");
69 elan_mmcr = pmap_mapdev(0xfffef000, 0x1000);
70
71 /*-
72 * The i8254 is driven with a nonstandard frequency which is
73 * derived thusly:
74 * f = 32768 * 45 * 25 / 31 = 1189161.29...
75 * We use the sysctl to get the timecounter etc into whack.
76 */
77
78 new = 1189161;
79 i = kernel_sysctlbyname(&thread0, "machdep.i8254_freq",
80 NULL, 0,
81 &new, sizeof new,
82 NULL);
83 if (bootverbose)
84 printf("sysctl machdep.i8254_freq=%d returns %d\n", new, i);
85
86 /* Start GP timer #2 and use it as timecounter, hz permitting */
87 elan_mmcr[0xc82 / 2] = 0xc001;
88 tc_init(&elan_timecounter);
89 }
90
91
92 /*
93 * Device driver initialization stuff
94 */
95
96 static d_write_t elan_write;
97 static d_ioctl_t elan_ioctl;
98 static d_mmap_t elan_mmap;
99
100 #define ELAN_MMCR 0
101 #define ELAN_ERRLED 1
102
103 #define CDEV_MAJOR 100 /* Share with xrpu */
104 static struct cdevsw elan_cdevsw = {
105 /* open */ nullopen,
106 /* close */ nullclose,
107 /* read */ noread,
108 /* write */ elan_write,
109 /* ioctl */ elan_ioctl,
110 /* poll */ nopoll,
111 /* mmap */ elan_mmap,
112 /* strategy */ nostrategy,
113 /* name */ "elan",
114 /* maj */ CDEV_MAJOR,
115 /* dump */ nodump,
116 /* psize */ nopsize,
117 /* flags */ 0,
118 };
119
120 static void
121 elan_drvinit(void)
122 {
123
124 if (elan_mmcr == NULL)
125 return;
126 printf("Elan-mmcr driver: MMCR at %p\n", elan_mmcr);
127 make_dev(&elan_cdevsw, ELAN_MMCR,
128 UID_ROOT, GID_WHEEL, 0600, "elan-mmcr");
129 make_dev(&elan_cdevsw, ELAN_ERRLED,
130 UID_ROOT, GID_WHEEL, 0600, "soekris-errled");
131 mtx_init(&errled_mtx, "Elan-errled", MTX_DEF, 0);
132 return;
133 }
134
135 SYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE+CDEV_MAJOR,elan_drvinit,NULL);
136
137 #define LED_ON() do {elan_mmcr[0xc34 / 2] = 0x200;} while(0)
138 #define LED_OFF() do {elan_mmcr[0xc38 / 2] = 0x200;} while(0)
139
140 static void
141 timeout_errled(void *p)
142 {
143 static enum {NOTHING, FLASH, DIGIT} mode;
144 static int count, cnt2, state;
145
146 mtx_lock(&errled_mtx);
147 if (p != NULL) {
148 mode = NOTHING;
149 /* Our instructions changed */
150 if (*errled == '1') { /* Turn LED on */
151 LED_ON();
152 } else if (*errled == '') { /* Turn LED off */
153 LED_OFF();
154 } else if (*errled == 'f') { /* Flash */
155 mode = FLASH;
156 cnt2 = 10;
157 if (errled[1] >= '1' && errled[1] <= '9')
158 cnt2 = errled[1] - '';
159 cnt2 = hz / cnt2;
160 LED_ON();
161 errled_h = timeout(timeout_errled, NULL, cnt2);
162 } else if (*errled == 'd') { /* Digit */
163 mode = DIGIT;
164 count = 0;
165 cnt2 = 0;
166 state = 0;
167 LED_OFF();
168 errled_h = timeout(timeout_errled, NULL, hz/10);
169 }
170 } else if (mode == FLASH) {
171 if (count)
172 LED_ON();
173 else
174 LED_OFF();
175 count = !count;
176 errled_h = timeout(timeout_errled, NULL, cnt2);
177 } else if (mode == DIGIT) {
178 if (cnt2 > 0) {
179 if (state) {
180 LED_OFF();
181 state = 0;
182 cnt2--;
183 } else {
184 LED_ON();
185 state = 1;
186 }
187 errled_h = timeout(timeout_errled, NULL, hz/5);
188 } else {
189 do
190 count++;
191 while (errled[count] != '\0' &&
192 (errled[count] < '' || errled[count] > '9'));
193 if (errled[count] == '\0') {
194 count = 0;
195 errled_h = timeout(timeout_errled, NULL, hz * 2);
196 } else {
197 cnt2 = errled[count] - '';
198 state = 0;
199 errled_h = timeout(timeout_errled, NULL, hz);
200 }
201 }
202 }
203 mtx_unlock(&errled_mtx);
204 return;
205 }
206
207 /*
208 * The write function is used for the error-LED.
209 */
210
211 static int
212 elan_write(dev_t dev, struct uio *uio, int ioflag)
213 {
214 int error;
215 char *s, *q;
216
217 if (minor(dev) != ELAN_ERRLED)
218 return (EOPNOTSUPP);
219
220 if (uio->uio_resid > 512)
221 return (EINVAL);
222 s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
223 if (s == NULL)
224 return (ENOMEM);
225 untimeout(timeout_errled, NULL, errled_h);
226 s[uio->uio_resid] = '\0';
227 error = uiomove(s, uio->uio_resid, uio);
228 if (error) {
229 free(s, M_DEVBUF);
230 return (error);
231 }
232 mtx_lock(&errled_mtx);
233 q = errled;
234 errled = s;
235 mtx_unlock(&errled_mtx);
236 if (q != NULL)
237 free(q, M_DEVBUF);
238 timeout_errled(errled);
239
240 return(0);
241 }
242
243 static int
244 elan_mmap(dev_t dev, vm_offset_t offset, int nprot)
245 {
246
247 if (minor(dev) != ELAN_MMCR)
248 return (EOPNOTSUPP);
249 if (offset >= 0x1000)
250 return (-1);
251 return (i386_btop(0xfffef000));
252 }
253
254 static int
255 elan_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr)
256 {
257 return(ENOENT);
258 }
259
Cache object: 891bc188a29888890558235e3764f948
|