From 0cbf83bbe524d6014daf11005b99084d50489b80 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Mon, 3 Sep 2012 01:37:02 +0200 Subject: [PATCH] drm/nouveau/fan: add toggle fan support v2: change percent from int to atomic_t v3: random fixes v4 (Ben Skeggs): - adapted for split-out fan-control "protocol" structure - removed need for timer resched - support for forcing 'toggle' control on PWM boards Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/Makefile | 1 + .../gpu/drm/nouveau/core/include/subdev/timer.h | 8 ++ drivers/gpu/drm/nouveau/core/subdev/therm/fan.c | 11 +- drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c | 117 +++++++++++++++++++++ drivers/gpu/drm/nouveau/core/subdev/therm/priv.h | 2 + drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c | 2 +- 6 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index c58d66c..21c5d9a 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -108,6 +108,7 @@ nouveau-y += core/subdev/therm/base.o nouveau-y += core/subdev/therm/fan.o nouveau-y += core/subdev/therm/fannil.o nouveau-y += core/subdev/therm/fanpwm.o +nouveau-y += core/subdev/therm/fantog.o nouveau-y += core/subdev/therm/ic.o nouveau-y += core/subdev/therm/temp.o nouveau-y += core/subdev/therm/nv40.o diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/timer.h b/drivers/gpu/drm/nouveau/core/include/subdev/timer.h index c24ec8a..e465d15 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/timer.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/timer.h @@ -10,6 +10,14 @@ struct nouveau_alarm { void (*func)(struct nouveau_alarm *); }; +static inline void +nouveau_alarm_init(struct nouveau_alarm *alarm, + void (*func)(struct nouveau_alarm *)) +{ + INIT_LIST_HEAD(&alarm->head); + alarm->func = func; +} + bool nouveau_timer_wait_eq(void *, u64 nsec, u32 addr, u32 mask, u32 data); bool nouveau_timer_wait_ne(void *, u64 nsec, u32 addr, u32 mask, u32 data); bool nouveau_timer_wait_cb(void *, u64 nsec, bool (*func)(void *), void *data); diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c index e2fbddd..aed72a2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c @@ -149,7 +149,6 @@ nouveau_therm_fan_set_defaults(struct nouveau_therm *therm) priv->fan->bios.max_duty = 100; } - static void nouveau_therm_fan_safety_checks(struct nouveau_therm *therm) { @@ -164,11 +163,6 @@ nouveau_therm_fan_safety_checks(struct nouveau_therm *therm) priv->fan->bios.min_duty = priv->fan->bios.max_duty; } -int nouveau_fan_pwm_clock_dummy(struct nouveau_therm *therm) -{ - return 1; -} - int nouveau_therm_fan_ctor(struct nouveau_therm *therm) { @@ -182,7 +176,9 @@ nouveau_therm_fan_ctor(struct nouveau_therm *therm) ret = gpio->find(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, &func); if (ret == 0) ret = nouveau_fanpwm_create(therm, &func); - if (ret) + if (ret != 0) + ret = nouveau_fantog_create(therm, &func); + if (ret != 0) ret = nouveau_fannil_create(therm); if (ret) return ret; @@ -202,6 +198,5 @@ nouveau_therm_fan_ctor(struct nouveau_therm *therm) nouveau_therm_fan_safety_checks(therm); nouveau_therm_fan_set_mode(therm, FAN_CONTROL_NONE); - return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c new file mode 100644 index 0000000..b08e4e2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c @@ -0,0 +1,117 @@ +/* + * 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 +#include + +#include +#include + +struct nouveau_fantog_priv { + struct nouveau_fan base; + struct nouveau_therm *parent; + struct nouveau_alarm alarm; + spinlock_t lock; + u32 period_us; + u32 percent; + struct dcb_gpio_func func; +}; + +static void +nouveau_fantog_update(struct nouveau_fantog_priv *priv, int percent) +{ + struct nouveau_therm_priv *tpriv = (void *)priv->parent; + struct nouveau_timer *ptimer = nouveau_timer(tpriv); + struct nouveau_gpio *gpio = nouveau_gpio(tpriv); + unsigned long flags; + int duty; + + spin_lock_irqsave(&priv->lock, flags); + if (percent < 0) + percent = priv->percent; + priv->percent = percent; + + duty = !gpio->get(gpio, 0, DCB_GPIO_PWM_FAN, 0xff); + gpio->set(gpio, 0, DCB_GPIO_PWM_FAN, 0xff, duty); + + if (list_empty(&priv->alarm.head) && percent != (duty * 100)) { + u64 next_change = (percent * priv->period_us) / 100; + if (!duty) + next_change = priv->period_us - next_change; + ptimer->alarm(ptimer, next_change * 1000, &priv->alarm); + } + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void +nouveau_fantog_alarm(struct nouveau_alarm *alarm) +{ + struct nouveau_fantog_priv *priv = + container_of(alarm, struct nouveau_fantog_priv, alarm); + nouveau_fantog_update(priv, -1); +} + +static int +nouveau_fantog_get(struct nouveau_therm *therm) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fantog_priv *priv = (void *)tpriv->fan; + return priv->percent; +} + +int +nouveau_fantog_set(struct nouveau_therm *therm, int percent) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fantog_priv *priv = (void *)tpriv->fan; + if (therm->pwm_ctrl) + therm->pwm_ctrl(therm, priv->func.line, false); + nouveau_fantog_update(priv, percent); + return 0; +} + +int +nouveau_fantog_create(struct nouveau_therm *therm, struct dcb_gpio_func *func) +{ + struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_fantog_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + tpriv->fan = &priv->base; + if (!priv) + return -ENOMEM; + + priv->parent = therm; + priv->base.type = "toggle"; + priv->base.get = nouveau_fantog_get; + priv->base.set = nouveau_fantog_set; + nouveau_alarm_init(&priv->alarm, nouveau_fantog_alarm); + priv->period_us = 100000; /* 10Hz */ + priv->percent = 100; + priv->func = *func; + spin_lock_init(&priv->lock); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h index fa93932..34c85e6 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h @@ -31,6 +31,7 @@ #include #include #include +#include struct nouveau_fan { const char *type; @@ -89,6 +90,7 @@ int nv50_temp_get(struct nouveau_therm *therm); int nva3_therm_fan_sense(struct nouveau_therm *); int nouveau_fanpwm_create(struct nouveau_therm *, struct dcb_gpio_func *); +int nouveau_fantog_create(struct nouveau_therm *, struct dcb_gpio_func *); int nouveau_fannil_create(struct nouveau_therm *); #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c index c26ca9b..8e1bae4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/timer/nv04.c @@ -79,7 +79,7 @@ nv04_timer_alarm_trigger(struct nouveau_timer *ptimer) /* execute any pending alarm handlers */ list_for_each_entry_safe(alarm, atemp, &exec, head) { - list_del(&alarm->head); + list_del_init(&alarm->head); alarm->func(alarm); } } -- 2.7.4