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