]>
Commit | Line | Data |
---|---|---|
903ac63f | 1 | // SPDX-License-Identifier: GPL-2.0 |
e61c7a1c AS |
2 | /* |
3 | * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. | |
4 | * All rights reserved. | |
5 | */ | |
6 | ||
8c05c98b TR |
7 | #include <linux/if_ether.h> |
8 | #include <linux/ip.h> | |
60bd1003 | 9 | #include "wilc_wfi_netdevice.h" |
17e8f165 | 10 | #include "wilc_wlan_cfg.h" |
c5c77ba1 | 11 | |
888505e7 | 12 | static enum chip_ps_states chip_ps_state = CHIP_WAKEDUP; |
c5c77ba1 | 13 | |
2345ef30 AS |
14 | static inline bool is_wilc1000(u32 id) |
15 | { | |
16 | return ((id & 0xfffff000) == 0x100000 ? true : false); | |
17 | } | |
18 | ||
13ae5a7b | 19 | static inline void acquire_bus(struct wilc *wilc, enum bus_acquire acquire) |
c5c77ba1 | 20 | { |
562ed3f1 | 21 | mutex_lock(&wilc->hif_cs); |
33c64975 GL |
22 | if (acquire == ACQUIRE_AND_WAKEUP) |
23 | chip_wakeup(wilc); | |
c5c77ba1 | 24 | } |
2d6973e6 | 25 | |
a9ee75e8 | 26 | static inline void release_bus(struct wilc *wilc, enum bus_release release) |
c5c77ba1 | 27 | { |
c5c77ba1 | 28 | if (release == RELEASE_ALLOW_SLEEP) |
00215dde | 29 | chip_allow_sleep(wilc); |
562ed3f1 | 30 | mutex_unlock(&wilc->hif_cs); |
c5c77ba1 | 31 | } |
c5c77ba1 | 32 | |
e9576e96 | 33 | static void wilc_wlan_txq_remove(struct wilc *wilc, struct txq_entry_t *tqe) |
c5c77ba1 | 34 | { |
3d9241d6 | 35 | list_del(&tqe->list); |
67e2a07e | 36 | wilc->txq_entries -= 1; |
c5c77ba1 JK |
37 | } |
38 | ||
718fc2c9 GL |
39 | static struct txq_entry_t * |
40 | wilc_wlan_txq_remove_from_head(struct net_device *dev) | |
c5c77ba1 | 41 | { |
3d9241d6 | 42 | struct txq_entry_t *tqe = NULL; |
c5c77ba1 | 43 | unsigned long flags; |
6bcba96e AS |
44 | struct wilc_vif *vif = netdev_priv(dev); |
45 | struct wilc *wilc = vif->wilc; | |
718fc2c9 GL |
46 | |
47 | spin_lock_irqsave(&wilc->txq_spinlock, flags); | |
39823a50 | 48 | |
3d9241d6 AS |
49 | if (!list_empty(&wilc->txq_head.list)) { |
50 | tqe = list_first_entry(&wilc->txq_head.list, struct txq_entry_t, | |
51 | list); | |
52 | list_del(&tqe->list); | |
67e2a07e | 53 | wilc->txq_entries -= 1; |
c5c77ba1 | 54 | } |
718fc2c9 | 55 | spin_unlock_irqrestore(&wilc->txq_spinlock, flags); |
c5c77ba1 JK |
56 | return tqe; |
57 | } | |
58 | ||
32f03328 GL |
59 | static void wilc_wlan_txq_add_to_tail(struct net_device *dev, |
60 | struct txq_entry_t *tqe) | |
c5c77ba1 | 61 | { |
c5c77ba1 | 62 | unsigned long flags; |
6bcba96e AS |
63 | struct wilc_vif *vif = netdev_priv(dev); |
64 | struct wilc *wilc = vif->wilc; | |
32f03328 GL |
65 | |
66 | spin_lock_irqsave(&wilc->txq_spinlock, flags); | |
c5c77ba1 | 67 | |
3d9241d6 | 68 | list_add_tail(&tqe->list, &wilc->txq_head.list); |
67e2a07e | 69 | wilc->txq_entries += 1; |
c5c77ba1 | 70 | |
32f03328 | 71 | spin_unlock_irqrestore(&wilc->txq_spinlock, flags); |
c5c77ba1 | 72 | |
b27a6d5e | 73 | complete(&wilc->txq_event); |
c5c77ba1 JK |
74 | } |
75 | ||
4d38a56b CP |
76 | static int wilc_wlan_txq_add_to_head(struct wilc_vif *vif, |
77 | struct txq_entry_t *tqe) | |
c5c77ba1 | 78 | { |
c5c77ba1 | 79 | unsigned long flags; |
79df6a49 GL |
80 | struct wilc *wilc = vif->wilc; |
81 | ||
334bed08 | 82 | mutex_lock(&wilc->txq_add_to_head_cs); |
c5c77ba1 | 83 | |
562ed3f1 | 84 | spin_lock_irqsave(&wilc->txq_spinlock, flags); |
c5c77ba1 | 85 | |
3d9241d6 | 86 | list_add(&tqe->list, &wilc->txq_head.list); |
67e2a07e | 87 | wilc->txq_entries += 1; |
c5c77ba1 | 88 | |
562ed3f1 | 89 | spin_unlock_irqrestore(&wilc->txq_spinlock, flags); |
334bed08 | 90 | mutex_unlock(&wilc->txq_add_to_head_cs); |
b27a6d5e | 91 | complete(&wilc->txq_event); |
c5c77ba1 | 92 | |
c5c77ba1 | 93 | return 0; |
c5c77ba1 JK |
94 | } |
95 | ||
130a41f0 LK |
96 | struct ack_session_info; |
97 | struct ack_session_info { | |
d06b6cb2 | 98 | u32 seq_num; |
9d54d3d5 | 99 | u32 bigger_ack_num; |
ec53adfe CL |
100 | u16 src_port; |
101 | u16 dst_port; | |
102 | u16 status; | |
eeb1c062 | 103 | }; |
c5c77ba1 | 104 | |
08b90b10 | 105 | struct pending_acks_info { |
fbc2fe16 | 106 | u32 ack_num; |
84136978 | 107 | u32 session_index; |
c5c77ba1 | 108 | struct txq_entry_t *txqe; |
08b90b10 | 109 | }; |
c5c77ba1 | 110 | |
c5c77ba1 JK |
111 | #define NOT_TCP_ACK (-1) |
112 | ||
113 | #define MAX_TCP_SESSION 25 | |
114 | #define MAX_PENDING_ACKS 256 | |
1608c403 AB |
115 | static struct ack_session_info ack_session_info[2 * MAX_TCP_SESSION]; |
116 | static struct pending_acks_info pending_acks_info[MAX_PENDING_ACKS]; | |
c5c77ba1 | 117 | |
1608c403 AB |
118 | static u32 pending_base; |
119 | static u32 tcp_session; | |
120 | static u32 pending_acks; | |
c5c77ba1 | 121 | |
67620788 | 122 | static inline int add_tcp_session(u32 src_prt, u32 dst_prt, u32 seq) |
c5c77ba1 | 123 | { |
4fd62291 GL |
124 | if (tcp_session < 2 * MAX_TCP_SESSION) { |
125 | ack_session_info[tcp_session].seq_num = seq; | |
126 | ack_session_info[tcp_session].bigger_ack_num = 0; | |
127 | ack_session_info[tcp_session].src_port = src_prt; | |
128 | ack_session_info[tcp_session].dst_port = dst_prt; | |
129 | tcp_session++; | |
130 | } | |
c5c77ba1 JK |
131 | return 0; |
132 | } | |
133 | ||
b801a96c | 134 | static inline int update_tcp_session(u32 index, u32 ack) |
c5c77ba1 | 135 | { |
4fd62291 GL |
136 | if (index < 2 * MAX_TCP_SESSION && |
137 | ack > ack_session_info[index].bigger_ack_num) | |
b801a96c | 138 | ack_session_info[index].bigger_ack_num = ack; |
c5c77ba1 | 139 | return 0; |
c5c77ba1 | 140 | } |
2d6973e6 | 141 | |
ea03f57b | 142 | static inline int add_tcp_pending_ack(u32 ack, u32 session_index, |
66a43a91 | 143 | struct txq_entry_t *txqe) |
c5c77ba1 | 144 | { |
6e4f1a85 AS |
145 | u32 i = pending_base + pending_acks; |
146 | ||
147 | if (i < MAX_PENDING_ACKS) { | |
148 | pending_acks_info[i].ack_num = ack; | |
149 | pending_acks_info[i].txqe = txqe; | |
150 | pending_acks_info[i].session_index = session_index; | |
151 | txqe->tcp_pending_ack_idx = i; | |
d12ac7e2 | 152 | pending_acks++; |
c5c77ba1 JK |
153 | } |
154 | return 0; | |
155 | } | |
c5c77ba1 | 156 | |
aae96201 | 157 | static inline void tcp_process(struct net_device *dev, struct txq_entry_t *tqe) |
c5c77ba1 | 158 | { |
8c05c98b TR |
159 | void *buffer = tqe->buffer; |
160 | const struct ethhdr *eth_hdr_ptr = buffer; | |
c5c77ba1 | 161 | int i; |
c5c77ba1 | 162 | unsigned long flags; |
6bcba96e AS |
163 | struct wilc_vif *vif = netdev_priv(dev); |
164 | struct wilc *wilc = vif->wilc; | |
7ec6f7f2 AS |
165 | const struct iphdr *ip_hdr_ptr; |
166 | const struct tcphdr *tcp_hdr_ptr; | |
167 | u32 IHL, total_length, data_offset; | |
82bb18e1 | 168 | |
c6866cc4 | 169 | spin_lock_irqsave(&wilc->txq_spinlock, flags); |
c5c77ba1 | 170 | |
7ec6f7f2 AS |
171 | if (eth_hdr_ptr->h_proto != htons(ETH_P_IP)) |
172 | goto out; | |
c5c77ba1 | 173 | |
7ec6f7f2 | 174 | ip_hdr_ptr = buffer + ETH_HLEN; |
8dfaafd6 | 175 | |
7ec6f7f2 AS |
176 | if (ip_hdr_ptr->protocol != IPPROTO_TCP) |
177 | goto out; | |
8c05c98b | 178 | |
7ec6f7f2 AS |
179 | IHL = ip_hdr_ptr->ihl << 2; |
180 | tcp_hdr_ptr = buffer + ETH_HLEN + IHL; | |
181 | total_length = ntohs(ip_hdr_ptr->tot_len); | |
8dfaafd6 | 182 | |
7ec6f7f2 AS |
183 | data_offset = tcp_hdr_ptr->doff << 2; |
184 | if (total_length == (IHL + data_offset)) { | |
185 | u32 seq_no, ack_no; | |
4998ec33 | 186 | |
7ec6f7f2 AS |
187 | seq_no = ntohl(tcp_hdr_ptr->seq); |
188 | ack_no = ntohl(tcp_hdr_ptr->ack_seq); | |
189 | for (i = 0; i < tcp_session; i++) { | |
190 | u32 j = ack_session_info[i].seq_num; | |
39823a50 | 191 | |
7ec6f7f2 AS |
192 | if (i < 2 * MAX_TCP_SESSION && |
193 | j == seq_no) { | |
194 | update_tcp_session(i, ack_no); | |
195 | break; | |
c5c77ba1 | 196 | } |
c5c77ba1 | 197 | } |
7ec6f7f2 AS |
198 | if (i == tcp_session) |
199 | add_tcp_session(0, 0, seq_no); | |
200 | ||
201 | add_tcp_pending_ack(ack_no, i, tqe); | |
c5c77ba1 | 202 | } |
7ec6f7f2 AS |
203 | |
204 | out: | |
82bb18e1 | 205 | spin_unlock_irqrestore(&wilc->txq_spinlock, flags); |
c5c77ba1 JK |
206 | } |
207 | ||
c029e99c | 208 | static int wilc_wlan_txq_filter_dup_tcp_ack(struct net_device *dev) |
c5c77ba1 | 209 | { |
6bcba96e AS |
210 | struct wilc_vif *vif = netdev_priv(dev); |
211 | struct wilc *wilc = vif->wilc; | |
fbc2fe16 | 212 | u32 i = 0; |
442eda4e | 213 | u32 dropped = 0; |
089a137a | 214 | unsigned long flags; |
c5c77ba1 | 215 | |
089a137a | 216 | spin_lock_irqsave(&wilc->txq_spinlock, flags); |
d12ac7e2 | 217 | for (i = pending_base; i < (pending_base + pending_acks); i++) { |
552699bc AS |
218 | u32 session_index; |
219 | u32 bigger_ack_num; | |
220 | ||
221 | if (i >= MAX_PENDING_ACKS) | |
222 | break; | |
223 | ||
224 | session_index = pending_acks_info[i].session_index; | |
225 | ||
226 | if (session_index >= 2 * MAX_TCP_SESSION) | |
4fd62291 | 227 | break; |
552699bc AS |
228 | |
229 | bigger_ack_num = ack_session_info[session_index].bigger_ack_num; | |
230 | ||
231 | if (pending_acks_info[i].ack_num < bigger_ack_num) { | |
c5c77ba1 | 232 | struct txq_entry_t *tqe; |
8dfaafd6 | 233 | |
2bb17087 | 234 | tqe = pending_acks_info[i].txqe; |
c5c77ba1 | 235 | if (tqe) { |
e9576e96 | 236 | wilc_wlan_txq_remove(wilc, tqe); |
ac087c82 | 237 | tqe->status = 1; |
c5c77ba1 | 238 | if (tqe->tx_complete_func) |
ab12d8c7 LK |
239 | tqe->tx_complete_func(tqe->priv, |
240 | tqe->status); | |
a18dd630 | 241 | kfree(tqe); |
442eda4e | 242 | dropped++; |
c5c77ba1 JK |
243 | } |
244 | } | |
245 | } | |
d12ac7e2 | 246 | pending_acks = 0; |
3056ec39 | 247 | tcp_session = 0; |
c5c77ba1 | 248 | |
2ae91edb LK |
249 | if (pending_base == 0) |
250 | pending_base = MAX_TCP_SESSION; | |
78174ada | 251 | else |
2ae91edb | 252 | pending_base = 0; |
c5c77ba1 | 253 | |
089a137a | 254 | spin_unlock_irqrestore(&wilc->txq_spinlock, flags); |
c5c77ba1 | 255 | |
442eda4e | 256 | while (dropped > 0) { |
b27a6d5e | 257 | wait_for_completion_timeout(&wilc->txq_event, |
27f2d03e | 258 | msecs_to_jiffies(1)); |
442eda4e | 259 | dropped--; |
c5c77ba1 JK |
260 | } |
261 | ||
262 | return 1; | |
263 | } | |
c5c77ba1 | 264 | |
d38f5ba4 | 265 | static bool enabled; |
c5c77ba1 | 266 | |
0e1af73d | 267 | void wilc_enable_tcp_ack_filter(bool value) |
c5c77ba1 | 268 | { |
7c4bafe9 | 269 | enabled = value; |
c5c77ba1 JK |
270 | } |
271 | ||
79df6a49 GL |
272 | static int wilc_wlan_txq_add_cfg_pkt(struct wilc_vif *vif, u8 *buffer, |
273 | u32 buffer_size) | |
c5c77ba1 | 274 | { |
c5c77ba1 | 275 | struct txq_entry_t *tqe; |
79df6a49 | 276 | struct wilc *wilc = vif->wilc; |
c5c77ba1 | 277 | |
79df6a49 | 278 | netdev_dbg(vif->ndev, "Adding config packet ...\n"); |
67e2a07e | 279 | if (wilc->quit) { |
79df6a49 | 280 | netdev_dbg(vif->ndev, "Return due to clear function\n"); |
fa659698 | 281 | complete(&wilc->cfg_event); |
c5c77ba1 JK |
282 | return 0; |
283 | } | |
284 | ||
1d401a4d | 285 | tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC); |
e27604ef | 286 | if (!tqe) |
c5c77ba1 | 287 | return 0; |
c5c77ba1 JK |
288 | |
289 | tqe->type = WILC_CFG_PKT; | |
290 | tqe->buffer = buffer; | |
291 | tqe->buffer_size = buffer_size; | |
292 | tqe->tx_complete_func = NULL; | |
293 | tqe->priv = NULL; | |
b719302d | 294 | tqe->tcp_pending_ack_idx = NOT_TCP_ACK; |
c5c77ba1 | 295 | |
245b431b CB |
296 | if (wilc_wlan_txq_add_to_head(vif, tqe)) { |
297 | kfree(tqe); | |
c5c77ba1 | 298 | return 0; |
245b431b CB |
299 | } |
300 | ||
c5c77ba1 JK |
301 | return 1; |
302 | } | |
303 | ||
691bbd42 GL |
304 | int wilc_wlan_txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer, |
305 | u32 buffer_size, wilc_tx_complete_func_t func) | |
c5c77ba1 | 306 | { |
c5c77ba1 | 307 | struct txq_entry_t *tqe; |
a4cac481 | 308 | struct wilc_vif *vif = netdev_priv(dev); |
67e2a07e GL |
309 | struct wilc *wilc; |
310 | ||
a4cac481 | 311 | wilc = vif->wilc; |
c5c77ba1 | 312 | |
67e2a07e | 313 | if (wilc->quit) |
c5c77ba1 JK |
314 | return 0; |
315 | ||
1d401a4d | 316 | tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC); |
c5c77ba1 | 317 | |
a4b17197 | 318 | if (!tqe) |
c5c77ba1 JK |
319 | return 0; |
320 | tqe->type = WILC_NET_PKT; | |
321 | tqe->buffer = buffer; | |
322 | tqe->buffer_size = buffer_size; | |
323 | tqe->tx_complete_func = func; | |
324 | tqe->priv = priv; | |
325 | ||
b719302d | 326 | tqe->tcp_pending_ack_idx = NOT_TCP_ACK; |
5a9ff74d | 327 | if (enabled) |
82bb18e1 | 328 | tcp_process(dev, tqe); |
32f03328 | 329 | wilc_wlan_txq_add_to_tail(dev, tqe); |
67e2a07e | 330 | return wilc->txq_entries; |
c5c77ba1 | 331 | } |
fcc6ef92 | 332 | |
829c477f GL |
333 | int wilc_wlan_txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer, |
334 | u32 buffer_size, wilc_tx_complete_func_t func) | |
c5c77ba1 | 335 | { |
c5c77ba1 | 336 | struct txq_entry_t *tqe; |
a4cac481 | 337 | struct wilc_vif *vif = netdev_priv(dev); |
67e2a07e GL |
338 | struct wilc *wilc; |
339 | ||
a4cac481 | 340 | wilc = vif->wilc; |
c5c77ba1 | 341 | |
67e2a07e | 342 | if (wilc->quit) |
c5c77ba1 JK |
343 | return 0; |
344 | ||
1d401a4d | 345 | tqe = kmalloc(sizeof(*tqe), GFP_KERNEL); |
c5c77ba1 | 346 | |
a4b17197 | 347 | if (!tqe) |
c5c77ba1 JK |
348 | return 0; |
349 | tqe->type = WILC_MGMT_PKT; | |
350 | tqe->buffer = buffer; | |
351 | tqe->buffer_size = buffer_size; | |
352 | tqe->tx_complete_func = func; | |
353 | tqe->priv = priv; | |
b719302d | 354 | tqe->tcp_pending_ack_idx = NOT_TCP_ACK; |
32f03328 | 355 | wilc_wlan_txq_add_to_tail(dev, tqe); |
c5c77ba1 JK |
356 | return 1; |
357 | } | |
fcc6ef92 | 358 | |
7af0522c | 359 | static struct txq_entry_t *wilc_wlan_txq_get_first(struct wilc *wilc) |
c5c77ba1 | 360 | { |
3d9241d6 | 361 | struct txq_entry_t *tqe = NULL; |
c5c77ba1 JK |
362 | unsigned long flags; |
363 | ||
7af0522c | 364 | spin_lock_irqsave(&wilc->txq_spinlock, flags); |
c5c77ba1 | 365 | |
3d9241d6 AS |
366 | if (!list_empty(&wilc->txq_head.list)) |
367 | tqe = list_first_entry(&wilc->txq_head.list, struct txq_entry_t, | |
368 | list); | |
c5c77ba1 | 369 | |
7af0522c | 370 | spin_unlock_irqrestore(&wilc->txq_spinlock, flags); |
c5c77ba1 | 371 | |
c5c77ba1 JK |
372 | return tqe; |
373 | } | |
374 | ||
50a0b3b7 GL |
375 | static struct txq_entry_t *wilc_wlan_txq_get_next(struct wilc *wilc, |
376 | struct txq_entry_t *tqe) | |
c5c77ba1 | 377 | { |
c5c77ba1 | 378 | unsigned long flags; |
8739eeba | 379 | |
50a0b3b7 | 380 | spin_lock_irqsave(&wilc->txq_spinlock, flags); |
c5c77ba1 | 381 | |
3d9241d6 AS |
382 | if (!list_is_last(&tqe->list, &wilc->txq_head.list)) |
383 | tqe = list_next_entry(tqe, list); | |
384 | else | |
385 | tqe = NULL; | |
50a0b3b7 | 386 | spin_unlock_irqrestore(&wilc->txq_spinlock, flags); |
c5c77ba1 | 387 | |
c5c77ba1 JK |
388 | return tqe; |
389 | } | |
390 | ||
42e4a3ad | 391 | static void wilc_wlan_rxq_add(struct wilc *wilc, struct rxq_entry_t *rqe) |
c5c77ba1 | 392 | { |
67e2a07e | 393 | if (wilc->quit) |
42e4a3ad | 394 | return; |
c5c77ba1 | 395 | |
d06f362c | 396 | mutex_lock(&wilc->rxq_cs); |
6adc35d9 | 397 | list_add_tail(&rqe->list, &wilc->rxq_head.list); |
d06f362c | 398 | mutex_unlock(&wilc->rxq_cs); |
c5c77ba1 JK |
399 | } |
400 | ||
db387635 | 401 | static struct rxq_entry_t *wilc_wlan_rxq_remove(struct wilc *wilc) |
c5c77ba1 | 402 | { |
6adc35d9 | 403 | struct rxq_entry_t *rqe = NULL; |
c5c77ba1 | 404 | |
6adc35d9 AS |
405 | mutex_lock(&wilc->rxq_cs); |
406 | if (!list_empty(&wilc->rxq_head.list)) { | |
407 | rqe = list_first_entry(&wilc->rxq_head.list, struct rxq_entry_t, | |
408 | list); | |
409 | list_del(&rqe->list); | |
c5c77ba1 | 410 | } |
6adc35d9 AS |
411 | mutex_unlock(&wilc->rxq_cs); |
412 | return rqe; | |
c5c77ba1 JK |
413 | } |
414 | ||
76855ba7 | 415 | void chip_allow_sleep(struct wilc *wilc) |
c5c77ba1 | 416 | { |
fbc2fe16 | 417 | u32 reg = 0; |
c5c77ba1 | 418 | |
af9ae09a | 419 | wilc->hif_func->hif_read_reg(wilc, 0xf0, ®); |
c5c77ba1 | 420 | |
af9ae09a | 421 | wilc->hif_func->hif_write_reg(wilc, 0xf0, reg & ~BIT(0)); |
fdc2ac1a | 422 | wilc->hif_func->hif_write_reg(wilc, 0xfa, 0); |
c5c77ba1 JK |
423 | } |
424 | ||
76855ba7 | 425 | void chip_wakeup(struct wilc *wilc) |
c5c77ba1 | 426 | { |
eed9e543 | 427 | u32 reg, clk_status_reg; |
c5c77ba1 | 428 | |
a3629a9e | 429 | if ((wilc->io_type & 0x1) == HIF_SPI) { |
c5c77ba1 | 430 | do { |
af9ae09a GL |
431 | wilc->hif_func->hif_read_reg(wilc, 1, ®); |
432 | wilc->hif_func->hif_write_reg(wilc, 1, reg | BIT(1)); | |
433 | wilc->hif_func->hif_write_reg(wilc, 1, reg & ~BIT(1)); | |
c5c77ba1 JK |
434 | |
435 | do { | |
80e29c7a | 436 | usleep_range(2 * 1000, 2 * 1000); |
7242eee1 | 437 | wilc_get_chipid(wilc, true); |
eed9e543 | 438 | } while (wilc_get_chipid(wilc, true) == 0); |
00215dde | 439 | } while (wilc_get_chipid(wilc, true) == 0); |
74cffafb | 440 | } else if ((wilc->io_type & 0x1) == HIF_SDIO) { |
fdc2ac1a GL |
441 | wilc->hif_func->hif_write_reg(wilc, 0xfa, 1); |
442 | udelay(200); | |
af9ae09a | 443 | wilc->hif_func->hif_read_reg(wilc, 0xf0, ®); |
c5c77ba1 | 444 | do { |
af9ae09a | 445 | wilc->hif_func->hif_write_reg(wilc, 0xf0, |
49dcd0dd | 446 | reg | BIT(0)); |
af9ae09a | 447 | wilc->hif_func->hif_read_reg(wilc, 0xf1, |
49dcd0dd | 448 | &clk_status_reg); |
c5c77ba1 | 449 | |
eed9e543 | 450 | while ((clk_status_reg & 0x1) == 0) { |
80e29c7a | 451 | usleep_range(2 * 1000, 2 * 1000); |
c5c77ba1 | 452 | |
af9ae09a | 453 | wilc->hif_func->hif_read_reg(wilc, 0xf1, |
49dcd0dd | 454 | &clk_status_reg); |
c5c77ba1 | 455 | } |
c5c77ba1 | 456 | if ((clk_status_reg & 0x1) == 0) { |
af9ae09a | 457 | wilc->hif_func->hif_write_reg(wilc, 0xf0, |
49dcd0dd | 458 | reg & (~BIT(0))); |
c5c77ba1 JK |
459 | } |
460 | } while ((clk_status_reg & 0x1) == 0); | |
461 | } | |
462 | ||
b7d1e18c | 463 | if (chip_ps_state == CHIP_SLEEPING_MANUAL) { |
fdc2ac1a | 464 | if (wilc_get_chipid(wilc, false) < 0x1002b0) { |
fbc2fe16 | 465 | u32 val32; |
8dfaafd6 | 466 | |
af9ae09a | 467 | wilc->hif_func->hif_read_reg(wilc, 0x1e1c, &val32); |
ffda203c | 468 | val32 |= BIT(6); |
af9ae09a | 469 | wilc->hif_func->hif_write_reg(wilc, 0x1e1c, val32); |
c5c77ba1 | 470 | |
af9ae09a | 471 | wilc->hif_func->hif_read_reg(wilc, 0x1e9c, &val32); |
ffda203c | 472 | val32 |= BIT(6); |
af9ae09a | 473 | wilc->hif_func->hif_write_reg(wilc, 0x1e9c, val32); |
c5c77ba1 JK |
474 | } |
475 | } | |
b7d1e18c | 476 | chip_ps_state = CHIP_WAKEDUP; |
c5c77ba1 | 477 | } |
c5c77ba1 | 478 | |
00215dde | 479 | void wilc_chip_sleep_manually(struct wilc *wilc) |
c5c77ba1 | 480 | { |
b7d1e18c | 481 | if (chip_ps_state != CHIP_WAKEDUP) |
c5c77ba1 | 482 | return; |
00215dde | 483 | acquire_bus(wilc, ACQUIRE_ONLY); |
c5c77ba1 | 484 | |
00215dde | 485 | chip_allow_sleep(wilc); |
af9ae09a | 486 | wilc->hif_func->hif_write_reg(wilc, 0x10a8, 1); |
c5c77ba1 | 487 | |
b7d1e18c | 488 | chip_ps_state = CHIP_SLEEPING_MANUAL; |
00215dde | 489 | release_bus(wilc, RELEASE_ONLY); |
c5c77ba1 JK |
490 | } |
491 | ||
fdc2ac1a GL |
492 | void host_wakeup_notify(struct wilc *wilc) |
493 | { | |
494 | acquire_bus(wilc, ACQUIRE_ONLY); | |
495 | wilc->hif_func->hif_write_reg(wilc, 0x10b0, 1); | |
496 | release_bus(wilc, RELEASE_ONLY); | |
497 | } | |
498 | ||
499 | void host_sleep_notify(struct wilc *wilc) | |
500 | { | |
501 | acquire_bus(wilc, ACQUIRE_ONLY); | |
502 | wilc->hif_func->hif_write_reg(wilc, 0x10ac, 1); | |
503 | release_bus(wilc, RELEASE_ONLY); | |
504 | } | |
505 | ||
b1d19298 | 506 | int wilc_wlan_handle_txq(struct net_device *dev, u32 *txq_count) |
c5c77ba1 | 507 | { |
c5c77ba1 | 508 | int i, entries = 0; |
fbc2fe16 CL |
509 | u32 sum; |
510 | u32 reg; | |
fbc2fe16 | 511 | u32 offset = 0; |
c5c77ba1 JK |
512 | int vmm_sz = 0; |
513 | struct txq_entry_t *tqe; | |
514 | int ret = 0; | |
515 | int counter; | |
516 | int timeout; | |
fbc2fe16 | 517 | u32 vmm_table[WILC_VMM_TBL_SIZE]; |
6bcba96e AS |
518 | struct wilc_vif *vif = netdev_priv(dev); |
519 | struct wilc *wilc = vif->wilc; | |
f782497c | 520 | const struct wilc_hif_func *func; |
6bcba96e | 521 | u8 *txb = wilc->tx_buffer; |
c5c77ba1 | 522 | |
89125618 AS |
523 | if (wilc->quit) |
524 | goto out; | |
39823a50 | 525 | |
89125618 AS |
526 | mutex_lock(&wilc->txq_add_to_head_cs); |
527 | wilc_wlan_txq_filter_dup_tcp_ack(dev); | |
528 | tqe = wilc_wlan_txq_get_first(wilc); | |
529 | i = 0; | |
530 | sum = 0; | |
531 | do { | |
532 | if (tqe && (i < (WILC_VMM_TBL_SIZE - 1))) { | |
533 | if (tqe->type == WILC_CFG_PKT) | |
534 | vmm_sz = ETH_CONFIG_PKT_HDR_OFFSET; | |
39823a50 | 535 | |
89125618 AS |
536 | else if (tqe->type == WILC_NET_PKT) |
537 | vmm_sz = ETH_ETHERNET_HDR_OFFSET; | |
133b22d6 | 538 | |
89125618 AS |
539 | else |
540 | vmm_sz = HOST_HDR_OFFSET; | |
ac087c82 | 541 | |
89125618 | 542 | vmm_sz += tqe->buffer_size; |
39823a50 | 543 | |
89125618 AS |
544 | if (vmm_sz & 0x3) |
545 | vmm_sz = (vmm_sz + 4) & ~0x3; | |
c5c77ba1 | 546 | |
89125618 | 547 | if ((sum + vmm_sz) > LINUX_TX_SIZE) |
c5c77ba1 | 548 | break; |
c5c77ba1 | 549 | |
89125618 AS |
550 | vmm_table[i] = vmm_sz / 4; |
551 | if (tqe->type == WILC_CFG_PKT) | |
552 | vmm_table[i] |= BIT(10); | |
02211edc | 553 | cpu_to_le32s(&vmm_table[i]); |
89125618 AS |
554 | |
555 | i++; | |
556 | sum += vmm_sz; | |
557 | tqe = wilc_wlan_txq_get_next(wilc, tqe); | |
558 | } else { | |
c5c77ba1 | 559 | break; |
89125618 AS |
560 | } |
561 | } while (1); | |
a4104796 | 562 | |
89125618 AS |
563 | if (i == 0) |
564 | goto out; | |
565 | vmm_table[i] = 0x0; | |
c5c77ba1 | 566 | |
89125618 AS |
567 | acquire_bus(wilc, ACQUIRE_AND_WAKEUP); |
568 | counter = 0; | |
569 | func = wilc->hif_func; | |
570 | do { | |
571 | ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, ®); | |
572 | if (!ret) | |
573 | break; | |
4ee4b9da | 574 | |
89125618 AS |
575 | if ((reg & 0x1) == 0) |
576 | break; | |
c5c77ba1 | 577 | |
89125618 AS |
578 | counter++; |
579 | if (counter > 200) { | |
580 | counter = 0; | |
581 | ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0); | |
582 | break; | |
583 | } | |
584 | } while (!wilc->quit); | |
c5c77ba1 | 585 | |
89125618 | 586 | if (!ret) |
588bf3c1 | 587 | goto out_release_bus; |
c5c77ba1 | 588 | |
89125618 AS |
589 | timeout = 200; |
590 | do { | |
591 | ret = func->hif_block_tx(wilc, | |
592 | WILC_VMM_TBL_RX_SHADOW_BASE, | |
593 | (u8 *)vmm_table, | |
594 | ((i + 1) * 4)); | |
595 | if (!ret) | |
596 | break; | |
c5c77ba1 | 597 | |
89125618 AS |
598 | ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x2); |
599 | if (!ret) | |
600 | break; | |
c5c77ba1 | 601 | |
89125618 AS |
602 | do { |
603 | ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, ®); | |
39823a50 | 604 | if (!ret) |
c5c77ba1 | 605 | break; |
89125618 AS |
606 | if ((reg >> 2) & 0x1) { |
607 | entries = ((reg >> 3) & 0x3f); | |
c5c77ba1 | 608 | break; |
c5c77ba1 | 609 | } |
89125618 AS |
610 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
611 | } while (--timeout); | |
612 | if (timeout <= 0) { | |
613 | ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0); | |
a5c3f8db | 614 | break; |
89125618 | 615 | } |
c5c77ba1 | 616 | |
39823a50 | 617 | if (!ret) |
89125618 | 618 | break; |
39823a50 | 619 | |
c5c77ba1 | 620 | if (entries == 0) { |
89125618 AS |
621 | ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, ®); |
622 | if (!ret) | |
623 | break; | |
624 | reg &= ~BIT(0); | |
625 | ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg); | |
626 | if (!ret) | |
627 | break; | |
628 | break; | |
c5c77ba1 | 629 | } |
89125618 AS |
630 | break; |
631 | } while (1); | |
c5c77ba1 | 632 | |
89125618 | 633 | if (!ret) |
588bf3c1 | 634 | goto out_release_bus; |
c5c77ba1 | 635 | |
89125618 AS |
636 | if (entries == 0) { |
637 | ret = WILC_TX_ERR_NO_BUF; | |
588bf3c1 | 638 | goto out_release_bus; |
89125618 | 639 | } |
c5c77ba1 | 640 | |
89125618 AS |
641 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
642 | ||
643 | offset = 0; | |
644 | i = 0; | |
645 | do { | |
82059dce AS |
646 | u32 header, buffer_offset; |
647 | char *bssid; | |
648 | ||
89125618 | 649 | tqe = wilc_wlan_txq_remove_from_head(dev); |
82059dce AS |
650 | if (!tqe) |
651 | break; | |
89125618 | 652 | |
82059dce AS |
653 | if (vmm_table[i] == 0) |
654 | break; | |
c5c77ba1 | 655 | |
02211edc | 656 | le32_to_cpus(&vmm_table[i]); |
82059dce AS |
657 | vmm_sz = (vmm_table[i] & 0x3ff); |
658 | vmm_sz *= 4; | |
659 | header = (tqe->type << 31) | | |
660 | (tqe->buffer_size << 15) | | |
661 | vmm_sz; | |
662 | if (tqe->type == WILC_MGMT_PKT) | |
663 | header |= BIT(30); | |
664 | else | |
665 | header &= ~BIT(30); | |
666 | ||
02211edc | 667 | cpu_to_le32s(&header); |
82059dce AS |
668 | memcpy(&txb[offset], &header, 4); |
669 | if (tqe->type == WILC_CFG_PKT) { | |
670 | buffer_offset = ETH_CONFIG_PKT_HDR_OFFSET; | |
671 | } else if (tqe->type == WILC_NET_PKT) { | |
672 | bssid = ((struct tx_complete_data *)(tqe->priv))->bssid; | |
673 | ||
674 | buffer_offset = ETH_ETHERNET_HDR_OFFSET; | |
675 | memcpy(&txb[offset + 8], bssid, 6); | |
89125618 | 676 | } else { |
82059dce | 677 | buffer_offset = HOST_HDR_OFFSET; |
89125618 | 678 | } |
82059dce AS |
679 | |
680 | memcpy(&txb[offset + buffer_offset], | |
681 | tqe->buffer, tqe->buffer_size); | |
682 | offset += vmm_sz; | |
683 | i++; | |
684 | tqe->status = 1; | |
685 | if (tqe->tx_complete_func) | |
686 | tqe->tx_complete_func(tqe->priv, tqe->status); | |
687 | if (tqe->tcp_pending_ack_idx != NOT_TCP_ACK && | |
688 | tqe->tcp_pending_ack_idx < MAX_PENDING_ACKS) | |
689 | pending_acks_info[tqe->tcp_pending_ack_idx].txqe = NULL; | |
690 | kfree(tqe); | |
89125618 AS |
691 | } while (--entries); |
692 | ||
693 | acquire_bus(wilc, ACQUIRE_AND_WAKEUP); | |
c5c77ba1 | 694 | |
89125618 AS |
695 | ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM); |
696 | if (!ret) | |
588bf3c1 | 697 | goto out_release_bus; |
c5c77ba1 | 698 | |
89125618 | 699 | ret = func->hif_block_tx_ext(wilc, 0, txb, offset); |
c5c77ba1 | 700 | |
588bf3c1 | 701 | out_release_bus: |
89125618 | 702 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
c5c77ba1 | 703 | |
89125618 | 704 | out: |
334bed08 | 705 | mutex_unlock(&wilc->txq_add_to_head_cs); |
c5c77ba1 | 706 | |
67e2a07e | 707 | *txq_count = wilc->txq_entries; |
c5c77ba1 JK |
708 | return ret; |
709 | } | |
710 | ||
4c1f0e3a AS |
711 | static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size) |
712 | { | |
713 | int offset = 0; | |
714 | u32 header; | |
715 | u32 pkt_len, pkt_offset, tp_len; | |
716 | int is_cfg_packet; | |
717 | u8 *buff_ptr; | |
718 | ||
719 | do { | |
720 | buff_ptr = buffer + offset; | |
721 | memcpy(&header, buff_ptr, 4); | |
02211edc | 722 | le32_to_cpus(&header); |
4c1f0e3a AS |
723 | |
724 | is_cfg_packet = (header >> 31) & 0x1; | |
725 | pkt_offset = (header >> 22) & 0x1ff; | |
726 | tp_len = (header >> 11) & 0x7ff; | |
727 | pkt_len = header & 0x7ff; | |
728 | ||
729 | if (pkt_len == 0 || tp_len == 0) | |
730 | break; | |
731 | ||
732 | if (pkt_offset & IS_MANAGMEMENT) { | |
733 | pkt_offset &= ~(IS_MANAGMEMENT | | |
734 | IS_MANAGMEMENT_CALLBACK | | |
735 | IS_MGMT_STATUS_SUCCES); | |
736 | buff_ptr += HOST_HDR_OFFSET; | |
737 | wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len); | |
738 | } else { | |
739 | if (!is_cfg_packet) { | |
740 | if (pkt_len > 0) { | |
741 | wilc_frmw_to_linux(wilc, buff_ptr, | |
742 | pkt_len, | |
743 | pkt_offset); | |
744 | } | |
745 | } else { | |
746 | struct wilc_cfg_rsp rsp; | |
747 | ||
748 | buff_ptr += pkt_offset; | |
749 | ||
750 | wilc_wlan_cfg_indicate_rx(wilc, buff_ptr, | |
751 | pkt_len, | |
752 | &rsp); | |
753 | if (rsp.type == WILC_CFG_RSP) { | |
754 | if (wilc->cfg_seq_no == rsp.seq_no) | |
755 | complete(&wilc->cfg_event); | |
756 | } else if (rsp.type == WILC_CFG_RSP_STATUS) { | |
c7ceac21 | 757 | wilc_mac_indicate(wilc); |
4c1f0e3a AS |
758 | } |
759 | } | |
760 | } | |
761 | offset += tp_len; | |
762 | if (offset >= size) | |
763 | break; | |
764 | } while (1); | |
765 | } | |
766 | ||
39ce4d3d | 767 | static void wilc_wlan_handle_rxq(struct wilc *wilc) |
c5c77ba1 | 768 | { |
4c1f0e3a | 769 | int size; |
51e825f7 | 770 | u8 *buffer; |
c5c77ba1 JK |
771 | struct rxq_entry_t *rqe; |
772 | ||
c5c77ba1 | 773 | do { |
67e2a07e | 774 | if (wilc->quit) { |
fa659698 | 775 | complete(&wilc->cfg_event); |
c5c77ba1 JK |
776 | break; |
777 | } | |
db387635 | 778 | rqe = wilc_wlan_rxq_remove(wilc); |
133b22d6 | 779 | if (!rqe) |
c5c77ba1 | 780 | break; |
133b22d6 | 781 | |
c5c77ba1 JK |
782 | buffer = rqe->buffer; |
783 | size = rqe->buffer_size; | |
4c1f0e3a | 784 | wilc_wlan_handle_rx_buff(wilc, buffer, size); |
c5c77ba1 | 785 | |
a18dd630 | 786 | kfree(rqe); |
c5c77ba1 | 787 | } while (1); |
c5c77ba1 JK |
788 | } |
789 | ||
00215dde | 790 | static void wilc_unknown_isr_ext(struct wilc *wilc) |
c5c77ba1 | 791 | { |
af9ae09a | 792 | wilc->hif_func->hif_clear_int_ext(wilc, 0); |
c5c77ba1 | 793 | } |
2d6973e6 | 794 | |
00215dde | 795 | static void wilc_pllupdate_isr_ext(struct wilc *wilc, u32 int_stats) |
c5c77ba1 | 796 | { |
c5c77ba1 JK |
797 | int trials = 10; |
798 | ||
af9ae09a | 799 | wilc->hif_func->hif_clear_int_ext(wilc, PLL_INT_CLR); |
c5c77ba1 | 800 | |
a3629a9e | 801 | if (wilc->io_type == HIF_SDIO) |
e28e84d2 AB |
802 | mdelay(WILC_PLL_TO_SDIO); |
803 | else | |
804 | mdelay(WILC_PLL_TO_SPI); | |
c5c77ba1 | 805 | |
2345ef30 | 806 | while (!(is_wilc1000(wilc_get_chipid(wilc, true)) && --trials)) |
c2e4c0f1 | 807 | mdelay(1); |
c5c77ba1 JK |
808 | } |
809 | ||
00215dde | 810 | static void wilc_sleeptimer_isr_ext(struct wilc *wilc, u32 int_stats1) |
c5c77ba1 | 811 | { |
af9ae09a | 812 | wilc->hif_func->hif_clear_int_ext(wilc, SLEEP_INT_CLR); |
c5c77ba1 JK |
813 | } |
814 | ||
3bcd45b6 | 815 | static void wilc_wlan_handle_isr_ext(struct wilc *wilc, u32 int_status) |
c5c77ba1 | 816 | { |
67e2a07e | 817 | u32 offset = wilc->rx_buffer_offset; |
51e825f7 | 818 | u8 *buffer = NULL; |
fbc2fe16 CL |
819 | u32 size; |
820 | u32 retries = 0; | |
c5c77ba1 JK |
821 | int ret = 0; |
822 | struct rxq_entry_t *rqe; | |
823 | ||
e682e91b | 824 | size = (int_status & 0x7fff) << 2; |
c5c77ba1 JK |
825 | |
826 | while (!size && retries < 10) { | |
af9ae09a | 827 | wilc->hif_func->hif_read_size(wilc, &size); |
e682e91b | 828 | size = (size & 0x7fff) << 2; |
c5c77ba1 | 829 | retries++; |
c5c77ba1 JK |
830 | } |
831 | ||
7878abde AS |
832 | if (size <= 0) |
833 | return; | |
c5c77ba1 | 834 | |
7878abde AS |
835 | if (LINUX_RX_SIZE - offset < size) |
836 | offset = 0; | |
837 | ||
838 | buffer = &wilc->rx_buffer[offset]; | |
839 | ||
840 | wilc->hif_func->hif_clear_int_ext(wilc, DATA_INT_CLR | ENABLE_RX_VMM); | |
841 | ret = wilc->hif_func->hif_block_rx_ext(wilc, 0, buffer, size); | |
842 | if (!ret) | |
843 | return; | |
844 | ||
845 | offset += size; | |
846 | wilc->rx_buffer_offset = offset; | |
847 | rqe = kmalloc(sizeof(*rqe), GFP_KERNEL); | |
848 | if (!rqe) | |
849 | return; | |
850 | ||
851 | rqe->buffer = buffer; | |
852 | rqe->buffer_size = size; | |
853 | wilc_wlan_rxq_add(wilc, rqe); | |
39ce4d3d | 854 | wilc_wlan_handle_rxq(wilc); |
c5c77ba1 JK |
855 | } |
856 | ||
562ed3f1 | 857 | void wilc_handle_isr(struct wilc *wilc) |
c5c77ba1 | 858 | { |
fbc2fe16 | 859 | u32 int_status; |
c5c77ba1 | 860 | |
562ed3f1 | 861 | acquire_bus(wilc, ACQUIRE_AND_WAKEUP); |
af9ae09a | 862 | wilc->hif_func->hif_read_int(wilc, &int_status); |
c5c77ba1 | 863 | |
39823a50 | 864 | if (int_status & PLL_INT_EXT) |
00215dde | 865 | wilc_pllupdate_isr_ext(wilc, int_status); |
39823a50 | 866 | |
f06b9baf | 867 | if (int_status & DATA_INT_EXT) |
3bcd45b6 | 868 | wilc_wlan_handle_isr_ext(wilc, int_status); |
f06b9baf | 869 | |
39823a50 | 870 | if (int_status & SLEEP_INT_EXT) |
00215dde | 871 | wilc_sleeptimer_isr_ext(wilc, int_status); |
c5c77ba1 | 872 | |
f06b9baf | 873 | if (!(int_status & (ALL_INT_EXT))) |
00215dde | 874 | wilc_unknown_isr_ext(wilc); |
f06b9baf | 875 | |
562ed3f1 | 876 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
c5c77ba1 JK |
877 | } |
878 | ||
4d38a56b CP |
879 | int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, |
880 | u32 buffer_size) | |
c5c77ba1 | 881 | { |
fbc2fe16 CL |
882 | u32 offset; |
883 | u32 addr, size, size2, blksz; | |
51e825f7 | 884 | u8 *dma_buffer; |
c5c77ba1 JK |
885 | int ret = 0; |
886 | ||
ffda203c | 887 | blksz = BIT(12); |
c5c77ba1 | 888 | |
f019b9d9 | 889 | dma_buffer = kmalloc(blksz, GFP_KERNEL); |
5c6021da LK |
890 | if (!dma_buffer) |
891 | return -EIO; | |
c5c77ba1 | 892 | |
c5c77ba1 JK |
893 | offset = 0; |
894 | do { | |
895 | memcpy(&addr, &buffer[offset], 4); | |
896 | memcpy(&size, &buffer[offset + 4], 4); | |
02211edc AS |
897 | le32_to_cpus(&addr); |
898 | le32_to_cpus(&size); | |
562ed3f1 | 899 | acquire_bus(wilc, ACQUIRE_ONLY); |
c5c77ba1 JK |
900 | offset += 8; |
901 | while (((int)size) && (offset < buffer_size)) { | |
78174ada | 902 | if (size <= blksz) |
c5c77ba1 | 903 | size2 = size; |
78174ada | 904 | else |
c5c77ba1 | 905 | size2 = blksz; |
ac087c82 | 906 | |
c5c77ba1 | 907 | memcpy(dma_buffer, &buffer[offset], size2); |
4d38a56b CP |
908 | ret = wilc->hif_func->hif_block_tx(wilc, addr, |
909 | dma_buffer, size2); | |
c5c77ba1 JK |
910 | if (!ret) |
911 | break; | |
912 | ||
913 | addr += size2; | |
914 | offset += size2; | |
915 | size -= size2; | |
916 | } | |
562ed3f1 | 917 | release_bus(wilc, RELEASE_ONLY); |
c5c77ba1 JK |
918 | |
919 | if (!ret) { | |
92e7d188 | 920 | ret = -EIO; |
5fe678b1 | 921 | goto fail; |
c5c77ba1 | 922 | } |
c5c77ba1 JK |
923 | } while (offset < buffer_size); |
924 | ||
5fe678b1 | 925 | fail: |
c5c77ba1 | 926 | |
a18dd630 | 927 | kfree(dma_buffer); |
c5c77ba1 | 928 | |
c5c77ba1 JK |
929 | return (ret < 0) ? ret : 0; |
930 | } | |
931 | ||
562ed3f1 | 932 | int wilc_wlan_start(struct wilc *wilc) |
c5c77ba1 | 933 | { |
fbc2fe16 | 934 | u32 reg = 0; |
c5c77ba1 | 935 | int ret; |
fbc2fe16 | 936 | u32 chipid; |
c5c77ba1 | 937 | |
a3629a9e | 938 | if (wilc->io_type == HIF_SDIO) { |
c5c77ba1 | 939 | reg = 0; |
ac087c82 | 940 | reg |= BIT(3); |
a3629a9e | 941 | } else if (wilc->io_type == HIF_SPI) { |
c5c77ba1 JK |
942 | reg = 1; |
943 | } | |
562ed3f1 | 944 | acquire_bus(wilc, ACQUIRE_ONLY); |
af9ae09a | 945 | ret = wilc->hif_func->hif_write_reg(wilc, WILC_VMM_CORE_CFG, reg); |
c5c77ba1 | 946 | if (!ret) { |
562ed3f1 | 947 | release_bus(wilc, RELEASE_ONLY); |
d37843d1 | 948 | return -EIO; |
c5c77ba1 JK |
949 | } |
950 | reg = 0; | |
a3629a9e | 951 | if (wilc->io_type == HIF_SDIO && wilc->dev_irq_num) |
c4d139cb | 952 | reg |= WILC_HAVE_SDIO_IRQ_GPIO; |
c5c77ba1 JK |
953 | |
954 | #ifdef WILC_DISABLE_PMU | |
955 | #else | |
956 | reg |= WILC_HAVE_USE_PMU; | |
957 | #endif | |
958 | ||
959 | #ifdef WILC_SLEEP_CLK_SRC_XO | |
960 | reg |= WILC_HAVE_SLEEP_CLK_SRC_XO; | |
961 | #elif defined WILC_SLEEP_CLK_SRC_RTC | |
962 | reg |= WILC_HAVE_SLEEP_CLK_SRC_RTC; | |
963 | #endif | |
964 | ||
965 | #ifdef WILC_EXT_PA_INV_TX_RX | |
966 | reg |= WILC_HAVE_EXT_PA_INV_TX_RX; | |
967 | #endif | |
fdc2ac1a | 968 | reg |= WILC_HAVE_USE_IRQ_AS_HOST_WAKE; |
c5c77ba1 | 969 | reg |= WILC_HAVE_LEGACY_RF_SETTINGS; |
c5c77ba1 JK |
970 | #ifdef XTAL_24 |
971 | reg |= WILC_HAVE_XTAL_24; | |
972 | #endif | |
c5c77ba1 JK |
973 | #ifdef DISABLE_WILC_UART |
974 | reg |= WILC_HAVE_DISABLE_WILC_UART; | |
975 | #endif | |
976 | ||
af9ae09a | 977 | ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_1, reg); |
c5c77ba1 | 978 | if (!ret) { |
562ed3f1 | 979 | release_bus(wilc, RELEASE_ONLY); |
0ca8df12 | 980 | return -EIO; |
c5c77ba1 | 981 | } |
c5c77ba1 | 982 | |
af9ae09a | 983 | wilc->hif_func->hif_sync_ext(wilc, NUM_INT_EXT); |
c5c77ba1 | 984 | |
af9ae09a | 985 | ret = wilc->hif_func->hif_read_reg(wilc, 0x1000, &chipid); |
c5c77ba1 | 986 | if (!ret) { |
562ed3f1 | 987 | release_bus(wilc, RELEASE_ONLY); |
0ca8df12 | 988 | return -EIO; |
c5c77ba1 JK |
989 | } |
990 | ||
af9ae09a | 991 | wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); |
ffda203c AB |
992 | if ((reg & BIT(10)) == BIT(10)) { |
993 | reg &= ~BIT(10); | |
af9ae09a GL |
994 | wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg); |
995 | wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); | |
c5c77ba1 JK |
996 | } |
997 | ||
ffda203c | 998 | reg |= BIT(10); |
af9ae09a GL |
999 | ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg); |
1000 | wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); | |
562ed3f1 | 1001 | release_bus(wilc, RELEASE_ONLY); |
c5c77ba1 JK |
1002 | |
1003 | return (ret < 0) ? ret : 0; | |
1004 | } | |
1005 | ||
562ed3f1 | 1006 | int wilc_wlan_stop(struct wilc *wilc) |
c5c77ba1 | 1007 | { |
fbc2fe16 | 1008 | u32 reg = 0; |
c5c77ba1 | 1009 | int ret; |
51e825f7 | 1010 | u8 timeout = 10; |
20f4e712 | 1011 | |
562ed3f1 | 1012 | acquire_bus(wilc, ACQUIRE_AND_WAKEUP); |
c5c77ba1 | 1013 | |
af9ae09a | 1014 | ret = wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); |
c5c77ba1 | 1015 | if (!ret) { |
562ed3f1 | 1016 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
c5c77ba1 JK |
1017 | return ret; |
1018 | } | |
1019 | ||
ffda203c | 1020 | reg &= ~BIT(10); |
af9ae09a | 1021 | ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg); |
c5c77ba1 | 1022 | if (!ret) { |
562ed3f1 | 1023 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
c5c77ba1 JK |
1024 | return ret; |
1025 | } | |
1026 | ||
c5c77ba1 | 1027 | do { |
4d38a56b CP |
1028 | ret = wilc->hif_func->hif_read_reg(wilc, |
1029 | WILC_GLB_RESET_0, ®); | |
c5c77ba1 | 1030 | if (!ret) { |
562ed3f1 | 1031 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
c5c77ba1 JK |
1032 | return ret; |
1033 | } | |
ac087c82 | 1034 | |
ffda203c | 1035 | if ((reg & BIT(10))) { |
ffda203c | 1036 | reg &= ~BIT(10); |
4d38a56b CP |
1037 | ret = wilc->hif_func->hif_write_reg(wilc, |
1038 | WILC_GLB_RESET_0, | |
1039 | reg); | |
c5c77ba1 JK |
1040 | timeout--; |
1041 | } else { | |
4d38a56b CP |
1042 | ret = wilc->hif_func->hif_read_reg(wilc, |
1043 | WILC_GLB_RESET_0, | |
1044 | ®); | |
c5c77ba1 | 1045 | if (!ret) { |
562ed3f1 | 1046 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
c5c77ba1 JK |
1047 | return ret; |
1048 | } | |
c5c77ba1 JK |
1049 | break; |
1050 | } | |
1051 | ||
1052 | } while (timeout); | |
ffda203c AB |
1053 | reg = (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(8) | BIT(9) | BIT(26) | |
1054 | BIT(29) | BIT(30) | BIT(31)); | |
65ead4ec | 1055 | |
af9ae09a | 1056 | wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg); |
ffda203c | 1057 | reg = (u32)~BIT(10); |
65ead4ec | 1058 | |
af9ae09a | 1059 | ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg); |
c5c77ba1 | 1060 | |
562ed3f1 | 1061 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
c5c77ba1 JK |
1062 | |
1063 | return ret; | |
1064 | } | |
1065 | ||
2de7cbec | 1066 | void wilc_wlan_cleanup(struct net_device *dev) |
c5c77ba1 | 1067 | { |
c5c77ba1 JK |
1068 | struct txq_entry_t *tqe; |
1069 | struct rxq_entry_t *rqe; | |
fbc2fe16 | 1070 | u32 reg = 0; |
c5c77ba1 | 1071 | int ret; |
6bcba96e AS |
1072 | struct wilc_vif *vif = netdev_priv(dev); |
1073 | struct wilc *wilc = vif->wilc; | |
c5c77ba1 | 1074 | |
67e2a07e | 1075 | wilc->quit = 1; |
c5c77ba1 | 1076 | do { |
718fc2c9 | 1077 | tqe = wilc_wlan_txq_remove_from_head(dev); |
a4b17197 | 1078 | if (!tqe) |
c5c77ba1 JK |
1079 | break; |
1080 | if (tqe->tx_complete_func) | |
1081 | tqe->tx_complete_func(tqe->priv, 0); | |
a18dd630 | 1082 | kfree(tqe); |
c5c77ba1 JK |
1083 | } while (1); |
1084 | ||
1085 | do { | |
db387635 | 1086 | rqe = wilc_wlan_rxq_remove(wilc); |
a4b17197 | 1087 | if (!rqe) |
c5c77ba1 | 1088 | break; |
a18dd630 | 1089 | kfree(rqe); |
c5c77ba1 JK |
1090 | } while (1); |
1091 | ||
67e2a07e GL |
1092 | kfree(wilc->rx_buffer); |
1093 | wilc->rx_buffer = NULL; | |
1094 | kfree(wilc->tx_buffer); | |
608b0515 | 1095 | wilc->tx_buffer = NULL; |
c5c77ba1 | 1096 | |
562ed3f1 | 1097 | acquire_bus(wilc, ACQUIRE_AND_WAKEUP); |
c5c77ba1 | 1098 | |
af9ae09a | 1099 | ret = wilc->hif_func->hif_read_reg(wilc, WILC_GP_REG_0, ®); |
133b22d6 | 1100 | if (!ret) |
562ed3f1 | 1101 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
133b22d6 | 1102 | |
af9ae09a | 1103 | ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_0, |
49dcd0dd | 1104 | (reg | ABORT_INT)); |
133b22d6 | 1105 | if (!ret) |
562ed3f1 | 1106 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
133b22d6 | 1107 | |
562ed3f1 | 1108 | release_bus(wilc, RELEASE_ALLOW_SLEEP); |
af9ae09a | 1109 | wilc->hif_func->hif_deinit(NULL); |
c5c77ba1 JK |
1110 | } |
1111 | ||
79df6a49 GL |
1112 | static int wilc_wlan_cfg_commit(struct wilc_vif *vif, int type, |
1113 | u32 drv_handler) | |
c5c77ba1 | 1114 | { |
79df6a49 | 1115 | struct wilc *wilc = vif->wilc; |
67e2a07e GL |
1116 | struct wilc_cfg_frame *cfg = &wilc->cfg_frame; |
1117 | int total_len = wilc->cfg_frame_offset + 4 + DRIVER_HANDLER_SIZE; | |
1118 | int seq_no = wilc->cfg_seq_no % 256; | |
48641679 | 1119 | int driver_handler = (u32)drv_handler; |
c5c77ba1 | 1120 | |
076ef657 | 1121 | if (type == WILC_CFG_SET) |
c5c77ba1 | 1122 | cfg->wid_header[0] = 'W'; |
076ef657 | 1123 | else |
c5c77ba1 | 1124 | cfg->wid_header[0] = 'Q'; |
ac087c82 | 1125 | cfg->wid_header[1] = seq_no; |
51e825f7 CL |
1126 | cfg->wid_header[2] = (u8)total_len; |
1127 | cfg->wid_header[3] = (u8)(total_len >> 8); | |
1128 | cfg->wid_header[4] = (u8)driver_handler; | |
1129 | cfg->wid_header[5] = (u8)(driver_handler >> 8); | |
1130 | cfg->wid_header[6] = (u8)(driver_handler >> 16); | |
1131 | cfg->wid_header[7] = (u8)(driver_handler >> 24); | |
67e2a07e | 1132 | wilc->cfg_seq_no = seq_no; |
c5c77ba1 | 1133 | |
79df6a49 | 1134 | if (!wilc_wlan_txq_add_cfg_pkt(vif, &cfg->wid_header[0], total_len)) |
c5c77ba1 JK |
1135 | return -1; |
1136 | ||
1137 | return 0; | |
1138 | } | |
1139 | ||
70011f5f | 1140 | int wilc_wlan_cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer, |
89758e13 | 1141 | u32 buffer_size, int commit, u32 drv_handler) |
c5c77ba1 | 1142 | { |
fbc2fe16 | 1143 | u32 offset; |
c5c77ba1 | 1144 | int ret_size; |
79df6a49 | 1145 | struct wilc *wilc = vif->wilc; |
c5c77ba1 | 1146 | |
67e2a07e | 1147 | if (wilc->cfg_frame_in_use) |
c5c77ba1 JK |
1148 | return 0; |
1149 | ||
1150 | if (start) | |
67e2a07e | 1151 | wilc->cfg_frame_offset = 0; |
c5c77ba1 | 1152 | |
67e2a07e GL |
1153 | offset = wilc->cfg_frame_offset; |
1154 | ret_size = wilc_wlan_cfg_set_wid(wilc->cfg_frame.frame, offset, | |
70011f5f | 1155 | wid, buffer, buffer_size); |
c5c77ba1 | 1156 | offset += ret_size; |
67e2a07e | 1157 | wilc->cfg_frame_offset = offset; |
c5c77ba1 | 1158 | |
e03aec25 AS |
1159 | if (!commit) |
1160 | return ret_size; | |
fa659698 | 1161 | |
a120adb8 | 1162 | netdev_dbg(vif->ndev, "%s: seqno[%d]\n", __func__, wilc->cfg_seq_no); |
e03aec25 AS |
1163 | wilc->cfg_frame_in_use = 1; |
1164 | ||
1165 | if (wilc_wlan_cfg_commit(vif, WILC_CFG_SET, drv_handler)) | |
1166 | ret_size = 0; | |
1167 | ||
1168 | if (!wait_for_completion_timeout(&wilc->cfg_event, | |
1169 | msecs_to_jiffies(CFG_PKTS_TIMEOUT))) { | |
a120adb8 | 1170 | netdev_dbg(vif->ndev, "%s: Timed Out\n", __func__); |
e03aec25 | 1171 | ret_size = 0; |
c5c77ba1 JK |
1172 | } |
1173 | ||
e03aec25 AS |
1174 | wilc->cfg_frame_in_use = 0; |
1175 | wilc->cfg_frame_offset = 0; | |
1176 | wilc->cfg_seq_no += 1; | |
1177 | ||
c5c77ba1 JK |
1178 | return ret_size; |
1179 | } | |
2d6973e6 | 1180 | |
7db699db | 1181 | int wilc_wlan_cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit, |
d40c99c7 | 1182 | u32 drv_handler) |
c5c77ba1 | 1183 | { |
fbc2fe16 | 1184 | u32 offset; |
c5c77ba1 | 1185 | int ret_size; |
79df6a49 | 1186 | struct wilc *wilc = vif->wilc; |
c5c77ba1 | 1187 | |
67e2a07e | 1188 | if (wilc->cfg_frame_in_use) |
c5c77ba1 JK |
1189 | return 0; |
1190 | ||
1191 | if (start) | |
67e2a07e | 1192 | wilc->cfg_frame_offset = 0; |
c5c77ba1 | 1193 | |
67e2a07e | 1194 | offset = wilc->cfg_frame_offset; |
7db699db | 1195 | ret_size = wilc_wlan_cfg_get_wid(wilc->cfg_frame.frame, offset, wid); |
c5c77ba1 | 1196 | offset += ret_size; |
67e2a07e | 1197 | wilc->cfg_frame_offset = offset; |
c5c77ba1 | 1198 | |
967c4bc3 AS |
1199 | if (!commit) |
1200 | return ret_size; | |
c5c77ba1 | 1201 | |
967c4bc3 | 1202 | wilc->cfg_frame_in_use = 1; |
c5c77ba1 | 1203 | |
967c4bc3 AS |
1204 | if (wilc_wlan_cfg_commit(vif, WILC_CFG_QUERY, drv_handler)) |
1205 | ret_size = 0; | |
1206 | ||
1207 | if (!wait_for_completion_timeout(&wilc->cfg_event, | |
1208 | msecs_to_jiffies(CFG_PKTS_TIMEOUT))) { | |
a120adb8 | 1209 | netdev_dbg(vif->ndev, "%s: Timed Out\n", __func__); |
967c4bc3 | 1210 | ret_size = 0; |
c5c77ba1 | 1211 | } |
967c4bc3 AS |
1212 | wilc->cfg_frame_in_use = 0; |
1213 | wilc->cfg_frame_offset = 0; | |
1214 | wilc->cfg_seq_no += 1; | |
c5c77ba1 JK |
1215 | |
1216 | return ret_size; | |
1217 | } | |
1218 | ||
c975f9dd | 1219 | int wilc_wlan_cfg_get_val(u16 wid, u8 *buffer, u32 buffer_size) |
c5c77ba1 | 1220 | { |
c975f9dd | 1221 | return wilc_wlan_cfg_get_wid_value(wid, buffer, buffer_size); |
c5c77ba1 JK |
1222 | } |
1223 | ||
6e944459 | 1224 | int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids, |
ef7e012f GL |
1225 | u32 count, u32 drv) |
1226 | { | |
b3306362 | 1227 | int i; |
6e944459 | 1228 | int ret = 0; |
ef7e012f GL |
1229 | |
1230 | if (mode == GET_CFG) { | |
b3306362 CL |
1231 | for (i = 0; i < count; i++) { |
1232 | if (!wilc_wlan_cfg_get(vif, !i, | |
1233 | wids[i].id, | |
1234 | (i == count - 1), | |
ef7e012f GL |
1235 | drv)) { |
1236 | ret = -ETIMEDOUT; | |
1237 | break; | |
1238 | } | |
1239 | } | |
b3306362 CL |
1240 | for (i = 0; i < count; i++) { |
1241 | wids[i].size = wilc_wlan_cfg_get_val(wids[i].id, | |
1242 | wids[i].val, | |
1243 | wids[i].size); | |
ef7e012f GL |
1244 | } |
1245 | } else if (mode == SET_CFG) { | |
b3306362 CL |
1246 | for (i = 0; i < count; i++) { |
1247 | if (!wilc_wlan_cfg_set(vif, !i, | |
1248 | wids[i].id, | |
1249 | wids[i].val, | |
1250 | wids[i].size, | |
1251 | (i == count - 1), | |
ef7e012f GL |
1252 | drv)) { |
1253 | ret = -ETIMEDOUT; | |
1254 | break; | |
1255 | } | |
1256 | } | |
1257 | } | |
1258 | ||
1259 | return ret; | |
1260 | } | |
1261 | ||
1608c403 | 1262 | static u32 init_chip(struct net_device *dev) |
c5c77ba1 | 1263 | { |
fbc2fe16 CL |
1264 | u32 chipid; |
1265 | u32 reg, ret = 0; | |
6bcba96e AS |
1266 | struct wilc_vif *vif = netdev_priv(dev); |
1267 | struct wilc *wilc = vif->wilc; | |
c5c77ba1 | 1268 | |
562ed3f1 | 1269 | acquire_bus(wilc, ACQUIRE_ONLY); |
c5c77ba1 | 1270 | |
00215dde | 1271 | chipid = wilc_get_chipid(wilc, true); |
c5c77ba1 | 1272 | |
c5c77ba1 | 1273 | if ((chipid & 0xfff) != 0xa0) { |
af9ae09a | 1274 | ret = wilc->hif_func->hif_read_reg(wilc, 0x1118, ®); |
c5c77ba1 | 1275 | if (!ret) { |
e974b537 | 1276 | netdev_err(dev, "fail read reg 0x1118\n"); |
c5c77ba1 JK |
1277 | return ret; |
1278 | } | |
ffda203c | 1279 | reg |= BIT(0); |
af9ae09a | 1280 | ret = wilc->hif_func->hif_write_reg(wilc, 0x1118, reg); |
c5c77ba1 | 1281 | if (!ret) { |
e974b537 | 1282 | netdev_err(dev, "fail write reg 0x1118\n"); |
c5c77ba1 JK |
1283 | return ret; |
1284 | } | |
af9ae09a | 1285 | ret = wilc->hif_func->hif_write_reg(wilc, 0xc0000, 0x71); |
c5c77ba1 | 1286 | if (!ret) { |
e974b537 | 1287 | netdev_err(dev, "fail write reg 0xc0000\n"); |
c5c77ba1 JK |
1288 | return ret; |
1289 | } | |
1290 | } | |
1291 | ||
562ed3f1 | 1292 | release_bus(wilc, RELEASE_ONLY); |
c5c77ba1 JK |
1293 | |
1294 | return ret; | |
c5c77ba1 JK |
1295 | } |
1296 | ||
65c3f000 | 1297 | u32 wilc_get_chipid(struct wilc *wilc, bool update) |
c5c77ba1 | 1298 | { |
fbc2fe16 | 1299 | static u32 chipid; |
fbc2fe16 | 1300 | u32 tempchipid = 0; |
65c3f000 | 1301 | u32 rfrevid = 0; |
c5c77ba1 | 1302 | |
65c3f000 | 1303 | if (chipid == 0 || update) { |
af9ae09a GL |
1304 | wilc->hif_func->hif_read_reg(wilc, 0x1000, &tempchipid); |
1305 | wilc->hif_func->hif_read_reg(wilc, 0x13f4, &rfrevid); | |
2345ef30 | 1306 | if (!is_wilc1000(tempchipid)) { |
c5c77ba1 | 1307 | chipid = 0; |
65c3f000 | 1308 | return chipid; |
c5c77ba1 JK |
1309 | } |
1310 | if (tempchipid == 0x1002a0) { | |
65c3f000 | 1311 | if (rfrevid != 0x1) |
c5c77ba1 | 1312 | tempchipid = 0x1002a1; |
c5c77ba1 | 1313 | } else if (tempchipid == 0x1002b0) { |
65c3f000 | 1314 | if (rfrevid == 0x4) |
c5c77ba1 | 1315 | tempchipid = 0x1002b1; |
65c3f000 | 1316 | else if (rfrevid != 0x3) |
c5c77ba1 | 1317 | tempchipid = 0x1002b2; |
c5c77ba1 JK |
1318 | } |
1319 | ||
1320 | chipid = tempchipid; | |
1321 | } | |
c5c77ba1 JK |
1322 | return chipid; |
1323 | } | |
1324 | ||
4bd7baf0 | 1325 | int wilc_wlan_init(struct net_device *dev) |
c5c77ba1 | 1326 | { |
c5c77ba1 | 1327 | int ret = 0; |
a4cac481 | 1328 | struct wilc_vif *vif = netdev_priv(dev); |
9c800322 GL |
1329 | struct wilc *wilc; |
1330 | ||
a4cac481 | 1331 | wilc = vif->wilc; |
c5c77ba1 | 1332 | |
0c94df02 GL |
1333 | wilc->quit = 0; |
1334 | ||
5397cbc2 | 1335 | if (!wilc->hif_func->hif_init(wilc, false)) { |
cdb99231 | 1336 | ret = -EIO; |
5fe678b1 | 1337 | goto fail; |
cdb99231 | 1338 | } |
c5c77ba1 | 1339 | |
bda2b2fb | 1340 | if (!wilc_wlan_cfg_init()) { |
92e7d188 | 1341 | ret = -ENOBUFS; |
5fe678b1 | 1342 | goto fail; |
c5c77ba1 | 1343 | } |
c5c77ba1 | 1344 | |
67e2a07e GL |
1345 | if (!wilc->tx_buffer) |
1346 | wilc->tx_buffer = kmalloc(LINUX_TX_SIZE, GFP_KERNEL); | |
c5c77ba1 | 1347 | |
67e2a07e | 1348 | if (!wilc->tx_buffer) { |
92e7d188 | 1349 | ret = -ENOBUFS; |
5fe678b1 | 1350 | goto fail; |
c5c77ba1 JK |
1351 | } |
1352 | ||
67e2a07e GL |
1353 | if (!wilc->rx_buffer) |
1354 | wilc->rx_buffer = kmalloc(LINUX_RX_SIZE, GFP_KERNEL); | |
133b22d6 | 1355 | |
67e2a07e | 1356 | if (!wilc->rx_buffer) { |
92e7d188 | 1357 | ret = -ENOBUFS; |
5fe678b1 | 1358 | goto fail; |
c5c77ba1 | 1359 | } |
c5c77ba1 | 1360 | |
ae6f772d | 1361 | if (!init_chip(dev)) { |
92e7d188 | 1362 | ret = -EIO; |
5fe678b1 | 1363 | goto fail; |
c5c77ba1 | 1364 | } |
c5c77ba1 | 1365 | |
c5c77ba1 JK |
1366 | return 1; |
1367 | ||
5fe678b1 | 1368 | fail: |
c5c77ba1 | 1369 | |
67e2a07e GL |
1370 | kfree(wilc->rx_buffer); |
1371 | wilc->rx_buffer = NULL; | |
1372 | kfree(wilc->tx_buffer); | |
1373 | wilc->tx_buffer = NULL; | |
c5c77ba1 | 1374 | |
c5c77ba1 | 1375 | return ret; |
c5c77ba1 | 1376 | } |