1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010 Andreas Tobler
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/systm.h>
34 #include <sys/module.h>
35 #include <sys/callout.h>
36 #include <sys/conf.h>
37 #include <sys/cpu.h>
38 #include <sys/ctype.h>
39 #include <sys/kernel.h>
40 #include <sys/reboot.h>
41 #include <sys/rman.h>
42 #include <sys/sysctl.h>
43 #include <sys/limits.h>
44
45 #include <machine/bus.h>
46 #include <machine/md_var.h>
47
48 #include <dev/iicbus/iicbus.h>
49 #include <dev/iicbus/iiconf.h>
50
51 #include <dev/ofw/openfirm.h>
52 #include <dev/ofw/ofw_bus.h>
53 #include <powerpc/powermac/powermac_thermal.h>
54
55 /* Inlet, Backside, U3 Heatsink sensor: MAX6690. */
56
57 #define MAX6690_INT_TEMP 0x0
58 #define MAX6690_EXT_TEMP 0x1
59 #define MAX6690_RSL_STATUS 0x2
60 #define MAX6690_EEXT_TEMP 0x10
61 #define MAX6690_IEXT_TEMP 0x11
62 #define MAX6690_TEMP_MASK 0xe0
63
64 struct max6690_sensor {
65 struct pmac_therm therm;
66 device_t dev;
67
68 int id;
69 };
70
71 /* Regular bus attachment functions */
72 static int max6690_probe(device_t);
73 static int max6690_attach(device_t);
74
75 /* Utility functions */
76 static int max6690_sensor_read(struct max6690_sensor *sens);
77 static int max6690_sensor_sysctl(SYSCTL_HANDLER_ARGS);
78 static void max6690_start(void *xdev);
79 static int max6690_read(device_t dev, uint32_t addr, uint8_t reg,
80 uint8_t *data);
81
82 struct max6690_softc {
83 device_t sc_dev;
84 struct intr_config_hook enum_hook;
85 uint32_t sc_addr;
86 struct max6690_sensor *sc_sensors;
87 int sc_nsensors;
88 };
89 static device_method_t max6690_methods[] = {
90 /* Device interface */
91 DEVMETHOD(device_probe, max6690_probe),
92 DEVMETHOD(device_attach, max6690_attach),
93 { 0, 0 },
94 };
95
96 static driver_t max6690_driver = {
97 "max6690",
98 max6690_methods,
99 sizeof(struct max6690_softc)
100 };
101
102 DRIVER_MODULE(max6690, iicbus, max6690_driver, 0, 0);
103 static MALLOC_DEFINE(M_MAX6690, "max6690", "Temp-Monitor MAX6690");
104
105 static int
106 max6690_read(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
107 {
108 uint8_t buf[4];
109 uint8_t busy[1], rsl;
110 int err, try = 0;
111
112 /* Busy register RSL. */
113 rsl = MAX6690_RSL_STATUS;
114 /* first read the status register, 0x2. If busy, retry. */
115 struct iic_msg msg[4] = {
116 { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &rsl },
117 { addr, IIC_M_RD, 1, busy },
118 { addr, IIC_M_WR | IIC_M_NOSTOP, 1, ® },
119 { addr, IIC_M_RD, 1, buf },
120 };
121
122 for (;;)
123 {
124 err = iicbus_transfer(dev, msg, nitems(msg));
125 if (err != 0)
126 goto retry;
127 if (busy[0] & 0x80)
128 goto retry;
129 /* Check for invalid value and retry. */
130 if (buf[0] == 0xff)
131 goto retry;
132
133 *data = *((uint8_t*)buf);
134 return (0);
135
136 retry:
137 if (++try > 5) {
138 device_printf(dev, "iicbus read failed\n");
139 return (-1);
140 }
141 pause("max6690_read", hz);
142 }
143 }
144
145 static int
146 max6690_probe(device_t dev)
147 {
148 const char *name, *compatible;
149 struct max6690_softc *sc;
150
151 name = ofw_bus_get_name(dev);
152 compatible = ofw_bus_get_compat(dev);
153
154 if (!name)
155 return (ENXIO);
156
157 if (strcmp(name, "temp-monitor") != 0 ||
158 strcmp(compatible, "max6690") != 0)
159 return (ENXIO);
160
161 sc = device_get_softc(dev);
162 sc->sc_dev = dev;
163 sc->sc_addr = iicbus_get_addr(dev);
164
165 device_set_desc(dev, "Temp-Monitor MAX6690");
166
167 return (0);
168 }
169
170 /*
171 * This function returns the number of sensors. If we call it the second time
172 * and we have allocated memory for sc->sc_sensors, we fill in the properties.
173 */
174 static int
175 max6690_fill_sensor_prop(device_t dev)
176 {
177 phandle_t child;
178 struct max6690_softc *sc;
179 u_int id[8];
180 char location[96];
181 int i = 0, j, len = 0, prop_len, prev_len = 0;
182
183 sc = device_get_softc(dev);
184
185 child = ofw_bus_get_node(dev);
186
187 /* Fill the sensor location property. */
188 prop_len = OF_getprop(child, "hwsensor-location", location,
189 sizeof(location));
190 while (len < prop_len) {
191 if (sc->sc_sensors != NULL)
192 strcpy(sc->sc_sensors[i].therm.name, location + len);
193 prev_len = strlen(location + len) + 1;
194 len += prev_len;
195 i++;
196 }
197 if (sc->sc_sensors == NULL)
198 return (i);
199
200 /* Fill the sensor id property. */
201 prop_len = OF_getprop(child, "hwsensor-id", id, sizeof(id));
202 for (j = 0; j < i; j++)
203 sc->sc_sensors[j].id = (id[j] & 0xf);
204
205 /* Fill the sensor zone property. */
206 prop_len = OF_getprop(child, "hwsensor-zone", id, sizeof(id));
207 for (j = 0; j < i; j++)
208 sc->sc_sensors[j].therm.zone = id[j];
209
210 /* Set up remaining sensor properties */
211 for (j = 0; j < i; j++) {
212 sc->sc_sensors[j].dev = dev;
213
214 /*
215 * Target value for "KODIAK DIODE" (= northbridge die) should
216 * be 64C (value from Linux). It operates fine at that
217 * temperature and has limited responsivity to the fan aimed at
218 * it, so no point in trying to cool it to 40C.
219 */
220 if (strcmp(sc->sc_sensors[j].therm.name, "KODIAK DIODE") == 0)
221 sc->sc_sensors[j].therm.target_temp = 640 + ZERO_C_TO_K;
222 else
223 sc->sc_sensors[j].therm.target_temp = 400 + ZERO_C_TO_K;
224 sc->sc_sensors[j].therm.max_temp = 850 + ZERO_C_TO_K;
225
226 sc->sc_sensors[j].therm.read =
227 (int (*)(struct pmac_therm *))(max6690_sensor_read);
228 }
229
230 return (i);
231 }
232 static int
233 max6690_attach(device_t dev)
234 {
235 struct max6690_softc *sc;
236
237 sc = device_get_softc(dev);
238
239 sc->enum_hook.ich_func = max6690_start;
240 sc->enum_hook.ich_arg = dev;
241
242 /* We have to wait until interrupts are enabled. I2C read and write
243 * only works if the interrupts are available.
244 * The unin/i2c is controlled by the htpic on unin. But this is not
245 * the master. The openpic on mac-io is controlling the htpic.
246 * This one gets attached after the mac-io probing and then the
247 * interrupts will be available.
248 */
249
250 if (config_intrhook_establish(&sc->enum_hook) != 0)
251 return (ENOMEM);
252
253 return (0);
254 }
255
256 static void
257 max6690_start(void *xdev)
258 {
259 struct max6690_softc *sc;
260 struct sysctl_oid *oid, *sensroot_oid;
261 struct sysctl_ctx_list *ctx;
262 char sysctl_desc[40], sysctl_name[32];
263 int i, j;
264
265 device_t dev = (device_t)xdev;
266
267 sc = device_get_softc(dev);
268
269 sc->sc_nsensors = 0;
270
271 /* Count the actual number of sensors. */
272 sc->sc_nsensors = max6690_fill_sensor_prop(dev);
273
274 device_printf(dev, "%d sensors detected.\n", sc->sc_nsensors);
275
276 if (sc->sc_nsensors == 0)
277 device_printf(dev, "WARNING: No MAX6690 sensors detected!\n");
278
279 sc->sc_sensors = malloc (sc->sc_nsensors * sizeof(struct max6690_sensor),
280 M_MAX6690, M_WAITOK | M_ZERO);
281
282 ctx = device_get_sysctl_ctx(dev);
283 sensroot_oid = SYSCTL_ADD_NODE(ctx,
284 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor",
285 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "MAX6690 Sensor Information");
286
287 /* Now we can fill the properties into the allocated struct. */
288 sc->sc_nsensors = max6690_fill_sensor_prop(dev);
289
290 /* Register with powermac_thermal */
291 for (i = 0; i < sc->sc_nsensors; i++)
292 pmac_thermal_sensor_register(&sc->sc_sensors[i].therm);
293
294 /* Add sysctls for the sensors. */
295 for (i = 0; i < sc->sc_nsensors; i++) {
296 for (j = 0; j < strlen(sc->sc_sensors[i].therm.name); j++) {
297 sysctl_name[j] =
298 tolower(sc->sc_sensors[i].therm.name[j]);
299 if (isspace(sysctl_name[j]))
300 sysctl_name[j] = '_';
301 }
302 sysctl_name[j] = 0;
303
304 sprintf(sysctl_desc,"%s %s", sc->sc_sensors[i].therm.name,
305 "(C)");
306 oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sensroot_oid),
307 OID_AUTO, sysctl_name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
308 "Sensor Information");
309 /* I use i to pass the sensor id. */
310 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "temp",
311 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
312 dev, i % 2,
313 max6690_sensor_sysctl, "IK", sysctl_desc);
314
315 }
316 /* Dump sensor location & ID. */
317 if (bootverbose) {
318 device_printf(dev, "Sensors\n");
319 for (i = 0; i < sc->sc_nsensors; i++) {
320 device_printf(dev, "Location : %s ID: %d\n",
321 sc->sc_sensors[i].therm.name,
322 sc->sc_sensors[i].id);
323 }
324 }
325
326 config_intrhook_disestablish(&sc->enum_hook);
327 }
328
329 static int
330 max6690_sensor_read(struct max6690_sensor *sens)
331 {
332 uint8_t reg_int = 0, reg_ext = 0;
333 uint8_t integer = 0;
334 uint8_t fraction = 0;
335 int err, temp;
336
337 struct max6690_softc *sc;
338
339 sc = device_get_softc(sens->dev);
340
341 /* The internal sensor id's are even, the external are odd. */
342 if ((sens->id % 2) == 0) {
343 reg_int = MAX6690_INT_TEMP;
344 reg_ext = MAX6690_IEXT_TEMP;
345 } else {
346 reg_int = MAX6690_EXT_TEMP;
347 reg_ext = MAX6690_EEXT_TEMP;
348 }
349
350 err = max6690_read(sc->sc_dev, sc->sc_addr, reg_int, &integer);
351
352 if (err < 0)
353 return (-1);
354
355 err = max6690_read(sc->sc_dev, sc->sc_addr, reg_ext, &fraction);
356
357 if (err < 0)
358 return (-1);
359
360 fraction &= MAX6690_TEMP_MASK;
361
362 /* The temperature is in tenth kelvin, the fractional part resolution
363 is 0.125.
364 */
365 temp = (integer * 10) + (fraction >> 5) * 10 / 8;
366
367 return (temp + ZERO_C_TO_K);
368 }
369
370 static int
371 max6690_sensor_sysctl(SYSCTL_HANDLER_ARGS)
372 {
373 device_t dev;
374 struct max6690_softc *sc;
375 struct max6690_sensor *sens;
376 int error;
377 int temp;
378
379 dev = arg1;
380 sc = device_get_softc(dev);
381 sens = &sc->sc_sensors[arg2];
382
383 temp = max6690_sensor_read(sens);
384 if (temp < 0)
385 return (EIO);
386
387 error = sysctl_handle_int(oidp, &temp, 0, req);
388
389 return (error);
390 }
Cache object: ddf7df70cee9f3e3dca4ec3854124235
|