]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/net/wireless/ath/ath10k/bmi.c
Merge remote-tracking branch 'asoc/fix/ab8500' into asoc-linus
[karo-tx-linux.git] / drivers / net / wireless / ath / ath10k / bmi.c
1 /*
2  * Copyright (c) 2005-2011 Atheros Communications Inc.
3  * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 #include "bmi.h"
19 #include "hif.h"
20 #include "debug.h"
21 #include "htc.h"
22
23 void ath10k_bmi_start(struct ath10k *ar)
24 {
25         ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
26         ar->bmi.done_sent = false;
27 }
28
29 int ath10k_bmi_done(struct ath10k *ar)
30 {
31         struct bmi_cmd cmd;
32         u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
33         int ret;
34
35         if (ar->bmi.done_sent) {
36                 ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
37                 return 0;
38         }
39
40         ar->bmi.done_sent = true;
41         cmd.id = __cpu_to_le32(BMI_DONE);
42
43         ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
44         if (ret) {
45                 ath10k_warn("unable to write to the device: %d\n", ret);
46                 return ret;
47         }
48
49         ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
50         return 0;
51 }
52
53 int ath10k_bmi_get_target_info(struct ath10k *ar,
54                                struct bmi_target_info *target_info)
55 {
56         struct bmi_cmd cmd;
57         union bmi_resp resp;
58         u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
59         u32 resplen = sizeof(resp.get_target_info);
60         int ret;
61
62         if (ar->bmi.done_sent) {
63                 ath10k_warn("BMI Get Target Info Command disallowed\n");
64                 return -EBUSY;
65         }
66
67         cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
68
69         ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
70         if (ret) {
71                 ath10k_warn("unable to get target info from device\n");
72                 return ret;
73         }
74
75         if (resplen < sizeof(resp.get_target_info)) {
76                 ath10k_warn("invalid get_target_info response length (%d)\n",
77                             resplen);
78                 return -EIO;
79         }
80
81         target_info->version = __le32_to_cpu(resp.get_target_info.version);
82         target_info->type    = __le32_to_cpu(resp.get_target_info.type);
83         return 0;
84 }
85
86 int ath10k_bmi_read_memory(struct ath10k *ar,
87                            u32 address, void *buffer, u32 length)
88 {
89         struct bmi_cmd cmd;
90         union bmi_resp resp;
91         u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
92         u32 rxlen;
93         int ret;
94
95         if (ar->bmi.done_sent) {
96                 ath10k_warn("command disallowed\n");
97                 return -EBUSY;
98         }
99
100         ath10k_dbg(ATH10K_DBG_CORE,
101                    "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
102                    __func__, ar, address, length);
103
104         while (length) {
105                 rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
106
107                 cmd.id            = __cpu_to_le32(BMI_READ_MEMORY);
108                 cmd.read_mem.addr = __cpu_to_le32(address);
109                 cmd.read_mem.len  = __cpu_to_le32(rxlen);
110
111                 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
112                                                   &resp, &rxlen);
113                 if (ret) {
114                         ath10k_warn("unable to read from the device (%d)\n",
115                                     ret);
116                         return ret;
117                 }
118
119                 memcpy(buffer, resp.read_mem.payload, rxlen);
120                 address += rxlen;
121                 buffer  += rxlen;
122                 length  -= rxlen;
123         }
124
125         return 0;
126 }
127
128 int ath10k_bmi_write_memory(struct ath10k *ar,
129                             u32 address, const void *buffer, u32 length)
130 {
131         struct bmi_cmd cmd;
132         u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
133         u32 txlen;
134         int ret;
135
136         if (ar->bmi.done_sent) {
137                 ath10k_warn("command disallowed\n");
138                 return -EBUSY;
139         }
140
141         ath10k_dbg(ATH10K_DBG_CORE,
142                    "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
143                    __func__, ar, address, length);
144
145         while (length) {
146                 txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
147
148                 /* copy before roundup to avoid reading beyond buffer*/
149                 memcpy(cmd.write_mem.payload, buffer, txlen);
150                 txlen = roundup(txlen, 4);
151
152                 cmd.id             = __cpu_to_le32(BMI_WRITE_MEMORY);
153                 cmd.write_mem.addr = __cpu_to_le32(address);
154                 cmd.write_mem.len  = __cpu_to_le32(txlen);
155
156                 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
157                                                   NULL, NULL);
158                 if (ret) {
159                         ath10k_warn("unable to write to the device (%d)\n",
160                                     ret);
161                         return ret;
162                 }
163
164                 /* fixup roundup() so `length` zeroes out for last chunk */
165                 txlen = min(txlen, length);
166
167                 address += txlen;
168                 buffer  += txlen;
169                 length  -= txlen;
170         }
171
172         return 0;
173 }
174
175 int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
176 {
177         struct bmi_cmd cmd;
178         union bmi_resp resp;
179         u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
180         u32 resplen = sizeof(resp.execute);
181         int ret;
182
183         if (ar->bmi.done_sent) {
184                 ath10k_warn("command disallowed\n");
185                 return -EBUSY;
186         }
187
188         ath10k_dbg(ATH10K_DBG_CORE,
189                    "%s: (device: 0x%p, address: 0x%x, param: %d)\n",
190                    __func__, ar, address, *param);
191
192         cmd.id            = __cpu_to_le32(BMI_EXECUTE);
193         cmd.execute.addr  = __cpu_to_le32(address);
194         cmd.execute.param = __cpu_to_le32(*param);
195
196         ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
197         if (ret) {
198                 ath10k_warn("unable to read from the device\n");
199                 return ret;
200         }
201
202         if (resplen < sizeof(resp.execute)) {
203                 ath10k_warn("invalid execute response length (%d)\n",
204                             resplen);
205                 return ret;
206         }
207
208         *param = __le32_to_cpu(resp.execute.result);
209         return 0;
210 }
211
212 int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
213 {
214         struct bmi_cmd cmd;
215         u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
216         u32 txlen;
217         int ret;
218
219         if (ar->bmi.done_sent) {
220                 ath10k_warn("command disallowed\n");
221                 return -EBUSY;
222         }
223
224         while (length) {
225                 txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
226
227                 WARN_ON_ONCE(txlen & 3);
228
229                 cmd.id          = __cpu_to_le32(BMI_LZ_DATA);
230                 cmd.lz_data.len = __cpu_to_le32(txlen);
231                 memcpy(cmd.lz_data.payload, buffer, txlen);
232
233                 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
234                                                   NULL, NULL);
235                 if (ret) {
236                         ath10k_warn("unable to write to the device\n");
237                         return ret;
238                 }
239
240                 buffer += txlen;
241                 length -= txlen;
242         }
243
244         return 0;
245 }
246
247 int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
248 {
249         struct bmi_cmd cmd;
250         u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
251         int ret;
252
253         if (ar->bmi.done_sent) {
254                 ath10k_warn("command disallowed\n");
255                 return -EBUSY;
256         }
257
258         cmd.id            = __cpu_to_le32(BMI_LZ_STREAM_START);
259         cmd.lz_start.addr = __cpu_to_le32(address);
260
261         ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
262         if (ret) {
263                 ath10k_warn("unable to Start LZ Stream to the device\n");
264                 return ret;
265         }
266
267         return 0;
268 }
269
270 int ath10k_bmi_fast_download(struct ath10k *ar,
271                              u32 address, const void *buffer, u32 length)
272 {
273         u8 trailer[4] = {};
274         u32 head_len = rounddown(length, 4);
275         u32 trailer_len = length - head_len;
276         int ret;
277
278         ret = ath10k_bmi_lz_stream_start(ar, address);
279         if (ret)
280                 return ret;
281
282         /* copy the last word into a zero padded buffer */
283         if (trailer_len > 0)
284                 memcpy(trailer, buffer + head_len, trailer_len);
285
286         ret = ath10k_bmi_lz_data(ar, buffer, head_len);
287         if (ret)
288                 return ret;
289
290         if (trailer_len > 0)
291                 ret = ath10k_bmi_lz_data(ar, trailer, 4);
292
293         if (ret != 0)
294                 return ret;
295
296         /*
297          * Close compressed stream and open a new (fake) one.
298          * This serves mainly to flush Target caches.
299          */
300         ret = ath10k_bmi_lz_stream_start(ar, 0x00);
301
302         return ret;
303 }