xref: /openbsd-src/sys/dev/fdt/pwmfan.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
1*9fdf0c62Smpi /*	$OpenBSD: pwmfan.c,v 1.2 2021/10/24 17:52:26 mpi Exp $	*/
2572fa3f5Spatrick /*
3572fa3f5Spatrick  * Copyright (c) 2019 Krystian Lewandowski
4572fa3f5Spatrick  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
5572fa3f5Spatrick  * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
6572fa3f5Spatrick  *
7572fa3f5Spatrick  * Permission to use, copy, modify, and distribute this software for any
8572fa3f5Spatrick  * purpose with or without fee is hereby granted, provided that the above
9572fa3f5Spatrick  * copyright notice and this permission notice appear in all copies.
10572fa3f5Spatrick  *
11572fa3f5Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12572fa3f5Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13572fa3f5Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14572fa3f5Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15572fa3f5Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16572fa3f5Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17572fa3f5Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18572fa3f5Spatrick  */
19572fa3f5Spatrick 
20572fa3f5Spatrick #include <sys/param.h>
21572fa3f5Spatrick #include <sys/systm.h>
22572fa3f5Spatrick #include <sys/device.h>
23572fa3f5Spatrick #include <sys/malloc.h>
24572fa3f5Spatrick 
25572fa3f5Spatrick #include <machine/fdt.h>
26572fa3f5Spatrick #include <machine/bus.h>
27572fa3f5Spatrick 
28572fa3f5Spatrick #include <dev/ofw/openfirm.h>
29572fa3f5Spatrick #include <dev/ofw/ofw_gpio.h>
30572fa3f5Spatrick #include <dev/ofw/ofw_misc.h>
31572fa3f5Spatrick #include <dev/ofw/ofw_thermal.h>
32572fa3f5Spatrick 
33572fa3f5Spatrick struct pwmfan_softc {
34572fa3f5Spatrick 	struct device		sc_dev;
35572fa3f5Spatrick 	uint32_t		*sc_pwm;
36572fa3f5Spatrick 	int			sc_pwm_len;
37572fa3f5Spatrick 	uint32_t		*sc_levels;
38572fa3f5Spatrick 	int			sc_nlevels;
39572fa3f5Spatrick 	int			sc_curlevel;
40572fa3f5Spatrick 
41572fa3f5Spatrick 	struct cooling_device	sc_cd;
42572fa3f5Spatrick };
43572fa3f5Spatrick 
44572fa3f5Spatrick int	pwmfan_match(struct device *, void *, void *);
45572fa3f5Spatrick void	pwmfan_attach(struct device *, struct device *, void *);
46572fa3f5Spatrick 
47*9fdf0c62Smpi const struct cfattach pwmfan_ca = {
48572fa3f5Spatrick 	sizeof(struct pwmfan_softc), pwmfan_match, pwmfan_attach
49572fa3f5Spatrick };
50572fa3f5Spatrick 
51572fa3f5Spatrick struct cfdriver pwmfan_cd = {
52572fa3f5Spatrick 	NULL, "pwmfan", DV_DULL
53572fa3f5Spatrick };
54572fa3f5Spatrick 
55572fa3f5Spatrick uint32_t pwmfan_get_cooling_level(void *, uint32_t *);
56572fa3f5Spatrick void	pwmfan_set_cooling_level(void *, uint32_t *, uint32_t);
57572fa3f5Spatrick 
58572fa3f5Spatrick int
pwmfan_match(struct device * parent,void * match,void * aux)59572fa3f5Spatrick pwmfan_match(struct device *parent, void *match, void *aux)
60572fa3f5Spatrick {
61572fa3f5Spatrick 	struct fdt_attach_args *faa = aux;
62572fa3f5Spatrick 
63572fa3f5Spatrick 	return OF_is_compatible(faa->fa_node, "pwm-fan");
64572fa3f5Spatrick }
65572fa3f5Spatrick 
66572fa3f5Spatrick void
pwmfan_attach(struct device * parent,struct device * self,void * aux)67572fa3f5Spatrick pwmfan_attach(struct device *parent, struct device *self, void *aux)
68572fa3f5Spatrick {
69572fa3f5Spatrick 	struct pwmfan_softc *sc = (struct pwmfan_softc *)self;
70572fa3f5Spatrick 	struct fdt_attach_args *faa = aux;
71572fa3f5Spatrick 	int len;
72572fa3f5Spatrick 
73572fa3f5Spatrick 	len = OF_getproplen(faa->fa_node, "pwms");
74572fa3f5Spatrick 	if (len < 0) {
75572fa3f5Spatrick 		printf(": no pwm\n");
76572fa3f5Spatrick 		return;
77572fa3f5Spatrick 	}
78572fa3f5Spatrick 
79572fa3f5Spatrick 	sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
80572fa3f5Spatrick 	OF_getpropintarray(faa->fa_node, "pwms", sc->sc_pwm, len);
81572fa3f5Spatrick 	sc->sc_pwm_len = len;
82572fa3f5Spatrick 
83572fa3f5Spatrick 	len = OF_getproplen(faa->fa_node, "cooling-levels");
84572fa3f5Spatrick 	if (len < 0) {
85572fa3f5Spatrick 		free(sc->sc_pwm, M_DEVBUF, sc->sc_pwm_len);
86572fa3f5Spatrick 		printf(": no cooling levels\n");
87572fa3f5Spatrick 		return;
88572fa3f5Spatrick 	}
89572fa3f5Spatrick 
90572fa3f5Spatrick 	sc->sc_levels = malloc(len, M_DEVBUF, M_WAITOK);
91572fa3f5Spatrick 	OF_getpropintarray(faa->fa_node, "cooling-levels",
92572fa3f5Spatrick 	    sc->sc_levels, len);
93572fa3f5Spatrick 	sc->sc_nlevels = len / sizeof(uint32_t);
94572fa3f5Spatrick 
95572fa3f5Spatrick 	printf("\n");
96572fa3f5Spatrick 
97572fa3f5Spatrick 	sc->sc_cd.cd_node = faa->fa_node;
98572fa3f5Spatrick 	sc->sc_cd.cd_cookie = sc;
99572fa3f5Spatrick 	sc->sc_cd.cd_get_level = pwmfan_get_cooling_level;
100572fa3f5Spatrick 	sc->sc_cd.cd_set_level = pwmfan_set_cooling_level;
101572fa3f5Spatrick 	cooling_device_register(&sc->sc_cd);
102572fa3f5Spatrick }
103572fa3f5Spatrick 
104572fa3f5Spatrick uint32_t
pwmfan_get_cooling_level(void * cookie,uint32_t * cells)105572fa3f5Spatrick pwmfan_get_cooling_level(void *cookie, uint32_t *cells)
106572fa3f5Spatrick {
107572fa3f5Spatrick 	struct pwmfan_softc *sc = cookie;
108572fa3f5Spatrick 
109572fa3f5Spatrick 	return sc->sc_curlevel;
110572fa3f5Spatrick }
111572fa3f5Spatrick 
112572fa3f5Spatrick void
pwmfan_set_cooling_level(void * cookie,uint32_t * cells,uint32_t level)113572fa3f5Spatrick pwmfan_set_cooling_level(void *cookie, uint32_t *cells, uint32_t level)
114572fa3f5Spatrick {
115572fa3f5Spatrick 	struct pwmfan_softc *sc = cookie;
116572fa3f5Spatrick 	struct pwm_state ps;
117572fa3f5Spatrick 
118572fa3f5Spatrick 	if (level == sc->sc_curlevel || level > sc->sc_nlevels ||
119572fa3f5Spatrick 	    sc->sc_levels[level] > 255)
120572fa3f5Spatrick 		return;
121572fa3f5Spatrick 
122572fa3f5Spatrick 	if (pwm_init_state(sc->sc_pwm, &ps))
123572fa3f5Spatrick 		return;
124572fa3f5Spatrick 
125572fa3f5Spatrick 	sc->sc_curlevel = level;
126572fa3f5Spatrick 	level = sc->sc_levels[level];
127572fa3f5Spatrick 
128572fa3f5Spatrick 	ps.ps_enabled = level ? 1 : 0;
129572fa3f5Spatrick 	ps.ps_pulse_width = (ps.ps_period * level) / 255;
130572fa3f5Spatrick 	pwm_set_state(sc->sc_pwm, &ps);
131572fa3f5Spatrick }
132