[Nouveau] [RFC PATCH 2/3] dvfs: add support for GK20A

Vince Hsu vinceh at nvidia.com
Thu Dec 18 17:25:07 PST 2014


On 12/19/2014 03:33 AM, Samuel Pitoiset wrote:
> On 12/18/2014 11:25 AM, Ben Skeggs wrote:
>> On Thu, Dec 18, 2014 at 8:13 PM, Vince Hsu <vinceh at nvidia.com> wrote:
>>> Hello Ben,
>>>
>>> On 12/18/2014 05:34 PM, Ben Skeggs wrote:
>>>> On Thu, Dec 18, 2014 at 4:28 PM, Vince Hsu <vinceh at nvidia.com> wrote:
>>>>> This patch creates a subdev for DVFS (Dynamic Voltage and Frequency
>>>>> Scaling)
>>>>> support in Nouveau. This subdev refers to the status information 
>>>>> provided
>>>>> by
>>>>> the NVIDIA hardware and tries to adjust the performance level 
>>>>> based on
>>>>> the
>>>>> calculated target. Only the GK20A is supported right now.
>>>> Hey Vince,
>>>>
>>>> I'd not make a new subdev, but implement this in the PMU (PWR
>>>> currently) subdev.  The ucode that's supposed to be running there can
>>>> do the performance monitoring in a more complex implementation, and
>>>> it's PMU counters you're touching to get the usage stats :) But the
>>>> simpler on-host implementation is totally fine.
>>> Yeah, I know we have the counters defined in daemon.c. Martin 
>>> reminded me
>>> that Roy had a patch to remove that, sorry that I could not find the 
>>> link
>>> now. Another reason I did not use the nouveau_specsig is because I 
>>> had to
>>> create some nvif structures to access the counters and that's not
>>> straightforward. I'm happy to hear you're fine with the on-host
>>> implementation. :)
>
> Hey Vince,
>
> It's not Roy but me who had submitted a patch which removes PDAEMON 
> signals. :-)
Oops. Sorry for the wrong name, my bad.

>
> However, the patch is not ready to be upstream because we don't have 
> any other ways to expose these performance counters.
>
> Currently, I'm working on perfmon and nvif to expose hardware events 
> to the userspace for NV50. I hope to submit the bunch of patches as 
> soon as possible but unfortunately,
> I don't have my full time on this, so it takes more time than expected.
>
>>>
>>> I will move all to PWR subdev in the next version.
>>>>
>>>> The current base PMU implementation tries to do a lot of stuff that
>>>> you don't need, just override the implementation entirely for GK20A
>>>> for now if that's easier than reworking it.
>>> So currently there is no dGPU using the PMU? I just don't want to 
>>> break any
>>> dGPU functions. ;)
>> We upload a custom firmware to it, which we (currently) only use as a
>> script interpreter for memory clock changes.  But for your needs right
>> now, it'd be fine to ignore all the common code there for GK20A.
>>
>>>>
>>>> Aside from the subdev move, it looks good so far to me. Glad to see
>>>> the CLK stuff isn't totally useless, despite it needing a *lot* of
>>>> work.
>>> BTW, can we add some execution time information for pstate? I mean 
>>> something
>>> like "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state". I 
>>> know we
>>> don't want to change the user space ABI though.
>>>
>>> Thanks,
>>> Vince
>>>
>>>
>>>> Thanks,
>>>> Ben.
>>>>
>>>>> Signed-off-by: Vince Hsu <vinceh at nvidia.com>
>>>>> ---
>>>>>    drm/Kbuild                     |   2 +
>>>>>    drm/core/include/subdev/dvfs.h |   1 +
>>>>>    drm/core/subdev/dvfs/base.c    |   1 +
>>>>>    drm/core/subdev/dvfs/gk20a.c   |   1 +
>>>>>    drm/core/subdev/dvfs/priv.h    |   1 +
>>>>>    nvkm/engine/device/nve0.c      |   2 +
>>>>>    nvkm/include/core/device.h     |   1 +
>>>>>    nvkm/include/subdev/dvfs.h     |  30 ++++++++
>>>>>    nvkm/subdev/Makefile.am        |   5 +-
>>>>>    nvkm/subdev/dvfs/Makefile.am   |   6 ++
>>>>>    nvkm/subdev/dvfs/base.c        | 129 
>>>>> ++++++++++++++++++++++++++++++++++
>>>>>    nvkm/subdev/dvfs/gk20a.c       | 156
>>>>> +++++++++++++++++++++++++++++++++++++++++
>>>>>    nvkm/subdev/dvfs/priv.h        |  43 ++++++++++++
>>>>>    13 files changed, 376 insertions(+), 2 deletions(-)
>>>>>    create mode 120000 drm/core/include/subdev/dvfs.h
>>>>>    create mode 120000 drm/core/subdev/dvfs/base.c
>>>>>    create mode 120000 drm/core/subdev/dvfs/gk20a.c
>>>>>    create mode 120000 drm/core/subdev/dvfs/priv.h
>>>>>    create mode 100644 nvkm/include/subdev/dvfs.h
>>>>>    create mode 100644 nvkm/subdev/dvfs/Makefile.am
>>>>>    create mode 100644 nvkm/subdev/dvfs/base.c
>>>>>    create mode 100644 nvkm/subdev/dvfs/gk20a.c
>>>>>    create mode 100644 nvkm/subdev/dvfs/priv.h
>>>>>
>>>>> diff --git a/drm/Kbuild b/drm/Kbuild
>>>>> index 061d9faef836..e7eb05e666a0 100644
>>>>> --- a/drm/Kbuild
>>>>> +++ b/drm/Kbuild
>>>>> @@ -227,6 +227,8 @@ nouveau-y += core/subdev/volt/base.o
>>>>>    nouveau-y += core/subdev/volt/gpio.o
>>>>>    nouveau-y += core/subdev/volt/nv40.o
>>>>>    nouveau-y += core/subdev/volt/gk20a.o
>>>>> +nouveau-y += core/subdev/dvfs/base.o
>>>>> +nouveau-y += core/subdev/dvfs/gk20a.o
>>>>>
>>>>>    nouveau-y += core/engine/falcon.o
>>>>>    nouveau-y += core/engine/xtensa.o
>>>>> diff --git a/drm/core/include/subdev/dvfs.h
>>>>> b/drm/core/include/subdev/dvfs.h
>>>>> new file mode 120000
>>>>> index 000000000000..553a6968350b
>>>>> --- /dev/null
>>>>> +++ b/drm/core/include/subdev/dvfs.h
>>>>> @@ -0,0 +1 @@
>>>>> +../../../../nvkm/include/subdev/dvfs.h
>>>>> \ No newline at end of file
>>>>> diff --git a/drm/core/subdev/dvfs/base.c 
>>>>> b/drm/core/subdev/dvfs/base.c
>>>>> new file mode 120000
>>>>> index 000000000000..296334ee52d1
>>>>> --- /dev/null
>>>>> +++ b/drm/core/subdev/dvfs/base.c
>>>>> @@ -0,0 +1 @@
>>>>> +../../../../nvkm/subdev/dvfs/base.c
>>>>> \ No newline at end of file
>>>>> diff --git a/drm/core/subdev/dvfs/gk20a.c 
>>>>> b/drm/core/subdev/dvfs/gk20a.c
>>>>> new file mode 120000
>>>>> index 000000000000..d05d493e3fc9
>>>>> --- /dev/null
>>>>> +++ b/drm/core/subdev/dvfs/gk20a.c
>>>>> @@ -0,0 +1 @@
>>>>> +../../../../nvkm/subdev/dvfs/gk20a.c
>>>>> \ No newline at end of file
>>>>> diff --git a/drm/core/subdev/dvfs/priv.h 
>>>>> b/drm/core/subdev/dvfs/priv.h
>>>>> new file mode 120000
>>>>> index 000000000000..9b471dc44f48
>>>>> --- /dev/null
>>>>> +++ b/drm/core/subdev/dvfs/priv.h
>>>>> @@ -0,0 +1 @@
>>>>> +../../../../nvkm/subdev/dvfs/priv.h
>>>>> \ No newline at end of file
>>>>> diff --git a/nvkm/engine/device/nve0.c b/nvkm/engine/device/nve0.c
>>>>> index ba7922d31351..5a830a37a15d 100644
>>>>> --- a/nvkm/engine/device/nve0.c
>>>>> +++ b/nvkm/engine/device/nve0.c
>>>>> @@ -41,6 +41,7 @@
>>>>>    #include <subdev/bar.h>
>>>>>    #include <subdev/pwr.h>
>>>>>    #include <subdev/volt.h>
>>>>> +#include <subdev/dvfs.h>
>>>>>
>>>>>    #include <engine/device.h>
>>>>>    #include <engine/dmaobj.h>
>>>>> @@ -180,6 +181,7 @@ nve0_identify(struct nouveau_device *device)
>>>>>                   device->oclass[NVDEV_ENGINE_COPY2  ] =
>>>>> &nve0_copy2_oclass;
>>>>>                   device->oclass[NVDEV_ENGINE_PERFMON] =
>>>>> &gk20a_perfmon_oclass;
>>>>>                   device->oclass[NVDEV_SUBDEV_VOLT   ] =
>>>>> &gk20a_volt_oclass;
>>>>> +               device->oclass[NVDEV_SUBDEV_DVFS   ] = 
>>>>> gk20a_dvfs_oclass;
>>>>>                   break;
>>>>>           case 0xf0:
>>>>>                   device->cname = "GK110";
>>>>> diff --git a/nvkm/include/core/device.h b/nvkm/include/core/device.h
>>>>> index 2ec2e50d3676..162fa6a3cfcc 100644
>>>>> --- a/nvkm/include/core/device.h
>>>>> +++ b/nvkm/include/core/device.h
>>>>> @@ -39,6 +39,7 @@ enum nv_subdev_type {
>>>>>           NVDEV_SUBDEV_VOLT,
>>>>>           NVDEV_SUBDEV_THERM,
>>>>>           NVDEV_SUBDEV_CLOCK,
>>>>> +       NVDEV_SUBDEV_DVFS,
>>>>>
>>>>>           NVDEV_ENGINE_FIRST,
>>>>>           NVDEV_ENGINE_DMAOBJ = NVDEV_ENGINE_FIRST,
>>>>> diff --git a/nvkm/include/subdev/dvfs.h b/nvkm/include/subdev/dvfs.h
>>>>> new file mode 100644
>>>>> index 000000000000..3851629a0de4
>>>>> --- /dev/null
>>>>> +++ b/nvkm/include/subdev/dvfs.h
>>>>> @@ -0,0 +1,30 @@
>>>>> +#ifndef __NOUVEAU_DVFS_H__
>>>>> +#define __NOUVEAU_DVFS_H__
>>>>> +
>>>>> +#include <core/subdev.h>
>>>>> +#include <core/device.h>
>>>>> +
>>>>> +#include <subdev/timer.h>
>>>>> +
>>>>> +struct nouveau_dvfs_data {
>>>>> +       int p_load_target;
>>>>> +       int p_load_max;
>>>>> +       int p_smooth;
>>>>> +       unsigned int avg_load;
>>>>> +};
>>>>> +
>>>>> +struct nouveau_dvfs {
>>>>> +       struct nouveau_subdev base;
>>>>> +       struct nouveau_alarm alarm;
>>>>> +       struct nouveau_dvfs_data *data;
>>>>> +};
>>>>> +
>>>>> +static inline struct nouveau_dvfs *
>>>>> +nouveau_dvfs(void *obj)
>>>>> +{
>>>>> +       return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_DVFS];
>>>>> +}
>>>>> +
>>>>> +extern struct nouveau_oclass *gk20a_dvfs_oclass;
>>>>> +
>>>>> +#endif
>>>>> diff --git a/nvkm/subdev/Makefile.am b/nvkm/subdev/Makefile.am
>>>>> index 5a48ddb87981..3d11e0833032 100644
>>>>> --- a/nvkm/subdev/Makefile.am
>>>>> +++ b/nvkm/subdev/Makefile.am
>>>>> @@ -1,5 +1,5 @@
>>>>>    SUBDIRS = bar bus bios clock devinit fb fuse gpio i2c ibus 
>>>>> instmem \
>>>>> -         ltc mc mxm pwr therm timer vm volt
>>>>> +         ltc mc mxm pwr therm timer vm volt dvfs
>>>>>
>>>>>    noinst_LTLIBRARIES = libsubdev.la
>>>>>
>>>>> @@ -22,4 +22,5 @@ libsubdev_la_LIBADD   = bar/libbar.la \
>>>>>                           therm/libtherm.la \
>>>>>                           timer/libtimer.la \
>>>>>                           vm/libvm.la \
>>>>> -                       volt/libvolt.la
>>>>> +                       volt/libvolt.la \
>>>>> +                       dvfs/dvfs.la
>>>>> diff --git a/nvkm/subdev/dvfs/Makefile.am 
>>>>> b/nvkm/subdev/dvfs/Makefile.am
>>>>> new file mode 100644
>>>>> index 000000000000..c09d4ec4124d
>>>>> --- /dev/null
>>>>> +++ b/nvkm/subdev/dvfs/Makefile.am
>>>>> @@ -0,0 +1,6 @@
>>>>> +noinst_LTLIBRARIES = libdvfs.la
>>>>> +
>>>>> +libdvfs_la_SOURCES = base.c \
>>>>> +                     gk20a.c
>>>>> +
>>>>> +include $(srcdir)/../Makefile.subdev
>>>>> diff --git a/nvkm/subdev/dvfs/base.c b/nvkm/subdev/dvfs/base.c
>>>>> new file mode 100644
>>>>> index 000000000000..88ae201840a8
>>>>> --- /dev/null
>>>>> +++ b/nvkm/subdev/dvfs/base.c
>>>>> @@ -0,0 +1,129 @@
>>>>> +/*
>>>>> + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
>>>>> + *
>>>>> + * Permission is hereby granted, free of charge, to any person 
>>>>> obtaining
>>>>> a
>>>>> + * copy of this software and associated documentation files (the
>>>>> "Software"),
>>>>> + * to deal in the Software without restriction, including without
>>>>> limitation
>>>>> + * the rights to use, copy, modify, merge, publish, distribute,
>>>>> sublicense,
>>>>> + * and/or sell copies of the Software, and to permit persons to 
>>>>> whom the
>>>>> + * Software is furnished to do so, subject to the following 
>>>>> conditions:
>>>>> + *
>>>>> + * The above copyright notice and this permission notice shall be
>>>>> included in
>>>>> + * all copies or substantial portions of the Software.
>>>>> + *
>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>> EXPRESS OR
>>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>>> MERCHANTABILITY,
>>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>>>> SHALL
>>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
>>>>> DAMAGES OR
>>>>> OTHER
>>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>>>> ARISING
>>>>> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
>>>>> OTHER
>>>>> + * DEALINGS IN THE SOFTWARE.
>>>>> + */
>>>>> +
>>>>> +#include <core/option.h>
>>>>> +
>>>>> +#include <subdev/clock.h>
>>>>> +#include <subdev/dvfs.h>
>>>>> +#include <subdev/volt.h>
>>>>> +
>>>>> +#include "priv.h"
>>>>> +
>>>>> +static void
>>>>> +_nouveau_dvfs_work(struct nouveau_alarm *alarm)
>>>>> +{
>>>>> +       struct nouveau_dvfs *dvfs = container_of(alarm, struct
>>>>> nouveau_dvfs,
>>>>> +                       alarm);
>>>>> +       struct nouveau_dvfs_data *data = dvfs->data;
>>>>> +       struct nouveau_object *object = nv_object(dvfs);
>>>>> +       struct nouveau_dvfs_impl *impl = (void *)object->oclass;
>>>>> +       struct nouveau_dvfs_dev_status status;
>>>>> +       u32 utilization = 0;
>>>>> +       int state, ret;
>>>>> +
>>>>> +       ret = impl->get_dev_status(dvfs, &status);
>>>>> +       if (ret) {
>>>>> +               nv_warn(dvfs, "failed to get device status\n");
>>>>> +               goto resched;
>>>>> +       }
>>>>> +
>>>>> +       if (status.total)
>>>>> +               utilization = div_u64((u64)status.busy * 100,
>>>>> status.total);
>>>>> +
>>>>> +       data->avg_load = (data->p_smooth * data->avg_load) + 
>>>>> utilization;
>>>>> +       data->avg_load /= data->p_smooth + 1;
>>>>> +       nv_trace(dvfs, "utilization = %d %%, avg_load = %d %%\n",
>>>>> +                       utilization, data->avg_load);
>>>>> +
>>>>> +       ret = impl->get_cur_state(dvfs, &state);
>>>>> +       if (ret) {
>>>>> +               nv_warn(dvfs, "failed to get current state\n");
>>>>> +               goto resched;
>>>>> +       }
>>>>> +
>>>>> +       if (impl->get_target_state(dvfs, &state, data->avg_load)) {
>>>>> +               nv_trace(dvfs, "set new state to %d\n", state);
>>>>> +               impl->target(dvfs, &state);
>>>>> +       }
>>>>> +
>>>>> +resched:
>>>>> +       impl->reset_dev_status(dvfs);
>>>>> +       nouveau_timer_alarm(dvfs, 100000000, alarm);
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +_nouveau_dvfs_fini(struct nouveau_object *object, bool suspend)
>>>>> +{
>>>>> +       struct nouveau_dvfs *dvfs = (void *)object;
>>>>> +
>>>>> +       nouveau_timer_alarm_cancel(dvfs, &dvfs->alarm);
>>>>> +
>>>>> +       return nouveau_subdev_fini(&dvfs->base, suspend);
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +_nouveau_dvfs_init(struct nouveau_object *object)
>>>>> +{
>>>>> +       struct nouveau_dvfs *dvfs = (void *)object;
>>>>> +       struct nouveau_clock *clk = nouveau_clock(object);
>>>>> +       struct nouveau_volt *volt = nouveau_volt(object);
>>>>> +       int ret;
>>>>> +
>>>>> +       ret = nouveau_subdev_init(&dvfs->base);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       if (!clk || !volt)
>>>>> +               return -EINVAL;
>>>>> +
>>>>> +       nouveau_timer_alarm(dvfs, 2000000000, &dvfs->alarm);
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +void
>>>>> +_nouveau_dvfs_dtor(struct nouveau_object *object)
>>>>> +{
>>>>> +       struct nouveau_dvfs *dvfs = (void *)object;
>>>>> +
>>>>> +       nouveau_subdev_destroy(&dvfs->base);
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +nouveau_dvfs_create_(struct nouveau_object *parent,
>>>>> +                       struct nouveau_object *engine,
>>>>> +                       struct nouveau_oclass *oclass,
>>>>> +                       int size, void **pobject)
>>>>> +{
>>>>> +       struct nouveau_dvfs *dvfs;
>>>>> +       int ret;
>>>>> +
>>>>> +       ret = nouveau_subdev_create_(parent, engine, oclass, 0, 
>>>>> "DVFS",
>>>>> +                                    "dvfs", size, pobject);
>>>>> +       dvfs = *pobject;
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       nouveau_alarm_init(&dvfs->alarm, _nouveau_dvfs_work);
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> diff --git a/nvkm/subdev/dvfs/gk20a.c b/nvkm/subdev/dvfs/gk20a.c
>>>>> new file mode 100644
>>>>> index 000000000000..efebd2c10f99
>>>>> --- /dev/null
>>>>> +++ b/nvkm/subdev/dvfs/gk20a.c
>>>>> @@ -0,0 +1,156 @@
>>>>> +/*
>>>>> + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
>>>>> + *
>>>>> + * Permission is hereby granted, free of charge, to any person 
>>>>> obtaining
>>>>> a
>>>>> + * copy of this software and associated documentation files (the
>>>>> "Software"),
>>>>> + * to deal in the Software without restriction, including without
>>>>> limitation
>>>>> + * the rights to use, copy, modify, merge, publish, distribute,
>>>>> sublicense,
>>>>> + * and/or sell copies of the Software, and to permit persons to 
>>>>> whom the
>>>>> + * Software is furnished to do so, subject to the following 
>>>>> conditions:
>>>>> + *
>>>>> + * The above copyright notice and this permission notice shall be
>>>>> included in
>>>>> + * all copies or substantial portions of the Software.
>>>>> + *
>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>> EXPRESS OR
>>>>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>>> MERCHANTABILITY,
>>>>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>>>> SHALL
>>>>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
>>>>> DAMAGES OR
>>>>> OTHER
>>>>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>>>> ARISING
>>>>> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
>>>>> OTHER
>>>>> + * DEALINGS IN THE SOFTWARE.
>>>>> + */
>>>>> +
>>>>> +#include <subdev/clock.h>
>>>>> +#include <subdev/dvfs.h>
>>>>> +#include <subdev/volt.h>
>>>>> +
>>>>> +#include "priv.h"
>>>>> +
>>>>> +#define BUSY_SLOT      0
>>>>> +#define CLK_SLOT       7
>>>>> +
>>>>> +struct gk20a_dvfs_priv {
>>>>> +       struct nouveau_dvfs base;
>>>>> +};
>>>>> +
>>>>> +static int
>>>>> +gk20a_dvfs_target(struct nouveau_dvfs *dvfs, int *state)
>>>>> +{
>>>>> +       struct nouveau_clock *clk = nouveau_clock(dvfs);
>>>>> +
>>>>> +       return nouveau_clock_astate(clk, *state, 0, false);
>>>>> +}
>>>>> +
>>>>> +static int
>>>>> +gk20a_dvfs_get_cur_state(struct nouveau_dvfs *dvfs, int *state)
>>>>> +{
>>>>> +       struct nouveau_clock *clk = nouveau_clock(dvfs);
>>>>> +
>>>>> +       *state = clk->pstate;
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +static int
>>>>> +gk20a_dvfs_get_target_state(struct nouveau_dvfs *dvfs, int 
>>>>> *state, int
>>>>> load)
>>>>> +{
>>>>> +       struct nouveau_dvfs_data *data = dvfs->data;
>>>>> +       struct nouveau_clock *clk = nouveau_clock(dvfs);
>>>>> +       int cur_level, level;
>>>>> +
>>>>> +       /* For GK20A, the performance level is directly mapped to 
>>>>> pstate
>>>>> */
>>>>> +       level = cur_level = clk->pstate;
>>>>> +
>>>>> +       if (load > data->p_load_max) {
>>>>> +               level = min(clk->state_nr - 1, level + 
>>>>> (clk->state_nr /
>>>>> 3));
>>>>> +       } else {
>>>>> +               level += ((load - data->p_load_target) * 10 /
>>>>> +                               data->p_load_target) / 2;
>>>>> +               level = max(0, level);
>>>>> +               level = min(clk->state_nr - 1, level);
>>>>> +       }
>>>>> +
>>>>> +       nv_trace(dvfs, "cur level = %d, new level = %d\n", cur_level,
>>>>> level);
>>>>> +
>>>>> +       *state = level;
>>>>> +
>>>>> +       if (level == cur_level)
>>>>> +               return 0;
>>>>> +       else
>>>>> +               return 1;
>>>>> +}
>>>>> +
>>>>> +static int
>>>>> +gk20a_dvfs_get_dev_status(struct nouveau_dvfs *dvfs,
>>>>> +       struct nouveau_dvfs_dev_status *status)
>>>>> +{
>>>>> +       status->busy = nv_rd32(dvfs, 0x10a508 + (BUSY_SLOT * 0x10));
>>>>> +       status->total= nv_rd32(dvfs, 0x10a508 + (CLK_SLOT * 0x10));
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +gk20a_dvfs_reset_dev_status(struct nouveau_dvfs *dvfs)
>>>>> +{
>>>>> +       nv_wr32(dvfs, 0x10a508 + (BUSY_SLOT * 0x10), 0x80000000);
>>>>> +       nv_wr32(dvfs, 0x10a508 + (CLK_SLOT * 0x10), 0x80000000);
>>>>> +}
>>>>> +
>>>>> +static int
>>>>> +gk20a_dvfs_init(struct nouveau_object *object)
>>>>> +{
>>>>> +       struct gk20a_dvfs_priv *priv = (void *)object;
>>>>> +       int ret;
>>>>> +
>>>>> +       /* init pwr perf counter */
>>>>> +       nv_wr32(priv, 0x10a504 + (BUSY_SLOT * 0x10), 0x00200001);
>>>>> +       nv_wr32(priv, 0x10a50c + (BUSY_SLOT * 0x10), 0x00000002);
>>>>> +       nv_wr32(priv, 0x10a50c + (CLK_SLOT * 0x10), 0x00000003);
>>>>> +
>>>>> +       ret = nouveau_dvfs_init(&priv->base);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +struct nouveau_dvfs_data gk20a_dvfs_data = {
>>>>> +       .p_load_target = 70,
>>>>> +       .p_load_max = 90,
>>>>> +       .p_smooth = 1,
>>>>> +};
>>>>> +
>>>>> +static int
>>>>> +gk20a_dvfs_ctor(struct nouveau_object *parent, struct nouveau_object
>>>>> *engine,
>>>>> +            struct nouveau_oclass *oclass, void *data, u32 size,
>>>>> +            struct nouveau_object **pobject)
>>>>> +{
>>>>> +       struct gk20a_dvfs_priv *priv;
>>>>> +       struct nouveau_dvfs *dvfs;
>>>>> +       int ret;
>>>>> +
>>>>> +       ret = nouveau_dvfs_create(parent, engine, oclass, &priv);
>>>>> +       *pobject = nv_object(priv);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +
>>>>> +       dvfs = &priv->base;
>>>>> +       dvfs->data = &gk20a_dvfs_data;
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +struct nouveau_oclass *
>>>>> +gk20a_dvfs_oclass = &(struct nouveau_dvfs_impl) {
>>>>> +       .base.handle = NV_SUBDEV(DVFS, 0xea),
>>>>> +       .base.ofuncs = &(struct nouveau_ofuncs) {
>>>>> +               .ctor = gk20a_dvfs_ctor,
>>>>> +               .dtor = _nouveau_dvfs_dtor,
>>>>> +               .init = gk20a_dvfs_init,
>>>>> +               .fini = _nouveau_dvfs_fini,
>>>>> +       },
>>>>> +       .get_dev_status = gk20a_dvfs_get_dev_status,
>>>>> +       .reset_dev_status = gk20a_dvfs_reset_dev_status,
>>>>> +       .target = gk20a_dvfs_target,
>>>>> +       .get_cur_state = gk20a_dvfs_get_cur_state,
>>>>> +       .get_target_state = gk20a_dvfs_get_target_state,
>>>>> +}.base;
>>>>> diff --git a/nvkm/subdev/dvfs/priv.h b/nvkm/subdev/dvfs/priv.h
>>>>> new file mode 100644
>>>>> index 000000000000..95c8a00a01c1
>>>>> --- /dev/null
>>>>> +++ b/nvkm/subdev/dvfs/priv.h
>>>>> @@ -0,0 +1,43 @@
>>>>> +#ifndef __NVKM_DVFS_PRIV_H__
>>>>> +#define __NVKM_DVFS_PRIV_H__
>>>>> +
>>>>> +#include <subdev/dvfs.h>
>>>>> +
>>>>> +struct nouveau_dvfs_dev_status {
>>>>> +       unsigned long total;
>>>>> +       unsigned long busy;
>>>>> +       int cur_state;
>>>>> +};
>>>>> +
>>>>> +struct nouveau_dvfs_impl {
>>>>> +       struct nouveau_oclass base;
>>>>> +       int (*get_dev_status)(struct nouveau_dvfs *,
>>>>> +                               struct nouveau_dvfs_dev_status *);
>>>>> +       void (*reset_dev_status)(struct nouveau_dvfs *);
>>>>> +       int (*target)(struct nouveau_dvfs *, int *state);
>>>>> +       int (*get_cur_state)(struct nouveau_dvfs *, int *state);
>>>>> +       int (*get_target_state)(struct nouveau_dvfs *, int *state, 
>>>>> int
>>>>> load);
>>>>> +};
>>>>> +
>>>>> +#define nouveau_dvfs_create(p,e,o,d)
>>>>> \
>>>>> +       nouveau_dvfs_create_((p), (e), (o), sizeof(**d), (void **)d)
>>>>> +#define nouveau_dvfs_destroy(p) ({
>>>>> \
>>>>> +       struct nouveau_dvfs *d = (p);
>>>>> \
>>>>> +       _nouveau_dvfs_dtor(nv_object(d));
>>>>> \
>>>>> +})
>>>>> +#define nouveau_dvfs_init(p) ({
>>>>> \
>>>>> +       struct nouveau_dvfs *d = (p);
>>>>> \
>>>>> +       _nouveau_dvfs_init(nv_object(d));
>>>>> \
>>>>> +})
>>>>> +#define nouveau_dvfs_fini(p,s) ({
>>>>> \
>>>>> +       struct nouveau_dvfs *d = (p);
>>>>> \
>>>>> +       _nouveau_dvfs_fini(nv_object(d), (s));
>>>>> \
>>>>> +})
>>>>> +
>>>>> +int nouveau_dvfs_create_(struct nouveau_object *, struct 
>>>>> nouveau_object
>>>>> *,
>>>>> +                           struct nouveau_oclass *, int, void **);
>>>>> +void _nouveau_dvfs_dtor(struct nouveau_object *);
>>>>> +int _nouveau_dvfs_init(struct nouveau_object *);
>>>>> +int _nouveau_dvfs_fini(struct nouveau_object *, bool suspend);
>>>>> +
>>>>> +#endif
>>>>> -- 
>>>>> 1.9.1
>>>>>
>>>>> _______________________________________________
>>>>> Nouveau mailing list
>>>>> Nouveau at lists.freedesktop.org
>>>>> http://lists.freedesktop.org/mailman/listinfo/nouveau
>>>
>>>
>>> ----------------------------------------------------------------------------------- 
>>>
>>> This email message is for the sole use of the intended recipient(s) 
>>> and may
>>> contain
>>> confidential information.  Any unauthorized review, use, disclosure or
>>> distribution
>>> is prohibited.  If you are not the intended recipient, please 
>>> contact the
>>> sender by
>>> reply email and destroy all copies of the original message.
>>> ----------------------------------------------------------------------------------- 
>>>
>> _______________________________________________
>> Nouveau mailing list
>> Nouveau at lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/nouveau
>



More information about the Nouveau mailing list