Path: blob/master/drivers/gpu/drm/nouveau/nouveau_pm.c
15112 views
/*1* Copyright 2010 Red Hat Inc.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice shall be included in11* all copies or substantial portions of the Software.12*13* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR17* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19* OTHER DEALINGS IN THE SOFTWARE.20*21* Authors: Ben Skeggs22*/2324#include "drmP.h"2526#include "nouveau_drv.h"27#include "nouveau_pm.h"2829#ifdef CONFIG_ACPI30#include <linux/acpi.h>31#endif32#include <linux/power_supply.h>33#include <linux/hwmon.h>34#include <linux/hwmon-sysfs.h>3536static int37nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl,38u8 id, u32 khz)39{40struct drm_nouveau_private *dev_priv = dev->dev_private;41struct nouveau_pm_engine *pm = &dev_priv->engine.pm;42void *pre_state;4344if (khz == 0)45return 0;4647pre_state = pm->clock_pre(dev, perflvl, id, khz);48if (IS_ERR(pre_state))49return PTR_ERR(pre_state);5051if (pre_state)52pm->clock_set(dev, pre_state);53return 0;54}5556static int57nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)58{59struct drm_nouveau_private *dev_priv = dev->dev_private;60struct nouveau_pm_engine *pm = &dev_priv->engine.pm;61int ret;6263if (perflvl == pm->cur)64return 0;6566if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) {67ret = pm->voltage_set(dev, perflvl->voltage);68if (ret) {69NV_ERROR(dev, "voltage_set %d failed: %d\n",70perflvl->voltage, ret);71}72}7374nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);75nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);76nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);77nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);7879pm->cur = perflvl;80return 0;81}8283static int84nouveau_pm_profile_set(struct drm_device *dev, const char *profile)85{86struct drm_nouveau_private *dev_priv = dev->dev_private;87struct nouveau_pm_engine *pm = &dev_priv->engine.pm;88struct nouveau_pm_level *perflvl = NULL;8990/* safety precaution, for now */91if (nouveau_perflvl_wr != 7777)92return -EPERM;9394if (!pm->clock_set)95return -EINVAL;9697if (!strncmp(profile, "boot", 4))98perflvl = &pm->boot;99else {100int pl = simple_strtol(profile, NULL, 10);101int i;102103for (i = 0; i < pm->nr_perflvl; i++) {104if (pm->perflvl[i].id == pl) {105perflvl = &pm->perflvl[i];106break;107}108}109110if (!perflvl)111return -EINVAL;112}113114NV_INFO(dev, "setting performance level: %s\n", profile);115return nouveau_pm_perflvl_set(dev, perflvl);116}117118static int119nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)120{121struct drm_nouveau_private *dev_priv = dev->dev_private;122struct nouveau_pm_engine *pm = &dev_priv->engine.pm;123int ret;124125if (!pm->clock_get)126return -EINVAL;127128memset(perflvl, 0, sizeof(*perflvl));129130ret = pm->clock_get(dev, PLL_CORE);131if (ret > 0)132perflvl->core = ret;133134ret = pm->clock_get(dev, PLL_MEMORY);135if (ret > 0)136perflvl->memory = ret;137138ret = pm->clock_get(dev, PLL_SHADER);139if (ret > 0)140perflvl->shader = ret;141142ret = pm->clock_get(dev, PLL_UNK05);143if (ret > 0)144perflvl->unk05 = ret;145146if (pm->voltage.supported && pm->voltage_get) {147ret = pm->voltage_get(dev);148if (ret > 0)149perflvl->voltage = ret;150}151152return 0;153}154155static void156nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)157{158char c[16], s[16], v[16], f[16], t[16];159160c[0] = '\0';161if (perflvl->core)162snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);163164s[0] = '\0';165if (perflvl->shader)166snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);167168v[0] = '\0';169if (perflvl->voltage)170snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10);171172f[0] = '\0';173if (perflvl->fanspeed)174snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);175176t[0] = '\0';177if (perflvl->timing)178snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);179180snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000,181c, s, v, f, t);182}183184static ssize_t185nouveau_pm_get_perflvl_info(struct device *d,186struct device_attribute *a, char *buf)187{188struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;189char *ptr = buf;190int len = PAGE_SIZE;191192snprintf(ptr, len, "%d: ", perflvl->id);193ptr += strlen(buf);194len -= strlen(buf);195196nouveau_pm_perflvl_info(perflvl, ptr, len);197return strlen(buf);198}199200static ssize_t201nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)202{203struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));204struct drm_nouveau_private *dev_priv = dev->dev_private;205struct nouveau_pm_engine *pm = &dev_priv->engine.pm;206struct nouveau_pm_level cur;207int len = PAGE_SIZE, ret;208char *ptr = buf;209210if (!pm->cur)211snprintf(ptr, len, "setting: boot\n");212else if (pm->cur == &pm->boot)213snprintf(ptr, len, "setting: boot\nc: ");214else215snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id);216ptr += strlen(buf);217len -= strlen(buf);218219ret = nouveau_pm_perflvl_get(dev, &cur);220if (ret == 0)221nouveau_pm_perflvl_info(&cur, ptr, len);222return strlen(buf);223}224225static ssize_t226nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,227const char *buf, size_t count)228{229struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));230int ret;231232ret = nouveau_pm_profile_set(dev, buf);233if (ret)234return ret;235return strlen(buf);236}237238static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,239nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);240241static int242nouveau_sysfs_init(struct drm_device *dev)243{244struct drm_nouveau_private *dev_priv = dev->dev_private;245struct nouveau_pm_engine *pm = &dev_priv->engine.pm;246struct device *d = &dev->pdev->dev;247int ret, i;248249ret = device_create_file(d, &dev_attr_performance_level);250if (ret)251return ret;252253for (i = 0; i < pm->nr_perflvl; i++) {254struct nouveau_pm_level *perflvl = &pm->perflvl[i];255256perflvl->dev_attr.attr.name = perflvl->name;257perflvl->dev_attr.attr.mode = S_IRUGO;258perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;259perflvl->dev_attr.store = NULL;260sysfs_attr_init(&perflvl->dev_attr.attr);261262ret = device_create_file(d, &perflvl->dev_attr);263if (ret) {264NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",265perflvl->id, i);266perflvl->dev_attr.attr.name = NULL;267nouveau_pm_fini(dev);268return ret;269}270}271272return 0;273}274275static void276nouveau_sysfs_fini(struct drm_device *dev)277{278struct drm_nouveau_private *dev_priv = dev->dev_private;279struct nouveau_pm_engine *pm = &dev_priv->engine.pm;280struct device *d = &dev->pdev->dev;281int i;282283device_remove_file(d, &dev_attr_performance_level);284for (i = 0; i < pm->nr_perflvl; i++) {285struct nouveau_pm_level *pl = &pm->perflvl[i];286287if (!pl->dev_attr.attr.name)288break;289290device_remove_file(d, &pl->dev_attr);291}292}293294#ifdef CONFIG_HWMON295static ssize_t296nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)297{298struct drm_device *dev = dev_get_drvdata(d);299struct drm_nouveau_private *dev_priv = dev->dev_private;300struct nouveau_pm_engine *pm = &dev_priv->engine.pm;301302return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);303}304static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,305NULL, 0);306307static ssize_t308nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)309{310struct drm_device *dev = dev_get_drvdata(d);311struct drm_nouveau_private *dev_priv = dev->dev_private;312struct nouveau_pm_engine *pm = &dev_priv->engine.pm;313struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;314315return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);316}317static ssize_t318nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,319const char *buf, size_t count)320{321struct drm_device *dev = dev_get_drvdata(d);322struct drm_nouveau_private *dev_priv = dev->dev_private;323struct nouveau_pm_engine *pm = &dev_priv->engine.pm;324struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;325long value;326327if (strict_strtol(buf, 10, &value) == -EINVAL)328return count;329330temp->down_clock = value/1000;331332nouveau_temp_safety_checks(dev);333334return count;335}336static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,337nouveau_hwmon_set_max_temp,3380);339340static ssize_t341nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,342char *buf)343{344struct drm_device *dev = dev_get_drvdata(d);345struct drm_nouveau_private *dev_priv = dev->dev_private;346struct nouveau_pm_engine *pm = &dev_priv->engine.pm;347struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;348349return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);350}351static ssize_t352nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,353const char *buf,354size_t count)355{356struct drm_device *dev = dev_get_drvdata(d);357struct drm_nouveau_private *dev_priv = dev->dev_private;358struct nouveau_pm_engine *pm = &dev_priv->engine.pm;359struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;360long value;361362if (strict_strtol(buf, 10, &value) == -EINVAL)363return count;364365temp->critical = value/1000;366367nouveau_temp_safety_checks(dev);368369return count;370}371static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,372nouveau_hwmon_critical_temp,373nouveau_hwmon_set_critical_temp,3740);375376static ssize_t nouveau_hwmon_show_name(struct device *dev,377struct device_attribute *attr,378char *buf)379{380return sprintf(buf, "nouveau\n");381}382static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);383384static ssize_t nouveau_hwmon_show_update_rate(struct device *dev,385struct device_attribute *attr,386char *buf)387{388return sprintf(buf, "1000\n");389}390static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,391nouveau_hwmon_show_update_rate,392NULL, 0);393394static struct attribute *hwmon_attributes[] = {395&sensor_dev_attr_temp1_input.dev_attr.attr,396&sensor_dev_attr_temp1_max.dev_attr.attr,397&sensor_dev_attr_temp1_crit.dev_attr.attr,398&sensor_dev_attr_name.dev_attr.attr,399&sensor_dev_attr_update_rate.dev_attr.attr,400NULL401};402403static const struct attribute_group hwmon_attrgroup = {404.attrs = hwmon_attributes,405};406#endif407408static int409nouveau_hwmon_init(struct drm_device *dev)410{411#ifdef CONFIG_HWMON412struct drm_nouveau_private *dev_priv = dev->dev_private;413struct nouveau_pm_engine *pm = &dev_priv->engine.pm;414struct device *hwmon_dev;415int ret;416417if (!pm->temp_get)418return -ENODEV;419420hwmon_dev = hwmon_device_register(&dev->pdev->dev);421if (IS_ERR(hwmon_dev)) {422ret = PTR_ERR(hwmon_dev);423NV_ERROR(dev,424"Unable to register hwmon device: %d\n", ret);425return ret;426}427dev_set_drvdata(hwmon_dev, dev);428ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);429if (ret) {430NV_ERROR(dev,431"Unable to create hwmon sysfs file: %d\n", ret);432hwmon_device_unregister(hwmon_dev);433return ret;434}435436pm->hwmon = hwmon_dev;437#endif438return 0;439}440441static void442nouveau_hwmon_fini(struct drm_device *dev)443{444#ifdef CONFIG_HWMON445struct drm_nouveau_private *dev_priv = dev->dev_private;446struct nouveau_pm_engine *pm = &dev_priv->engine.pm;447448if (pm->hwmon) {449sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);450hwmon_device_unregister(pm->hwmon);451}452#endif453}454455#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)456static int457nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)458{459struct drm_nouveau_private *dev_priv =460container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb);461struct drm_device *dev = dev_priv->dev;462struct acpi_bus_event *entry = (struct acpi_bus_event *)data;463464if (strcmp(entry->device_class, "ac_adapter") == 0) {465bool ac = power_supply_is_system_supplied();466467NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");468}469470return NOTIFY_OK;471}472#endif473474int475nouveau_pm_init(struct drm_device *dev)476{477struct drm_nouveau_private *dev_priv = dev->dev_private;478struct nouveau_pm_engine *pm = &dev_priv->engine.pm;479char info[256];480int ret, i;481482nouveau_mem_timing_init(dev);483nouveau_volt_init(dev);484nouveau_perf_init(dev);485nouveau_temp_init(dev);486487NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);488for (i = 0; i < pm->nr_perflvl; i++) {489nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));490NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info);491}492493/* determine current ("boot") performance level */494ret = nouveau_pm_perflvl_get(dev, &pm->boot);495if (ret == 0) {496strncpy(pm->boot.name, "boot", 4);497pm->cur = &pm->boot;498499nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));500NV_INFO(dev, "c: %s", info);501}502503/* switch performance levels now if requested */504if (nouveau_perflvl != NULL) {505ret = nouveau_pm_profile_set(dev, nouveau_perflvl);506if (ret) {507NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",508nouveau_perflvl, ret);509}510}511512nouveau_sysfs_init(dev);513nouveau_hwmon_init(dev);514#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)515pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;516register_acpi_notifier(&pm->acpi_nb);517#endif518519return 0;520}521522void523nouveau_pm_fini(struct drm_device *dev)524{525struct drm_nouveau_private *dev_priv = dev->dev_private;526struct nouveau_pm_engine *pm = &dev_priv->engine.pm;527528if (pm->cur != &pm->boot)529nouveau_pm_perflvl_set(dev, &pm->boot);530531nouveau_temp_fini(dev);532nouveau_perf_fini(dev);533nouveau_volt_fini(dev);534nouveau_mem_timing_fini(dev);535536#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)537unregister_acpi_notifier(&pm->acpi_nb);538#endif539nouveau_hwmon_fini(dev);540nouveau_sysfs_fini(dev);541}542543void544nouveau_pm_resume(struct drm_device *dev)545{546struct drm_nouveau_private *dev_priv = dev->dev_private;547struct nouveau_pm_engine *pm = &dev_priv->engine.pm;548struct nouveau_pm_level *perflvl;549550if (!pm->cur || pm->cur == &pm->boot)551return;552553perflvl = pm->cur;554pm->cur = &pm->boot;555nouveau_pm_perflvl_set(dev, perflvl);556}557558559