obio.c revision 1.31 1 /* $NetBSD: obio.c,v 1.31 2010/12/05 13:33:50 phx Exp $ */
2
3 /*-
4 * Copyright (C) 1998 Internet Research Institute, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by
18 * Internet Research Institute, Inc.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: obio.c,v 1.31 2010/12/05 13:33:50 phx Exp $");
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/device.h>
41 #include <sys/sysctl.h>
42
43 #include <dev/pci/pcivar.h>
44 #include <dev/pci/pcidevs.h>
45
46 #include <dev/ofw/openfirm.h>
47
48 #include <machine/autoconf.h>
49
50 #include <macppc/dev/obiovar.h>
51
52 #include <powerpc/cpu.h>
53
54 #include "opt_obio.h"
55
56 #ifdef OBIO_DEBUG
57 # define DPRINTF printf
58 #else
59 # define DPRINTF while (0) printf
60 #endif
61
62 static void obio_attach(struct device *, struct device *, void *);
63 static int obio_match(struct device *, struct cfdata *, void *);
64 static int obio_print(void *, const char *);
65
66 struct obio_softc {
67 struct device sc_dev;
68 bus_space_tag_t sc_tag;
69 bus_space_handle_t sc_bh;
70 int sc_node;
71 #ifdef OBIO_SPEED_CONTROL
72 int sc_voltage;
73 int sc_busspeed;
74 #endif
75 };
76
77 static struct obio_softc *obio0 = NULL;
78
79 #ifdef OBIO_SPEED_CONTROL
80 static void obio_setup_gpios(struct obio_softc *, int);
81 static void obio_set_cpu_speed(struct obio_softc *, int);
82 static int obio_get_cpu_speed(struct obio_softc *);
83 static int sysctl_cpuspeed_temp(SYSCTLFN_ARGS);
84
85 static const char *keylargo[] = {"Keylargo",
86 "AAPL,Keylargo",
87 NULL};
88
89 #endif
90
91 CFATTACH_DECL(obio, sizeof(struct obio_softc),
92 obio_match, obio_attach, NULL, NULL);
93
94 int
95 obio_match(struct device *parent, struct cfdata *cf, void *aux)
96 {
97 struct pci_attach_args *pa = aux;
98
99 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE)
100 switch (PCI_PRODUCT(pa->pa_id)) {
101 case PCI_PRODUCT_APPLE_GC:
102 case PCI_PRODUCT_APPLE_OHARE:
103 case PCI_PRODUCT_APPLE_HEATHROW:
104 case PCI_PRODUCT_APPLE_PADDINGTON:
105 case PCI_PRODUCT_APPLE_KEYLARGO:
106 case PCI_PRODUCT_APPLE_PANGEA_MACIO:
107 case PCI_PRODUCT_APPLE_INTREPID:
108 case PCI_PRODUCT_APPLE_K2:
109 return 1;
110 }
111
112 return 0;
113 }
114
115 /*
116 * Attach all the sub-devices we can find
117 */
118 void
119 obio_attach(struct device *parent, struct device *self, void *aux)
120 {
121 struct obio_softc *sc = (struct obio_softc *)self;
122 struct pci_attach_args *pa = aux;
123 struct confargs ca;
124 bus_space_handle_t bsh;
125 int node, child, namelen, error;
126 u_int reg[20];
127 int intr[6], parent_intr = 0, parent_nintr = 0;
128 char name[32];
129 char compat[32];
130
131 #ifdef OBIO_SPEED_CONTROL
132 sc->sc_voltage = -1;
133 sc->sc_busspeed = -1;
134 #endif
135
136 switch (PCI_PRODUCT(pa->pa_id)) {
137
138 case PCI_PRODUCT_APPLE_GC:
139 case PCI_PRODUCT_APPLE_OHARE:
140 case PCI_PRODUCT_APPLE_HEATHROW:
141 case PCI_PRODUCT_APPLE_PADDINGTON:
142 case PCI_PRODUCT_APPLE_KEYLARGO:
143 case PCI_PRODUCT_APPLE_PANGEA_MACIO:
144 case PCI_PRODUCT_APPLE_INTREPID:
145 node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag);
146 if (node == -1)
147 node = OF_finddevice("mac-io");
148 if (node == -1)
149 node = OF_finddevice("/pci/mac-io");
150 break;
151 case PCI_PRODUCT_APPLE_K2:
152 node = OF_finddevice("mac-io");
153 break;
154
155 default:
156 node = -1;
157 break;
158 }
159 if (node == -1)
160 panic("macio not found or unknown");
161
162 sc->sc_node = node;
163
164 #if defined (PMAC_G5)
165 if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 20)
166 {
167 return;
168 }
169 #else
170 if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 12)
171 return;
172 #endif /* PMAC_G5 */
173
174 /*
175 * XXX
176 * This relies on the primary obio always attaching first which is
177 * true on the PowerBook 3400c and similar machines but may or may
178 * not work on others. We can't rely on the node name since Apple
179 * didn't follow anything remotely resembling a consistent naming
180 * scheme.
181 */
182 if (obio0 == NULL)
183 obio0 = sc;
184
185 ca.ca_baseaddr = reg[2];
186 ca.ca_tag = pa->pa_memt;
187 sc->sc_tag = pa->pa_memt;
188 error = bus_space_map (pa->pa_memt, ca.ca_baseaddr, 0x80, 0, &bsh);
189 if (error)
190 panic(": failed to map mac-io %#x", ca.ca_baseaddr);
191 sc->sc_bh = bsh;
192
193 printf(": addr 0x%x\n", ca.ca_baseaddr);
194
195 /* Enable internal modem (KeyLargo) */
196 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_KEYLARGO) {
197 aprint_normal("%s: enabling KeyLargo internal modem\n",
198 self->dv_xname);
199 bus_space_write_4(ca.ca_tag, bsh, 0x40,
200 bus_space_read_4(ca.ca_tag, bsh, 0x40) & ~(1<<25));
201 }
202
203 /* Enable internal modem (Pangea) */
204 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PANGEA_MACIO) {
205 /* set reset */
206 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x04);
207 /* power modem on */
208 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x02, 0x04);
209 /* unset reset */
210 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x05);
211 }
212
213 /* Gatwick and Paddington use same product ID */
214 namelen = OF_getprop(node, "compatible", compat, sizeof(compat));
215
216 if (strcmp(compat, "gatwick") == 0) {
217 parent_nintr = OF_getprop(node, "AAPL,interrupts", intr,
218 sizeof(intr));
219 parent_intr = intr[0];
220 } else {
221 /* Enable CD and microphone sound input. */
222 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PADDINGTON)
223 bus_space_write_1(ca.ca_tag, bsh, 0x37, 0x03);
224 }
225
226 for (child = OF_child(node); child; child = OF_peer(child)) {
227 namelen = OF_getprop(child, "name", name, sizeof(name));
228 if (namelen < 0)
229 continue;
230 if (namelen >= sizeof(name))
231 continue;
232
233 #ifdef OBIO_SPEED_CONTROL
234 if (strcmp(name, "gpio") == 0) {
235
236 obio_setup_gpios(sc, child);
237 continue;
238 }
239 #endif
240
241 name[namelen] = 0;
242 ca.ca_name = name;
243 ca.ca_node = child;
244 ca.ca_tag = pa->pa_memt;
245
246 ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg));
247
248 if (strcmp(compat, "gatwick") != 0) {
249 ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
250 sizeof(intr));
251 if (ca.ca_nintr == -1)
252 ca.ca_nintr = OF_getprop(child, "interrupts", intr,
253 sizeof(intr));
254 } else {
255 intr[0] = parent_intr;
256 ca.ca_nintr = parent_nintr;
257 }
258 ca.ca_reg = reg;
259 ca.ca_intr = intr;
260
261 config_found(self, &ca, obio_print);
262 }
263 }
264
265 static const char * const skiplist[] = {
266 "interrupt-controller",
267 "gpio",
268 "escc-legacy",
269 "timer",
270 "i2c",
271 "power-mgt",
272 "escc",
273 "battery",
274 "backlight"
275
276 };
277
278 #define N_LIST (sizeof(skiplist) / sizeof(skiplist[0]))
279
280 int
281 obio_print(void *aux, const char *obio)
282 {
283 struct confargs *ca = aux;
284 int i;
285
286 for (i = 0; i < N_LIST; i++)
287 if (strcmp(ca->ca_name, skiplist[i]) == 0)
288 return QUIET;
289
290 if (obio)
291 aprint_normal("%s at %s", ca->ca_name, obio);
292
293 if (ca->ca_nreg > 0)
294 aprint_normal(" offset 0x%x", ca->ca_reg[0]);
295
296 return UNCONF;
297 }
298
299 void obio_write_4(int offset, uint32_t value)
300 {
301 if (obio0 == NULL)
302 return;
303 bus_space_write_4(obio0->sc_tag, obio0->sc_bh, offset, value);
304 }
305
306 void obio_write_1(int offset, uint8_t value)
307 {
308 if (obio0 == NULL)
309 return;
310 bus_space_write_1(obio0->sc_tag, obio0->sc_bh, offset, value);
311 }
312
313 uint32_t obio_read_4(int offset)
314 {
315 if (obio0 == NULL)
316 return 0xffffffff;
317 return bus_space_read_4(obio0->sc_tag, obio0->sc_bh, offset);
318 }
319
320 uint8_t obio_read_1(int offset)
321 {
322 if (obio0 == NULL)
323 return 0xff;
324 return bus_space_read_1(obio0->sc_tag, obio0->sc_bh, offset);
325 }
326
327 #ifdef OBIO_SPEED_CONTROL
328
329 static void
330 obio_setup_gpios(struct obio_softc *sc, int node)
331 {
332 uint32_t gpio_base, reg[6];
333 struct sysctlnode *sysctl_node;
334 char name[32];
335 int child, use_dfs;
336
337 if (of_compatible(sc->sc_node, keylargo) == -1)
338 return;
339
340 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 4)
341 return;
342
343 gpio_base = reg[0];
344 DPRINTF("gpio_base: %02x\n", gpio_base);
345
346 /* now look for voltage and bus speed gpios */
347 use_dfs = 0;
348 for (child = OF_child(node); child; child = OF_peer(child)) {
349
350 if (OF_getprop(child, "name", name, sizeof(name)) < 1)
351 continue;
352
353 if (OF_getprop(child, "reg", reg, sizeof(reg)) < 4)
354 continue;
355
356 /*
357 * These register offsets either have to be added to the obio
358 * base address or to the gpio base address. This differs
359 * even in the same OF-tree! So we guess the offset is
360 * based on obio when it is larger than the gpio_base.
361 */
362 if (reg[0] >= gpio_base)
363 reg[0] -= gpio_base;
364
365 if (strcmp(name, "frequency-gpio") == 0) {
366 DPRINTF("found frequency_gpio at %02x\n", reg[0]);
367 sc->sc_busspeed = gpio_base + reg[0];
368 }
369 if (strcmp(name, "voltage-gpio") == 0) {
370 DPRINTF("found voltage_gpio at %02x\n", reg[0]);
371 sc->sc_voltage = gpio_base + reg[0];
372 }
373 if (strcmp(name, "cpu-vcore-select") == 0) {
374 DPRINTF("found cpu-vcore-select at %02x\n", reg[0]);
375 sc->sc_voltage = gpio_base + reg[0];
376 /* frequency gpio is not needed, we use cpu's DFS */
377 use_dfs = 1;
378 }
379 }
380
381 if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0 && !use_dfs))
382 return;
383
384 printf("%s: enabling Intrepid CPU speed control\n",
385 sc->sc_dev.dv_xname);
386
387 sysctl_node = NULL;
388 sysctl_createv(NULL, 0, NULL,
389 (const struct sysctlnode **)&sysctl_node,
390 CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE,
391 CTLTYPE_INT, "cpu_speed", "CPU speed", sysctl_cpuspeed_temp,
392 (unsigned long)sc, NULL, 0, CTL_MACHDEP, CTL_CREATE, CTL_EOL);
393 if (sysctl_node != NULL)
394 sysctl_node->sysctl_data = (void *)sc;
395 }
396
397 static void
398 obio_set_cpu_speed(struct obio_softc *sc, int fast)
399 {
400
401 if (sc->sc_voltage < 0)
402 return;
403
404 if (sc->sc_busspeed >= 0) {
405 /* set voltage and speed via gpio */
406 if (fast) {
407 bus_space_write_1(sc->sc_tag, sc->sc_bh,
408 sc->sc_voltage, 5);
409 bus_space_write_1(sc->sc_tag, sc->sc_bh,
410 sc->sc_busspeed, 5);
411 } else {
412 bus_space_write_1(sc->sc_tag, sc->sc_bh,
413 sc->sc_busspeed, 4);
414 bus_space_write_1(sc->sc_tag, sc->sc_bh,
415 sc->sc_voltage, 4);
416 }
417 }
418 else {
419 /* set voltage via gpio and speed via the 7447A's DFS bit */
420 if (fast) {
421 bus_space_write_1(sc->sc_tag, sc->sc_bh,
422 sc->sc_voltage, 5);
423 DELAY(1000);
424 }
425
426 /* set DFS for all cpus */
427 cpu_set_dfs(fast ? 1 : 2);
428 DELAY(100);
429
430 if (!fast) {
431 bus_space_write_1(sc->sc_tag, sc->sc_bh,
432 sc->sc_voltage, 4);
433 DELAY(1000);
434 }
435 }
436 }
437
438 static int
439 obio_get_cpu_speed(struct obio_softc *sc)
440 {
441
442 if (sc->sc_voltage < 0)
443 return 0;
444
445 if (sc->sc_busspeed >= 0) {
446 if (bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed)
447 & 1)
448 return 1;
449 }
450 else
451 return cpu_get_dfs() == 1;
452
453 return 0;
454 }
455
456 static int
457 sysctl_cpuspeed_temp(SYSCTLFN_ARGS)
458 {
459 struct sysctlnode node = *rnode;
460 struct obio_softc *sc = node.sysctl_data;
461 const int *np = newp;
462 int speed, nd = 0;
463
464 speed = obio_get_cpu_speed(sc);
465 node.sysctl_idata = speed;
466 if (np) {
467 /* we're asked to write */
468 nd = *np;
469 node.sysctl_data = &speed;
470 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
471 int new_reg;
472
473 new_reg = (max(0, min(1, node.sysctl_idata)));
474 obio_set_cpu_speed(sc, new_reg);
475 return 0;
476 }
477 return EINVAL;
478 } else {
479 node.sysctl_size = 4;
480 return(sysctl_lookup(SYSCTLFN_CALL(&node)));
481 }
482 }
483
484 #endif /* OBIO_SPEEDCONTROL */
485