Hello tech@, some time ago I sent patch for u-boot port (aarch4), enabling sxitemp driver (though it's wasn't reviewed yet) - there were just some entries in device tree missing that were added in the patch.
Then I noticed sxitemp (OpenBSD) and aw_thermal (FreeBSD) are showing different values for temperature data and the difference isn't just related to the CPU load or environment I think. FreeBSD: # sysctl dev.aw_thermal dev.aw_thermal.0.gpu2: 24C dev.aw_thermal.0.gpu1: 22C dev.aw_thermal.0.cpu: 24C OpenBSD: $ sysctl hw hw.sensors.sxitemp0.temp0=43.57 degC (CPU) hw.sensors.sxitemp0.temp1=43.57 degC (GPU) hw.sensors.sxitemp0.temp2=44.39 degC I started looking at sxitemp driver and found out that FreeBSD driver sets calibration data stored in SID memory (e-fuses). Access to this memory area is implemented as a separate driver and OpenBSD is missing it completely. Trying to learn something, I started the sxisid implementation and updated sxitemp driver to get and set calibration values. I also thought it may be useful to set 'uuid' value using another SID e-fuse called 'chip id'. With the added sxisid and calibration data the output is: $ sysctl hw hw.sensors.sxitemp0.temp0=28.04 degC (CPU) hw.sensors.sxitemp0.temp1=26.64 degC (GPU) hw.sensors.sxitemp0.temp2=30.14 degC hw.uuid=ba00c09220460004080890510e0b1964 FreeBSD aw_sid shows: # sysctl dev.aw_sid dev.aw_sid.0.ths-calib: 830775078707 dev.aw_sid.0.rootkey: ba00c09220460004080890510e0b1964 OpenBSD doesn't show calibration value anywhere but I confirmed using additional (now removed) debug logs that read value is the same. OpenBSD sxisid driver: 1. exposes uint32_t sxisid_read(bus_size_t, uint32_t, uint8_t *) function (used by sxitemp by passing SID memory offset and size from device tree) 2. added sxisid driver as 'early' because of this `sxisid_read` that could be used later by another driver - was it good idea? 3. OpenBSD sxisid does not access information directly - 'public' flag in the aw_sid driver - everything is read using SID_PRCTL and SID_READ registers. Having only a64 board, not being able to test other I wanted to focus on one way to access the data 4. Pine A64+ - the only board I have had all fields marked as 'public' in aw_sid but it seems they can be accessed same way as '!public' e-fuses just fine 5. I couldn't find anything in the A64 manual regarding SID/e-fuses so I relied on linux-sunxi.org and aw_sid implementation 6. I'm not sure if this is a thing in OpenBSD but I assumed access to e-fuses (via SID_PRCTL) should be in a critical section (because driver requests the data using SID_PRCTL register, waits for the data and reads the data using SID_READ register) - I used mutex (IPL_NONE) for that 7. I tried to avoid any hardcoded addresses and instead to move them to device tree definition (I'll send updated u-boot patch afterwards) 8. with unmodified u-boot sxisid is not attached and sxitemp behaves like before (no calibration data is set) I'm not sure if implementing this was a good idea and if it's good enough. As I wrote earlier - I wanted to learn something and for sure I had a lot of fun with it. References: http://linux-sunxi.org/SID_Register_Guide https://github.com/freebsd/freebsd/blob/master/sys/arm/allwinner/ (aw_sid and aw_thermal) Index: sys/arch/arm64/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v retrieving revision 1.98 diff -u -p -r1.98 GENERIC --- sys/arch/arm64/conf/GENERIC 1 Apr 2019 08:46:16 -0000 1.98 +++ sys/arch/arm64/conf/GENERIC 8 Apr 2019 20:11:51 -0000 @@ -172,6 +172,7 @@ axppmic* at rsb? sxirtc* at fdt? # Real Time Clock sximmc* at fdt? # SD/MMC card controller sdmmc* at sximmc? # SD/MMC bus +sxisid* at fdt? early 1 # SID ROM sxitemp* at fdt? # Temperature sensor sxitwi* at fdt? # I2C controller iic* at sxitwi? # I2C bus Index: sys/dev/fdt/files.fdt =================================================================== RCS file: /cvs/src/sys/dev/fdt/files.fdt,v retrieving revision 1.78 diff -u -p -r1.78 files.fdt --- sys/dev/fdt/files.fdt 1 Apr 2019 08:46:16 -0000 1.78 +++ sys/dev/fdt/files.fdt 8 Apr 2019 20:11:51 -0000 @@ -44,6 +44,10 @@ device sxitwi: i2cbus attach sxitwi at fdt file dev/fdt/sxitwi.c sxitwi +device sxisid +attach sxisid at fdt +file dev/fdt/sxisid.c sxisid + device axppmic attach axppmic at i2c attach axppmic at rsb with axppmic_rsb Index: sys/dev/fdt/sxisid.c =================================================================== RCS file: sys/dev/fdt/sxisid.c diff -N sys/dev/fdt/sxisid.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/fdt/sxisid.c 8 Apr 2019 20:11:51 -0000 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2019 Krystian Lewandowski + * + * 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/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/mutex.h> + +#include <machine/fdt.h> +#include <machine/bus.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/fdt.h> + +#define EFUSE_OFFSET 0x0200 +#define SID_PRCTL_OFFSET(n) (((n) & 0xff) << 16) +#define SID_PRCTL_OP_LOCK (0xac << 8) +#define SID_PRCTL_READ (0x01 << 1) +#define SID_PRCTL 0x40 +#define SID_RDKEY 0x60 + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh,(reg))) + +#define HWRITE4(sc, reg, val) \ + (bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))) + +struct sxisid_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + struct mutex sc_mtx; +}; + +int sxisid_match(struct device *, void *, void *); +void sxisid_attach(struct device *, struct device *, void *); +uint32_t sxisid_read(bus_size_t, uint32_t, uint8_t *); + +struct cfattach sxisid_ca = { + sizeof(struct sxisid_softc), sxisid_match, sxisid_attach +}; + +struct cfdriver sxisid_cd = { + NULL, "sxisid", DV_DULL +}; + +static struct sxisid_softc *sxisid_sc; + +extern char *hw_uuid; + +int +sxisid_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + return OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-sid"); +} + +void +sxisid_attach(struct device *parent, struct device *self, void *aux) +{ + struct sxisid_softc *sc = (struct sxisid_softc *) self; + struct fdt_attach_args *faa = aux; + uint8_t *sid, hw_uuid_len; + uint32_t i, *chipid_reg; + int chipid_node, chipid_len; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + printf("\n"); + + mtx_init(&sc->sc_mtx, IPL_NONE); + sxisid_sc = sc; + + chipid_node = OF_getnodebyname(faa->fa_node, "chip-id@200"); + if (chipid_node == 0) { + return; + } + chipid_len = OF_getproplen(chipid_node, "reg"); + if (chipid_len == 0) { + return; + } + chipid_reg = malloc(chipid_len, M_TEMP, M_NOWAIT); + if (chipid_reg == NULL) { + return; + } + if (OF_getpropintarray(chipid_node, "reg", chipid_reg, chipid_len) != + chipid_len) { + free(chipid_reg, M_TEMP, chipid_len); + return; + } + hw_uuid_len = chipid_reg[1] * 2 + 1; + + hw_uuid = malloc(hw_uuid_len, M_DEVBUF, M_NOWAIT); + sid = malloc(chipid_reg[1], M_TEMP, M_NOWAIT); + if (hw_uuid != NULL && sid != NULL && + sxisid_read(chipid_reg[0], chipid_reg[1], sid) == chipid_reg[1]) { + for (i = 0; i < chipid_reg[1]; i++) { + snprintf(hw_uuid + (i * 2), + hw_uuid_len - (i * 2), + "%.2x", sid[i]); + } + } + free(sid, M_TEMP, chipid_reg[1]); + free(chipid_reg, M_TEMP, chipid_len); +} + +uint32_t +sxisid_read(bus_size_t offset, uint32_t size, uint8_t *out) +{ + struct sxisid_softc *sc = sxisid_sc; + uint32_t i, j, val; + + if (sc == NULL) { + return 0; + } + mtx_enter(&sc->sc_mtx); + + for (i = 0; i < size; i += 4) { + val = SID_PRCTL_OFFSET(offset - EFUSE_OFFSET + i) | + SID_PRCTL_OP_LOCK | SID_PRCTL_READ; + HWRITE4(sc, SID_PRCTL, val); + + while (HREAD4(sc, SID_PRCTL) & SID_PRCTL_READ) { + continue; + } + + val = HREAD4(sc, SID_RDKEY); + + for (j = i; j < size; j++) { + if (j - i == sizeof(val)) { + break; + } + out[j] = val & 0xFF; + val = val >> 8; + } + } + + mtx_leave(&sc->sc_mtx); + + return size; +} Index: sys/dev/fdt/sxitemp.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/sxitemp.c,v retrieving revision 1.4 diff -u -p -r1.4 sxitemp.c --- sys/dev/fdt/sxitemp.c 27 May 2018 21:59:26 -0000 1.4 +++ sys/dev/fdt/sxitemp.c 8 Apr 2019 20:11:51 -0000 @@ -19,6 +19,7 @@ #include <sys/systm.h> #include <sys/device.h> #include <sys/sensors.h> +#include <sys/malloc.h> #include <machine/intr.h> #include <machine/bus.h> @@ -46,6 +47,7 @@ #define THS0_DATA 0x0080 #define THS1_DATA 0x0084 #define THS2_DATA 0x0088 +#define THS_CALIB0 0x0074 #define HREAD4(sc, reg) \ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) @@ -81,7 +83,13 @@ uint64_t sxitemp_r40_calc_temp(int64_t); uint64_t sxitemp_a64_calc_temp(int64_t); uint64_t sxitemp_h5_calc_temp0(int64_t); uint64_t sxitemp_h5_calc_temp1(int64_t); -void sxitemp_refresh_sensors(void *); +void sxitemp_refresh_sensors(void *); +void sxitemp_setup_calib(struct sxitemp_softc *, + struct fdt_attach_args *); +uint32_t sxitemp_calib_mask(uint32_t, uint32_t); +void sxitemp_tx(struct sxitemp_softc *, uint32_t, uint32_t, uint32_t *); + +extern uint32_t sxisid_read(bus_size_t, uint32_t, uint8_t *); int sxitemp_match(struct device *parent, void *match, void *aux) @@ -140,6 +148,8 @@ sxitemp_attach(struct device *parent, st enable = THS_CTRL2_SENSE0_EN | THS_CTRL2_SENSE1_EN; } + sxitemp_setup_calib(sc, faa); + /* Start data acquisition. */ HWRITE4(sc, THS_FILTER, THS_FILTER_EN | THS_FILTER_TYPE(1)); HWRITE4(sc, THS_INT_CTRL, THS_INT_CTRL_THERMAL_PER(800)); @@ -236,4 +246,70 @@ sxitemp_refresh_sensors(void *arg) sc->sc_sensors[2].value = sc->sc_calc_temp2(data) + 273150000; sc->sc_sensors[2].flags &= ~SENSOR_FINVALID; } +} + +void +sxitemp_setup_calib(struct sxitemp_softc *sc, struct fdt_attach_args *faa) +{ + int phandle , node, len; + uint32_t *reg, *calib, calib_len; + + phandle = OF_getpropint(faa->fa_node, "nvmem-cells", 0); + if (phandle == 0) { + return; + } + node = OF_getnodebyphandle(phandle); + if (node == 0) { + return; + } + len = OF_getproplen(node, "reg"); + if (len == 0) { + return; + } + reg = malloc(len, M_TEMP, M_NOWAIT); + + if (reg == NULL) { + return; + } + if (OF_getpropintarray(node, "reg", reg, len) != len) { + free(reg, M_TEMP, len); + return; + } + calib_len = (((reg[1] + 7) / 8) * 8); + calib = malloc(calib_len, M_TEMP, M_NOWAIT); + + if (calib != NULL) { + sxitemp_tx(sc, reg[0], reg[1], calib); + } + free(calib, M_TEMP, calib_len); + free(reg, M_TEMP, len); +} + +void +sxitemp_tx(struct sxitemp_softc *sc, uint32_t addr, uint32_t len, uint32_t *buff) +{ + uint32_t i, calib_mask, calib_val; + + if (sxisid_read(addr, len, (uint8_t *) buff) != len) { + return; + } + for (i = 0; i < len; i += 4) { + calib_mask = sxitemp_calib_mask(len, i + 4); + calib_val = buff[i / 4] & calib_mask; + HWRITE4(sc, THS_CALIB0 + i, calib_val); + } +} + +uint32_t +sxitemp_calib_mask(uint32_t total_len, uint32_t sizeof_len) +{ + uint32_t i, calib_mask; + calib_mask = 0xffffffff; + + if (total_len < sizeof_len) { + for (i = 0; i < sizeof_len - total_len; i++) { + calib_mask = calib_mask >> 8; + } + } + return calib_mask; }