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 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 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 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 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