xref: /openbsd-src/sys/dev/fdt/pwmfan.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
1 /*	$OpenBSD: pwmfan.c,v 1.2 2021/10/24 17:52:26 mpi Exp $	*/
2 /*
3  * Copyright (c) 2019 Krystian Lewandowski
4  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
5  * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 #include <sys/malloc.h>
24 
25 #include <machine/fdt.h>
26 #include <machine/bus.h>
27 
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/ofw_gpio.h>
30 #include <dev/ofw/ofw_misc.h>
31 #include <dev/ofw/ofw_thermal.h>
32 
33 struct pwmfan_softc {
34 	struct device		sc_dev;
35 	uint32_t		*sc_pwm;
36 	int			sc_pwm_len;
37 	uint32_t		*sc_levels;
38 	int			sc_nlevels;
39 	int			sc_curlevel;
40 
41 	struct cooling_device	sc_cd;
42 };
43 
44 int	pwmfan_match(struct device *, void *, void *);
45 void	pwmfan_attach(struct device *, struct device *, void *);
46 
47 const struct cfattach pwmfan_ca = {
48 	sizeof(struct pwmfan_softc), pwmfan_match, pwmfan_attach
49 };
50 
51 struct cfdriver pwmfan_cd = {
52 	NULL, "pwmfan", DV_DULL
53 };
54 
55 uint32_t pwmfan_get_cooling_level(void *, uint32_t *);
56 void	pwmfan_set_cooling_level(void *, uint32_t *, uint32_t);
57 
58 int
pwmfan_match(struct device * parent,void * match,void * aux)59 pwmfan_match(struct device *parent, void *match, void *aux)
60 {
61 	struct fdt_attach_args *faa = aux;
62 
63 	return OF_is_compatible(faa->fa_node, "pwm-fan");
64 }
65 
66 void
pwmfan_attach(struct device * parent,struct device * self,void * aux)67 pwmfan_attach(struct device *parent, struct device *self, void *aux)
68 {
69 	struct pwmfan_softc *sc = (struct pwmfan_softc *)self;
70 	struct fdt_attach_args *faa = aux;
71 	int len;
72 
73 	len = OF_getproplen(faa->fa_node, "pwms");
74 	if (len < 0) {
75 		printf(": no pwm\n");
76 		return;
77 	}
78 
79 	sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
80 	OF_getpropintarray(faa->fa_node, "pwms", sc->sc_pwm, len);
81 	sc->sc_pwm_len = len;
82 
83 	len = OF_getproplen(faa->fa_node, "cooling-levels");
84 	if (len < 0) {
85 		free(sc->sc_pwm, M_DEVBUF, sc->sc_pwm_len);
86 		printf(": no cooling levels\n");
87 		return;
88 	}
89 
90 	sc->sc_levels = malloc(len, M_DEVBUF, M_WAITOK);
91 	OF_getpropintarray(faa->fa_node, "cooling-levels",
92 	    sc->sc_levels, len);
93 	sc->sc_nlevels = len / sizeof(uint32_t);
94 
95 	printf("\n");
96 
97 	sc->sc_cd.cd_node = faa->fa_node;
98 	sc->sc_cd.cd_cookie = sc;
99 	sc->sc_cd.cd_get_level = pwmfan_get_cooling_level;
100 	sc->sc_cd.cd_set_level = pwmfan_set_cooling_level;
101 	cooling_device_register(&sc->sc_cd);
102 }
103 
104 uint32_t
pwmfan_get_cooling_level(void * cookie,uint32_t * cells)105 pwmfan_get_cooling_level(void *cookie, uint32_t *cells)
106 {
107 	struct pwmfan_softc *sc = cookie;
108 
109 	return sc->sc_curlevel;
110 }
111 
112 void
pwmfan_set_cooling_level(void * cookie,uint32_t * cells,uint32_t level)113 pwmfan_set_cooling_level(void *cookie, uint32_t *cells, uint32_t level)
114 {
115 	struct pwmfan_softc *sc = cookie;
116 	struct pwm_state ps;
117 
118 	if (level == sc->sc_curlevel || level > sc->sc_nlevels ||
119 	    sc->sc_levels[level] > 255)
120 		return;
121 
122 	if (pwm_init_state(sc->sc_pwm, &ps))
123 		return;
124 
125 	sc->sc_curlevel = level;
126 	level = sc->sc_levels[level];
127 
128 	ps.ps_enabled = level ? 1 : 0;
129 	ps.ps_pulse_width = (ps.ps_period * level) / 255;
130 	pwm_set_state(sc->sc_pwm, &ps);
131 }
132