1330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs/*
2330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * Copyright 2010 Red Hat Inc.
3330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs *
4330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * Permission is hereby granted, free of charge, to any person obtaining a
5330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * copy of this software and associated documentation files (the "Software"),
6330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * to deal in the Software without restriction, including without limitation
7330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * and/or sell copies of the Software, and to permit persons to whom the
9330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * Software is furnished to do so, subject to the following conditions:
10330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs *
11330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * The above copyright notice and this permission notice shall be included in
12330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * all copies or substantial portions of the Software.
13330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs *
14330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * OTHER DEALINGS IN THE SOFTWARE.
21330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs *
22330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs * Authors: Ben Skeggs
23330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs */
24330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
25330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs#include "drmP.h"
26330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
27330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs#include "nouveau_drv.h"
28330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs#include "nouveau_pm.h"
29a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs#include "nouveau_gpio.h"
30330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
316032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#ifdef CONFIG_ACPI
326032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#include <linux/acpi.h>
336032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
346032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#include <linux/power_supply.h>
3534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres#include <linux/hwmon.h>
3634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres#include <linux/hwmon-sysfs.h>
3734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
38330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic int
39a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggsnouveau_pwmfan_get(struct drm_device *dev)
40a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs{
41a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
42a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
43a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	struct gpio_func gpio;
44a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	u32 divs, duty;
45a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	int ret;
46a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
471e05415733b0d4668fbce92856fafabfa1a33333Ben Skeggs	if (!pm->pwm_get)
48a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		return -ENODEV;
49a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
50a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
51a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	if (ret == 0) {
52a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs		ret = pm->pwm_get(dev, gpio.line, &divs, &duty);
53a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		if (ret == 0) {
54a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs			divs = max(divs, duty);
55a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs			if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
56a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs				duty = divs - duty;
57a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs			return (duty * 100) / divs;
58a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		}
59a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
60a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs		return nouveau_gpio_func_get(dev, gpio.func) * 100;
61a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	}
62a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
63a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	return -ENODEV;
64a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs}
65a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
66a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggsstatic int
67a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggsnouveau_pwmfan_set(struct drm_device *dev, int percent)
68a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs{
69a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
70a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
71a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	struct gpio_func gpio;
72a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	u32 divs, duty;
73a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	int ret;
74a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
751e05415733b0d4668fbce92856fafabfa1a33333Ben Skeggs	if (!pm->pwm_set)
76a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		return -ENODEV;
77a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
78a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
79a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	if (ret == 0) {
80a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		divs = pm->pwm_divisor;
81a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		if (pm->fan.pwm_freq) {
82a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs			/*XXX: PNVIO clock more than likely... */
83a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs			divs = 135000 / pm->fan.pwm_freq;
84a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs			if (dev_priv->chipset < 0xa3)
85a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs				divs /= 4;
86a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		}
87a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
88a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		duty = ((divs * percent) + 99) / 100;
89a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs		if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
90a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs			duty = divs - duty;
91a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
92a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs		return pm->pwm_set(dev, gpio.line, divs, duty);
93a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	}
94a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
95a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	return -ENODEV;
96a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs}
97a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs
98a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggsstatic int
990b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggsnouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl,
1000b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs		       struct nouveau_pm_level *a, struct nouveau_pm_level *b)
10164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs{
10264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
10364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
10464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	int ret;
10564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
10611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	/*XXX: not on all boards, we should control based on temperature
10711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	 *     on recent boards..  or maybe on some other factor we don't
10811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	 *     know about?
10911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	 */
1100b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	if (a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) {
111a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		ret = nouveau_pwmfan_set(dev, perflvl->fanspeed);
1120b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs		if (ret && ret != -ENODEV) {
1130b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs			NV_ERROR(dev, "fanspeed set failed: %d\n", ret);
1140b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs			return ret;
1150b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs		}
116771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs	}
117771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs
1180b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	if (pm->voltage.supported && pm->voltage_set) {
119d2edab4acffb35a6e24259886d377774efd37e6eBen Skeggs		if (perflvl->volt_min && b->volt_min > a->volt_min) {
1200b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs			ret = pm->voltage_set(dev, perflvl->volt_min);
1210b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs			if (ret) {
1220b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs				NV_ERROR(dev, "voltage set failed: %d\n", ret);
1230b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs				return ret;
1240b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs			}
12564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		}
12664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	}
12764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
1280b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	return 0;
1290b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs}
1300b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs
1310b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggsstatic int
1320b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggsnouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
1330b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs{
1340b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
1350b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
1360b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	void *state;
1370b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	int ret;
1380b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs
1390b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	if (perflvl == pm->cur)
1400b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs		return 0;
1410b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs
1420b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl);
1430b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	if (ret)
1440b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs		return ret;
1450b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs
146ff2b6c6e587cf2add3071b3a9a5c61abbbaf4677Ben Skeggs	state = pm->clocks_pre(dev, perflvl);
147ff2b6c6e587cf2add3071b3a9a5c61abbbaf4677Ben Skeggs	if (IS_ERR(state))
148ff2b6c6e587cf2add3071b3a9a5c61abbbaf4677Ben Skeggs		return PTR_ERR(state);
149ff2b6c6e587cf2add3071b3a9a5c61abbbaf4677Ben Skeggs	pm->clocks_set(dev, state);
15064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
1510b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
1520b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs	if (ret)
1530b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs		return ret;
1540b627a0b23404d97d1720c0c1abaee602aee9518Ben Skeggs
15564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	pm->cur = perflvl;
15664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	return 0;
15764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs}
15864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
15964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsstatic int
1606f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsnouveau_pm_profile_set(struct drm_device *dev, const char *profile)
1616f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs{
1626f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
1636f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
1646f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_level *perflvl = NULL;
1656f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1666f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	/* safety precaution, for now */
1676f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (nouveau_perflvl_wr != 7777)
1686f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return -EPERM;
1696f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1706f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (!strncmp(profile, "boot", 4))
1716f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		perflvl = &pm->boot;
1726f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	else {
1736f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		int pl = simple_strtol(profile, NULL, 10);
1746f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		int i;
1756f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1766f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		for (i = 0; i < pm->nr_perflvl; i++) {
1776f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			if (pm->perflvl[i].id == pl) {
1786f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs				perflvl = &pm->perflvl[i];
1796f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs				break;
1806f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			}
1816f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		}
1826f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1836f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		if (!perflvl)
1846f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			return -EINVAL;
1856f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	}
1866f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1876f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	NV_INFO(dev, "setting performance level: %s\n", profile);
18864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	return nouveau_pm_perflvl_set(dev, perflvl);
1896f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs}
1906f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1916f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsstatic int
192330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
193330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
194330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
195330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
196330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int ret;
197330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
198330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	memset(perflvl, 0, sizeof(*perflvl));
199330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
200ff2b6c6e587cf2add3071b3a9a5c61abbbaf4677Ben Skeggs	ret = pm->clocks_get(dev, perflvl);
201ff2b6c6e587cf2add3071b3a9a5c61abbbaf4677Ben Skeggs	if (ret)
202ff2b6c6e587cf2add3071b3a9a5c61abbbaf4677Ben Skeggs		return ret;
203330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
204330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (pm->voltage.supported && pm->voltage_get) {
205330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		ret = pm->voltage_get(dev);
2063b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		if (ret > 0) {
2073b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs			perflvl->volt_min = ret;
2083b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs			perflvl->volt_max = ret;
2093b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		}
210330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
211330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
212a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	ret = nouveau_pwmfan_get(dev);
213a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	if (ret > 0)
214a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs		perflvl->fanspeed = ret;
215771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs
216330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return 0;
217330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
218330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
219330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic void
220330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
221330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
22293dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	char c[16], s[16], v[32], f[16], t[16], m[16];
2230fbb114af7ea63227599460c412fb8796556a169Francisco Jerez
2240fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	c[0] = '\0';
2250fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	if (perflvl->core)
2260fbb114af7ea63227599460c412fb8796556a169Francisco Jerez		snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
227330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
228330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	s[0] = '\0';
229330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->shader)
230330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
231330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
23293dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	m[0] = '\0';
23393dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	if (perflvl->memory)
23493dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
23593dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs
236330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	v[0] = '\0';
2373b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
2383b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		snprintf(v, sizeof(v), " voltage %dmV-%dmV",
2393b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs			 perflvl->volt_min / 1000, perflvl->volt_max / 1000);
2403b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	} else
2413b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	if (perflvl->volt_min) {
2423b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		snprintf(v, sizeof(v), " voltage %dmV",
2433b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs			 perflvl->volt_min / 1000);
2443b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	}
245330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
246330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	f[0] = '\0';
247330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->fanspeed)
248330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
249330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
250e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	t[0] = '\0';
251e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	if (perflvl->timing)
252e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres		snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
253e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres
25493dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
255330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
256330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
257330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
258330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_get_perflvl_info(struct device *d,
259330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			    struct device_attribute *a, char *buf)
260330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
261330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
262330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	char *ptr = buf;
263330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int len = PAGE_SIZE;
264330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
26593dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	snprintf(ptr, len, "%d:", perflvl->id);
266330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ptr += strlen(buf);
267330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	len -= strlen(buf);
268330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
269330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_pm_perflvl_info(perflvl, ptr, len);
270330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return strlen(buf);
271330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
272330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
273330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
274330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
275330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
27634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
277330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
278330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
279330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_level cur;
280330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int len = PAGE_SIZE, ret;
281330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	char *ptr = buf;
282330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
283330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (!pm->cur)
284330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(ptr, len, "setting: boot\n");
285330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	else if (pm->cur == &pm->boot)
28693dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		snprintf(ptr, len, "setting: boot\nc:");
287330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	else
28893dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
289330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ptr += strlen(buf);
290330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	len -= strlen(buf);
291330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
292330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = nouveau_pm_perflvl_get(dev, &cur);
293330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret == 0)
294330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		nouveau_pm_perflvl_info(&cur, ptr, len);
295330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return strlen(buf);
296330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
297330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
298330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
299330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
300330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		       const char *buf, size_t count)
301330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
30234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
3036f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	int ret;
3046f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
3056f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	ret = nouveau_pm_profile_set(dev, buf);
3066f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (ret)
3076f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return ret;
3086f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	return strlen(buf);
309330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
310330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
3115c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerezstatic DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
3125c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez		   nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
313330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
31434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic int
31534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_sysfs_init(struct drm_device *dev)
316330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
317330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
318330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
319330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct device *d = &dev->pdev->dev;
320330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int ret, i;
321330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
322330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = device_create_file(d, &dev_attr_performance_level);
323330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret)
324330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		return ret;
325330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
326330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	for (i = 0; i < pm->nr_perflvl; i++) {
327330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		struct nouveau_pm_level *perflvl = &pm->perflvl[i];
328330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
329330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.attr.name = perflvl->name;
330330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.attr.mode = S_IRUGO;
331330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
332330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.store = NULL;
333330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		sysfs_attr_init(&perflvl->dev_attr.attr);
334330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
335330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		ret = device_create_file(d, &perflvl->dev_attr);
336330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (ret) {
337330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
338330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs				 perflvl->id, i);
339330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			perflvl->dev_attr.attr.name = NULL;
340330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			nouveau_pm_fini(dev);
341330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			return ret;
342330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		}
343330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
344330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
345330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return 0;
346330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
347330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
34834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic void
34934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_sysfs_fini(struct drm_device *dev)
350330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
351330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
352330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
353330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct device *d = &dev->pdev->dev;
354330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int i;
355330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
356330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	device_remove_file(d, &dev_attr_performance_level);
357330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	for (i = 0; i < pm->nr_perflvl; i++) {
358330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		struct nouveau_pm_level *pl = &pm->perflvl[i];
359330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
360330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (!pl->dev_attr.attr.name)
361330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			break;
362330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
363330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		device_remove_file(d, &pl->dev_attr);
364330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
36534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
36634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
367658e86ee2db9500aea529a04008cce10972416bcKen Milmore#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
36834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
36934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
37034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
37134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
3728155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct drm_nouveau_private *dev_priv = dev->dev_private;
3738155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
37434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3758155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
37634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
37734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
37834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  NULL, 0);
37934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
38034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
38134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
38234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
38334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
38434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
38534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
38634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
38734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
38834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
38934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
39034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
39134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
39234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						const char *buf, size_t count)
39334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
39434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
39534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
39634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
39734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
39834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	long value;
39934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4005c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez	if (strict_strtol(buf, 10, &value) == -EINVAL)
40134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return count;
40234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
40334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	temp->down_clock = value/1000;
40434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
40534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_safety_checks(dev);
40634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
40734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return count;
40834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
40934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
41034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  nouveau_hwmon_set_max_temp,
41134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  0);
41234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
41334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
41434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
41534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres							char *buf)
41634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
41734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
41834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
41934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
42034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
42134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
42234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
42334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
42434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
42534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
42634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres							    const char *buf,
42734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres								size_t count)
42834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
42934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
43034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
43134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
43234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
43334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	long value;
43434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4355c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez	if (strict_strtol(buf, 10, &value) == -EINVAL)
43634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return count;
43734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
43834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	temp->critical = value/1000;
43934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
44034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_safety_checks(dev);
44134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
44234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return count;
44334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
44434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
44534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_critical_temp,
44634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_set_critical_temp,
44734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						0);
44834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
44934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t nouveau_hwmon_show_name(struct device *dev,
45034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      struct device_attribute *attr,
45134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      char *buf)
45234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
45334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return sprintf(buf, "nouveau\n");
45434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
45534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
45634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
45734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
45834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      struct device_attribute *attr,
45934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      char *buf)
46034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
46134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return sprintf(buf, "1000\n");
46234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
46334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
46434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_show_update_rate,
46534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						NULL, 0);
46634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
46711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic ssize_t
46811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresnouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr,
46911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			      char *buf)
47011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres{
47111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
47211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
47311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
474a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	struct gpio_func gpio;
47511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	u32 cycles, cur, prev;
47611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	u64 start;
477a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	int ret;
47811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
479a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	ret = nouveau_gpio_find(dev, 0, DCB_GPIO_FAN_SENSE, 0xff, &gpio);
480a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	if (ret)
481a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs		return ret;
48211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
48311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	/* Monitor the GPIO input 0x3b for 250ms.
48411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	 * When the fan spins, it changes the value of GPIO FAN_SENSE.
48511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	 * We get 4 changes (0 -> 1 -> 0 -> 1 -> [...]) per complete rotation.
48611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	 */
48711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	start = ptimer->read(dev);
488a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	prev = nouveau_gpio_sense(dev, 0, gpio.line);
48911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	cycles = 0;
49011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	do {
491a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs		cur = nouveau_gpio_sense(dev, 0, gpio.line);
49211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		if (prev != cur) {
49311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			cycles++;
49411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			prev = cur;
49511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		}
49611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
49711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		usleep_range(500, 1000); /* supports 0 < rpm < 7500 */
49811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	} while (ptimer->read(dev) - start < 250000000);
49911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
50011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	/* interpolate to get rpm */
50111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return sprintf(buf, "%i\n", cycles / 4 * 4 * 60);
50211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres}
50311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input,
50411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			  NULL, 0);
50511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
50611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic ssize_t
50711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresnouveau_hwmon_get_pwm0(struct device *d, struct device_attribute *a, char *buf)
50811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres{
50911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
510a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	int ret;
51111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
512a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	ret = nouveau_pwmfan_get(dev);
51311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (ret < 0)
51411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		return ret;
51511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
51611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return sprintf(buf, "%i\n", ret);
51711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres}
51811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
51911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic ssize_t
52011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresnouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a,
52111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		       const char *buf, size_t count)
52211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres{
52311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
52411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
52511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
52611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	int ret = -ENODEV;
52711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	long value;
52811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
52911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (nouveau_perflvl_wr != 7777)
53011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		return -EPERM;
53111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
53211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (strict_strtol(buf, 10, &value) == -EINVAL)
53311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		return -EINVAL;
53411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
53511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (value < pm->fan.min_duty)
53611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		value = pm->fan.min_duty;
53711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (value > pm->fan.max_duty)
53811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		value = pm->fan.max_duty;
53911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
540a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	ret = nouveau_pwmfan_set(dev, value);
54111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (ret)
54211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		return ret;
54311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
54411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return count;
54511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres}
54611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
54711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic SENSOR_DEVICE_ATTR(pwm0, S_IRUGO | S_IWUSR,
54811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			  nouveau_hwmon_get_pwm0,
54911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			  nouveau_hwmon_set_pwm0, 0);
55011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
55111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic ssize_t
55211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresnouveau_hwmon_get_pwm0_min(struct device *d,
55311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			   struct device_attribute *a, char *buf)
55411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres{
55511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
55611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
55711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
55811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
55911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return sprintf(buf, "%i\n", pm->fan.min_duty);
56011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres}
56111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
56211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic ssize_t
56311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresnouveau_hwmon_set_pwm0_min(struct device *d, struct device_attribute *a,
56411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			   const char *buf, size_t count)
56511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres{
56611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
56711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
56811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
56911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	long value;
57011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
57111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (strict_strtol(buf, 10, &value) == -EINVAL)
57211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		return -EINVAL;
57311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
57411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (value < 0)
57511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		value = 0;
57611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
57711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (pm->fan.max_duty - value < 10)
57811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		value = pm->fan.max_duty - 10;
57911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
58011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (value < 10)
58111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		pm->fan.min_duty = 10;
58211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	else
58311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		pm->fan.min_duty = value;
58411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
58511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return count;
58611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres}
58711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
58811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic SENSOR_DEVICE_ATTR(pwm0_min, S_IRUGO | S_IWUSR,
58911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			  nouveau_hwmon_get_pwm0_min,
59011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			  nouveau_hwmon_set_pwm0_min, 0);
59111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
59211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic ssize_t
59311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresnouveau_hwmon_get_pwm0_max(struct device *d,
59411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			   struct device_attribute *a, char *buf)
59511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres{
59611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
59711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
59811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
59911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
60011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return sprintf(buf, "%i\n", pm->fan.max_duty);
60111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres}
60211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
60311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic ssize_t
60411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresnouveau_hwmon_set_pwm0_max(struct device *d, struct device_attribute *a,
60511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			   const char *buf, size_t count)
60611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres{
60711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
60811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
60911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
61011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	long value;
61111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
61211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (strict_strtol(buf, 10, &value) == -EINVAL)
61311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		return -EINVAL;
61411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
61511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (value < 0)
61611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		value = 0;
61711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
61811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (value - pm->fan.min_duty < 10)
61911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		value = pm->fan.min_duty + 10;
62011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
62111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	if (value > 100)
62211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		pm->fan.max_duty = 100;
62311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	else
62411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		pm->fan.max_duty = value;
62511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
62611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return count;
62711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres}
62811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
62911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic SENSOR_DEVICE_ATTR(pwm0_max, S_IRUGO | S_IWUSR,
63011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			  nouveau_hwmon_get_pwm0_max,
63111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			  nouveau_hwmon_set_pwm0_max, 0);
63211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
63334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic struct attribute *hwmon_attributes[] = {
63434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_input.dev_attr.attr,
63534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_max.dev_attr.attr,
63634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_crit.dev_attr.attr,
63734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_name.dev_attr.attr,
63834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_update_rate.dev_attr.attr,
63934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	NULL
64034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres};
64111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic struct attribute *hwmon_fan_rpm_attributes[] = {
64211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	&sensor_dev_attr_fan0_input.dev_attr.attr,
64311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	NULL
64411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres};
64511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic struct attribute *hwmon_pwm_fan_attributes[] = {
64611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	&sensor_dev_attr_pwm0.dev_attr.attr,
64711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	&sensor_dev_attr_pwm0_min.dev_attr.attr,
64811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	&sensor_dev_attr_pwm0_max.dev_attr.attr,
64911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	NULL
65011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres};
65134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
65234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic const struct attribute_group hwmon_attrgroup = {
65334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	.attrs = hwmon_attributes,
65434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres};
65511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic const struct attribute_group hwmon_fan_rpm_attrgroup = {
65611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	.attrs = hwmon_fan_rpm_attributes,
65711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres};
65811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peresstatic const struct attribute_group hwmon_pwm_fan_attrgroup = {
65911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	.attrs = hwmon_pwm_fan_attributes,
66011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres};
661b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#endif
66234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
66334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic int
66434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_init(struct drm_device *dev)
66534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
66634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
6678155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
668095f979a539245a46b9e5d600ec9c720b4d928e5Dave Airlie#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
66934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct device *hwmon_dev;
67011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	int ret = 0;
67134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
6728155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	if (!pm->temp_get)
6738155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		return -ENODEV;
67434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
67534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	hwmon_dev = hwmon_device_register(&dev->pdev->dev);
67634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (IS_ERR(hwmon_dev)) {
67734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		ret = PTR_ERR(hwmon_dev);
67834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_ERROR(dev,
67934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			"Unable to register hwmon device: %d\n", ret);
68034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return ret;
68134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
68234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	dev_set_drvdata(hwmon_dev, dev);
68311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
68411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	/* default sysfs entries */
68507cfe0e7a820ecad078c04e9c2a102521709145dLucas Stach	ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
68634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (ret) {
68711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		if (ret)
68811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			goto error;
68911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	}
69011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
69111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	/* if the card has a pwm fan */
69211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	/*XXX: incorrect, need better detection for this, some boards have
69311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	 *     the gpio entries for pwm fan control even when there's no
69411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	 *     actual fan connected to it... therm table? */
695a175094cd8f3d46060d8e3510bdca57eb2369a86Ben Skeggs	if (nouveau_pwmfan_get(dev) >= 0) {
69611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		ret = sysfs_create_group(&dev->pdev->dev.kobj,
69711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres					 &hwmon_pwm_fan_attrgroup);
69811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		if (ret)
69911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			goto error;
70011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	}
70111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
70211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	/* if the card can read the fan rpm */
703a0b25635515ef5049f93b032a1e37f18b16e0f6fBen Skeggs	if (nouveau_gpio_func_valid(dev, DCB_GPIO_FAN_SENSE)) {
70411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		ret = sysfs_create_group(&dev->pdev->dev.kobj,
70511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres					 &hwmon_fan_rpm_attrgroup);
70611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		if (ret)
70711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres			goto error;
70834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
70934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
7108155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	pm->hwmon = hwmon_dev;
71111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
71234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return 0;
71311b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
71411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Pereserror:
71511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	NV_ERROR(dev, "Unable to create some hwmon sysfs files: %d\n", ret);
71611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	hwmon_device_unregister(hwmon_dev);
71711b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	pm->hwmon = NULL;
71811b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return ret;
71911b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres#else
72011b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	pm->hwmon = NULL;
72111b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres	return 0;
72211b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres#endif
72334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
72434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
72534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic void
72634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_fini(struct drm_device *dev)
72734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
728658e86ee2db9500aea529a04008cce10972416bcKen Milmore#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
72934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
7308155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
73134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
7328155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	if (pm->hwmon) {
7338c06a3e02062a9beb71a9444c49fb0fbcaa1eed3Lucas Stach		sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
73411b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup);
73511b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres		sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup);
73611b7d895216f7f954c6cfa0c23b76dccb7a890c1Martin Peres
7378155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		hwmon_device_unregister(pm->hwmon);
73834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
739b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#endif
74034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
74134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
7421f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
7436032649df9f456f379be8d51f64488cacbfa8317Ben Skeggsstatic int
7446032649df9f456f379be8d51f64488cacbfa8317Ben Skeggsnouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
7456032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs{
7466032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct drm_nouveau_private *dev_priv =
7476032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb);
7486032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct drm_device *dev = dev_priv->dev;
7496032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
7506032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
7516032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	if (strcmp(entry->device_class, "ac_adapter") == 0) {
7526032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		bool ac = power_supply_is_system_supplied();
7536032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
7546032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
7556032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	}
7566032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
7576032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	return NOTIFY_OK;
7586032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs}
7596032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
7606032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
76134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresint
76234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_pm_init(struct drm_device *dev)
76334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
76434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
76534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
76634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	char info[256];
76734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	int ret, i;
76834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
769e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	nouveau_mem_timing_init(dev);
77034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_volt_init(dev);
77134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_perf_init(dev);
77234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_init(dev);
77334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
77434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
77534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	for (i = 0; i < pm->nr_perflvl; i++) {
77634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
77793dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
77834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
77934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
78034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	/* determine current ("boot") performance level */
78134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	ret = nouveau_pm_perflvl_get(dev, &pm->boot);
78234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (ret == 0) {
78301e542c65de11a47e726ebef63f5e59b4a74568dMartin Peres		strncpy(pm->boot.name, "boot", 4);
78434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		pm->cur = &pm->boot;
78534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
78634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
78793dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		NV_INFO(dev, "c:%s", info);
78834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
78934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
79034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	/* switch performance levels now if requested */
79134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (nouveau_perflvl != NULL) {
79234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
79334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		if (ret) {
79434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
79534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				 nouveau_perflvl, ret);
79634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		}
79734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
79834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
79934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_sysfs_init(dev);
80034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_hwmon_init(dev);
8011f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
8026032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
8036032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	register_acpi_notifier(&pm->acpi_nb);
8046032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
80534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
80634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return 0;
80734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
80834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
80934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresvoid
81034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_pm_fini(struct drm_device *dev)
81134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
81234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
81334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
81434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
81534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (pm->cur != &pm->boot)
81634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_set(dev, &pm->boot);
817330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
8187760fcb020b41352af4e675ce65a6aa0e93c170fRoy Spliet	nouveau_temp_fini(dev);
819330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_perf_fini(dev);
820330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_volt_fini(dev);
821e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	nouveau_mem_timing_fini(dev);
82234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
8231f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
8246032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	unregister_acpi_notifier(&pm->acpi_nb);
8256032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
82634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_hwmon_fini(dev);
82734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_sysfs_fini(dev);
828330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
829330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
83064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsvoid
83164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsnouveau_pm_resume(struct drm_device *dev)
83264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs{
83364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
83464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
83564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_level *perflvl;
83664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
837317495b25ec1f0beb0dbac8ee0dfec59a1addf03Ben Skeggs	if (!pm->cur || pm->cur == &pm->boot)
83864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		return;
83964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
84064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	perflvl = pm->cur;
84164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	pm->cur = &pm->boot;
84264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	nouveau_pm_perflvl_set(dev, perflvl);
84364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs}
844