xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nvkm/subdev/pmu/nouveau_nvkm_subdev_pmu_gk20a.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
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
gk20a_pmu_dvfs_target(struct gk20a_pmu * pmu,int * state)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
gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu * pmu,int * state)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
gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu * pmu,int * state,int load)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
gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu * pmu,struct gk20a_pmu_dvfs_dev_status * status)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
gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu * pmu)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
gk20a_pmu_dvfs_work(struct nvkm_alarm * alarm)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
gk20a_pmu_fini(struct nvkm_pmu * pmu)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
gk20a_pmu_init(struct nvkm_pmu * pmu)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
gk20a_pmu_new(struct nvkm_device * device,int index,struct nvkm_pmu ** ppmu)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