Home | History | Annotate | Line # | Download | only in dev
      1  1.14      jdc /*	$NetBSD: tda.c,v 1.14 2020/10/31 13:17:34 jdc 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.14      jdc __KERNEL_RCSID(0, "$NetBSD: tda.c,v 1.14 2020/10/31 13:17:34 jdc 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.13      mrg #define DEGC_TO_mK(c)		(((c) * 1000000) + 273150000)
     56  1.13      mrg 
     57  1.13      mrg #define CPU_TEMP_MAX		DEGC_TO_mK(67)
     58  1.13      mrg #define CPU_TEMP_MIN		DEGC_TO_mK(57)
     59  1.13      mrg #define SYS_TEMP_MAX		DEGC_TO_mK(30)
     60  1.13      mrg #define SYS_TEMP_MIN		DEGC_TO_mK(20)
     61   1.1   martin 
     62   1.1   martin struct tda_softc {
     63   1.1   martin 	device_t		sc_dev;
     64   1.1   martin 	i2c_tag_t		sc_tag;
     65   1.1   martin 	i2c_addr_t		sc_addr;
     66   1.1   martin 
     67   1.1   martin 	u_int16_t		sc_cfan_speed;	/* current CPU fan speed */
     68   1.1   martin 	u_int16_t		sc_sfan_speed;	/* current SYS fan speed */
     69   1.1   martin 
     70   1.7      jdc 	struct sysmon_envsys	*sc_sme;
     71   1.7      jdc 	envsys_data_t		sc_sensor[2];
     72   1.7      jdc 
     73   1.1   martin 	callout_t		sc_timer;
     74   1.1   martin };
     75   1.1   martin 
     76   1.6      chs int	tda_match(device_t, cfdata_t, void *);
     77   1.6      chs void	tda_attach(device_t, device_t, void *);
     78   1.8      jdc static int	tda_detach(device_t, int);
     79   1.7      jdc void	tda_refresh(struct sysmon_envsys *, envsys_data_t *);
     80   1.1   martin 
     81   1.1   martin void	tda_setspeed(struct tda_softc *);
     82   1.1   martin static void	tda_adjust(void *);
     83   1.1   martin static void	tda_timeout(void *);
     84   1.1   martin 
     85   1.1   martin 
     86   1.8      jdc CFATTACH_DECL3_NEW(tda, sizeof(struct tda_softc),
     87   1.8      jdc 	tda_match, tda_attach, tda_detach, NULL, NULL, NULL,
     88   1.8      jdc 	DVF_DETACH_SHUTDOWN);
     89   1.1   martin 
     90   1.1   martin int
     91   1.6      chs tda_match(device_t parent, cfdata_t match, void *aux)
     92   1.1   martin {
     93   1.1   martin 	struct i2c_attach_args *ia = aux;
     94   1.1   martin 
     95   1.1   martin 	/* Only attach on the Sun Blade 1000/2000. */
     96   1.9      jdc 	if (strcmp(machine_model, "SUNW,Sun-Blade-1000") != 0)
     97   1.1   martin 		return (0);
     98   1.1   martin 
     99   1.1   martin 	/*
    100   1.1   martin 	 * No need for "compatible" matching, we know exactly what
    101   1.1   martin 	 * firmware calls us.
    102   1.1   martin 	 */
    103   1.4      jdc 	if (ia->ia_name == NULL)
    104   1.4      jdc 		return(0);
    105  1.12  thorpej 
    106  1.12  thorpej 	return strcmp(ia->ia_name, "fan-control") == 0 ?
    107  1.12  thorpej 	    I2C_MATCH_DIRECT_SPECIFIC : 0;
    108   1.1   martin }
    109   1.1   martin 
    110   1.1   martin void
    111   1.6      chs tda_attach(device_t parent, device_t self, void *aux)
    112   1.1   martin {
    113   1.1   martin 	struct tda_softc *sc = device_private(self);
    114   1.1   martin 	struct i2c_attach_args *ia = aux;
    115  1.10   martin 	int rc;
    116   1.1   martin 
    117   1.1   martin 	sc->sc_dev = self;
    118   1.1   martin 	sc->sc_tag = ia->ia_tag;
    119   1.1   martin 	sc->sc_addr = ia->ia_addr;
    120   1.1   martin 
    121   1.1   martin 	aprint_normal(": %s\n", ia->ia_name);
    122   1.3      mrg 	aprint_naive(": Environment sensor\n");
    123   1.1   martin 
    124   1.1   martin 	/*
    125   1.1   martin 	 * Set the fans to maximum speed and save the power levels;
    126   1.1   martin 	 * the controller is write-only.
    127   1.1   martin 	 */
    128   1.1   martin 	sc->sc_cfan_speed = sc->sc_sfan_speed = (TDA_FANSPEED_MAX+TDA_FANSPEED_MIN)/2;
    129   1.1   martin 	tda_setspeed(sc);
    130   1.7      jdc 
    131   1.1   martin 	callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
    132   1.1   martin 	callout_reset(&sc->sc_timer, hz*20, tda_timeout, sc);
    133   1.7      jdc 
    134   1.7      jdc 	/* Initialise sensor data */
    135   1.7      jdc 	sc->sc_sensor[SENSOR_FAN_CPU].state = ENVSYS_SINVALID;
    136   1.7      jdc 	sc->sc_sensor[SENSOR_FAN_CPU].units = ENVSYS_INTEGER;
    137   1.7      jdc 	sc->sc_sensor[SENSOR_FAN_CPU].flags = ENVSYS_FMONNOTSUPP;
    138   1.7      jdc 	strlcpy(sc->sc_sensor[SENSOR_FAN_CPU].desc,
    139   1.7      jdc 	    "fan.cpu",sizeof("fan.cpu"));
    140   1.7      jdc 	sc->sc_sensor[SENSOR_FAN_SYS].state = ENVSYS_SINVALID;
    141   1.7      jdc 	sc->sc_sensor[SENSOR_FAN_SYS].units = ENVSYS_INTEGER;
    142   1.7      jdc 	sc->sc_sensor[SENSOR_FAN_SYS].flags = ENVSYS_FMONNOTSUPP;
    143   1.7      jdc 	strlcpy(sc->sc_sensor[SENSOR_FAN_SYS].desc,
    144   1.7      jdc 	    "fan.sys",sizeof("fan.sys"));
    145   1.7      jdc 	sc->sc_sme = sysmon_envsys_create();
    146  1.10   martin 	rc = sysmon_envsys_sensor_attach(
    147  1.10   martin 	    sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_CPU]);
    148  1.10   martin 	if (rc) {
    149   1.7      jdc 		sysmon_envsys_destroy(sc->sc_sme);
    150   1.7      jdc 		aprint_error_dev(self,
    151  1.10   martin 		    "unable to attach cpu fan at sysmon, error %d\n", rc);
    152   1.7      jdc 		return;
    153   1.7      jdc 	}
    154  1.10   martin 	rc = sysmon_envsys_sensor_attach(
    155  1.10   martin 	    sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_SYS]);
    156  1.10   martin 	if (rc) {
    157   1.7      jdc 		sysmon_envsys_destroy(sc->sc_sme);
    158   1.7      jdc 		aprint_error_dev(self,
    159  1.10   martin 		    "unable to attach sys fan at sysmon, error %d\n", rc);
    160   1.7      jdc 		return;
    161   1.7      jdc 	}
    162   1.7      jdc         sc->sc_sme->sme_name = device_xname(self);
    163   1.7      jdc         sc->sc_sme->sme_cookie = sc;
    164   1.7      jdc         sc->sc_sme->sme_refresh = tda_refresh;
    165  1.10   martin 	rc = sysmon_envsys_register(sc->sc_sme);
    166  1.10   martin 	if (rc) {
    167   1.7      jdc 		aprint_error_dev(self,
    168  1.10   martin 		    "unable to register with sysmon, error %d\n", rc);
    169   1.7      jdc 		sysmon_envsys_destroy(sc->sc_sme);
    170   1.7      jdc 		return;
    171   1.7      jdc 	}
    172   1.1   martin }
    173   1.1   martin 
    174   1.8      jdc int
    175   1.8      jdc tda_detach(device_t self, int flags)
    176   1.8      jdc {
    177   1.8      jdc 	struct tda_softc *sc = device_private(self);
    178   1.8      jdc 
    179   1.8      jdc 	if (sc->sc_sme != NULL)
    180  1.14      jdc 		sysmon_envsys_unregister(sc->sc_sme);
    181   1.8      jdc 
    182   1.8      jdc 	callout_halt(&sc->sc_timer, NULL);
    183   1.8      jdc 	callout_destroy(&sc->sc_timer);
    184   1.8      jdc 
    185   1.8      jdc 	sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX;
    186   1.8      jdc 	tda_setspeed(sc);
    187   1.8      jdc 	return 0;
    188   1.8      jdc }
    189   1.8      jdc 
    190   1.1   martin static void
    191   1.1   martin tda_timeout(void *v)
    192   1.1   martin {
    193   1.1   martin 	struct tda_softc *sc = v;
    194   1.1   martin 
    195   1.1   martin 	sysmon_task_queue_sched(0, tda_adjust, sc);
    196   1.1   martin 	callout_reset(&sc->sc_timer, hz*60, tda_timeout, sc);
    197   1.1   martin }
    198   1.1   martin 
    199   1.1   martin void
    200   1.1   martin tda_setspeed(struct tda_softc *sc)
    201   1.1   martin {
    202   1.1   martin 	u_int8_t cmd[2];
    203   1.1   martin 
    204   1.1   martin 	if (sc->sc_cfan_speed < TDA_FANSPEED_MIN)
    205   1.1   martin 		sc->sc_cfan_speed = TDA_FANSPEED_MIN;
    206   1.1   martin 	if (sc->sc_sfan_speed < TDA_FANSPEED_MIN)
    207   1.1   martin 		sc->sc_sfan_speed = TDA_FANSPEED_MIN;
    208   1.1   martin 	if (sc->sc_cfan_speed > TDA_FANSPEED_MAX)
    209   1.1   martin 		sc->sc_cfan_speed = TDA_FANSPEED_MAX;
    210   1.1   martin 	if (sc->sc_sfan_speed > TDA_FANSPEED_MAX)
    211   1.1   martin 		sc->sc_sfan_speed = TDA_FANSPEED_MAX;
    212   1.1   martin 
    213   1.1   martin 	iic_acquire_bus(sc->sc_tag, 0);
    214   1.1   martin 
    215   1.1   martin 	cmd[0] = TDA_CPUFAN_REG;
    216   1.1   martin 	cmd[1] = sc->sc_cfan_speed;
    217   1.1   martin 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
    218   1.1   martin 	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
    219   1.1   martin 		aprint_error_dev(sc->sc_dev, "cannot write cpu-fan register\n");
    220   1.1   martin 		iic_release_bus(sc->sc_tag, 0);
    221   1.1   martin 		return;
    222   1.1   martin         }
    223   1.1   martin 
    224   1.1   martin 	cmd[0] = TDA_SYSFAN_REG;
    225   1.1   martin 	cmd[1] = sc->sc_sfan_speed;
    226   1.1   martin 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
    227   1.1   martin 	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
    228   1.1   martin 		aprint_error_dev(sc->sc_dev, "cannot write system-fan register\n");
    229   1.1   martin 		iic_release_bus(sc->sc_tag, 0);
    230   1.1   martin 		return;
    231   1.1   martin         }
    232   1.1   martin 
    233   1.1   martin 	iic_release_bus(sc->sc_tag, 0);
    234   1.1   martin 
    235   1.1   martin 	aprint_debug_dev(sc->sc_dev, "changed fan speed to cpu=%d system=%d\n",
    236  1.11  msaitoh 		sc->sc_cfan_speed, sc->sc_sfan_speed);
    237   1.1   martin }
    238   1.1   martin 
    239   1.1   martin static bool
    240   1.1   martin is_cpu_sensor(const envsys_data_t *edata)
    241   1.1   martin {
    242   1.1   martin 	if (edata->units != ENVSYS_STEMP)
    243   1.1   martin 		return false;
    244   1.1   martin 	return strcmp(edata->desc, "external") == 0;
    245   1.1   martin }
    246   1.1   martin 
    247   1.1   martin static bool
    248   1.1   martin is_system_sensor(const envsys_data_t *edata)
    249   1.1   martin {
    250   1.1   martin 	if (edata->units != ENVSYS_STEMP)
    251   1.1   martin 		return false;
    252   1.1   martin 	return strcmp(edata->desc, "internal") == 0;
    253   1.1   martin }
    254   1.1   martin 
    255   1.1   martin static void
    256   1.1   martin tda_adjust(void *v)
    257   1.1   martin {
    258   1.1   martin 	struct tda_softc *sc = v;
    259   1.1   martin 	u_int64_t ctemp, stemp;
    260   1.1   martin 	u_int16_t cspeed, sspeed;
    261   1.1   martin 
    262   1.1   martin 	/* Default to running the fans at maximum speed. */
    263   1.1   martin 	sspeed = cspeed = TDA_FANSPEED_MAX;
    264   1.1   martin 
    265   1.1   martin 	/* fetch maximum current temperature */
    266   1.1   martin 	ctemp = sysmon_envsys_get_max_value(is_cpu_sensor, true);
    267   1.1   martin 	stemp = sysmon_envsys_get_max_value(is_system_sensor, true);
    268   1.1   martin 
    269   1.1   martin 	/* the predicates for selecting sensors must have gone wrong */
    270   1.1   martin 	if (ctemp == 0 || stemp == 0) {
    271   1.1   martin 		aprint_error_dev(sc->sc_dev, "skipping temp adjustment"
    272   1.1   martin 			" - no sensor values\n");
    273   1.1   martin 		return;
    274   1.1   martin 	}
    275   1.1   martin 
    276   1.2   martin 	aprint_debug_dev(sc->sc_dev, "current temperature: cpu %" PRIu64
    277   1.2   martin 		" system %" PRIu64 "\n",
    278   1.1   martin 		ctemp, stemp);
    279   1.1   martin 
    280   1.1   martin 	if (ctemp < CPU_TEMP_MIN)
    281   1.1   martin 		cspeed = TDA_FANSPEED_MIN;
    282   1.1   martin 	else if (ctemp < CPU_TEMP_MAX)
    283   1.1   martin 		cspeed = TDA_FANSPEED_MIN +
    284   1.1   martin 			(ctemp - CPU_TEMP_MIN) *
    285   1.1   martin 			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
    286   1.1   martin 			(CPU_TEMP_MAX - CPU_TEMP_MIN);
    287   1.1   martin 
    288   1.1   martin 	if (stemp < SYS_TEMP_MIN)
    289   1.1   martin 		sspeed = TDA_FANSPEED_MIN;
    290   1.1   martin 	else if (stemp < SYS_TEMP_MAX)
    291   1.7      jdc 		sspeed = TDA_FANSPEED_MIN +
    292   1.1   martin 			(stemp - SYS_TEMP_MIN) *
    293   1.1   martin 			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) /
    294   1.1   martin 			(SYS_TEMP_MAX - SYS_TEMP_MIN);
    295   1.1   martin 
    296   1.1   martin 	if (sspeed == sc->sc_sfan_speed && cspeed == sc->sc_cfan_speed)
    297   1.1   martin 		return;
    298   1.1   martin 
    299   1.1   martin 	sc->sc_sfan_speed = sspeed;
    300   1.1   martin 	sc->sc_cfan_speed = cspeed;
    301   1.1   martin 	tda_setspeed(sc);
    302   1.1   martin }
    303   1.7      jdc 
    304   1.7      jdc void
    305   1.7      jdc tda_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    306   1.7      jdc {
    307   1.7      jdc 	struct tda_softc *sc = sme->sme_cookie;
    308   1.7      jdc 	u_int16_t speed;
    309   1.7      jdc 
    310   1.7      jdc 	if (edata->sensor == SENSOR_FAN_CPU)
    311   1.7      jdc 		speed = sc->sc_cfan_speed;
    312   1.7      jdc 	else
    313   1.7      jdc 		speed = sc->sc_sfan_speed;
    314   1.7      jdc 	if (!speed)
    315   1.7      jdc 		edata->state = ENVSYS_SINVALID;
    316   1.7      jdc 	else {
    317   1.7      jdc 		edata->value_cur = speed;
    318   1.7      jdc 		edata->state = ENVSYS_SVALID;
    319   1.7      jdc 	}
    320   1.7      jdc }
    321   1.7      jdc 
    322