git: da3557899886 - main - lm75: Refactor code to fix io error
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 01 Dec 2022 08:18:26 UTC
The branch main has been updated by wma: URL: https://cgit.FreeBSD.org/src/commit/?id=da35578998869daf998a7a81d58fd90b2daf8f7d commit da35578998869daf998a7a81d58fd90b2daf8f7d Author: Jakub Kolodziej <jkol@semihalf.com> AuthorDate: 2022-12-01 08:02:52 +0000 Commit: Wojciech Macek <wma@FreeBSD.org> CommitDate: 2022-12-01 08:12:39 +0000 lm75: Refactor code to fix io error Use correct resolution by compat table. If dtb is not defined use default 9 bit mode. 11 bit detection is called if 9 bit mode is used. Sysctl resolution variable is added to change resolution in case. Some sensors didn't pull ACK while reading from nonexistent registers and it caused I2C read error and detect failure, so now detect failure does not cause driver break. Obtained from: Semihalf Sponsored by: Alstom Differential revision: https://reviews.freebsd.org/D37497 --- sys/dev/iicbus/lm75.c | 189 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 67 deletions(-) diff --git a/sys/dev/iicbus/lm75.c b/sys/dev/iicbus/lm75.c index 19630aee11fe..bbc88050b488 100644 --- a/sys/dev/iicbus/lm75.c +++ b/sys/dev/iicbus/lm75.c @@ -51,8 +51,6 @@ __FBSDID("$FreeBSD$"); /* LM75 registers. */ #define LM75_TEMP 0x0 -#define LM75_TEMP_MASK 0xff80 -#define LM75A_TEMP_MASK 0xffe0 #define LM75_CONF 0x1 #define LM75_CONF_FSHIFT 3 #define LM75_CONF_FAULT 0x18 @@ -67,16 +65,13 @@ __FBSDID("$FreeBSD$"); #define LM75_TEST_PATTERN 0xa #define LM75_MIN_TEMP -55 #define LM75_MAX_TEMP 125 -#define LM75_0500C 0x80 -#define LM75_0250C 0x40 -#define LM75_0125C 0x20 -#define LM75_MSB 0x8000 -#define LM75_NEG_BIT LM75_MSB -#define TZ_ZEROC 2731 +#define TZ_ZEROC 27315 +#define TZ_ZEROC_DIVIDER 100 -/* LM75 supported models. */ -#define HWTYPE_LM75 1 -#define HWTYPE_LM75A 2 +enum max_resolution{ + BITS_9 = 1, + BITS_11 +}; /* Regular bus attachment functions */ static int lm75_probe(device_t); @@ -85,9 +80,11 @@ static int lm75_attach(device_t); struct lm75_softc { device_t sc_dev; struct intr_config_hook enum_hook; - int32_t sc_hwtype; uint32_t sc_addr; uint32_t sc_conf; + uint8_t sc_resolution; + uint8_t sc_max_resolution; + uint16_t sc_multiplier; }; /* Utility functions */ @@ -105,6 +102,7 @@ static int lm75_faults_sysctl(SYSCTL_HANDLER_ARGS); static int lm75_mode_sysctl(SYSCTL_HANDLER_ARGS); static int lm75_pol_sysctl(SYSCTL_HANDLER_ARGS); static int lm75_shutdown_sysctl(SYSCTL_HANDLER_ARGS); +static int lm75_resolution_sysctl(SYSCTL_HANDLER_ARGS); static device_method_t lm75_methods[] = { /* Device interface */ @@ -120,6 +118,14 @@ static driver_t lm75_driver = { sizeof(struct lm75_softc) }; +#ifdef FDT +static struct ofw_compat_data compat_data[] = { + {"national,lm75", BITS_9}, + {"ti,lm75", BITS_9}, + {0,0} +}; +#endif + DRIVER_MODULE(lm75, iicbus, lm75_driver, 0, 0); static int @@ -152,13 +158,30 @@ lm75_write(device_t dev, uint32_t addr, uint8_t *data, size_t len) static int lm75_probe(device_t dev) { +#ifdef FDT + const struct ofw_compat_data *compat_ptr; +#endif struct lm75_softc *sc; sc = device_get_softc(dev); - sc->sc_hwtype = HWTYPE_LM75; + sc->sc_max_resolution = 9; + #ifdef FDT - if (!ofw_bus_is_compatible(dev, "national,lm75")) + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + compat_ptr = ofw_bus_search_compatible(dev, compat_data); + + switch (compat_ptr->ocd_data){ + case BITS_9: + sc->sc_max_resolution = 9; + break; + case BITS_11: + sc->sc_max_resolution = 11; + break; + default: return (ENXIO); + } #endif device_set_desc(dev, "LM75 temperature sensor"); @@ -177,6 +200,21 @@ lm75_attach(device_t dev) sc->enum_hook.ich_func = lm75_start; sc->enum_hook.ich_arg = dev; + switch (sc->sc_max_resolution) { + case 9: + sc->sc_resolution = 9; + sc->sc_max_resolution = 9; + sc->sc_multiplier = 10; + break; + case 11: + sc->sc_resolution = 11; + sc->sc_max_resolution = 11; + sc->sc_multiplier = 1000; + break; + default: + return (ENXIO); + } + /* * We have to wait until interrupts are enabled. Usually I2C read * and write only works when the interrupts are available. @@ -232,8 +270,11 @@ lm75_type_detect(struct lm75_softc *sc) if (buf8 == 0xff) lm75a++; } - if (lm75a == 3) - sc->sc_hwtype = HWTYPE_LM75A; + if (lm75a == 3){ + sc->sc_multiplier = 1000; + sc->sc_resolution = 11; + sc->sc_max_resolution = 11; + } /* Restore the configuration register. */ sc->sc_conf = conf; @@ -251,6 +292,7 @@ lm75_start(void *xdev) struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; + char *mult_format; dev = (device_t)xdev; sc = device_get_softc(dev); @@ -265,23 +307,30 @@ lm75_start(void *xdev) * This may not work for LM75 clones. */ if (lm75_type_detect(sc) != 0) { - device_printf(dev, "cannot read from sensor.\n"); + device_printf(dev, "cannot detect sesnor.\n"); +#ifndef FDT return; +#endif } - if (sc->sc_hwtype == HWTYPE_LM75A) - device_printf(dev, - "LM75A type sensor detected (11bits resolution).\n"); + + device_printf(dev,"%d bit resolution sensor attached.\n", + sc->sc_resolution); + + if (sc->sc_multiplier == 1000) + mult_format = "IK3"; + else + mult_format = "IK"; /* Temperature. */ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, LM75_TEMP, - lm75_temp_sysctl, "IK", "Current temperature"); + lm75_temp_sysctl, mult_format, "Current temperature"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "thyst", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, LM75_THYST, - lm75_temp_sysctl, "IK", "Hysteresis temperature"); + lm75_temp_sysctl, mult_format, "Hysteresis temperature"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "tos", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, LM75_TOS, - lm75_temp_sysctl, "IK", "Overtemperature"); + lm75_temp_sysctl, mult_format, "Overtemperature"); /* Configuration parameters. */ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "faults", @@ -296,6 +345,9 @@ lm75_start(void *xdev) SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "shutdown", CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, dev, 0, lm75_shutdown_sysctl, "IU", "LM75 shutdown"); + SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "resolution", + CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_MPSAFE, dev, 0, + lm75_resolution_sysctl, "IU", "LM75 resolution"); } static int @@ -325,65 +377,43 @@ lm75_conf_write(struct lm75_softc *sc) } static int -lm75_temp_read(struct lm75_softc *sc, uint8_t reg, int *temp) +lm75_temp_read(struct lm75_softc *sc, uint8_t reg, int32_t *temp) { + int32_t buf; uint8_t buf8[2]; - uint16_t buf; - int neg, t; + uint8_t resolution = sc->sc_resolution; + uint16_t multiplier = sc->sc_multiplier; if (lm75_read(sc->sc_dev, sc->sc_addr, reg, buf8, sizeof(buf8)) < 0) return (-1); - buf = (uint16_t)((buf8[0] << 8) | (buf8[1] & 0xff)); - /* - * LM75 has a 9 bit ADC with resolution of 0.5 C per bit. - * LM75A has an 11 bit ADC with resolution of 0.125 C per bit. - * Temperature is stored with two's complement. - */ - neg = 0; - if (buf & LM75_NEG_BIT) { - if (sc->sc_hwtype == HWTYPE_LM75A) - buf = ~(buf & LM75A_TEMP_MASK) + 1; - else - buf = ~(buf & LM75_TEMP_MASK) + 1; - neg = 1; - } - *temp = ((int16_t)buf >> 8) * 10; - t = 0; - if (sc->sc_hwtype == HWTYPE_LM75A) { - if (buf & LM75_0125C) - t += 125; - if (buf & LM75_0250C) - t += 250; - } - if (buf & LM75_0500C) - t += 500; - t /= 100; - *temp += t; - if (neg) - *temp = -(*temp); - *temp += TZ_ZEROC; + + buf = (int16_t)((buf8[0] << 8) | buf8[1]); + *temp = ((buf >> (16 - resolution)) * multiplier) >> (resolution - 8); + + *temp += TZ_ZEROC * sc->sc_multiplier / TZ_ZEROC_DIVIDER; return (0); } static int -lm75_temp_write(struct lm75_softc *sc, uint8_t reg, int temp) +lm75_temp_write(struct lm75_softc *sc, uint8_t reg, int32_t temp) { - uint8_t buf8[3]; - uint16_t buf; + int32_t buf; + uint8_t buf8[3], resolution = sc->sc_resolution; + uint16_t multiplier = sc->sc_multiplier; - temp = (temp - TZ_ZEROC) / 10; - if (temp > LM75_MAX_TEMP) - temp = LM75_MAX_TEMP; - if (temp < LM75_MIN_TEMP) - temp = LM75_MIN_TEMP; + temp -= TZ_ZEROC * multiplier / TZ_ZEROC_DIVIDER; + if (temp > LM75_MAX_TEMP * multiplier) + temp = LM75_MAX_TEMP * multiplier; + if (temp < LM75_MIN_TEMP * multiplier) + temp = LM75_MIN_TEMP * multiplier; - buf = (uint16_t)temp; - buf <<= 8; + buf = ((temp << (resolution - 8)) / multiplier) << (16 - resolution); buf8[0] = reg; - buf8[1] = buf >> 8; + buf8[1] = (buf >> 8) & 0xff; buf8[2] = buf & 0xff; + if (lm75_write(sc->sc_dev, sc->sc_addr, buf8, sizeof(buf8)) < 0) return (-1); @@ -428,7 +458,8 @@ static int lm75_temp_sysctl(SYSCTL_HANDLER_ARGS) { device_t dev; - int error, temp; + int error; + int32_t temp; struct lm75_softc *sc; uint8_t reg; @@ -575,3 +606,27 @@ lm75_shutdown_sysctl(SYSCTL_HANDLER_ARGS) return (error); } + +static int +lm75_resolution_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev; + int error; + struct lm75_softc *sc; + int resolution; + + dev = (device_t)arg1; + sc = device_get_softc(dev); + resolution = sc->sc_resolution; + + error = sysctl_handle_int(oidp, &resolution, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + + if (resolution > sc->sc_max_resolution || resolution < 9) + return (EINVAL); + + sc->sc_resolution = (uint8_t) resolution; + + return (0); +}