wil6210: fix race condition between BACK event and Rx data
[deliverable/linux.git] / drivers / net / wireless / ath / wil6210 / wil_platform_msm.c
CommitLineData
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 */
31struct 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 */
51static 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
141err:
142 kfree(pdata);
143
144 return NULL;
145}
146
147/* wil_platform API (callbacks) */
148
149static 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
183static 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
196static 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 */
224void *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
254cleanup:
255 kfree(msm);
256 return NULL;
257}
This page took 0.044194 seconds and 5 git commands to generate.