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