1*f74360b9Sjdc /* $NetBSD: tda.c,v 1.14 2020/10/31 13:17:34 jdc Exp $ */
2f0ca32ddSmartin /* $OpenBSD: tda.c,v 1.4 2008/02/27 17:25:00 robert Exp $ */
3f0ca32ddSmartin
4f0ca32ddSmartin /*
5f0ca32ddSmartin * Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
6f0ca32ddSmartin * Copyright (c) 2008 Mark Kettenis <kettenis@openbsd.org>
7f0ca32ddSmartin *
8f0ca32ddSmartin * Permission to use, copy, modify, and distribute this software for any
9f0ca32ddSmartin * purpose with or without fee is hereby granted, provided that the above
10f0ca32ddSmartin * copyright notice and this permission notice appear in all copies.
11f0ca32ddSmartin *
12f0ca32ddSmartin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13f0ca32ddSmartin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14f0ca32ddSmartin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15f0ca32ddSmartin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16f0ca32ddSmartin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17f0ca32ddSmartin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18f0ca32ddSmartin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19f0ca32ddSmartin */
20f0ca32ddSmartin
21f72d1cdaSmrg #include <sys/cdefs.h>
22*f74360b9Sjdc __KERNEL_RCSID(0, "$NetBSD: tda.c,v 1.14 2020/10/31 13:17:34 jdc Exp $");
23f72d1cdaSmrg
24f0ca32ddSmartin #include <sys/param.h>
25f0ca32ddSmartin #include <sys/systm.h>
26f0ca32ddSmartin #include <sys/kernel.h>
27f0ca32ddSmartin #include <sys/device.h>
28f0ca32ddSmartin #include <dev/sysmon/sysmonvar.h>
29f0ca32ddSmartin #include <dev/sysmon/sysmon_taskq.h>
30f0ca32ddSmartin
31f0ca32ddSmartin #include <machine/autoconf.h>
32f0ca32ddSmartin #include <machine/openfirm.h>
33f0ca32ddSmartin
34f0ca32ddSmartin #include <dev/i2c/i2cvar.h>
35f0ca32ddSmartin
36f0ca32ddSmartin /* fan control registers */
37f0ca32ddSmartin #define TDA_SYSFAN_REG 0xf0
38f0ca32ddSmartin #define TDA_CPUFAN_REG 0xf2
39f0ca32ddSmartin #define TDA_PSFAN_REG 0xf4
40f0ca32ddSmartin
41f0ca32ddSmartin #define TDA_FANSPEED_MIN 0x0c
42f0ca32ddSmartin #define TDA_FANSPEED_MAX 0x3f
43f0ca32ddSmartin
44f0ca32ddSmartin #define TDA_PSFAN_ON 0x1f
45f0ca32ddSmartin #define TDA_PSFAN_OFF 0x00
46f0ca32ddSmartin
47674d0e26Sjdc /* Internal and External temperature sensor numbers */
48f0ca32ddSmartin #define SENSOR_TEMP_EXT 0
49f0ca32ddSmartin #define SENSOR_TEMP_INT 1
50f0ca32ddSmartin
51674d0e26Sjdc /* Fan sensor numbers */
52674d0e26Sjdc #define SENSOR_FAN_CPU 0
53674d0e26Sjdc #define SENSOR_FAN_SYS 1
54674d0e26Sjdc
5550c8ce9fSmrg #define DEGC_TO_mK(c) (((c) * 1000000) + 273150000)
5650c8ce9fSmrg
5750c8ce9fSmrg #define CPU_TEMP_MAX DEGC_TO_mK(67)
5850c8ce9fSmrg #define CPU_TEMP_MIN DEGC_TO_mK(57)
5950c8ce9fSmrg #define SYS_TEMP_MAX DEGC_TO_mK(30)
6050c8ce9fSmrg #define SYS_TEMP_MIN DEGC_TO_mK(20)
61f0ca32ddSmartin
62f0ca32ddSmartin struct tda_softc {
63f0ca32ddSmartin device_t sc_dev;
64f0ca32ddSmartin i2c_tag_t sc_tag;
65f0ca32ddSmartin i2c_addr_t sc_addr;
66f0ca32ddSmartin
67f0ca32ddSmartin u_int16_t sc_cfan_speed; /* current CPU fan speed */
68f0ca32ddSmartin u_int16_t sc_sfan_speed; /* current SYS fan speed */
69f0ca32ddSmartin
70674d0e26Sjdc struct sysmon_envsys *sc_sme;
71674d0e26Sjdc envsys_data_t sc_sensor[2];
72674d0e26Sjdc
73f0ca32ddSmartin callout_t sc_timer;
74f0ca32ddSmartin };
75f0ca32ddSmartin
76cbab9cadSchs int tda_match(device_t, cfdata_t, void *);
77cbab9cadSchs void tda_attach(device_t, device_t, void *);
78dd24a5a7Sjdc static int tda_detach(device_t, int);
79674d0e26Sjdc void tda_refresh(struct sysmon_envsys *, envsys_data_t *);
80f0ca32ddSmartin
81f0ca32ddSmartin void tda_setspeed(struct tda_softc *);
82f0ca32ddSmartin static void tda_adjust(void *);
83f0ca32ddSmartin static void tda_timeout(void *);
84f0ca32ddSmartin
85f0ca32ddSmartin
86dd24a5a7Sjdc CFATTACH_DECL3_NEW(tda, sizeof(struct tda_softc),
87dd24a5a7Sjdc tda_match, tda_attach, tda_detach, NULL, NULL, NULL,
88dd24a5a7Sjdc DVF_DETACH_SHUTDOWN);
89f0ca32ddSmartin
90f0ca32ddSmartin int
tda_match(device_t parent,cfdata_t match,void * aux)91cbab9cadSchs tda_match(device_t parent, cfdata_t match, void *aux)
92f0ca32ddSmartin {
93f0ca32ddSmartin struct i2c_attach_args *ia = aux;
94f0ca32ddSmartin
95f0ca32ddSmartin /* Only attach on the Sun Blade 1000/2000. */
960eda4b70Sjdc if (strcmp(machine_model, "SUNW,Sun-Blade-1000") != 0)
97f0ca32ddSmartin return (0);
98f0ca32ddSmartin
99f0ca32ddSmartin /*
100f0ca32ddSmartin * No need for "compatible" matching, we know exactly what
101f0ca32ddSmartin * firmware calls us.
102f0ca32ddSmartin */
1032025001eSjdc if (ia->ia_name == NULL)
1042025001eSjdc return(0);
105aa41e992Sthorpej
106aa41e992Sthorpej return strcmp(ia->ia_name, "fan-control") == 0 ?
107aa41e992Sthorpej I2C_MATCH_DIRECT_SPECIFIC : 0;
108f0ca32ddSmartin }
109f0ca32ddSmartin
110f0ca32ddSmartin void
tda_attach(device_t parent,device_t self,void * aux)111cbab9cadSchs tda_attach(device_t parent, device_t self, void *aux)
112f0ca32ddSmartin {
113f0ca32ddSmartin struct tda_softc *sc = device_private(self);
114f0ca32ddSmartin struct i2c_attach_args *ia = aux;
11592e2cbacSmartin int rc;
116f0ca32ddSmartin
117f0ca32ddSmartin sc->sc_dev = self;
118f0ca32ddSmartin sc->sc_tag = ia->ia_tag;
119f0ca32ddSmartin sc->sc_addr = ia->ia_addr;
120f0ca32ddSmartin
121f0ca32ddSmartin aprint_normal(": %s\n", ia->ia_name);
122ebc9e233Smrg aprint_naive(": Environment sensor\n");
123f0ca32ddSmartin
124f0ca32ddSmartin /*
125f0ca32ddSmartin * Set the fans to maximum speed and save the power levels;
126f0ca32ddSmartin * the controller is write-only.
127f0ca32ddSmartin */
128f0ca32ddSmartin sc->sc_cfan_speed = sc->sc_sfan_speed = (TDA_FANSPEED_MAX+TDA_FANSPEED_MIN)/2;
129f0ca32ddSmartin tda_setspeed(sc);
130f0ca32ddSmartin
131f0ca32ddSmartin callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
132f0ca32ddSmartin callout_reset(&sc->sc_timer, hz*20, tda_timeout, sc);
133674d0e26Sjdc
134674d0e26Sjdc /* Initialise sensor data */
135674d0e26Sjdc sc->sc_sensor[SENSOR_FAN_CPU].state = ENVSYS_SINVALID;
136674d0e26Sjdc sc->sc_sensor[SENSOR_FAN_CPU].units = ENVSYS_INTEGER;
137674d0e26Sjdc sc->sc_sensor[SENSOR_FAN_CPU].flags = ENVSYS_FMONNOTSUPP;
138674d0e26Sjdc strlcpy(sc->sc_sensor[SENSOR_FAN_CPU].desc,
139674d0e26Sjdc "fan.cpu",sizeof("fan.cpu"));
140674d0e26Sjdc sc->sc_sensor[SENSOR_FAN_SYS].state = ENVSYS_SINVALID;
141674d0e26Sjdc sc->sc_sensor[SENSOR_FAN_SYS].units = ENVSYS_INTEGER;
142674d0e26Sjdc sc->sc_sensor[SENSOR_FAN_SYS].flags = ENVSYS_FMONNOTSUPP;
143674d0e26Sjdc strlcpy(sc->sc_sensor[SENSOR_FAN_SYS].desc,
144674d0e26Sjdc "fan.sys",sizeof("fan.sys"));
145674d0e26Sjdc sc->sc_sme = sysmon_envsys_create();
14692e2cbacSmartin rc = sysmon_envsys_sensor_attach(
14792e2cbacSmartin sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_CPU]);
14892e2cbacSmartin if (rc) {
149674d0e26Sjdc sysmon_envsys_destroy(sc->sc_sme);
150674d0e26Sjdc aprint_error_dev(self,
15192e2cbacSmartin "unable to attach cpu fan at sysmon, error %d\n", rc);
152674d0e26Sjdc return;
153674d0e26Sjdc }
15492e2cbacSmartin rc = sysmon_envsys_sensor_attach(
15592e2cbacSmartin sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_SYS]);
15692e2cbacSmartin if (rc) {
157674d0e26Sjdc sysmon_envsys_destroy(sc->sc_sme);
158674d0e26Sjdc aprint_error_dev(self,
15992e2cbacSmartin "unable to attach sys fan at sysmon, error %d\n", rc);
160674d0e26Sjdc return;
161674d0e26Sjdc }
162674d0e26Sjdc sc->sc_sme->sme_name = device_xname(self);
163674d0e26Sjdc sc->sc_sme->sme_cookie = sc;
164674d0e26Sjdc sc->sc_sme->sme_refresh = tda_refresh;
16592e2cbacSmartin rc = sysmon_envsys_register(sc->sc_sme);
16692e2cbacSmartin if (rc) {
167674d0e26Sjdc aprint_error_dev(self,
16892e2cbacSmartin "unable to register with sysmon, error %d\n", rc);
169674d0e26Sjdc sysmon_envsys_destroy(sc->sc_sme);
170674d0e26Sjdc return;
171674d0e26Sjdc }
172f0ca32ddSmartin }
173f0ca32ddSmartin
174dd24a5a7Sjdc int
tda_detach(device_t self,int flags)175dd24a5a7Sjdc tda_detach(device_t self, int flags)
176dd24a5a7Sjdc {
177dd24a5a7Sjdc struct tda_softc *sc = device_private(self);
178dd24a5a7Sjdc
179dd24a5a7Sjdc if (sc->sc_sme != NULL)
180*f74360b9Sjdc sysmon_envsys_unregister(sc->sc_sme);
181dd24a5a7Sjdc
182dd24a5a7Sjdc callout_halt(&sc->sc_timer, NULL);
183dd24a5a7Sjdc callout_destroy(&sc->sc_timer);
184dd24a5a7Sjdc
185dd24a5a7Sjdc sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX;
186dd24a5a7Sjdc tda_setspeed(sc);
187dd24a5a7Sjdc return 0;
188dd24a5a7Sjdc }
189dd24a5a7Sjdc
190f0ca32ddSmartin static void
tda_timeout(void * v)191f0ca32ddSmartin tda_timeout(void *v)
192f0ca32ddSmartin {
193f0ca32ddSmartin struct tda_softc *sc = v;
194f0ca32ddSmartin
195f0ca32ddSmartin sysmon_task_queue_sched(0, tda_adjust, sc);
196f0ca32ddSmartin callout_reset(&sc->sc_timer, hz*60, tda_timeout, sc);
197f0ca32ddSmartin }
198f0ca32ddSmartin
199f0ca32ddSmartin void
tda_setspeed(struct tda_softc * sc)200f0ca32ddSmartin tda_setspeed(struct tda_softc *sc)
201f0ca32ddSmartin {
202f0ca32ddSmartin u_int8_t cmd[2];
203f0ca32ddSmartin
204f0ca32ddSmartin if (sc->sc_cfan_speed < TDA_FANSPEED_MIN)
205f0ca32ddSmartin sc->sc_cfan_speed = TDA_FANSPEED_MIN;
206f0ca32ddSmartin if (sc->sc_sfan_speed < TDA_FANSPEED_MIN)
207f0ca32ddSmartin sc->sc_sfan_speed = TDA_FANSPEED_MIN;
208f0ca32ddSmartin if (sc->sc_cfan_speed > TDA_FANSPEED_MAX)
209f0ca32ddSmartin sc->sc_cfan_speed = TDA_FANSPEED_MAX;
210f0ca32ddSmartin if (sc->sc_sfan_speed > TDA_FANSPEED_MAX)
211f0ca32ddSmartin sc->sc_sfan_speed = TDA_FANSPEED_MAX;
212f0ca32ddSmartin
213f0ca32ddSmartin iic_acquire_bus(sc->sc_tag, 0);
214f0ca32ddSmartin
215f0ca32ddSmartin cmd[0] = TDA_CPUFAN_REG;
216f0ca32ddSmartin cmd[1] = sc->sc_cfan_speed;
217f0ca32ddSmartin if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
218f0ca32ddSmartin sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
219f0ca32ddSmartin aprint_error_dev(sc->sc_dev, "cannot write cpu-fan register\n");
220f0ca32ddSmartin iic_release_bus(sc->sc_tag, 0);
221f0ca32ddSmartin return;
222f0ca32ddSmartin }
223f0ca32ddSmartin
224f0ca32ddSmartin cmd[0] = TDA_SYSFAN_REG;
225f0ca32ddSmartin cmd[1] = sc->sc_sfan_speed;
226f0ca32ddSmartin if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
227f0ca32ddSmartin sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
228f0ca32ddSmartin aprint_error_dev(sc->sc_dev, "cannot write system-fan register\n");
229f0ca32ddSmartin iic_release_bus(sc->sc_tag, 0);
230f0ca32ddSmartin return;
231f0ca32ddSmartin }
232f0ca32ddSmartin
233f0ca32ddSmartin iic_release_bus(sc->sc_tag, 0);
234f0ca32ddSmartin
235f0ca32ddSmartin aprint_debug_dev(sc->sc_dev, "changed fan speed to cpu=%d system=%d\n",
236f0ca32ddSmartin sc->sc_cfan_speed, sc->sc_sfan_speed);
237f0ca32ddSmartin }
238f0ca32ddSmartin
239f0ca32ddSmartin static bool
is_cpu_sensor(const envsys_data_t * edata)240f0ca32ddSmartin is_cpu_sensor(const envsys_data_t *edata)
241f0ca32ddSmartin {
242f0ca32ddSmartin if (edata->units != ENVSYS_STEMP)
243f0ca32ddSmartin return false;
244f0ca32ddSmartin return strcmp(edata->desc, "external") == 0;
245f0ca32ddSmartin }
246f0ca32ddSmartin
247f0ca32ddSmartin static bool
is_system_sensor(const envsys_data_t * edata)248f0ca32ddSmartin is_system_sensor(const envsys_data_t *edata)
249f0ca32ddSmartin {
250f0ca32ddSmartin if (edata->units != ENVSYS_STEMP)
251f0ca32ddSmartin return false;
252f0ca32ddSmartin return strcmp(edata->desc, "internal") == 0;
253f0ca32ddSmartin }
254f0ca32ddSmartin
255f0ca32ddSmartin static void
tda_adjust(void * v)256f0ca32ddSmartin tda_adjust(void *v)
257f0ca32ddSmartin {
258f0ca32ddSmartin struct tda_softc *sc = v;
259f0ca32ddSmartin u_int64_t ctemp, stemp;
260f0ca32ddSmartin u_int16_t cspeed, sspeed;
261f0ca32ddSmartin
262f0ca32ddSmartin /* Default to running the fans at maximum speed. */
263f0ca32ddSmartin sspeed = cspeed = TDA_FANSPEED_MAX;
264f0ca32ddSmartin
265f0ca32ddSmartin /* fetch maximum current temperature */
266f0ca32ddSmartin ctemp = sysmon_envsys_get_max_value(is_cpu_sensor, true);
267f0ca32ddSmartin stemp = sysmon_envsys_get_max_value(is_system_sensor, true);
268f0ca32ddSmartin
269f0ca32ddSmartin /* the predicates for selecting sensors must have gone wrong */
270f0ca32ddSmartin if (ctemp == 0 || stemp == 0) {
271f0ca32ddSmartin aprint_error_dev(sc->sc_dev, "skipping temp adjustment"
272f0ca32ddSmartin " - no sensor values\n");
273f0ca32ddSmartin return;
274f0ca32ddSmartin }
275f0ca32ddSmartin
276999add04Smartin aprint_debug_dev(sc->sc_dev, "current temperature: cpu %" PRIu64
277999add04Smartin " system %" PRIu64 "\n",
278f0ca32ddSmartin ctemp, stemp);
279f0ca32ddSmartin
280f0ca32ddSmartin if (ctemp < CPU_TEMP_MIN)
281f0ca32ddSmartin cspeed = TDA_FANSPEED_MIN;
282f0ca32ddSmartin else if (ctemp < CPU_TEMP_MAX)
283f0ca32ddSmartin cspeed = TDA_FANSPEED_MIN +
284f0ca32ddSmartin (ctemp - CPU_TEMP_MIN) *
285f0ca32ddSmartin (TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
286f0ca32ddSmartin (CPU_TEMP_MAX - CPU_TEMP_MIN);
287f0ca32ddSmartin
288f0ca32ddSmartin if (stemp < SYS_TEMP_MIN)
289f0ca32ddSmartin sspeed = TDA_FANSPEED_MIN;
290f0ca32ddSmartin else if (stemp < SYS_TEMP_MAX)
291674d0e26Sjdc sspeed = TDA_FANSPEED_MIN +
292f0ca32ddSmartin (stemp - SYS_TEMP_MIN) *
293f0ca32ddSmartin (TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
294f0ca32ddSmartin (SYS_TEMP_MAX - SYS_TEMP_MIN);
295f0ca32ddSmartin
296f0ca32ddSmartin if (sspeed == sc->sc_sfan_speed && cspeed == sc->sc_cfan_speed)
297f0ca32ddSmartin return;
298f0ca32ddSmartin
299f0ca32ddSmartin sc->sc_sfan_speed = sspeed;
300f0ca32ddSmartin sc->sc_cfan_speed = cspeed;
301f0ca32ddSmartin tda_setspeed(sc);
302f0ca32ddSmartin }
303674d0e26Sjdc
304674d0e26Sjdc void
tda_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)305674d0e26Sjdc tda_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
306674d0e26Sjdc {
307674d0e26Sjdc struct tda_softc *sc = sme->sme_cookie;
308674d0e26Sjdc u_int16_t speed;
309674d0e26Sjdc
310674d0e26Sjdc if (edata->sensor == SENSOR_FAN_CPU)
311674d0e26Sjdc speed = sc->sc_cfan_speed;
312674d0e26Sjdc else
313674d0e26Sjdc speed = sc->sc_sfan_speed;
314674d0e26Sjdc if (!speed)
315674d0e26Sjdc edata->state = ENVSYS_SINVALID;
316674d0e26Sjdc else {
317674d0e26Sjdc edata->value_cur = speed;
318674d0e26Sjdc edata->state = ENVSYS_SVALID;
319674d0e26Sjdc }
320674d0e26Sjdc }
321674d0e26Sjdc
322