nouveau_pm.c revision 5c4abd09bdefb41d0c80055aa9d98433624ce1f0
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
3034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres#include <linux/hwmon.h>
3134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres#include <linux/hwmon-sysfs.h>
3234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
33330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic int
346f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsnouveau_pm_clock_set(struct drm_device *dev, u8 id, u32 khz)
356f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs{
366f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
376f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
386f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	void *pre_state;
396f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
406f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (khz == 0)
416f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return 0;
426f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
436f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	pre_state = pm->clock_pre(dev, id, khz);
446f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (IS_ERR(pre_state))
456f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return PTR_ERR(pre_state);
466f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
476f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (pre_state)
486f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		pm->clock_set(dev, pre_state);
496f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	return 0;
506f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs}
516f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
526f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsstatic int
5364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsnouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
5464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs{
5564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
5664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
5764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	int ret;
5864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
5964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	if (perflvl == pm->cur)
6064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		return 0;
6164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
6264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) {
6364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		ret = pm->voltage_set(dev, perflvl->voltage);
6464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		if (ret) {
6564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs			NV_ERROR(dev, "voltage_set %d failed: %d\n",
6664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs				 perflvl->voltage, ret);
6764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		}
6864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	}
6964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
7064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	nouveau_pm_clock_set(dev, PLL_CORE, perflvl->core);
7164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	nouveau_pm_clock_set(dev, PLL_SHADER, perflvl->shader);
7264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	nouveau_pm_clock_set(dev, PLL_MEMORY, perflvl->memory);
7364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	nouveau_pm_clock_set(dev, PLL_UNK05, perflvl->unk05);
7464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
7564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	pm->cur = perflvl;
7664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	return 0;
7764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs}
7864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
7964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsstatic int
806f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsnouveau_pm_profile_set(struct drm_device *dev, const char *profile)
816f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs{
826f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
836f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
846f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_level *perflvl = NULL;
856f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
866f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	/* safety precaution, for now */
876f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (nouveau_perflvl_wr != 7777)
886f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return -EPERM;
896f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
906f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (!pm->clock_set)
916f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return -EINVAL;
926f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
936f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (!strncmp(profile, "boot", 4))
946f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		perflvl = &pm->boot;
956f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	else {
966f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		int pl = simple_strtol(profile, NULL, 10);
976f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		int i;
986f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
996f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		for (i = 0; i < pm->nr_perflvl; i++) {
1006f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			if (pm->perflvl[i].id == pl) {
1016f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs				perflvl = &pm->perflvl[i];
1026f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs				break;
1036f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			}
1046f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		}
1056f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1066f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		if (!perflvl)
1076f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			return -EINVAL;
1086f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	}
1096f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1106f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	NV_INFO(dev, "setting performance level: %s\n", profile);
11164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	return nouveau_pm_perflvl_set(dev, perflvl);
1126f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs}
1136f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1146f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsstatic int
115330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
116330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
117330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
118330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
119330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int ret;
120330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
121330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (!pm->clock_get)
122330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		return -EINVAL;
123330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
124330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	memset(perflvl, 0, sizeof(*perflvl));
125330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
126330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = pm->clock_get(dev, PLL_CORE);
127330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret > 0)
128330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->core = ret;
129330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
130330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = pm->clock_get(dev, PLL_MEMORY);
131330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret > 0)
132330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->memory = ret;
133330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
134330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = pm->clock_get(dev, PLL_SHADER);
135330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret > 0)
136330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->shader = ret;
137330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
138330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = pm->clock_get(dev, PLL_UNK05);
139330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret > 0)
140330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->unk05 = ret;
141330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
142330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (pm->voltage.supported && pm->voltage_get) {
143330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		ret = pm->voltage_get(dev);
144330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (ret > 0)
145330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			perflvl->voltage = ret;
146330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
147330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
148330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return 0;
149330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
150330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
151330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic void
152330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
153330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
1540fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	char c[16], s[16], v[16], f[16];
1550fbb114af7ea63227599460c412fb8796556a169Francisco Jerez
1560fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	c[0] = '\0';
1570fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	if (perflvl->core)
1580fbb114af7ea63227599460c412fb8796556a169Francisco Jerez		snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
159330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
160330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	s[0] = '\0';
161330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->shader)
162330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
163330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
164330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	v[0] = '\0';
165330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->voltage)
166330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10);
167330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
168330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	f[0] = '\0';
169330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->fanspeed)
170330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
171330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
1720fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	snprintf(ptr, len, "memory %dMHz%s%s%s%s\n", perflvl->memory / 1000,
1730fbb114af7ea63227599460c412fb8796556a169Francisco Jerez		 c, s, v, f);
174330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
175330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
176330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
177330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_get_perflvl_info(struct device *d,
178330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			    struct device_attribute *a, char *buf)
179330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
180330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
181330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	char *ptr = buf;
182330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int len = PAGE_SIZE;
183330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
184330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	snprintf(ptr, len, "%d: ", perflvl->id);
185330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ptr += strlen(buf);
186330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	len -= strlen(buf);
187330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
188330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_pm_perflvl_info(perflvl, ptr, len);
189330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return strlen(buf);
190330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
191330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
192330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
193330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
194330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
19534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
196330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
197330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
198330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_level cur;
199330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int len = PAGE_SIZE, ret;
200330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	char *ptr = buf;
201330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
202330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (!pm->cur)
203330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(ptr, len, "setting: boot\n");
204330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	else if (pm->cur == &pm->boot)
205330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(ptr, len, "setting: boot\nc: ");
206330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	else
207330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id);
208330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ptr += strlen(buf);
209330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	len -= strlen(buf);
210330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
211330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = nouveau_pm_perflvl_get(dev, &cur);
212330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret == 0)
213330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		nouveau_pm_perflvl_info(&cur, ptr, len);
214330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return strlen(buf);
215330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
216330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
217330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
218330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
219330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		       const char *buf, size_t count)
220330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
22134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
2226f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	int ret;
2236f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
2246f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	ret = nouveau_pm_profile_set(dev, buf);
2256f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (ret)
2266f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return ret;
2276f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	return strlen(buf);
228330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
229330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
2305c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerezstatic DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
2315c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez		   nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
232330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
23334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic int
23434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_sysfs_init(struct drm_device *dev)
235330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
236330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
237330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
238330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct device *d = &dev->pdev->dev;
239330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int ret, i;
240330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
241330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = device_create_file(d, &dev_attr_performance_level);
242330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret)
243330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		return ret;
244330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
245330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	for (i = 0; i < pm->nr_perflvl; i++) {
246330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		struct nouveau_pm_level *perflvl = &pm->perflvl[i];
247330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
248330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.attr.name = perflvl->name;
249330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.attr.mode = S_IRUGO;
250330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
251330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.store = NULL;
252330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		sysfs_attr_init(&perflvl->dev_attr.attr);
253330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
254330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		ret = device_create_file(d, &perflvl->dev_attr);
255330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (ret) {
256330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
257330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs				 perflvl->id, i);
258330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			perflvl->dev_attr.attr.name = NULL;
259330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			nouveau_pm_fini(dev);
260330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			return ret;
261330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		}
262330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
263330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
264330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return 0;
265330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
266330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
26734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic void
26834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_sysfs_fini(struct drm_device *dev)
269330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
270330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
271330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
272330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct device *d = &dev->pdev->dev;
273330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int i;
274330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
275330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	device_remove_file(d, &dev_attr_performance_level);
276330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	for (i = 0; i < pm->nr_perflvl; i++) {
277330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		struct nouveau_pm_level *pl = &pm->perflvl[i];
278330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
279330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (!pl->dev_attr.attr.name)
280330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			break;
281330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
282330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		device_remove_file(d, &pl->dev_attr);
283330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
28434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
28534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
28634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
28734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
28834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
28934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
2908155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct drm_nouveau_private *dev_priv = dev->dev_private;
2918155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
29234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
2938155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
29434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
29534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
29634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  NULL, 0);
29734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
29834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
29934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
30034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
30134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
30234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
30334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
30434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
30534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
30634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
30734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
30834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
30934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
31034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						const char *buf, size_t count)
31134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
31234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
31334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
31434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
31534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
31634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	long value;
31734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3185c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez	if (strict_strtol(buf, 10, &value) == -EINVAL)
31934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return count;
32034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
32134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	temp->down_clock = value/1000;
32234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
32334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_safety_checks(dev);
32434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
32534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return count;
32634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
32734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
32834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  nouveau_hwmon_set_max_temp,
32934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  0);
33034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
33134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
33234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
33334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres							char *buf)
33434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
33534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
33634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
33734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
33834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
33934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
34034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
34134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
34234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
34334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
34434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres							    const char *buf,
34534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres								size_t count)
34634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
34734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
34834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
34934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
35034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
35134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	long value;
35234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3535c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez	if (strict_strtol(buf, 10, &value) == -EINVAL)
35434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return count;
35534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
35634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	temp->critical = value/1000;
35734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
35834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_safety_checks(dev);
35934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
36034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return count;
36134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
36234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
36334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_critical_temp,
36434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_set_critical_temp,
36534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						0);
36634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
36734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t nouveau_hwmon_show_name(struct device *dev,
36834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      struct device_attribute *attr,
36934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      char *buf)
37034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
37134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return sprintf(buf, "nouveau\n");
37234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
37334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
37434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
37534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
37634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      struct device_attribute *attr,
37734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      char *buf)
37834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
37934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return sprintf(buf, "1000\n");
38034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
38134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
38234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_show_update_rate,
38334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						NULL, 0);
38434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
38534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic struct attribute *hwmon_attributes[] = {
38634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_input.dev_attr.attr,
38734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_max.dev_attr.attr,
38834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_crit.dev_attr.attr,
38934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_name.dev_attr.attr,
39034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_update_rate.dev_attr.attr,
39134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	NULL
39234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres};
39334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
39434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic const struct attribute_group hwmon_attrgroup = {
39534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	.attrs = hwmon_attributes,
39634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres};
39734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
39834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic int
39934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_init(struct drm_device *dev)
40034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
40134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
4028155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
40334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct device *hwmon_dev;
40434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	int ret;
40534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4068155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	if (!pm->temp_get)
4078155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		return -ENODEV;
40834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
40934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	hwmon_dev = hwmon_device_register(&dev->pdev->dev);
41034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (IS_ERR(hwmon_dev)) {
41134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		ret = PTR_ERR(hwmon_dev);
41234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_ERROR(dev,
41334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			"Unable to register hwmon device: %d\n", ret);
41434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return ret;
41534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
41634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	dev_set_drvdata(hwmon_dev, dev);
41734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	ret = sysfs_create_group(&hwmon_dev->kobj,
41834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres					&hwmon_attrgroup);
41934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (ret) {
42034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_ERROR(dev,
42134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			"Unable to create hwmon sysfs file: %d\n", ret);
42234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		hwmon_device_unregister(hwmon_dev);
42334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return ret;
42434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
42534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4268155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	pm->hwmon = hwmon_dev;
42734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
42834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return 0;
42934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
43034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
43134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic void
43234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_fini(struct drm_device *dev)
43334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
43434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
4358155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
43634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4378155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	if (pm->hwmon) {
4388155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup);
4398155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		hwmon_device_unregister(pm->hwmon);
44034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
44134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
44234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
44334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresint
44434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_pm_init(struct drm_device *dev)
44534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
44634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
44734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
44834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	char info[256];
44934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	int ret, i;
45034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
45134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_volt_init(dev);
45234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_perf_init(dev);
45334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_init(dev);
45434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
45534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
45634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	for (i = 0; i < pm->nr_perflvl; i++) {
45734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
45834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info);
45934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
46034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
46134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	/* determine current ("boot") performance level */
46234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	ret = nouveau_pm_perflvl_get(dev, &pm->boot);
46334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (ret == 0) {
46434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		pm->cur = &pm->boot;
46534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
46634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
46734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_INFO(dev, "c: %s", info);
46834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
46934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
47034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	/* switch performance levels now if requested */
47134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (nouveau_perflvl != NULL) {
47234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
47334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		if (ret) {
47434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
47534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				 nouveau_perflvl, ret);
47634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		}
47734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
47834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
47934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_sysfs_init(dev);
48034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_hwmon_init(dev);
48134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
48234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return 0;
48334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
48434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
48534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresvoid
48634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_pm_fini(struct drm_device *dev)
48734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
48834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
48934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
49034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
49134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (pm->cur != &pm->boot)
49234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_set(dev, &pm->boot);
493330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
494330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_perf_fini(dev);
495330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_volt_fini(dev);
49634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_fini(dev);
49734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
49834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_hwmon_fini(dev);
49934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_sysfs_fini(dev);
500330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
501330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
50264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsvoid
50364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsnouveau_pm_resume(struct drm_device *dev)
50464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs{
50564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
50664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
50764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_level *perflvl;
50864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
50964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	if (pm->cur == &pm->boot)
51064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		return;
51164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
51264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	perflvl = pm->cur;
51364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	pm->cur = &pm->boot;
51464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	nouveau_pm_perflvl_set(dev, perflvl);
51564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs}
516