/* $NetBSD: emcfan.c,v 1.1 2025/03/11 13:56:46 brad Exp $ */ /* * Copyright (c) 2025 Brad Spencer * * 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 __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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; } }