#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/sensors.h>
#include <sys/timeout.h>
#include <machine/fdt.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/fdt.h>
#include <dev/ofw/ofw_misc.h>
#define TEMPMON_TEMPSENSE0 0x180
#define TEMPMON_TEMPSENSE0_SET 0x184
#define TEMPMON_TEMPSENSE0_CLR 0x188
#define TEMPMON_TEMPSENSE0_TOG 0x18c
#define TEMPMON_TEMPSENSE1 0x190
#define TEMPMON_TEMPSENSE1_SET 0x194
#define TEMPMON_TEMPSENSE1_CLR 0x198
#define TEMPMON_TEMPSENSE1_TOG 0x19c
#define TEMPMON_TEMPSENSE0_POWER_DOWN (1 << 0)
#define TEMPMON_TEMPSENSE0_MEASURE_TEMP (1 << 1)
#define TEMPMON_TEMPSENSE0_FINISHED (1 << 2)
#define TEMPMON_TEMPSENSE0_TEMP_CNT_MASK 0xfff
#define TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT 8
#define TEMPMON_TEMPSENSE0_ALARM_VALUE_MASK 0xfff
#define TEMPMON_TEMPSENSE0_ALARM_VALUE_SHIFT 20
#define OCOTP_ANA1 0x4e0
#define OCOTP_ANA1_HOT_TEMP_MASK 0xff
#define OCOTP_ANA1_HOT_TEMP_SHIFT 0
#define OCOTP_ANA1_HOT_COUNT_MASK 0xfff
#define OCOTP_ANA1_HOT_COUNT_SHIFT 8
#define OCOTP_ANA1_ROOM_COUNT_MASK 0xfff
#define OCOTP_ANA1_ROOM_COUNT_SHIFT 20
#define HREAD4(sc, reg) \
regmap_read_4((sc)->sc_rm, (reg))
#define HWRITE4(sc, reg, val) \
regmap_write_4((sc)->sc_rm, (reg), (val))
#define HSET4(sc, reg, bits) \
HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits) \
HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
struct imxtemp_softc {
struct device sc_dev;
int sc_node;
struct regmap *sc_rm;
uint32_t sc_hot_count;
uint32_t sc_hot_temp;
uint32_t sc_room_count;
struct ksensor sc_sensor;
struct ksensordev sc_sensordev;
struct timeout sc_sensorto;
};
int imxtemp_match(struct device *, void *, void *);
void imxtemp_attach(struct device *, struct device *, void *);
const struct cfattach imxtemp_ca = {
sizeof(struct imxtemp_softc), imxtemp_match, imxtemp_attach
};
struct cfdriver imxtemp_cd = {
NULL, "imxtemp", DV_DULL
};
void imxtemp_calibration(struct device *);
int32_t imxtemp_calc_temp(struct imxtemp_softc *, uint32_t);
void imxtemp_refresh_sensors(void *);
void imxtemp_pickup_sensors(void *);
int
imxtemp_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "fsl,imx6q-tempmon");
}
void
imxtemp_attach(struct device *parent, struct device *self, void *aux)
{
struct imxtemp_softc *sc = (struct imxtemp_softc *)self;
struct fdt_attach_args *faa = aux;
uint32_t phandle;
sc->sc_node = faa->fa_node;
phandle = OF_getpropint(sc->sc_node, "fsl,tempmon", 0);
sc->sc_rm = regmap_byphandle(phandle);
if (sc->sc_rm == NULL)
return;
printf("\n");
config_mountroot(self, imxtemp_calibration);
}
void
imxtemp_calibration(struct device *self)
{
struct imxtemp_softc *sc = (struct imxtemp_softc *)self;
uint32_t calibration;
uint32_t phandle;
struct regmap *rm;
phandle = OF_getpropint(sc->sc_node, "fsl,tempmon-data", 0);
rm = regmap_byphandle(phandle);
if (rm == NULL)
return;
calibration = regmap_read_4(rm, OCOTP_ANA1);
sc->sc_hot_count = (calibration >> OCOTP_ANA1_HOT_COUNT_SHIFT) &
OCOTP_ANA1_HOT_COUNT_MASK;
sc->sc_hot_temp = (calibration >> OCOTP_ANA1_HOT_TEMP_SHIFT) &
OCOTP_ANA1_HOT_TEMP_MASK;
sc->sc_room_count = (calibration >> OCOTP_ANA1_ROOM_COUNT_SHIFT) &
OCOTP_ANA1_ROOM_COUNT_MASK;
strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
sizeof(sc->sc_sensordev.xname));
strlcpy(sc->sc_sensor.desc, "core",
sizeof(sc->sc_sensor.desc));
sc->sc_sensor.type = SENSOR_TEMP;
sc->sc_sensor.flags = SENSOR_FINVALID;
sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
sensordev_install(&sc->sc_sensordev);
timeout_set(&sc->sc_sensorto, imxtemp_pickup_sensors, sc);
sensor_task_register(sc, imxtemp_refresh_sensors, 5);
}
int32_t
imxtemp_calc_temp(struct imxtemp_softc *sc, uint32_t temp_cnt)
{
int32_t value;
value = ((sc->sc_hot_temp - 25) * 1000000) /
(sc->sc_room_count - sc->sc_hot_count);
value *= (temp_cnt - sc->sc_hot_count);
return ((sc->sc_hot_temp * 1000000) - value);
}
void
imxtemp_refresh_sensors(void *arg)
{
struct imxtemp_softc *sc = (struct imxtemp_softc *)arg;
timeout_del(&sc->sc_sensorto);
HCLR4(sc, TEMPMON_TEMPSENSE0, TEMPMON_TEMPSENSE0_POWER_DOWN);
HSET4(sc, TEMPMON_TEMPSENSE0, TEMPMON_TEMPSENSE0_MEASURE_TEMP);
timeout_add_usec(&sc->sc_sensorto, 25);
}
void
imxtemp_pickup_sensors(void *arg)
{
struct imxtemp_softc *sc = (struct imxtemp_softc *)arg;
uint32_t value;
uint32_t temp_cnt;
value = HREAD4(sc, TEMPMON_TEMPSENSE0);
HCLR4(sc, TEMPMON_TEMPSENSE0, TEMPMON_TEMPSENSE0_MEASURE_TEMP);
HSET4(sc, TEMPMON_TEMPSENSE0, TEMPMON_TEMPSENSE0_POWER_DOWN);
if ((value & TEMPMON_TEMPSENSE0_FINISHED) == 0) {
sc->sc_sensor.flags |= SENSOR_FINVALID;
return;
}
temp_cnt = (value >> TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT) &
TEMPMON_TEMPSENSE0_TEMP_CNT_MASK;
sc->sc_sensor.value = imxtemp_calc_temp(sc, temp_cnt) + 273150000;
sc->sc_sensor.flags &= ~SENSOR_FINVALID;
}