obiofan.c revision 1.3 1 /* $NetBSD: obiofan.c,v 1.3 2025/07/01 06:36:35 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[4], sc_shift[4];
57 int sc_count[4], sc_rpm[4];
58 int sc_fans;
59 };
60
61 int obiofan_match(device_t, cfdata_t, void *);
62 void obiofan_attach(device_t, device_t, void *);
63 static void obiofan_refresh(struct sysmon_envsys *, envsys_data_t *);
64 static void obiofan_update(void *);
65
66 CFATTACH_DECL_NEW(obiofan, sizeof(struct obiofan_softc), obiofan_match,
67 obiofan_attach, NULL, NULL);
68
69 int
70 obiofan_match(device_t parent, cfdata_t match, void *aux)
71 {
72 struct confargs *ca = aux;
73
74 if (strcmp(ca->ca_name, "fans") == 0)
75 return 1;
76
77 return 0;
78 }
79
80 void
81 obiofan_attach(device_t parent, device_t self, void *aux)
82 {
83 struct obiofan_softc *sc = device_private(self);
84 struct confargs *ca = aux;
85 char descr[32] = "fan";
86 int node = ca->ca_node, tc_node, f, cnt;
87 uint32_t regs[8], tc_regs[32];
88
89 printf("\n");
90
91 if (OF_getprop(node, "reg", regs, sizeof(regs)) <= 0)
92 return;
93
94 sc->sc_node = node;
95
96 #ifdef OBIOFAN_DEBUG
97 int i;
98 for (i = 0; i < 7; i += 2)
99 printf("%02x: %08x\n", regs[i], obio_read_4(regs[i]));
100 #endif
101
102 tc_node = OF_parent(node);
103
104 if (OF_getprop(tc_node, "platform-do-getTACHCount", tc_regs, 128) <= 0) {
105 aprint_error("no platform-do-getTACHCount\n");
106 return;
107 }
108
109 /*
110 * XXX this is guesswork based on poking around & observation
111 *
112 * the parameter format seems to be:
113 * reg[0] - node number for the fan, or pointer into OF space
114 * reg[1] - 0x08000000
115 * reg[2] - 0x0000001a - varies with different platform-do-*
116 * reg[3] - register number in obio space
117 * reg[4] - bitmask in the register
118 * reg[5] - unknown
119 * reg[6] - unknown
120 */
121
122 sc->sc_sme = sysmon_envsys_create();
123 sc->sc_sme->sme_name = device_xname(self);
124 sc->sc_sme->sme_cookie = sc;
125 sc->sc_sme->sme_refresh = obiofan_refresh;
126
127 /* now look for which fan register(s) we can use */
128 f = OF_child(node);
129 cnt = 0;
130 while (f != 0) {
131 int num = -1;
132 OF_getprop(f, "reg", &num, 4);
133 if (OF_getprop(f, "hwctrl-location", descr, 32) <= 0)
134 goto skip;
135 sc->sc_reg[cnt] = tc_regs[num * 7 + 3];
136 sc->sc_shift[cnt] = ffs(tc_regs[num * 7 + 4]) - 1;
137
138 DPRINTF("%s: %d %02x %d\n", descr, num, sc->sc_reg[cnt], sc->sc_shift[cnt]);
139
140 sc->sc_stamp = time_second;
141 sc->sc_count[cnt] =
142 obio_read_4(sc->sc_reg[cnt]) >> sc->sc_shift[cnt];
143 sc->sc_rpm[cnt] = -1;
144
145 sc->sc_sensors[cnt].units = ENVSYS_SFANRPM;
146 sc->sc_sensors[cnt].state = ENVSYS_SINVALID;
147 sc->sc_sensors[cnt].private = cnt;
148 strcpy(sc->sc_sensors[cnt].desc, descr);
149 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensors[cnt]);
150 cnt++;
151 skip:
152 f = OF_peer(f);
153 }
154 sc->sc_fans = cnt;
155 sysmon_envsys_register(sc->sc_sme);
156
157 callout_init(&sc->sc_callout, 0);
158 callout_setfunc(&sc->sc_callout, obiofan_update, sc);
159 callout_schedule(&sc->sc_callout, mstohz(5000));
160 }
161
162 static void
163 obiofan_update(void *cookie)
164 {
165 struct obiofan_softc *sc = cookie;
166 time_t now = time_second, diff;
167 int spin, spins, i;
168 for (i = 0; i < sc->sc_fans; i++) {
169 diff = now - sc->sc_stamp;
170 if (diff < 5) return;
171 spin = obio_read_4(sc->sc_reg[i]) >> sc->sc_shift[i];
172 spins = (spin - sc->sc_count[i]) & 0xffff;
173 sc->sc_rpm[i] = spins * 60 / diff;
174 sc->sc_count[i] = spin;
175 DPRINTF("%s %lld %d\n", __func__, diff, spins);
176 }
177 sc->sc_stamp = now;
178 callout_schedule(&sc->sc_callout, mstohz(5000));
179 }
180
181 static void
182 obiofan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
183 {
184 struct obiofan_softc *sc = sme->sme_cookie;
185 int i = edata->private;
186
187 if (sc->sc_rpm[i] >= 0) {
188 edata->state = ENVSYS_SVALID;
189 edata->value_cur = sc->sc_rpm[i];
190 }
191 }
192