1 /* $NetBSD: obiofan.c,v 1.2 2021/09/11 16:29:18 macallan Exp $ */ 2 3 /*- 4 * Copyright (c) 2021 Michael Lorenz 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/device.h> 31 #include <sys/systm.h> 32 #include <sys/mutex.h> 33 #include <sys/callout.h> 34 #include <sys/time.h> 35 36 #include <dev/ofw/openfirm.h> 37 #include <dev/sysmon/sysmonvar.h> 38 #include <machine/autoconf.h> 39 #include <macppc/dev/obiovar.h> 40 41 #include "opt_obiofan.h" 42 43 #ifdef OBIOFAN_DEBUG 44 #define DPRINTF printf 45 #else 46 #define DPRINTF if (0) printf 47 #endif 48 49 struct obiofan_softc { 50 device_t sc_dev; 51 struct sysmon_envsys *sc_sme; 52 envsys_data_t sc_sensors[4]; 53 callout_t sc_callout; 54 time_t sc_stamp; 55 int sc_node; 56 int sc_reg, sc_shift; 57 int sc_count, sc_rpm; 58 }; 59 60 int obiofan_match(device_t, cfdata_t, void *); 61 void obiofan_attach(device_t, device_t, void *); 62 static void obiofan_refresh(struct sysmon_envsys *, envsys_data_t *); 63 static void obiofan_update(void *); 64 65 CFATTACH_DECL_NEW(obiofan, sizeof(struct obiofan_softc), obiofan_match, 66 obiofan_attach, NULL, NULL); 67 68 int 69 obiofan_match(device_t parent, cfdata_t match, void *aux) 70 { 71 struct confargs *ca = aux; 72 73 if (strcmp(ca->ca_name, "fans") == 0) 74 return 1; 75 76 return 0; 77 } 78 79 void 80 obiofan_attach(device_t parent, device_t self, void *aux) 81 { 82 struct obiofan_softc *sc = device_private(self); 83 struct confargs *ca = aux; 84 char descr[32] = "fan"; 85 int node = ca->ca_node, tc_node; 86 int regs[8], tc_regs[32]; 87 88 printf("\n"); 89 90 if (OF_getprop(node, "reg", regs, sizeof(regs)) <= 0) 91 return; 92 93 sc->sc_node = node; 94 95 #ifdef OBIOFAN_DEBUG 96 int i; 97 for (i = 0; i < 7; i += 2) 98 printf("%02x: %08x\n", regs[i], obio_read_4(regs[i])); 99 #endif 100 101 tc_node = OF_parent(node); 102 103 if (OF_getprop(tc_node, "platform-do-getTACHCount", tc_regs, 32) <= 0) { 104 aprint_error("no platform-do-getTACHCount\n"); 105 return; 106 } 107 108 /* 109 * XXX this is guesswork based on poking around & observation 110 * 111 * the parameter format seems to be: 112 * reg[0] - node number for the fan, or pointer into OF space 113 * reg[1] - 0x08000000 114 * reg[2] - 0x0000001a - varies with different platform-do-* 115 * reg[3] - register number in obio space 116 * reg[4] - bitmask in the register 117 * reg[5] - unknown 118 * reg[6] - unknown 119 * 120 * for now only get the parameters for the 1st fan since that's all we 121 * have on my 7,3 122 */ 123 124 sc->sc_reg = tc_regs[3]; 125 sc->sc_shift = ffs(tc_regs[4]) - 1; 126 127 OF_getprop(tc_regs[0], "location", descr, 32); 128 DPRINTF("%s: %02x %d\n", descr, sc->sc_reg, sc->sc_shift); 129 130 sc->sc_stamp = time_second; 131 sc->sc_count = obio_read_4(sc->sc_reg) >> sc->sc_shift; 132 sc->sc_rpm = -1; 133 134 sc->sc_sme = sysmon_envsys_create(); 135 sc->sc_sme->sme_name = device_xname(self); 136 sc->sc_sme->sme_cookie = sc; 137 sc->sc_sme->sme_refresh = obiofan_refresh; 138 139 sc->sc_sensors[0].units = ENVSYS_SFANRPM; 140 sc->sc_sensors[0].state = ENVSYS_SINVALID; 141 strcpy(sc->sc_sensors[0].desc, descr); 142 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensors[0]); 143 sysmon_envsys_register(sc->sc_sme); 144 145 callout_init(&sc->sc_callout, 0); 146 callout_setfunc(&sc->sc_callout, obiofan_update, sc); 147 callout_schedule(&sc->sc_callout, mstohz(5000)); 148 } 149 150 static void 151 obiofan_update(void *cookie) 152 { 153 struct obiofan_softc *sc = cookie; 154 time_t now = time_second, diff; 155 int spin, spins; 156 diff = now - sc->sc_stamp; 157 if (diff < 5) return; 158 spin = obio_read_4(sc->sc_reg) >> sc->sc_shift; 159 spins = (spin - sc->sc_count) & 0xffff; 160 sc->sc_rpm = spins * 60 / diff; 161 sc->sc_count = spin; 162 sc->sc_stamp = now; 163 DPRINTF("%s %lld %d\n", __func__, diff, spins); 164 callout_schedule(&sc->sc_callout, mstohz(5000)); 165 } 166 167 static void 168 obiofan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 169 { 170 struct obiofan_softc *sc = sme->sme_cookie; 171 if (sc->sc_rpm >= 0) { 172 edata->state = ENVSYS_SVALID; 173 edata->value_cur = sc->sc_rpm; 174 } 175 } 176