1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2016-2018, 2020-2021 The Linux Foundation. All rights reserved. |
4 | * Copyright (C) 2013 Red Hat |
5 | * Author: Rob Clark <robdclark@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/interconnect.h> |
9 | #include <linux/io.h> |
10 | |
11 | #include "msm_drv.h" |
12 | |
13 | /* |
14 | * Util/helpers: |
15 | */ |
16 | |
17 | struct clk *msm_clk_bulk_get_clock(struct clk_bulk_data *bulk, int count, |
18 | const char *name) |
19 | { |
20 | int i; |
21 | char n[32]; |
22 | |
23 | snprintf(buf: n, size: sizeof(n), fmt: "%s_clk" , name); |
24 | |
25 | for (i = 0; bulk && i < count; i++) { |
26 | if (!strcmp(bulk[i].id, name) || !strcmp(bulk[i].id, n)) |
27 | return bulk[i].clk; |
28 | } |
29 | |
30 | |
31 | return NULL; |
32 | } |
33 | |
34 | struct clk *msm_clk_get(struct platform_device *pdev, const char *name) |
35 | { |
36 | struct clk *clk; |
37 | char name2[32]; |
38 | |
39 | clk = devm_clk_get(dev: &pdev->dev, id: name); |
40 | if (!IS_ERR(ptr: clk) || PTR_ERR(ptr: clk) == -EPROBE_DEFER) |
41 | return clk; |
42 | |
43 | snprintf(buf: name2, size: sizeof(name2), fmt: "%s_clk" , name); |
44 | |
45 | clk = devm_clk_get(dev: &pdev->dev, id: name2); |
46 | if (!IS_ERR(ptr: clk)) |
47 | dev_warn(&pdev->dev, "Using legacy clk name binding. Use " |
48 | "\"%s\" instead of \"%s\"\n" , name, name2); |
49 | |
50 | return clk; |
51 | } |
52 | |
53 | void __iomem *msm_ioremap_mdss(struct platform_device *mdss_pdev, |
54 | struct platform_device *pdev, |
55 | const char *name) |
56 | { |
57 | struct resource *res; |
58 | |
59 | res = platform_get_resource_byname(mdss_pdev, IORESOURCE_MEM, name); |
60 | if (!res) |
61 | return ERR_PTR(error: -EINVAL); |
62 | |
63 | return devm_ioremap_resource(dev: &pdev->dev, res); |
64 | } |
65 | |
66 | static void __iomem *_msm_ioremap(struct platform_device *pdev, const char *name, |
67 | bool quiet, phys_addr_t *psize) |
68 | { |
69 | struct resource *res; |
70 | unsigned long size; |
71 | void __iomem *ptr; |
72 | |
73 | if (name) |
74 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); |
75 | else |
76 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
77 | |
78 | if (!res) { |
79 | if (!quiet) |
80 | DRM_DEV_ERROR(&pdev->dev, "failed to get memory resource: %s\n" , name); |
81 | return ERR_PTR(error: -EINVAL); |
82 | } |
83 | |
84 | size = resource_size(res); |
85 | |
86 | ptr = devm_ioremap(dev: &pdev->dev, offset: res->start, size); |
87 | if (!ptr) { |
88 | if (!quiet) |
89 | DRM_DEV_ERROR(&pdev->dev, "failed to ioremap: %s\n" , name); |
90 | return ERR_PTR(error: -ENOMEM); |
91 | } |
92 | |
93 | if (psize) |
94 | *psize = size; |
95 | |
96 | return ptr; |
97 | } |
98 | |
99 | void __iomem *msm_ioremap(struct platform_device *pdev, const char *name) |
100 | { |
101 | return _msm_ioremap(pdev, name, quiet: false, NULL); |
102 | } |
103 | |
104 | void __iomem *msm_ioremap_quiet(struct platform_device *pdev, const char *name) |
105 | { |
106 | return _msm_ioremap(pdev, name, quiet: true, NULL); |
107 | } |
108 | |
109 | void __iomem *msm_ioremap_size(struct platform_device *pdev, const char *name, |
110 | phys_addr_t *psize) |
111 | { |
112 | return _msm_ioremap(pdev, name, quiet: false, psize); |
113 | } |
114 | |
115 | static enum hrtimer_restart msm_hrtimer_worktimer(struct hrtimer *t) |
116 | { |
117 | struct msm_hrtimer_work *work = container_of(t, |
118 | struct msm_hrtimer_work, timer); |
119 | |
120 | kthread_queue_work(worker: work->worker, work: &work->work); |
121 | |
122 | return HRTIMER_NORESTART; |
123 | } |
124 | |
125 | void msm_hrtimer_queue_work(struct msm_hrtimer_work *work, |
126 | ktime_t wakeup_time, |
127 | enum hrtimer_mode mode) |
128 | { |
129 | hrtimer_start(timer: &work->timer, tim: wakeup_time, mode); |
130 | } |
131 | |
132 | void msm_hrtimer_work_init(struct msm_hrtimer_work *work, |
133 | struct kthread_worker *worker, |
134 | kthread_work_func_t fn, |
135 | clockid_t clock_id, |
136 | enum hrtimer_mode mode) |
137 | { |
138 | hrtimer_init(timer: &work->timer, which_clock: clock_id, mode); |
139 | work->timer.function = msm_hrtimer_worktimer; |
140 | work->worker = worker; |
141 | kthread_init_work(&work->work, fn); |
142 | } |
143 | |
144 | struct icc_path *msm_icc_get(struct device *dev, const char *name) |
145 | { |
146 | struct device *mdss_dev = dev->parent; |
147 | struct icc_path *path; |
148 | |
149 | path = of_icc_get(dev, name); |
150 | if (path) |
151 | return path; |
152 | |
153 | /* |
154 | * If there are no interconnects attached to the corresponding device |
155 | * node, of_icc_get() will return NULL. |
156 | * |
157 | * If the MDP5/DPU device node doesn't have interconnects, lookup the |
158 | * path in the parent (MDSS) device. |
159 | */ |
160 | return of_icc_get(dev: mdss_dev, name); |
161 | |
162 | } |
163 | |