/*      $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, &reg, 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, &reg, 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;
       }
}