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;
 }

Reply via email to