1/*	$OpenBSD: sxits.c,v 1.3 2021/10/24 17:52:27 mpi 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/intr.h>
24#include <machine/bus.h>
25#include <machine/fdt.h>
26
27#include <dev/ofw/openfirm.h>
28#include <dev/ofw/ofw_thermal.h>
29#include <dev/ofw/fdt.h>
30
31/* Registers */
32#define TP_CTRL0			0x00
33#define  TP_CTRL0_ADC_CLK_DIVIDER(x)	(((x) & 0x3) << 20)
34#define  TP_CTRL0_FS_DIV(x)		(((x) & 0xf) << 16)
35#define  TP_CTRL0_TACQ(x)		((x) & 0xffff)
36#define TP_CTRL1			0x04
37#define  TP_CTRL1_TP_MODE_EN		(1 << 4)
38#define TP_CTRL3			0x0c
39#define  TP_CTRL3_FILTER_EN		(1 << 2)
40#define  TP_CTRL3_FILTER_TYPE(x)	((x) & 0x3)
41#define TP_TPR				0x18
42#define  TP_TPR_TEMP_EN			(1 << 16)
43#define  TP_TPR_TEMP_PER(x)		((x) & 0xffff)
44#define TEMP_DATA			0x20
45
46#define HREAD4(sc, reg)							\
47	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
48#define HWRITE4(sc, reg, val)						\
49	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
50
51struct sxits_softc {
52	struct device		sc_dev;
53	bus_space_tag_t		sc_iot;
54	bus_space_handle_t	sc_ioh;
55
56	uint16_t		sc_offset;
57	uint16_t		sc_scale;
58
59	struct ksensor		sc_sensor;
60	struct ksensordev	sc_sensordev;
61
62	struct thermal_sensor	sc_ts;
63};
64
65int	sxits_match(struct device *, void *, void *);
66void	sxits_attach(struct device *, struct device *, void *);
67
68const struct cfattach	sxits_ca = {
69	sizeof (struct sxits_softc), sxits_match, sxits_attach
70};
71
72struct cfdriver sxits_cd = {
73	NULL, "sxits", DV_DULL
74};
75
76void	sxits_refresh_sensors(void *);
77int32_t sxits_get_temperature(void *, uint32_t *);
78
79int
80sxits_match(struct device *parent, void *match, void *aux)
81{
82	struct fdt_attach_args *faa = aux;
83
84	return (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-ts") ||
85	    OF_is_compatible(faa->fa_node, "allwinner,sun5i-a13-ts"));
86}
87
88void
89sxits_attach(struct device *parent, struct device *self, void *aux)
90{
91	struct sxits_softc *sc = (struct sxits_softc *)self;
92	struct fdt_attach_args *faa = aux;
93
94	if (faa->fa_nreg < 1) {
95		printf(": no registers\n");
96		return;
97	}
98
99	sc->sc_iot = faa->fa_iot;
100	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
101	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
102		printf(": can't map registers\n");
103		return;
104	}
105
106	printf("\n");
107
108	if (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-ts")) {
109		sc->sc_offset = 1932;
110		sc->sc_scale = 133;
111	} else {
112		sc->sc_offset = 1447;
113		sc->sc_scale = 100;
114	}
115
116	/* Start data acquisition. */
117	HWRITE4(sc, TP_CTRL0, TP_CTRL0_ADC_CLK_DIVIDER(2) |
118	    TP_CTRL0_FS_DIV(7) | TP_CTRL0_TACQ(63));
119	HWRITE4(sc, TP_CTRL1, TP_CTRL1_TP_MODE_EN);
120	HWRITE4(sc, TP_CTRL3, TP_CTRL3_FILTER_EN | TP_CTRL3_FILTER_TYPE(1));
121	HWRITE4(sc, TP_TPR, TP_TPR_TEMP_EN | TP_TPR_TEMP_PER(800));
122
123	/* Register sensors. */
124	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
125	    sizeof(sc->sc_sensordev.xname));
126	sc->sc_sensor.type = SENSOR_TEMP;
127	sc->sc_sensor.flags = SENSOR_FINVALID;
128	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
129	sensordev_install(&sc->sc_sensordev);
130	sensor_task_register(sc, sxits_refresh_sensors, 5);
131
132	sc->sc_ts.ts_node = faa->fa_node;
133	sc->sc_ts.ts_cookie = sc;
134	sc->sc_ts.ts_get_temperature = sxits_get_temperature;
135	thermal_sensor_register(&sc->sc_ts);
136}
137
138void
139sxits_refresh_sensors(void *arg)
140{
141	struct sxits_softc *sc = arg;
142	uint32_t data, temp;
143
144	data = HREAD4(sc, TEMP_DATA);
145	if (data == 0) {
146		sc->sc_sensor.flags |= SENSOR_FINVALID;
147		return;
148	}
149
150	temp = (data - sc->sc_offset) * sc->sc_scale;
151	sc->sc_sensor.value = temp * 1000 + 273150000;
152	sc->sc_sensor.flags &= ~SENSOR_FINVALID;
153}
154
155int32_t
156sxits_get_temperature(void *cookie, uint32_t *cells)
157{
158	struct sxits_softc *sc = cookie;
159	uint32_t data;
160
161	data = HREAD4(sc, TEMP_DATA);
162	if (data == 0)
163		return THERMAL_SENSOR_MAX;
164
165	return (data - sc->sc_offset) * sc->sc_scale;
166}
167