Home | History | Annotate | Line # | Download | only in pmu
      1 /*	$NetBSD: nouveau_nvkm_subdev_pmu_gk20a.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     22  * DEALINGS IN THE SOFTWARE.
     23  */
     24 #include <sys/cdefs.h>
     25 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_pmu_gk20a.c,v 1.3 2021/12/18 23:45:41 riastradh Exp $");
     26 
     27 #define gk20a_pmu(p) container_of((p), struct gk20a_pmu, base)
     28 #include "priv.h"
     29 
     30 #include <subdev/clk.h>
     31 #include <subdev/timer.h>
     32 #include <subdev/volt.h>
     33 
     34 #define BUSY_SLOT	0
     35 #define CLK_SLOT	7
     36 
     37 struct gk20a_pmu_dvfs_data {
     38 	int p_load_target;
     39 	int p_load_max;
     40 	int p_smooth;
     41 	unsigned int avg_load;
     42 };
     43 
     44 struct gk20a_pmu {
     45 	struct nvkm_pmu base;
     46 	struct nvkm_alarm alarm;
     47 	struct gk20a_pmu_dvfs_data *data;
     48 };
     49 
     50 struct gk20a_pmu_dvfs_dev_status {
     51 	u32 total;
     52 	u32 busy;
     53 };
     54 
     55 static int
     56 gk20a_pmu_dvfs_target(struct gk20a_pmu *pmu, int *state)
     57 {
     58 	struct nvkm_clk *clk = pmu->base.subdev.device->clk;
     59 
     60 	return nvkm_clk_astate(clk, *state, 0, false);
     61 }
     62 
     63 static void
     64 gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu *pmu, int *state)
     65 {
     66 	struct nvkm_clk *clk = pmu->base.subdev.device->clk;
     67 
     68 	*state = clk->pstate;
     69 }
     70 
     71 static int
     72 gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu *pmu,
     73 				int *state, int load)
     74 {
     75 	struct gk20a_pmu_dvfs_data *data = pmu->data;
     76 	struct nvkm_clk *clk = pmu->base.subdev.device->clk;
     77 	int cur_level, level;
     78 
     79 	/* For GK20A, the performance level is directly mapped to pstate */
     80 	level = cur_level = clk->pstate;
     81 
     82 	if (load > data->p_load_max) {
     83 		level = min(clk->state_nr - 1, level + (clk->state_nr / 3));
     84 	} else {
     85 		level += ((load - data->p_load_target) * 10 /
     86 				data->p_load_target) / 2;
     87 		level = max(0, level);
     88 		level = min(clk->state_nr - 1, level);
     89 	}
     90 
     91 	nvkm_trace(&pmu->base.subdev, "cur level = %d, new level = %d\n",
     92 		   cur_level, level);
     93 
     94 	*state = level;
     95 
     96 	return (level != cur_level);
     97 }
     98 
     99 static void
    100 gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu *pmu,
    101 			      struct gk20a_pmu_dvfs_dev_status *status)
    102 {
    103 	struct nvkm_falcon *falcon = &pmu->base.falcon;
    104 
    105 	status->busy = nvkm_falcon_rd32(falcon, 0x508 + (BUSY_SLOT * 0x10));
    106 	status->total= nvkm_falcon_rd32(falcon, 0x508 + (CLK_SLOT * 0x10));
    107 }
    108 
    109 static void
    110 gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu *pmu)
    111 {
    112 	struct nvkm_falcon *falcon = &pmu->base.falcon;
    113 
    114 	nvkm_falcon_wr32(falcon, 0x508 + (BUSY_SLOT * 0x10), 0x80000000);
    115 	nvkm_falcon_wr32(falcon, 0x508 + (CLK_SLOT * 0x10), 0x80000000);
    116 }
    117 
    118 static void
    119 gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm)
    120 {
    121 	struct gk20a_pmu *pmu =
    122 		container_of(alarm, struct gk20a_pmu, alarm);
    123 	struct gk20a_pmu_dvfs_data *data = pmu->data;
    124 	struct gk20a_pmu_dvfs_dev_status status;
    125 	struct nvkm_subdev *subdev = &pmu->base.subdev;
    126 	struct nvkm_device *device = subdev->device;
    127 	struct nvkm_clk *clk = device->clk;
    128 	struct nvkm_timer *tmr = device->timer;
    129 	struct nvkm_volt *volt = device->volt;
    130 	u32 utilization = 0;
    131 	int state;
    132 
    133 	/*
    134 	 * The PMU is initialized before CLK and VOLT, so we have to make sure the
    135 	 * CLK and VOLT are ready here.
    136 	 */
    137 	if (!clk || !volt)
    138 		goto resched;
    139 
    140 	gk20a_pmu_dvfs_get_dev_status(pmu, &status);
    141 
    142 	if (status.total)
    143 		utilization = div_u64((u64)status.busy * 100, status.total);
    144 
    145 	data->avg_load = (data->p_smooth * data->avg_load) + utilization;
    146 	data->avg_load /= data->p_smooth + 1;
    147 	nvkm_trace(subdev, "utilization = %d %%, avg_load = %d %%\n",
    148 		   utilization, data->avg_load);
    149 
    150 	gk20a_pmu_dvfs_get_cur_state(pmu, &state);
    151 
    152 	if (gk20a_pmu_dvfs_get_target_state(pmu, &state, data->avg_load)) {
    153 		nvkm_trace(subdev, "set new state to %d\n", state);
    154 		gk20a_pmu_dvfs_target(pmu, &state);
    155 	}
    156 
    157 resched:
    158 	gk20a_pmu_dvfs_reset_dev_status(pmu);
    159 	nvkm_timer_alarm(tmr, 100000000, alarm);
    160 }
    161 
    162 static void
    163 gk20a_pmu_fini(struct nvkm_pmu *pmu)
    164 {
    165 	struct gk20a_pmu *gpmu = gk20a_pmu(pmu);
    166 	nvkm_timer_alarm(pmu->subdev.device->timer, 0, &gpmu->alarm);
    167 
    168 	nvkm_falcon_put(&pmu->falcon, &pmu->subdev);
    169 }
    170 
    171 static int
    172 gk20a_pmu_init(struct nvkm_pmu *pmu)
    173 {
    174 	struct gk20a_pmu *gpmu = gk20a_pmu(pmu);
    175 	struct nvkm_subdev *subdev = &pmu->subdev;
    176 	struct nvkm_device *device = pmu->subdev.device;
    177 	struct nvkm_falcon *falcon = &pmu->falcon;
    178 	int ret;
    179 
    180 	ret = nvkm_falcon_get(falcon, subdev);
    181 	if (ret) {
    182 		nvkm_error(subdev, "cannot acquire %s falcon!\n", falcon->name);
    183 		return ret;
    184 	}
    185 
    186 	/* init pwr perf counter */
    187 	nvkm_falcon_wr32(falcon, 0x504 + (BUSY_SLOT * 0x10), 0x00200001);
    188 	nvkm_falcon_wr32(falcon, 0x50c + (BUSY_SLOT * 0x10), 0x00000002);
    189 	nvkm_falcon_wr32(falcon, 0x50c + (CLK_SLOT * 0x10), 0x00000003);
    190 
    191 	nvkm_timer_alarm(device->timer, 2000000000, &gpmu->alarm);
    192 	return 0;
    193 }
    194 
    195 static struct gk20a_pmu_dvfs_data
    196 gk20a_dvfs_data= {
    197 	.p_load_target = 70,
    198 	.p_load_max = 90,
    199 	.p_smooth = 1,
    200 };
    201 
    202 static const struct nvkm_pmu_func
    203 gk20a_pmu = {
    204 	.flcn = &gt215_pmu_flcn,
    205 	.enabled = gf100_pmu_enabled,
    206 	.init = gk20a_pmu_init,
    207 	.fini = gk20a_pmu_fini,
    208 	.reset = gf100_pmu_reset,
    209 };
    210 
    211 static const struct nvkm_pmu_fwif
    212 gk20a_pmu_fwif[] = {
    213 	{ -1, gf100_pmu_nofw, &gk20a_pmu },
    214 	{}
    215 };
    216 
    217 int
    218 gk20a_pmu_new(struct nvkm_device *device, int index, struct nvkm_pmu **ppmu)
    219 {
    220 	struct gk20a_pmu *pmu;
    221 	int ret;
    222 
    223 	if (!(pmu = kzalloc(sizeof(*pmu), GFP_KERNEL)))
    224 		return -ENOMEM;
    225 	*ppmu = &pmu->base;
    226 
    227 	ret = nvkm_pmu_ctor(gk20a_pmu_fwif, device, index, &pmu->base);
    228 	if (ret)
    229 		return ret;
    230 
    231 	pmu->data = &gk20a_dvfs_data;
    232 	nvkm_alarm_init(&pmu->alarm, gk20a_pmu_dvfs_work);
    233 	return 0;
    234 }
    235