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