1/* $NetBSD: nouveau_nvkm_subdev_therm_fanpwm.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $ */ 2 3/* 4 * Copyright 2012 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs 25 * Martin Peres 26 */ 27#include <sys/cdefs.h> 28__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_therm_fanpwm.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $"); 29 30#include "priv.h" 31 32#include <core/option.h> 33#include <subdev/bios.h> 34#include <subdev/bios/fan.h> 35#include <subdev/gpio.h> 36 37struct nvkm_fanpwm { 38 struct nvkm_fan base; 39 struct dcb_gpio_func func; 40}; 41 42static int 43nvkm_fanpwm_get(struct nvkm_therm *therm) 44{ 45 struct nvkm_fanpwm *fan = (void *)therm->fan; 46 struct nvkm_device *device = therm->subdev.device; 47 struct nvkm_gpio *gpio = device->gpio; 48 int card_type = device->card_type; 49 u32 divs, duty; 50 int ret; 51 52 ret = therm->func->pwm_get(therm, fan->func.line, &divs, &duty); 53 if (ret == 0 && divs) { 54 divs = max(divs, duty); 55 if (card_type <= NV_40 || (fan->func.log[0] & 1)) 56 duty = divs - duty; 57 return (duty * 100) / divs; 58 } 59 60 return nvkm_gpio_get(gpio, 0, fan->func.func, fan->func.line) * 100; 61} 62 63static int 64nvkm_fanpwm_set(struct nvkm_therm *therm, int percent) 65{ 66 struct nvkm_fanpwm *fan = (void *)therm->fan; 67 int card_type = therm->subdev.device->card_type; 68 u32 divs, duty; 69 int ret; 70 71 divs = fan->base.perf.pwm_divisor; 72 if (fan->base.bios.pwm_freq) { 73 divs = 1; 74 if (therm->func->pwm_clock) 75 divs = therm->func->pwm_clock(therm, fan->func.line); 76 divs /= fan->base.bios.pwm_freq; 77 } 78 79 duty = ((divs * percent) + 99) / 100; 80 if (card_type <= NV_40 || (fan->func.log[0] & 1)) 81 duty = divs - duty; 82 83 ret = therm->func->pwm_set(therm, fan->func.line, divs, duty); 84 if (ret == 0) 85 ret = therm->func->pwm_ctrl(therm, fan->func.line, true); 86 return ret; 87} 88 89int 90nvkm_fanpwm_create(struct nvkm_therm *therm, struct dcb_gpio_func *func) 91{ 92 struct nvkm_device *device = therm->subdev.device; 93 struct nvkm_bios *bios = device->bios; 94 struct nvkm_fanpwm *fan; 95 struct nvbios_therm_fan info = {}; 96 u32 divs, duty; 97 98 nvbios_fan_parse(bios, &info); 99 100 if (!nvkm_boolopt(device->cfgopt, "NvFanPWM", func->param) || 101 !therm->func->pwm_ctrl || info.type == NVBIOS_THERM_FAN_TOGGLE || 102 therm->func->pwm_get(therm, func->line, &divs, &duty) == -ENODEV) 103 return -ENODEV; 104 105 fan = kzalloc(sizeof(*fan), GFP_KERNEL); 106 therm->fan = &fan->base; 107 if (!fan) 108 return -ENOMEM; 109 110 fan->base.type = "PWM"; 111 fan->base.get = nvkm_fanpwm_get; 112 fan->base.set = nvkm_fanpwm_set; 113 fan->func = *func; 114 return 0; 115} 116