nouveau_pm.c revision e614b2e7ca9f9946cede13b34c950b92af6fa7ef
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
6764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) {
6864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		ret = pm->voltage_set(dev, perflvl->voltage);
6964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		if (ret) {
7064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs			NV_ERROR(dev, "voltage_set %d failed: %d\n",
7164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs				 perflvl->voltage, ret);
7264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		}
7364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	}
7464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
755c6dc6575460a0afe56d8cae7666e769e08ef942Ben Skeggs	nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
765c6dc6575460a0afe56d8cae7666e769e08ef942Ben Skeggs	nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
775c6dc6575460a0afe56d8cae7666e769e08ef942Ben Skeggs	nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
785c6dc6575460a0afe56d8cae7666e769e08ef942Ben Skeggs	nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
7964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
8064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	pm->cur = perflvl;
8164f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	return 0;
8264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs}
8364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
8464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsstatic int
856f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsnouveau_pm_profile_set(struct drm_device *dev, const char *profile)
866f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs{
876f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
886f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
896f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	struct nouveau_pm_level *perflvl = NULL;
906f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
916f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	/* safety precaution, for now */
926f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (nouveau_perflvl_wr != 7777)
936f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return -EPERM;
946f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
956f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (!pm->clock_set)
966f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return -EINVAL;
976f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
986f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (!strncmp(profile, "boot", 4))
996f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		perflvl = &pm->boot;
1006f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	else {
1016f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		int pl = simple_strtol(profile, NULL, 10);
1026f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		int i;
1036f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1046f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		for (i = 0; i < pm->nr_perflvl; i++) {
1056f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			if (pm->perflvl[i].id == pl) {
1066f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs				perflvl = &pm->perflvl[i];
1076f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs				break;
1086f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			}
1096f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		}
1106f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1116f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		if (!perflvl)
1126f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs			return -EINVAL;
1136f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	}
1146f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1156f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	NV_INFO(dev, "setting performance level: %s\n", profile);
11664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	return nouveau_pm_perflvl_set(dev, perflvl);
1176f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs}
1186f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
1196f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggsstatic int
120330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
121330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
122330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
123330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
124330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int ret;
125330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
126330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (!pm->clock_get)
127330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		return -EINVAL;
128330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
129330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	memset(perflvl, 0, sizeof(*perflvl));
130330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
131330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = pm->clock_get(dev, PLL_CORE);
132330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret > 0)
133330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->core = ret;
134330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
135330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = pm->clock_get(dev, PLL_MEMORY);
136330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret > 0)
137330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->memory = ret;
138330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
139330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = pm->clock_get(dev, PLL_SHADER);
140330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret > 0)
141330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->shader = ret;
142330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
143330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = pm->clock_get(dev, PLL_UNK05);
144330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret > 0)
145330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->unk05 = ret;
146330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
147330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (pm->voltage.supported && pm->voltage_get) {
148330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		ret = pm->voltage_get(dev);
149330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (ret > 0)
150330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			perflvl->voltage = ret;
151330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
152330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
153330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return 0;
154330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
155330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
156330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic void
157330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
158330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
159e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	char c[16], s[16], v[16], f[16], t[16];
1600fbb114af7ea63227599460c412fb8796556a169Francisco Jerez
1610fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	c[0] = '\0';
1620fbb114af7ea63227599460c412fb8796556a169Francisco Jerez	if (perflvl->core)
1630fbb114af7ea63227599460c412fb8796556a169Francisco Jerez		snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
164330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
165330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	s[0] = '\0';
166330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->shader)
167330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
168330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
169330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	v[0] = '\0';
170330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->voltage)
171330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10);
172330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
173330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	f[0] = '\0';
174330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (perflvl->fanspeed)
175330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
176330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
177e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	t[0] = '\0';
178e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	if (perflvl->timing)
179e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres		snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
180e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres
181e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000,
182e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres		 c, s, v, f, t);
183330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
184330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
185330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
186330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_get_perflvl_info(struct device *d,
187330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			    struct device_attribute *a, char *buf)
188330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
189330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
190330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	char *ptr = buf;
191330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int len = PAGE_SIZE;
192330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
193330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	snprintf(ptr, len, "%d: ", perflvl->id);
194330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ptr += strlen(buf);
195330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	len -= strlen(buf);
196330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
197330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_pm_perflvl_info(perflvl, ptr, len);
198330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return strlen(buf);
199330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
200330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
201330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
202330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
203330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
20434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
205330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
206330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
207330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_level cur;
208330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int len = PAGE_SIZE, ret;
209330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	char *ptr = buf;
210330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
211330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (!pm->cur)
212330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(ptr, len, "setting: boot\n");
213330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	else if (pm->cur == &pm->boot)
214330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(ptr, len, "setting: boot\nc: ");
215330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	else
216330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id);
217330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ptr += strlen(buf);
218330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	len -= strlen(buf);
219330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
220330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = nouveau_pm_perflvl_get(dev, &cur);
221330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret == 0)
222330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		nouveau_pm_perflvl_info(&cur, ptr, len);
223330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return strlen(buf);
224330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
225330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
226330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsstatic ssize_t
227330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggsnouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
228330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		       const char *buf, size_t count)
229330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
23034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
2316f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	int ret;
2326f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs
2336f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	ret = nouveau_pm_profile_set(dev, buf);
2346f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	if (ret)
2356f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs		return ret;
2366f876986bedf23b40ab707543e88fae7eac27f1fBen Skeggs	return strlen(buf);
237330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
238330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
2395c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerezstatic DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
2405c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez		   nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
241330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
24234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic int
24334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_sysfs_init(struct drm_device *dev)
244330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
245330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
246330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
247330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct device *d = &dev->pdev->dev;
248330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int ret, i;
249330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
250330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	ret = device_create_file(d, &dev_attr_performance_level);
251330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	if (ret)
252330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		return ret;
253330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
254330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	for (i = 0; i < pm->nr_perflvl; i++) {
255330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		struct nouveau_pm_level *perflvl = &pm->perflvl[i];
256330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
257330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.attr.name = perflvl->name;
258330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.attr.mode = S_IRUGO;
259330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
260330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		perflvl->dev_attr.store = NULL;
261330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		sysfs_attr_init(&perflvl->dev_attr.attr);
262330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
263330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		ret = device_create_file(d, &perflvl->dev_attr);
264330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (ret) {
265330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
266330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs				 perflvl->id, i);
267330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			perflvl->dev_attr.attr.name = NULL;
268330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			nouveau_pm_fini(dev);
269330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			return ret;
270330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		}
271330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
272330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
273330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	return 0;
274330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
275330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
27634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic void
27734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_sysfs_fini(struct drm_device *dev)
278330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs{
279330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
280330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
281330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	struct device *d = &dev->pdev->dev;
282330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	int i;
283330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
284330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	device_remove_file(d, &dev_attr_performance_level);
285330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	for (i = 0; i < pm->nr_perflvl; i++) {
286330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		struct nouveau_pm_level *pl = &pm->perflvl[i];
287330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
288330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		if (!pl->dev_attr.attr.name)
289330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs			break;
290330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
291330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs		device_remove_file(d, &pl->dev_attr);
292330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	}
29334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
29434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
295b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#ifdef CONFIG_HWMON
29634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
29734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
29834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
29934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
3008155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct drm_nouveau_private *dev_priv = dev->dev_private;
3018155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
30234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3038155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
30434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
30534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
30634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  NULL, 0);
30734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
30834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
30934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
31034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
31134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
31234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
31334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
31434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
31534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
31634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
31734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
31834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
31934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
32034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						const char *buf, size_t count)
32134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
32234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
32334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
32434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
32534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
32634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	long value;
32734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3285c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez	if (strict_strtol(buf, 10, &value) == -EINVAL)
32934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return count;
33034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
33134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	temp->down_clock = value/1000;
33234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
33334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_safety_checks(dev);
33434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
33534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return count;
33634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
33734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
33834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  nouveau_hwmon_set_max_temp,
33934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						  0);
34034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
34134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
34234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
34334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres							char *buf)
34434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
34534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
34634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
34734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
34834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
34934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
35034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
35134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
35234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t
35334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
35434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres							    const char *buf,
35534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres								size_t count)
35634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
35734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_device *dev = dev_get_drvdata(d);
35834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
35934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
36034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
36134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	long value;
36234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
3635c4abd09bdefb41d0c80055aa9d98433624ce1f0Francisco Jerez	if (strict_strtol(buf, 10, &value) == -EINVAL)
36434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return count;
36534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
36634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	temp->critical = value/1000;
36734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
36834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_safety_checks(dev);
36934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
37034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return count;
37134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
37234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
37334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_critical_temp,
37434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_set_critical_temp,
37534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						0);
37634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
37734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t nouveau_hwmon_show_name(struct device *dev,
37834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      struct device_attribute *attr,
37934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      char *buf)
38034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
38134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return sprintf(buf, "nouveau\n");
38234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
38334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
38434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
38534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
38634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      struct device_attribute *attr,
38734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				      char *buf)
38834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
38934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return sprintf(buf, "1000\n");
39034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
39134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
39234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						nouveau_hwmon_show_update_rate,
39334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres						NULL, 0);
39434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
39534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic struct attribute *hwmon_attributes[] = {
39634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_input.dev_attr.attr,
39734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_max.dev_attr.attr,
39834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_temp1_crit.dev_attr.attr,
39934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_name.dev_attr.attr,
40034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	&sensor_dev_attr_update_rate.dev_attr.attr,
40134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	NULL
40234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres};
40334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
40434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic const struct attribute_group hwmon_attrgroup = {
40534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	.attrs = hwmon_attributes,
40634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres};
407b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#endif
40834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
40934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic int
41034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_init(struct drm_device *dev)
41134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
412b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#ifdef CONFIG_HWMON
41334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
4148155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
41534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct device *hwmon_dev;
41634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	int ret;
41734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4188155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	if (!pm->temp_get)
4198155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		return -ENODEV;
42034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
42134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	hwmon_dev = hwmon_device_register(&dev->pdev->dev);
42234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (IS_ERR(hwmon_dev)) {
42334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		ret = PTR_ERR(hwmon_dev);
42434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_ERROR(dev,
42534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			"Unable to register hwmon device: %d\n", ret);
42634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return ret;
42734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
42834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	dev_set_drvdata(hwmon_dev, dev);
42907cfe0e7a820ecad078c04e9c2a102521709145dLucas Stach	ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
43034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (ret) {
43134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_ERROR(dev,
43234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			"Unable to create hwmon sysfs file: %d\n", ret);
43334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		hwmon_device_unregister(hwmon_dev);
43434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		return ret;
43534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
43634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4378155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	pm->hwmon = hwmon_dev;
438b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#endif
43934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return 0;
44034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
44134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
44234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresstatic void
44334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_hwmon_fini(struct drm_device *dev)
44434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
445b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#ifdef CONFIG_HWMON
44634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
4478155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
44834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4498155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez	if (pm->hwmon) {
4508c06a3e02062a9beb71a9444c49fb0fbcaa1eed3Lucas Stach		sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
4518155cac489eb8cc6fd96b9bdefacdf5a56e6ea32Francisco Jerez		hwmon_device_unregister(pm->hwmon);
45234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
453b54262f3c828ee17e27632d0d60255281c02e1a5Martin Peres#endif
45434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
45534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
4561f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
4576032649df9f456f379be8d51f64488cacbfa8317Ben Skeggsstatic int
4586032649df9f456f379be8d51f64488cacbfa8317Ben Skeggsnouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
4596032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs{
4606032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct drm_nouveau_private *dev_priv =
4616032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb);
4626032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct drm_device *dev = dev_priv->dev;
4636032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
4646032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
4656032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	if (strcmp(entry->device_class, "ac_adapter") == 0) {
4666032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		bool ac = power_supply_is_system_supplied();
4676032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
4686032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs		NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
4696032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	}
4706032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
4716032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	return NOTIFY_OK;
4726032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs}
4736032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
4746032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs
47534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresint
47634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_pm_init(struct drm_device *dev)
47734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
47834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
47934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
48034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	char info[256];
48134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	int ret, i;
48234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
483e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	nouveau_mem_timing_init(dev);
48434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_volt_init(dev);
48534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_perf_init(dev);
48634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_temp_init(dev);
48734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
48834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
48934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	for (i = 0; i < pm->nr_perflvl; i++) {
49034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
49134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info);
49234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
49334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
49434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	/* determine current ("boot") performance level */
49534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	ret = nouveau_pm_perflvl_get(dev, &pm->boot);
49634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (ret == 0) {
49701e542c65de11a47e726ebef63f5e59b4a74568dMartin Peres		strncpy(pm->boot.name, "boot", 4);
49834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		pm->cur = &pm->boot;
49934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
50034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
50134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		NV_INFO(dev, "c: %s", info);
50234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
50334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
50434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	/* switch performance levels now if requested */
50534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (nouveau_perflvl != NULL) {
50634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
50734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		if (ret) {
50834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres			NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
50934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres				 nouveau_perflvl, ret);
51034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		}
51134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	}
51234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
51334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_sysfs_init(dev);
51434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_hwmon_init(dev);
5151f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
5166032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
5176032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	register_acpi_notifier(&pm->acpi_nb);
5186032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
51934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
52034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	return 0;
52134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres}
52234e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
52334e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresvoid
52434e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peresnouveau_pm_fini(struct drm_device *dev)
52534e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres{
52634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct drm_nouveau_private *dev_priv = dev->dev_private;
52734e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
52834e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
52934e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	if (pm->cur != &pm->boot)
53034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres		nouveau_pm_perflvl_set(dev, &pm->boot);
531330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
5327760fcb020b41352af4e675ce65a6aa0e93c170fRoy Spliet	nouveau_temp_fini(dev);
533330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_perf_fini(dev);
534330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs	nouveau_volt_fini(dev);
535e614b2e7ca9f9946cede13b34c950b92af6fa7efMartin Peres	nouveau_mem_timing_fini(dev);
53634e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres
5371f962797fb1343f02cbacb94d80c4560d47b67a9Martin Peres#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
5386032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs	unregister_acpi_notifier(&pm->acpi_nb);
5396032649df9f456f379be8d51f64488cacbfa8317Ben Skeggs#endif
54034e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_hwmon_fini(dev);
54134e9d85a1aae28b090ec4e72a0f98a5483c198c4Martin Peres	nouveau_sysfs_fini(dev);
542330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs}
543330c5988ee78045e6a731c3693251aaa5b0d14e3Ben Skeggs
54464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsvoid
54564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggsnouveau_pm_resume(struct drm_device *dev)
54664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs{
54764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct drm_nouveau_private *dev_priv = dev->dev_private;
54864f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
54964f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	struct nouveau_pm_level *perflvl;
55064f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
551317495b25ec1f0beb0dbac8ee0dfec59a1addf03Ben Skeggs	if (!pm->cur || pm->cur == &pm->boot)
55264f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs		return;
55364f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs
55464f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	perflvl = pm->cur;
55564f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	pm->cur = &pm->boot;
55664f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs	nouveau_pm_perflvl_set(dev, perflvl);
55764f1c11a477cb76e1572ee0793234739e045b3d5Ben Skeggs}
558