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 = >215_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