xref: /netbsd-src/sys/arch/sparc64/dev/tda.c (revision f74360b94110aefca8ed3382a8e2bc41d9566539)
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