xref: /openbsd-src/sys/arch/armv7/imx/imxtemp.c (revision f7b0f15d77d860c9fe33fa0f7e2e22bf08ff5f55)
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