1 /* $OpenBSD: imxtemp.c,v 1.8 2022/02/21 05:54:35 jsg Exp $ */
2 /*
3 * Copyright (c) 2014 Patrick Wildt <patrick@blueri.se>
4 * Copyright (c) 2016 Mark Kettenis <kettenis@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/sensors.h>
23 #include <sys/timeout.h>
24
25 #include <machine/fdt.h>
26
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/fdt.h>
29 #include <dev/ofw/ofw_misc.h>
30
31 /* registers */
32 #define TEMPMON_TEMPSENSE0 0x180
33 #define TEMPMON_TEMPSENSE0_SET 0x184
34 #define TEMPMON_TEMPSENSE0_CLR 0x188
35 #define TEMPMON_TEMPSENSE0_TOG 0x18c
36 #define TEMPMON_TEMPSENSE1 0x190
37 #define TEMPMON_TEMPSENSE1_SET 0x194
38 #define TEMPMON_TEMPSENSE1_CLR 0x198
39 #define TEMPMON_TEMPSENSE1_TOG 0x19c
40
41 /* bits and bytes */
42 #define TEMPMON_TEMPSENSE0_POWER_DOWN (1 << 0)
43 #define TEMPMON_TEMPSENSE0_MEASURE_TEMP (1 << 1)
44 #define TEMPMON_TEMPSENSE0_FINISHED (1 << 2)
45 #define TEMPMON_TEMPSENSE0_TEMP_CNT_MASK 0xfff
46 #define TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT 8
47 #define TEMPMON_TEMPSENSE0_ALARM_VALUE_MASK 0xfff
48 #define TEMPMON_TEMPSENSE0_ALARM_VALUE_SHIFT 20
49
50 /* calibration registers */
51 #define OCOTP_ANA1 0x4e0
52
53 /* calibration bits and bytes */
54 #define OCOTP_ANA1_HOT_TEMP_MASK 0xff
55 #define OCOTP_ANA1_HOT_TEMP_SHIFT 0
56 #define OCOTP_ANA1_HOT_COUNT_MASK 0xfff
57 #define OCOTP_ANA1_HOT_COUNT_SHIFT 8
58 #define OCOTP_ANA1_ROOM_COUNT_MASK 0xfff
59 #define OCOTP_ANA1_ROOM_COUNT_SHIFT 20
60
61 #define HREAD4(sc, reg) \
62 regmap_read_4((sc)->sc_rm, (reg))
63 #define HWRITE4(sc, reg, val) \
64 regmap_write_4((sc)->sc_rm, (reg), (val))
65 #define HSET4(sc, reg, bits) \
66 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
67 #define HCLR4(sc, reg, bits) \
68 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
69
70 struct imxtemp_softc {
71 struct device sc_dev;
72 int sc_node;
73 struct regmap *sc_rm;
74
75 uint32_t sc_hot_count;
76 uint32_t sc_hot_temp;
77 uint32_t sc_room_count;
78
79 struct ksensor sc_sensor;
80 struct ksensordev sc_sensordev;
81 struct timeout sc_sensorto;
82 };
83
84 int imxtemp_match(struct device *, void *, void *);
85 void imxtemp_attach(struct device *, struct device *, void *);
86
87 const struct cfattach imxtemp_ca = {
88 sizeof(struct imxtemp_softc), imxtemp_match, imxtemp_attach
89 };
90
91 struct cfdriver imxtemp_cd = {
92 NULL, "imxtemp", DV_DULL
93 };
94
95 void imxtemp_calibration(struct device *);
96 int32_t imxtemp_calc_temp(struct imxtemp_softc *, uint32_t);
97 void imxtemp_refresh_sensors(void *);
98 void imxtemp_pickup_sensors(void *);
99
100 int
imxtemp_match(struct device * parent,void * match,void * aux)101 imxtemp_match(struct device *parent, void *match, void *aux)
102 {
103 struct fdt_attach_args *faa = aux;
104
105 return OF_is_compatible(faa->fa_node, "fsl,imx6q-tempmon");
106 }
107
108 void
imxtemp_attach(struct device * parent,struct device * self,void * aux)109 imxtemp_attach(struct device *parent, struct device *self, void *aux)
110 {
111 struct imxtemp_softc *sc = (struct imxtemp_softc *)self;
112 struct fdt_attach_args *faa = aux;
113 uint32_t phandle;
114
115 sc->sc_node = faa->fa_node;
116 phandle = OF_getpropint(sc->sc_node, "fsl,tempmon", 0);
117 sc->sc_rm = regmap_byphandle(phandle);
118 if (sc->sc_rm == NULL)
119 return;
120
121 printf("\n");
122
123 config_mountroot(self, imxtemp_calibration);
124 }
125
126 void
imxtemp_calibration(struct device * self)127 imxtemp_calibration(struct device *self)
128 {
129 struct imxtemp_softc *sc = (struct imxtemp_softc *)self;
130 uint32_t calibration;
131 uint32_t phandle;
132 struct regmap *rm;
133
134 phandle = OF_getpropint(sc->sc_node, "fsl,tempmon-data", 0);
135 rm = regmap_byphandle(phandle);
136 if (rm == NULL)
137 return;
138
139 calibration = regmap_read_4(rm, OCOTP_ANA1);
140 sc->sc_hot_count = (calibration >> OCOTP_ANA1_HOT_COUNT_SHIFT) &
141 OCOTP_ANA1_HOT_COUNT_MASK;
142 sc->sc_hot_temp = (calibration >> OCOTP_ANA1_HOT_TEMP_SHIFT) &
143 OCOTP_ANA1_HOT_TEMP_MASK;
144 sc->sc_room_count = (calibration >> OCOTP_ANA1_ROOM_COUNT_SHIFT) &
145 OCOTP_ANA1_ROOM_COUNT_MASK;
146
147 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
148 sizeof(sc->sc_sensordev.xname));
149 strlcpy(sc->sc_sensor.desc, "core",
150 sizeof(sc->sc_sensor.desc));
151 sc->sc_sensor.type = SENSOR_TEMP;
152 sc->sc_sensor.flags = SENSOR_FINVALID;
153 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
154 sensordev_install(&sc->sc_sensordev);
155 timeout_set(&sc->sc_sensorto, imxtemp_pickup_sensors, sc);
156 sensor_task_register(sc, imxtemp_refresh_sensors, 5);
157 }
158
159 int32_t
imxtemp_calc_temp(struct imxtemp_softc * sc,uint32_t temp_cnt)160 imxtemp_calc_temp(struct imxtemp_softc *sc, uint32_t temp_cnt)
161 {
162 int32_t value;
163
164 /*
165 * Calculate the calibrated temperature based on the equation
166 * provided in the i.MX6 reference manual:
167 *
168 * Tmeas = HOT_TEMP - (Nmeas - HOT_COUNT) * ((HOT_TEMP - 25.0) /
169 * (ROOM_COUNT - HOT_COUNT))
170 *
171 * Note that we calculate the temperature in uC to avoid loss
172 * of precision.
173 */
174 value = ((sc->sc_hot_temp - 25) * 1000000) /
175 (sc->sc_room_count - sc->sc_hot_count);
176 value *= (temp_cnt - sc->sc_hot_count);
177 return ((sc->sc_hot_temp * 1000000) - value);
178 }
179
180 void
imxtemp_refresh_sensors(void * arg)181 imxtemp_refresh_sensors(void *arg)
182 {
183 struct imxtemp_softc *sc = (struct imxtemp_softc *)arg;
184
185 timeout_del(&sc->sc_sensorto);
186
187 /* Power on temperature sensor. */
188 HCLR4(sc, TEMPMON_TEMPSENSE0, TEMPMON_TEMPSENSE0_POWER_DOWN);
189 HSET4(sc, TEMPMON_TEMPSENSE0, TEMPMON_TEMPSENSE0_MEASURE_TEMP);
190
191 /* It may require up to ~17us to complete a measurement. */
192 timeout_add_usec(&sc->sc_sensorto, 25);
193 }
194
195 void
imxtemp_pickup_sensors(void * arg)196 imxtemp_pickup_sensors(void *arg)
197 {
198 struct imxtemp_softc *sc = (struct imxtemp_softc *)arg;
199 uint32_t value;
200 uint32_t temp_cnt;
201
202 value = HREAD4(sc, TEMPMON_TEMPSENSE0);
203
204 /* Power down temperature sensor. */
205 HCLR4(sc, TEMPMON_TEMPSENSE0, TEMPMON_TEMPSENSE0_MEASURE_TEMP);
206 HSET4(sc, TEMPMON_TEMPSENSE0, TEMPMON_TEMPSENSE0_POWER_DOWN);
207
208 if ((value & TEMPMON_TEMPSENSE0_FINISHED) == 0) {
209 sc->sc_sensor.flags |= SENSOR_FINVALID;
210 return;
211 }
212
213 temp_cnt = (value >> TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT) &
214 TEMPMON_TEMPSENSE0_TEMP_CNT_MASK;
215 sc->sc_sensor.value = imxtemp_calc_temp(sc, temp_cnt) + 273150000;
216 sc->sc_sensor.flags &= ~SENSOR_FINVALID;
217 }
218