diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c new file mode 100644 index 000000000..ff9fbe795 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c @@ -0,0 +1,116 @@ +/* + * Copyright 2012 The Nouveau community + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Martin Peres + */ +#include "priv.h" + +#include <subdev/gpio.h> +#include <subdev/timer.h> + +struct nvkm_fantog { + struct nvkm_fan base; + struct nvkm_alarm alarm; + spinlock_t lock; + u32 period_us; + u32 percent; + struct dcb_gpio_func func; +}; + +static void +nvkm_fantog_update(struct nvkm_fantog *fan, int percent) +{ + struct nvkm_therm *therm = fan->base.parent; + struct nvkm_device *device = therm->subdev.device; + struct nvkm_timer *tmr = device->timer; + struct nvkm_gpio *gpio = device->gpio; + unsigned long flags; + int duty; + + spin_lock_irqsave(&fan->lock, flags); + if (percent < 0) + percent = fan->percent; + fan->percent = percent; + + duty = !nvkm_gpio_get(gpio, 0, DCB_GPIO_FAN, 0xff); + nvkm_gpio_set(gpio, 0, DCB_GPIO_FAN, 0xff, duty); + + if (percent != (duty * 100)) { + u64 next_change = (percent * fan->period_us) / 100; + if (!duty) + next_change = fan->period_us - next_change; + nvkm_timer_alarm(tmr, next_change * 1000, &fan->alarm); + } + spin_unlock_irqrestore(&fan->lock, flags); +} + +static void +nvkm_fantog_alarm(struct nvkm_alarm *alarm) +{ + struct nvkm_fantog *fan = + container_of(alarm, struct nvkm_fantog, alarm); + nvkm_fantog_update(fan, -1); +} + +static int +nvkm_fantog_get(struct nvkm_therm *therm) +{ + struct nvkm_fantog *fan = (void *)therm->fan; + return fan->percent; +} + +static int +nvkm_fantog_set(struct nvkm_therm *therm, int percent) +{ + struct nvkm_fantog *fan = (void *)therm->fan; + if (therm->func->pwm_ctrl) + therm->func->pwm_ctrl(therm, fan->func.line, false); + nvkm_fantog_update(fan, percent); + return 0; +} + +int +nvkm_fantog_create(struct nvkm_therm *therm, struct dcb_gpio_func *func) +{ + struct nvkm_fantog *fan; + int ret; + + if (therm->func->pwm_ctrl) { + ret = therm->func->pwm_ctrl(therm, func->line, false); + if (ret) + return ret; + } + + fan = kzalloc(sizeof(*fan), GFP_KERNEL); + therm->fan = &fan->base; + if (!fan) + return -ENOMEM; + + fan->base.type = "toggle"; + fan->base.get = nvkm_fantog_get; + fan->base.set = nvkm_fantog_set; + nvkm_alarm_init(&fan->alarm, nvkm_fantog_alarm); + fan->period_us = 100000; /* 10Hz */ + fan->percent = 100; + fan->func = *func; + spin_lock_init(&fan->lock); + return 0; +} |