video: tegra: host: fix Coverity issues
[linux-3.10.git] / drivers / video / tegra / host / nvhost_acm.c
1 /*
2  * drivers/video/tegra/host/nvhost_acm.c
3  *
4  * Tegra Graphics Host Automatic Clock Management
5  *
6  * Copyright (c) 2010-2013, NVIDIA Corporation. All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <linux/slab.h>
22 #include <linux/stat.h>
23 #include <linux/string.h>
24 #include <linux/sched.h>
25 #include <linux/err.h>
26 #include <linux/export.h>
27 #include <linux/device.h>
28 #include <linux/delay.h>
29 #include <linux/platform_device.h>
30 #include <linux/pm.h>
31 #include <linux/pm_runtime.h>
32 #include <trace/events/nvhost.h>
33
34 #include <mach/powergate.h>
35 #include <mach/clk.h>
36 #include <mach/hardware.h>
37 #include <mach/mc.h>
38 #include <mach/pm_domains.h>
39
40 #include "nvhost_acm.h"
41 #include "dev.h"
42
43 #define ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT       (2 * HZ)
44 #define POWERGATE_DELAY                         10
45 #define MAX_DEVID_LENGTH                        16
46
47 DEFINE_MUTEX(client_list_lock);
48
49 struct nvhost_module_client {
50         struct list_head node;
51         unsigned long rate[NVHOST_MODULE_MAX_CLOCKS];
52         void *priv;
53 };
54
55 static void do_powergate_locked(int id)
56 {
57         nvhost_dbg_fn("%d", id);
58         if (id != -1 && tegra_powergate_is_powered(id))
59                 tegra_powergate_partition(id);
60 }
61
62 static void do_unpowergate_locked(int id)
63 {
64         int ret = 0;
65         if (id != -1) {
66                 ret = tegra_unpowergate_partition(id);
67                 if (ret)
68                         pr_err("%s: unpowergate failed: id = %d\n",
69                                         __func__, id);
70         }
71 }
72
73 static void do_module_reset_locked(struct platform_device *dev)
74 {
75         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
76
77         /* assert module and mc client reset */
78         if (pdata->powergate_ids[0] != -1)
79                 tegra_powergate_mc_flush(pdata->powergate_ids[0]);
80         if (pdata->powergate_ids[0] != -1)
81                 tegra_powergate_mc_disable(pdata->powergate_ids[0]);
82         if (pdata->clocks[0].reset)
83                 tegra_periph_reset_assert(pdata->clk[0]);
84
85         if (pdata->powergate_ids[1] != -1)
86                 tegra_powergate_mc_flush(pdata->powergate_ids[1]);
87         if (pdata->powergate_ids[1] != -1)
88                 tegra_powergate_mc_disable(pdata->powergate_ids[1]);
89         if (pdata->clocks[1].reset)
90                 tegra_periph_reset_assert(pdata->clk[1]);
91
92         udelay(POWERGATE_DELAY);
93
94         /* deassert reset */
95         if (pdata->clocks[0].reset)
96                 tegra_periph_reset_deassert(pdata->clk[0]);
97         if (pdata->powergate_ids[0] != -1)
98                 tegra_powergate_mc_enable(pdata->powergate_ids[0]);
99         if (pdata->powergate_ids[0] != -1)
100                 tegra_powergate_mc_flush_done(pdata->powergate_ids[0]);
101
102         if (pdata->clocks[1].reset)
103                 tegra_periph_reset_deassert(pdata->clk[1]);
104         if (pdata->powergate_ids[1] != -1)
105                 tegra_powergate_mc_enable(pdata->powergate_ids[1]);
106         if (pdata->powergate_ids[1] != -1)
107                 tegra_powergate_mc_flush_done(pdata->powergate_ids[1]);
108 }
109
110 void nvhost_module_reset(struct platform_device *dev)
111 {
112         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
113
114         dev_dbg(&dev->dev,
115                 "%s: asserting %s module reset (id %d, id2 %d)\n",
116                 __func__, dev_name(&dev->dev),
117                 pdata->powergate_ids[0], pdata->powergate_ids[1]);
118
119         mutex_lock(&pdata->lock);
120         do_module_reset_locked(dev);
121         mutex_unlock(&pdata->lock);
122
123         if (pdata->finalize_poweron)
124                 pdata->finalize_poweron(dev);
125
126         dev_dbg(&dev->dev, "%s: module %s out of reset\n",
127                 __func__, dev_name(&dev->dev));
128 }
129
130 void nvhost_module_busy(struct platform_device *dev)
131 {
132         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
133
134         /* Explicitly turn on the host1x clocks
135          * - This is needed as host1x driver sets ignore_children = true
136          * to cater the use case of display clock ON but host1x clock OFF
137          * in OS-Idle-Display-ON case
138          * - This was easily done in ACM as it only checked the ref count
139          * of host1x (or any device for that matter) to be zero before
140          * turning off its clock
141          * - However, runtime PM checks to see if *ANY* child of device is
142          * in ACTIVE state and if yes, it doesn't suspend the parent. As a
143          * result of this, display && host1x clocks remains ON during
144          * OS-Idle-Display-ON case
145          * - The code below fixes this use-case
146          */
147         if (dev->dev.parent && (dev->dev.parent != &platform_bus))
148                 nvhost_module_busy(nvhost_get_parent(dev));
149
150 #ifdef CONFIG_PM_RUNTIME
151         pm_runtime_get_sync(&dev->dev);
152 #endif
153
154         if (pdata->busy)
155                 pdata->busy(dev);
156 }
157
158 void nvhost_module_idle_mult(struct platform_device *dev, int refs)
159 {
160         int original_refs = refs;
161         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
162
163 #ifdef CONFIG_PM_RUNTIME
164         if (atomic_read(&dev->dev.power.usage_count) == refs) {
165                 if (pdata->idle)
166                         pdata->idle(dev);
167         }
168
169         while (refs--) {
170                 pm_runtime_mark_last_busy(&dev->dev);
171                 if (pdata->clockgate_delay)
172                         pm_runtime_put_sync_autosuspend(&dev->dev);
173                 else
174                         pm_runtime_put(&dev->dev);
175         }
176 #else
177         if (pdata->idle)
178                 pdata->idle(dev);
179 #endif
180
181         /* Explicitly turn off the host1x clocks */
182         if (dev->dev.parent && (dev->dev.parent != &platform_bus))
183                 nvhost_module_idle_mult(nvhost_get_parent(dev), original_refs);
184 }
185
186 int nvhost_module_get_rate(struct platform_device *dev, unsigned long *rate,
187                 int index)
188 {
189         struct clk *c;
190         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
191
192         c = pdata->clk[index];
193         if (!c)
194                 return -EINVAL;
195
196         /* Need to enable client to get correct rate */
197         nvhost_module_busy(dev);
198         *rate = clk_get_rate(c);
199         nvhost_module_idle(dev);
200         return 0;
201 }
202
203 static int nvhost_module_update_rate(struct platform_device *dev, int index)
204 {
205         unsigned long rate = 0;
206         struct nvhost_module_client *m;
207         unsigned long devfreq_rate, default_rate;
208         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
209         int ret;
210
211         if (!pdata->clk[index])
212                 return -EINVAL;
213
214         /* If devfreq is on, use that clock rate, otherwise default */
215         devfreq_rate = pdata->clocks[index].devfreq_rate;
216         default_rate = devfreq_rate ?
217                 devfreq_rate : pdata->clocks[index].default_rate;
218         default_rate = clk_round_rate(pdata->clk[index], default_rate);
219
220         list_for_each_entry(m, &pdata->client_list, node) {
221                 unsigned long r = m->rate[index];
222                 if (!r)
223                         r = default_rate;
224                 rate = max(r, rate);
225         }
226         if (!rate)
227                 rate = default_rate;
228
229         trace_nvhost_module_update_rate(dev->name,
230                         pdata->clocks[index].name, rate);
231
232         ret = clk_set_rate(pdata->clk[index], rate);
233
234         if (pdata->update_clk)
235                 pdata->update_clk(dev);
236
237         return ret;
238
239 }
240
241 int nvhost_module_set_rate(struct platform_device *dev, void *priv,
242                 unsigned long rate, int index, int bBW)
243 {
244         struct nvhost_module_client *m;
245         int ret = 0;
246         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
247
248         nvhost_dbg_fn("%s", dev->name);
249
250         mutex_lock(&client_list_lock);
251         list_for_each_entry(m, &pdata->client_list, node) {
252                 if (m->priv == priv) {
253                         if (bBW) {
254                                 /*
255                                  * If client sets BW, then we need to
256                                  * convert it to freq.
257                                  * rate is Bps and input param of
258                                  * tegra_emc_bw_to_freq_req is KBps.
259                                  */
260                                 unsigned int freq_khz =
261                                 tegra_emc_bw_to_freq_req
262                                         ((unsigned long)(rate >> 10));
263
264                                 m->rate[index] =
265                                         clk_round_rate(pdata->clk[index],
266                                         (unsigned long)(freq_khz << 10));
267                         } else
268                                 m->rate[index] =
269                                         clk_round_rate(pdata->clk[index], rate);
270                 }
271         }
272
273         ret = nvhost_module_update_rate(dev, index);
274         mutex_unlock(&client_list_lock);
275         return ret;
276 }
277
278 int nvhost_module_add_client(struct platform_device *dev, void *priv)
279 {
280         struct nvhost_module_client *client;
281         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
282
283         nvhost_dbg_fn("%s num_clks=%d priv=%p", dev->name,
284                       pdata->num_clks, priv);
285
286         client = kzalloc(sizeof(*client), GFP_KERNEL);
287         if (!client)
288                 return -ENOMEM;
289
290         INIT_LIST_HEAD(&client->node);
291         client->priv = priv;
292
293         mutex_lock(&client_list_lock);
294         list_add_tail(&client->node, &pdata->client_list);
295         mutex_unlock(&client_list_lock);
296
297         return 0;
298 }
299
300 void nvhost_module_remove_client(struct platform_device *dev, void *priv)
301 {
302         int i;
303         struct nvhost_module_client *m;
304         int found = 0;
305         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
306
307         nvhost_dbg_fn("%s priv=%p", dev->name, priv);
308
309         mutex_lock(&client_list_lock);
310         list_for_each_entry(m, &pdata->client_list, node) {
311                 if (priv == m->priv) {
312                         list_del(&m->node);
313                         found = 1;
314                         break;
315                 }
316         }
317         if (found) {
318                 kfree(m);
319                 for (i = 0; i < pdata->num_clks; i++)
320                         nvhost_module_update_rate(dev, i);
321         }
322         mutex_unlock(&client_list_lock);
323 }
324
325 static ssize_t powergate_delay_store(struct kobject *kobj,
326         struct kobj_attribute *attr, const char *buf, size_t count)
327 {
328         int powergate_delay = 0, ret = 0;
329         struct nvhost_device_power_attr *power_attribute =
330                 container_of(attr, struct nvhost_device_power_attr, \
331                         power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]);
332         struct platform_device *dev = power_attribute->ndev;
333         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
334
335         if (!pdata->can_powergate) {
336                 dev_info(&dev->dev, "does not support power-gating\n");
337                 return count;
338         }
339
340         mutex_lock(&pdata->lock);
341         ret = sscanf(buf, "%d", &powergate_delay);
342         if (ret == 1 && powergate_delay >= 0) {
343                 struct generic_pm_domain *genpd =
344                         pd_to_genpd(dev->dev.pm_domain);
345                 pdata->powergate_delay = powergate_delay;
346                 pm_genpd_set_poweroff_delay(genpd, pdata->powergate_delay);
347         }
348         else
349                 dev_err(&dev->dev, "Invalid powergate delay\n");
350         mutex_unlock(&pdata->lock);
351
352         return count;
353 }
354
355 static ssize_t powergate_delay_show(struct kobject *kobj,
356         struct kobj_attribute *attr, char *buf)
357 {
358         int ret;
359         struct nvhost_device_power_attr *power_attribute =
360                 container_of(attr, struct nvhost_device_power_attr, \
361                         power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]);
362         struct platform_device *dev = power_attribute->ndev;
363         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
364
365         mutex_lock(&pdata->lock);
366         ret = sprintf(buf, "%d\n", pdata->powergate_delay);
367         mutex_unlock(&pdata->lock);
368
369         return ret;
370 }
371
372 static ssize_t clockgate_delay_store(struct kobject *kobj,
373         struct kobj_attribute *attr, const char *buf, size_t count)
374 {
375         int clockgate_delay = 0, ret = 0;
376         struct nvhost_device_power_attr *power_attribute =
377                 container_of(attr, struct nvhost_device_power_attr, \
378                         power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]);
379         struct platform_device *dev = power_attribute->ndev;
380         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
381
382         mutex_lock(&pdata->lock);
383         ret = sscanf(buf, "%d", &clockgate_delay);
384         if (ret == 1 && clockgate_delay >= 0) {
385                 pdata->clockgate_delay = clockgate_delay;
386                 pm_runtime_set_autosuspend_delay(&dev->dev,
387                         pdata->clockgate_delay);
388         }
389         else
390                 dev_err(&dev->dev, "Invalid clockgate delay\n");
391         mutex_unlock(&pdata->lock);
392
393         return count;
394 }
395
396 static ssize_t clockgate_delay_show(struct kobject *kobj,
397         struct kobj_attribute *attr, char *buf)
398 {
399         int ret;
400         struct nvhost_device_power_attr *power_attribute =
401                 container_of(attr, struct nvhost_device_power_attr, \
402                         power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]);
403         struct platform_device *dev = power_attribute->ndev;
404         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
405
406         mutex_lock(&pdata->lock);
407         ret = sprintf(buf, "%d\n", pdata->clockgate_delay);
408         mutex_unlock(&pdata->lock);
409
410         return ret;
411 }
412
413 int nvhost_module_set_devfreq_rate(struct platform_device *dev, int index,
414                 unsigned long rate)
415 {
416         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
417         int ret;
418
419         rate = clk_round_rate(pdata->clk[index], rate);
420         pdata->clocks[index].devfreq_rate = rate;
421
422         trace_nvhost_module_set_devfreq_rate(dev->name,
423                         pdata->clocks[index].name, rate);
424
425         mutex_lock(&client_list_lock);
426         ret = nvhost_module_update_rate(dev, index);
427         mutex_unlock(&client_list_lock);
428
429         return ret;
430 }
431
432 int nvhost_module_init(struct platform_device *dev)
433 {
434         int i = 0, err = 0;
435         struct kobj_attribute *attr = NULL;
436         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
437
438         /* initialize clocks to known state (=enabled) */
439         pdata->num_clks = 0;
440         INIT_LIST_HEAD(&pdata->client_list);
441         while (i < NVHOST_MODULE_MAX_CLOCKS && pdata->clocks[i].name) {
442                 char devname[MAX_DEVID_LENGTH];
443                 long rate = pdata->clocks[i].default_rate;
444                 struct clk *c;
445
446                 snprintf(devname, MAX_DEVID_LENGTH,
447                          (dev->id <= 0) ? "tegra_%s" : "tegra_%s.%d",
448                          dev->name, dev->id);
449                 c = clk_get_sys(devname, pdata->clocks[i].name);
450                 if (IS_ERR(c)) {
451                         dev_err(&dev->dev, "clk_get_sys failed for i=%d %s:%s",
452                                 i, devname, pdata->clocks[i].name);
453                         /* arguably we should fail init here instead... */
454                         i++;
455                         continue;
456                 }
457                 nvhost_dbg_fn("%s->clk[%d] -> %s:%s:%p",
458                               dev->name, pdata->num_clks,
459                               devname, pdata->clocks[i].name,
460                               c);
461                 rate = clk_round_rate(c, rate);
462                 clk_prepare_enable(c);
463                 clk_set_rate(c, rate);
464                 pdata->clk[pdata->num_clks++] = c;
465                 i++;
466         }
467         pdata->num_clks = i;
468
469         /* reset the module */
470         mutex_lock(&pdata->lock);
471         do_module_reset_locked(dev);
472         mutex_unlock(&pdata->lock);
473
474         /* disable the clocks */
475         for (i = 0; i < pdata->num_clks; ++i)
476                 clk_disable_unprepare(pdata->clk[i]);
477
478         /* power gate units that we can power gate */
479         if (pdata->can_powergate) {
480                 do_powergate_locked(pdata->powergate_ids[0]);
481                 do_powergate_locked(pdata->powergate_ids[1]);
482         } else {
483                 do_unpowergate_locked(pdata->powergate_ids[0]);
484                 do_unpowergate_locked(pdata->powergate_ids[1]);
485         }
486
487         /* Init the power sysfs attributes for this device */
488         pdata->power_attrib = devm_kzalloc(&dev->dev,
489                 sizeof(struct nvhost_device_power_attr), GFP_KERNEL);
490         if (!pdata->power_attrib) {
491                 dev_err(&dev->dev, "Unable to allocate sysfs attributes\n");
492                 return -ENOMEM;
493         }
494         pdata->power_attrib->ndev = dev;
495
496         pdata->power_kobj = kobject_create_and_add("acm", &dev->dev.kobj);
497         if (!pdata->power_kobj) {
498                 dev_err(&dev->dev, "Could not add dir 'power'\n");
499                 err = -EIO;
500                 goto fail_attrib_alloc;
501         }
502
503         attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY];
504         attr->attr.name = "clockgate_delay";
505         attr->attr.mode = S_IWUSR | S_IRUGO;
506         attr->show = clockgate_delay_show;
507         attr->store = clockgate_delay_store;
508         sysfs_attr_init(&attr->attr);
509         if (sysfs_create_file(pdata->power_kobj, &attr->attr)) {
510                 dev_err(&dev->dev, "Could not create sysfs attribute clockgate_delay\n");
511                 err = -EIO;
512                 goto fail_clockdelay;
513         }
514
515         attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY];
516         attr->attr.name = "powergate_delay";
517         attr->attr.mode = S_IWUSR | S_IRUGO;
518         attr->show = powergate_delay_show;
519         attr->store = powergate_delay_store;
520         sysfs_attr_init(&attr->attr);
521         if (sysfs_create_file(pdata->power_kobj, &attr->attr)) {
522                 dev_err(&dev->dev, "Could not create sysfs attribute powergate_delay\n");
523                 err = -EIO;
524                 goto fail_powergatedelay;
525         }
526
527         return 0;
528
529 fail_powergatedelay:
530         attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY];
531         sysfs_remove_file(pdata->power_kobj, &attr->attr);
532
533 fail_clockdelay:
534         kobject_put(pdata->power_kobj);
535
536 fail_attrib_alloc:
537         kfree(pdata->power_attrib);
538
539         return err;
540 }
541 EXPORT_SYMBOL(nvhost_module_init);
542
543 int nvhost_module_suspend(struct platform_device *dev)
544 {
545         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
546
547         if (pm_runtime_barrier(&dev->dev))
548                 return -EBUSY;
549
550         if (pdata->suspend_ndev)
551                 pdata->suspend_ndev(dev);
552
553         return 0;
554 }
555
556 void nvhost_module_deinit(struct platform_device *dev)
557 {
558         int i;
559         struct kobj_attribute *attr = NULL;
560         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
561
562         nvhost_module_suspend(dev);
563         for (i = 0; i < pdata->num_clks; i++)
564                 clk_put(pdata->clk[i]);
565
566         if (pdata->power_kobj) {
567                 for (i = 0; i < NVHOST_POWER_SYSFS_ATTRIB_MAX; i++) {
568                         attr = &pdata->power_attrib->power_attr[i];
569                         sysfs_remove_file(pdata->power_kobj, &attr->attr);
570                 }
571
572                 kobject_put(pdata->power_kobj);
573         }
574
575 }
576
577 #ifdef CONFIG_PM
578 const struct dev_pm_ops nvhost_module_pm_ops = {
579 #if defined(CONFIG_PM_RUNTIME) && !defined(CONFIG_PM_GENERIC_DOMAINS)
580         .runtime_suspend = nvhost_module_disable_clk,
581         .runtime_resume = nvhost_module_enable_clk,
582 #endif
583 };
584 #endif
585
586 /* common runtime pm and power domain APIs */
587 int nvhost_module_add_domain(struct generic_pm_domain *domain,
588         struct platform_device *pdev)
589 {
590         int ret = 0;
591         struct nvhost_device_data *pdata;
592         struct dev_power_governor *pm_domain_gov = NULL;
593
594         pdata = platform_get_drvdata(pdev);
595         if (!pdata)
596                 return -EINVAL;
597
598         if (!pdata->can_powergate)
599 #ifdef CONFIG_PM_GENERIC_DOMAINS
600                 pm_domain_gov = &pm_domain_always_on_gov;
601
602         if (__pm_genpd_name_add_device(domain->name, &pdev->dev, NULL)) {
603                 pm_genpd_init(domain, pm_domain_gov, true);
604                 ret = pm_genpd_add_device(domain, &pdev->dev);
605                 if (pdata->powergate_delay)
606                         pm_genpd_set_poweroff_delay(domain,
607                                         pdata->powergate_delay);
608                 tegra_pd_add_sd(domain);
609         }
610 #endif
611
612         return ret;
613 }
614
615 int nvhost_module_enable_clk(struct device *dev)
616 {
617         int index = 0;
618         struct nvhost_device_data *pdata;
619
620         /* enable parent's clock if required */
621         if (dev->parent && dev->parent != &platform_bus)
622                 nvhost_module_enable_clk(dev->parent);
623
624         pdata = dev_get_drvdata(dev);
625         if (!pdata)
626                 return -EINVAL;
627
628         for (index = 0; index < pdata->num_clks; index++) {
629                 int err = clk_prepare_enable(pdata->clk[index]);
630                 if (err) {
631                         dev_err(dev, "Cannot turn on clock %s",
632                                 pdata->clocks[index].name);
633                         return -EINVAL;
634                 }
635         }
636
637         return 0;
638 }
639 EXPORT_SYMBOL(nvhost_module_enable_clk);
640
641 int nvhost_module_disable_clk(struct device *dev)
642 {
643         int index = 0;
644         struct nvhost_device_data *pdata;
645
646         pdata = dev_get_drvdata(dev);
647         if (!pdata)
648                 return -EINVAL;
649
650         for (index = 0; index < pdata->num_clks; index++)
651                 clk_disable_unprepare(pdata->clk[index]);
652
653         /* disable parent's clock if required */
654         if (dev->parent && dev->parent != &platform_bus)
655                 nvhost_module_disable_clk(dev->parent);
656
657         return 0;
658 }
659 EXPORT_SYMBOL(nvhost_module_disable_clk);
660
661 int nvhost_module_power_on(struct platform_device *pdev)
662 {
663         struct nvhost_device_data *pdata;
664
665         pdata = platform_get_drvdata(pdev);
666         if (!pdata)
667                 return -EINVAL;
668
669         mutex_lock(&pdata->lock);
670         if (pdata->can_powergate) {
671                 do_unpowergate_locked(pdata->powergate_ids[0]);
672                 do_unpowergate_locked(pdata->powergate_ids[1]);
673         }
674
675         if (pdata->powerup_reset)
676                 do_module_reset_locked(pdev);
677         mutex_unlock(&pdata->lock);
678
679         return 0;
680 }
681
682 int nvhost_module_power_off(struct platform_device *pdev)
683 {
684         struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
685         if (!pdata)
686                 return -EINVAL;
687
688         mutex_lock(&pdata->lock);
689         if (pdata->can_powergate) {
690                 do_powergate_locked(pdata->powergate_ids[0]);
691                 do_powergate_locked(pdata->powergate_ids[1]);
692         }
693         mutex_unlock(&pdata->lock);
694
695         return 0;
696 }
697
698 int nvhost_module_prepare_poweroff(struct device *dev)
699 {
700         struct nvhost_device_data *pdata;
701
702         pdata = dev_get_drvdata(dev);
703         if (!pdata)
704                 return -EINVAL;
705
706         if (pdata->prepare_poweroff)
707                 pdata->prepare_poweroff(to_platform_device(dev));
708
709         return 0;
710 }
711
712 int nvhost_module_finalize_poweron(struct device *dev)
713 {
714         struct nvhost_device_data *pdata;
715
716         pdata = dev_get_drvdata(dev);
717         if (!pdata)
718                 return -EINVAL;
719
720         if (pdata->finalize_poweron)
721                 pdata->finalize_poweron(to_platform_device(dev));
722
723         return 0;
724 }
725
726 /* public host1x power management APIs */
727 bool nvhost_module_powered_ext(struct platform_device *dev)
728 {
729         struct platform_device *pdev;
730
731         if (!nvhost_get_parent(dev)) {
732                 dev_err(&dev->dev, "Module powered called with wrong dev\n");
733                 return 0;
734         }
735
736         /* get the parent */
737         pdev = to_platform_device(dev->dev.parent);
738
739         return nvhost_module_powered(pdev);
740 }
741
742 void nvhost_module_busy_ext(struct platform_device *dev)
743 {
744         struct platform_device *pdev;
745
746         if (!nvhost_get_parent(dev)) {
747                 dev_err(&dev->dev, "Module busy called with wrong dev\n");
748                 return;
749         }
750
751         /* get the parent */
752         pdev = to_platform_device(dev->dev.parent);
753
754         nvhost_module_busy(pdev);
755 }
756 EXPORT_SYMBOL(nvhost_module_busy_ext);
757
758 void nvhost_module_idle_ext(struct platform_device *dev)
759 {
760         struct platform_device *pdev;
761
762         if (!nvhost_get_parent(dev)) {
763                 dev_err(&dev->dev, "Module idle called with wrong dev\n");
764                 return;
765         }
766
767         /* get the parent */
768         pdev = to_platform_device(dev->dev.parent);
769
770         nvhost_module_idle(pdev);
771 }
772 EXPORT_SYMBOL(nvhost_module_idle_ext);