Commit | Line | Data |
---|---|---|
b2ba99ff LC |
1 | /* |
2 | * This file is part of wl1271 | |
3 | * | |
4 | * Copyright (C) 2008-2010 Nokia Corporation | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * version 2 as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
18 | * 02110-1301 USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #include <linux/module.h> | |
23 | #include <linux/platform_device.h> | |
24 | ||
ffeb501c LC |
25 | #include <linux/err.h> |
26 | ||
dd5512eb LC |
27 | #include <linux/wl12xx.h> |
28 | ||
b2ba99ff | 29 | #include "../wlcore/wlcore.h" |
ffeb501c | 30 | #include "../wlcore/debug.h" |
4ded91ce | 31 | #include "../wlcore/io.h" |
dd5512eb | 32 | #include "../wlcore/acx.h" |
b3b4b4b8 | 33 | #include "../wlcore/tx.h" |
cd70f6a4 | 34 | #include "../wlcore/rx.h" |
b14684a0 | 35 | #include "../wlcore/io.h" |
dd5512eb | 36 | #include "../wlcore/boot.h" |
ffeb501c | 37 | |
00782136 | 38 | #include "reg.h" |
9d68d1ee LC |
39 | #include "cmd.h" |
40 | #include "acx.h" | |
25a43d78 | 41 | |
3edab305 AN |
42 | #define WL12XX_TX_HW_BLOCK_SPARE_DEFAULT 1 |
43 | #define WL12XX_TX_HW_BLOCK_GEM_SPARE 2 | |
b3b4b4b8 | 44 | #define WL12XX_TX_HW_BLOCK_SIZE 252 |
3edab305 | 45 | |
43a8bc5a AN |
46 | static const u8 wl12xx_rate_to_idx_2ghz[] = { |
47 | /* MCS rates are used only with 11n */ | |
48 | 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */ | |
49 | 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */ | |
50 | 6, /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */ | |
51 | 5, /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */ | |
52 | 4, /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */ | |
53 | 3, /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */ | |
54 | 2, /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */ | |
55 | 1, /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */ | |
56 | 0, /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */ | |
57 | ||
58 | 11, /* WL12XX_CONF_HW_RXTX_RATE_54 */ | |
59 | 10, /* WL12XX_CONF_HW_RXTX_RATE_48 */ | |
60 | 9, /* WL12XX_CONF_HW_RXTX_RATE_36 */ | |
61 | 8, /* WL12XX_CONF_HW_RXTX_RATE_24 */ | |
62 | ||
63 | /* TI-specific rate */ | |
64 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22 */ | |
65 | ||
66 | 7, /* WL12XX_CONF_HW_RXTX_RATE_18 */ | |
67 | 6, /* WL12XX_CONF_HW_RXTX_RATE_12 */ | |
68 | 3, /* WL12XX_CONF_HW_RXTX_RATE_11 */ | |
69 | 5, /* WL12XX_CONF_HW_RXTX_RATE_9 */ | |
70 | 4, /* WL12XX_CONF_HW_RXTX_RATE_6 */ | |
71 | 2, /* WL12XX_CONF_HW_RXTX_RATE_5_5 */ | |
72 | 1, /* WL12XX_CONF_HW_RXTX_RATE_2 */ | |
73 | 0 /* WL12XX_CONF_HW_RXTX_RATE_1 */ | |
74 | }; | |
75 | ||
76 | static const u8 wl12xx_rate_to_idx_5ghz[] = { | |
77 | /* MCS rates are used only with 11n */ | |
78 | 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */ | |
79 | 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */ | |
80 | 6, /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */ | |
81 | 5, /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */ | |
82 | 4, /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */ | |
83 | 3, /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */ | |
84 | 2, /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */ | |
85 | 1, /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */ | |
86 | 0, /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */ | |
87 | ||
88 | 7, /* WL12XX_CONF_HW_RXTX_RATE_54 */ | |
89 | 6, /* WL12XX_CONF_HW_RXTX_RATE_48 */ | |
90 | 5, /* WL12XX_CONF_HW_RXTX_RATE_36 */ | |
91 | 4, /* WL12XX_CONF_HW_RXTX_RATE_24 */ | |
92 | ||
93 | /* TI-specific rate */ | |
94 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22 */ | |
95 | ||
96 | 3, /* WL12XX_CONF_HW_RXTX_RATE_18 */ | |
97 | 2, /* WL12XX_CONF_HW_RXTX_RATE_12 */ | |
98 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_11 */ | |
99 | 1, /* WL12XX_CONF_HW_RXTX_RATE_9 */ | |
100 | 0, /* WL12XX_CONF_HW_RXTX_RATE_6 */ | |
101 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_5_5 */ | |
102 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_2 */ | |
103 | CONF_HW_RXTX_RATE_UNSUPPORTED /* WL12XX_CONF_HW_RXTX_RATE_1 */ | |
104 | }; | |
105 | ||
106 | static const u8 *wl12xx_band_rate_to_idx[] = { | |
107 | [IEEE80211_BAND_2GHZ] = wl12xx_rate_to_idx_2ghz, | |
108 | [IEEE80211_BAND_5GHZ] = wl12xx_rate_to_idx_5ghz | |
109 | }; | |
110 | ||
111 | enum wl12xx_hw_rates { | |
112 | WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI = 0, | |
113 | WL12XX_CONF_HW_RXTX_RATE_MCS7, | |
114 | WL12XX_CONF_HW_RXTX_RATE_MCS6, | |
115 | WL12XX_CONF_HW_RXTX_RATE_MCS5, | |
116 | WL12XX_CONF_HW_RXTX_RATE_MCS4, | |
117 | WL12XX_CONF_HW_RXTX_RATE_MCS3, | |
118 | WL12XX_CONF_HW_RXTX_RATE_MCS2, | |
119 | WL12XX_CONF_HW_RXTX_RATE_MCS1, | |
120 | WL12XX_CONF_HW_RXTX_RATE_MCS0, | |
121 | WL12XX_CONF_HW_RXTX_RATE_54, | |
122 | WL12XX_CONF_HW_RXTX_RATE_48, | |
123 | WL12XX_CONF_HW_RXTX_RATE_36, | |
124 | WL12XX_CONF_HW_RXTX_RATE_24, | |
125 | WL12XX_CONF_HW_RXTX_RATE_22, | |
126 | WL12XX_CONF_HW_RXTX_RATE_18, | |
127 | WL12XX_CONF_HW_RXTX_RATE_12, | |
128 | WL12XX_CONF_HW_RXTX_RATE_11, | |
129 | WL12XX_CONF_HW_RXTX_RATE_9, | |
130 | WL12XX_CONF_HW_RXTX_RATE_6, | |
131 | WL12XX_CONF_HW_RXTX_RATE_5_5, | |
132 | WL12XX_CONF_HW_RXTX_RATE_2, | |
133 | WL12XX_CONF_HW_RXTX_RATE_1, | |
134 | WL12XX_CONF_HW_RXTX_RATE_MAX, | |
135 | }; | |
3edab305 | 136 | |
25a43d78 LC |
137 | static struct wlcore_partition_set wl12xx_ptable[PART_TABLE_LEN] = { |
138 | [PART_DOWN] = { | |
139 | .mem = { | |
140 | .start = 0x00000000, | |
141 | .size = 0x000177c0 | |
142 | }, | |
143 | .reg = { | |
144 | .start = REGISTERS_BASE, | |
145 | .size = 0x00008800 | |
146 | }, | |
147 | .mem2 = { | |
148 | .start = 0x00000000, | |
149 | .size = 0x00000000 | |
150 | }, | |
151 | .mem3 = { | |
152 | .start = 0x00000000, | |
153 | .size = 0x00000000 | |
154 | }, | |
155 | }, | |
156 | ||
00782136 LC |
157 | [PART_BOOT] = { /* in wl12xx we can use a mix of work and down |
158 | * partition here */ | |
159 | .mem = { | |
160 | .start = 0x00040000, | |
161 | .size = 0x00014fc0 | |
162 | }, | |
163 | .reg = { | |
164 | .start = REGISTERS_BASE, | |
165 | .size = 0x00008800 | |
166 | }, | |
167 | .mem2 = { | |
168 | .start = 0x00000000, | |
169 | .size = 0x00000000 | |
170 | }, | |
171 | .mem3 = { | |
172 | .start = 0x00000000, | |
173 | .size = 0x00000000 | |
174 | }, | |
175 | }, | |
176 | ||
25a43d78 LC |
177 | [PART_WORK] = { |
178 | .mem = { | |
179 | .start = 0x00040000, | |
180 | .size = 0x00014fc0 | |
181 | }, | |
182 | .reg = { | |
183 | .start = REGISTERS_BASE, | |
184 | .size = 0x0000a000 | |
185 | }, | |
186 | .mem2 = { | |
187 | .start = 0x003004f8, | |
188 | .size = 0x00000004 | |
189 | }, | |
190 | .mem3 = { | |
191 | .start = 0x00040404, | |
192 | .size = 0x00000000 | |
193 | }, | |
194 | }, | |
195 | ||
196 | [PART_DRPW] = { | |
197 | .mem = { | |
198 | .start = 0x00040000, | |
199 | .size = 0x00014fc0 | |
200 | }, | |
201 | .reg = { | |
202 | .start = DRPW_BASE, | |
203 | .size = 0x00006000 | |
204 | }, | |
205 | .mem2 = { | |
206 | .start = 0x00000000, | |
207 | .size = 0x00000000 | |
208 | }, | |
209 | .mem3 = { | |
210 | .start = 0x00000000, | |
211 | .size = 0x00000000 | |
212 | } | |
213 | } | |
214 | }; | |
215 | ||
00782136 LC |
216 | static const int wl12xx_rtable[REG_TABLE_LEN] = { |
217 | [REG_ECPU_CONTROL] = WL12XX_REG_ECPU_CONTROL, | |
218 | [REG_INTERRUPT_NO_CLEAR] = WL12XX_REG_INTERRUPT_NO_CLEAR, | |
219 | [REG_INTERRUPT_ACK] = WL12XX_REG_INTERRUPT_ACK, | |
220 | [REG_COMMAND_MAILBOX_PTR] = WL12XX_REG_COMMAND_MAILBOX_PTR, | |
221 | [REG_EVENT_MAILBOX_PTR] = WL12XX_REG_EVENT_MAILBOX_PTR, | |
222 | [REG_INTERRUPT_TRIG] = WL12XX_REG_INTERRUPT_TRIG, | |
223 | [REG_INTERRUPT_MASK] = WL12XX_REG_INTERRUPT_MASK, | |
224 | [REG_PC_ON_RECOVERY] = WL12XX_SCR_PAD4, | |
225 | [REG_CHIP_ID_B] = WL12XX_CHIP_ID_B, | |
226 | [REG_CMD_MBOX_ADDRESS] = WL12XX_CMD_MBOX_ADDRESS, | |
227 | ||
228 | /* data access memory addresses, used with partition translation */ | |
229 | [REG_SLV_MEM_DATA] = WL1271_SLV_MEM_DATA, | |
230 | [REG_SLV_REG_DATA] = WL1271_SLV_REG_DATA, | |
231 | ||
232 | /* raw data access memory addresses */ | |
233 | [REG_RAW_FW_STATUS_ADDR] = FW_STATUS_ADDR, | |
234 | }; | |
235 | ||
6f7dd16c LC |
236 | /* TODO: maybe move to a new header file? */ |
237 | #define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin" | |
238 | #define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin" | |
239 | #define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin" | |
240 | ||
241 | #define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin" | |
242 | #define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin" | |
243 | #define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" | |
244 | ||
b14684a0 LC |
245 | static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) |
246 | { | |
247 | if (wl->chip.id != CHIP_ID_1283_PG20) { | |
248 | struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; | |
249 | struct wl1271_rx_mem_pool_addr rx_mem_addr; | |
250 | ||
251 | /* | |
252 | * Choose the block we want to read | |
253 | * For aggregated packets, only the first memory block | |
254 | * should be retrieved. The FW takes care of the rest. | |
255 | */ | |
256 | u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK; | |
257 | ||
258 | rx_mem_addr.addr = (mem_block << 8) + | |
259 | le32_to_cpu(wl_mem_map->packet_memory_pool_start); | |
260 | ||
261 | rx_mem_addr.addr_extra = rx_mem_addr.addr + 4; | |
262 | ||
263 | wl1271_write(wl, WL1271_SLV_REG_DATA, | |
264 | &rx_mem_addr, sizeof(rx_mem_addr), false); | |
265 | } | |
266 | } | |
267 | ||
6f7dd16c LC |
268 | static int wl12xx_identify_chip(struct wl1271 *wl) |
269 | { | |
270 | int ret = 0; | |
271 | ||
272 | switch (wl->chip.id) { | |
273 | case CHIP_ID_1271_PG10: | |
274 | wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", | |
275 | wl->chip.id); | |
276 | ||
f83985bb AN |
277 | /* clear the alignment quirk, since we don't support it */ |
278 | wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; | |
279 | ||
280 | wl->quirks |= WLCORE_QUIRK_LEGACY_NVS; | |
6f7dd16c LC |
281 | wl->sr_fw_name = WL127X_FW_NAME_SINGLE; |
282 | wl->mr_fw_name = WL127X_FW_NAME_MULTI; | |
b14684a0 LC |
283 | |
284 | /* read data preparation is only needed by wl127x */ | |
285 | wl->ops->prepare_read = wl127x_prepare_read; | |
286 | ||
6f7dd16c LC |
287 | break; |
288 | ||
289 | case CHIP_ID_1271_PG20: | |
290 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", | |
291 | wl->chip.id); | |
292 | ||
f83985bb AN |
293 | /* clear the alignment quirk, since we don't support it */ |
294 | wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; | |
295 | ||
296 | wl->quirks |= WLCORE_QUIRK_LEGACY_NVS; | |
6f7dd16c LC |
297 | wl->plt_fw_name = WL127X_PLT_FW_NAME; |
298 | wl->sr_fw_name = WL127X_FW_NAME_SINGLE; | |
299 | wl->mr_fw_name = WL127X_FW_NAME_MULTI; | |
b14684a0 LC |
300 | |
301 | /* read data preparation is only needed by wl127x */ | |
302 | wl->ops->prepare_read = wl127x_prepare_read; | |
303 | ||
6f7dd16c LC |
304 | break; |
305 | ||
306 | case CHIP_ID_1283_PG20: | |
307 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)", | |
308 | wl->chip.id); | |
309 | wl->plt_fw_name = WL128X_PLT_FW_NAME; | |
310 | wl->sr_fw_name = WL128X_FW_NAME_SINGLE; | |
311 | wl->mr_fw_name = WL128X_FW_NAME_MULTI; | |
312 | break; | |
313 | case CHIP_ID_1283_PG10: | |
314 | default: | |
315 | wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); | |
316 | ret = -ENODEV; | |
317 | goto out; | |
318 | } | |
319 | ||
320 | out: | |
321 | return ret; | |
322 | } | |
323 | ||
dd5512eb LC |
324 | static void wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) |
325 | { | |
326 | /* write address >> 1 + 0x30000 to OCP_POR_CTR */ | |
327 | addr = (addr >> 1) + 0x30000; | |
328 | wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr); | |
329 | ||
330 | /* write value to OCP_POR_WDATA */ | |
331 | wl1271_write32(wl, WL12XX_OCP_DATA_WRITE, val); | |
332 | ||
333 | /* write 1 to OCP_CMD */ | |
334 | wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE); | |
335 | } | |
336 | ||
337 | static u16 wl12xx_top_reg_read(struct wl1271 *wl, int addr) | |
338 | { | |
339 | u32 val; | |
340 | int timeout = OCP_CMD_LOOP; | |
341 | ||
342 | /* write address >> 1 + 0x30000 to OCP_POR_CTR */ | |
343 | addr = (addr >> 1) + 0x30000; | |
344 | wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr); | |
345 | ||
346 | /* write 2 to OCP_CMD */ | |
347 | wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ); | |
348 | ||
349 | /* poll for data ready */ | |
350 | do { | |
351 | val = wl1271_read32(wl, WL12XX_OCP_DATA_READ); | |
352 | } while (!(val & OCP_READY_MASK) && --timeout); | |
353 | ||
354 | if (!timeout) { | |
355 | wl1271_warning("Top register access timed out."); | |
356 | return 0xffff; | |
357 | } | |
358 | ||
359 | /* check data status and return if OK */ | |
360 | if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) | |
361 | return val & 0xffff; | |
362 | else { | |
363 | wl1271_warning("Top register access returned error."); | |
364 | return 0xffff; | |
365 | } | |
366 | } | |
367 | ||
368 | static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) | |
369 | { | |
370 | u16 spare_reg; | |
371 | ||
372 | /* Mask bits [2] & [8:4] in the sys_clk_cfg register */ | |
373 | spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG); | |
374 | if (spare_reg == 0xFFFF) | |
375 | return -EFAULT; | |
376 | spare_reg |= (BIT(3) | BIT(5) | BIT(6)); | |
377 | wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); | |
378 | ||
379 | /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */ | |
380 | wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG, | |
381 | WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); | |
382 | ||
383 | /* Delay execution for 15msec, to let the HW settle */ | |
384 | mdelay(15); | |
385 | ||
386 | return 0; | |
387 | } | |
388 | ||
389 | static bool wl128x_is_tcxo_valid(struct wl1271 *wl) | |
390 | { | |
391 | u16 tcxo_detection; | |
392 | ||
393 | tcxo_detection = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG); | |
394 | if (tcxo_detection & TCXO_DET_FAILED) | |
395 | return false; | |
396 | ||
397 | return true; | |
398 | } | |
399 | ||
400 | static bool wl128x_is_fref_valid(struct wl1271 *wl) | |
401 | { | |
402 | u16 fref_detection; | |
403 | ||
404 | fref_detection = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG); | |
405 | if (fref_detection & FREF_CLK_DETECT_FAIL) | |
406 | return false; | |
407 | ||
408 | return true; | |
409 | } | |
410 | ||
411 | static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl) | |
412 | { | |
413 | wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); | |
414 | wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); | |
415 | wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL); | |
416 | ||
417 | return 0; | |
418 | } | |
419 | ||
420 | static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) | |
421 | { | |
422 | u16 spare_reg; | |
423 | u16 pll_config; | |
424 | u8 input_freq; | |
425 | ||
426 | /* Mask bits [3:1] in the sys_clk_cfg register */ | |
427 | spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG); | |
428 | if (spare_reg == 0xFFFF) | |
429 | return -EFAULT; | |
430 | spare_reg |= BIT(2); | |
431 | wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); | |
432 | ||
433 | /* Handle special cases of the TCXO clock */ | |
434 | if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || | |
435 | wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6) | |
436 | return wl128x_manually_configure_mcs_pll(wl); | |
437 | ||
438 | /* Set the input frequency according to the selected clock source */ | |
439 | input_freq = (clk & 1) + 1; | |
440 | ||
441 | pll_config = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG); | |
442 | if (pll_config == 0xFFFF) | |
443 | return -EFAULT; | |
444 | pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT); | |
445 | pll_config |= MCS_PLL_ENABLE_HP; | |
446 | wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config); | |
447 | ||
448 | return 0; | |
449 | } | |
450 | ||
451 | /* | |
452 | * WL128x has two clocks input - TCXO and FREF. | |
453 | * TCXO is the main clock of the device, while FREF is used to sync | |
454 | * between the GPS and the cellular modem. | |
455 | * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used | |
456 | * as the WLAN/BT main clock. | |
457 | */ | |
458 | static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) | |
459 | { | |
460 | u16 sys_clk_cfg; | |
461 | ||
462 | /* For XTAL-only modes, FREF will be used after switching from TCXO */ | |
463 | if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL || | |
464 | wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) { | |
465 | if (!wl128x_switch_tcxo_to_fref(wl)) | |
466 | return -EINVAL; | |
467 | goto fref_clk; | |
468 | } | |
469 | ||
470 | /* Query the HW, to determine which clock source we should use */ | |
471 | sys_clk_cfg = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG); | |
472 | if (sys_clk_cfg == 0xFFFF) | |
473 | return -EINVAL; | |
474 | if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF) | |
475 | goto fref_clk; | |
476 | ||
477 | /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */ | |
478 | if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 || | |
479 | wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) { | |
480 | if (!wl128x_switch_tcxo_to_fref(wl)) | |
481 | return -EINVAL; | |
482 | goto fref_clk; | |
483 | } | |
484 | ||
485 | /* TCXO clock is selected */ | |
486 | if (!wl128x_is_tcxo_valid(wl)) | |
487 | return -EINVAL; | |
488 | *selected_clock = wl->tcxo_clock; | |
489 | goto config_mcs_pll; | |
490 | ||
491 | fref_clk: | |
492 | /* FREF clock is selected */ | |
493 | if (!wl128x_is_fref_valid(wl)) | |
494 | return -EINVAL; | |
495 | *selected_clock = wl->ref_clock; | |
496 | ||
497 | config_mcs_pll: | |
498 | return wl128x_configure_mcs_pll(wl, *selected_clock); | |
499 | } | |
500 | ||
501 | static int wl127x_boot_clk(struct wl1271 *wl) | |
502 | { | |
503 | u32 pause; | |
504 | u32 clk; | |
505 | ||
506 | if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3) | |
507 | wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION; | |
508 | ||
509 | if (wl->ref_clock == CONF_REF_CLK_19_2_E || | |
510 | wl->ref_clock == CONF_REF_CLK_38_4_E || | |
511 | wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) | |
512 | /* ref clk: 19.2/38.4/38.4-XTAL */ | |
513 | clk = 0x3; | |
514 | else if (wl->ref_clock == CONF_REF_CLK_26_E || | |
515 | wl->ref_clock == CONF_REF_CLK_52_E) | |
516 | /* ref clk: 26/52 */ | |
517 | clk = 0x5; | |
518 | else | |
519 | return -EINVAL; | |
520 | ||
521 | if (wl->ref_clock != CONF_REF_CLK_19_2_E) { | |
522 | u16 val; | |
523 | /* Set clock type (open drain) */ | |
524 | val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE); | |
525 | val &= FREF_CLK_TYPE_BITS; | |
526 | wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val); | |
527 | ||
528 | /* Set clock pull mode (no pull) */ | |
529 | val = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL); | |
530 | val |= NO_PULL; | |
531 | wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val); | |
532 | } else { | |
533 | u16 val; | |
534 | /* Set clock polarity */ | |
535 | val = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY); | |
536 | val &= FREF_CLK_POLARITY_BITS; | |
537 | val |= CLK_REQ_OUTN_SEL; | |
538 | wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); | |
539 | } | |
540 | ||
541 | wl1271_write32(wl, WL12XX_PLL_PARAMETERS, clk); | |
542 | ||
543 | pause = wl1271_read32(wl, WL12XX_PLL_PARAMETERS); | |
544 | ||
545 | wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); | |
546 | ||
547 | pause &= ~(WU_COUNTER_PAUSE_VAL); | |
548 | pause |= WU_COUNTER_PAUSE_VAL; | |
549 | wl1271_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause); | |
550 | ||
551 | return 0; | |
552 | } | |
553 | ||
554 | static int wl1271_boot_soft_reset(struct wl1271 *wl) | |
555 | { | |
556 | unsigned long timeout; | |
557 | u32 boot_data; | |
558 | ||
559 | /* perform soft reset */ | |
560 | wl1271_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); | |
561 | ||
562 | /* SOFT_RESET is self clearing */ | |
563 | timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); | |
564 | while (1) { | |
565 | boot_data = wl1271_read32(wl, WL12XX_SLV_SOFT_RESET); | |
566 | wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); | |
567 | if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) | |
568 | break; | |
569 | ||
570 | if (time_after(jiffies, timeout)) { | |
571 | /* 1.2 check pWhalBus->uSelfClearTime if the | |
572 | * timeout was reached */ | |
573 | wl1271_error("soft reset timeout"); | |
574 | return -1; | |
575 | } | |
576 | ||
577 | udelay(SOFT_RESET_STALL_TIME); | |
578 | } | |
579 | ||
580 | /* disable Rx/Tx */ | |
581 | wl1271_write32(wl, WL12XX_ENABLE, 0x0); | |
582 | ||
583 | /* disable auto calibration on start*/ | |
584 | wl1271_write32(wl, WL12XX_SPARE_A2, 0xffff); | |
585 | ||
586 | return 0; | |
587 | } | |
588 | ||
589 | static int wl12xx_pre_boot(struct wl1271 *wl) | |
590 | { | |
591 | int ret = 0; | |
592 | u32 clk; | |
593 | int selected_clock = -1; | |
594 | ||
595 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
596 | ret = wl128x_boot_clk(wl, &selected_clock); | |
597 | if (ret < 0) | |
598 | goto out; | |
599 | } else { | |
600 | ret = wl127x_boot_clk(wl); | |
601 | if (ret < 0) | |
602 | goto out; | |
603 | } | |
604 | ||
605 | /* Continue the ELP wake up sequence */ | |
606 | wl1271_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); | |
607 | udelay(500); | |
608 | ||
609 | wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); | |
610 | ||
611 | /* Read-modify-write DRPW_SCRATCH_START register (see next state) | |
612 | to be used by DRPw FW. The RTRIM value will be added by the FW | |
613 | before taking DRPw out of reset */ | |
614 | ||
615 | clk = wl1271_read32(wl, WL12XX_DRPW_SCRATCH_START); | |
616 | ||
617 | wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); | |
618 | ||
619 | if (wl->chip.id == CHIP_ID_1283_PG20) | |
620 | clk |= ((selected_clock & 0x3) << 1) << 4; | |
621 | else | |
622 | clk |= (wl->ref_clock << 1) << 4; | |
623 | ||
624 | wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk); | |
625 | ||
626 | wlcore_set_partition(wl, &wl->ptable[PART_WORK]); | |
627 | ||
628 | /* Disable interrupts */ | |
629 | wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); | |
630 | ||
631 | ret = wl1271_boot_soft_reset(wl); | |
632 | if (ret < 0) | |
633 | goto out; | |
634 | ||
635 | out: | |
636 | return ret; | |
637 | } | |
638 | ||
639 | static void wl12xx_pre_upload(struct wl1271 *wl) | |
640 | { | |
641 | u32 tmp; | |
642 | ||
643 | /* write firmware's last address (ie. it's length) to | |
644 | * ACX_EEPROMLESS_IND_REG */ | |
645 | wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); | |
646 | ||
647 | wl1271_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND); | |
648 | ||
649 | tmp = wlcore_read_reg(wl, REG_CHIP_ID_B); | |
650 | ||
651 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); | |
652 | ||
653 | /* 6. read the EEPROM parameters */ | |
654 | tmp = wl1271_read32(wl, WL12XX_SCR_PAD2); | |
655 | ||
656 | /* WL1271: The reference driver skips steps 7 to 10 (jumps directly | |
657 | * to upload_fw) */ | |
658 | ||
659 | if (wl->chip.id == CHIP_ID_1283_PG20) | |
660 | wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); | |
661 | } | |
662 | ||
663 | static void wl12xx_enable_interrupts(struct wl1271 *wl) | |
664 | { | |
665 | u32 polarity; | |
666 | ||
667 | polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY); | |
668 | ||
669 | /* We use HIGH polarity, so unset the LOW bit */ | |
670 | polarity &= ~POLARITY_LOW; | |
671 | wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity); | |
672 | ||
673 | wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); | |
674 | ||
675 | wlcore_enable_interrupts(wl); | |
676 | wlcore_write_reg(wl, REG_INTERRUPT_MASK, | |
677 | WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); | |
678 | ||
679 | wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL); | |
680 | } | |
681 | ||
682 | static int wl12xx_boot(struct wl1271 *wl) | |
683 | { | |
684 | int ret; | |
685 | ||
686 | ret = wl12xx_pre_boot(wl); | |
687 | if (ret < 0) | |
688 | goto out; | |
689 | ||
690 | ret = wlcore_boot_upload_nvs(wl); | |
691 | if (ret < 0) | |
692 | goto out; | |
693 | ||
694 | wl12xx_pre_upload(wl); | |
695 | ||
696 | ret = wlcore_boot_upload_firmware(wl); | |
697 | if (ret < 0) | |
698 | goto out; | |
699 | ||
700 | ret = wlcore_boot_run_firmware(wl); | |
701 | if (ret < 0) | |
702 | goto out; | |
703 | ||
704 | wl12xx_enable_interrupts(wl); | |
705 | ||
706 | out: | |
707 | return ret; | |
708 | } | |
709 | ||
f16ff758 LC |
710 | static void wl12xx_trigger_cmd(struct wl1271 *wl) |
711 | { | |
712 | wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD); | |
713 | } | |
714 | ||
715 | static void wl12xx_ack_event(struct wl1271 *wl) | |
716 | { | |
717 | wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_EVENT_ACK); | |
718 | } | |
719 | ||
b3b4b4b8 AN |
720 | static u32 wl12xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) |
721 | { | |
722 | u32 blk_size = WL12XX_TX_HW_BLOCK_SIZE; | |
723 | u32 align_len = wlcore_calc_packet_alignment(wl, len); | |
724 | ||
725 | return (align_len + blk_size - 1) / blk_size + spare_blks; | |
726 | } | |
727 | ||
4a3b97ee AN |
728 | static void |
729 | wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, | |
730 | u32 blks, u32 spare_blks) | |
731 | { | |
732 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
733 | desc->wl128x_mem.total_mem_blocks = blks; | |
734 | } else { | |
735 | desc->wl127x_mem.extra_blocks = spare_blks; | |
736 | desc->wl127x_mem.total_mem_blocks = blks; | |
737 | } | |
738 | } | |
739 | ||
6f266e91 AN |
740 | static void |
741 | wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, | |
742 | struct sk_buff *skb) | |
743 | { | |
744 | u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len); | |
745 | ||
746 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
747 | desc->wl128x_mem.extra_bytes = aligned_len - skb->len; | |
748 | desc->length = cpu_to_le16(aligned_len >> 2); | |
749 | ||
750 | wl1271_debug(DEBUG_TX, | |
751 | "tx_fill_hdr: hlid: %d len: %d life: %d mem: %d extra: %d", | |
752 | desc->hlid, | |
753 | le16_to_cpu(desc->length), | |
754 | le16_to_cpu(desc->life_time), | |
755 | desc->wl128x_mem.total_mem_blocks, | |
756 | desc->wl128x_mem.extra_bytes); | |
757 | } else { | |
758 | /* calculate number of padding bytes */ | |
759 | int pad = aligned_len - skb->len; | |
760 | desc->tx_attr |= | |
761 | cpu_to_le16(pad << TX_HW_ATTR_OFST_LAST_WORD_PAD); | |
762 | ||
763 | /* Store the aligned length in terms of words */ | |
764 | desc->length = cpu_to_le16(aligned_len >> 2); | |
765 | ||
766 | wl1271_debug(DEBUG_TX, | |
767 | "tx_fill_hdr: pad: %d hlid: %d len: %d life: %d mem: %d", | |
768 | pad, desc->hlid, | |
769 | le16_to_cpu(desc->length), | |
770 | le16_to_cpu(desc->life_time), | |
771 | desc->wl127x_mem.total_mem_blocks); | |
772 | } | |
773 | } | |
774 | ||
cd70f6a4 AN |
775 | static enum wl_rx_buf_align |
776 | wl12xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc) | |
777 | { | |
778 | if (rx_desc & RX_BUF_UNALIGNED_PAYLOAD) | |
779 | return WLCORE_RX_BUF_UNALIGNED; | |
780 | ||
781 | return WLCORE_RX_BUF_ALIGNED; | |
782 | } | |
783 | ||
4158149c AN |
784 | static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, |
785 | u32 data_len) | |
786 | { | |
787 | struct wl1271_rx_descriptor *desc = rx_data; | |
788 | ||
789 | /* invalid packet */ | |
790 | if (data_len < sizeof(*desc) || | |
791 | data_len < sizeof(*desc) + desc->pad_len) | |
792 | return 0; | |
793 | ||
794 | return data_len - sizeof(*desc) - desc->pad_len; | |
795 | } | |
796 | ||
53d67a50 AN |
797 | static void wl12xx_tx_delayed_compl(struct wl1271 *wl) |
798 | { | |
799 | if (wl->fw_status->tx_results_counter == (wl->tx_results_count & 0xff)) | |
800 | return; | |
801 | ||
802 | wl1271_tx_complete(wl); | |
803 | } | |
804 | ||
9d68d1ee LC |
805 | static int wl12xx_hw_init(struct wl1271 *wl) |
806 | { | |
807 | int ret; | |
808 | ||
809 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
810 | u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; | |
811 | ||
812 | ret = wl128x_cmd_general_parms(wl); | |
813 | if (ret < 0) | |
814 | goto out; | |
815 | ret = wl128x_cmd_radio_parms(wl); | |
816 | if (ret < 0) | |
817 | goto out; | |
818 | ||
819 | if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) | |
820 | /* Enable SDIO padding */ | |
821 | host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; | |
822 | ||
823 | /* Must be before wl1271_acx_init_mem_config() */ | |
824 | ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap); | |
825 | if (ret < 0) | |
826 | goto out; | |
827 | } else { | |
828 | ret = wl1271_cmd_general_parms(wl); | |
829 | if (ret < 0) | |
830 | goto out; | |
831 | ret = wl1271_cmd_radio_parms(wl); | |
832 | if (ret < 0) | |
833 | goto out; | |
834 | ret = wl1271_cmd_ext_radio_parms(wl); | |
835 | if (ret < 0) | |
836 | goto out; | |
837 | } | |
838 | out: | |
839 | return ret; | |
840 | } | |
841 | ||
30d9b4a5 LC |
842 | static bool wl12xx_mac_in_fuse(struct wl1271 *wl) |
843 | { | |
844 | bool supported = false; | |
845 | u8 major, minor; | |
846 | ||
847 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
848 | major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver); | |
849 | minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver); | |
850 | ||
851 | /* in wl128x we have the MAC address if the PG is >= (2, 1) */ | |
852 | if (major > 2 || (major == 2 && minor >= 1)) | |
853 | supported = true; | |
854 | } else { | |
855 | major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver); | |
856 | minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver); | |
857 | ||
858 | /* in wl127x we have the MAC address if the PG is >= (3, 1) */ | |
859 | if (major == 3 && minor >= 1) | |
860 | supported = true; | |
861 | } | |
862 | ||
863 | wl1271_debug(DEBUG_PROBE, | |
864 | "PG Ver major = %d minor = %d, MAC %s present", | |
865 | major, minor, supported ? "is" : "is not"); | |
866 | ||
867 | return supported; | |
868 | } | |
869 | ||
870 | static void wl12xx_get_fuse_mac(struct wl1271 *wl) | |
871 | { | |
872 | u32 mac1, mac2; | |
873 | ||
874 | wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); | |
875 | ||
876 | mac1 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1); | |
877 | mac2 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2); | |
878 | ||
879 | /* these are the two parts of the BD_ADDR */ | |
880 | wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) + | |
881 | ((mac1 & 0xff000000) >> 24); | |
882 | wl->fuse_nic_addr = mac1 & 0xffffff; | |
883 | ||
884 | wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); | |
885 | } | |
886 | ||
4ded91ce LC |
887 | static s8 wl12xx_get_pg_ver(struct wl1271 *wl) |
888 | { | |
889 | u32 die_info; | |
890 | ||
891 | if (wl->chip.id == CHIP_ID_1283_PG20) | |
dd5512eb | 892 | die_info = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1); |
4ded91ce | 893 | else |
dd5512eb | 894 | die_info = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1); |
4ded91ce LC |
895 | |
896 | return (s8) (die_info & PG_VER_MASK) >> PG_VER_OFFSET; | |
897 | } | |
898 | ||
30d9b4a5 LC |
899 | static void wl12xx_get_mac(struct wl1271 *wl) |
900 | { | |
901 | if (wl12xx_mac_in_fuse(wl)) | |
902 | wl12xx_get_fuse_mac(wl); | |
903 | } | |
904 | ||
6f7dd16c | 905 | static struct wlcore_ops wl12xx_ops = { |
4a3b97ee AN |
906 | .identify_chip = wl12xx_identify_chip, |
907 | .boot = wl12xx_boot, | |
908 | .trigger_cmd = wl12xx_trigger_cmd, | |
909 | .ack_event = wl12xx_ack_event, | |
910 | .calc_tx_blocks = wl12xx_calc_tx_blocks, | |
911 | .set_tx_desc_blocks = wl12xx_set_tx_desc_blocks, | |
6f266e91 | 912 | .set_tx_desc_data_len = wl12xx_set_tx_desc_data_len, |
cd70f6a4 | 913 | .get_rx_buf_align = wl12xx_get_rx_buf_align, |
4158149c | 914 | .get_rx_packet_len = wl12xx_get_rx_packet_len, |
53d67a50 AN |
915 | .tx_immediate_compl = NULL, |
916 | .tx_delayed_compl = wl12xx_tx_delayed_compl, | |
9d68d1ee | 917 | .hw_init = wl12xx_hw_init, |
4a3b97ee AN |
918 | .get_pg_ver = wl12xx_get_pg_ver, |
919 | .get_mac = wl12xx_get_mac, | |
6f7dd16c LC |
920 | }; |
921 | ||
96e0c683 AN |
922 | struct wl12xx_priv { |
923 | }; | |
924 | ||
ffeb501c LC |
925 | static int __devinit wl12xx_probe(struct platform_device *pdev) |
926 | { | |
927 | struct wl1271 *wl; | |
928 | struct ieee80211_hw *hw; | |
96e0c683 | 929 | struct wl12xx_priv *priv; |
ffeb501c | 930 | |
96e0c683 | 931 | hw = wlcore_alloc_hw(sizeof(*priv)); |
ffeb501c LC |
932 | if (IS_ERR(hw)) { |
933 | wl1271_error("can't allocate hw"); | |
934 | return PTR_ERR(hw); | |
935 | } | |
936 | ||
937 | wl = hw->priv; | |
c31be25a | 938 | wl->ops = &wl12xx_ops; |
25a43d78 | 939 | wl->ptable = wl12xx_ptable; |
00782136 | 940 | wl->rtable = wl12xx_rtable; |
72b0624f | 941 | wl->num_tx_desc = 16; |
3edab305 AN |
942 | wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT; |
943 | wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE; | |
43a8bc5a AN |
944 | wl->band_rate_to_idx = wl12xx_band_rate_to_idx; |
945 | wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; | |
946 | wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; | |
ffeb501c LC |
947 | |
948 | return wlcore_probe(wl, pdev); | |
949 | } | |
b2ba99ff LC |
950 | |
951 | static const struct platform_device_id wl12xx_id_table[] __devinitconst = { | |
952 | { "wl12xx", 0 }, | |
953 | { } /* Terminating Entry */ | |
954 | }; | |
955 | MODULE_DEVICE_TABLE(platform, wl12xx_id_table); | |
956 | ||
957 | static struct platform_driver wl12xx_driver = { | |
ffeb501c | 958 | .probe = wl12xx_probe, |
b2ba99ff LC |
959 | .remove = __devexit_p(wlcore_remove), |
960 | .id_table = wl12xx_id_table, | |
961 | .driver = { | |
962 | .name = "wl12xx_driver", | |
963 | .owner = THIS_MODULE, | |
964 | } | |
965 | }; | |
966 | ||
967 | static int __init wl12xx_init(void) | |
968 | { | |
969 | return platform_driver_register(&wl12xx_driver); | |
970 | } | |
971 | module_init(wl12xx_init); | |
972 | ||
973 | static void __exit wl12xx_exit(void) | |
974 | { | |
975 | platform_driver_unregister(&wl12xx_driver); | |
976 | } | |
977 | module_exit(wl12xx_exit); | |
978 | ||
979 | MODULE_LICENSE("GPL v2"); | |
980 | MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); | |
6f7dd16c LC |
981 | MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); |
982 | MODULE_FIRMWARE(WL127X_FW_NAME_MULTI); | |
983 | MODULE_FIRMWARE(WL127X_PLT_FW_NAME); | |
984 | MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE); | |
985 | MODULE_FIRMWARE(WL128X_FW_NAME_MULTI); | |
986 | MODULE_FIRMWARE(WL128X_PLT_FW_NAME); |