Home | History | Annotate | Line # | Download | only in dev
tda.c revision 1.11.16.1
      1  1.11.16.1  pgoyette /*	$NetBSD: tda.c,v 1.11.16.1 2018/06/25 07:25:45 pgoyette Exp $	*/
      2        1.1    martin /*	$OpenBSD: tda.c,v 1.4 2008/02/27 17:25:00 robert Exp $ */
      3        1.1    martin 
      4        1.1    martin /*
      5        1.1    martin  * Copyright (c) 2008 Robert Nagy <robert (at) openbsd.org>
      6        1.1    martin  * Copyright (c) 2008 Mark Kettenis <kettenis (at) openbsd.org>
      7        1.1    martin  *
      8        1.1    martin  * Permission to use, copy, modify, and distribute this software for any
      9        1.1    martin  * purpose with or without fee is hereby granted, provided that the above
     10        1.1    martin  * copyright notice and this permission notice appear in all copies.
     11        1.1    martin  *
     12        1.1    martin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     13        1.1    martin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     14        1.1    martin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     15        1.1    martin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     16        1.1    martin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     17        1.1    martin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     18        1.1    martin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     19        1.1    martin  */
     20        1.1    martin 
     21        1.5       mrg #include <sys/cdefs.h>
     22  1.11.16.1  pgoyette __KERNEL_RCSID(0, "$NetBSD: tda.c,v 1.11.16.1 2018/06/25 07:25:45 pgoyette Exp $");
     23        1.5       mrg 
     24        1.1    martin #include <sys/param.h>
     25        1.1    martin #include <sys/systm.h>
     26        1.1    martin #include <sys/kernel.h>
     27        1.1    martin #include <sys/device.h>
     28        1.1    martin #include <dev/sysmon/sysmonvar.h>
     29        1.1    martin #include <dev/sysmon/sysmon_taskq.h>
     30        1.1    martin 
     31        1.1    martin #include <machine/autoconf.h>
     32        1.1    martin #include <machine/openfirm.h>
     33        1.1    martin 
     34        1.1    martin #include <dev/i2c/i2cvar.h>
     35        1.1    martin 
     36        1.1    martin /* fan control registers */
     37        1.1    martin #define TDA_SYSFAN_REG		0xf0
     38        1.1    martin #define TDA_CPUFAN_REG		0xf2
     39        1.1    martin #define TDA_PSFAN_REG		0xf4
     40        1.1    martin 
     41        1.1    martin #define TDA_FANSPEED_MIN        0x0c
     42        1.1    martin #define TDA_FANSPEED_MAX        0x3f
     43        1.1    martin 
     44        1.1    martin #define TDA_PSFAN_ON            0x1f
     45        1.1    martin #define TDA_PSFAN_OFF           0x00
     46        1.1    martin 
     47        1.7       jdc /* Internal and External temperature sensor numbers */
     48        1.1    martin #define SENSOR_TEMP_EXT		0
     49        1.1    martin #define SENSOR_TEMP_INT		1
     50        1.1    martin 
     51        1.7       jdc /* Fan sensor numbers */
     52        1.7       jdc #define SENSOR_FAN_CPU		0
     53        1.7       jdc #define SENSOR_FAN_SYS		1
     54        1.7       jdc 
     55        1.1    martin #define CPU_TEMP_MAX		(67 * 1000000 + 273150000)
     56        1.1    martin #define CPU_TEMP_MIN		(57 * 1000000 + 273150000)
     57        1.1    martin #define SYS_TEMP_MAX		(30 * 1000000 + 273150000)
     58        1.1    martin #define SYS_TEMP_MIN		(20 * 1000000 + 273150000)
     59        1.1    martin 
     60        1.1    martin struct tda_softc {
     61        1.1    martin 	device_t		sc_dev;
     62        1.1    martin 	i2c_tag_t		sc_tag;
     63        1.1    martin 	i2c_addr_t		sc_addr;
     64        1.1    martin 
     65        1.1    martin 	u_int16_t		sc_cfan_speed;	/* current CPU fan speed */
     66        1.1    martin 	u_int16_t		sc_sfan_speed;	/* current SYS fan speed */
     67        1.1    martin 
     68        1.7       jdc 	struct sysmon_envsys	*sc_sme;
     69        1.7       jdc 	envsys_data_t		sc_sensor[2];
     70        1.7       jdc 
     71        1.1    martin 	callout_t		sc_timer;
     72        1.1    martin };
     73        1.1    martin 
     74        1.6       chs int	tda_match(device_t, cfdata_t, void *);
     75        1.6       chs void	tda_attach(device_t, device_t, void *);
     76        1.8       jdc static int	tda_detach(device_t, int);
     77        1.7       jdc void	tda_refresh(struct sysmon_envsys *, envsys_data_t *);
     78        1.1    martin 
     79        1.1    martin void	tda_setspeed(struct tda_softc *);
     80        1.1    martin static void	tda_adjust(void *);
     81        1.1    martin static void	tda_timeout(void *);
     82        1.1    martin 
     83        1.1    martin 
     84        1.8       jdc CFATTACH_DECL3_NEW(tda, sizeof(struct tda_softc),
     85        1.8       jdc 	tda_match, tda_attach, tda_detach, NULL, NULL, NULL,
     86        1.8       jdc 	DVF_DETACH_SHUTDOWN);
     87        1.1    martin 
     88        1.1    martin int
     89        1.6       chs tda_match(device_t parent, cfdata_t match, void *aux)
     90        1.1    martin {
     91        1.1    martin 	struct i2c_attach_args *ia = aux;
     92        1.1    martin 
     93        1.1    martin 	/* Only attach on the Sun Blade 1000/2000. */
     94        1.9       jdc 	if (strcmp(machine_model, "SUNW,Sun-Blade-1000") != 0)
     95        1.1    martin 		return (0);
     96        1.1    martin 
     97        1.1    martin 	/*
     98        1.1    martin 	 * No need for "compatible" matching, we know exactly what
     99        1.1    martin 	 * firmware calls us.
    100        1.1    martin 	 */
    101        1.4       jdc 	if (ia->ia_name == NULL)
    102        1.4       jdc 		return(0);
    103  1.11.16.1  pgoyette 
    104  1.11.16.1  pgoyette 	return strcmp(ia->ia_name, "fan-control") == 0 ?
    105  1.11.16.1  pgoyette 	    I2C_MATCH_DIRECT_SPECIFIC : 0;
    106        1.1    martin }
    107        1.1    martin 
    108        1.1    martin void
    109        1.6       chs tda_attach(device_t parent, device_t self, void *aux)
    110        1.1    martin {
    111        1.1    martin 	struct tda_softc *sc = device_private(self);
    112        1.1    martin 	struct i2c_attach_args *ia = aux;
    113       1.10    martin 	int rc;
    114        1.1    martin 
    115        1.1    martin 	sc->sc_dev = self;
    116        1.1    martin 	sc->sc_tag = ia->ia_tag;
    117        1.1    martin 	sc->sc_addr = ia->ia_addr;
    118        1.1    martin 
    119        1.1    martin 	aprint_normal(": %s\n", ia->ia_name);
    120        1.3       mrg 	aprint_naive(": Environment sensor\n");
    121        1.1    martin 
    122        1.1    martin 	/*
    123        1.1    martin 	 * Set the fans to maximum speed and save the power levels;
    124        1.1    martin 	 * the controller is write-only.
    125        1.1    martin 	 */
    126        1.1    martin 	sc->sc_cfan_speed = sc->sc_sfan_speed = (TDA_FANSPEED_MAX+TDA_FANSPEED_MIN)/2;
    127        1.1    martin 	tda_setspeed(sc);
    128        1.7       jdc 
    129        1.1    martin 	callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
    130        1.1    martin 	callout_reset(&sc->sc_timer, hz*20, tda_timeout, sc);
    131        1.7       jdc 
    132        1.7       jdc 	/* Initialise sensor data */
    133        1.7       jdc 	sc->sc_sensor[SENSOR_FAN_CPU].state = ENVSYS_SINVALID;
    134        1.7       jdc 	sc->sc_sensor[SENSOR_FAN_CPU].units = ENVSYS_INTEGER;
    135        1.7       jdc 	sc->sc_sensor[SENSOR_FAN_CPU].flags = ENVSYS_FMONNOTSUPP;
    136        1.7       jdc 	strlcpy(sc->sc_sensor[SENSOR_FAN_CPU].desc,
    137        1.7       jdc 	    "fan.cpu",sizeof("fan.cpu"));
    138        1.7       jdc 	sc->sc_sensor[SENSOR_FAN_SYS].state = ENVSYS_SINVALID;
    139        1.7       jdc 	sc->sc_sensor[SENSOR_FAN_SYS].units = ENVSYS_INTEGER;
    140        1.7       jdc 	sc->sc_sensor[SENSOR_FAN_SYS].flags = ENVSYS_FMONNOTSUPP;
    141        1.7       jdc 	strlcpy(sc->sc_sensor[SENSOR_FAN_SYS].desc,
    142        1.7       jdc 	    "fan.sys",sizeof("fan.sys"));
    143        1.7       jdc 	sc->sc_sme = sysmon_envsys_create();
    144       1.10    martin 	rc = sysmon_envsys_sensor_attach(
    145       1.10    martin 	    sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_CPU]);
    146       1.10    martin 	if (rc) {
    147        1.7       jdc 		sysmon_envsys_destroy(sc->sc_sme);
    148        1.7       jdc 		aprint_error_dev(self,
    149       1.10    martin 		    "unable to attach cpu fan at sysmon, error %d\n", rc);
    150        1.7       jdc 		return;
    151        1.7       jdc 	}
    152       1.10    martin 	rc = sysmon_envsys_sensor_attach(
    153       1.10    martin 	    sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_SYS]);
    154       1.10    martin 	if (rc) {
    155        1.7       jdc 		sysmon_envsys_destroy(sc->sc_sme);
    156        1.7       jdc 		aprint_error_dev(self,
    157       1.10    martin 		    "unable to attach sys fan at sysmon, error %d\n", rc);
    158        1.7       jdc 		return;
    159        1.7       jdc 	}
    160        1.7       jdc         sc->sc_sme->sme_name = device_xname(self);
    161        1.7       jdc         sc->sc_sme->sme_cookie = sc;
    162        1.7       jdc         sc->sc_sme->sme_refresh = tda_refresh;
    163       1.10    martin 	rc = sysmon_envsys_register(sc->sc_sme);
    164       1.10    martin 	if (rc) {
    165        1.7       jdc 		aprint_error_dev(self,
    166       1.10    martin 		    "unable to register with sysmon, error %d\n", rc);
    167        1.7       jdc 		sysmon_envsys_destroy(sc->sc_sme);
    168        1.7       jdc 		return;
    169        1.7       jdc 	}
    170        1.1    martin }
    171        1.1    martin 
    172        1.8       jdc int
    173        1.8       jdc tda_detach(device_t self, int flags)
    174        1.8       jdc {
    175        1.8       jdc 	struct tda_softc *sc = device_private(self);
    176        1.8       jdc 
    177        1.8       jdc 	if (sc->sc_sme != NULL)
    178        1.8       jdc 		sysmon_envsys_destroy(sc->sc_sme);
    179        1.8       jdc 
    180        1.8       jdc 	callout_halt(&sc->sc_timer, NULL);
    181        1.8       jdc 	callout_destroy(&sc->sc_timer);
    182        1.8       jdc 
    183        1.8       jdc 	sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX;
    184        1.8       jdc 	tda_setspeed(sc);
    185        1.8       jdc 	return 0;
    186        1.8       jdc }
    187        1.8       jdc 
    188        1.1    martin static void
    189        1.1    martin tda_timeout(void *v)
    190        1.1    martin {
    191        1.1    martin 	struct tda_softc *sc = v;
    192        1.1    martin 
    193        1.1    martin 	sysmon_task_queue_sched(0, tda_adjust, sc);
    194        1.1    martin 	callout_reset(&sc->sc_timer, hz*60, tda_timeout, sc);
    195        1.1    martin }
    196        1.1    martin 
    197        1.1    martin void
    198        1.1    martin tda_setspeed(struct tda_softc *sc)
    199        1.1    martin {
    200        1.1    martin 	u_int8_t cmd[2];
    201        1.1    martin 
    202        1.1    martin 	if (sc->sc_cfan_speed < TDA_FANSPEED_MIN)
    203        1.1    martin 		sc->sc_cfan_speed = TDA_FANSPEED_MIN;
    204        1.1    martin 	if (sc->sc_sfan_speed < TDA_FANSPEED_MIN)
    205        1.1    martin 		sc->sc_sfan_speed = TDA_FANSPEED_MIN;
    206        1.1    martin 	if (sc->sc_cfan_speed > TDA_FANSPEED_MAX)
    207        1.1    martin 		sc->sc_cfan_speed = TDA_FANSPEED_MAX;
    208        1.1    martin 	if (sc->sc_sfan_speed > TDA_FANSPEED_MAX)
    209        1.1    martin 		sc->sc_sfan_speed = TDA_FANSPEED_MAX;
    210        1.1    martin 
    211        1.1    martin 	iic_acquire_bus(sc->sc_tag, 0);
    212        1.1    martin 
    213        1.1    martin 	cmd[0] = TDA_CPUFAN_REG;
    214        1.1    martin 	cmd[1] = sc->sc_cfan_speed;
    215        1.1    martin 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
    216        1.1    martin 	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
    217        1.1    martin 		aprint_error_dev(sc->sc_dev, "cannot write cpu-fan register\n");
    218        1.1    martin 		iic_release_bus(sc->sc_tag, 0);
    219        1.1    martin 		return;
    220        1.1    martin         }
    221        1.1    martin 
    222        1.1    martin 	cmd[0] = TDA_SYSFAN_REG;
    223        1.1    martin 	cmd[1] = sc->sc_sfan_speed;
    224        1.1    martin 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
    225        1.1    martin 	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
    226        1.1    martin 		aprint_error_dev(sc->sc_dev, "cannot write system-fan register\n");
    227        1.1    martin 		iic_release_bus(sc->sc_tag, 0);
    228        1.1    martin 		return;
    229        1.1    martin         }
    230        1.1    martin 
    231        1.1    martin 	iic_release_bus(sc->sc_tag, 0);
    232        1.1    martin 
    233        1.1    martin 	aprint_debug_dev(sc->sc_dev, "changed fan speed to cpu=%d system=%d\n",
    234       1.11   msaitoh 		sc->sc_cfan_speed, sc->sc_sfan_speed);
    235        1.1    martin }
    236        1.1    martin 
    237        1.1    martin static bool
    238        1.1    martin is_cpu_sensor(const envsys_data_t *edata)
    239        1.1    martin {
    240        1.1    martin 	if (edata->units != ENVSYS_STEMP)
    241        1.1    martin 		return false;
    242        1.1    martin 	return strcmp(edata->desc, "external") == 0;
    243        1.1    martin }
    244        1.1    martin 
    245        1.1    martin static bool
    246        1.1    martin is_system_sensor(const envsys_data_t *edata)
    247        1.1    martin {
    248        1.1    martin 	if (edata->units != ENVSYS_STEMP)
    249        1.1    martin 		return false;
    250        1.1    martin 	return strcmp(edata->desc, "internal") == 0;
    251        1.1    martin }
    252        1.1    martin 
    253        1.1    martin static void
    254        1.1    martin tda_adjust(void *v)
    255        1.1    martin {
    256        1.1    martin 	struct tda_softc *sc = v;
    257        1.1    martin 	u_int64_t ctemp, stemp;
    258        1.1    martin 	u_int16_t cspeed, sspeed;
    259        1.1    martin 
    260        1.1    martin 	/* Default to running the fans at maximum speed. */
    261        1.1    martin 	sspeed = cspeed = TDA_FANSPEED_MAX;
    262        1.1    martin 
    263        1.1    martin 	/* fetch maximum current temperature */
    264        1.1    martin 	ctemp = sysmon_envsys_get_max_value(is_cpu_sensor, true);
    265        1.1    martin 	stemp = sysmon_envsys_get_max_value(is_system_sensor, true);
    266        1.1    martin 
    267        1.1    martin 	/* the predicates for selecting sensors must have gone wrong */
    268        1.1    martin 	if (ctemp == 0 || stemp == 0) {
    269        1.1    martin 		aprint_error_dev(sc->sc_dev, "skipping temp adjustment"
    270        1.1    martin 			" - no sensor values\n");
    271        1.1    martin 		return;
    272        1.1    martin 	}
    273        1.1    martin 
    274        1.2    martin 	aprint_debug_dev(sc->sc_dev, "current temperature: cpu %" PRIu64
    275        1.2    martin 		" system %" PRIu64 "\n",
    276        1.1    martin 		ctemp, stemp);
    277        1.1    martin 
    278        1.1    martin 	if (ctemp < CPU_TEMP_MIN)
    279        1.1    martin 		cspeed = TDA_FANSPEED_MIN;
    280        1.1    martin 	else if (ctemp < CPU_TEMP_MAX)
    281        1.1    martin 		cspeed = TDA_FANSPEED_MIN +
    282        1.1    martin 			(ctemp - CPU_TEMP_MIN) *
    283        1.1    martin 			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
    284        1.1    martin 			(CPU_TEMP_MAX - CPU_TEMP_MIN);
    285        1.1    martin 
    286        1.1    martin 	if (stemp < SYS_TEMP_MIN)
    287        1.1    martin 		sspeed = TDA_FANSPEED_MIN;
    288        1.1    martin 	else if (stemp < SYS_TEMP_MAX)
    289        1.7       jdc 		sspeed = TDA_FANSPEED_MIN +
    290        1.1    martin 			(stemp - SYS_TEMP_MIN) *
    291        1.1    martin 			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
    292        1.1    martin 			(SYS_TEMP_MAX - SYS_TEMP_MIN);
    293        1.1    martin 
    294        1.1    martin 	if (sspeed == sc->sc_sfan_speed && cspeed == sc->sc_cfan_speed)
    295        1.1    martin 		return;
    296        1.1    martin 
    297        1.1    martin 	sc->sc_sfan_speed = sspeed;
    298        1.1    martin 	sc->sc_cfan_speed = cspeed;
    299        1.1    martin 	tda_setspeed(sc);
    300        1.1    martin }
    301        1.7       jdc 
    302        1.7       jdc void
    303        1.7       jdc tda_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    304        1.7       jdc {
    305        1.7       jdc 	struct tda_softc *sc = sme->sme_cookie;
    306        1.7       jdc 	u_int16_t speed;
    307        1.7       jdc 
    308        1.7       jdc 	if (edata->sensor == SENSOR_FAN_CPU)
    309        1.7       jdc 		speed = sc->sc_cfan_speed;
    310        1.7       jdc 	else
    311        1.7       jdc 		speed = sc->sc_sfan_speed;
    312        1.7       jdc 	if (!speed)
    313        1.7       jdc 		edata->state = ENVSYS_SINVALID;
    314        1.7       jdc 	else {
    315        1.7       jdc 		edata->value_cur = speed;
    316        1.7       jdc 		edata->state = ENVSYS_SVALID;
    317        1.7       jdc 	}
    318        1.7       jdc }
    319        1.7       jdc 
    320