Home | History | Annotate | Line # | Download | only in dev
tda.c revision 1.10
      1  1.10  martin /*	$NetBSD: tda.c,v 1.10 2015/04/27 11:55:29 martin 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.10  martin __KERNEL_RCSID(0, "$NetBSD: tda.c,v 1.10 2015/04/27 11:55:29 martin 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.1  martin 	return strcmp(ia->ia_name, "fan-control") == 0;
    104   1.1  martin }
    105   1.1  martin 
    106   1.1  martin void
    107   1.6     chs tda_attach(device_t parent, device_t self, void *aux)
    108   1.1  martin {
    109   1.1  martin 	struct tda_softc *sc = device_private(self);
    110   1.1  martin 	struct i2c_attach_args *ia = aux;
    111  1.10  martin 	int rc;
    112   1.1  martin 
    113   1.1  martin 	sc->sc_dev = self;
    114   1.1  martin 	sc->sc_tag = ia->ia_tag;
    115   1.1  martin 	sc->sc_addr = ia->ia_addr;
    116   1.1  martin 
    117   1.1  martin 	aprint_normal(": %s\n", ia->ia_name);
    118   1.3     mrg 	aprint_naive(": Environment sensor\n");
    119   1.1  martin 
    120   1.1  martin 	/*
    121   1.1  martin 	 * Set the fans to maximum speed and save the power levels;
    122   1.1  martin 	 * the controller is write-only.
    123   1.1  martin 	 */
    124   1.1  martin 	sc->sc_cfan_speed = sc->sc_sfan_speed = (TDA_FANSPEED_MAX+TDA_FANSPEED_MIN)/2;
    125   1.1  martin 	tda_setspeed(sc);
    126   1.7     jdc 
    127   1.1  martin 	callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
    128   1.1  martin 	callout_reset(&sc->sc_timer, hz*20, tda_timeout, sc);
    129   1.7     jdc 
    130   1.7     jdc 	/* Initialise sensor data */
    131   1.7     jdc 	sc->sc_sensor[SENSOR_FAN_CPU].state = ENVSYS_SINVALID;
    132   1.7     jdc 	sc->sc_sensor[SENSOR_FAN_CPU].units = ENVSYS_INTEGER;
    133   1.7     jdc 	sc->sc_sensor[SENSOR_FAN_CPU].flags = ENVSYS_FMONNOTSUPP;
    134   1.7     jdc 	strlcpy(sc->sc_sensor[SENSOR_FAN_CPU].desc,
    135   1.7     jdc 	    "fan.cpu",sizeof("fan.cpu"));
    136   1.7     jdc 	sc->sc_sensor[SENSOR_FAN_SYS].state = ENVSYS_SINVALID;
    137   1.7     jdc 	sc->sc_sensor[SENSOR_FAN_SYS].units = ENVSYS_INTEGER;
    138   1.7     jdc 	sc->sc_sensor[SENSOR_FAN_SYS].flags = ENVSYS_FMONNOTSUPP;
    139   1.7     jdc 	strlcpy(sc->sc_sensor[SENSOR_FAN_SYS].desc,
    140   1.7     jdc 	    "fan.sys",sizeof("fan.sys"));
    141   1.7     jdc 	sc->sc_sme = sysmon_envsys_create();
    142  1.10  martin 	rc = sysmon_envsys_sensor_attach(
    143  1.10  martin 	    sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_CPU]);
    144  1.10  martin 	if (rc) {
    145   1.7     jdc 		sysmon_envsys_destroy(sc->sc_sme);
    146   1.7     jdc 		aprint_error_dev(self,
    147  1.10  martin 		    "unable to attach cpu fan at sysmon, error %d\n", rc);
    148   1.7     jdc 		return;
    149   1.7     jdc 	}
    150  1.10  martin 	rc = sysmon_envsys_sensor_attach(
    151  1.10  martin 	    sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_SYS]);
    152  1.10  martin 	if (rc) {
    153   1.7     jdc 		sysmon_envsys_destroy(sc->sc_sme);
    154   1.7     jdc 		aprint_error_dev(self,
    155  1.10  martin 		    "unable to attach sys fan at sysmon, error %d\n", rc);
    156   1.7     jdc 		return;
    157   1.7     jdc 	}
    158   1.7     jdc         sc->sc_sme->sme_name = device_xname(self);
    159   1.7     jdc         sc->sc_sme->sme_cookie = sc;
    160   1.7     jdc         sc->sc_sme->sme_refresh = tda_refresh;
    161  1.10  martin 	rc = sysmon_envsys_register(sc->sc_sme);
    162  1.10  martin 	if (rc) {
    163   1.7     jdc 		aprint_error_dev(self,
    164  1.10  martin 		    "unable to register with sysmon, error %d\n", rc);
    165   1.7     jdc 		sysmon_envsys_destroy(sc->sc_sme);
    166   1.7     jdc 		return;
    167   1.7     jdc 	}
    168   1.1  martin }
    169   1.1  martin 
    170   1.8     jdc int
    171   1.8     jdc tda_detach(device_t self, int flags)
    172   1.8     jdc {
    173   1.8     jdc 	struct tda_softc *sc = device_private(self);
    174   1.8     jdc 
    175   1.8     jdc 	if (sc->sc_sme != NULL)
    176   1.8     jdc 		sysmon_envsys_destroy(sc->sc_sme);
    177   1.8     jdc 
    178   1.8     jdc 	callout_halt(&sc->sc_timer, NULL);
    179   1.8     jdc 	callout_destroy(&sc->sc_timer);
    180   1.8     jdc 
    181   1.8     jdc 	sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX;
    182   1.8     jdc 	tda_setspeed(sc);
    183   1.8     jdc 	return 0;
    184   1.8     jdc }
    185   1.8     jdc 
    186   1.1  martin static void
    187   1.1  martin tda_timeout(void *v)
    188   1.1  martin {
    189   1.1  martin 	struct tda_softc *sc = v;
    190   1.1  martin 
    191   1.1  martin 	sysmon_task_queue_sched(0, tda_adjust, sc);
    192   1.1  martin 	callout_reset(&sc->sc_timer, hz*60, tda_timeout, sc);
    193   1.1  martin }
    194   1.1  martin 
    195   1.1  martin void
    196   1.1  martin tda_setspeed(struct tda_softc *sc)
    197   1.1  martin {
    198   1.1  martin 	u_int8_t cmd[2];
    199   1.1  martin 
    200   1.1  martin 	if (sc->sc_cfan_speed < TDA_FANSPEED_MIN)
    201   1.1  martin 		sc->sc_cfan_speed = TDA_FANSPEED_MIN;
    202   1.1  martin 	if (sc->sc_sfan_speed < TDA_FANSPEED_MIN)
    203   1.1  martin 		sc->sc_sfan_speed = TDA_FANSPEED_MIN;
    204   1.1  martin 	if (sc->sc_cfan_speed > TDA_FANSPEED_MAX)
    205   1.1  martin 		sc->sc_cfan_speed = TDA_FANSPEED_MAX;
    206   1.1  martin 	if (sc->sc_sfan_speed > TDA_FANSPEED_MAX)
    207   1.1  martin 		sc->sc_sfan_speed = TDA_FANSPEED_MAX;
    208   1.1  martin 
    209   1.1  martin 	iic_acquire_bus(sc->sc_tag, 0);
    210   1.1  martin 
    211   1.1  martin 	cmd[0] = TDA_CPUFAN_REG;
    212   1.1  martin 	cmd[1] = sc->sc_cfan_speed;
    213   1.1  martin 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
    214   1.1  martin 	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
    215   1.1  martin 		aprint_error_dev(sc->sc_dev, "cannot write cpu-fan register\n");
    216   1.1  martin 		iic_release_bus(sc->sc_tag, 0);
    217   1.1  martin 		return;
    218   1.1  martin         }
    219   1.1  martin 
    220   1.1  martin 	cmd[0] = TDA_SYSFAN_REG;
    221   1.1  martin 	cmd[1] = sc->sc_sfan_speed;
    222   1.1  martin 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
    223   1.1  martin 	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
    224   1.1  martin 		aprint_error_dev(sc->sc_dev, "cannot write system-fan register\n");
    225   1.1  martin 		iic_release_bus(sc->sc_tag, 0);
    226   1.1  martin 		return;
    227   1.1  martin         }
    228   1.1  martin 
    229   1.1  martin 	iic_release_bus(sc->sc_tag, 0);
    230   1.1  martin 
    231   1.1  martin 	aprint_debug_dev(sc->sc_dev, "changed fan speed to cpu=%d system=%d\n",
    232   1.1  martin 		sc->sc_cfan_speed, sc->sc_sfan_speed);
    233   1.1  martin }
    234   1.1  martin 
    235   1.1  martin static bool
    236   1.1  martin is_cpu_sensor(const envsys_data_t *edata)
    237   1.1  martin {
    238   1.1  martin 	if (edata->units != ENVSYS_STEMP)
    239   1.1  martin 		return false;
    240   1.1  martin 	return strcmp(edata->desc, "external") == 0;
    241   1.1  martin }
    242   1.1  martin 
    243   1.1  martin static bool
    244   1.1  martin is_system_sensor(const envsys_data_t *edata)
    245   1.1  martin {
    246   1.1  martin 	if (edata->units != ENVSYS_STEMP)
    247   1.1  martin 		return false;
    248   1.1  martin 	return strcmp(edata->desc, "internal") == 0;
    249   1.1  martin }
    250   1.1  martin 
    251   1.1  martin static void
    252   1.1  martin tda_adjust(void *v)
    253   1.1  martin {
    254   1.1  martin 	struct tda_softc *sc = v;
    255   1.1  martin 	u_int64_t ctemp, stemp;
    256   1.1  martin 	u_int16_t cspeed, sspeed;
    257   1.1  martin 
    258   1.1  martin 	/* Default to running the fans at maximum speed. */
    259   1.1  martin 	sspeed = cspeed = TDA_FANSPEED_MAX;
    260   1.1  martin 
    261   1.1  martin 	/* fetch maximum current temperature */
    262   1.1  martin 	ctemp = sysmon_envsys_get_max_value(is_cpu_sensor, true);
    263   1.1  martin 	stemp = sysmon_envsys_get_max_value(is_system_sensor, true);
    264   1.1  martin 
    265   1.1  martin 	/* the predicates for selecting sensors must have gone wrong */
    266   1.1  martin 	if (ctemp == 0 || stemp == 0) {
    267   1.1  martin 		aprint_error_dev(sc->sc_dev, "skipping temp adjustment"
    268   1.1  martin 			" - no sensor values\n");
    269   1.1  martin 		return;
    270   1.1  martin 	}
    271   1.1  martin 
    272   1.2  martin 	aprint_debug_dev(sc->sc_dev, "current temperature: cpu %" PRIu64
    273   1.2  martin 		" system %" PRIu64 "\n",
    274   1.1  martin 		ctemp, stemp);
    275   1.1  martin 
    276   1.1  martin 	if (ctemp < CPU_TEMP_MIN)
    277   1.1  martin 		cspeed = TDA_FANSPEED_MIN;
    278   1.1  martin 	else if (ctemp < CPU_TEMP_MAX)
    279   1.1  martin 		cspeed = TDA_FANSPEED_MIN +
    280   1.1  martin 			(ctemp - CPU_TEMP_MIN) *
    281   1.1  martin 			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
    282   1.1  martin 			(CPU_TEMP_MAX - CPU_TEMP_MIN);
    283   1.1  martin 
    284   1.1  martin 	if (stemp < SYS_TEMP_MIN)
    285   1.1  martin 		sspeed = TDA_FANSPEED_MIN;
    286   1.1  martin 	else if (stemp < SYS_TEMP_MAX)
    287   1.7     jdc 		sspeed = TDA_FANSPEED_MIN +
    288   1.1  martin 			(stemp - SYS_TEMP_MIN) *
    289   1.1  martin 			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
    290   1.1  martin 			(SYS_TEMP_MAX - SYS_TEMP_MIN);
    291   1.1  martin 
    292   1.1  martin 	if (sspeed == sc->sc_sfan_speed && cspeed == sc->sc_cfan_speed)
    293   1.1  martin 		return;
    294   1.1  martin 
    295   1.1  martin 	sc->sc_sfan_speed = sspeed;
    296   1.1  martin 	sc->sc_cfan_speed = cspeed;
    297   1.1  martin 	tda_setspeed(sc);
    298   1.1  martin }
    299   1.7     jdc 
    300   1.7     jdc void
    301   1.7     jdc tda_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    302   1.7     jdc {
    303   1.7     jdc 	struct tda_softc *sc = sme->sme_cookie;
    304   1.7     jdc 	u_int16_t speed;
    305   1.7     jdc 
    306   1.7     jdc 	if (edata->sensor == SENSOR_FAN_CPU)
    307   1.7     jdc 		speed = sc->sc_cfan_speed;
    308   1.7     jdc 	else
    309   1.7     jdc 		speed = sc->sc_sfan_speed;
    310   1.7     jdc 	if (!speed)
    311   1.7     jdc 		edata->state = ENVSYS_SINVALID;
    312   1.7     jdc 	else {
    313   1.7     jdc 		edata->value_cur = speed;
    314   1.7     jdc 		edata->state = ENVSYS_SVALID;
    315   1.7     jdc 	}
    316   1.7     jdc }
    317   1.7     jdc 
    318