nouveau_pm.c revision 771e1035b9bfdb0c3f0e34bd281d73b721a10adb
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"
29330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
306032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#ifdef CONFIG_ACPI
316032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#include <linux/acpi.h>
326032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
336032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#include <linux/power_supply.h>
3434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres#include <linux/hwmon.h>
3534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres#include <linux/hwmon-sysfs.h>
3634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
37330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic int
385c6dc6575460a0afe56d8cae7666e769e08ef942Ben Skeggsnouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl,
395c6dc6575460a0afe56d8cae7666e769e08ef942Ben Skeggs		     u8 id, u32 khz)
406f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs{
416f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
426f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
436f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	void *pre_state;
446f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
456f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (khz == 0)
466f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return 0;
476f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
485c6dc6575460a0afe56d8cae7666e769e08ef942Ben Skeggs	pre_state = pm->clock_pre(dev, perflvl, id, khz);
496f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (IS_ERR(pre_state))
506f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return PTR_ERR(pre_state);
516f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
526f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (pre_state)
536f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		pm->clock_set(dev, pre_state);
546f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	return 0;
556f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs}
566f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
576f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsstatic int
5864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsnouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
5964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs{
6064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
6164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
6264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	int ret;
6364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
6464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	if (perflvl == pm->cur)
6564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		return 0;
6664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
67771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs	if (pm->fanspeed_set && perflvl->fanspeed) {
68771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs		ret = pm->fanspeed_set(dev, perflvl->fanspeed);
69771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs		if (ret)
70771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs			NV_ERROR(dev, "set fanspeed failed: %d\n", ret);
71771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs	}
72771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs
733b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
743b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		ret = pm->voltage_set(dev, perflvl->volt_min);
7564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		if (ret) {
7664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs			NV_ERROR(dev, "voltage_set %d failed: %d\n",
773b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs				 perflvl->volt_min, ret);
7864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		}
7964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	}
8064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
8177e7da6814623927cc4435d992bef9c84075594cBen Skeggs	if (pm->clocks_pre) {
8277e7da6814623927cc4435d992bef9c84075594cBen Skeggs		void *state = pm->clocks_pre(dev, perflvl);
8377e7da6814623927cc4435d992bef9c84075594cBen Skeggs		if (IS_ERR(state))
8477e7da6814623927cc4435d992bef9c84075594cBen Skeggs			return PTR_ERR(state);
8577e7da6814623927cc4435d992bef9c84075594cBen Skeggs		pm->clocks_set(dev, state);
8677e7da6814623927cc4435d992bef9c84075594cBen Skeggs	} else
87da1dc4cfecdf314241cc5e0c5df1f66b4cc80cc7Ben Skeggs	if (pm->clock_set) {
88da1dc4cfecdf314241cc5e0c5df1f66b4cc80cc7Ben Skeggs		nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
89da1dc4cfecdf314241cc5e0c5df1f66b4cc80cc7Ben Skeggs		nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
90da1dc4cfecdf314241cc5e0c5df1f66b4cc80cc7Ben Skeggs		nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
91da1dc4cfecdf314241cc5e0c5df1f66b4cc80cc7Ben Skeggs		nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
92da1dc4cfecdf314241cc5e0c5df1f66b4cc80cc7Ben Skeggs	}
9364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
9464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	pm->cur = perflvl;
9564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	return 0;
9664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs}
9764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
9864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsstatic int
996f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsnouveau_pm_profile_set(struct drm_device *dev, const char *profile)
1006f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs{
1016f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
1026f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
1036f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_level *perflvl = NULL;
1046f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1056f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	/* safety precaution, for now */
1066f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (nouveau_perflvl_wr != 7777)
1076f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return -EPERM;
1086f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1096f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (!strncmp(profile, "boot", 4))
1106f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		perflvl = &pm->boot;
1116f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	else {
1126f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		int pl = simple_strtol(profile, NULL, 10);
1136f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		int i;
1146f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1156f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		for (i = 0; i < pm->nr_perflvl; i++) {
1166f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			if (pm->perflvl[i].id == pl) {
1176f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs				perflvl = &pm->perflvl[i];
1186f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs				break;
1196f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			}
1206f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		}
1216f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1226f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		if (!perflvl)
1236f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			return -EINVAL;
1246f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	}
1256f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1266f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	NV_INFO(dev, "setting performance level: %s\n", profile);
12764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	return nouveau_pm_perflvl_set(dev, perflvl);
1286f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs}
1296f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1306f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsstatic int
131330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
132330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
133330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
134330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
135330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int ret;
136330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
137330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	memset(perflvl, 0, sizeof(*perflvl));
138330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
13977e7da6814623927cc4435d992bef9c84075594cBen Skeggs	if (pm->clocks_get) {
14077e7da6814623927cc4435d992bef9c84075594cBen Skeggs		ret = pm->clocks_get(dev, perflvl);
14177e7da6814623927cc4435d992bef9c84075594cBen Skeggs		if (ret)
14277e7da6814623927cc4435d992bef9c84075594cBen Skeggs			return ret;
14377e7da6814623927cc4435d992bef9c84075594cBen Skeggs	} else
14493dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	if (pm->clock_get) {
14593dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		ret = pm->clock_get(dev, PLL_CORE);
14693dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		if (ret > 0)
14793dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs			perflvl->core = ret;
148330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
14993dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		ret = pm->clock_get(dev, PLL_MEMORY);
15093dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		if (ret > 0)
15193dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs			perflvl->memory = ret;
152330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
15393dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		ret = pm->clock_get(dev, PLL_SHADER);
15493dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		if (ret > 0)
15593dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs			perflvl->shader = ret;
156330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
15793dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		ret = pm->clock_get(dev, PLL_UNK05);
15893dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		if (ret > 0)
15993dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs			perflvl->unk05 = ret;
16093dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	}
161330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
162330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (pm->voltage.supported && pm->voltage_get) {
163330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		ret = pm->voltage_get(dev);
1643b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		if (ret > 0) {
1653b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs			perflvl->volt_min = ret;
1663b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs			perflvl->volt_max = ret;
1673b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		}
168330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
169330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
170771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs	if (pm->fanspeed_get)
171771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs		perflvl->fanspeed = pm->fanspeed_get(dev);
172771e1035b9bfdb0c3f0e34bd281d73b721a10adbBen Skeggs
173330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return 0;
174330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
175330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
176330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic void
177330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
178330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
17993dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	char c[16], s[16], v[32], f[16], t[16], m[16];
1800fbb114af7ea63227599460c412fb8796556a169Francisco Jerez
1810fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	c[0] = '\0';
1820fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	if (perflvl->core)
1830fbb114af7ea63227599460c412fb8796556a169Francisco Jerez		snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
184330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
185330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	s[0] = '\0';
186330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->shader)
187330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
188330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
18993dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	m[0] = '\0';
19093dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	if (perflvl->memory)
19193dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
19293dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs
193330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	v[0] = '\0';
1943b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
1953b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		snprintf(v, sizeof(v), " voltage %dmV-%dmV",
1963b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs			 perflvl->volt_min / 1000, perflvl->volt_max / 1000);
1973b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	} else
1983b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	if (perflvl->volt_min) {
1993b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs		snprintf(v, sizeof(v), " voltage %dmV",
2003b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs			 perflvl->volt_min / 1000);
2013b5565ddfd8fe71f6470a5d240a6bb50ba90d4ffBen Skeggs	}
202330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
203330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	f[0] = '\0';
204330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->fanspeed)
205330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
206330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
207e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	t[0] = '\0';
208e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	if (perflvl->timing)
209e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres		snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
210e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres
21193dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
212330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
213330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
214330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
215330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_get_perflvl_info(struct device *d,
216330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			    struct device_attribute *a, char *buf)
217330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
218330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
219330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	char *ptr = buf;
220330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int len = PAGE_SIZE;
221330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
22293dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs	snprintf(ptr, len, "%d:", perflvl->id);
223330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ptr += strlen(buf);
224330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	len -= strlen(buf);
225330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
226330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_pm_perflvl_info(perflvl, ptr, len);
227330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return strlen(buf);
228330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
229330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
230330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
231330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
232330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
23334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
234330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
235330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
236330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_level cur;
237330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int len = PAGE_SIZE, ret;
238330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	char *ptr = buf;
239330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
240330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (!pm->cur)
241330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(ptr, len, "setting: boot\n");
242330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	else if (pm->cur == &pm->boot)
24393dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		snprintf(ptr, len, "setting: boot\nc:");
244330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	else
24593dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
246330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ptr += strlen(buf);
247330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	len -= strlen(buf);
248330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
249330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = nouveau_pm_perflvl_get(dev, &cur);
250330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret == 0)
251330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		nouveau_pm_perflvl_info(&cur, ptr, len);
252330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return strlen(buf);
253330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
254330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
255330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
256330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
257330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		       const char *buf, size_t count)
258330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
25934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
2606f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	int ret;
2616f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
2626f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	ret = nouveau_pm_profile_set(dev, buf);
2636f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (ret)
2646f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return ret;
2656f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	return strlen(buf);
266330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
267330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
2685c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerezstatic DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
2695c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez		   nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
270330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
27134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic int
27234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_sysfs_init(struct drm_device *dev)
273330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
274330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
275330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
276330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct device *d = &dev->pdev->dev;
277330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int ret, i;
278330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
279330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = device_create_file(d, &dev_attr_performance_level);
280330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret)
281330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		return ret;
282330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
283330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	for (i = 0; i < pm->nr_perflvl; i++) {
284330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		struct nouveau_pm_level *perflvl = &pm->perflvl[i];
285330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
286330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.attr.name = perflvl->name;
287330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.attr.mode = S_IRUGO;
288330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
289330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.store = NULL;
290330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		sysfs_attr_init(&perflvl->dev_attr.attr);
291330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
292330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		ret = device_create_file(d, &perflvl->dev_attr);
293330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (ret) {
294330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
295330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs				 perflvl->id, i);
296330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			perflvl->dev_attr.attr.name = NULL;
297330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			nouveau_pm_fini(dev);
298330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			return ret;
299330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		}
300330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
301330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
302330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return 0;
303330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
304330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
30534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic void
30634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_sysfs_fini(struct drm_device *dev)
307330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
308330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
309330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
310330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct device *d = &dev->pdev->dev;
311330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int i;
312330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
313330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	device_remove_file(d, &dev_attr_performance_level);
314330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	for (i = 0; i < pm->nr_perflvl; i++) {
315330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		struct nouveau_pm_level *pl = &pm->perflvl[i];
316330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
317330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (!pl->dev_attr.attr.name)
318330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			break;
319330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
320330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		device_remove_file(d, &pl->dev_attr);
321330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
32234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
32334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
324658e86ee2db9500aea529a04008cce10972416bcKen Milmore#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
32534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
32634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
32734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
32834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
3298155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct drm_nouveau_private *dev_priv = dev->dev_private;
3308155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
33134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3328155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
33334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
33434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
33534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  NULL, 0);
33634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
33734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
33834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
33934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
34034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
34134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
34234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
34334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
34434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
34534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
34634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
34734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
34834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
34934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						const char *buf, size_t count)
35034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
35134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
35234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
35334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
35434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
35534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	long value;
35634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3575c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez	if (strict_strtol(buf, 10, &value) == -EINVAL)
35834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return count;
35934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
36034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	temp->down_clock = value/1000;
36134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
36234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_safety_checks(dev);
36334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
36434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return count;
36534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
36634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
36734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  nouveau_hwmon_set_max_temp,
36834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  0);
36934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
37034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
37134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
37234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres							char *buf)
37334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
37434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
37534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
37634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
37734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
37834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
37934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
38034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
38134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
38234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
38334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres							    const char *buf,
38434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres								size_t count)
38534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
38634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
38734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
38834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
38934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
39034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	long value;
39134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3925c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez	if (strict_strtol(buf, 10, &value) == -EINVAL)
39334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return count;
39434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
39534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	temp->critical = value/1000;
39634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
39734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_safety_checks(dev);
39834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
39934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return count;
40034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
40134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
40234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_critical_temp,
40334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_set_critical_temp,
40434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						0);
40534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
40634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t nouveau_hwmon_show_name(struct device *dev,
40734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      struct device_attribute *attr,
40834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      char *buf)
40934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
41034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return sprintf(buf, "nouveau\n");
41134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
41234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
41334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
41434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
41534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      struct device_attribute *attr,
41634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      char *buf)
41734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
41834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return sprintf(buf, "1000\n");
41934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
42034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
42134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_show_update_rate,
42234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						NULL, 0);
42334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
42434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic struct attribute *hwmon_attributes[] = {
42534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_input.dev_attr.attr,
42634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_max.dev_attr.attr,
42734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_crit.dev_attr.attr,
42834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_name.dev_attr.attr,
42934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_update_rate.dev_attr.attr,
43034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	NULL
43134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres};
43234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
43334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic const struct attribute_group hwmon_attrgroup = {
43434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	.attrs = hwmon_attributes,
43534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres};
436b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#endif
43734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
43834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic int
43934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_init(struct drm_device *dev)
44034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
441658e86ee2db9500aea529a04008cce10972416bcKen Milmore#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
44234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
4438155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
44434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct device *hwmon_dev;
44534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	int ret;
44634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4478155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	if (!pm->temp_get)
4488155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		return -ENODEV;
44934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
45034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	hwmon_dev = hwmon_device_register(&dev->pdev->dev);
45134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (IS_ERR(hwmon_dev)) {
45234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		ret = PTR_ERR(hwmon_dev);
45334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_ERROR(dev,
45434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			"Unable to register hwmon device: %d\n", ret);
45534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return ret;
45634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
45734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	dev_set_drvdata(hwmon_dev, dev);
45807cfe0e7a820ecad078c04e9c2a102521709145dLucas Stach	ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
45934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (ret) {
46034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_ERROR(dev,
46134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			"Unable to create hwmon sysfs file: %d\n", ret);
46234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		hwmon_device_unregister(hwmon_dev);
46334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return ret;
46434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
46534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4668155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	pm->hwmon = hwmon_dev;
467b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#endif
46834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return 0;
46934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
47034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
47134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic void
47234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_fini(struct drm_device *dev)
47334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
474658e86ee2db9500aea529a04008cce10972416bcKen Milmore#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
47534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
4768155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
47734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4788155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	if (pm->hwmon) {
4798c06a3e02062a9beb71a9444c49fb0fbcaa1eed3Lucas Stach		sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
4808155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		hwmon_device_unregister(pm->hwmon);
48134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
482b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#endif
48334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
48434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4851f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
4866032649df9f456f379be8d51f64488cacbfa8317Ben Skeggsstatic int
4876032649df9f456f379be8d51f64488cacbfa8317Ben Skeggsnouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
4886032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs{
4896032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct drm_nouveau_private *dev_priv =
4906032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb);
4916032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct drm_device *dev = dev_priv->dev;
4926032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
4936032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
4946032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	if (strcmp(entry->device_class, "ac_adapter") == 0) {
4956032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		bool ac = power_supply_is_system_supplied();
4966032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
4976032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
4986032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	}
4996032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
5006032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	return NOTIFY_OK;
5016032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs}
5026032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
5036032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
50434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresint
50534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_pm_init(struct drm_device *dev)
50634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
50734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
50834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
50934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	char info[256];
51034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	int ret, i;
51134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
512e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	nouveau_mem_timing_init(dev);
51334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_volt_init(dev);
51434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_perf_init(dev);
51534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_init(dev);
51634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
51734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
51834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	for (i = 0; i < pm->nr_perflvl; i++) {
51934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
52093dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
52134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
52234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
52334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	/* determine current ("boot") performance level */
52434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	ret = nouveau_pm_perflvl_get(dev, &pm->boot);
52534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (ret == 0) {
52601e542c65de11a47e726ebef63f5e59b4a74568dMartin Peres		strncpy(pm->boot.name, "boot", 4);
52734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		pm->cur = &pm->boot;
52834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
52934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
53093dccbedeb2280ca2c234530236b950b232afa65Ben Skeggs		NV_INFO(dev, "c:%s", info);
53134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
53234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
53334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	/* switch performance levels now if requested */
53434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (nouveau_perflvl != NULL) {
53534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
53634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		if (ret) {
53734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
53834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				 nouveau_perflvl, ret);
53934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		}
54034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
54134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
54234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_sysfs_init(dev);
54334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_hwmon_init(dev);
5441f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
5456032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
5466032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	register_acpi_notifier(&pm->acpi_nb);
5476032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
54834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
54934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return 0;
55034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
55134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
55234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresvoid
55334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_pm_fini(struct drm_device *dev)
55434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
55534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
55634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
55734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
55834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (pm->cur != &pm->boot)
55934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_set(dev, &pm->boot);
560330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
5617760fcb020b41352af4e675ce65a6aa0e93c170fRoy Spliet	nouveau_temp_fini(dev);
562330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_perf_fini(dev);
563330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_volt_fini(dev);
564e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	nouveau_mem_timing_fini(dev);
56534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
5661f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
5676032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	unregister_acpi_notifier(&pm->acpi_nb);
5686032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
56934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_hwmon_fini(dev);
57034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_sysfs_fini(dev);
571330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
572330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
57364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsvoid
57464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsnouveau_pm_resume(struct drm_device *dev)
57564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs{
57664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
57764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
57864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_level *perflvl;
57964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
580317495b25ec1f0beb0dbac8ee0dfec59a1addf03Ben Skeggs	if (!pm->cur || pm->cur == &pm->boot)
58164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		return;
58264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
58364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	perflvl = pm->cur;
58464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	pm->cur = &pm->boot;
58564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	nouveau_pm_perflvl_set(dev, perflvl);
58664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs}
587