obio.c revision 1.54 1 1.54 macallan /* $NetBSD: obio.c,v 1.54 2025/08/20 07:53:33 macallan Exp $ */
2 1.2 tsubai
3 1.2 tsubai /*-
4 1.2 tsubai * Copyright (C) 1998 Internet Research Institute, Inc.
5 1.2 tsubai * All rights reserved.
6 1.2 tsubai *
7 1.2 tsubai * Redistribution and use in source and binary forms, with or without
8 1.2 tsubai * modification, are permitted provided that the following conditions
9 1.2 tsubai * are met:
10 1.2 tsubai * 1. Redistributions of source code must retain the above copyright
11 1.2 tsubai * notice, this list of conditions and the following disclaimer.
12 1.2 tsubai * 2. Redistributions in binary form must reproduce the above copyright
13 1.2 tsubai * notice, this list of conditions and the following disclaimer in the
14 1.2 tsubai * documentation and/or other materials provided with the distribution.
15 1.2 tsubai * 3. All advertising materials mentioning features or use of this software
16 1.2 tsubai * must display the following acknowledgement:
17 1.2 tsubai * This product includes software developed by
18 1.2 tsubai * Internet Research Institute, Inc.
19 1.2 tsubai * 4. The name of the author may not be used to endorse or promote products
20 1.2 tsubai * derived from this software without specific prior written permission.
21 1.2 tsubai *
22 1.2 tsubai * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 1.2 tsubai * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 1.2 tsubai * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 1.2 tsubai * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 1.2 tsubai * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 1.2 tsubai * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 1.2 tsubai * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 1.2 tsubai * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 1.2 tsubai * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 1.2 tsubai * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 1.2 tsubai */
33 1.19 lukem
34 1.19 lukem #include <sys/cdefs.h>
35 1.54 macallan __KERNEL_RCSID(0, "$NetBSD: obio.c,v 1.54 2025/08/20 07:53:33 macallan Exp $");
36 1.2 tsubai
37 1.1 tsubai #include <sys/param.h>
38 1.1 tsubai #include <sys/systm.h>
39 1.1 tsubai #include <sys/kernel.h>
40 1.1 tsubai #include <sys/device.h>
41 1.27 garbled #include <sys/sysctl.h>
42 1.54 macallan #include <sys/kthread.h>
43 1.1 tsubai
44 1.1 tsubai #include <dev/pci/pcivar.h>
45 1.1 tsubai #include <dev/pci/pcidevs.h>
46 1.54 macallan #include <dev/sysmon/sysmonvar.h>
47 1.1 tsubai
48 1.1 tsubai #include <dev/ofw/openfirm.h>
49 1.1 tsubai
50 1.1 tsubai #include <machine/autoconf.h>
51 1.1 tsubai
52 1.28 macallan #include <macppc/dev/obiovar.h>
53 1.28 macallan
54 1.30 phx #include <powerpc/cpu.h>
55 1.36 macallan #include <sys/cpufreq.h>
56 1.54 macallan #include <dev/led.h>
57 1.54 macallan #include <macppc/dev/fancontrolvar.h>
58 1.30 phx
59 1.27 garbled #include "opt_obio.h"
60 1.27 garbled
61 1.27 garbled #ifdef OBIO_DEBUG
62 1.27 garbled # define DPRINTF printf
63 1.27 garbled #else
64 1.27 garbled # define DPRINTF while (0) printf
65 1.27 garbled #endif
66 1.27 garbled
67 1.33 matt static void obio_attach(device_t, device_t, void *);
68 1.33 matt static int obio_match(device_t, cfdata_t, void *);
69 1.27 garbled static int obio_print(void *, const char *);
70 1.1 tsubai
71 1.1 tsubai struct obio_softc {
72 1.34 macallan device_t sc_dev;
73 1.27 garbled bus_space_tag_t sc_tag;
74 1.27 garbled bus_space_handle_t sc_bh;
75 1.1 tsubai int sc_node;
76 1.27 garbled int sc_voltage;
77 1.27 garbled int sc_busspeed;
78 1.32 macallan int sc_spd_hi, sc_spd_lo;
79 1.36 macallan struct cpufreq sc_cf;
80 1.54 macallan /* Xserve G4 pwm fan control */
81 1.54 macallan fancontrol_zone_t sc_zones[2];
82 1.54 macallan int sc_duty[2];
83 1.54 macallan int sc_target[2];
84 1.54 macallan int sc_pwm;
85 1.54 macallan lwp_t *sc_thread;
86 1.54 macallan int sc_dying;
87 1.1 tsubai };
88 1.1 tsubai
89 1.28 macallan static struct obio_softc *obio0 = NULL;
90 1.28 macallan
91 1.27 garbled static void obio_setup_gpios(struct obio_softc *, int);
92 1.27 garbled static void obio_set_cpu_speed(struct obio_softc *, int);
93 1.27 garbled static int obio_get_cpu_speed(struct obio_softc *);
94 1.27 garbled static int sysctl_cpuspeed_temp(SYSCTLFN_ARGS);
95 1.32 macallan static int sysctl_cpuspeed_cur(SYSCTLFN_ARGS);
96 1.32 macallan static int sysctl_cpuspeed_available(SYSCTLFN_ARGS);
97 1.54 macallan static int sysctl_gpio(SYSCTLFN_ARGS);
98 1.54 macallan #ifdef OBIO_GPIO_DEBUG
99 1.54 macallan static int sysctl_level(SYSCTLFN_ARGS);
100 1.54 macallan #endif
101 1.54 macallan static int sysctl_pwm(SYSCTLFN_ARGS);
102 1.36 macallan static void obio_get_freq(void *, void *);
103 1.36 macallan static void obio_set_freq(void *, void *);
104 1.54 macallan static int gpio_get(void *);
105 1.54 macallan static void gpio_set(void *, int);
106 1.54 macallan
107 1.54 macallan static bool is_cpu(const envsys_data_t *);
108 1.54 macallan static bool is_case(const envsys_data_t *);
109 1.54 macallan
110 1.54 macallan static int obio_set_rpm(void *, int, int);
111 1.54 macallan static int obio_get_rpm(void *, int);
112 1.54 macallan static int obio_get_pwm(int);
113 1.54 macallan static void obio_set_pwm(int, int);
114 1.54 macallan
115 1.54 macallan static void obio_adjust(void *);
116 1.54 macallan
117 1.27 garbled static const char *keylargo[] = {"Keylargo",
118 1.27 garbled "AAPL,Keylargo",
119 1.54 macallan "K2-Keylargo",
120 1.27 garbled NULL};
121 1.27 garbled
122 1.54 macallan static const char *xserve[] = {"RackMac1,2",
123 1.54 macallan NULL};
124 1.54 macallan
125 1.1 tsubai
126 1.34 macallan CFATTACH_DECL_NEW(obio, sizeof(struct obio_softc),
127 1.16 thorpej obio_match, obio_attach, NULL, NULL);
128 1.1 tsubai
129 1.1 tsubai int
130 1.33 matt obio_match(device_t parent, cfdata_t cf, void *aux)
131 1.1 tsubai {
132 1.1 tsubai struct pci_attach_args *pa = aux;
133 1.1 tsubai
134 1.2 tsubai if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE)
135 1.2 tsubai switch (PCI_PRODUCT(pa->pa_id)) {
136 1.7 tsubai case PCI_PRODUCT_APPLE_GC:
137 1.7 tsubai case PCI_PRODUCT_APPLE_OHARE:
138 1.7 tsubai case PCI_PRODUCT_APPLE_HEATHROW:
139 1.7 tsubai case PCI_PRODUCT_APPLE_PADDINGTON:
140 1.7 tsubai case PCI_PRODUCT_APPLE_KEYLARGO:
141 1.14 tsubai case PCI_PRODUCT_APPLE_PANGEA_MACIO:
142 1.18 hamajima case PCI_PRODUCT_APPLE_INTREPID:
143 1.24 sanjayl case PCI_PRODUCT_APPLE_K2:
144 1.39 macallan case PCI_PRODUCT_APPLE_SHASTA:
145 1.2 tsubai return 1;
146 1.2 tsubai }
147 1.1 tsubai
148 1.1 tsubai return 0;
149 1.1 tsubai }
150 1.1 tsubai
151 1.1 tsubai /*
152 1.1 tsubai * Attach all the sub-devices we can find
153 1.1 tsubai */
154 1.1 tsubai void
155 1.33 matt obio_attach(device_t parent, device_t self, void *aux)
156 1.1 tsubai {
157 1.33 matt struct obio_softc *sc = device_private(self);
158 1.2 tsubai struct pci_attach_args *pa = aux;
159 1.1 tsubai struct confargs ca;
160 1.27 garbled bus_space_handle_t bsh;
161 1.54 macallan int node, child, namelen, error, root, is_xserve = 0;
162 1.1 tsubai u_int reg[20];
163 1.21 briggs int intr[6], parent_intr = 0, parent_nintr = 0;
164 1.40 macallan int map_size = 0x1000;
165 1.1 tsubai char name[32];
166 1.21 briggs char compat[32];
167 1.1 tsubai
168 1.34 macallan sc->sc_dev = self;
169 1.27 garbled sc->sc_voltage = -1;
170 1.27 garbled sc->sc_busspeed = -1;
171 1.32 macallan sc->sc_spd_lo = 600;
172 1.32 macallan sc->sc_spd_hi = 800;
173 1.54 macallan sc->sc_dying = 0;
174 1.54 macallan root = OF_finddevice("/");
175 1.54 macallan is_xserve = of_compatible(root, xserve);
176 1.54 macallan
177 1.2 tsubai switch (PCI_PRODUCT(pa->pa_id)) {
178 1.2 tsubai
179 1.7 tsubai case PCI_PRODUCT_APPLE_GC:
180 1.7 tsubai case PCI_PRODUCT_APPLE_OHARE:
181 1.7 tsubai case PCI_PRODUCT_APPLE_HEATHROW:
182 1.7 tsubai case PCI_PRODUCT_APPLE_PADDINGTON:
183 1.7 tsubai case PCI_PRODUCT_APPLE_KEYLARGO:
184 1.14 tsubai case PCI_PRODUCT_APPLE_PANGEA_MACIO:
185 1.18 hamajima case PCI_PRODUCT_APPLE_INTREPID:
186 1.21 briggs node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag);
187 1.43 mrg if (node == -1)
188 1.21 briggs node = OF_finddevice("mac-io");
189 1.43 mrg if (node == -1)
190 1.43 mrg node = OF_finddevice("/pci/mac-io");
191 1.2 tsubai break;
192 1.24 sanjayl case PCI_PRODUCT_APPLE_K2:
193 1.39 macallan case PCI_PRODUCT_APPLE_SHASTA:
194 1.24 sanjayl node = OF_finddevice("mac-io");
195 1.45 macallan map_size = 0x10000;
196 1.24 sanjayl break;
197 1.2 tsubai
198 1.2 tsubai default:
199 1.27 garbled node = -1;
200 1.27 garbled break;
201 1.2 tsubai }
202 1.27 garbled if (node == -1)
203 1.27 garbled panic("macio not found or unknown");
204 1.2 tsubai
205 1.1 tsubai sc->sc_node = node;
206 1.1 tsubai
207 1.24 sanjayl #if defined (PMAC_G5)
208 1.24 sanjayl if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 20)
209 1.24 sanjayl {
210 1.24 sanjayl return;
211 1.24 sanjayl }
212 1.24 sanjayl #else
213 1.1 tsubai if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 12)
214 1.1 tsubai return;
215 1.24 sanjayl #endif /* PMAC_G5 */
216 1.24 sanjayl
217 1.28 macallan /*
218 1.28 macallan * XXX
219 1.28 macallan * This relies on the primary obio always attaching first which is
220 1.28 macallan * true on the PowerBook 3400c and similar machines but may or may
221 1.28 macallan * not work on others. We can't rely on the node name since Apple
222 1.28 macallan * didn't follow anything remotely resembling a consistent naming
223 1.28 macallan * scheme.
224 1.28 macallan */
225 1.28 macallan if (obio0 == NULL)
226 1.28 macallan obio0 = sc;
227 1.28 macallan
228 1.1 tsubai ca.ca_baseaddr = reg[2];
229 1.27 garbled ca.ca_tag = pa->pa_memt;
230 1.27 garbled sc->sc_tag = pa->pa_memt;
231 1.40 macallan error = bus_space_map (pa->pa_memt, ca.ca_baseaddr, map_size, 0, &bsh);
232 1.27 garbled if (error)
233 1.27 garbled panic(": failed to map mac-io %#x", ca.ca_baseaddr);
234 1.27 garbled sc->sc_bh = bsh;
235 1.1 tsubai
236 1.1 tsubai printf(": addr 0x%x\n", ca.ca_baseaddr);
237 1.13 tsubai
238 1.20 briggs /* Enable internal modem (KeyLargo) */
239 1.20 briggs if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_KEYLARGO) {
240 1.27 garbled aprint_normal("%s: enabling KeyLargo internal modem\n",
241 1.38 chs device_xname(self));
242 1.27 garbled bus_space_write_4(ca.ca_tag, bsh, 0x40,
243 1.27 garbled bus_space_read_4(ca.ca_tag, bsh, 0x40) & ~(1<<25));
244 1.20 briggs }
245 1.26 macallan
246 1.20 briggs /* Enable internal modem (Pangea) */
247 1.20 briggs if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PANGEA_MACIO) {
248 1.27 garbled /* set reset */
249 1.27 garbled bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x04);
250 1.27 garbled /* power modem on */
251 1.27 garbled bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x02, 0x04);
252 1.27 garbled /* unset reset */
253 1.27 garbled bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x05);
254 1.20 briggs }
255 1.20 briggs
256 1.21 briggs /* Gatwick and Paddington use same product ID */
257 1.21 briggs namelen = OF_getprop(node, "compatible", compat, sizeof(compat));
258 1.1 tsubai
259 1.21 briggs if (strcmp(compat, "gatwick") == 0) {
260 1.21 briggs parent_nintr = OF_getprop(node, "AAPL,interrupts", intr,
261 1.21 briggs sizeof(intr));
262 1.21 briggs parent_intr = intr[0];
263 1.21 briggs } else {
264 1.21 briggs /* Enable CD and microphone sound input. */
265 1.21 briggs if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PADDINGTON)
266 1.27 garbled bus_space_write_1(ca.ca_tag, bsh, 0x37, 0x03);
267 1.21 briggs }
268 1.27 garbled
269 1.51 thorpej devhandle_t selfh = device_handle(self);
270 1.1 tsubai for (child = OF_child(node); child; child = OF_peer(child)) {
271 1.1 tsubai namelen = OF_getprop(child, "name", name, sizeof(name));
272 1.1 tsubai if (namelen < 0)
273 1.1 tsubai continue;
274 1.1 tsubai if (namelen >= sizeof(name))
275 1.1 tsubai continue;
276 1.1 tsubai
277 1.27 garbled if (strcmp(name, "gpio") == 0) {
278 1.27 garbled
279 1.27 garbled obio_setup_gpios(sc, child);
280 1.27 garbled continue;
281 1.27 garbled }
282 1.27 garbled
283 1.1 tsubai name[namelen] = 0;
284 1.1 tsubai ca.ca_name = name;
285 1.1 tsubai ca.ca_node = child;
286 1.25 macallan ca.ca_tag = pa->pa_memt;
287 1.26 macallan
288 1.21 briggs ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg));
289 1.26 macallan
290 1.21 briggs if (strcmp(compat, "gatwick") != 0) {
291 1.21 briggs ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
292 1.5 tsubai sizeof(intr));
293 1.21 briggs if (ca.ca_nintr == -1)
294 1.21 briggs ca.ca_nintr = OF_getprop(child, "interrupts", intr,
295 1.21 briggs sizeof(intr));
296 1.21 briggs } else {
297 1.21 briggs intr[0] = parent_intr;
298 1.21 briggs ca.ca_nintr = parent_nintr;
299 1.21 briggs }
300 1.1 tsubai ca.ca_reg = reg;
301 1.1 tsubai ca.ca_intr = intr;
302 1.1 tsubai
303 1.49 thorpej config_found(self, &ca, obio_print,
304 1.51 thorpej CFARGS(.devhandle = devhandle_from_of(selfh, child)));
305 1.1 tsubai }
306 1.54 macallan if (is_xserve) {
307 1.54 macallan struct sysctlnode *me;
308 1.54 macallan
309 1.54 macallan printf("starting Xserve fan control\n");
310 1.54 macallan
311 1.54 macallan sysctl_createv(NULL, 0, NULL, (void *) &me,
312 1.54 macallan CTLFLAG_READWRITE,
313 1.54 macallan CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
314 1.54 macallan NULL, 0, NULL, 0,
315 1.54 macallan CTL_MACHDEP, CTL_CREATE, CTL_EOL);
316 1.54 macallan
317 1.54 macallan sc->sc_zones[0].name = "CPU";
318 1.54 macallan sc->sc_zones[0].filter = is_cpu;
319 1.54 macallan sc->sc_zones[0].cookie = sc;
320 1.54 macallan sc->sc_zones[0].get_rpm = obio_get_rpm;
321 1.54 macallan sc->sc_zones[0].set_rpm = obio_set_rpm;
322 1.54 macallan sc->sc_zones[0].Tmin = 30;
323 1.54 macallan sc->sc_zones[0].Tmax = 60;
324 1.54 macallan sc->sc_zones[0].nfans = 1;
325 1.54 macallan sc->sc_zones[0].fans[0].name = "CPU fan";
326 1.54 macallan sc->sc_zones[0].fans[0].num = 0;
327 1.54 macallan sc->sc_zones[0].fans[0].min_rpm = 800;
328 1.54 macallan sc->sc_zones[0].fans[0].max_rpm = 6000;
329 1.54 macallan sc->sc_duty[0] = obio_get_pwm(0);
330 1.54 macallan fancontrol_init_zone(&sc->sc_zones[0], me);
331 1.54 macallan
332 1.54 macallan sc->sc_zones[1].name = "Case";
333 1.54 macallan sc->sc_zones[1].filter = is_case;
334 1.54 macallan sc->sc_zones[1].cookie = sc;
335 1.54 macallan sc->sc_zones[1].get_rpm = obio_get_rpm;
336 1.54 macallan sc->sc_zones[1].set_rpm = obio_set_rpm;
337 1.54 macallan sc->sc_zones[1].Tmin = 30;
338 1.54 macallan sc->sc_zones[1].Tmax = 50;
339 1.54 macallan sc->sc_zones[1].nfans = 1;
340 1.54 macallan sc->sc_zones[1].fans[0].name = "Case fan";
341 1.54 macallan sc->sc_zones[1].fans[0].num = 1;
342 1.54 macallan sc->sc_zones[1].fans[0].min_rpm = 800;
343 1.54 macallan sc->sc_zones[1].fans[0].max_rpm = 6000;
344 1.54 macallan sc->sc_duty[1] = obio_get_pwm(1);
345 1.54 macallan fancontrol_init_zone(&sc->sc_zones[1], me);
346 1.54 macallan
347 1.54 macallan kthread_create(PRI_NONE, 0, curcpu(), obio_adjust, sc,
348 1.54 macallan &sc->sc_thread, "fan control");
349 1.54 macallan }
350 1.1 tsubai }
351 1.1 tsubai
352 1.27 garbled static const char * const skiplist[] = {
353 1.8 tsubai "interrupt-controller",
354 1.52 macallan "chrp,open-pic",
355 1.52 macallan "open-pic",
356 1.53 macallan "mpic",
357 1.8 tsubai "gpio",
358 1.8 tsubai "escc-legacy",
359 1.8 tsubai "timer",
360 1.9 tsubai "i2c",
361 1.24 sanjayl "power-mgt",
362 1.27 garbled "escc",
363 1.27 garbled "battery",
364 1.27 garbled "backlight"
365 1.24 sanjayl
366 1.8 tsubai };
367 1.8 tsubai
368 1.8 tsubai #define N_LIST (sizeof(skiplist) / sizeof(skiplist[0]))
369 1.8 tsubai
370 1.1 tsubai int
371 1.29 dsl obio_print(void *aux, const char *obio)
372 1.1 tsubai {
373 1.1 tsubai struct confargs *ca = aux;
374 1.8 tsubai int i;
375 1.8 tsubai
376 1.8 tsubai for (i = 0; i < N_LIST; i++)
377 1.8 tsubai if (strcmp(ca->ca_name, skiplist[i]) == 0)
378 1.8 tsubai return QUIET;
379 1.1 tsubai
380 1.1 tsubai if (obio)
381 1.17 thorpej aprint_normal("%s at %s", ca->ca_name, obio);
382 1.1 tsubai
383 1.1 tsubai if (ca->ca_nreg > 0)
384 1.17 thorpej aprint_normal(" offset 0x%x", ca->ca_reg[0]);
385 1.1 tsubai
386 1.1 tsubai return UNCONF;
387 1.1 tsubai }
388 1.27 garbled
389 1.28 macallan void obio_write_4(int offset, uint32_t value)
390 1.28 macallan {
391 1.28 macallan if (obio0 == NULL)
392 1.28 macallan return;
393 1.28 macallan bus_space_write_4(obio0->sc_tag, obio0->sc_bh, offset, value);
394 1.28 macallan }
395 1.28 macallan
396 1.28 macallan void obio_write_1(int offset, uint8_t value)
397 1.28 macallan {
398 1.28 macallan if (obio0 == NULL)
399 1.28 macallan return;
400 1.28 macallan bus_space_write_1(obio0->sc_tag, obio0->sc_bh, offset, value);
401 1.28 macallan }
402 1.28 macallan
403 1.28 macallan uint32_t obio_read_4(int offset)
404 1.28 macallan {
405 1.28 macallan if (obio0 == NULL)
406 1.28 macallan return 0xffffffff;
407 1.28 macallan return bus_space_read_4(obio0->sc_tag, obio0->sc_bh, offset);
408 1.28 macallan }
409 1.28 macallan
410 1.28 macallan uint8_t obio_read_1(int offset)
411 1.28 macallan {
412 1.28 macallan if (obio0 == NULL)
413 1.28 macallan return 0xff;
414 1.28 macallan return bus_space_read_1(obio0->sc_tag, obio0->sc_bh, offset);
415 1.28 macallan }
416 1.28 macallan
417 1.45 macallan int
418 1.45 macallan obio_space_map(bus_addr_t addr, bus_size_t size, bus_space_handle_t *bh)
419 1.45 macallan {
420 1.45 macallan if (obio0 == NULL)
421 1.45 macallan return 0xff;
422 1.45 macallan return bus_space_subregion(obio0->sc_tag, obio0->sc_bh,
423 1.45 macallan addr & 0xfffff, size, bh);
424 1.45 macallan }
425 1.27 garbled
426 1.27 garbled static void
427 1.36 macallan obio_setup_cpufreq(device_t dev)
428 1.36 macallan {
429 1.36 macallan struct obio_softc *sc = device_private(dev);
430 1.36 macallan int ret;
431 1.36 macallan
432 1.36 macallan ret = cpufreq_register(&sc->sc_cf);
433 1.36 macallan if (ret != 0)
434 1.36 macallan aprint_error_dev(sc->sc_dev, "cpufreq_register() failed, error %d\n", ret);
435 1.36 macallan }
436 1.36 macallan
437 1.36 macallan static void
438 1.27 garbled obio_setup_gpios(struct obio_softc *sc, int node)
439 1.27 garbled {
440 1.31 phx uint32_t gpio_base, reg[6];
441 1.54 macallan const struct sysctlnode *sysctl_node, *me, *freq, *gpio;
442 1.36 macallan struct cpufreq *cf = &sc->sc_cf;
443 1.27 garbled char name[32];
444 1.32 macallan int child, use_dfs, cpunode, hiclock;
445 1.27 garbled
446 1.48 thorpej if (! of_compatible(sc->sc_node, keylargo))
447 1.27 garbled return;
448 1.27 garbled
449 1.27 garbled if (OF_getprop(node, "reg", reg, sizeof(reg)) < 4)
450 1.27 garbled return;
451 1.27 garbled
452 1.27 garbled gpio_base = reg[0];
453 1.27 garbled DPRINTF("gpio_base: %02x\n", gpio_base);
454 1.27 garbled
455 1.54 macallan if (sysctl_createv(NULL, 0, NULL,
456 1.54 macallan &gpio,
457 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_NODE, "gpio", NULL, NULL,
458 1.54 macallan 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL) != 0)
459 1.54 macallan printf("couldn't create 'gpio' node\n");
460 1.54 macallan #ifdef OBIO_GPIO_DEBUG
461 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
462 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "level0", NULL,
463 1.54 macallan sysctl_level, 1, (void *)0x50, 0, CTL_HW,
464 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
465 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
466 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "level1", NULL,
467 1.54 macallan sysctl_level, 1, (void *)0x54, 0, CTL_HW,
468 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
469 1.54 macallan #endif
470 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
471 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "pwm0", NULL,
472 1.54 macallan sysctl_pwm, 1, (void *)0, 0, CTL_HW,
473 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
474 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
475 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "pwm1", NULL,
476 1.54 macallan sysctl_pwm, 1, (void *)1, 0, CTL_HW,
477 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
478 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
479 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "pwm2", NULL,
480 1.54 macallan sysctl_pwm, 1, (void *)2, 0, CTL_HW,
481 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
482 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
483 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "pwm3", NULL,
484 1.54 macallan sysctl_pwm, 1, (void *)3, 0, CTL_HW,
485 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
486 1.54 macallan #ifdef OBIO_GPIO_DEBUG
487 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
488 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "prescale", NULL,
489 1.54 macallan sysctl_level, 1, (void *)0x4c, 0, CTL_HW,
490 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
491 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
492 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "tach0", NULL,
493 1.54 macallan sysctl_level, 1, (void *)0x34, 0, CTL_HW,
494 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
495 1.54 macallan sysctl_createv(NULL, 0, NULL, &me,
496 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, "tach1", NULL,
497 1.54 macallan sysctl_level, 1, (void *)0x2c, 0, CTL_HW,
498 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL);
499 1.54 macallan #endif
500 1.27 garbled /* now look for voltage and bus speed gpios */
501 1.30 phx use_dfs = 0;
502 1.27 garbled for (child = OF_child(node); child; child = OF_peer(child)) {
503 1.27 garbled
504 1.27 garbled if (OF_getprop(child, "name", name, sizeof(name)) < 1)
505 1.27 garbled continue;
506 1.27 garbled
507 1.27 garbled if (OF_getprop(child, "reg", reg, sizeof(reg)) < 4)
508 1.27 garbled continue;
509 1.27 garbled
510 1.31 phx /*
511 1.31 phx * These register offsets either have to be added to the obio
512 1.31 phx * base address or to the gpio base address. This differs
513 1.31 phx * even in the same OF-tree! So we guess the offset is
514 1.31 phx * based on obio when it is larger than the gpio_base.
515 1.31 phx */
516 1.31 phx if (reg[0] >= gpio_base)
517 1.31 phx reg[0] -= gpio_base;
518 1.31 phx
519 1.27 garbled if (strcmp(name, "frequency-gpio") == 0) {
520 1.27 garbled DPRINTF("found frequency_gpio at %02x\n", reg[0]);
521 1.27 garbled sc->sc_busspeed = gpio_base + reg[0];
522 1.54 macallan } else
523 1.27 garbled if (strcmp(name, "voltage-gpio") == 0) {
524 1.27 garbled DPRINTF("found voltage_gpio at %02x\n", reg[0]);
525 1.27 garbled sc->sc_voltage = gpio_base + reg[0];
526 1.54 macallan } else
527 1.30 phx if (strcmp(name, "cpu-vcore-select") == 0) {
528 1.30 phx DPRINTF("found cpu-vcore-select at %02x\n", reg[0]);
529 1.30 phx sc->sc_voltage = gpio_base + reg[0];
530 1.30 phx /* frequency gpio is not needed, we use cpu's DFS */
531 1.30 phx use_dfs = 1;
532 1.54 macallan } else if (strcmp(name, "indicatorLED-gpio") == 0) {
533 1.54 macallan led_attach("indicator", (void *)(gpio_base + reg[0]),
534 1.54 macallan gpio_get, gpio_set);
535 1.54 macallan } else {
536 1.54 macallan /* attach a sysctl node */
537 1.54 macallan if (sysctl_createv(NULL, 0, NULL, &me,
538 1.54 macallan CTLFLAG_READWRITE, CTLTYPE_INT, name, NULL,
539 1.54 macallan sysctl_gpio, 1, (void *)(reg[0] + gpio_base), 0, CTL_HW,
540 1.54 macallan gpio->sysctl_num, CTL_CREATE, CTL_EOL) != 0) {
541 1.54 macallan printf("failed to create %s node\n", name);
542 1.54 macallan }
543 1.54 macallan }
544 1.27 garbled }
545 1.27 garbled
546 1.30 phx if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0 && !use_dfs))
547 1.27 garbled return;
548 1.27 garbled
549 1.27 garbled printf("%s: enabling Intrepid CPU speed control\n",
550 1.34 macallan device_xname(sc->sc_dev));
551 1.27 garbled
552 1.32 macallan sc->sc_spd_lo = curcpu()->ci_khz / 1000;
553 1.32 macallan hiclock = 0;
554 1.32 macallan cpunode = OF_finddevice("/cpus/@0");
555 1.32 macallan OF_getprop(cpunode, "clock-frequency", &hiclock, 4);
556 1.36 macallan if (hiclock != 0)
557 1.36 macallan sc->sc_spd_hi = (hiclock + 500000) / 1000000;
558 1.36 macallan printf("hiclock: %d\n", sc->sc_spd_hi);
559 1.46 macallan if (use_dfs) sc->sc_spd_lo = sc->sc_spd_hi / 2;
560 1.46 macallan
561 1.30 phx sysctl_node = NULL;
562 1.32 macallan
563 1.32 macallan if (sysctl_createv(NULL, 0, NULL,
564 1.35 macallan &me,
565 1.47 nia CTLFLAG_READWRITE, CTLTYPE_NODE, "cpu", NULL, NULL,
566 1.32 macallan 0, NULL, 0, CTL_MACHDEP, CTL_CREATE, CTL_EOL) != 0)
567 1.47 nia printf("couldn't create 'cpu' node\n");
568 1.32 macallan
569 1.32 macallan if (sysctl_createv(NULL, 0, NULL,
570 1.35 macallan &freq,
571 1.32 macallan CTLFLAG_READWRITE, CTLTYPE_NODE, "frequency", NULL, NULL,
572 1.32 macallan 0, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL) != 0)
573 1.32 macallan printf("couldn't create 'frequency' node\n");
574 1.32 macallan
575 1.32 macallan if (sysctl_createv(NULL, 0, NULL,
576 1.35 macallan &sysctl_node,
577 1.35 macallan CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
578 1.32 macallan CTLTYPE_INT, "target", "CPU speed", sysctl_cpuspeed_temp,
579 1.37 dsl 0, (void *)sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
580 1.32 macallan CTL_CREATE, CTL_EOL) == 0) {
581 1.32 macallan } else
582 1.32 macallan printf("couldn't create 'target' node\n");
583 1.32 macallan
584 1.32 macallan if (sysctl_createv(NULL, 0, NULL,
585 1.35 macallan &sysctl_node,
586 1.35 macallan CTLFLAG_READWRITE,
587 1.32 macallan CTLTYPE_INT, "current", NULL, sysctl_cpuspeed_cur,
588 1.37 dsl 1, (void *)sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
589 1.32 macallan CTL_CREATE, CTL_EOL) == 0) {
590 1.32 macallan } else
591 1.32 macallan printf("couldn't create 'current' node\n");
592 1.32 macallan
593 1.32 macallan if (sysctl_createv(NULL, 0, NULL,
594 1.35 macallan &sysctl_node,
595 1.32 macallan CTLFLAG_READWRITE,
596 1.32 macallan CTLTYPE_STRING, "available", NULL, sysctl_cpuspeed_available,
597 1.37 dsl 2, (void *)sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
598 1.32 macallan CTL_CREATE, CTL_EOL) == 0) {
599 1.32 macallan } else
600 1.32 macallan printf("couldn't create 'available' node\n");
601 1.32 macallan printf("speed: %d\n", curcpu()->ci_khz);
602 1.36 macallan
603 1.36 macallan /* support cpufreq */
604 1.36 macallan snprintf(cf->cf_name, CPUFREQ_NAME_MAX, "Intrepid");
605 1.36 macallan cf->cf_state[0].cfs_freq = sc->sc_spd_hi;
606 1.36 macallan cf->cf_state[1].cfs_freq = sc->sc_spd_lo;
607 1.36 macallan cf->cf_state_count = 2;
608 1.36 macallan cf->cf_mp = FALSE;
609 1.36 macallan cf->cf_cookie = sc;
610 1.36 macallan cf->cf_get_freq = obio_get_freq;
611 1.36 macallan cf->cf_set_freq = obio_set_freq;
612 1.36 macallan /*
613 1.36 macallan * XXX
614 1.36 macallan * cpufreq_register() calls xc_broadcast() which relies on kthreads
615 1.36 macallan * running so we need to postpone it
616 1.36 macallan */
617 1.36 macallan config_interrupts(sc->sc_dev, obio_setup_cpufreq);
618 1.27 garbled }
619 1.27 garbled
620 1.27 garbled static void
621 1.27 garbled obio_set_cpu_speed(struct obio_softc *sc, int fast)
622 1.27 garbled {
623 1.27 garbled
624 1.30 phx if (sc->sc_voltage < 0)
625 1.27 garbled return;
626 1.27 garbled
627 1.30 phx if (sc->sc_busspeed >= 0) {
628 1.30 phx /* set voltage and speed via gpio */
629 1.30 phx if (fast) {
630 1.30 phx bus_space_write_1(sc->sc_tag, sc->sc_bh,
631 1.30 phx sc->sc_voltage, 5);
632 1.30 phx bus_space_write_1(sc->sc_tag, sc->sc_bh,
633 1.30 phx sc->sc_busspeed, 5);
634 1.30 phx } else {
635 1.30 phx bus_space_write_1(sc->sc_tag, sc->sc_bh,
636 1.30 phx sc->sc_busspeed, 4);
637 1.30 phx bus_space_write_1(sc->sc_tag, sc->sc_bh,
638 1.30 phx sc->sc_voltage, 4);
639 1.30 phx }
640 1.30 phx }
641 1.30 phx else {
642 1.30 phx /* set voltage via gpio and speed via the 7447A's DFS bit */
643 1.30 phx if (fast) {
644 1.30 phx bus_space_write_1(sc->sc_tag, sc->sc_bh,
645 1.30 phx sc->sc_voltage, 5);
646 1.30 phx DELAY(1000);
647 1.30 phx }
648 1.30 phx
649 1.30 phx /* set DFS for all cpus */
650 1.30 phx cpu_set_dfs(fast ? 1 : 2);
651 1.30 phx DELAY(100);
652 1.30 phx
653 1.30 phx if (!fast) {
654 1.30 phx bus_space_write_1(sc->sc_tag, sc->sc_bh,
655 1.30 phx sc->sc_voltage, 4);
656 1.30 phx DELAY(1000);
657 1.30 phx }
658 1.27 garbled }
659 1.27 garbled }
660 1.27 garbled
661 1.27 garbled static int
662 1.27 garbled obio_get_cpu_speed(struct obio_softc *sc)
663 1.27 garbled {
664 1.27 garbled
665 1.30 phx if (sc->sc_voltage < 0)
666 1.27 garbled return 0;
667 1.27 garbled
668 1.30 phx if (sc->sc_busspeed >= 0) {
669 1.30 phx if (bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed)
670 1.30 phx & 1)
671 1.31 phx return 1;
672 1.30 phx }
673 1.30 phx else
674 1.30 phx return cpu_get_dfs() == 1;
675 1.27 garbled
676 1.27 garbled return 0;
677 1.27 garbled }
678 1.27 garbled
679 1.36 macallan static void
680 1.36 macallan obio_get_freq(void *cookie, void *spd)
681 1.36 macallan {
682 1.36 macallan struct obio_softc *sc = cookie;
683 1.36 macallan uint32_t *freq;
684 1.36 macallan
685 1.36 macallan freq = spd;
686 1.36 macallan if (obio_get_cpu_speed(sc) == 0) {
687 1.36 macallan *freq = sc->sc_spd_lo;
688 1.36 macallan } else
689 1.36 macallan *freq = sc->sc_spd_hi;
690 1.36 macallan }
691 1.36 macallan
692 1.36 macallan static void
693 1.36 macallan obio_set_freq(void *cookie, void *spd)
694 1.36 macallan {
695 1.36 macallan struct obio_softc *sc = cookie;
696 1.36 macallan uint32_t *freq;
697 1.36 macallan
698 1.36 macallan freq = spd;
699 1.36 macallan if (*freq == sc->sc_spd_lo) {
700 1.36 macallan obio_set_cpu_speed(sc, 0);
701 1.36 macallan } else if (*freq == sc->sc_spd_hi) {
702 1.36 macallan obio_set_cpu_speed(sc, 1);
703 1.36 macallan } else
704 1.36 macallan aprint_error_dev(sc->sc_dev, "%s(%d) bogus CPU speed\n", __func__, *freq);
705 1.36 macallan }
706 1.36 macallan
707 1.27 garbled static int
708 1.27 garbled sysctl_cpuspeed_temp(SYSCTLFN_ARGS)
709 1.27 garbled {
710 1.27 garbled struct sysctlnode node = *rnode;
711 1.27 garbled struct obio_softc *sc = node.sysctl_data;
712 1.32 macallan int speed, mhz;
713 1.27 garbled
714 1.27 garbled speed = obio_get_cpu_speed(sc);
715 1.32 macallan switch (speed) {
716 1.32 macallan case 0:
717 1.32 macallan mhz = sc->sc_spd_lo;
718 1.32 macallan break;
719 1.32 macallan case 1:
720 1.32 macallan mhz = sc->sc_spd_hi;
721 1.32 macallan break;
722 1.32 macallan default:
723 1.32 macallan speed = -1;
724 1.32 macallan }
725 1.32 macallan node.sysctl_data = &mhz;
726 1.32 macallan if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
727 1.32 macallan int new_reg;
728 1.32 macallan
729 1.35 macallan new_reg = *(int *)node.sysctl_data;
730 1.32 macallan if (new_reg == sc->sc_spd_lo) {
731 1.32 macallan obio_set_cpu_speed(sc, 0);
732 1.32 macallan } else if (new_reg == sc->sc_spd_hi) {
733 1.32 macallan obio_set_cpu_speed(sc, 1);
734 1.32 macallan } else {
735 1.32 macallan printf("%s: new_reg %d\n", __func__, new_reg);
736 1.32 macallan return EINVAL;
737 1.27 garbled }
738 1.32 macallan return 0;
739 1.27 garbled }
740 1.32 macallan return EINVAL;
741 1.32 macallan }
742 1.32 macallan
743 1.32 macallan static int
744 1.32 macallan sysctl_cpuspeed_cur(SYSCTLFN_ARGS)
745 1.32 macallan {
746 1.32 macallan struct sysctlnode node = *rnode;
747 1.32 macallan struct obio_softc *sc = node.sysctl_data;
748 1.32 macallan int speed, mhz;
749 1.32 macallan
750 1.32 macallan speed = obio_get_cpu_speed(sc);
751 1.32 macallan switch (speed) {
752 1.32 macallan case 0:
753 1.32 macallan mhz = sc->sc_spd_lo;
754 1.32 macallan break;
755 1.32 macallan case 1:
756 1.32 macallan mhz = sc->sc_spd_hi;
757 1.32 macallan break;
758 1.32 macallan default:
759 1.32 macallan speed = -1;
760 1.32 macallan }
761 1.32 macallan node.sysctl_data = &mhz;
762 1.32 macallan return sysctl_lookup(SYSCTLFN_CALL(&node));
763 1.32 macallan }
764 1.32 macallan
765 1.32 macallan static int
766 1.32 macallan sysctl_cpuspeed_available(SYSCTLFN_ARGS)
767 1.32 macallan {
768 1.32 macallan struct sysctlnode node = *rnode;
769 1.32 macallan struct obio_softc *sc = node.sysctl_data;
770 1.32 macallan char buf[128];
771 1.32 macallan
772 1.32 macallan snprintf(buf, 128, "%d %d", sc->sc_spd_lo, sc->sc_spd_hi);
773 1.32 macallan node.sysctl_data = buf;
774 1.32 macallan return(sysctl_lookup(SYSCTLFN_CALL(&node)));
775 1.32 macallan }
776 1.32 macallan
777 1.54 macallan static int
778 1.54 macallan sysctl_gpio(SYSCTLFN_ARGS)
779 1.54 macallan {
780 1.54 macallan struct sysctlnode node = *rnode;
781 1.54 macallan int reg = (int)node.sysctl_data;
782 1.54 macallan uint32_t val, ret;
783 1.54 macallan DPRINTF("%s: reg %x\n", __func__, reg);
784 1.54 macallan val = obio_read_1(reg);
785 1.54 macallan if (val & GPIO_DDR_OUTPUT) {
786 1.54 macallan ret = (val & GPIO_DATA) != 0;
787 1.54 macallan } else
788 1.54 macallan ret = (val & GPIO_LEVEL) != 0;
789 1.54 macallan node.sysctl_data = &ret;
790 1.54 macallan node.sysctl_size = 4;
791 1.54 macallan return(sysctl_lookup(SYSCTLFN_CALL(&node)));
792 1.54 macallan }
793 1.54 macallan
794 1.54 macallan #ifdef OBIO_GPIO_DEBUG
795 1.54 macallan static int
796 1.54 macallan sysctl_level(SYSCTLFN_ARGS)
797 1.54 macallan {
798 1.54 macallan struct sysctlnode node = *rnode;
799 1.54 macallan int reg = (int)node.sysctl_data;
800 1.54 macallan uint32_t val;
801 1.54 macallan val = obio_read_4(reg);
802 1.54 macallan DPRINTF("%s: reg %x %08x\n", __func__, reg, val);
803 1.54 macallan node.sysctl_data = &val;
804 1.54 macallan node.sysctl_size = 4;
805 1.54 macallan return(sysctl_lookup(SYSCTLFN_CALL(&node)));
806 1.54 macallan }
807 1.54 macallan #endif
808 1.54 macallan
809 1.54 macallan static int
810 1.54 macallan sysctl_pwm(SYSCTLFN_ARGS)
811 1.54 macallan {
812 1.54 macallan struct sysctlnode node = *rnode;
813 1.54 macallan int which = (int)node.sysctl_data;
814 1.54 macallan uint32_t pwm;
815 1.54 macallan
816 1.54 macallan pwm = obio_get_pwm(which);
817 1.54 macallan
818 1.54 macallan if (newp) {
819 1.54 macallan /* we're asked to write */
820 1.54 macallan node.sysctl_data = &pwm;
821 1.54 macallan if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
822 1.54 macallan
823 1.54 macallan pwm = *(int *)node.sysctl_data;
824 1.54 macallan obio_set_pwm(which, pwm);
825 1.54 macallan return 0;
826 1.54 macallan }
827 1.54 macallan return EINVAL;
828 1.54 macallan }
829 1.54 macallan node.sysctl_data = &pwm;
830 1.54 macallan node.sysctl_size = 4;
831 1.54 macallan return(sysctl_lookup(SYSCTLFN_CALL(&node)));
832 1.54 macallan }
833 1.54 macallan
834 1.32 macallan SYSCTL_SETUP(sysctl_ams_setup, "sysctl obio subtree setup")
835 1.32 macallan {
836 1.32 macallan
837 1.32 macallan sysctl_createv(NULL, 0, NULL, NULL,
838 1.32 macallan CTLFLAG_PERMANENT,
839 1.32 macallan CTLTYPE_NODE, "machdep", NULL,
840 1.32 macallan NULL, 0, NULL, 0,
841 1.32 macallan CTL_MACHDEP, CTL_EOL);
842 1.54 macallan sysctl_createv(NULL, 0, NULL, NULL,
843 1.54 macallan CTLFLAG_PERMANENT,
844 1.54 macallan CTLTYPE_NODE, "hw", NULL,
845 1.54 macallan NULL, 0, NULL, 0,
846 1.54 macallan CTL_HW, CTL_EOL);
847 1.27 garbled }
848 1.27 garbled
849 1.54 macallan static int
850 1.54 macallan gpio_get(void *cookie)
851 1.54 macallan {
852 1.54 macallan uint32_t reg = (uint32_t)cookie;
853 1.54 macallan
854 1.54 macallan return ((obio_read_1(reg) & GPIO_LEVEL) != 0);
855 1.54 macallan }
856 1.54 macallan
857 1.54 macallan static void
858 1.54 macallan gpio_set(void *cookie, int val)
859 1.54 macallan {
860 1.54 macallan uint32_t reg = (uint32_t)cookie;
861 1.54 macallan uint8_t gpio = obio_read_1(reg);
862 1.54 macallan
863 1.54 macallan if (val) {
864 1.54 macallan gpio |= GPIO_DATA;
865 1.54 macallan } else
866 1.54 macallan gpio &= ~GPIO_DATA;
867 1.54 macallan obio_write_1(reg, gpio);
868 1.54 macallan }
869 1.54 macallan
870 1.54 macallan /* Xserve fan control */
871 1.54 macallan static bool
872 1.54 macallan is_cpu(const envsys_data_t *edata)
873 1.54 macallan {
874 1.54 macallan if (edata->units != ENVSYS_STEMP)
875 1.54 macallan return false;
876 1.54 macallan if ((strstr(edata->desc, "CPU") != NULL) ||
877 1.54 macallan (strstr(edata->desc, "ternal") != NULL))
878 1.54 macallan return TRUE;
879 1.54 macallan return false;
880 1.54 macallan }
881 1.54 macallan
882 1.54 macallan static bool
883 1.54 macallan is_case(const envsys_data_t *edata)
884 1.54 macallan {
885 1.54 macallan if (edata->units != ENVSYS_STEMP)
886 1.54 macallan return false;
887 1.54 macallan if ((strstr(edata->desc, "CASE") != NULL) ||
888 1.54 macallan (strstr(edata->desc, "monitor") != NULL))
889 1.54 macallan return TRUE;
890 1.54 macallan return false;
891 1.54 macallan }
892 1.54 macallan
893 1.54 macallan static bool
894 1.54 macallan is_fan0(const envsys_data_t *edata)
895 1.54 macallan {
896 1.54 macallan if (edata->units != ENVSYS_SFANRPM)
897 1.54 macallan return false;
898 1.54 macallan if (strstr(edata->desc, "FAN1") != NULL)
899 1.54 macallan return TRUE;
900 1.54 macallan return false;
901 1.54 macallan }
902 1.54 macallan
903 1.54 macallan static bool
904 1.54 macallan is_fan1(const envsys_data_t *edata)
905 1.54 macallan {
906 1.54 macallan if (edata->units != ENVSYS_SFANRPM)
907 1.54 macallan return false;
908 1.54 macallan if (strstr(edata->desc, "FAN2") != NULL)
909 1.54 macallan return TRUE;
910 1.54 macallan return false;
911 1.54 macallan }
912 1.54 macallan
913 1.54 macallan static int
914 1.54 macallan obio_get_pwm(int which)
915 1.54 macallan {
916 1.54 macallan uint32_t reg, mask;
917 1.54 macallan int shift;
918 1.54 macallan
919 1.54 macallan shift = (3 - which) * 8;
920 1.54 macallan mask = 0xff << shift;
921 1.54 macallan reg = obio_read_4(0x30);
922 1.54 macallan return (reg & mask) >> shift;
923 1.54 macallan }
924 1.54 macallan
925 1.54 macallan static void
926 1.54 macallan obio_set_pwm(int which, int pwm)
927 1.54 macallan {
928 1.54 macallan uint32_t reg, mask;
929 1.54 macallan int shift;
930 1.54 macallan
931 1.54 macallan shift = (3 - which) * 8;
932 1.54 macallan mask = 0xff << shift;
933 1.54 macallan reg = obio_read_4(0x30);
934 1.54 macallan reg &= ~mask;
935 1.54 macallan reg |= (pwm & 0xff) << shift;
936 1.54 macallan obio_write_4(0x30, reg);
937 1.54 macallan }
938 1.54 macallan
939 1.54 macallan static int
940 1.54 macallan obio_get_rpm(void *cookie, int which)
941 1.54 macallan {
942 1.54 macallan int rpm = 0;
943 1.54 macallan
944 1.54 macallan if (which == 0) {
945 1.54 macallan rpm = sysmon_envsys_get_max_value(is_fan0, true);
946 1.54 macallan } else
947 1.54 macallan rpm = sysmon_envsys_get_max_value(is_fan1, true);
948 1.54 macallan return rpm;
949 1.54 macallan }
950 1.54 macallan
951 1.54 macallan static int
952 1.54 macallan obio_set_rpm(void *cookie, int which, int speed)
953 1.54 macallan {
954 1.54 macallan struct obio_softc *sc = cookie;
955 1.54 macallan int diff;
956 1.54 macallan unsigned int nduty = sc->sc_duty[which];
957 1.54 macallan int current_speed;
958 1.54 macallan
959 1.54 macallan sc->sc_target[which] = speed;
960 1.54 macallan current_speed = obio_get_rpm(sc, which);
961 1.54 macallan diff = current_speed - speed;
962 1.54 macallan DPRINTF("d %d s %d t %d diff %d ", nduty, current_speed, speed, diff);
963 1.54 macallan if (diff > 200) {
964 1.54 macallan /* slow down */
965 1.54 macallan nduty += 2;
966 1.54 macallan if (nduty > 200) nduty = 200;
967 1.54 macallan }
968 1.54 macallan if (diff < -200) {
969 1.54 macallan /* speed up */
970 1.54 macallan nduty -= 2;
971 1.54 macallan if (nduty < 50) nduty = 50;
972 1.54 macallan }
973 1.54 macallan if (nduty != sc->sc_duty[which]) {
974 1.54 macallan sc->sc_duty[which] = nduty;
975 1.54 macallan obio_set_pwm(which, nduty);
976 1.54 macallan sc->sc_pwm = TRUE;
977 1.54 macallan }
978 1.54 macallan return 0;
979 1.54 macallan }
980 1.54 macallan
981 1.54 macallan static void
982 1.54 macallan obio_adjust(void *cookie)
983 1.54 macallan {
984 1.54 macallan struct obio_softc *sc = cookie;
985 1.54 macallan
986 1.54 macallan while (!sc->sc_dying) {
987 1.54 macallan sc->sc_pwm = FALSE;
988 1.54 macallan fancontrol_adjust_zone(&sc->sc_zones[0]);
989 1.54 macallan fancontrol_adjust_zone(&sc->sc_zones[1]);
990 1.54 macallan /*
991 1.54 macallan * take a shorter nap if we're in the process of adjusting a
992 1.54 macallan * PWM fan, which relies on measuring speed and then changing
993 1.54 macallan * its duty cycle until we're reasonable close to the target
994 1.54 macallan * speed
995 1.54 macallan */
996 1.54 macallan kpause("fanctrl", true, mstohz(sc->sc_pwm ? 1000 : 2000), NULL);
997 1.54 macallan }
998 1.54 macallan kthread_exit(0);
999 1.54 macallan }
1000