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 33struct 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 44int pwmfan_match(struct device *, void *, void *); 45void pwmfan_attach(struct device *, struct device *, void *); 46 47const struct cfattach pwmfan_ca = { 48 sizeof(struct pwmfan_softc), pwmfan_match, pwmfan_attach 49}; 50 51struct cfdriver pwmfan_cd = { 52 NULL, "pwmfan", DV_DULL 53}; 54 55uint32_t pwmfan_get_cooling_level(void *, uint32_t *); 56void pwmfan_set_cooling_level(void *, uint32_t *, uint32_t); 57 58int 59pwmfan_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 66void 67pwmfan_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 104uint32_t 105pwmfan_get_cooling_level(void *cookie, uint32_t *cells) 106{ 107 struct pwmfan_softc *sc = cookie; 108 109 return sc->sc_curlevel; 110} 111 112void 113pwmfan_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