summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/clk/clk_arb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/clk/clk_arb.c')
-rw-r--r--drivers/gpu/nvgpu/clk/clk_arb.c387
1 files changed, 387 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/clk/clk_arb.c b/drivers/gpu/nvgpu/clk/clk_arb.c
new file mode 100644
index 00000000..1d02c7d7
--- /dev/null
+++ b/drivers/gpu/nvgpu/clk/clk_arb.c
@@ -0,0 +1,387 @@
1/*
2 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 */
13
14#include "gk20a/gk20a.h"
15
16#include <linux/cdev.h>
17#include <linux/file.h>
18#include <linux/anon_inodes.h>
19#include <linux/nvgpu.h>
20#include <linux/bitops.h>
21
22#include "clk/clk_arb.h"
23
24static int nvgpu_clk_arb_release_session_dev(struct inode *inode, struct file *filp);
25static unsigned int nvgpu_clk_arb_poll_session_dev(struct file *filp, poll_table *wait);
26
27static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work);
28
29struct nvgpu_clk_arb {
30 struct mutex wlock;
31 struct mutex users_lock;
32 struct list_head users;
33 u32 gpc2clk_current_mhz;
34 u32 gpc2clk_target_mhz;
35 u32 gpc2clk_default_mhz;
36 u32 mclk_current_mhz;
37 u32 mclk_target_mhz;
38 u32 mclk_default_mhz;
39 atomic_t usercount;
40 struct work_struct update_fn_work;
41
42 atomic_t req_nr; /* for allocations */
43 atomic_t last_req_nr; /* last completed by arbiter */
44};
45
46struct nvgpu_clk_session {
47 struct gk20a *g;
48 int fd;
49 atomic_t req_nr;
50 struct kref refcount;
51 wait_queue_head_t readout_wq;
52 atomic_t poll_mask;
53 struct list_head user;
54 u32 gpc2clk_target_mhz;
55 u32 mclk_target_mhz;
56};
57
58const struct file_operations clk_dev_ops = {
59 .owner = THIS_MODULE,
60 .release = nvgpu_clk_arb_release_session_dev,
61 .poll = nvgpu_clk_arb_poll_session_dev,
62};
63
64int nvgpu_clk_arb_init_arbiter(struct gk20a *g)
65{
66 struct nvgpu_clk_arb *arb;
67 u16 default_mhz;
68 int err;
69
70 gk20a_dbg_fn("");
71
72 if (!g->ops.clk_arb.get_arbiter_clk_domains)
73 return 0;
74
75 arb = kzalloc(sizeof(struct nvgpu_clk_arb), GFP_KERNEL);
76 if (!arb)
77 return -ENOMEM;
78
79 g->clk_arb = arb;
80
81 mutex_init(&arb->wlock);
82 mutex_init(&arb->users_lock);
83
84 err = g->ops.clk_arb.get_arbiter_clk_default(g,
85 NVGPU_GPU_CLK_DOMAIN_MCLK, &default_mhz);
86 if (err)
87 return -EINVAL;
88
89 arb->mclk_target_mhz = default_mhz;
90 arb->mclk_current_mhz = default_mhz;
91 arb->mclk_default_mhz = default_mhz;
92
93 err = g->ops.clk_arb.get_arbiter_clk_default(g,
94 NVGPU_GPU_CLK_DOMAIN_GPC2CLK, &default_mhz);
95 if (err)
96 return -EINVAL;
97
98 arb->gpc2clk_target_mhz = default_mhz;
99 arb->gpc2clk_current_mhz = default_mhz;
100 arb->gpc2clk_default_mhz = default_mhz;
101
102 atomic_set(&arb->usercount, 0);
103 atomic_set(&arb->req_nr, 0);
104 atomic_set(&arb->last_req_nr, 0);
105
106 INIT_LIST_HEAD(&arb->users);
107 INIT_WORK(&arb->update_fn_work, nvgpu_clk_arb_run_arbiter_cb);
108
109 return 0;
110}
111
112void nvgpu_clk_arb_cleanup_arbiter(struct gk20a *g)
113{
114 kfree(g->clk_arb);
115}
116
117
118int nvgpu_clk_arb_install_session_fd(struct gk20a *g,
119 struct nvgpu_clk_session *session)
120{
121 struct file *file;
122 char *name;
123 int fd;
124 int err;
125
126 gk20a_dbg_fn("");
127
128 if (session->fd >= 0)
129 goto done;
130
131 fd = get_unused_fd_flags(O_RDWR);
132 if (fd < 0)
133 return fd;
134
135 name = kasprintf(GFP_KERNEL, "%s-clk-fd%d", dev_name(g->dev), fd);
136 file = anon_inode_getfile(name, &clk_dev_ops, session, O_RDWR);
137 kfree(name);
138 if (IS_ERR(file)) {
139 err = PTR_ERR(file);
140 goto clean_up_fd;
141 }
142
143 BUG_ON(file->private_data != session);
144
145 fd_install(fd, file);
146 kref_get(&session->refcount);
147
148 session->fd = fd;
149done:
150 return session->fd;
151
152clean_up_fd:
153 put_unused_fd(fd);
154
155 return err;
156}
157
158int nvgpu_clk_arb_init_session(struct gk20a *g,
159 struct nvgpu_clk_session **_session)
160{
161 struct nvgpu_clk_arb *arb = g->clk_arb;
162 struct nvgpu_clk_session *session = *(_session);
163
164 gk20a_dbg_fn("");
165
166 *_session = NULL;
167
168 if (!g->ops.clk_arb.get_arbiter_clk_domains)
169 return 0;
170
171 session = kzalloc(sizeof(struct nvgpu_clk_session), GFP_KERNEL);
172 if (!session)
173 return -ENOMEM;
174 session->g = g;
175 session->fd = -1;
176
177 kref_init(&session->refcount);
178 init_waitqueue_head(&session->readout_wq);
179
180 atomic_set(&session->poll_mask, 0);
181 atomic_set(&session->req_nr, 0);
182
183 mutex_lock(&arb->users_lock);
184 list_add_tail(&session->user, &arb->users);
185 mutex_unlock(&arb->users_lock);
186 atomic_inc(&arb->usercount);
187
188 mutex_lock(&arb->wlock);
189 session->mclk_target_mhz = arb->mclk_default_mhz;
190 session->gpc2clk_target_mhz = arb->gpc2clk_default_mhz;
191 mutex_unlock(&arb->wlock);
192
193 *_session = session;
194
195 return 0;
196}
197
198void nvgpu_clk_arb_free_session(struct kref *refcount)
199{
200 struct nvgpu_clk_session *session = container_of(refcount,
201 struct nvgpu_clk_session, refcount);
202 struct gk20a *g = session->g;
203 struct nvgpu_clk_arb *arb = g->clk_arb;
204
205 mutex_lock(&arb->users_lock);
206 list_del_init(&session->user);
207 mutex_unlock(&arb->users_lock);
208
209 if (atomic_dec_and_test(&arb->usercount))
210 nvgpu_clk_arb_apply_session_constraints(g, NULL);
211
212 kfree(session);
213}
214
215void nvgpu_clk_arb_cleanup_session(struct gk20a *g,
216 struct nvgpu_clk_session *session)
217{
218 kref_put(&session->refcount, nvgpu_clk_arb_free_session);
219}
220
221static void nvgpu_clk_arb_run_arbiter_cb(struct work_struct *work)
222{
223 struct nvgpu_clk_arb *arb =
224 container_of(work, struct nvgpu_clk_arb, update_fn_work);
225 struct nvgpu_clk_session *session;
226
227 mutex_lock(&arb->wlock);
228
229 /* TODO: loop up higher or equal VF points */
230
231 arb->mclk_current_mhz = arb->mclk_target_mhz;
232 arb->gpc2clk_current_mhz = arb->gpc2clk_target_mhz;
233
234 /* TODO: actually program the clocks */
235
236 atomic_set(&arb->last_req_nr, atomic_read(&arb->req_nr));
237 mutex_unlock(&arb->wlock);
238
239 mutex_lock(&arb->users_lock);
240 list_for_each_entry(session, &arb->users, user) {
241 atomic_set(&session->poll_mask, POLLIN | POLLRDNORM);
242 wake_up_interruptible(&session->readout_wq);
243 }
244 mutex_unlock(&arb->users_lock);
245
246}
247
248void nvgpu_clk_arb_apply_session_constraints(struct gk20a *g,
249 struct nvgpu_clk_session *session)
250{
251 struct nvgpu_clk_arb *arb = g->clk_arb;
252
253 mutex_lock(&arb->wlock);
254 atomic_inc(&arb->req_nr);
255
256 /* TODO: arbitration between users.
257 For now, last session to run arbiter wins.
258 */
259
260 if (session) {
261 arb->mclk_target_mhz = session->mclk_target_mhz;
262 arb->gpc2clk_target_mhz = session->gpc2clk_target_mhz;
263
264 atomic_set(&session->req_nr, atomic_read(&arb->req_nr));
265 } else {
266 arb->mclk_target_mhz = arb->mclk_default_mhz;
267 arb->gpc2clk_target_mhz = arb->gpc2clk_default_mhz;
268 }
269 mutex_unlock(&arb->wlock);
270
271 schedule_work(&arb->update_fn_work);
272}
273
274static unsigned int nvgpu_clk_arb_poll_session_dev(struct file *filp, poll_table *wait)
275{
276 struct nvgpu_clk_session *session = filp->private_data;
277
278 gk20a_dbg_fn("");
279
280 poll_wait(filp, &session->readout_wq, wait);
281 return atomic_xchg(&session->poll_mask, 0);
282}
283
284static int nvgpu_clk_arb_release_session_dev(struct inode *inode, struct file *filp)
285{
286 struct nvgpu_clk_session *session = filp->private_data;
287 struct gk20a *g = session->g;
288
289 session->fd = -1;
290 nvgpu_clk_arb_cleanup_session(g, session);
291
292 return 0;
293}
294
295int nvgpu_clk_arb_set_session_target_mhz(struct nvgpu_clk_session *session,
296 u32 api_domain, u16 target_mhz)
297{
298
299 gk20a_dbg_fn("domain=0x%08x target_mhz=%u", api_domain, target_mhz);
300
301 switch (api_domain) {
302 case NVGPU_GPU_CLK_DOMAIN_MCLK:
303 session->mclk_target_mhz = target_mhz;
304 return 0;
305
306 case NVGPU_GPU_CLK_DOMAIN_GPC2CLK:
307 session->gpc2clk_target_mhz = target_mhz;
308 return 0;
309
310 default:
311 return -EINVAL;
312 }
313}
314
315int nvgpu_clk_arb_get_session_target_mhz(struct nvgpu_clk_session *session,
316 u32 api_domain, u16 *target_mhz)
317{
318 switch (api_domain) {
319 case NVGPU_GPU_CLK_DOMAIN_MCLK:
320 *target_mhz = session->mclk_target_mhz;
321 return 0;
322
323 case NVGPU_GPU_CLK_DOMAIN_GPC2CLK:
324 *target_mhz = session->gpc2clk_target_mhz;
325 return 0;
326
327 default:
328 *target_mhz = 0;
329 return -EINVAL;
330 }
331}
332
333int nvgpu_clk_arb_get_arbiter_actual_mhz(struct gk20a *g,
334 u32 api_domain, u16 *actual_mhz)
335{
336 struct nvgpu_clk_arb *arb = g->clk_arb;
337 int err = 0;
338
339 mutex_lock(&arb->wlock);
340 switch (api_domain) {
341 case NVGPU_GPU_CLK_DOMAIN_MCLK:
342 *actual_mhz = arb->mclk_current_mhz;
343 break;
344
345 case NVGPU_GPU_CLK_DOMAIN_GPC2CLK:
346 *actual_mhz = arb->gpc2clk_current_mhz;
347 break;
348
349 default:
350 *actual_mhz = 0;
351 err = -EINVAL;
352 }
353 mutex_unlock(&arb->wlock);
354
355 return err;
356}
357
358u32 nvgpu_clk_arb_get_arbiter_req_nr(struct gk20a *g)
359{
360 struct nvgpu_clk_arb *arb = g->clk_arb;
361
362 return atomic_read(&arb->last_req_nr);
363}
364
365int nvgpu_clk_arb_get_arbiter_clk_range(struct gk20a *g, u32 api_domain,
366 u16 *min_mhz, u16 *max_mhz)
367{
368 return g->ops.clk_arb.get_arbiter_clk_range(g, api_domain,
369 min_mhz, max_mhz);
370}
371
372u32 nvgpu_clk_arb_get_arbiter_clk_domains(struct gk20a *g)
373{
374 return g->ops.clk_arb.get_arbiter_clk_domains(g);
375}
376
377u32 nvgpu_clk_arb_get_session_req_nr(struct gk20a *g,
378 struct nvgpu_clk_session *session)
379{
380 return atomic_read(&session->req_nr);
381}
382
383int nvgpu_clk_arb_get_arbiter_clk_f_points(struct gk20a *g,
384 u32 api_domain, u32 *max_points, u16 *fpoints)
385{
386 return (int)clk_domain_get_f_points(g, api_domain, max_points, fpoints);
387}