xref: /openbsd-src/sys/dev/fdt/mvtemp.c (revision 12fbe0774cc89ffbd690f7e96ad30cd11636bf73)
1 /*	$OpenBSD: mvtemp.c,v 1.5 2023/04/18 08:35:02 patrick Exp $	*/
2 /*
3  * Copyright (c) 2018 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/device.h>
21 #include <sys/sensors.h>
22 
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_misc.h>
28 #include <dev/ofw/fdt.h>
29 
30 /* Registers */
31 #define TEMP_STAT			0x0000
32 #define TEMP_CTRL0			0x0000
33 #define  TEMP_CTRL0_TSEN_TC_TRIM_MASK	0x7
34 #define  TEMP_CTRL0_TSEN_TC_TRIM_VAL	0x3
35 #define  TEMP_CTRL0_TSEN_START		(1 << 0)
36 #define  TEMP_CTRL0_TSEN_RESET		(1 << 1)
37 #define  TEMP_CTRL0_TSEN_ENABLE		(1 << 2)
38 #define  TEMP_CTRL0_TSEN_AVG_BYPASS	(1 << 6)
39 #define  TEMP_CTRL0_TSEN_OSR_MAX	(0x3 << 24)
40 #define TEMP_CTRL1			0x0004
41 #define  TMEP_CTRL1_TSEN_START		(1 << 0)
42 #define  TEMP_CTRL1_TSEN_SW_RESET	(1 << 7)
43 #define  TEMP_CTRL1_TSEN_HW_RESETN	(1 << 8)
44 #define  TEMP_CTRL1_TSEN_AVG_MASK	0x7
45 
46 #define REG_CTRL0			0
47 #define REG_CTRL1			1
48 #define REG_STAT			2
49 #define REG_MAX				3
50 
51 struct mvtemp_softc {
52 	struct device		sc_dev;
53 	bus_space_tag_t		sc_iot;
54 	bus_space_handle_t	sc_stat_ioh;
55 	bus_space_handle_t	sc_ctrl_ioh;
56 	struct regmap		*sc_rm;
57 
58 	uint32_t		sc_stat_valid;
59 	int32_t			(*sc_calc_temp)(uint32_t);
60 	bus_size_t		sc_offs[REG_MAX];
61 
62 	struct ksensor		sc_sensor;
63 	struct ksensordev	sc_sensordev;
64 };
65 
66 int	mvtemp_match(struct device *, void *, void *);
67 void	mvtemp_attach(struct device *, struct device *, void *);
68 
69 const struct cfattach	mvtemp_ca = {
70 	sizeof (struct mvtemp_softc), mvtemp_match, mvtemp_attach
71 };
72 
73 struct cfdriver mvtemp_cd = {
74 	NULL, "mvtemp", DV_DULL
75 };
76 
77 struct mvtemp_compat {
78 	const char *compat;
79 	uint32_t stat_valid;
80 	void	(*init)(struct mvtemp_softc *);
81 	int32_t	(*calc_temp)(uint32_t);
82 	bus_size_t offs[REG_MAX];
83 };
84 
85 void	mvtemp_380_init(struct mvtemp_softc *);
86 void	mvtemp_ap806_init(struct mvtemp_softc *);
87 int32_t mvtemp_ap806_calc_temp(uint32_t);
88 void	mvtemp_cp110_init(struct mvtemp_softc *);
89 int32_t mvtemp_cp110_calc_temp(uint32_t);
90 
91 const struct mvtemp_compat mvtemp_compat[] = {
92 	{
93 		"marvell,armada-ap806-thermal", (1 << 16),
94 		mvtemp_ap806_init, mvtemp_ap806_calc_temp,
95 		{ 0x84, 0x88, 0x8c },
96 	},
97 	{
98 		"marvell,armada-cp110-thermal", (1 << 10),
99 		mvtemp_cp110_init, mvtemp_cp110_calc_temp,
100 		{ 0x70, 0x74, 0x78 },
101 	},
102 	{
103 		"marvell,armada380-thermal", (1 << 10),
104 		mvtemp_380_init, mvtemp_cp110_calc_temp,
105 		{ 0x70, 0x74, 0x78 },
106 	},
107 };
108 
109 void	mvtemp_refresh_sensors(void *);
110 
111 int
mvtemp_match(struct device * parent,void * match,void * aux)112 mvtemp_match(struct device *parent, void *match, void *aux)
113 {
114 	struct fdt_attach_args *faa = aux;
115 	int i;
116 
117 	for (i = 0; i < nitems(mvtemp_compat); i++) {
118 		if (OF_is_compatible(faa->fa_node, mvtemp_compat[i].compat))
119 			return 1;
120 	}
121 
122 	return 0;
123 }
124 
125 void
mvtemp_attach(struct device * parent,struct device * self,void * aux)126 mvtemp_attach(struct device *parent, struct device *self, void *aux)
127 {
128 	struct mvtemp_softc *sc = (struct mvtemp_softc *)self;
129 	struct fdt_attach_args *faa = aux;
130 	int i;
131 
132 	if (faa->fa_nreg >= 2) {
133 		sc->sc_iot = faa->fa_iot;
134 		if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
135 		    faa->fa_reg[0].size, 0, &sc->sc_stat_ioh)) {
136 			printf(": can't map registers\n");
137 			return;
138 		}
139 		if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
140 		    faa->fa_reg[1].size, 0, &sc->sc_ctrl_ioh)) {
141 			bus_space_unmap(sc->sc_iot, sc->sc_stat_ioh,
142 			    faa->fa_reg[0].size);
143 			printf(": can't map registers\n");
144 			return;
145 		}
146 	} else {
147 		sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node));
148 		if (sc->sc_rm == NULL) {
149 			printf(": no registers\n");
150 			return;
151 		}
152 	}
153 
154 	printf("\n");
155 
156 	for (i = 0; i < nitems(mvtemp_compat); i++) {
157 		if (OF_is_compatible(faa->fa_node, mvtemp_compat[i].compat)) {
158 			break;
159 		}
160 	}
161 	KASSERT(i < nitems(mvtemp_compat));
162 
163 	/* Legacy offsets */
164 	sc->sc_offs[REG_CTRL0] = TEMP_CTRL0;
165 	sc->sc_offs[REG_CTRL1] = TEMP_CTRL1;
166 	sc->sc_offs[REG_STAT] = TEMP_STAT;
167 
168 	/* Syscon offsets */
169 	if (sc->sc_rm != NULL) {
170 		sc->sc_offs[REG_CTRL0] = mvtemp_compat[i].offs[REG_CTRL0];
171 		sc->sc_offs[REG_CTRL1] = mvtemp_compat[i].offs[REG_CTRL1];
172 		sc->sc_offs[REG_STAT] = mvtemp_compat[i].offs[REG_STAT];
173 	}
174 
175 	mvtemp_compat[i].init(sc);
176 	sc->sc_stat_valid = mvtemp_compat[i].stat_valid;
177 	sc->sc_calc_temp = mvtemp_compat[i].calc_temp;
178 
179 	/* Register sensors. */
180 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
181 	    sizeof(sc->sc_sensordev.xname));
182 	sc->sc_sensor.type = SENSOR_TEMP;
183 	sc->sc_sensor.flags = SENSOR_FINVALID;
184 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
185 	sensordev_install(&sc->sc_sensordev);
186 	sensor_task_register(sc, mvtemp_refresh_sensors, 5);
187 }
188 
189 uint32_t
mvtemp_read(struct mvtemp_softc * sc,int reg)190 mvtemp_read(struct mvtemp_softc *sc, int reg)
191 {
192 	if (sc->sc_rm != NULL)
193 		return regmap_read_4(sc->sc_rm, sc->sc_offs[reg]);
194 	else if (reg == REG_STAT)
195 		return bus_space_read_4(sc->sc_iot, sc->sc_stat_ioh, sc->sc_offs[reg]);
196 	else
197 		return bus_space_read_4(sc->sc_iot, sc->sc_ctrl_ioh, sc->sc_offs[reg]);
198 }
199 
200 void
mvtemp_write(struct mvtemp_softc * sc,int reg,uint32_t val)201 mvtemp_write(struct mvtemp_softc *sc, int reg, uint32_t val)
202 {
203 	if (sc->sc_rm != NULL)
204 		regmap_write_4(sc->sc_rm, sc->sc_offs[reg], val);
205 	else if (reg == REG_STAT)
206 		bus_space_write_4(sc->sc_iot, sc->sc_stat_ioh, sc->sc_offs[reg], val);
207 	else
208 		bus_space_write_4(sc->sc_iot, sc->sc_ctrl_ioh, sc->sc_offs[reg], val);
209 }
210 
211 /* 380 */
212 
213 void
mvtemp_380_init(struct mvtemp_softc * sc)214 mvtemp_380_init(struct mvtemp_softc *sc)
215 {
216 	uint32_t ctrl;
217 
218 	ctrl = mvtemp_read(sc, REG_CTRL1);
219 	ctrl |= TEMP_CTRL1_TSEN_HW_RESETN;
220 	ctrl &= ~TEMP_CTRL1_TSEN_SW_RESET;
221 	mvtemp_write(sc, REG_CTRL1, ctrl);
222 
223 	ctrl = mvtemp_read(sc, REG_CTRL0);
224 	ctrl &= ~TEMP_CTRL0_TSEN_TC_TRIM_MASK;
225 	ctrl |= TEMP_CTRL0_TSEN_TC_TRIM_VAL;
226 	mvtemp_write(sc, REG_CTRL0, ctrl);
227 }
228 
229 /* AP806 */
230 
231 void
mvtemp_ap806_init(struct mvtemp_softc * sc)232 mvtemp_ap806_init(struct mvtemp_softc *sc)
233 {
234 	uint32_t ctrl;
235 
236 	ctrl = mvtemp_read(sc, REG_CTRL0);
237 	ctrl &= ~TEMP_CTRL0_TSEN_RESET;
238 	ctrl |= TEMP_CTRL0_TSEN_START | TEMP_CTRL0_TSEN_ENABLE;
239 	ctrl |= TEMP_CTRL0_TSEN_OSR_MAX;
240 	ctrl &= ~TEMP_CTRL0_TSEN_AVG_BYPASS;
241 	mvtemp_write(sc, REG_CTRL0, ctrl);
242 }
243 
244 int32_t
mvtemp_ap806_calc_temp(uint32_t stat)245 mvtemp_ap806_calc_temp(uint32_t stat)
246 {
247 	stat = ((stat & 0x3ff) ^ 0x200) - 0x200;
248 	return (stat * 423000) + 150000000 + 273150000;
249 }
250 
251 /* CP110 */
252 
253 void
mvtemp_cp110_init(struct mvtemp_softc * sc)254 mvtemp_cp110_init(struct mvtemp_softc *sc)
255 {
256 	uint32_t ctrl;
257 
258 	mvtemp_380_init(sc);
259 
260 	ctrl = mvtemp_read(sc, REG_CTRL0);
261 	ctrl |= TEMP_CTRL0_TSEN_OSR_MAX;
262 	mvtemp_write(sc, REG_CTRL0, ctrl);
263 
264 	ctrl = mvtemp_read(sc, REG_CTRL1);
265 	ctrl |= TMEP_CTRL1_TSEN_START;
266 	ctrl &= ~TEMP_CTRL1_TSEN_AVG_MASK;
267 	mvtemp_write(sc, REG_CTRL1, ctrl);
268 }
269 
270 int32_t
mvtemp_cp110_calc_temp(uint32_t stat)271 mvtemp_cp110_calc_temp(uint32_t stat)
272 {
273 	return ((stat & 0x3ff) * 476100) - 279100000 + 273150000;
274 }
275 
276 void
mvtemp_refresh_sensors(void * arg)277 mvtemp_refresh_sensors(void *arg)
278 {
279 	struct mvtemp_softc *sc = arg;
280 	int32_t stat, temp;
281 
282 	stat = mvtemp_read(sc, REG_STAT);
283 	temp = sc->sc_calc_temp(stat);
284 	sc->sc_sensor.value = temp;
285 	if ((stat & sc->sc_stat_valid) && temp >= 0)
286 		sc->sc_sensor.flags &= ~SENSOR_FINVALID;
287 	else
288 		sc->sc_sensor.flags |= SENSOR_FINVALID;
289 }
290