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