Commit | Line | Data |
---|---|---|
f772ebfb VK |
1 | /* |
2 | * Copyright (c) 2014 Qualcomm Atheros, Inc. | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
17 | #include <linux/of.h> | |
18 | #include <linux/slab.h> | |
19 | #include <linux/msm-bus.h> | |
20 | ||
21 | #include "wil_platform.h" | |
22 | #include "wil_platform_msm.h" | |
23 | ||
24 | /** | |
25 | * struct wil_platform_msm - wil6210 msm platform module info | |
26 | * | |
27 | * @dev: device object | |
28 | * @msm_bus_handle: handle for using msm_bus API | |
29 | * @pdata: bus scale info retrieved from DT | |
30 | */ | |
31 | struct wil_platform_msm { | |
32 | struct device *dev; | |
33 | uint32_t msm_bus_handle; | |
34 | struct msm_bus_scale_pdata *pdata; | |
35 | }; | |
36 | ||
37 | #define KBTOB(a) (a * 1000ULL) | |
38 | ||
39 | /** | |
40 | * wil_platform_get_pdata() - Generate bus client data from device tree | |
41 | * provided by clients. | |
42 | * | |
43 | * dev: device object | |
44 | * of_node: Device tree node to extract information from | |
45 | * | |
46 | * The function returns a valid pointer to the allocated bus-scale-pdata | |
47 | * if the vectors were correctly read from the client's device node. | |
48 | * Any error in reading or parsing the device node will return NULL | |
49 | * to the caller. | |
50 | */ | |
51 | static struct msm_bus_scale_pdata *wil_platform_get_pdata( | |
52 | struct device *dev, | |
53 | struct device_node *of_node) | |
54 | { | |
55 | struct msm_bus_scale_pdata *pdata; | |
56 | struct msm_bus_paths *usecase; | |
57 | int i, j, ret, len; | |
58 | unsigned int num_usecases, num_paths, mem_size; | |
59 | const uint32_t *vec_arr; | |
60 | struct msm_bus_vectors *vectors; | |
61 | ||
62 | /* first read num_usecases and num_paths so we can calculate | |
63 | * amount of memory to allocate | |
64 | */ | |
65 | ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases", | |
66 | &num_usecases); | |
67 | if (ret) { | |
68 | dev_err(dev, "Error: num-usecases not found\n"); | |
69 | return NULL; | |
70 | } | |
71 | ||
72 | ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths", | |
73 | &num_paths); | |
74 | if (ret) { | |
75 | dev_err(dev, "Error: num_paths not found\n"); | |
76 | return NULL; | |
77 | } | |
78 | ||
79 | /* pdata memory layout: | |
80 | * msm_bus_scale_pdata | |
81 | * msm_bus_paths[num_usecases] | |
82 | * msm_bus_vectors[num_usecases][num_paths] | |
83 | */ | |
84 | mem_size = sizeof(struct msm_bus_scale_pdata) + | |
85 | sizeof(struct msm_bus_paths) * num_usecases + | |
86 | sizeof(struct msm_bus_vectors) * num_usecases * num_paths; | |
87 | ||
88 | pdata = kzalloc(mem_size, GFP_KERNEL); | |
89 | if (!pdata) | |
90 | return NULL; | |
91 | ||
92 | ret = of_property_read_string(of_node, "qcom,msm-bus,name", | |
93 | &pdata->name); | |
94 | if (ret) { | |
95 | dev_err(dev, "Error: Client name not found\n"); | |
96 | goto err; | |
97 | } | |
98 | ||
99 | if (of_property_read_bool(of_node, "qcom,msm-bus,active-only")) { | |
100 | pdata->active_only = 1; | |
101 | } else { | |
102 | dev_info(dev, "active_only flag absent.\n"); | |
103 | dev_info(dev, "Using dual context by default\n"); | |
104 | } | |
105 | ||
106 | pdata->num_usecases = num_usecases; | |
107 | pdata->usecase = (struct msm_bus_paths *)(pdata + 1); | |
108 | ||
109 | vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len); | |
110 | if (vec_arr == NULL) { | |
111 | dev_err(dev, "Error: Vector array not found\n"); | |
112 | goto err; | |
113 | } | |
114 | ||
115 | if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) { | |
116 | dev_err(dev, "Error: Length-error on getting vectors\n"); | |
117 | goto err; | |
118 | } | |
119 | ||
120 | vectors = (struct msm_bus_vectors *)(pdata->usecase + num_usecases); | |
121 | for (i = 0; i < num_usecases; i++) { | |
122 | usecase = &pdata->usecase[i]; | |
123 | usecase->num_paths = num_paths; | |
124 | usecase->vectors = &vectors[i]; | |
125 | ||
126 | for (j = 0; j < num_paths; j++) { | |
127 | int index = ((i * num_paths) + j) * 4; | |
128 | ||
129 | usecase->vectors[j].src = be32_to_cpu(vec_arr[index]); | |
130 | usecase->vectors[j].dst = | |
131 | be32_to_cpu(vec_arr[index + 1]); | |
132 | usecase->vectors[j].ab = (uint64_t) | |
133 | KBTOB(be32_to_cpu(vec_arr[index + 2])); | |
134 | usecase->vectors[j].ib = (uint64_t) | |
135 | KBTOB(be32_to_cpu(vec_arr[index + 3])); | |
136 | } | |
137 | } | |
138 | ||
139 | return pdata; | |
140 | ||
141 | err: | |
142 | kfree(pdata); | |
143 | ||
144 | return NULL; | |
145 | } | |
146 | ||
147 | /* wil_platform API (callbacks) */ | |
148 | ||
149 | static int wil_platform_bus_request(void *handle, | |
150 | uint32_t kbps /* KBytes/Sec */) | |
151 | { | |
152 | int rc, i; | |
153 | struct wil_platform_msm *msm = (struct wil_platform_msm *)handle; | |
154 | int vote = 0; /* vote 0 in case requested kbps cannot be satisfied */ | |
155 | struct msm_bus_paths *usecase; | |
156 | uint32_t usecase_kbps; | |
157 | uint32_t min_kbps = ~0; | |
158 | ||
159 | /* find the lowest usecase that is bigger than requested kbps */ | |
160 | for (i = 0; i < msm->pdata->num_usecases; i++) { | |
161 | usecase = &msm->pdata->usecase[i]; | |
162 | /* assume we have single path (vectors[0]). If we ever | |
163 | * have multiple paths, need to define the behavior */ | |
164 | usecase_kbps = div64_u64(usecase->vectors[0].ib, 1000); | |
165 | if (usecase_kbps >= kbps && usecase_kbps < min_kbps) { | |
166 | min_kbps = usecase_kbps; | |
167 | vote = i; | |
168 | } | |
169 | } | |
170 | ||
171 | rc = msm_bus_scale_client_update_request(msm->msm_bus_handle, vote); | |
172 | if (rc) | |
173 | dev_err(msm->dev, "Failed msm_bus voting. kbps=%d vote=%d, rc=%d\n", | |
174 | kbps, vote, rc); | |
175 | else | |
176 | /* TOOD: remove */ | |
177 | dev_info(msm->dev, "msm_bus_scale_client_update_request succeeded. kbps=%d vote=%d\n", | |
178 | kbps, vote); | |
179 | ||
180 | return rc; | |
181 | } | |
182 | ||
183 | static void wil_platform_uninit(void *handle) | |
184 | { | |
185 | struct wil_platform_msm *msm = (struct wil_platform_msm *)handle; | |
186 | ||
187 | dev_info(msm->dev, "wil_platform_uninit\n"); | |
188 | ||
189 | if (msm->msm_bus_handle) | |
190 | msm_bus_scale_unregister_client(msm->msm_bus_handle); | |
191 | ||
192 | kfree(msm->pdata); | |
193 | kfree(msm); | |
194 | } | |
195 | ||
196 | static int wil_platform_msm_bus_register(struct wil_platform_msm *msm, | |
197 | struct device_node *node) | |
198 | { | |
199 | msm->pdata = wil_platform_get_pdata(msm->dev, node); | |
200 | if (!msm->pdata) { | |
201 | dev_err(msm->dev, "Failed getting DT info\n"); | |
202 | return -EINVAL; | |
203 | } | |
204 | ||
205 | msm->msm_bus_handle = msm_bus_scale_register_client(msm->pdata); | |
206 | if (!msm->msm_bus_handle) { | |
207 | dev_err(msm->dev, "Failed msm_bus registration\n"); | |
208 | return -EINVAL; | |
209 | } | |
210 | ||
211 | dev_info(msm->dev, "msm_bus registration succeeded! handle 0x%x\n", | |
212 | msm->msm_bus_handle); | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | /** | |
218 | * wil_platform_msm_init() - wil6210 msm platform module init | |
219 | * | |
220 | * The function must be called before all other functions in this module. | |
221 | * It returns a handle which is used with the rest of the API | |
222 | * | |
223 | */ | |
224 | void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops) | |
225 | { | |
226 | struct device_node *of_node; | |
227 | struct wil_platform_msm *msm; | |
228 | int rc; | |
229 | ||
230 | of_node = of_find_compatible_node(NULL, NULL, "qcom,wil6210"); | |
231 | if (!of_node) { | |
232 | /* this could mean non-msm platform */ | |
233 | dev_err(dev, "DT node not found\n"); | |
234 | return NULL; | |
235 | } | |
236 | ||
237 | msm = kzalloc(sizeof(*msm), GFP_KERNEL); | |
238 | if (!msm) | |
239 | return NULL; | |
240 | ||
241 | msm->dev = dev; | |
242 | ||
243 | /* register with msm_bus module for scaling requests */ | |
244 | rc = wil_platform_msm_bus_register(msm, of_node); | |
245 | if (rc) | |
246 | goto cleanup; | |
247 | ||
248 | memset(ops, 0, sizeof(*ops)); | |
249 | ops->bus_request = wil_platform_bus_request; | |
250 | ops->uninit = wil_platform_uninit; | |
251 | ||
252 | return (void *)msm; | |
253 | ||
254 | cleanup: | |
255 | kfree(msm); | |
256 | return NULL; | |
257 | } |