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