blob: 57c70a4321e846475cee63d89f590e389307571e [file] [log] [blame]
Luciano Coelhob2ba99f2011-11-20 23:32:10 +02001/*
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
Luciano Coelhoffeb5012011-11-21 18:55:51 +020025#include <linux/err.h>
26
Luciano Coelhodd5512eb2012-04-11 11:03:14 +030027#include <linux/wl12xx.h>
28
Luciano Coelhob2ba99f2011-11-20 23:32:10 +020029#include "../wlcore/wlcore.h"
Luciano Coelhoffeb5012011-11-21 18:55:51 +020030#include "../wlcore/debug.h"
Luciano Coelho4ded91c2012-04-11 10:54:52 +030031#include "../wlcore/io.h"
Luciano Coelhodd5512eb2012-04-11 11:03:14 +030032#include "../wlcore/acx.h"
33#include "../wlcore/boot.h"
Luciano Coelhoffeb5012011-11-21 18:55:51 +020034
Luciano Coelho00782132011-11-29 13:38:37 +020035#include "reg.h"
Luciano Coelho25a43d72011-11-21 20:37:14 +020036
Luciano Coelho25a43d72011-11-21 20:37:14 +020037static struct wlcore_partition_set wl12xx_ptable[PART_TABLE_LEN] = {
38 [PART_DOWN] = {
39 .mem = {
40 .start = 0x00000000,
41 .size = 0x000177c0
42 },
43 .reg = {
44 .start = REGISTERS_BASE,
45 .size = 0x00008800
46 },
47 .mem2 = {
48 .start = 0x00000000,
49 .size = 0x00000000
50 },
51 .mem3 = {
52 .start = 0x00000000,
53 .size = 0x00000000
54 },
55 },
56
Luciano Coelho00782132011-11-29 13:38:37 +020057 [PART_BOOT] = { /* in wl12xx we can use a mix of work and down
58 * partition here */
59 .mem = {
60 .start = 0x00040000,
61 .size = 0x00014fc0
62 },
63 .reg = {
64 .start = REGISTERS_BASE,
65 .size = 0x00008800
66 },
67 .mem2 = {
68 .start = 0x00000000,
69 .size = 0x00000000
70 },
71 .mem3 = {
72 .start = 0x00000000,
73 .size = 0x00000000
74 },
75 },
76
Luciano Coelho25a43d72011-11-21 20:37:14 +020077 [PART_WORK] = {
78 .mem = {
79 .start = 0x00040000,
80 .size = 0x00014fc0
81 },
82 .reg = {
83 .start = REGISTERS_BASE,
84 .size = 0x0000a000
85 },
86 .mem2 = {
87 .start = 0x003004f8,
88 .size = 0x00000004
89 },
90 .mem3 = {
91 .start = 0x00040404,
92 .size = 0x00000000
93 },
94 },
95
96 [PART_DRPW] = {
97 .mem = {
98 .start = 0x00040000,
99 .size = 0x00014fc0
100 },
101 .reg = {
102 .start = DRPW_BASE,
103 .size = 0x00006000
104 },
105 .mem2 = {
106 .start = 0x00000000,
107 .size = 0x00000000
108 },
109 .mem3 = {
110 .start = 0x00000000,
111 .size = 0x00000000
112 }
113 }
114};
115
Luciano Coelho00782132011-11-29 13:38:37 +0200116static const int wl12xx_rtable[REG_TABLE_LEN] = {
117 [REG_ECPU_CONTROL] = WL12XX_REG_ECPU_CONTROL,
118 [REG_INTERRUPT_NO_CLEAR] = WL12XX_REG_INTERRUPT_NO_CLEAR,
119 [REG_INTERRUPT_ACK] = WL12XX_REG_INTERRUPT_ACK,
120 [REG_COMMAND_MAILBOX_PTR] = WL12XX_REG_COMMAND_MAILBOX_PTR,
121 [REG_EVENT_MAILBOX_PTR] = WL12XX_REG_EVENT_MAILBOX_PTR,
122 [REG_INTERRUPT_TRIG] = WL12XX_REG_INTERRUPT_TRIG,
123 [REG_INTERRUPT_MASK] = WL12XX_REG_INTERRUPT_MASK,
124 [REG_PC_ON_RECOVERY] = WL12XX_SCR_PAD4,
125 [REG_CHIP_ID_B] = WL12XX_CHIP_ID_B,
126 [REG_CMD_MBOX_ADDRESS] = WL12XX_CMD_MBOX_ADDRESS,
127
128 /* data access memory addresses, used with partition translation */
129 [REG_SLV_MEM_DATA] = WL1271_SLV_MEM_DATA,
130 [REG_SLV_REG_DATA] = WL1271_SLV_REG_DATA,
131
132 /* raw data access memory addresses */
133 [REG_RAW_FW_STATUS_ADDR] = FW_STATUS_ADDR,
134};
135
Luciano Coelho6f7dd162011-11-29 16:27:31 +0200136/* TODO: maybe move to a new header file? */
137#define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin"
138#define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin"
139#define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin"
140
141#define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin"
142#define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin"
143#define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin"
144
145static int wl12xx_identify_chip(struct wl1271 *wl)
146{
147 int ret = 0;
148
149 switch (wl->chip.id) {
150 case CHIP_ID_1271_PG10:
151 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
152 wl->chip.id);
153
Luciano Coelhod203e592011-11-30 12:30:01 +0200154 wl->quirks |= WLCORE_QUIRK_NO_BLOCKSIZE_ALIGNMENT |
155 WLCORE_QUIRK_LEGACY_NVS;
Luciano Coelho6f7dd162011-11-29 16:27:31 +0200156 wl->plt_fw_name = WL127X_PLT_FW_NAME;
157 wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
158 wl->mr_fw_name = WL127X_FW_NAME_MULTI;
159 break;
160
161 case CHIP_ID_1271_PG20:
162 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
163 wl->chip.id);
164
Luciano Coelhod203e592011-11-30 12:30:01 +0200165 wl->quirks |= WLCORE_QUIRK_NO_BLOCKSIZE_ALIGNMENT |
166 WLCORE_QUIRK_LEGACY_NVS;
Luciano Coelho6f7dd162011-11-29 16:27:31 +0200167 wl->plt_fw_name = WL127X_PLT_FW_NAME;
168 wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
169 wl->mr_fw_name = WL127X_FW_NAME_MULTI;
170 break;
171
172 case CHIP_ID_1283_PG20:
173 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
174 wl->chip.id);
175 wl->plt_fw_name = WL128X_PLT_FW_NAME;
176 wl->sr_fw_name = WL128X_FW_NAME_SINGLE;
177 wl->mr_fw_name = WL128X_FW_NAME_MULTI;
178 break;
179 case CHIP_ID_1283_PG10:
180 default:
181 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
182 ret = -ENODEV;
183 goto out;
184 }
185
186out:
187 return ret;
188}
189
Luciano Coelhodd5512eb2012-04-11 11:03:14 +0300190static void wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val)
191{
192 /* write address >> 1 + 0x30000 to OCP_POR_CTR */
193 addr = (addr >> 1) + 0x30000;
194 wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr);
195
196 /* write value to OCP_POR_WDATA */
197 wl1271_write32(wl, WL12XX_OCP_DATA_WRITE, val);
198
199 /* write 1 to OCP_CMD */
200 wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE);
201}
202
203static u16 wl12xx_top_reg_read(struct wl1271 *wl, int addr)
204{
205 u32 val;
206 int timeout = OCP_CMD_LOOP;
207
208 /* write address >> 1 + 0x30000 to OCP_POR_CTR */
209 addr = (addr >> 1) + 0x30000;
210 wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr);
211
212 /* write 2 to OCP_CMD */
213 wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ);
214
215 /* poll for data ready */
216 do {
217 val = wl1271_read32(wl, WL12XX_OCP_DATA_READ);
218 } while (!(val & OCP_READY_MASK) && --timeout);
219
220 if (!timeout) {
221 wl1271_warning("Top register access timed out.");
222 return 0xffff;
223 }
224
225 /* check data status and return if OK */
226 if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK)
227 return val & 0xffff;
228 else {
229 wl1271_warning("Top register access returned error.");
230 return 0xffff;
231 }
232}
233
234static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
235{
236 u16 spare_reg;
237
238 /* Mask bits [2] & [8:4] in the sys_clk_cfg register */
239 spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG);
240 if (spare_reg == 0xFFFF)
241 return -EFAULT;
242 spare_reg |= (BIT(3) | BIT(5) | BIT(6));
243 wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
244
245 /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */
246 wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG,
247 WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
248
249 /* Delay execution for 15msec, to let the HW settle */
250 mdelay(15);
251
252 return 0;
253}
254
255static bool wl128x_is_tcxo_valid(struct wl1271 *wl)
256{
257 u16 tcxo_detection;
258
259 tcxo_detection = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG);
260 if (tcxo_detection & TCXO_DET_FAILED)
261 return false;
262
263 return true;
264}
265
266static bool wl128x_is_fref_valid(struct wl1271 *wl)
267{
268 u16 fref_detection;
269
270 fref_detection = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG);
271 if (fref_detection & FREF_CLK_DETECT_FAIL)
272 return false;
273
274 return true;
275}
276
277static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl)
278{
279 wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL);
280 wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL);
281 wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL);
282
283 return 0;
284}
285
286static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
287{
288 u16 spare_reg;
289 u16 pll_config;
290 u8 input_freq;
291
292 /* Mask bits [3:1] in the sys_clk_cfg register */
293 spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG);
294 if (spare_reg == 0xFFFF)
295 return -EFAULT;
296 spare_reg |= BIT(2);
297 wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
298
299 /* Handle special cases of the TCXO clock */
300 if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
301 wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
302 return wl128x_manually_configure_mcs_pll(wl);
303
304 /* Set the input frequency according to the selected clock source */
305 input_freq = (clk & 1) + 1;
306
307 pll_config = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG);
308 if (pll_config == 0xFFFF)
309 return -EFAULT;
310 pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT);
311 pll_config |= MCS_PLL_ENABLE_HP;
312 wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
313
314 return 0;
315}
316
317/*
318 * WL128x has two clocks input - TCXO and FREF.
319 * TCXO is the main clock of the device, while FREF is used to sync
320 * between the GPS and the cellular modem.
321 * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used
322 * as the WLAN/BT main clock.
323 */
324static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
325{
326 u16 sys_clk_cfg;
327
328 /* For XTAL-only modes, FREF will be used after switching from TCXO */
329 if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
330 wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
331 if (!wl128x_switch_tcxo_to_fref(wl))
332 return -EINVAL;
333 goto fref_clk;
334 }
335
336 /* Query the HW, to determine which clock source we should use */
337 sys_clk_cfg = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG);
338 if (sys_clk_cfg == 0xFFFF)
339 return -EINVAL;
340 if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF)
341 goto fref_clk;
342
343 /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
344 if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
345 wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
346 if (!wl128x_switch_tcxo_to_fref(wl))
347 return -EINVAL;
348 goto fref_clk;
349 }
350
351 /* TCXO clock is selected */
352 if (!wl128x_is_tcxo_valid(wl))
353 return -EINVAL;
354 *selected_clock = wl->tcxo_clock;
355 goto config_mcs_pll;
356
357fref_clk:
358 /* FREF clock is selected */
359 if (!wl128x_is_fref_valid(wl))
360 return -EINVAL;
361 *selected_clock = wl->ref_clock;
362
363config_mcs_pll:
364 return wl128x_configure_mcs_pll(wl, *selected_clock);
365}
366
367static int wl127x_boot_clk(struct wl1271 *wl)
368{
369 u32 pause;
370 u32 clk;
371
372 if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
373 wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
374
375 if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
376 wl->ref_clock == CONF_REF_CLK_38_4_E ||
377 wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
378 /* ref clk: 19.2/38.4/38.4-XTAL */
379 clk = 0x3;
380 else if (wl->ref_clock == CONF_REF_CLK_26_E ||
381 wl->ref_clock == CONF_REF_CLK_52_E)
382 /* ref clk: 26/52 */
383 clk = 0x5;
384 else
385 return -EINVAL;
386
387 if (wl->ref_clock != CONF_REF_CLK_19_2_E) {
388 u16 val;
389 /* Set clock type (open drain) */
390 val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE);
391 val &= FREF_CLK_TYPE_BITS;
392 wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
393
394 /* Set clock pull mode (no pull) */
395 val = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL);
396 val |= NO_PULL;
397 wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val);
398 } else {
399 u16 val;
400 /* Set clock polarity */
401 val = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY);
402 val &= FREF_CLK_POLARITY_BITS;
403 val |= CLK_REQ_OUTN_SEL;
404 wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
405 }
406
407 wl1271_write32(wl, WL12XX_PLL_PARAMETERS, clk);
408
409 pause = wl1271_read32(wl, WL12XX_PLL_PARAMETERS);
410
411 wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause);
412
413 pause &= ~(WU_COUNTER_PAUSE_VAL);
414 pause |= WU_COUNTER_PAUSE_VAL;
415 wl1271_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause);
416
417 return 0;
418}
419
420static int wl1271_boot_soft_reset(struct wl1271 *wl)
421{
422 unsigned long timeout;
423 u32 boot_data;
424
425 /* perform soft reset */
426 wl1271_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
427
428 /* SOFT_RESET is self clearing */
429 timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME);
430 while (1) {
431 boot_data = wl1271_read32(wl, WL12XX_SLV_SOFT_RESET);
432 wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data);
433 if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0)
434 break;
435
436 if (time_after(jiffies, timeout)) {
437 /* 1.2 check pWhalBus->uSelfClearTime if the
438 * timeout was reached */
439 wl1271_error("soft reset timeout");
440 return -1;
441 }
442
443 udelay(SOFT_RESET_STALL_TIME);
444 }
445
446 /* disable Rx/Tx */
447 wl1271_write32(wl, WL12XX_ENABLE, 0x0);
448
449 /* disable auto calibration on start*/
450 wl1271_write32(wl, WL12XX_SPARE_A2, 0xffff);
451
452 return 0;
453}
454
455static int wl12xx_pre_boot(struct wl1271 *wl)
456{
457 int ret = 0;
458 u32 clk;
459 int selected_clock = -1;
460
461 if (wl->chip.id == CHIP_ID_1283_PG20) {
462 ret = wl128x_boot_clk(wl, &selected_clock);
463 if (ret < 0)
464 goto out;
465 } else {
466 ret = wl127x_boot_clk(wl);
467 if (ret < 0)
468 goto out;
469 }
470
471 /* Continue the ELP wake up sequence */
472 wl1271_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
473 udelay(500);
474
475 wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
476
477 /* Read-modify-write DRPW_SCRATCH_START register (see next state)
478 to be used by DRPw FW. The RTRIM value will be added by the FW
479 before taking DRPw out of reset */
480
481 clk = wl1271_read32(wl, WL12XX_DRPW_SCRATCH_START);
482
483 wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
484
485 if (wl->chip.id == CHIP_ID_1283_PG20)
486 clk |= ((selected_clock & 0x3) << 1) << 4;
487 else
488 clk |= (wl->ref_clock << 1) << 4;
489
490 wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
491
492 wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
493
494 /* Disable interrupts */
495 wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
496
497 ret = wl1271_boot_soft_reset(wl);
498 if (ret < 0)
499 goto out;
500
501out:
502 return ret;
503}
504
505static void wl12xx_pre_upload(struct wl1271 *wl)
506{
507 u32 tmp;
508
509 /* write firmware's last address (ie. it's length) to
510 * ACX_EEPROMLESS_IND_REG */
511 wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG");
512
513 wl1271_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND);
514
515 tmp = wlcore_read_reg(wl, REG_CHIP_ID_B);
516
517 wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
518
519 /* 6. read the EEPROM parameters */
520 tmp = wl1271_read32(wl, WL12XX_SCR_PAD2);
521
522 /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
523 * to upload_fw) */
524
525 if (wl->chip.id == CHIP_ID_1283_PG20)
526 wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
527}
528
529static void wl12xx_enable_interrupts(struct wl1271 *wl)
530{
531 u32 polarity;
532
533 polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY);
534
535 /* We use HIGH polarity, so unset the LOW bit */
536 polarity &= ~POLARITY_LOW;
537 wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
538
539 wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR);
540
541 wlcore_enable_interrupts(wl);
542 wlcore_write_reg(wl, REG_INTERRUPT_MASK,
543 WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
544
545 wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL);
546}
547
548static int wl12xx_boot(struct wl1271 *wl)
549{
550 int ret;
551
552 ret = wl12xx_pre_boot(wl);
553 if (ret < 0)
554 goto out;
555
556 ret = wlcore_boot_upload_nvs(wl);
557 if (ret < 0)
558 goto out;
559
560 wl12xx_pre_upload(wl);
561
562 ret = wlcore_boot_upload_firmware(wl);
563 if (ret < 0)
564 goto out;
565
566 ret = wlcore_boot_run_firmware(wl);
567 if (ret < 0)
568 goto out;
569
570 wl12xx_enable_interrupts(wl);
571
572out:
573 return ret;
574}
575
Luciano Coelhof16ff752012-04-11 10:15:46 +0300576static void wl12xx_trigger_cmd(struct wl1271 *wl)
577{
578 wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD);
579}
580
581static void wl12xx_ack_event(struct wl1271 *wl)
582{
583 wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_EVENT_ACK);
584}
585
Luciano Coelho30d9b4a2012-04-11 11:07:28 +0300586static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
587{
588 bool supported = false;
589 u8 major, minor;
590
591 if (wl->chip.id == CHIP_ID_1283_PG20) {
592 major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
593 minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
594
595 /* in wl128x we have the MAC address if the PG is >= (2, 1) */
596 if (major > 2 || (major == 2 && minor >= 1))
597 supported = true;
598 } else {
599 major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver);
600 minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver);
601
602 /* in wl127x we have the MAC address if the PG is >= (3, 1) */
603 if (major == 3 && minor >= 1)
604 supported = true;
605 }
606
607 wl1271_debug(DEBUG_PROBE,
608 "PG Ver major = %d minor = %d, MAC %s present",
609 major, minor, supported ? "is" : "is not");
610
611 return supported;
612}
613
614static void wl12xx_get_fuse_mac(struct wl1271 *wl)
615{
616 u32 mac1, mac2;
617
618 wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
619
620 mac1 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1);
621 mac2 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2);
622
623 /* these are the two parts of the BD_ADDR */
624 wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) +
625 ((mac1 & 0xff000000) >> 24);
626 wl->fuse_nic_addr = mac1 & 0xffffff;
627
628 wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
629}
630
Luciano Coelho4ded91c2012-04-11 10:54:52 +0300631static s8 wl12xx_get_pg_ver(struct wl1271 *wl)
632{
633 u32 die_info;
634
635 if (wl->chip.id == CHIP_ID_1283_PG20)
Luciano Coelhodd5512eb2012-04-11 11:03:14 +0300636 die_info = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1);
Luciano Coelho4ded91c2012-04-11 10:54:52 +0300637 else
Luciano Coelhodd5512eb2012-04-11 11:03:14 +0300638 die_info = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1);
Luciano Coelho4ded91c2012-04-11 10:54:52 +0300639
640 return (s8) (die_info & PG_VER_MASK) >> PG_VER_OFFSET;
641}
642
Luciano Coelho30d9b4a2012-04-11 11:07:28 +0300643static void wl12xx_get_mac(struct wl1271 *wl)
644{
645 if (wl12xx_mac_in_fuse(wl))
646 wl12xx_get_fuse_mac(wl);
647}
648
Luciano Coelho6f7dd162011-11-29 16:27:31 +0200649static struct wlcore_ops wl12xx_ops = {
Luciano Coelhodd5512eb2012-04-11 11:03:14 +0300650 .identify_chip = wl12xx_identify_chip,
651 .boot = wl12xx_boot,
Luciano Coelhof16ff752012-04-11 10:15:46 +0300652 .trigger_cmd = wl12xx_trigger_cmd,
653 .ack_event = wl12xx_ack_event,
Luciano Coelhodd5512eb2012-04-11 11:03:14 +0300654 .get_pg_ver = wl12xx_get_pg_ver,
Luciano Coelho30d9b4a2012-04-11 11:07:28 +0300655 .get_mac = wl12xx_get_mac,
Luciano Coelho6f7dd162011-11-29 16:27:31 +0200656};
657
Arik Nemtsov96e0c682011-12-07 21:09:03 +0200658struct wl12xx_priv {
659};
660
Luciano Coelhoffeb5012011-11-21 18:55:51 +0200661static int __devinit wl12xx_probe(struct platform_device *pdev)
662{
663 struct wl1271 *wl;
664 struct ieee80211_hw *hw;
Arik Nemtsov96e0c682011-12-07 21:09:03 +0200665 struct wl12xx_priv *priv;
Luciano Coelhoffeb5012011-11-21 18:55:51 +0200666
Arik Nemtsov96e0c682011-12-07 21:09:03 +0200667 hw = wlcore_alloc_hw(sizeof(*priv));
Luciano Coelhoffeb5012011-11-21 18:55:51 +0200668 if (IS_ERR(hw)) {
669 wl1271_error("can't allocate hw");
670 return PTR_ERR(hw);
671 }
672
673 wl = hw->priv;
Luciano Coelhoc31be252011-11-21 19:25:24 +0200674 wl->ops = &wl12xx_ops;
Luciano Coelho25a43d72011-11-21 20:37:14 +0200675 wl->ptable = wl12xx_ptable;
Luciano Coelho00782132011-11-29 13:38:37 +0200676 wl->rtable = wl12xx_rtable;
Arik Nemtsov72b06242011-12-07 21:21:51 +0200677 wl->num_tx_desc = 16;
Luciano Coelhoffeb5012011-11-21 18:55:51 +0200678
679 return wlcore_probe(wl, pdev);
680}
Luciano Coelhob2ba99f2011-11-20 23:32:10 +0200681
682static const struct platform_device_id wl12xx_id_table[] __devinitconst = {
683 { "wl12xx", 0 },
684 { } /* Terminating Entry */
685};
686MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
687
688static struct platform_driver wl12xx_driver = {
Luciano Coelhoffeb5012011-11-21 18:55:51 +0200689 .probe = wl12xx_probe,
Luciano Coelhob2ba99f2011-11-20 23:32:10 +0200690 .remove = __devexit_p(wlcore_remove),
691 .id_table = wl12xx_id_table,
692 .driver = {
693 .name = "wl12xx_driver",
694 .owner = THIS_MODULE,
695 }
696};
697
698static int __init wl12xx_init(void)
699{
700 return platform_driver_register(&wl12xx_driver);
701}
702module_init(wl12xx_init);
703
704static void __exit wl12xx_exit(void)
705{
706 platform_driver_unregister(&wl12xx_driver);
707}
708module_exit(wl12xx_exit);
709
710MODULE_LICENSE("GPL v2");
711MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
Luciano Coelho6f7dd162011-11-29 16:27:31 +0200712MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
713MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
714MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
715MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
716MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
717MODULE_FIRMWARE(WL128X_PLT_FW_NAME);