/* $NetBSD: emcfan.c,v 1.1 2025/03/11 13:56:46 brad Exp $ */
/*
* Copyright (c) 2025 Brad Spencer <
[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: emcfan.c,v 1.1 2025/03/11 13:56:46 brad Exp $");
/*
* Driver for the EMC-210x and EMC-230x fan controllers on a
* I2C bus.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/sysctl.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/kthread.h>
#include <sys/pool.h>
#include <sys/kmem.h>
#include <dev/sysmon/sysmonvar.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/emcfanreg.h>
#include <dev/i2c/emcfanvar.h>
#include <dev/i2c/emcfaninfo.h>
static int emcfan_poke(i2c_tag_t, i2c_addr_t, bool);
static int emcfan_match(device_t, cfdata_t, void *);
static void emcfan_attach(device_t, device_t, void *);
static int emcfan_detach(device_t, int);
static void emcfan_refresh(struct sysmon_envsys *, envsys_data_t *);
static int emcfan_activate(device_t, enum devact);
static int emcfan_verify_sysctl(SYSCTLFN_ARGS);
static void emcfan_attach_gpio(struct emcfan_sc *, uint8_t);
#define EMCFAN_DEBUG
#ifdef EMCFAN_DEBUG
#define DPRINTF(s, l, x) \
do { \
if (l <= s->sc_emcfandebug) \
printf x; \
} while (/*CONSTCOND*/0)
#else
#define DPRINTF(s, l, x)
#endif
CFATTACH_DECL_NEW(emcfan, sizeof(struct emcfan_sc),
emcfan_match, emcfan_attach, emcfan_detach, emcfan_activate);
extern struct cfdriver emcfan_cd;
static dev_type_open(emcfanopen);
static dev_type_read(emcfanread);
static dev_type_write(emcfanwrite);
static dev_type_close(emcfanclose);
const struct cdevsw emcfan_cdevsw = {
.d_open = emcfanopen,
.d_close = emcfanclose,
.d_read = emcfanread,
.d_write = emcfanwrite,
.d_ioctl = noioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = D_OTHER
};
static bool
emcfan_reg_is_real(struct emcfan_sc *sc, uint8_t reg)
{
int segment;
uint64_t index;
segment = reg / 64;
index = reg % 64;
DPRINTF(sc, 10, ("%s: void check 1: reg=%02x, segment=%d, index=%jd, sc_info_info=%d\n", __func__, reg,
segment, index, sc->sc_info_index));
DPRINTF(sc, 10, ("%s: void check 2: register_void=%jx, shift=%jx\n", __func__,
emcfan_chip_infos[sc->sc_info_index].register_void[segment], ((uint64_t)1 << index)));
return(emcfan_chip_infos[sc->sc_info_index].register_void[segment] & ((uint64_t)1 << index));
}
static int
emcfan_read_registerr(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg,
uint8_t *res)
{
int error = 0;
error = iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, ®, 1, res, 1, 0);
return error;
}
static int
emcfan_read_register(struct emcfan_sc *sc, uint8_t reg, uint8_t *res)
{
if (emcfan_reg_is_real(sc,reg))
return(emcfan_read_registerr(sc->sc_tag, sc->sc_addr, reg, res));
else
*res = EMCFAN_VOID_READ;
return 0;
}
static int
emcfan_write_registerr(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg,
uint8_t value)
{
int error = 0;
error = iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, &value, 1, 0);
return error;
}
static int
emcfan_write_register(struct emcfan_sc *sc, uint8_t reg, uint8_t value)
{
if (emcfan_reg_is_real(sc,reg))
return(emcfan_write_registerr(sc->sc_tag, sc->sc_addr, reg, value));
else
return EACCES;
}
static int
emcfan_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
{
int error;
uint8_t res;
error = emcfan_read_registerr(tag, addr, EMCFAN_MANUFACTURER_ID, &res);
if (matchdebug) {
printf("poke X 1: %d %d\n", addr, error);
}
/* Ok.. something was there, but the ID did not match what was expected.
* We get away with doing this because the poke is just getting the Manufacturer
* ID, which is a fixed value.
*/
if (!error) {
if (res != EMCFAN_VALID_MANUFACTURER_ID)
error = EIO;
}
return error;
}
static bool
emcfan_check_i2c_addr(i2c_addr_t addr)
{
bool r = false;
for(int i = 0;i < __arraycount(emcfan_typical_addrs); i++)
if (addr == emcfan_typical_addrs[i]) {
r = true;
break;
}
return(r);
}
static int
emcfan_match(device_t parent, cfdata_t match, void *aux)
{
struct i2c_attach_args *ia = aux;
int error, match_result;
const bool matchdebug = false;
if (iic_use_direct_match(ia, match, NULL, &match_result))
return match_result;
if (matchdebug) {
printf("Looking at ia_addr: %x\n",ia->ia_addr);
}
/* Look to see if there is a device indirectly */
if (! emcfan_check_i2c_addr(ia->ia_addr))
return 0;
/*
* Check to see if something is really at this i2c address.
* This will keep phantom devices from appearing
*/
if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
if (matchdebug)
printf("in match acquire bus failed\n");
return 0;
}
error = emcfan_poke(ia->ia_tag, ia->ia_addr, matchdebug);
iic_release_bus(ia->ia_tag, 0);
return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
}
static int
emcfan_find_info(uint8_t product)
{
for(int i = 0;i < __arraycount(emcfan_chip_infos); i++)
if (product == emcfan_chip_infos[i].product_id)
return(i);
return(-1);
}
static const char *
emcfan_product_to_name(uint8_t info_index)
{
KASSERT(info_index >= 0);
return(emcfan_chip_infos[info_index].name);
}
int
emcfan_verify_sysctl(SYSCTLFN_ARGS)
{
int error, t;
struct sysctlnode node;
node = *rnode;
t = *(int *)rnode->sysctl_data;
node.sysctl_data = &t;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return error;
if (t < 0)
return EINVAL;
*(int *)rnode->sysctl_data = t;
return 0;
}
static int
emcfan_sysctl_init(struct emcfan_sc *sc)
{
int error;
const struct sysctlnode *cnode;
int sysctlroot_num;
char pole_name[8];
if ((error = sysctl_createv(&sc->sc_emcfanlog, 0, NULL, &cnode,
0, CTLTYPE_NODE, device_xname(sc->sc_dev),
SYSCTL_DESCR("emcfan controls"), NULL, 0, NULL, 0, CTL_HW,
CTL_CREATE, CTL_EOL)) != 0)
return error;
sysctlroot_num = cnode->sysctl_num;
#ifdef EMCFAN_DEBUG
if ((error = sysctl_createv(&sc->sc_emcfanlog, 0, NULL, &cnode,
CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
SYSCTL_DESCR("Debug level"), emcfan_verify_sysctl, 0,
&sc->sc_emcfandebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
CTL_EOL)) != 0)
return error;
#endif
if (emcfan_chip_infos[sc->sc_info_index].family == EMCFAN_FAMILY_230X ||
emcfan_chip_infos[sc->sc_info_index].product_id == EMCFAN_PRODUCT_2103_1 ||
emcfan_chip_infos[sc->sc_info_index].product_id == EMCFAN_PRODUCT_2103_24 ||
emcfan_chip_infos[sc->sc_info_index].product_id == EMCFAN_PRODUCT_2104 ||
emcfan_chip_infos[sc->sc_info_index].product_id == EMCFAN_PRODUCT_2106) {
for(int i=0;i < emcfan_chip_infos[sc->sc_info_index].num_tachs;i++) {
snprintf(pole_name,sizeof(pole_name),"poles%d",i+1);
if ((error = sysctl_createv(&sc->sc_emcfanlog, 0, NULL, &cnode,
CTLFLAG_READWRITE, CTLTYPE_INT, pole_name,
SYSCTL_DESCR("Number of poles"), emcfan_verify_sysctl, 0,
&sc->sc_num_poles[i], 0, CTL_HW, sysctlroot_num, CTL_CREATE,
CTL_EOL)) != 0)
return error;
}
}
if (emcfan_chip_infos[sc->sc_info_index].product_id == EMCFAN_PRODUCT_2103_1 ||
emcfan_chip_infos[sc->sc_info_index].product_id == EMCFAN_PRODUCT_2103_24) {
if ((error = sysctl_createv(&sc->sc_emcfanlog, 0, NULL, &cnode,
CTLFLAG_READWRITE, CTLTYPE_INT, "ftach",
SYSCTL_DESCR("ftach frequency"), emcfan_verify_sysctl, 0,
&sc->sc_ftach, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
CTL_EOL)) != 0)
return error;
}
if (emcfan_chip_infos[sc->sc_info_index].vin4_temp_zone) {
if ((error = sysctl_createv(&sc->sc_emcfanlog, 0, NULL, &cnode,
CTLFLAG_READWRITE, CTLTYPE_BOOL, "vin4",
SYSCTL_DESCR("Use VIN4 pin as a temperature sensor input"), NULL, 0,
&sc->sc_vin4_temp, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
CTL_EOL)) != 0)
return error;
}
return 0;
}
static void
emcfan_attach(device_t parent, device_t self, void *aux)
{
struct emcfan_sc *sc;
struct i2c_attach_args *ia;
uint8_t product_id, revision;
int error;
ia = aux;
sc = device_private(self);
sc->sc_dev = self;
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
sc->sc_opened = false;
sc->sc_dying = false;
sc->sc_ftach = 32000;
sc->sc_vin4_temp = false;
for(int i=0;i < EMCFAN_NUM_FANS;i++)
sc->sc_num_poles[i] = 2;
sc->sc_emcfandebug = 0;
mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
aprint_normal("\n");
if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
aprint_error_dev(self,
"Unable to create sysmon structure\n");
sc->sc_sme = NULL;
return;
}
error = iic_acquire_bus(sc->sc_tag, 0);
if (error) {
aprint_error_dev(self, "Could not acquire iic bus: %d\n",
error);
goto out;
}
error = emcfan_read_registerr(sc->sc_tag, sc->sc_addr, EMCFAN_PRODUCT_ID, &product_id);
if (error != 0) {
aprint_error_dev(self, "Could not get the product id\n");
} else {
error = emcfan_read_registerr(sc->sc_tag, sc->sc_addr, EMCFAN_REVISION, &revision);
if (error != 0) {
aprint_error_dev(self, "Could not get the revision of the chip\n");
}
}
iic_release_bus(sc->sc_tag, 0);
if (error != 0) {
aprint_error_dev(self, "Unable to setup device\n");
goto out;
}
sc->sc_info_index = emcfan_find_info(product_id);
if (sc->sc_info_index < 0) {
aprint_error_dev(self, "Unknown product id: %02x\n",product_id);
goto out;
}
if ((error = emcfan_sysctl_init(sc)) != 0) {
sc->sc_emcfanlog = NULL;
aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
goto out;
}
for(int i=0;i < EMCFAN_NUM_SENSORS;i++)
sc->sc_sensor_instances[i].sc_i_member = -1;
int sensor_instance = 0;
/* Set up the tachs */
for(int i = 0;i < emcfan_chip_infos[sc->sc_info_index].num_tachs &&
sensor_instance < EMCFAN_NUM_SENSORS;
i++) {
snprintf(sc->sc_sensors[sensor_instance].desc,
sizeof(sc->sc_sensors[sensor_instance].desc),
"FAN %d",i+1);
DPRINTF(sc, 2, ("%s: TACH registering fan sensor %d (%s)\n", __func__,
sensor_instance, sc->sc_sensors[sensor_instance].desc));
sc->sc_sensor_instances[sensor_instance].sc_i_flags = 0;
sc->sc_sensor_instances[sensor_instance].sc_i_member = i + 1;
sc->sc_sensors[sensor_instance].units = ENVSYS_SFANRPM;
sc->sc_sensors[sensor_instance].state = ENVSYS_SINVALID;
error = sysmon_envsys_sensor_attach(sc->sc_sme,
&sc->sc_sensors[sensor_instance]);
if (error) {
aprint_error_dev(self,
"Unable to attach sensor %d: %d\n", i, error);
goto out;
}
sc->sc_sensor_instances[sensor_instance].sc_i_envnum =
sc->sc_sensors[sensor_instance].sensor;
DPRINTF(sc, 2, ("%s: TACH recorded sensor instance number %d->%d\n", __func__,
sensor_instance, sc->sc_sensor_instances[sensor_instance].sc_i_envnum));
sensor_instance++;
}
/* Set up internal temperature sensor */
if (emcfan_chip_infos[sc->sc_info_index].internal_temp_zone) {
snprintf(sc->sc_sensors[sensor_instance].desc,
sizeof(sc->sc_sensors[sensor_instance].desc),
"internal temperature");
DPRINTF(sc, 2, ("%s: IT registering internal temperature sensor %d (%s)\n", __func__,
sensor_instance, sc->sc_sensors[sensor_instance].desc));
sc->sc_sensor_instances[sensor_instance].sc_i_flags = EMCFAN_INTERNAL_TEMP;
sc->sc_sensor_instances[sensor_instance].sc_i_member = 1;
sc->sc_sensors[sensor_instance].units = ENVSYS_STEMP;
sc->sc_sensors[sensor_instance].state = ENVSYS_SINVALID;
error = sysmon_envsys_sensor_attach(sc->sc_sme,
&sc->sc_sensors[sensor_instance]);
if (error) {
aprint_error_dev(self,
"Unable to attach internal sensor: %d\n", error);
goto out;
}
sc->sc_sensor_instances[sensor_instance].sc_i_envnum =
sc->sc_sensors[sensor_instance].sensor;
DPRINTF(sc, 2, ("%s: IT recorded sensor instance number %d->%d\n", __func__,
sensor_instance, sc->sc_sensor_instances[sensor_instance].sc_i_envnum));
sensor_instance++;
}
/* Set up VIN4 temperature sensor */
if (emcfan_chip_infos[sc->sc_info_index].vin4_temp_zone) {
snprintf(sc->sc_sensors[sensor_instance].desc,
sizeof(sc->sc_sensors[sensor_instance].desc),
"VIN4 temperature");
DPRINTF(sc, 2, ("%s: registering VIN4 temperature sensor %d (%s)\n", __func__,
sensor_instance, sc->sc_sensors[sensor_instance].desc));
sc->sc_sensor_instances[sensor_instance].sc_i_flags = EMCFAN_VIN4_TEMP;
sc->sc_sensor_instances[sensor_instance].sc_i_member = 1;
sc->sc_sensors[sensor_instance].units = ENVSYS_STEMP;
sc->sc_sensors[sensor_instance].state = ENVSYS_SINVALID;
error = sysmon_envsys_sensor_attach(sc->sc_sme,
&sc->sc_sensors[sensor_instance]);
if (error) {
aprint_error_dev(self,
"Unable to attach VIN4 sensor: %d\n", error);
goto out;
}
sc->sc_sensor_instances[sensor_instance].sc_i_envnum =
sc->sc_sensors[sensor_instance].sensor;
DPRINTF(sc, 2, ("%s: VIN4 recorded sensor instance number %d->%d\n", __func__,
sensor_instance, sc->sc_sensor_instances[sensor_instance].sc_i_envnum));
sensor_instance++;
}
/* Set up external temperature sensors */
for(int i = 0;i < emcfan_chip_infos[sc->sc_info_index].num_external_temp_zones &&
sensor_instance < EMCFAN_NUM_SENSORS;
i++) {
snprintf(sc->sc_sensors[sensor_instance].desc,
sizeof(sc->sc_sensors[sensor_instance].desc),
"temperature zone %d",i+1);
DPRINTF(sc, 2, ("%s: ET registering fan sensor %d (%s)\n", __func__,
sensor_instance, sc->sc_sensors[sensor_instance].desc));
sc->sc_sensor_instances[sensor_instance].sc_i_flags = 0;
sc->sc_sensor_instances[sensor_instance].sc_i_member = i + 1;
sc->sc_sensors[sensor_instance].units = ENVSYS_STEMP;
sc->sc_sensors[sensor_instance].state = ENVSYS_SINVALID;
error = sysmon_envsys_sensor_attach(sc->sc_sme,
&sc->sc_sensors[sensor_instance]);
if (error) {
aprint_error_dev(self,
"Unable to attach sensor %d: %d\n", i, error);
goto out;
}
sc->sc_sensor_instances[sensor_instance].sc_i_envnum =
sc->sc_sensors[sensor_instance].sensor;
DPRINTF(sc, 2, ("%s: ET recorded sensor instance number %d->%d\n", __func__,
sensor_instance, sc->sc_sensor_instances[sensor_instance].sc_i_envnum));
sensor_instance++;
}
sc->sc_sme->sme_name = device_xname(sc->sc_dev);
sc->sc_sme->sme_cookie = sc;
sc->sc_sme->sme_refresh = emcfan_refresh;
if (sysmon_envsys_register(sc->sc_sme)) {
aprint_error_dev(self, "unable to register with sysmon\n");
sysmon_envsys_destroy(sc->sc_sme);
sc->sc_sme = NULL;
return;
}
aprint_normal_dev(self, "Microchip Technology %s fan controller, "
"Revision: %02x\n",
emcfan_product_to_name(sc->sc_info_index),
revision);
int e = emcfan_chip_infos[sc->sc_info_index].num_external_temp_zones;
if (emcfan_chip_infos[sc->sc_info_index].vin4_temp_zone)
e++;
aprint_normal_dev(self, "Fans: %d, Tachometers: %d, Internal Temperature: %s, External Sensors: %d, GPIO: %s\n",
emcfan_chip_infos[sc->sc_info_index].num_fans,
emcfan_chip_infos[sc->sc_info_index].num_tachs,
(emcfan_chip_infos[sc->sc_info_index].internal_temp_zone) ? "Yes" : "No",
e,
(emcfan_chip_infos[sc->sc_info_index].num_gpio_pins > 0) ? "Yes" : "No");
if (emcfan_chip_infos[sc->sc_info_index].num_gpio_pins > 0)
emcfan_attach_gpio(sc, product_id);
return;
out:
sysmon_envsys_destroy(sc->sc_sme);
sc->sc_sme = NULL;
}
/* The EMC-2101 is quite a bit different than the other EMC fan controllers.
* Handle it differently.
*/
static void
emcfan_refresh_2101_tach(struct sysmon_envsys *sme, envsys_data_t *edata, int instance)
{
struct emcfan_sc *sc = sme->sme_cookie;
int error;
uint8_t tach_high_reg;
uint8_t tach_low_reg;
uint8_t tach_high;
uint8_t tach_low;
switch(sc->sc_sensor_instances[instance].sc_i_member) {
case 1:
tach_high_reg = EMCFAN_2101_TACH_HIGH;
tach_low_reg = EMCFAN_2101_TACH_LOW;
break;
default:
panic("A 2101 can not have more than one tach\n");
break;
};
DPRINTF(sc, 2, ("%s: dev=%s, instance=%d, sc_i_member=%d, tach_high_reg=0x%02X, tach_low_reg=0x%02X\n", __func__,
device_xname(sc->sc_dev), instance,
sc->sc_sensor_instances[instance].sc_i_member,
tach_high_reg, tach_low_reg));
error = iic_acquire_bus(sc->sc_tag, 0);
if (error) {
device_printf(sc->sc_dev,"%s: could not acquire I2C bus: %d\n",__func__, error);
return;
}
/* There is a interlock thing with the low and high bytes. Read the
* low byte first.
*/
error = emcfan_read_register(sc, tach_low_reg, &tach_low);
if (error) {
device_printf(sc->sc_dev,"%s: could not read tach low register: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
error = emcfan_read_register(sc, tach_high_reg, &tach_high);
if (error) {
device_printf(sc->sc_dev,"%s: could not read tach high register: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
iic_release_bus(sc->sc_tag, 0);
uint16_t count;
count = tach_high << 8;
count |= tach_low;
DPRINTF(sc, 2, ("%s: instance=%d, tach_high=%d 0x%02X, tach_low=%d 0x%02X, count=%d\n", __func__,
instance, tach_high, tach_high, tach_low, tach_low, count));
/* 0xffff indicates that the fan is not present, stopped / stalled
* or below the RPM that can be measured or the chip is not configured
* to read tach signals on the pin, but is being used for an alert
*/
if (count == 0xffff)
return;
/* The formula is:
*
* rpm = 5400000 / count
*
*/
uint64_t irpm;
irpm = 5400000 / count;
edata->value_cur = (uint32_t) irpm;
edata->state = ENVSYS_SVALID;
}
static void
emcfan_refresh_210_346_230x_tach(int product_family, uint8_t product_id,
struct sysmon_envsys *sme, envsys_data_t *edata, int instance)
{
struct emcfan_sc *sc = sme->sme_cookie;
int error;
uint8_t tach_high_reg;
uint8_t tach_low_reg;
uint8_t fan_config_reg;
uint8_t chip_config;
uint8_t fan_config;
uint8_t tach_high;
uint8_t tach_low;
int ftach = 32000;
int edges;
int poles;
int m;
if (product_family == EMCFAN_FAMILY_210X) {
switch(sc->sc_sensor_instances[instance].sc_i_member) {
case 1:
fan_config_reg = EMCFAN_210_346_CONFIG_1;
tach_high_reg = EMCFAN_210_346_TACH_1_HIGH;
tach_low_reg = EMCFAN_210_346_TACH_1_LOW;
break;
case 2:
fan_config_reg = EMCFAN_210_346_CONFIG_2;
tach_high_reg = EMCFAN_210_346_TACH_2_HIGH;
tach_low_reg = EMCFAN_210_346_TACH_2_LOW;
break;
default:
panic("210X family do not know how to deal with member: %d\n",
sc->sc_sensor_instances[instance].sc_i_member);
break;
};
} else {
switch(sc->sc_sensor_instances[instance].sc_i_member) {
case 1:
fan_config_reg = EMCFAN_230X_CONFIG_1;
tach_high_reg = EMCFAN_230X_TACH_1_HIGH;
tach_low_reg = EMCFAN_230X_TACH_1_LOW;
break;
case 2:
fan_config_reg = EMCFAN_230X_CONFIG_2;
tach_high_reg = EMCFAN_230X_TACH_2_HIGH;
tach_low_reg = EMCFAN_230X_TACH_2_LOW;
break;
case 3:
fan_config_reg = EMCFAN_230X_CONFIG_3;
tach_high_reg = EMCFAN_230X_TACH_3_HIGH;
tach_low_reg = EMCFAN_230X_TACH_3_LOW;
break;
case 4:
fan_config_reg = EMCFAN_230X_CONFIG_4;
tach_high_reg = EMCFAN_230X_TACH_4_HIGH;
tach_low_reg = EMCFAN_230X_TACH_4_LOW;
break;
case 5:
fan_config_reg = EMCFAN_230X_CONFIG_5;
tach_high_reg = EMCFAN_230X_TACH_5_HIGH;
tach_low_reg = EMCFAN_230X_TACH_5_LOW;
break;
default:
panic("230X family do not know how to deal with member: %d\n",
sc->sc_sensor_instances[instance].sc_i_member);
break;
};
}
DPRINTF(sc, 2, ("%s: dev=%s, instance=%d, sc_i_member=%d, fan_config_reg=0x%02X, tach_high_reg=0x%02X, tach_low_reg=0x%02X\n", __func__,
device_xname(sc->sc_dev), instance,
sc->sc_sensor_instances[instance].sc_i_member,
fan_config_reg, tach_high_reg, tach_low_reg));
error = iic_acquire_bus(sc->sc_tag, 0);
if (error) {
device_printf(sc->sc_dev,"%s: could not acquire I2C bus: %d\n",__func__, error);
return;
}
if (product_id == EMCFAN_PRODUCT_2103_1 ||
product_id == EMCFAN_PRODUCT_2103_24) {
ftach = sc->sc_ftach;
} else {
chip_config = 0x00;
if (product_family == EMCFAN_FAMILY_230X) {
error = emcfan_read_register(sc, EMCFAN_CHIP_CONFIG, &chip_config);
} else {
if (product_id == EMCFAN_PRODUCT_2104 ||
product_id == EMCFAN_PRODUCT_2106) {
error = emcfan_read_register(sc, EMCFAN_MUX_PINS, &chip_config);
}
}
if (error) {
device_printf(sc->sc_dev,"%s: could not read chip config: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
/* Figure out if there is an external clock involved */
if (product_family == EMCFAN_FAMILY_230X) {
if (chip_config & 0x02)
ftach = 32000;
else
if (chip_config & 0x01)
ftach = 32768;
else
ftach = 32000;
} else {
if (product_id == EMCFAN_PRODUCT_2104 ||
product_id == EMCFAN_PRODUCT_2106) {
if (chip_config & 0x01)
ftach = 32768;
else
ftach = 32000;
}
}
}
error = emcfan_read_register(sc, fan_config_reg, &fan_config);
if (error) {
device_printf(sc->sc_dev,"%s: could not read fan config: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
/* There is a interlock thing with the low and high bytes. Read the
* low byte first.
*/
error = emcfan_read_register(sc, tach_low_reg, &tach_low);
if (error) {
device_printf(sc->sc_dev,"%s: could not read tach low register: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
error = emcfan_read_register(sc, tach_high_reg, &tach_high);
if (error) {
device_printf(sc->sc_dev,"%s: could not read tach high register: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
iic_release_bus(sc->sc_tag, 0);
/* Return early if the fan is stalled or not hooked up. It might be better to look at
* the stalled fan status register, but that works differently depending on which chip
* you are looking at.
*/
if (product_family == EMCFAN_FAMILY_210X) {
/* The datasheet is not at all clear as to what will be set in the low byte of the tach
* 0xc0, 0xe0 and 0xf0 all seem to depend on the minimum expected rpm and 0xf8 appears
* to mean that the fan is stalled in some way.
*
* Further to confuse matters, some chips may be able to adjust what invalid means.
* See the fan config register (0x4A) on the EMC2101 for an example of that. We check
* tach_low here just in case these chips can do that too.
*/
if (tach_high == 0xff &&
(tach_low == 0xc0 || tach_low == 0xe0 ||
tach_low == 0xf0 || tach_low == 0xf8 ||
tach_low == 0xff))
return;
} else {
/* The datasheet for the 230X family was a little clearer. In that one, if the high byte is
* 0xff the tach reading is invalid.
*/
if (tach_high == 0xff)
return;
}
/* Extract the M value, also known as the tach multiplier */
m = fan_config & 0b01100000;
m = m >> 5;
DPRINTF(sc, 2, ("%s: fan_config=%d 0x%02X, raw m=%d 0x%02X\n",
__func__, fan_config, fan_config, m, m));
m = (1 << m);
/* Extract the number of configured edges */
edges = fan_config & 0b00011000;
edges = edges >> 3;
DPRINTF(sc, 2, ("%s: fan_config=%d 0x%02X, raw edges=%d 0x%02X\n",
__func__, fan_config, fan_config, edges, edges));
edges = ((edges + 1) * 2) + 1;
/* Calculate the tach count, which needs to use bit weights */
int count = 0;
count = (tach_high << 5) | tach_low;
/* The number of poles is a sysctl setting */
poles = sc->sc_num_poles[sc->sc_sensor_instances[instance].sc_i_member - 1];
DPRINTF(sc, 2, ("%s: instance=%d, ftach=%d, m=%d, edges=%d, poles=%d, tach_high=%d 0x%02X, tach_low=%d 0x%02X, count=%d\n", __func__,
instance, ftach, m, edges, poles, tach_high, tach_high, tach_low, tach_low, count));
/* The formula is:
*
* rpm = 1/poles * ((edges - 1) / count * 1/m) * ftach * 60
*
* ftach is either 32.000khz or 32.768khz
*
*/
int64_t irpm;
int ip1, ip2;
int64_t ip3;
ip1 = 10000 / poles;
/*
printf("poles: %d ; ip1: %d\n",poles,ip1);
*/
ip2 = 10000 / m;
/*
printf("m: %d ; ip2: %d\n",m,ip2);
*/
ip2 = count * ip2;
/*
printf("count: %d ; ip2: %d\n",count,ip2);
*/
ip3 = (int64_t)((edges - 1) * (int64_t)100000000000) / (int64_t)ip2;
/*
printf("edges: %d ; ip3: %d\n",edges,ip3);
*/
irpm = (ip1 * ip3 * ftach * 60) / 100000000000;
edata->value_cur = (uint32_t) irpm;
edata->state = ENVSYS_SVALID;
}
/* These two tables are taken from Appendix A in the 2104 and 2106 datasheet.
* The index into the array is the ADC value and the value of the array is a
* precomputed kelvin1000 (i.e celcius to kelvin * 1000) temperature.
*
* There are unusual holes as not all of the ADC values are present in the
* *center* of the table these were made into xx.5 temperature values.
*
* Another quirk is that the table in the datasheets have multiple temperatures
* for a particular ADC. This behavior seems more common on the edges of the
* table and may make sense. What these tables do, is just take the first
* temperature for any ADC value.
*
*/
#define EMCFAN_VIN_NO_TEMP -1
static const int32_t emcfan_vin_temps[] = {
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
463150,
461150,
459150,
457150,
455150,
453150,
451150,
450150,
448150,
446150,
445150,
443150,
441150,
440150,
438150,
437150,
435150,
434150,
433150,
431150,
430150,
429150,
427150,
426150,
425150,
424150,
423150,
421150,
420150,
419150,
418150,
417150,
416150,
415150,
414150,
413150,
412150,
411150,
410150,
409150,
408150,
407150,
406150,
405150,
404150,
403150,
402150,
398150,
397150,
396150,
395150,
394650,
394150,
393150,
392150,
391650,
391150,
390150,
389150,
388650,
388150,
387150,
386650,
386150,
385150,
384150,
383650,
383150,
382150,
381650,
381150,
380150,
379650,
379150,
378150,
377650,
377150,
376650,
376150,
375150,
374650,
374150,
373150,
372650,
372150,
371650,
371150,
370150,
369650,
369150,
368650,
368150,
367150,
366650,
366150,
365650,
365150,
364150,
363650,
363150,
362650,
362150,
361650,
361150,
360150,
359650,
359150,
358150,
357650,
357150,
356650,
356150,
355650,
355150,
354150,
353650,
353150,
352650,
352150,
351650,
351150,
350650,
350150,
349150,
348650,
348150,
347650,
347150,
346150,
345650,
345150,
344650,
344150,
343650,
343150,
342150,
341650,
341150,
340650,
340150,
339150,
338650,
338150,
337650,
337150,
336150,
335650,
335150,
334650,
334150,
333150,
332650,
332150,
331150,
330650,
330150,
329650,
329150,
328150,
327650,
327150,
326150,
325650,
325150,
324150,
323650,
323150,
322150,
321150,
320650,
320150,
319150,
318650,
318150,
317150,
316150,
315150,
314650,
314150,
313150,
312150,
311150,
310650,
310150,
309150,
308150,
307150,
306150,
305150,
304150,
303150,
302150,
301150,
300150,
299150,
298150,
297150,
296150,
295150,
293150,
292150,
291150,
290150,
288150,
287150,
285150,
283150,
282150,
280150,
278150,
276150,
273150,
271150,
268150,
265150,
262150,
259150,
255150,
250150,
244150,
236150,
229150,
228150,
EMCFAN_VIN_NO_TEMP
};
static const int32_t emcfan_vin_temps_i[] = {
228150,
229150,
236150,
244150,
250150,
255150,
259150,
262150,
265150,
268150,
271150,
273150,
276150,
278150,
280150,
281150,
283150,
285150,
286150,
288150,
289150,
291150,
292150,
293150,
295150,
296150,
297150,
298150,
299150,
300150,
301150,
302150,
303150,
304150,
305150,
306150,
307150,
308150,
309150,
310150,
310650,
311150,
312150,
313150,
314150,
314650,
315150,
316150,
317150,
317650,
318150,
319150,
320150,
320650,
321150,
322150,
323150,
323650,
324150,
325150,
325650,
326150,
327150,
327650,
328150,
329150,
329650,
330150,
330650,
331150,
332150,
332650,
333150,
334150,
334650,
335150,
335650,
336150,
337150,
337650,
338150,
338650,
339150,
340150,
340650,
341150,
341650,
342150,
343150,
343650,
344150,
344650,
345150,
345650,
346150,
347150,
347650,
348150,
348650,
349150,
350150,
350650,
351150,
351650,
352150,
352650,
353150,
353650,
354150,
355150,
355650,
356150,
356650,
357150,
357650,
358150,
359150,
359650,
360150,
360650,
361150,
362150,
362650,
363150,
363650,
364150,
365150,
365650,
366150,
366650,
367150,
368150,
368650,
369150,
369650,
370150,
371150,
371650,
372150,
372650,
373150,
374150,
374650,
375150,
376150,
376650,
377150,
377650,
378150,
379150,
379650,
380150,
381150,
381650,
382150,
383150,
383650,
384150,
385150,
386150,
386650,
387150,
388150,
388650,
389150,
390150,
391150,
391650,
392150,
393150,
394150,
394650,
395150,
396150,
397150,
398150,
402150,
403150,
404150,
405150,
406150,
407150,
408150,
409150,
410150,
411150,
412150,
413150,
414150,
415150,
416150,
417150,
418150,
419150,
420150,
421150,
423150,
424150,
425150,
426150,
427150,
429150,
430150,
431150,
433150,
434150,
435150,
437150,
438150,
440150,
441150,
443150,
445150,
446150,
448150,
450150,
451150,
453150,
455150,
457150,
459150,
461150,
463150,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP,
EMCFAN_VIN_NO_TEMP
};
static void
emcfan_refresh_temp(int product_family, uint8_t product_id,
struct sysmon_envsys *sme, envsys_data_t *edata, int instance)
{
struct emcfan_sc *sc = sme->sme_cookie;
int error;
uint8_t temp_config;
uint8_t raw_temp_config_3;
uint8_t temp_config_3;
uint8_t temp_high;
uint8_t temp_low;
uint8_t external_temp_high_reg;
uint8_t external_temp_low_reg;
bool is_internal = false;
bool is_vin4 = false;
bool using_apd = false;
bool using_vin = false;
bool inverted = false;
is_internal = sc->sc_sensor_instances[instance].sc_i_flags & EMCFAN_INTERNAL_TEMP;
is_vin4 = sc->sc_sensor_instances[instance].sc_i_flags & EMCFAN_VIN4_TEMP;
error = iic_acquire_bus(sc->sc_tag, 0);
if (error) {
device_printf(sc->sc_dev,"%s: could not acquire I2C bus: %d\n",__func__, error);
return;
}
if (is_internal) {
/* There might be a data interlock thing going on with the high and low
* registers, to make sure, read the high one first. This works in the
* opposite of the tach.
*/
error = emcfan_read_register(sc, EMCFAN_INTERNAL_TEMP_HIGH, &temp_high);
if (error) {
device_printf(sc->sc_dev,"%s: could not read internal temp high: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
/* The 2101 does not have fractions on the internal temperature sensor */
if (product_id == EMCFAN_PRODUCT_2101) {
temp_low = 0;
} else {
error = emcfan_read_register(sc, EMCFAN_INTERNAL_TEMP_LOW, &temp_low);
if (error) {
device_printf(sc->sc_dev,"%s: could not read internal temp low: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
}
} else {
if (is_vin4) {
if (sc->sc_vin4_temp) {
using_vin = true;
error = emcfan_read_register(sc, EMCFAN_CHIP_CONFIG, &temp_config);
if (error) {
device_printf(sc->sc_dev,"%s: could not read chip config register: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
inverted = temp_config & 0x80;
error = emcfan_read_register(sc, EMCFAN_VIN4_VOLTAGE, &temp_high);
if (error) {
device_printf(sc->sc_dev,"%s: could not read external temp high: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
} else {
iic_release_bus(sc->sc_tag, 0);
return;
}
} else {
/* The 2101 has its external sensor on a different set of registers
* than the rest.
*/
if (product_id == EMCFAN_PRODUCT_2101) {
error = emcfan_read_register(sc, EMCFAN_2101_EXTERNAL_TEMP_LOW, &temp_low);
if (error) {
device_printf(sc->sc_dev,"%s: could not read external temp low: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
error = emcfan_read_register(sc, EMCFAN_2101_EXTERNAL_TEMP_HIGH, &temp_high);
if (error) {
device_printf(sc->sc_dev,"%s: could not read external temp high: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
} else {
switch(sc->sc_sensor_instances[instance].sc_i_member) {
case 1:
external_temp_high_reg = EMCFAN_EXTERNAL_1_TEMP_HIGH;
external_temp_low_reg = EMCFAN_EXTERNAL_1_TEMP_LOW;
break;
case 2:
external_temp_high_reg = EMCFAN_EXTERNAL_2_TEMP_HIGH;
external_temp_low_reg = EMCFAN_EXTERNAL_2_TEMP_LOW;
break;
case 3:
external_temp_high_reg = EMCFAN_EXTERNAL_3_TEMP_HIGH;
external_temp_low_reg = EMCFAN_EXTERNAL_3_TEMP_LOW;
break;
case 4:
external_temp_high_reg = EMCFAN_EXTERNAL_4_TEMP_HIGH;
external_temp_low_reg = EMCFAN_EXTERNAL_4_TEMP_LOW;
break;
default:
panic("Unknown member: %d\n",
sc->sc_sensor_instances[instance].sc_i_member);
break;
};
/* The 2103-2, 2103-4, 2104 and 2106 can use APD mode. This is a method
* of using two sensors in parallel on a single set of pins. The way one
* wires this up is in the datasheets for the chip.
*/
if (product_id == EMCFAN_PRODUCT_2103_24 ||
product_id == EMCFAN_PRODUCT_2104 ||
product_id == EMCFAN_PRODUCT_2106) {
error = emcfan_read_register(sc, EMCFAN_CHIP_CONFIG, &temp_config);
if (error) {
device_printf(sc->sc_dev,"%s: could not read chip config register: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
using_apd = temp_config & 0x01;
}
/* The 2104, 2105 and 2106 has some other special abilities, such as being
* able to use thermistors.
*/
if (product_id == EMCFAN_PRODUCT_2104 ||
product_id == EMCFAN_PRODUCT_2106) {
error = emcfan_read_register(sc, EMCFAN_TEMP_CONFIG_3, &raw_temp_config_3);
if (error) {
device_printf(sc->sc_dev,"%s: could not read temperature config register: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
switch(sc->sc_sensor_instances[instance].sc_i_member) {
case 1:
temp_config_3 = raw_temp_config_3 & 0x03;
break;
case 2:
temp_config_3 = raw_temp_config_3 >> 2;
temp_config_3 = temp_config_3 & 0x03;
break;
case 3:
temp_config_3 = raw_temp_config_3 >> 4;
temp_config_3 = temp_config_3 & 0x03;
break;
default:
temp_config_3 = 0;
break;
};
using_vin = temp_config_3 & 0x02;
inverted = temp_config_3 & 0x01;
/* there is a strange situation if sensor 3 is being used as a VIN
* sensor, then sensor 4 is not available at all. Note that this
* sensor 4 is *NOT* the sensor that might be attached to the VIN4
* pin.
*/
if (sc->sc_sensor_instances[instance].sc_i_member == 4 &&
raw_temp_config_3 & 0x20) {
iic_release_bus(sc->sc_tag, 0);
return;
}
}
if (product_id == EMCFAN_PRODUCT_2103_24) {
/* The anti-parallel mode, apd, must be enabled before sensor 3 will
* be available.
*/
if (!using_apd &&
sc->sc_sensor_instances[instance].sc_i_member == 3) {
iic_release_bus(sc->sc_tag, 0);
return;
}
}
if (product_id == EMCFAN_PRODUCT_2104 ||
product_id == EMCFAN_PRODUCT_2106) {
/* The anti-parallel mode, apd, must be enabled before sensor 4 will
* be available. This, of course, might conflict if sensor 3 is a VIN
* sensor. Don't do that....
*/
if (!using_apd &&
sc->sc_sensor_instances[instance].sc_i_member == 4) {
iic_release_bus(sc->sc_tag, 0);
return;
}
}
/* There is a data interlock thing going on with the high and low
* registers, but it works the opposite from the tach.
*/
error = emcfan_read_register(sc, external_temp_high_reg, &temp_high);
if (error) {
device_printf(sc->sc_dev,"%s: could not read external temp high: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
if (using_vin) {
temp_low = 0;
} else {
error = emcfan_read_register(sc, external_temp_low_reg, &temp_low);
if (error) {
device_printf(sc->sc_dev,"%s: could not read external temp low: %d\n",__func__, error);
iic_release_bus(sc->sc_tag, 0);
return;
}
}
}
}
}
iic_release_bus(sc->sc_tag, 0);
/* It appears that on the 2101, if the high byte is 0x7f and the low byte is 0,\
* then there is a fault or no sensor.
*/
if (product_id == EMCFAN_PRODUCT_2101 &&
!is_internal) {
if (temp_high == 0x7f &&
temp_low == 0) {
return;
}
}
/* For everyone else, if the external sensor read 0x80 on the high byte and
* the fraction is 0, then there is a fault or no sensor.
*/
if (!is_internal && !using_vin) {
if (temp_high == 0x80 &&
(temp_low >> 5) == 0x00) {
return;
}
}
int32_t kelvin1000 = 0;
int32_t frac = 0;
uint8_t tl;
if (!using_vin) {
kelvin1000 = (int8_t)temp_high * 1000;
tl = temp_low >> 5;
if (temp_high & 0x80) {
tl = (~tl) & 0x07;
tl++;
}
frac = 125 * tl;
if (temp_high & 0x80) {
kelvin1000 -= frac;
} else {
kelvin1000 += frac;
}
kelvin1000 += 273150;
} else {
int32_t vin1000 = EMCFAN_VIN_NO_TEMP;
if (inverted) {
if (emcfan_vin_temps_i[temp_high] != EMCFAN_VIN_NO_TEMP) {
vin1000 = emcfan_vin_temps_i[temp_high];
}
} else {
if (emcfan_vin_temps[temp_high] != EMCFAN_VIN_NO_TEMP) {
vin1000 = emcfan_vin_temps[temp_high];
}
}
if (vin1000 != EMCFAN_VIN_NO_TEMP)
kelvin1000 = vin1000;
else
return;
}
edata->value_cur = (uint32_t) kelvin1000 * 1000;
edata->state = ENVSYS_SVALID;
}
static void
emcfan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
{
struct emcfan_sc *sc = sme->sme_cookie;
int instance = -1;
/* Hunt down the instance for this sensor.
*
* The type is held in sc_sensors, but the actual hardware
* instance is held in sc_sensor_instances in the member
* field. It would be nice if the edata structure had a
* field that could be used as an opaque value.
*/
for(int i = 0;i < EMCFAN_NUM_SENSORS;i++)
if (sc->sc_sensor_instances[i].sc_i_envnum == edata->sensor) {
instance = i;
break;
}
KASSERT(instance > -1);
DPRINTF(sc, 2, ("%s: using sensor instance %d\n", __func__,
instance));
edata->state = ENVSYS_SINVALID;
/* Unlike manor of the refresh functions in other drivers, this
* one will select the sensor based upon the type and instance.
*
* Due to the fact that the order will vary depending on which
* chip you are using.
*/
switch(edata->units) {
case ENVSYS_SFANRPM:
switch(emcfan_chip_infos[sc->sc_info_index].family) {
case EMCFAN_FAMILY_210X:
switch(emcfan_chip_infos[sc->sc_info_index].product_id) {
case EMCFAN_PRODUCT_2101:
emcfan_refresh_2101_tach(sme, edata, instance);
break;
/* 2103, 2104 and 2106 use nearly the same algorithm as the 230x family */
default:
emcfan_refresh_210_346_230x_tach(emcfan_chip_infos[sc->sc_info_index].family,
emcfan_chip_infos[sc->sc_info_index].product_id,
sme, edata, instance);
break;
};
break;
case EMCFAN_FAMILY_230X:
emcfan_refresh_210_346_230x_tach(emcfan_chip_infos[sc->sc_info_index].family,
emcfan_chip_infos[sc->sc_info_index].product_id,
sme, edata, instance);
break;
default:
panic("Unknown family: %d\n",emcfan_chip_infos[sc->sc_info_index].family);
break;
}
break;
case ENVSYS_STEMP:
emcfan_refresh_temp(emcfan_chip_infos[sc->sc_info_index].family,
emcfan_chip_infos[sc->sc_info_index].product_id,
sme, edata, instance);
break;
default:
panic("Unknown edata units value: %d\n",edata->units);
break;
};
}
static int
emcfanopen(dev_t dev, int flags, int fmt, struct lwp *l)
{
struct emcfan_sc *sc;
sc = device_lookup_private(&emcfan_cd, minor(dev));
if (!sc)
return ENXIO;
if (sc->sc_opened)
return EBUSY;
mutex_enter(&sc->sc_mutex);
sc->sc_opened = true;
mutex_exit(&sc->sc_mutex);
return 0;
}
static int
emcfanread(dev_t dev, struct uio *uio, int flags)
{
struct emcfan_sc *sc;
int error;
if ((sc = device_lookup_private(&emcfan_cd, minor(dev))) == NULL)
return ENXIO;
/* We do not make this an error. There is nothing wrong with running
* off the end here, just return EOF.
*/
if (uio->uio_offset > 0xff)
return 0;
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
return error;
while (uio->uio_resid &&
uio->uio_offset <= 0xff &&
!sc->sc_dying) {
uint8_t buf;
int reg_addr = uio->uio_offset;
if ((error = emcfan_read_register(sc, reg_addr, &buf)) != 0) {
iic_release_bus(sc->sc_tag, 0);
aprint_error_dev(sc->sc_dev,
"%s: read failed at 0x%02x: %d\n",
__func__, reg_addr, error);
return error;
}
if (sc->sc_dying)
break;
if ((error = uiomove(&buf, 1, uio)) != 0) {
iic_release_bus(sc->sc_tag, 0);
return error;
}
}
iic_release_bus(sc->sc_tag, 0);
if (sc->sc_dying) {
return EIO;
}
return 0;
}
static int
emcfanwrite(dev_t dev, struct uio *uio, int flags)
{
struct emcfan_sc *sc;
int error;
if ((sc = device_lookup_private(&emcfan_cd, minor(dev))) == NULL)
return ENXIO;
/* Same thing as read, this is not considered an error */
if (uio->uio_offset > 0xff)
return 0;
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
return error;
while (uio->uio_resid &&
uio->uio_offset <= 0xff &&
!sc->sc_dying) {
uint8_t buf;
int reg_addr = uio->uio_offset;
if ((error = uiomove(&buf, 1, uio)) != 0)
break;
if (sc->sc_dying)
break;
if ((error = emcfan_write_register(sc, (uint8_t)reg_addr, buf)) != 0) {
iic_release_bus(sc->sc_tag, 0);
aprint_error_dev(sc->sc_dev,
"%s: write failed at 0x%02x: %d\n",
__func__, reg_addr, error);
return error;
}
}
iic_release_bus(sc->sc_tag, 0);
if (sc->sc_dying) {
return EIO;
}
return error;
}
static int
emcfanclose(dev_t dev, int flags, int fmt, struct lwp *l)
{
struct emcfan_sc *sc;
sc = device_lookup_private(&emcfan_cd, minor(dev));
mutex_enter(&sc->sc_mutex);
sc->sc_opened = false;
mutex_exit(&sc->sc_mutex);
return(0);
}
static int
emcfan_detach(device_t self, int flags)
{
int err;
struct emcfan_sc *sc;
sc = device_private(self);
mutex_enter(&sc->sc_mutex);
sc->sc_dying = true;
err = config_detach_children(self, flags);
if (err)
return err;
/* Remove the sysctl tree */
if (sc->sc_emcfanlog != NULL)
sysctl_teardown(&sc->sc_emcfanlog);
/* Remove the sensors */
if (sc->sc_sme != NULL) {
sysmon_envsys_unregister(sc->sc_sme);
sc->sc_sme = NULL;
}
mutex_exit(&sc->sc_mutex);
mutex_destroy(&sc->sc_mutex);
return 0;
}
int
emcfan_activate(device_t self, enum devact act)
{
struct emcfan_sc *sc = device_private(self);
switch (act) {
case DVACT_DEACTIVATE:
sc->sc_dying = true;
return 0;
default:
return EOPNOTSUPP;
}
}
/* --- GPIO --- */
static int
emcfan_current_gpio_flags(struct emcfan_sc *sc,
uint8_t product_id,
int pin)
{
int error = 0;
int f = 0;
uint8_t mux_reg = 0;
uint8_t dir_reg;
uint8_t out_config;
uint8_t pin_mask;
uint8_t pin_maska1;
uint8_t pin_maska2;
error = iic_acquire_bus(sc->sc_tag, 0);
if (error) {
return 0;
}
if (product_id != EMCFAN_PRODUCT_2103_24) {
error = emcfan_read_register(sc, EMCFAN_MUX_PINS, &mux_reg);
if (error != 0) {
return 0;
}
}
error = emcfan_read_register(sc, EMCFAN_DIR_PINS, &dir_reg);
if (error != 0) {
return 0;
}
error = emcfan_read_register(sc, EMCFAN_OUTPUT_PIN_CONFIG, &out_config);
if (error != 0) {
return 0;
}
iic_release_bus(sc->sc_tag, 0);
pin_mask = 1 << pin;
if (product_id != EMCFAN_PRODUCT_2103_24) {
if (pin <= 4) {
if (pin <= 2) {
if ((mux_reg & pin_mask) == 0)
f = GPIO_PIN_ALT0;
} else {
if (pin == 3) {
pin_maska1 = 0x08;
pin_maska2 = 0x10;
} else {
pin_maska1 = 0x20;
pin_maska2 = 0x40;
}
if (mux_reg & pin_maska1 &&
mux_reg & pin_maska2) {
f = GPIO_PIN_ALT1;
} else {
if (((mux_reg & pin_maska1) == 0) &&
((mux_reg & pin_maska2) == 0)) {
f = GPIO_PIN_ALT0;
}
}
}
}
}
if (f == 0) {
if (dir_reg & pin_mask) {
f = GPIO_PIN_OUTPUT;
} else {
f = GPIO_PIN_INPUT;
}
if (out_config & pin_mask) {
f |= GPIO_PIN_PUSHPULL;
} else {
f |= GPIO_PIN_OPENDRAIN;
}
}
return f;
}
static int
emcfan_gpio_pin_read(void *arg, int pin)
{
struct emcfan_sc *sc = arg;
int error = 0;
int r = GPIO_PIN_LOW;
uint8_t input_reg;
uint8_t pin_mask;
error = iic_acquire_bus(sc->sc_tag, 0);
if (!error) {
error = emcfan_read_register(sc, EMCFAN_PINS_INPUT, &input_reg);
if (!error) {
pin_mask = 1 << pin;
if (input_reg & pin_mask)
r = GPIO_PIN_HIGH;
}
}
iic_release_bus(sc->sc_tag, 0);
return r;
}
static void
emcfan_gpio_pin_write(void *arg, int pin, int value)
{
struct emcfan_sc *sc = arg;
int error = 0;
uint8_t output_reg;
uint8_t pin_mask;
error = iic_acquire_bus(sc->sc_tag, 0);
if (!error) {
error = emcfan_read_register(sc, EMCFAN_PINS_OUTPUT, &output_reg);
if (!error) {
pin_mask = 1 << pin;
if (value == 0) {
pin_mask = ~pin_mask;
output_reg &= pin_mask;
} else {
output_reg |= pin_mask;
}
emcfan_write_register(sc, EMCFAN_PINS_OUTPUT, output_reg);
}
}
iic_release_bus(sc->sc_tag, 0);
}
static void
emcfan_gpio_pin_ctl(void *arg, int pin, int flags)
{
struct emcfan_sc *sc = arg;
int error = 0;
uint8_t product_id = emcfan_chip_infos[sc->sc_info_index].product_id;
uint8_t pin_mask = 1 << pin;
uint8_t pin_maska1;
uint8_t pin_maska2;
uint8_t mux_reg = 0;
uint8_t dir_reg;
uint8_t out_config;
error = iic_acquire_bus(sc->sc_tag, 0);
if (error) {
return;
}
if (product_id != EMCFAN_PRODUCT_2103_24) {
error = emcfan_read_register(sc, EMCFAN_MUX_PINS, &mux_reg);
if (error != 0) {
return;
}
}
error = emcfan_read_register(sc, EMCFAN_DIR_PINS, &dir_reg);
if (error != 0) {
return;
}
error = emcfan_read_register(sc, EMCFAN_OUTPUT_PIN_CONFIG, &out_config);
if (error != 0) {
return;
}
iic_release_bus(sc->sc_tag, 0);
if (flags & GPIO_PIN_ALT0 ||
flags & GPIO_PIN_ALT1) {
if (product_id != EMCFAN_PRODUCT_2103_24) {
if (pin <= 4) {
if (pin <= 2) {
mux_reg &= ~pin_mask;
} else {
if (pin == 3) {
pin_maska2 = 0x18;
} else {
pin_maska2 = 0x60;
}
if (flags & GPIO_PIN_ALT0) {
mux_reg &= ~pin_maska2;
} else {
if (flags & GPIO_PIN_ALT1) {
mux_reg |= pin_maska2;
}
}
}
emcfan_write_register(sc, EMCFAN_MUX_PINS, mux_reg);
}
}
} else {
if (product_id != EMCFAN_PRODUCT_2103_24) {
if (pin <= 4) {
if (pin <= 2) {
mux_reg |= pin_mask;
} else {
if (pin == 3) {
pin_maska1 = 0x08;
pin_maska2 = 0x18;
} else {
pin_maska1 = 0x20;
pin_maska2 = 0x60;
}
mux_reg &= ~pin_maska2;
mux_reg |= pin_maska1;
}
emcfan_write_register(sc, EMCFAN_MUX_PINS, mux_reg);
}
}
if (flags & GPIO_PIN_OPENDRAIN) {
out_config &= ~pin_mask;
} else {
if (flags & GPIO_PIN_PUSHPULL)
out_config |= pin_mask;
}
emcfan_write_register(sc, EMCFAN_OUTPUT_PIN_CONFIG, out_config);
if (flags & GPIO_PIN_INPUT) {
dir_reg &= ~pin_mask;
} else {
if (flags & GPIO_PIN_OUTPUT)
dir_reg |= pin_mask;
}
emcfan_write_register(sc, EMCFAN_DIR_PINS, dir_reg);
}
}
static void
emcfan_attach_gpio(struct emcfan_sc *sc, uint8_t product_id)
{
struct gpiobus_attach_args gba;
for(int i = 0; i < emcfan_chip_infos[sc->sc_info_index].num_gpio_pins;i++) {
sc->sc_gpio_pins[i].pin_num = i;
sc->sc_gpio_pins[i].pin_caps = emcfan_chip_infos[sc->sc_info_index].gpio_pin_ability[i];
sc->sc_gpio_pins[i].pin_flags = emcfan_current_gpio_flags(sc, emcfan_chip_infos[sc->sc_info_index].product_id, i);
sc->sc_gpio_pins[i].pin_intrcaps = 0;
strncpy(sc->sc_gpio_pins[i].pin_defname,
emcfan_chip_infos[sc->sc_info_index].gpio_names[i],
strlen(emcfan_chip_infos[sc->sc_info_index].gpio_names[i]) + 1);
}
sc->sc_gpio_gc.gp_cookie = sc;
sc->sc_gpio_gc.gp_pin_read = emcfan_gpio_pin_read;
sc->sc_gpio_gc.gp_pin_write = emcfan_gpio_pin_write;
sc->sc_gpio_gc.gp_pin_ctl = emcfan_gpio_pin_ctl;
gba.gba_gc = &sc->sc_gpio_gc;
gba.gba_pins = sc->sc_gpio_pins;
gba.gba_npins = emcfan_chip_infos[sc->sc_info_index].num_gpio_pins;
sc->sc_gpio_dev = config_found(sc->sc_dev, &gba, gpiobus_print, CFARGS(.iattr = "gpiobus"));
return;
}
MODULE(MODULE_CLASS_DRIVER, emcfan, "iic,sysmon_envsys,gpio");
#ifdef _MODULE
#include "ioconf.c"
#endif
static int
emcfan_modcmd(modcmd_t cmd, void *opaque)
{
int error;
#ifdef _MODULE
int bmaj = -1, cmaj = -1;
#endif
switch (cmd) {
case MODULE_CMD_INIT:
#ifdef _MODULE
error = devsw_attach("emcfan", NULL, &bmaj,
&emcfan_cdevsw, &cmaj);
if (error) {
aprint_error("%s: unable to attach devsw\n",
emcfan_cd.cd_name);
return error;
}
error = config_init_component(cfdriver_ioconf_emcfan,
cfattach_ioconf_emcfan, cfdata_ioconf_emcfan);
if (error) {
aprint_error("%s: unable to init component\n",
emcfan_cd.cd_name);
devsw_detach(NULL, &emcfan_cdevsw);
}
return error;
#else
return 0;
#endif
case MODULE_CMD_FINI:
#ifdef _MODULE
error = config_fini_component(cfdriver_ioconf_emcfan,
cfattach_ioconf_emcfan, cfdata_ioconf_emcfan);
devsw_detach(NULL, &emcfan_cdevsw);
return error;
#else
return 0;
#endif
default:
return ENOTTY;
}
}