Loading drivers/net/wireless/wl12xx/Kconfig +1 −1 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ menuconfig WL12XX_MENU depends on MAC80211 && EXPERIMENTAL ---help--- This will enable TI wl12xx driver support for the following chips: wl1271 and wl1273. wl1271, wl1273, wl1281 and wl1283. The drivers make use of the mac80211 stack. config WL12XX Loading drivers/net/wireless/wl12xx/acx.c +47 −12 Original line number Diff line number Diff line Loading @@ -965,10 +965,13 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) } /* memory config */ mem_conf->num_stations = wl->conf.mem.num_stations; mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; /* FIXME: for now we always use mem_wl127x for AP, because it * doesn't support dynamic memory and we don't have the * optimal values for wl128x without dynamic memory yet */ mem_conf->num_stations = wl->conf.mem_wl127x.num_stations; mem_conf->rx_mem_block_num = wl->conf.mem_wl127x.rx_block_num; mem_conf->tx_min_mem_block_num = wl->conf.mem_wl127x.tx_min_block_num; mem_conf->num_ssid_profiles = wl->conf.mem_wl127x.ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, Loading @@ -986,6 +989,7 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) { struct wl1271_acx_sta_config_memory *mem_conf; struct conf_memory_settings *mem; int ret; wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); Loading @@ -996,16 +1000,21 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) goto out; } if (wl->chip.id == CHIP_ID_1283_PG20) mem = &wl->conf.mem_wl128x; else mem = &wl->conf.mem_wl127x; /* memory config */ mem_conf->num_stations = wl->conf.mem.num_stations; mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; mem_conf->num_stations = mem->num_stations; mem_conf->rx_mem_block_num = mem->rx_block_num; mem_conf->tx_min_mem_block_num = mem->tx_min_block_num; mem_conf->num_ssid_profiles = mem->ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); mem_conf->dyn_mem_enable = wl->conf.mem.dynamic_memory; mem_conf->tx_free_req = wl->conf.mem.min_req_tx_blocks; mem_conf->rx_free_req = wl->conf.mem.min_req_rx_blocks; mem_conf->tx_min = wl->conf.mem.tx_min; mem_conf->dyn_mem_enable = mem->dynamic_memory; mem_conf->tx_free_req = mem->min_req_tx_blocks; mem_conf->rx_free_req = mem->min_req_rx_blocks; mem_conf->tx_min = mem->tx_min; ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, sizeof(*mem_conf)); Loading @@ -1019,6 +1028,32 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) return ret; } int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap) { struct wl1271_acx_host_config_bitmap *bitmap_conf; int ret; bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); if (!bitmap_conf) { ret = -ENOMEM; goto out; } bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP, bitmap_conf, sizeof(*bitmap_conf)); if (ret < 0) { wl1271_warning("wl1271 bitmap config opt failed: %d", ret); goto out; } out: kfree(bitmap_conf); return ret; } int wl1271_acx_init_mem_config(struct wl1271 *wl) { int ret; Loading drivers/net/wireless/wl12xx/acx.h +11 −0 Original line number Diff line number Diff line Loading @@ -939,6 +939,16 @@ struct wl1271_acx_keep_alive_config { u8 padding; } __packed; #define HOST_IF_CFG_RX_FIFO_ENABLE BIT(0) #define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1) #define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3) struct wl1271_acx_host_config_bitmap { struct acx_header header; __le32 host_cfg_bitmap; } __packed; enum { WL1271_ACX_TRIG_TYPE_LEVEL = 0, WL1271_ACX_TRIG_TYPE_EDGE, Loading Loading @@ -1275,6 +1285,7 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl1271_acx_ap_mem_cfg(struct wl1271 *wl); int wl1271_acx_sta_mem_cfg(struct wl1271 *wl); int wl1271_acx_init_mem_config(struct wl1271 *wl); int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); Loading drivers/net/wireless/wl12xx/boot.c +236 −43 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ */ #include <linux/slab.h> #include <linux/wl12xx.h> #include "acx.h" #include "reg.h" Loading Loading @@ -243,16 +244,39 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) if (wl->nvs == NULL) return -ENODEV; if (wl->chip.id == CHIP_ID_1283_PG20) { struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) { if (nvs->general_params.dual_mode_select) wl->enable_11a = true; } else { wl1271_error("nvs size is not as expected: %zu != %zu", wl->nvs_len, sizeof(struct wl128x_nvs_file)); kfree(wl->nvs); wl->nvs = NULL; wl->nvs_len = 0; return -EILSEQ; } /* only the first part of the NVS needs to be uploaded */ nvs_len = sizeof(nvs->nvs); nvs_ptr = (u8 *)nvs->nvs; } else { struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs; /* * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz band * configurations) can be removed when those NVS files stop floating * around. * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz * band configurations) can be removed when those NVS files stop * floating around. */ if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { /* for now 11a is unsupported in AP mode */ if (wl->bss_type != BSS_TYPE_AP_BSS && wl->nvs->general_params.dual_mode_select) nvs->general_params.dual_mode_select) wl->enable_11a = true; } Loading @@ -268,8 +292,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) } /* only the first part of the NVS needs to be uploaded */ nvs_len = sizeof(wl->nvs->nvs); nvs_ptr = (u8 *)wl->nvs->nvs; nvs_len = sizeof(nvs->nvs); nvs_ptr = (u8 *) nvs->nvs; } /* update current MAC address to NVS */ nvs_ptr[11] = wl->mac_addr[0]; Loading Loading @@ -319,10 +344,13 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) /* * We've reached the first zero length, the first NVS table * is located at an aligned offset which is at least 7 bytes further. * NOTE: The wl->nvs->nvs element must be first, in order to * simplify the casting, we assume it is at the beginning of * the wl->nvs structure. */ nvs_ptr = (u8 *)wl->nvs->nvs + ALIGN(nvs_ptr - (u8 *)wl->nvs->nvs + 7, 4); nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs; nvs_ptr = (u8 *)wl->nvs + ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4); nvs_len -= nvs_ptr - (u8 *)wl->nvs; /* Now we must set the partition correctly */ wl1271_set_partition(wl, &part_table[PART_WORK]); Loading Loading @@ -454,6 +482,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) if (wl->bss_type == BSS_TYPE_AP_BSS) wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; else wl->event_mask |= DUMMY_PACKET_EVENT_ID; ret = wl1271_event_unmask(wl); if (ret < 0) { Loading Loading @@ -493,24 +523,159 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; } /* uploads NVS and firmware */ int wl1271_load_firmware(struct wl1271 *wl) static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) { int ret = 0; u32 tmp, clk, pause; u16 spare_reg; /* Mask bits [2] & [8:4] in the sys_clk_cfg register */ spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG); if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= (BIT(3) | BIT(5) | BIT(6)); wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg); /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */ wl1271_top_reg_write(wl, SYS_CLK_CFG_REG, WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); /* Delay execution for 15msec, to let the HW settle */ mdelay(15); return 0; } static bool wl128x_is_tcxo_valid(struct wl1271 *wl) { u16 tcxo_detection; tcxo_detection = wl1271_top_reg_read(wl, TCXO_CLK_DETECT_REG); if (tcxo_detection & TCXO_DET_FAILED) return false; return true; } static bool wl128x_is_fref_valid(struct wl1271 *wl) { u16 fref_detection; fref_detection = wl1271_top_reg_read(wl, FREF_CLK_DETECT_REG); if (fref_detection & FREF_CLK_DETECT_FAIL) return false; return true; } static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl) { wl1271_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); wl1271_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL); return 0; } static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) { u16 spare_reg; u16 pll_config; u8 input_freq; /* Mask bits [3:1] in the sys_clk_cfg register */ spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG); if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= BIT(2); wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg); /* Handle special cases of the TCXO clock */ if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6) return wl128x_manually_configure_mcs_pll(wl); /* Set the input frequency according to the selected clock source */ input_freq = (clk & 1) + 1; pll_config = wl1271_top_reg_read(wl, MCS_PLL_CONFIG_REG); if (pll_config == 0xFFFF) return -EFAULT; pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT); pll_config |= MCS_PLL_ENABLE_HP; wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config); return 0; } /* * WL128x has two clocks input - TCXO and FREF. * TCXO is the main clock of the device, while FREF is used to sync * between the GPS and the cellular modem. * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used * as the WLAN/BT main clock. */ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) { u16 sys_clk_cfg; /* For XTAL-only modes, FREF will be used after switching from TCXO */ if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL || wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) { if (!wl128x_switch_tcxo_to_fref(wl)) return -EINVAL; goto fref_clk; } /* Query the HW, to determine which clock source we should use */ sys_clk_cfg = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG); if (sys_clk_cfg == 0xFFFF) return -EINVAL; if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF) goto fref_clk; /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */ if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 || wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) { if (!wl128x_switch_tcxo_to_fref(wl)) return -EINVAL; goto fref_clk; } /* TCXO clock is selected */ if (!wl128x_is_tcxo_valid(wl)) return -EINVAL; *selected_clock = wl->tcxo_clock; goto config_mcs_pll; fref_clk: /* FREF clock is selected */ if (!wl128x_is_fref_valid(wl)) return -EINVAL; *selected_clock = wl->ref_clock; config_mcs_pll: return wl128x_configure_mcs_pll(wl, *selected_clock); } static int wl127x_boot_clk(struct wl1271 *wl) { u32 pause; u32 clk; wl1271_boot_hw_version(wl); if (wl->ref_clock == 0 || wl->ref_clock == 2 || wl->ref_clock == 4) if (wl->ref_clock == CONF_REF_CLK_19_2_E || wl->ref_clock == CONF_REF_CLK_38_4_E || wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; else if (wl->ref_clock == 1 || wl->ref_clock == 3) else if (wl->ref_clock == CONF_REF_CLK_26_E || wl->ref_clock == CONF_REF_CLK_52_E) /* ref clk: 26/52 */ clk = 0x5; else return -EINVAL; if (wl->ref_clock != 0) { if (wl->ref_clock != CONF_REF_CLK_19_2_E) { u16 val; /* Set clock type (open drain) */ val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); Loading Loading @@ -540,6 +705,26 @@ int wl1271_load_firmware(struct wl1271 *wl) pause |= WU_COUNTER_PAUSE_VAL; wl1271_write32(wl, WU_COUNTER_PAUSE, pause); return 0; } /* uploads NVS and firmware */ int wl1271_load_firmware(struct wl1271 *wl) { int ret = 0; u32 tmp, clk; int selected_clock = -1; if (wl->chip.id == CHIP_ID_1283_PG20) { ret = wl128x_boot_clk(wl, &selected_clock); if (ret < 0) goto out; } else { ret = wl127x_boot_clk(wl); if (ret < 0) goto out; } /* Continue the ELP wake up sequence */ wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); Loading @@ -555,7 +740,12 @@ int wl1271_load_firmware(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); if (wl->chip.id == CHIP_ID_1283_PG20) { clk |= ((selected_clock & 0x3) << 1) << 4; } else { clk |= (wl->ref_clock << 1) << 4; } wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); Loading Loading @@ -585,16 +775,12 @@ int wl1271_load_firmware(struct wl1271 *wl) /* 6. read the EEPROM parameters */ tmp = wl1271_read32(wl, SCR_PAD2); ret = wl1271_boot_write_irq_polarity(wl); if (ret < 0) goto out; wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ if (wl->chip.id == CHIP_ID_1283_PG20) wl1271_top_reg_write(wl, SDIO_IO_DS, wl->conf.hci_io_ds); ret = wl1271_boot_upload_firmware(wl); if (ret < 0) goto out; Loading @@ -618,6 +804,13 @@ int wl1271_boot(struct wl1271 *wl) if (ret < 0) goto out; ret = wl1271_boot_write_irq_polarity(wl); if (ret < 0) goto out; wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); /* Enable firmware interrupts now */ wl1271_boot_enable_interrupts(wl); Loading drivers/net/wireless/wl12xx/boot.h +52 −0 Original line number Diff line number Diff line Loading @@ -74,4 +74,56 @@ struct wl1271_static_data { #define FREF_CLK_POLARITY_BITS 0xfffff8ff #define CLK_REQ_OUTN_SEL 0x700 /* PLL configuration algorithm for wl128x */ #define SYS_CLK_CFG_REG 0x2200 /* Bit[0] - 0-TCXO, 1-FREF */ #define MCS_PLL_CLK_SEL_FREF BIT(0) /* Bit[3:2] - 01-TCXO, 10-FREF */ #define WL_CLK_REQ_TYPE_FREF BIT(3) #define WL_CLK_REQ_TYPE_PG2 (BIT(3) | BIT(2)) /* Bit[4] - 0-TCXO, 1-FREF */ #define PRCM_CM_EN_MUX_WLAN_FREF BIT(4) #define TCXO_ILOAD_INT_REG 0x2264 #define TCXO_CLK_DETECT_REG 0x2266 #define TCXO_DET_FAILED BIT(4) #define FREF_ILOAD_INT_REG 0x2084 #define FREF_CLK_DETECT_REG 0x2086 #define FREF_CLK_DETECT_FAIL BIT(4) /* Use this reg for masking during driver access */ #define WL_SPARE_REG 0x2320 #define WL_SPARE_VAL BIT(2) /* Bit[6:5:3] - mask wl write SYS_CLK_CFG[8:5:2:4] */ #define WL_SPARE_MASK_8526 (BIT(6) | BIT(5) | BIT(3)) #define PLL_LOCK_COUNTERS_REG 0xD8C #define PLL_LOCK_COUNTERS_COEX 0x0F #define PLL_LOCK_COUNTERS_MCS 0xF0 #define MCS_PLL_OVERRIDE_REG 0xD90 #define MCS_PLL_CONFIG_REG 0xD92 #define MCS_SEL_IN_FREQ_MASK 0x0070 #define MCS_SEL_IN_FREQ_SHIFT 4 #define MCS_PLL_CONFIG_REG_VAL 0x73 #define MCS_PLL_ENABLE_HP (BIT(0) | BIT(1)) #define MCS_PLL_M_REG 0xD94 #define MCS_PLL_N_REG 0xD96 #define MCS_PLL_M_REG_VAL 0xC8 #define MCS_PLL_N_REG_VAL 0x07 #define SDIO_IO_DS 0xd14 /* SDIO/wSPI DS configuration values */ enum { HCI_IO_DS_8MA = 0, HCI_IO_DS_4MA = 1, /* default */ HCI_IO_DS_6MA = 2, HCI_IO_DS_2MA = 3, }; /* end PLL configuration algorithm for wl128x */ #endif Loading
drivers/net/wireless/wl12xx/Kconfig +1 −1 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ menuconfig WL12XX_MENU depends on MAC80211 && EXPERIMENTAL ---help--- This will enable TI wl12xx driver support for the following chips: wl1271 and wl1273. wl1271, wl1273, wl1281 and wl1283. The drivers make use of the mac80211 stack. config WL12XX Loading
drivers/net/wireless/wl12xx/acx.c +47 −12 Original line number Diff line number Diff line Loading @@ -965,10 +965,13 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) } /* memory config */ mem_conf->num_stations = wl->conf.mem.num_stations; mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; /* FIXME: for now we always use mem_wl127x for AP, because it * doesn't support dynamic memory and we don't have the * optimal values for wl128x without dynamic memory yet */ mem_conf->num_stations = wl->conf.mem_wl127x.num_stations; mem_conf->rx_mem_block_num = wl->conf.mem_wl127x.rx_block_num; mem_conf->tx_min_mem_block_num = wl->conf.mem_wl127x.tx_min_block_num; mem_conf->num_ssid_profiles = wl->conf.mem_wl127x.ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, Loading @@ -986,6 +989,7 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) { struct wl1271_acx_sta_config_memory *mem_conf; struct conf_memory_settings *mem; int ret; wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); Loading @@ -996,16 +1000,21 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) goto out; } if (wl->chip.id == CHIP_ID_1283_PG20) mem = &wl->conf.mem_wl128x; else mem = &wl->conf.mem_wl127x; /* memory config */ mem_conf->num_stations = wl->conf.mem.num_stations; mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; mem_conf->num_stations = mem->num_stations; mem_conf->rx_mem_block_num = mem->rx_block_num; mem_conf->tx_min_mem_block_num = mem->tx_min_block_num; mem_conf->num_ssid_profiles = mem->ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); mem_conf->dyn_mem_enable = wl->conf.mem.dynamic_memory; mem_conf->tx_free_req = wl->conf.mem.min_req_tx_blocks; mem_conf->rx_free_req = wl->conf.mem.min_req_rx_blocks; mem_conf->tx_min = wl->conf.mem.tx_min; mem_conf->dyn_mem_enable = mem->dynamic_memory; mem_conf->tx_free_req = mem->min_req_tx_blocks; mem_conf->rx_free_req = mem->min_req_rx_blocks; mem_conf->tx_min = mem->tx_min; ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, sizeof(*mem_conf)); Loading @@ -1019,6 +1028,32 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) return ret; } int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap) { struct wl1271_acx_host_config_bitmap *bitmap_conf; int ret; bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); if (!bitmap_conf) { ret = -ENOMEM; goto out; } bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP, bitmap_conf, sizeof(*bitmap_conf)); if (ret < 0) { wl1271_warning("wl1271 bitmap config opt failed: %d", ret); goto out; } out: kfree(bitmap_conf); return ret; } int wl1271_acx_init_mem_config(struct wl1271 *wl) { int ret; Loading
drivers/net/wireless/wl12xx/acx.h +11 −0 Original line number Diff line number Diff line Loading @@ -939,6 +939,16 @@ struct wl1271_acx_keep_alive_config { u8 padding; } __packed; #define HOST_IF_CFG_RX_FIFO_ENABLE BIT(0) #define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1) #define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3) struct wl1271_acx_host_config_bitmap { struct acx_header header; __le32 host_cfg_bitmap; } __packed; enum { WL1271_ACX_TRIG_TYPE_LEVEL = 0, WL1271_ACX_TRIG_TYPE_EDGE, Loading Loading @@ -1275,6 +1285,7 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl1271_acx_ap_mem_cfg(struct wl1271 *wl); int wl1271_acx_sta_mem_cfg(struct wl1271 *wl); int wl1271_acx_init_mem_config(struct wl1271 *wl); int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); Loading
drivers/net/wireless/wl12xx/boot.c +236 −43 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ */ #include <linux/slab.h> #include <linux/wl12xx.h> #include "acx.h" #include "reg.h" Loading Loading @@ -243,16 +244,39 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) if (wl->nvs == NULL) return -ENODEV; if (wl->chip.id == CHIP_ID_1283_PG20) { struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) { if (nvs->general_params.dual_mode_select) wl->enable_11a = true; } else { wl1271_error("nvs size is not as expected: %zu != %zu", wl->nvs_len, sizeof(struct wl128x_nvs_file)); kfree(wl->nvs); wl->nvs = NULL; wl->nvs_len = 0; return -EILSEQ; } /* only the first part of the NVS needs to be uploaded */ nvs_len = sizeof(nvs->nvs); nvs_ptr = (u8 *)nvs->nvs; } else { struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs; /* * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz band * configurations) can be removed when those NVS files stop floating * around. * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz * band configurations) can be removed when those NVS files stop * floating around. */ if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { /* for now 11a is unsupported in AP mode */ if (wl->bss_type != BSS_TYPE_AP_BSS && wl->nvs->general_params.dual_mode_select) nvs->general_params.dual_mode_select) wl->enable_11a = true; } Loading @@ -268,8 +292,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) } /* only the first part of the NVS needs to be uploaded */ nvs_len = sizeof(wl->nvs->nvs); nvs_ptr = (u8 *)wl->nvs->nvs; nvs_len = sizeof(nvs->nvs); nvs_ptr = (u8 *) nvs->nvs; } /* update current MAC address to NVS */ nvs_ptr[11] = wl->mac_addr[0]; Loading Loading @@ -319,10 +344,13 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) /* * We've reached the first zero length, the first NVS table * is located at an aligned offset which is at least 7 bytes further. * NOTE: The wl->nvs->nvs element must be first, in order to * simplify the casting, we assume it is at the beginning of * the wl->nvs structure. */ nvs_ptr = (u8 *)wl->nvs->nvs + ALIGN(nvs_ptr - (u8 *)wl->nvs->nvs + 7, 4); nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs; nvs_ptr = (u8 *)wl->nvs + ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4); nvs_len -= nvs_ptr - (u8 *)wl->nvs; /* Now we must set the partition correctly */ wl1271_set_partition(wl, &part_table[PART_WORK]); Loading Loading @@ -454,6 +482,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) if (wl->bss_type == BSS_TYPE_AP_BSS) wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; else wl->event_mask |= DUMMY_PACKET_EVENT_ID; ret = wl1271_event_unmask(wl); if (ret < 0) { Loading Loading @@ -493,24 +523,159 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; } /* uploads NVS and firmware */ int wl1271_load_firmware(struct wl1271 *wl) static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) { int ret = 0; u32 tmp, clk, pause; u16 spare_reg; /* Mask bits [2] & [8:4] in the sys_clk_cfg register */ spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG); if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= (BIT(3) | BIT(5) | BIT(6)); wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg); /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */ wl1271_top_reg_write(wl, SYS_CLK_CFG_REG, WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); /* Delay execution for 15msec, to let the HW settle */ mdelay(15); return 0; } static bool wl128x_is_tcxo_valid(struct wl1271 *wl) { u16 tcxo_detection; tcxo_detection = wl1271_top_reg_read(wl, TCXO_CLK_DETECT_REG); if (tcxo_detection & TCXO_DET_FAILED) return false; return true; } static bool wl128x_is_fref_valid(struct wl1271 *wl) { u16 fref_detection; fref_detection = wl1271_top_reg_read(wl, FREF_CLK_DETECT_REG); if (fref_detection & FREF_CLK_DETECT_FAIL) return false; return true; } static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl) { wl1271_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); wl1271_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL); return 0; } static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) { u16 spare_reg; u16 pll_config; u8 input_freq; /* Mask bits [3:1] in the sys_clk_cfg register */ spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG); if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= BIT(2); wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg); /* Handle special cases of the TCXO clock */ if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6) return wl128x_manually_configure_mcs_pll(wl); /* Set the input frequency according to the selected clock source */ input_freq = (clk & 1) + 1; pll_config = wl1271_top_reg_read(wl, MCS_PLL_CONFIG_REG); if (pll_config == 0xFFFF) return -EFAULT; pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT); pll_config |= MCS_PLL_ENABLE_HP; wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config); return 0; } /* * WL128x has two clocks input - TCXO and FREF. * TCXO is the main clock of the device, while FREF is used to sync * between the GPS and the cellular modem. * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used * as the WLAN/BT main clock. */ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) { u16 sys_clk_cfg; /* For XTAL-only modes, FREF will be used after switching from TCXO */ if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL || wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) { if (!wl128x_switch_tcxo_to_fref(wl)) return -EINVAL; goto fref_clk; } /* Query the HW, to determine which clock source we should use */ sys_clk_cfg = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG); if (sys_clk_cfg == 0xFFFF) return -EINVAL; if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF) goto fref_clk; /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */ if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 || wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) { if (!wl128x_switch_tcxo_to_fref(wl)) return -EINVAL; goto fref_clk; } /* TCXO clock is selected */ if (!wl128x_is_tcxo_valid(wl)) return -EINVAL; *selected_clock = wl->tcxo_clock; goto config_mcs_pll; fref_clk: /* FREF clock is selected */ if (!wl128x_is_fref_valid(wl)) return -EINVAL; *selected_clock = wl->ref_clock; config_mcs_pll: return wl128x_configure_mcs_pll(wl, *selected_clock); } static int wl127x_boot_clk(struct wl1271 *wl) { u32 pause; u32 clk; wl1271_boot_hw_version(wl); if (wl->ref_clock == 0 || wl->ref_clock == 2 || wl->ref_clock == 4) if (wl->ref_clock == CONF_REF_CLK_19_2_E || wl->ref_clock == CONF_REF_CLK_38_4_E || wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; else if (wl->ref_clock == 1 || wl->ref_clock == 3) else if (wl->ref_clock == CONF_REF_CLK_26_E || wl->ref_clock == CONF_REF_CLK_52_E) /* ref clk: 26/52 */ clk = 0x5; else return -EINVAL; if (wl->ref_clock != 0) { if (wl->ref_clock != CONF_REF_CLK_19_2_E) { u16 val; /* Set clock type (open drain) */ val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); Loading Loading @@ -540,6 +705,26 @@ int wl1271_load_firmware(struct wl1271 *wl) pause |= WU_COUNTER_PAUSE_VAL; wl1271_write32(wl, WU_COUNTER_PAUSE, pause); return 0; } /* uploads NVS and firmware */ int wl1271_load_firmware(struct wl1271 *wl) { int ret = 0; u32 tmp, clk; int selected_clock = -1; if (wl->chip.id == CHIP_ID_1283_PG20) { ret = wl128x_boot_clk(wl, &selected_clock); if (ret < 0) goto out; } else { ret = wl127x_boot_clk(wl); if (ret < 0) goto out; } /* Continue the ELP wake up sequence */ wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); Loading @@ -555,7 +740,12 @@ int wl1271_load_firmware(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); if (wl->chip.id == CHIP_ID_1283_PG20) { clk |= ((selected_clock & 0x3) << 1) << 4; } else { clk |= (wl->ref_clock << 1) << 4; } wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); Loading Loading @@ -585,16 +775,12 @@ int wl1271_load_firmware(struct wl1271 *wl) /* 6. read the EEPROM parameters */ tmp = wl1271_read32(wl, SCR_PAD2); ret = wl1271_boot_write_irq_polarity(wl); if (ret < 0) goto out; wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ if (wl->chip.id == CHIP_ID_1283_PG20) wl1271_top_reg_write(wl, SDIO_IO_DS, wl->conf.hci_io_ds); ret = wl1271_boot_upload_firmware(wl); if (ret < 0) goto out; Loading @@ -618,6 +804,13 @@ int wl1271_boot(struct wl1271 *wl) if (ret < 0) goto out; ret = wl1271_boot_write_irq_polarity(wl); if (ret < 0) goto out; wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); /* Enable firmware interrupts now */ wl1271_boot_enable_interrupts(wl); Loading
drivers/net/wireless/wl12xx/boot.h +52 −0 Original line number Diff line number Diff line Loading @@ -74,4 +74,56 @@ struct wl1271_static_data { #define FREF_CLK_POLARITY_BITS 0xfffff8ff #define CLK_REQ_OUTN_SEL 0x700 /* PLL configuration algorithm for wl128x */ #define SYS_CLK_CFG_REG 0x2200 /* Bit[0] - 0-TCXO, 1-FREF */ #define MCS_PLL_CLK_SEL_FREF BIT(0) /* Bit[3:2] - 01-TCXO, 10-FREF */ #define WL_CLK_REQ_TYPE_FREF BIT(3) #define WL_CLK_REQ_TYPE_PG2 (BIT(3) | BIT(2)) /* Bit[4] - 0-TCXO, 1-FREF */ #define PRCM_CM_EN_MUX_WLAN_FREF BIT(4) #define TCXO_ILOAD_INT_REG 0x2264 #define TCXO_CLK_DETECT_REG 0x2266 #define TCXO_DET_FAILED BIT(4) #define FREF_ILOAD_INT_REG 0x2084 #define FREF_CLK_DETECT_REG 0x2086 #define FREF_CLK_DETECT_FAIL BIT(4) /* Use this reg for masking during driver access */ #define WL_SPARE_REG 0x2320 #define WL_SPARE_VAL BIT(2) /* Bit[6:5:3] - mask wl write SYS_CLK_CFG[8:5:2:4] */ #define WL_SPARE_MASK_8526 (BIT(6) | BIT(5) | BIT(3)) #define PLL_LOCK_COUNTERS_REG 0xD8C #define PLL_LOCK_COUNTERS_COEX 0x0F #define PLL_LOCK_COUNTERS_MCS 0xF0 #define MCS_PLL_OVERRIDE_REG 0xD90 #define MCS_PLL_CONFIG_REG 0xD92 #define MCS_SEL_IN_FREQ_MASK 0x0070 #define MCS_SEL_IN_FREQ_SHIFT 4 #define MCS_PLL_CONFIG_REG_VAL 0x73 #define MCS_PLL_ENABLE_HP (BIT(0) | BIT(1)) #define MCS_PLL_M_REG 0xD94 #define MCS_PLL_N_REG 0xD96 #define MCS_PLL_M_REG_VAL 0xC8 #define MCS_PLL_N_REG_VAL 0x07 #define SDIO_IO_DS 0xd14 /* SDIO/wSPI DS configuration values */ enum { HCI_IO_DS_8MA = 0, HCI_IO_DS_4MA = 1, /* default */ HCI_IO_DS_6MA = 2, HCI_IO_DS_2MA = 3, }; /* end PLL configuration algorithm for wl128x */ #endif