xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1ebb58dc2SBen Skeggs /*
2ebb58dc2SBen Skeggs  * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
3ebb58dc2SBen Skeggs  *
4ebb58dc2SBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5ebb58dc2SBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6ebb58dc2SBen Skeggs  * to deal in the Software without restriction, including without limitation
7ebb58dc2SBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8ebb58dc2SBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9ebb58dc2SBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10ebb58dc2SBen Skeggs  *
11ebb58dc2SBen Skeggs  * The above copyright notice and this permission notice shall be included in
12ebb58dc2SBen Skeggs  * all copies or substantial portions of the Software.
13ebb58dc2SBen Skeggs  *
14ebb58dc2SBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15ebb58dc2SBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16ebb58dc2SBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17ebb58dc2SBen Skeggs  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18ebb58dc2SBen Skeggs  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19ebb58dc2SBen Skeggs  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20ebb58dc2SBen Skeggs  * DEALINGS IN THE SOFTWARE.
21ebb58dc2SBen Skeggs  */
22d8711c5aSAlexandre Courbot #define gk20a_pmu(p) container_of((p), struct gk20a_pmu, base)
23ebb58dc2SBen Skeggs #include "priv.h"
24ebb58dc2SBen Skeggs 
25ebb58dc2SBen Skeggs #include <subdev/clk.h>
26ebb58dc2SBen Skeggs #include <subdev/timer.h>
27ebb58dc2SBen Skeggs #include <subdev/volt.h>
28ebb58dc2SBen Skeggs 
29ebb58dc2SBen Skeggs #define BUSY_SLOT	0
30ebb58dc2SBen Skeggs #define CLK_SLOT	7
31ebb58dc2SBen Skeggs 
32ebb58dc2SBen Skeggs struct gk20a_pmu_dvfs_data {
33ebb58dc2SBen Skeggs 	int p_load_target;
34ebb58dc2SBen Skeggs 	int p_load_max;
35ebb58dc2SBen Skeggs 	int p_smooth;
36ebb58dc2SBen Skeggs 	unsigned int avg_load;
37ebb58dc2SBen Skeggs };
38ebb58dc2SBen Skeggs 
395a7d1e22SBen Skeggs struct gk20a_pmu {
4021b13791SBen Skeggs 	struct nvkm_pmu base;
4121b13791SBen Skeggs 	struct nvkm_alarm alarm;
42ebb58dc2SBen Skeggs 	struct gk20a_pmu_dvfs_data *data;
43ebb58dc2SBen Skeggs };
44ebb58dc2SBen Skeggs 
45ebb58dc2SBen Skeggs struct gk20a_pmu_dvfs_dev_status {
4668d82161SAlexandre Courbot 	u32 total;
4768d82161SAlexandre Courbot 	u32 busy;
48ebb58dc2SBen Skeggs };
49ebb58dc2SBen Skeggs 
50ebb58dc2SBen Skeggs static int
gk20a_pmu_dvfs_target(struct gk20a_pmu * pmu,int * state)515a7d1e22SBen Skeggs gk20a_pmu_dvfs_target(struct gk20a_pmu *pmu, int *state)
52ebb58dc2SBen Skeggs {
536625f55cSBen Skeggs 	struct nvkm_clk *clk = pmu->base.subdev.device->clk;
54ebb58dc2SBen Skeggs 
5521b13791SBen Skeggs 	return nvkm_clk_astate(clk, *state, 0, false);
56ebb58dc2SBen Skeggs }
57ebb58dc2SBen Skeggs 
5868d82161SAlexandre Courbot static void
gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu * pmu,int * state)595a7d1e22SBen Skeggs gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu *pmu, int *state)
60ebb58dc2SBen Skeggs {
616625f55cSBen Skeggs 	struct nvkm_clk *clk = pmu->base.subdev.device->clk;
62ebb58dc2SBen Skeggs 
63ebb58dc2SBen Skeggs 	*state = clk->pstate;
64ebb58dc2SBen Skeggs }
65ebb58dc2SBen Skeggs 
66ebb58dc2SBen Skeggs static int
gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu * pmu,int * state,int load)675a7d1e22SBen Skeggs gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu *pmu,
68ebb58dc2SBen Skeggs 				int *state, int load)
69ebb58dc2SBen Skeggs {
705a7d1e22SBen Skeggs 	struct gk20a_pmu_dvfs_data *data = pmu->data;
716625f55cSBen Skeggs 	struct nvkm_clk *clk = pmu->base.subdev.device->clk;
72ebb58dc2SBen Skeggs 	int cur_level, level;
73ebb58dc2SBen Skeggs 
74ebb58dc2SBen Skeggs 	/* For GK20A, the performance level is directly mapped to pstate */
75ebb58dc2SBen Skeggs 	level = cur_level = clk->pstate;
76ebb58dc2SBen Skeggs 
77ebb58dc2SBen Skeggs 	if (load > data->p_load_max) {
78ebb58dc2SBen Skeggs 		level = min(clk->state_nr - 1, level + (clk->state_nr / 3));
79ebb58dc2SBen Skeggs 	} else {
80ebb58dc2SBen Skeggs 		level += ((load - data->p_load_target) * 10 /
81ebb58dc2SBen Skeggs 				data->p_load_target) / 2;
82ebb58dc2SBen Skeggs 		level = max(0, level);
83ebb58dc2SBen Skeggs 		level = min(clk->state_nr - 1, level);
84ebb58dc2SBen Skeggs 	}
85ebb58dc2SBen Skeggs 
86c19e329dSBen Skeggs 	nvkm_trace(&pmu->base.subdev, "cur level = %d, new level = %d\n",
87c19e329dSBen Skeggs 		   cur_level, level);
88ebb58dc2SBen Skeggs 
89ebb58dc2SBen Skeggs 	*state = level;
90ebb58dc2SBen Skeggs 
9168d82161SAlexandre Courbot 	return (level != cur_level);
92ebb58dc2SBen Skeggs }
93ebb58dc2SBen Skeggs 
9468d82161SAlexandre Courbot static void
gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu * pmu,struct gk20a_pmu_dvfs_dev_status * status)955a7d1e22SBen Skeggs gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu *pmu,
96ebb58dc2SBen Skeggs 			      struct gk20a_pmu_dvfs_dev_status *status)
97ebb58dc2SBen Skeggs {
982952a2b4SBen Skeggs 	struct nvkm_falcon *falcon = &pmu->base.falcon;
999b071c79SAlexandre Courbot 
1009b071c79SAlexandre Courbot 	status->busy = nvkm_falcon_rd32(falcon, 0x508 + (BUSY_SLOT * 0x10));
1019b071c79SAlexandre Courbot 	status->total= nvkm_falcon_rd32(falcon, 0x508 + (CLK_SLOT * 0x10));
102ebb58dc2SBen Skeggs }
103ebb58dc2SBen Skeggs 
104ebb58dc2SBen Skeggs static void
gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu * pmu)1055a7d1e22SBen Skeggs gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu *pmu)
106ebb58dc2SBen Skeggs {
1072952a2b4SBen Skeggs 	struct nvkm_falcon *falcon = &pmu->base.falcon;
1089b071c79SAlexandre Courbot 
1099b071c79SAlexandre Courbot 	nvkm_falcon_wr32(falcon, 0x508 + (BUSY_SLOT * 0x10), 0x80000000);
1109b071c79SAlexandre Courbot 	nvkm_falcon_wr32(falcon, 0x508 + (CLK_SLOT * 0x10), 0x80000000);
111ebb58dc2SBen Skeggs }
112ebb58dc2SBen Skeggs 
113ebb58dc2SBen Skeggs static void
gk20a_pmu_dvfs_work(struct nvkm_alarm * alarm)11421b13791SBen Skeggs gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm)
115ebb58dc2SBen Skeggs {
1165a7d1e22SBen Skeggs 	struct gk20a_pmu *pmu =
1175a7d1e22SBen Skeggs 		container_of(alarm, struct gk20a_pmu, alarm);
1185a7d1e22SBen Skeggs 	struct gk20a_pmu_dvfs_data *data = pmu->data;
119ebb58dc2SBen Skeggs 	struct gk20a_pmu_dvfs_dev_status status;
120c19e329dSBen Skeggs 	struct nvkm_subdev *subdev = &pmu->base.subdev;
121c19e329dSBen Skeggs 	struct nvkm_device *device = subdev->device;
122c19e329dSBen Skeggs 	struct nvkm_clk *clk = device->clk;
12331649ecfSBen Skeggs 	struct nvkm_timer *tmr = device->timer;
124c19e329dSBen Skeggs 	struct nvkm_volt *volt = device->volt;
125ebb58dc2SBen Skeggs 	u32 utilization = 0;
12668d82161SAlexandre Courbot 	int state;
127ebb58dc2SBen Skeggs 
128ebb58dc2SBen Skeggs 	/*
129ebb58dc2SBen Skeggs 	 * The PMU is initialized before CLK and VOLT, so we have to make sure the
130ebb58dc2SBen Skeggs 	 * CLK and VOLT are ready here.
131ebb58dc2SBen Skeggs 	 */
132ebb58dc2SBen Skeggs 	if (!clk || !volt)
133ebb58dc2SBen Skeggs 		goto resched;
134ebb58dc2SBen Skeggs 
13568d82161SAlexandre Courbot 	gk20a_pmu_dvfs_get_dev_status(pmu, &status);
136ebb58dc2SBen Skeggs 
137ebb58dc2SBen Skeggs 	if (status.total)
138ebb58dc2SBen Skeggs 		utilization = div_u64((u64)status.busy * 100, status.total);
139ebb58dc2SBen Skeggs 
140ebb58dc2SBen Skeggs 	data->avg_load = (data->p_smooth * data->avg_load) + utilization;
141ebb58dc2SBen Skeggs 	data->avg_load /= data->p_smooth + 1;
142c19e329dSBen Skeggs 	nvkm_trace(subdev, "utilization = %d %%, avg_load = %d %%\n",
143ebb58dc2SBen Skeggs 		   utilization, data->avg_load);
144ebb58dc2SBen Skeggs 
14568d82161SAlexandre Courbot 	gk20a_pmu_dvfs_get_cur_state(pmu, &state);
146ebb58dc2SBen Skeggs 
1475a7d1e22SBen Skeggs 	if (gk20a_pmu_dvfs_get_target_state(pmu, &state, data->avg_load)) {
148c19e329dSBen Skeggs 		nvkm_trace(subdev, "set new state to %d\n", state);
1495a7d1e22SBen Skeggs 		gk20a_pmu_dvfs_target(pmu, &state);
150ebb58dc2SBen Skeggs 	}
151ebb58dc2SBen Skeggs 
152ebb58dc2SBen Skeggs resched:
1535a7d1e22SBen Skeggs 	gk20a_pmu_dvfs_reset_dev_status(pmu);
15431649ecfSBen Skeggs 	nvkm_timer_alarm(tmr, 100000000, alarm);
155ebb58dc2SBen Skeggs }
156ebb58dc2SBen Skeggs 
157d8711c5aSAlexandre Courbot static void
gk20a_pmu_fini(struct nvkm_pmu * pmu)158d8711c5aSAlexandre Courbot gk20a_pmu_fini(struct nvkm_pmu *pmu)
159ebb58dc2SBen Skeggs {
160d8711c5aSAlexandre Courbot 	struct gk20a_pmu *gpmu = gk20a_pmu(pmu);
1617eaf1198SBen Skeggs 	nvkm_timer_alarm(pmu->subdev.device->timer, 0, &gpmu->alarm);
1629b071c79SAlexandre Courbot 
1632952a2b4SBen Skeggs 	nvkm_falcon_put(&pmu->falcon, &pmu->subdev);
164ebb58dc2SBen Skeggs }
165ebb58dc2SBen Skeggs 
1667120908dSAlexandre Courbot static int
gk20a_pmu_init(struct nvkm_pmu * pmu)167d8711c5aSAlexandre Courbot gk20a_pmu_init(struct nvkm_pmu *pmu)
168ebb58dc2SBen Skeggs {
169d8711c5aSAlexandre Courbot 	struct gk20a_pmu *gpmu = gk20a_pmu(pmu);
1709b071c79SAlexandre Courbot 	struct nvkm_subdev *subdev = &pmu->subdev;
171d8711c5aSAlexandre Courbot 	struct nvkm_device *device = pmu->subdev.device;
1722952a2b4SBen Skeggs 	struct nvkm_falcon *falcon = &pmu->falcon;
1739b071c79SAlexandre Courbot 	int ret;
1749b071c79SAlexandre Courbot 
1759b071c79SAlexandre Courbot 	ret = nvkm_falcon_get(falcon, subdev);
1769b071c79SAlexandre Courbot 	if (ret) {
1779b071c79SAlexandre Courbot 		nvkm_error(subdev, "cannot acquire %s falcon!\n", falcon->name);
1789b071c79SAlexandre Courbot 		return ret;
1799b071c79SAlexandre Courbot 	}
180ebb58dc2SBen Skeggs 
181ebb58dc2SBen Skeggs 	/* init pwr perf counter */
1829b071c79SAlexandre Courbot 	nvkm_falcon_wr32(falcon, 0x504 + (BUSY_SLOT * 0x10), 0x00200001);
1839b071c79SAlexandre Courbot 	nvkm_falcon_wr32(falcon, 0x50c + (BUSY_SLOT * 0x10), 0x00000002);
1849b071c79SAlexandre Courbot 	nvkm_falcon_wr32(falcon, 0x50c + (CLK_SLOT * 0x10), 0x00000003);
185ebb58dc2SBen Skeggs 
186d8711c5aSAlexandre Courbot 	nvkm_timer_alarm(device->timer, 2000000000, &gpmu->alarm);
187e2ca4e7dSBen Skeggs 	return 0;
188ebb58dc2SBen Skeggs }
189ebb58dc2SBen Skeggs 
1907120908dSAlexandre Courbot static struct gk20a_pmu_dvfs_data
1917120908dSAlexandre Courbot gk20a_dvfs_data= {
192ebb58dc2SBen Skeggs 	.p_load_target = 70,
193ebb58dc2SBen Skeggs 	.p_load_max = 90,
194ebb58dc2SBen Skeggs 	.p_smooth = 1,
195ebb58dc2SBen Skeggs };
196ebb58dc2SBen Skeggs 
197d8711c5aSAlexandre Courbot static const struct nvkm_pmu_func
198e2ca4e7dSBen Skeggs gk20a_pmu = {
1992952a2b4SBen Skeggs 	.flcn = &gt215_pmu_flcn,
200e2ca4e7dSBen Skeggs 	.init = gk20a_pmu_init,
201e2ca4e7dSBen Skeggs 	.fini = gk20a_pmu_fini,
202715e7d26SBen Skeggs 	.reset = gf100_pmu_reset,
203e2ca4e7dSBen Skeggs };
204e2ca4e7dSBen Skeggs 
205989863d7SBen Skeggs static const struct nvkm_pmu_fwif
206989863d7SBen Skeggs gk20a_pmu_fwif[] = {
207989863d7SBen Skeggs 	{ -1, gf100_pmu_nofw, &gk20a_pmu },
208989863d7SBen Skeggs 	{}
209989863d7SBen Skeggs };
210989863d7SBen Skeggs 
211e2ca4e7dSBen Skeggs int
gk20a_pmu_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_pmu ** ppmu)212*e4b15b4cSBen Skeggs gk20a_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
213*e4b15b4cSBen Skeggs 	      struct nvkm_pmu **ppmu)
214ebb58dc2SBen Skeggs {
2155a7d1e22SBen Skeggs 	struct gk20a_pmu *pmu;
216989863d7SBen Skeggs 	int ret;
217ebb58dc2SBen Skeggs 
218e2ca4e7dSBen Skeggs 	if (!(pmu = kzalloc(sizeof(*pmu), GFP_KERNEL)))
219e2ca4e7dSBen Skeggs 		return -ENOMEM;
220e2ca4e7dSBen Skeggs 	*ppmu = &pmu->base;
221ebb58dc2SBen Skeggs 
222*e4b15b4cSBen Skeggs 	ret = nvkm_pmu_ctor(gk20a_pmu_fwif, device, type, inst, &pmu->base);
223989863d7SBen Skeggs 	if (ret)
224989863d7SBen Skeggs 		return ret;
225d8711c5aSAlexandre Courbot 
2265a7d1e22SBen Skeggs 	pmu->data = &gk20a_dvfs_data;
2275a7d1e22SBen Skeggs 	nvkm_alarm_init(&pmu->alarm, gk20a_pmu_dvfs_work);
228ebb58dc2SBen Skeggs 	return 0;
229ebb58dc2SBen Skeggs }
230