Commit 2f01a1f5 authored by Kalle Valo's avatar Kalle Valo Committed by John W. Linville

wl12xx: add driver

wl12xx is a driver for TI wl1251 802.11 chipset designed for embedded
devices, supporting both SDIO and SPI busses. Currently the driver
supports only SPI. Adding support 1253 (the 5 GHz version) should be
relatively easy. More information here:

http://focus.ti.com/general/docs/wtbu/wtbuproductcontent.tsp?contentId=4711&navigationId=12494&templateId=6123

(Collapsed original sequence of pre-merge patches into single commit for
initial merge. -- JWL)
Signed-off-by: default avatarKalle Valo <kalle.valo@nokia.com>
Signed-off-by: default avatarBob Copeland <me@bobcopeland.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent d53d9e67
...@@ -500,5 +500,6 @@ source "drivers/net/wireless/b43legacy/Kconfig" ...@@ -500,5 +500,6 @@ source "drivers/net/wireless/b43legacy/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/rt2x00/Kconfig" source "drivers/net/wireless/rt2x00/Kconfig"
source "drivers/net/wireless/orinoco/Kconfig" source "drivers/net/wireless/orinoco/Kconfig"
source "drivers/net/wireless/wl12xx/Kconfig"
endmenu endmenu
...@@ -58,3 +58,5 @@ obj-$(CONFIG_P54_COMMON) += p54/ ...@@ -58,3 +58,5 @@ obj-$(CONFIG_P54_COMMON) += p54/
obj-$(CONFIG_ATH_COMMON) += ath/ obj-$(CONFIG_ATH_COMMON) += ath/
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
obj-$(CONFIG_WL12XX) += wl12xx/
config WL12XX
tristate "TI wl1251/wl1271 support"
depends on MAC80211 && WLAN_80211 && SPI_MASTER && EXPERIMENTAL
select FW_LOADER
select CRC7
---help---
This module adds support for wireless adapters based on
TI wl1251/wl1271 chipsets.
If you choose to build a module, it'll be called wl12xx. Say N if
unsure.
wl12xx-objs = main.o spi.o event.o tx.o rx.o \
ps.o cmd.o acx.o boot.o init.o wl1251.o \
debugfs.o
obj-$(CONFIG_WL12XX) += wl12xx.o
This diff is collapsed.
This diff is collapsed.
/*
* This file is part of wl12xx
*
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/gpio.h>
#include "reg.h"
#include "boot.h"
#include "spi.h"
#include "event.h"
static void wl12xx_boot_enable_interrupts(struct wl12xx *wl)
{
enable_irq(wl->irq);
}
void wl12xx_boot_target_enable_interrupts(struct wl12xx *wl)
{
wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask));
wl12xx_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL);
}
int wl12xx_boot_soft_reset(struct wl12xx *wl)
{
unsigned long timeout;
u32 boot_data;
/* perform soft reset */
wl12xx_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
/* SOFT_RESET is self clearing */
timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME);
while (1) {
boot_data = wl12xx_reg_read32(wl, ACX_REG_SLV_SOFT_RESET);
wl12xx_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data);
if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0)
break;
if (time_after(jiffies, timeout)) {
/* 1.2 check pWhalBus->uSelfClearTime if the
* timeout was reached */
wl12xx_error("soft reset timeout");
return -1;
}
udelay(SOFT_RESET_STALL_TIME);
}
/* disable Rx/Tx */
wl12xx_reg_write32(wl, ENABLE, 0x0);
/* disable auto calibration on start*/
wl12xx_reg_write32(wl, SPARE_A2, 0xffff);
return 0;
}
int wl12xx_boot_init_seq(struct wl12xx *wl)
{
u32 scr_pad6, init_data, tmp, elp_cmd, ref_freq;
/*
* col #1: INTEGER_DIVIDER
* col #2: FRACTIONAL_DIVIDER
* col #3: ATTN_BB
* col #4: ALPHA_BB
* col #5: STOP_TIME_BB
* col #6: BB_PLL_LOOP_FILTER
*/
static const u32 LUT[REF_FREQ_NUM][LUT_PARAM_NUM] = {
{ 83, 87381, 0xB, 5, 0xF00, 3}, /* REF_FREQ_19_2*/
{ 61, 141154, 0xB, 5, 0x1450, 2}, /* REF_FREQ_26_0*/
{ 41, 174763, 0xC, 6, 0x2D00, 1}, /* REF_FREQ_38_4*/
{ 40, 0, 0xC, 6, 0x2EE0, 1}, /* REF_FREQ_40_0*/
{ 47, 162280, 0xC, 6, 0x2760, 1} /* REF_FREQ_33_6 */
};
/* read NVS params */
scr_pad6 = wl12xx_reg_read32(wl, SCR_PAD6);
wl12xx_debug(DEBUG_BOOT, "scr_pad6 0x%x", scr_pad6);
/* read ELP_CMD */
elp_cmd = wl12xx_reg_read32(wl, ELP_CMD);
wl12xx_debug(DEBUG_BOOT, "elp_cmd 0x%x", elp_cmd);
/* set the BB calibration time to be 300 usec (PLL_CAL_TIME) */
ref_freq = scr_pad6 & 0x000000FF;
wl12xx_debug(DEBUG_BOOT, "ref_freq 0x%x", ref_freq);
wl12xx_reg_write32(wl, PLL_CAL_TIME, 0x9);
/*
* PG 1.2: set the clock buffer time to be 210 usec (CLK_BUF_TIME)
*/
wl12xx_reg_write32(wl, CLK_BUF_TIME, 0x6);
/*
* set the clock detect feature to work in the restart wu procedure
* (ELP_CFG_MODE[14]) and Select the clock source type
* (ELP_CFG_MODE[13:12])
*/
tmp = ((scr_pad6 & 0x0000FF00) << 4) | 0x00004000;
wl12xx_reg_write32(wl, ELP_CFG_MODE, tmp);
/* PG 1.2: enable the BB PLL fix. Enable the PLL_LIMP_CLK_EN_CMD */
elp_cmd |= 0x00000040;
wl12xx_reg_write32(wl, ELP_CMD, elp_cmd);
/* PG 1.2: Set the BB PLL stable time to be 1000usec
* (PLL_STABLE_TIME) */
wl12xx_reg_write32(wl, CFG_PLL_SYNC_CNT, 0x20);
/* PG 1.2: read clock request time */
init_data = wl12xx_reg_read32(wl, CLK_REQ_TIME);
/*
* PG 1.2: set the clock request time to be ref_clk_settling_time -
* 1ms = 4ms
*/
if (init_data > 0x21)
tmp = init_data - 0x21;
else
tmp = 0;
wl12xx_reg_write32(wl, CLK_REQ_TIME, tmp);
/* set BB PLL configurations in RF AFE */
wl12xx_reg_write32(wl, 0x003058cc, 0x4B5);
/* set RF_AFE_REG_5 */
wl12xx_reg_write32(wl, 0x003058d4, 0x50);
/* set RF_AFE_CTRL_REG_2 */
wl12xx_reg_write32(wl, 0x00305948, 0x11c001);
/*
* change RF PLL and BB PLL divider for VCO clock and adjust VCO
* bais current(RF_AFE_REG_13)
*/
wl12xx_reg_write32(wl, 0x003058f4, 0x1e);
/* set BB PLL configurations */
tmp = LUT[ref_freq][LUT_PARAM_INTEGER_DIVIDER] | 0x00017000;
wl12xx_reg_write32(wl, 0x00305840, tmp);
/* set fractional divider according to Appendix C-BB PLL
* Calculations
*/
tmp = LUT[ref_freq][LUT_PARAM_FRACTIONAL_DIVIDER];
wl12xx_reg_write32(wl, 0x00305844, tmp);
/* set the initial data for the sigma delta */
wl12xx_reg_write32(wl, 0x00305848, 0x3039);
/*
* set the accumulator attenuation value, calibration loop1
* (alpha), calibration loop2 (beta), calibration loop3 (gamma) and
* the VCO gain
*/
tmp = (LUT[ref_freq][LUT_PARAM_ATTN_BB] << 16) |
(LUT[ref_freq][LUT_PARAM_ALPHA_BB] << 12) | 0x1;
wl12xx_reg_write32(wl, 0x00305854, tmp);
/*
* set the calibration stop time after holdoff time expires and set
* settling time HOLD_OFF_TIME_BB
*/
tmp = LUT[ref_freq][LUT_PARAM_STOP_TIME_BB] | 0x000A0000;
wl12xx_reg_write32(wl, 0x00305858, tmp);
/*
* set BB PLL Loop filter capacitor3- BB_C3[2:0] and set BB PLL
* constant leakage current to linearize PFD to 0uA -
* BB_ILOOPF[7:3]
*/
tmp = LUT[ref_freq][LUT_PARAM_BB_PLL_LOOP_FILTER] | 0x00000030;
wl12xx_reg_write32(wl, 0x003058f8, tmp);
/*
* set regulator output voltage for n divider to
* 1.35-BB_REFDIV[1:0], set charge pump current- BB_CPGAIN[4:2],
* set BB PLL Loop filter capacitor2- BB_C2[7:5], set gain of BB
* PLL auto-call to normal mode- BB_CALGAIN_3DB[8]
*/
wl12xx_reg_write32(wl, 0x003058f0, 0x29);
/* enable restart wakeup sequence (ELP_CMD[0]) */
wl12xx_reg_write32(wl, ELP_CMD, elp_cmd | 0x1);
/* restart sequence completed */
udelay(2000);
return 0;
}
int wl12xx_boot_run_firmware(struct wl12xx *wl)
{
int loop, ret;
u32 chip_id, interrupt;
wl->chip.op_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
chip_id = wl12xx_reg_read32(wl, CHIP_ID_B);
wl12xx_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id);
if (chip_id != wl->chip.id) {
wl12xx_error("chip id doesn't match after firmware boot");
return -EIO;
}
/* wait for init to complete */
loop = 0;
while (loop++ < INIT_LOOP) {
udelay(INIT_LOOP_DELAY);
interrupt = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
if (interrupt == 0xffffffff) {
wl12xx_error("error reading hardware complete "
"init indication");
return -EIO;
}
/* check that ACX_INTR_INIT_COMPLETE is enabled */
else if (interrupt & wl->chip.intr_init_complete) {
wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_ACK,
wl->chip.intr_init_complete);
break;
}
}
if (loop >= INIT_LOOP) {
wl12xx_error("timeout waiting for the hardware to "
"complete initialization");
return -EIO;
}
/* get hardware config command mail box */
wl->cmd_box_addr = wl12xx_reg_read32(wl, REG_COMMAND_MAILBOX_PTR);
/* get hardware config event mail box */
wl->event_box_addr = wl12xx_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
/* set the working partition to its "running" mode offset */
wl12xx_set_partition(wl,
wl->chip.p_table[PART_WORK].mem.start,
wl->chip.p_table[PART_WORK].mem.size,
wl->chip.p_table[PART_WORK].reg.start,
wl->chip.p_table[PART_WORK].reg.size);
wl12xx_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x",
wl->cmd_box_addr, wl->event_box_addr);
/*
* in case of full asynchronous mode the firmware event must be
* ready to receive event from the command mailbox
*/
/* enable gpio interrupts */
wl12xx_boot_enable_interrupts(wl);
wl->chip.op_target_enable_interrupts(wl);
/* unmask all mbox events */
wl->event_mask = 0xffffffff;
ret = wl12xx_event_unmask(wl);
if (ret < 0) {
wl12xx_error("EVENT mask setting failed");
return ret;
}
wl12xx_event_mbox_config(wl);
/* firmware startup completed */
return 0;
}
/*
* This file is part of wl12xx
*
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __BOOT_H__
#define __BOOT_H__
#include "wl12xx.h"
int wl12xx_boot_soft_reset(struct wl12xx *wl);
int wl12xx_boot_init_seq(struct wl12xx *wl);
int wl12xx_boot_run_firmware(struct wl12xx *wl);
void wl12xx_boot_target_enable_interrupts(struct wl12xx *wl);
/* number of times we try to read the INIT interrupt */
#define INIT_LOOP 20000
/* delay between retries */
#define INIT_LOOP_DELAY 50
#endif
#include "cmd.h"
#include <linux/module.h>
#include <linux/crc7.h>
#include <linux/spi/spi.h>
#include "wl12xx.h"
#include "wl12xx_80211.h"
#include "reg.h"
#include "spi.h"
#include "ps.h"
int wl12xx_cmd_send(struct wl12xx *wl, u16 type, void *buf, size_t buf_len)
{
struct wl12xx_command cmd;
unsigned long timeout;
size_t cmd_len;
u32 intr;
int ret = 0;
memset(&cmd, 0, sizeof(cmd));
cmd.id = type;
cmd.status = 0;
memcpy(cmd.parameters, buf, buf_len);
cmd_len = ALIGN(buf_len, 4) + CMDMBOX_HEADER_LEN;
wl12xx_ps_elp_wakeup(wl);
wl12xx_spi_mem_write(wl, wl->cmd_box_addr, &cmd, cmd_len);
wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD);
timeout = jiffies + msecs_to_jiffies(WL12XX_COMMAND_TIMEOUT);
intr = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
while (!(intr & wl->chip.intr_cmd_complete)) {
if (time_after(jiffies, timeout)) {
wl12xx_error("command complete timeout");
ret = -ETIMEDOUT;
goto out;
}
msleep(1);
intr = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
}
wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_ACK,
wl->chip.intr_cmd_complete);
out:
wl12xx_ps_elp_sleep(wl);
return ret;
}
int wl12xx_cmd_test(struct wl12xx *wl, void *buf, size_t buf_len, u8 answer)
{
int ret;
wl12xx_debug(DEBUG_CMD, "cmd test");
ret = wl12xx_cmd_send(wl, CMD_TEST, buf, buf_len);
if (ret < 0) {
wl12xx_warning("TEST command failed");
return ret;
}
if (answer) {
struct wl12xx_command *cmd_answer;
/*
* The test command got in, we can read the answer.
* The answer would be a wl12xx_command, where the
* parameter array contains the actual answer.
*/
wl12xx_ps_elp_wakeup(wl);
wl12xx_spi_mem_read(wl, wl->cmd_box_addr, buf, buf_len);
wl12xx_ps_elp_sleep(wl);
cmd_answer = buf;
if (cmd_answer->status != CMD_STATUS_SUCCESS)
wl12xx_error("TEST command answer error: %d",
cmd_answer->status);
}
return 0;
}
int wl12xx_cmd_interrogate(struct wl12xx *wl, u16 ie_id, u16 ie_len,
void *answer)
{
struct wl12xx_command *cmd;
struct acx_header header;
int ret;
wl12xx_debug(DEBUG_CMD, "cmd interrogate");
header.id = ie_id;
header.len = ie_len - sizeof(header);
ret = wl12xx_cmd_send(wl, CMD_INTERROGATE, &header, sizeof(header));
if (ret < 0) {
wl12xx_error("INTERROGATE command failed");
return ret;
}
wl12xx_ps_elp_wakeup(wl);
/* the interrogate command got in, we can read the answer */
wl12xx_spi_mem_read(wl, wl->cmd_box_addr, answer,
CMDMBOX_HEADER_LEN + ie_len);
wl12xx_ps_elp_sleep(wl);
cmd = answer;
if (cmd->status != CMD_STATUS_SUCCESS)
wl12xx_error("INTERROGATE command error: %d",
cmd->status);
return 0;
}
int wl12xx_cmd_configure(struct wl12xx *wl, void *ie, int ie_len)
{
int ret;
wl12xx_debug(DEBUG_CMD, "cmd configure");
ret = wl12xx_cmd_send(wl, CMD_CONFIGURE, ie,
ie_len);
if (ret < 0) {
wl12xx_warning("CONFIGURE command NOK");
return ret;
}
return 0;
}
int wl12xx_cmd_vbm(struct wl12xx *wl, u8 identity,
void *bitmap, u16 bitmap_len, u8 bitmap_control)
{
struct vbm_update_request vbm;
int ret;
wl12xx_debug(DEBUG_CMD, "cmd vbm");
/* Count and period will be filled by the target */
vbm.tim.bitmap_ctrl = bitmap_control;
if (bitmap_len > PARTIAL_VBM_MAX) {
wl12xx_warning("cmd vbm len is %d B, truncating to %d",
bitmap_len, PARTIAL_VBM_MAX);
bitmap_len = PARTIAL_VBM_MAX;
}
memcpy(vbm.tim.pvb_field, bitmap, bitmap_len);
vbm.tim.identity = identity;
vbm.tim.length = bitmap_len + 3;
vbm.len = cpu_to_le16(bitmap_len + 5);
ret = wl12xx_cmd_send(wl, CMD_VBM, &vbm, sizeof(vbm));
if (ret < 0) {
wl12xx_error("VBM command failed");
return ret;
}
return 0;
}
int wl12xx_cmd_data_path(struct wl12xx *wl, u8 channel, u8 enable)
{
int ret;
u16 cmd_rx, cmd_tx;
wl12xx_debug(DEBUG_CMD, "cmd data path");
if (enable) {
cmd_rx = CMD_ENABLE_RX;
cmd_tx = CMD_ENABLE_TX;
} else {
cmd_rx = CMD_DISABLE_RX;
cmd_tx = CMD_DISABLE_TX;
}
ret = wl12xx_cmd_send(wl, cmd_rx, &channel, sizeof(channel));
if (ret < 0) {
wl12xx_error("rx %s cmd for channel %d failed",
enable ? "start" : "stop", channel);
return ret;
}
wl12xx_debug(DEBUG_BOOT, "rx %s cmd channel %d",
enable ? "start" : "stop", channel);
ret = wl12xx_cmd_send(wl, cmd_tx, &channel, sizeof(channel));
if (ret < 0) {
wl12xx_error("tx %s cmd for channel %d failed",
enable ? "start" : "stop", channel);
return ret;
}
wl12xx_debug(DEBUG_BOOT, "tx %s cmd channel %d",
enable ? "start" : "stop", channel);
return 0;
}
int wl12xx_cmd_join(struct wl12xx *wl, u8 bss_type, u8 dtim_interval,
u16 beacon_interval, u8 wait)
{
unsigned long timeout;
struct cmd_join join = {};
int ret, i;
u8 *bssid;
/* FIXME: this should be in main.c */
ret = wl12xx_acx_frame_rates(wl, DEFAULT_HW_GEN_TX_RATE,
DEFAULT_HW_GEN_MODULATION_TYPE,
wl->tx_mgmt_frm_rate,
wl->tx_mgmt_frm_mod);
if (ret < 0)
return ret;
wl12xx_debug(DEBUG_CMD, "cmd join");
/* Reverse order BSSID */
bssid = (u8 *)&join.bssid_lsb;
for (i = 0; i < ETH_ALEN; i++)
bssid[i] = wl->bssid[ETH_ALEN - i - 1];
join.rx_config_options = wl->rx_config;
join.rx_filter_options = wl->rx_filter;
join.basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS |
RATE_MASK_5_5MBPS | RATE_MASK_11MBPS;
join.beacon_interval = beacon_interval;
join.dtim_interval = dtim_interval;
join.bss_type = bss_type;
join.channel = wl->channel;
join.ctrl = JOIN_CMD_CTRL_TX_FLUSH;
ret = wl12xx_cmd_send(wl, CMD_START_JOIN, &join, sizeof(join));
if (ret < 0) {
wl12xx_error("failed to initiate cmd join");
return ret;
}
timeout = msecs_to_jiffies(JOIN_TIMEOUT);
/*
* ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to
* simplify locking we just sleep instead, for now
*/
if (wait)
msleep(10);
return 0;
}
int wl12xx_cmd_ps_mode(struct wl12xx *wl, u8 ps_mode)
{
int ret;
struct acx_ps_params ps_params;
/* FIXME: this should be in ps.c */
ret = wl12xx_acx_wake_up_conditions(wl, wl->listen_int);
if (ret < 0) {
wl12xx_error("Couldnt set wake up conditions");
return ret;
}
wl12xx_debug(DEBUG_CMD, "cmd set ps mode");
ps_params.ps_mode = ps_mode;
ps_params.send_null_data = 1;
ps_params.retries = 5;
ps_params.hang_over_period = 128;
ps_params.null_data_rate = 1; /* 1 Mbps */
ret = wl12xx_cmd_send(wl, CMD_SET_PS_MODE, &ps_params,
sizeof(ps_params));
if (ret < 0) {
wl12xx_error("cmd set_ps_mode failed");
return ret;
}
return 0;
}
int wl12xx_cmd_read_memory(struct wl12xx *wl, u32 addr, u32 len, void *answer)
{
struct cmd_read_write_memory mem_cmd, *mem_answer;
struct wl12xx_command cmd;
int ret;
wl12xx_debug(DEBUG_CMD, "cmd read memory");
memset(&mem_cmd, 0, sizeof(mem_cmd));
mem_cmd.addr = addr;
mem_cmd.size = len;
ret = wl12xx_cmd_send(wl, CMD_READ_MEMORY, &mem_cmd, sizeof(mem_cmd));
if (ret < 0) {
wl12xx_error("read memory command failed: %d", ret);
return ret;
}
/* the read command got in, we can now read the answer */
wl12xx_spi_mem_read(wl, wl->cmd_box_addr, &cmd,
CMDMBOX_HEADER_LEN + sizeof(mem_cmd));
if (cmd.status != CMD_STATUS_SUCCESS)
wl12xx_error("error in read command result: %d", cmd.status);
mem_answer = (struct cmd_read_write_memory *) cmd.parameters;
memcpy(answer, mem_answer->value, len);
return 0;
}
int wl12xx_cmd_template_set(struct wl12xx *wl, u16 cmd_id,
void *buf, size_t buf_len)
{
struct wl12xx_cmd_packet_template template;
int ret;
wl12xx_debug(DEBUG_CMD, "cmd template %d", cmd_id);
memset(&template, 0, sizeof(template));
WARN_ON(buf_len > WL12XX_MAX_TEMPLATE_SIZE);
buf_len = min_t(size_t, buf_len, WL12XX_MAX_TEMPLATE_SIZE);
template.size = cpu_to_le16(buf_len);
if (buf)
memcpy(template.template, buf, buf_len);
ret = wl12xx_cmd_send(wl, cmd_id, &template,
sizeof(template.size) + buf_len);
if (ret < 0) {
wl12xx_warning("cmd set_template failed: %d", ret);
return ret;
}
return 0;
}
/*
* This file is part of wl12xx
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_CMD_H__
#define __WL12XX_CMD_H__
#include "wl12xx.h"
int wl12xx_cmd_send(struct wl12xx *wl, u16 type, void *buf, size_t buf_len);
int wl12xx_cmd_test(struct wl12xx *wl, void *buf, size_t buf_len, u8 answer);
int wl12xx_cmd_interrogate(struct wl12xx *wl, u16 ie_id, u16 ie_len,
void *answer);
int wl12xx_cmd_configure(struct wl12xx *wl, void *ie, int ie_len);
int wl12xx_cmd_vbm(struct wl12xx *wl, u8 identity,
void *bitmap, u16 bitmap_len, u8 bitmap_control);
int wl12xx_cmd_data_path(struct wl12xx *wl, u8 channel, u8 enable);
int wl12xx_cmd_join(struct wl12xx *wl, u8 bss_type, u8 dtim_interval,
u16 beacon_interval, u8 wait);
int wl12xx_cmd_ps_mode(struct wl12xx *wl, u8 ps_mode);
int wl12xx_cmd_read_memory(struct wl12xx *wl, u32 addr, u32 len, void *answer);
int wl12xx_cmd_template_set(struct wl12xx *wl, u16 cmd_id,
void *buf, size_t buf_len);
/* unit ms */
#define WL12XX_COMMAND_TIMEOUT 2000
#define WL12XX_MAX_TEMPLATE_SIZE 300
struct wl12xx_cmd_packet_template {
__le16 size;
u8 template[WL12XX_MAX_TEMPLATE_SIZE];
} __attribute__ ((packed));
enum wl12xx_commands {
CMD_RESET = 0,
CMD_INTERROGATE = 1, /*use this to read information elements*/
CMD_CONFIGURE = 2, /*use this to write information elements*/
CMD_ENABLE_RX = 3,
CMD_ENABLE_TX = 4,
CMD_DISABLE_RX = 5,
CMD_DISABLE_TX = 6,
CMD_SCAN = 8,
CMD_STOP_SCAN = 9,
CMD_VBM = 10,
CMD_START_JOIN = 11,
CMD_SET_KEYS = 12,
CMD_READ_MEMORY = 13,
CMD_WRITE_MEMORY = 14,
CMD_BEACON = 19,
CMD_PROBE_RESP = 20,
CMD_NULL_DATA = 21,
CMD_PROBE_REQ = 22,
CMD_TEST = 23,
CMD_RADIO_CALIBRATE = 25, /* OBSOLETE */
CMD_ENABLE_RX_PATH = 27, /* OBSOLETE */
CMD_NOISE_HIST = 28,
CMD_RX_RESET = 29,
CMD_PS_POLL = 30,
CMD_QOS_NULL_DATA = 31,
CMD_LNA_CONTROL = 32,
CMD_SET_BCN_MODE = 33,
CMD_MEASUREMENT = 34,
CMD_STOP_MEASUREMENT = 35,
CMD_DISCONNECT = 36,
CMD_SET_PS_MODE = 37,
CMD_CHANNEL_SWITCH = 38,
CMD_STOP_CHANNEL_SWICTH = 39,
CMD_AP_DISCOVERY = 40,
CMD_STOP_AP_DISCOVERY = 41,
CMD_SPS_SCAN = 42,
CMD_STOP_SPS_SCAN = 43,
CMD_HEALTH_CHECK = 45,
CMD_DEBUG = 46,
CMD_TRIGGER_SCAN_TO = 47,
NUM_COMMANDS,
MAX_COMMAND_ID = 0xFFFF,
};
#define MAX_CMD_PARAMS 572
struct wl12xx_command {
u16 id;
u16 status;
u8 parameters[MAX_CMD_PARAMS];
};
enum {
CMD_MAILBOX_IDLE = 0,
CMD_STATUS_SUCCESS = 1,
CMD_STATUS_UNKNOWN_CMD = 2,
CMD_STATUS_UNKNOWN_IE = 3,
CMD_STATUS_REJECT_MEAS_SG_ACTIVE = 11,
CMD_STATUS_RX_BUSY = 13,
CMD_STATUS_INVALID_PARAM = 14,
CMD_STATUS_TEMPLATE_TOO_LARGE = 15,
CMD_STATUS_OUT_OF_MEMORY = 16,
CMD_STATUS_STA_TABLE_FULL = 17,
CMD_STATUS_RADIO_ERROR = 18,
CMD_STATUS_WRONG_NESTING = 19,
CMD_STATUS_TIMEOUT = 21, /* Driver internal use.*/
CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/
MAX_COMMAND_STATUS = 0xff
};
/*
* CMD_READ_MEMORY
*
* The host issues this command to read the WiLink device memory/registers.
*
* Note: The Base Band address has special handling (16 bits registers and
* addresses). For more information, see the hardware specification.
*/
/*
* CMD_WRITE_MEMORY
*
* The host issues this command to write the WiLink device memory/registers.
*
* The Base Band address has special handling (16 bits registers and
* addresses). For more information, see the hardware specification.
*/
#define MAX_READ_SIZE 256
struct cmd_read_write_memory {
/* The address of the memory to read from or write to.*/
u32 addr;
/* The amount of data in bytes to read from or write to the WiLink
* device.*/
u32 size;
/* The actual value read from or written to the Wilink. The source
of this field is the Host in WRITE command or the Wilink in READ
command. */
u8 value[MAX_READ_SIZE];
};
#define CMDMBOX_HEADER_LEN 4
#define CMDMBOX_INFO_ELEM_HEADER_LEN 4
struct basic_scan_parameters {
u32 rx_config_options;
u32 rx_filter_options;
/*
* Scan options:
* bit 0: When this bit is set, passive scan.
* bit 1: Band, when this bit is set we scan
* in the 5Ghz band.
* bit 2: voice mode, 0 for normal scan.
* bit 3: scan priority, 1 for high priority.
*/
u16 scan_options;
/* Number of channels to scan */
u8 num_channels;
/* Number opf probe requests to send, per channel */
u8 num_probe_requests;
/* Rate and modulation for probe requests */
u16 tx_rate;
u8 tid_trigger;
u8 ssid_len;
u32 ssid[8];
} __attribute__ ((packed));
struct basic_scan_channel_parameters {
u32 min_duration; /* in TU */
u32 max_duration; /* in TU */
u32 bssid_lsb;
u16 bssid_msb;
/*
* bits 0-3: Early termination count.
* bits 4-5: Early termination condition.
*/
u8 early_termination;
u8 tx_power_att;
u8 channel;
u8 pad[3];
} __attribute__ ((packed));
/* SCAN parameters */
#define SCAN_MAX_NUM_OF_CHANNELS 16
struct cmd_scan {
struct basic_scan_parameters params;
struct basic_scan_channel_parameters channels[SCAN_MAX_NUM_OF_CHANNELS];
} __attribute__ ((packed));
enum {
BSS_TYPE_IBSS = 0,
BSS_TYPE_STA_BSS = 2,
BSS_TYPE_AP_BSS = 3,
MAX_BSS_TYPE = 0xFF
};
#define JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */
#define JOIN_CMD_CTRL_EARLY_WAKEUP_ENABLE 0x01 /* Early wakeup time */
struct cmd_join {
u32 bssid_lsb;
u16 bssid_msb;
u16 beacon_interval; /* in TBTTs */
u32 rx_config_options;
u32 rx_filter_options;
/*
* The target uses this field to determine the rate at
* which to transmit control frame responses (such as
* ACK or CTS frames).
*/
u16 basic_rate_set;
u8 dtim_interval;
u8 tx_ctrl_frame_rate; /* OBSOLETE */
u8 tx_ctrl_frame_mod; /* OBSOLETE */
/*
* bits 0-2: This bitwise field specifies the type
* of BSS to start or join (BSS_TYPE_*).
* bit 4: Band - The radio band in which to join
* or start.
* 0 - 2.4GHz band
* 1 - 5GHz band
* bits 3, 5-7: Reserved
*/
u8 bss_type;
u8 channel;
u8 ssid_len;
u8 ssid[IW_ESSID_MAX_SIZE];
u8 ctrl; /* JOIN_CMD_CTRL_* */
u8 tx_mgt_frame_rate; /* OBSOLETE */
u8 tx_mgt_frame_mod; /* OBSOLETE */
u8 reserved;
} __attribute__ ((packed));
#endif /* __WL12XX_CMD_H__ */
This diff is collapsed.
/*
* This file is part of wl12xx
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef WL12XX_DEBUGFS_H
#define WL12XX_DEBUGFS_H
#include "wl12xx.h"
int wl12xx_debugfs_init(struct wl12xx *wl);
void wl12xx_debugfs_exit(struct wl12xx *wl);
void wl12xx_debugfs_reset(struct wl12xx *wl);
#endif /* WL12XX_DEBUGFS_H */
/*
* This file is part of wl12xx
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "wl12xx.h"
#include "reg.h"
#include "spi.h"
#include "event.h"
#include "ps.h"
static int wl12xx_event_scan_complete(struct wl12xx *wl,
struct event_mailbox *mbox)
{
wl12xx_debug(DEBUG_EVENT, "status: 0x%x, channels: %d",
mbox->scheduled_scan_status,
mbox->scheduled_scan_channels);
if (wl->scanning) {
mutex_unlock(&wl->mutex);
ieee80211_scan_completed(wl->hw, false);
mutex_lock(&wl->mutex);
wl->scanning = false;
}
return 0;
}
static void wl12xx_event_mbox_dump(struct event_mailbox *mbox)
{
wl12xx_debug(DEBUG_EVENT, "MBOX DUMP:");
wl12xx_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
wl12xx_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
}
static int wl12xx_event_process(struct wl12xx *wl, struct event_mailbox *mbox)
{
int ret;
u32 vector;
wl12xx_event_mbox_dump(mbox);
vector = mbox->events_vector & ~(mbox->events_mask);
wl12xx_debug(DEBUG_EVENT, "vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) {
ret = wl12xx_event_scan_complete(wl, mbox);
if (ret < 0)
return ret;
}
if (vector & BSS_LOSE_EVENT_ID) {
wl12xx_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
if (wl->psm_requested && wl->psm) {
ret = wl12xx_ps_set_mode(wl, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
}
}
return 0;
}
int wl12xx_event_unmask(struct wl12xx *wl)
{
int ret;
ret = wl12xx_acx_event_mbox_mask(wl, ~(wl->event_mask));
if (ret < 0)
return ret;
return 0;
}
void wl12xx_event_mbox_config(struct wl12xx *wl)
{
wl->mbox_ptr[0] = wl12xx_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
wl12xx_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]);
}
int wl12xx_event_handle(struct wl12xx *wl, u8 mbox_num)
{
struct event_mailbox mbox;
int ret;
wl12xx_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num);
if (mbox_num > 1)
return -EINVAL;
/* first we read the mbox descriptor */
wl12xx_spi_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox,
sizeof(struct event_mailbox));
/* process the descriptor */
ret = wl12xx_event_process(wl, &mbox);
if (ret < 0)
return ret;
/* then we let the firmware know it can go on...*/
wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK);
return 0;
}
/*
* This file is part of wl12xx
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_EVENT_H__
#define __WL12XX_EVENT_H__
/*
* Mbox events
*
* The event mechanism is based on a pair of event buffers (buffers A and
* B) at fixed locations in the target's memory. The host processes one
* buffer while the other buffer continues to collect events. If the host
* is not processing events, an interrupt is issued to signal that a buffer
* is ready. Once the host is done with processing events from one buffer,
* it signals the target (with an ACK interrupt) that the event buffer is
* free.
*/
enum {
RESERVED1_EVENT_ID = BIT(0),
RESERVED2_EVENT_ID = BIT(1),
MEASUREMENT_START_EVENT_ID = BIT(2),
SCAN_COMPLETE_EVENT_ID = BIT(3),
CALIBRATION_COMPLETE_EVENT_ID = BIT(4),
ROAMING_TRIGGER_LOW_RSSI_EVENT_ID = BIT(5),
PS_REPORT_EVENT_ID = BIT(6),
SYNCHRONIZATION_TIMEOUT_EVENT_ID = BIT(7),
HEALTH_REPORT_EVENT_ID = BIT(8),
ACI_DETECTION_EVENT_ID = BIT(9),
DEBUG_REPORT_EVENT_ID = BIT(10),
MAC_STATUS_EVENT_ID = BIT(11),
DISCONNECT_EVENT_COMPLETE_ID = BIT(12),
JOIN_EVENT_COMPLETE_ID = BIT(13),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(14),
BSS_LOSE_EVENT_ID = BIT(15),
ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(16),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(17),
AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(18),
SCHEDULED_SCAN_COMPLETE_EVENT_ID = BIT(19),
PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(20),
RESET_BSS_EVENT_ID = BIT(21),
REGAINED_BSS_EVENT_ID = BIT(22),
ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID = BIT(23),
ROAMING_TRIGGER_LOW_SNR_EVENT_ID = BIT(24),
ROAMING_TRIGGER_REGAINED_SNR_EVENT_ID = BIT(25),
DBG_EVENT_ID = BIT(26),
BT_PTA_SENSE_EVENT_ID = BIT(27),
BT_PTA_PREDICTION_EVENT_ID = BIT(28),
BT_PTA_AVALANCHE_EVENT_ID = BIT(29),
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(30),
EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff,
};
struct event_debug_report {
u8 debug_event_id;
u8 num_params;
u16 pad;
u32 report_1;
u32 report_2;
u32 report_3;
} __attribute__ ((packed));
struct event_mailbox {
u32 events_vector;
u32 events_mask;
u32 reserved_1;
u32 reserved_2;
char average_rssi_level;
u8 ps_status;
u8 channel_switch_status;
u8 scheduled_scan_status;
/* Channels scanned by the scheduled scan */
u16 scheduled_scan_channels;
/* If bit 0 is set -> target's fatal error */
u16 health_report;
u16 bad_fft_counter;
u8 bt_pta_sense_info;
u8 bt_pta_protective_info;
u32 reserved;
u32 debug_report[2];
/* Number of FCS errors since last event */
u32 fcs_err_counter;
struct event_debug_report report;
u8 average_snr_level;
u8 padding[19];
} __attribute__ ((packed));
int wl12xx_event_unmask(struct wl12xx *wl);
void wl12xx_event_mbox_config(struct wl12xx *wl);
int wl12xx_event_handle(struct wl12xx *wl, u8 mbox);
#endif
/*
* This file is part of wl12xx
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include "init.h"
#include "wl12xx_80211.h"
#include "acx.h"
#include "cmd.h"
int wl12xx_hw_init_hwenc_config(struct wl12xx *wl)
{
int ret;
ret = wl12xx_acx_feature_cfg(wl);
if (ret < 0) {
wl12xx_warning("couldn't set feature config");
return ret;
}
ret = wl12xx_acx_default_key(wl, wl->default_key);
if (ret < 0) {
wl12xx_warning("couldn't set default key");
return ret;
}
return 0;
}
int wl12xx_hw_init_templates_config(struct wl12xx *wl)
{
int ret;
u8 partial_vbm[PARTIAL_VBM_MAX];
/* send empty templates for fw memory reservation */
ret = wl12xx_cmd_template_set(wl, CMD_PROBE_REQ, NULL,
sizeof(struct wl12xx_probe_req_template));
if (ret < 0)
return ret;
ret = wl12xx_cmd_template_set(wl, CMD_NULL_DATA, NULL,
sizeof(struct wl12xx_null_data_template));
if (ret < 0)
return ret;
ret = wl12xx_cmd_template_set(wl, CMD_PS_POLL, NULL,
sizeof(struct wl12xx_ps_poll_template));
if (ret < 0)
return ret;
ret = wl12xx_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL,
sizeof
(struct wl12xx_qos_null_data_template));
if (ret < 0)
return ret;
ret = wl12xx_cmd_template_set(wl, CMD_PROBE_RESP, NULL,
sizeof
(struct wl12xx_probe_resp_template));
if (ret < 0)
return ret;
ret = wl12xx_cmd_template_set(wl, CMD_BEACON, NULL,
sizeof
(struct wl12xx_beacon_template));
if (ret < 0)
return ret;
/* tim templates, first reserve space then allocate an empty one */
memset(partial_vbm, 0, PARTIAL_VBM_MAX);
ret = wl12xx_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0);
if (ret < 0)
return ret;
ret = wl12xx_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0);
if (ret < 0)
return ret;
return 0;
}
int wl12xx_hw_init_rx_config(struct wl12xx *wl, u32 config, u32 filter)
{
int ret;
ret = wl12xx_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF);
if (ret < 0)
return ret;
ret = wl12xx_acx_rx_config(wl, config, filter);
if (ret < 0)
return ret;
return 0;
}
int wl12xx_hw_init_phy_config(struct wl12xx *wl)
{
int ret;
ret = wl12xx_acx_pd_threshold(wl);
if (ret < 0)
return ret;
ret = wl12xx_acx_slot(wl, DEFAULT_SLOT_TIME);
if (ret < 0)
return ret;
ret = wl12xx_acx_group_address_tbl(wl);
if (ret < 0)
return ret;
ret = wl12xx_acx_service_period_timeout(wl);
if (ret < 0)
return ret;
ret = wl12xx_acx_rts_threshold(wl, RTS_THRESHOLD_DEF);
if (ret < 0)
return ret;
return 0;
}
int wl12xx_hw_init_beacon_filter(struct wl12xx *wl)
{
int ret;
ret = wl12xx_acx_beacon_filter_opt(wl);
if (ret < 0)
return ret;
ret = wl12xx_acx_beacon_filter_table(wl);
if (ret < 0)
return ret;
return 0;
}
int wl12xx_hw_init_pta(struct wl12xx *wl)
{
int ret;
ret = wl12xx_acx_sg_enable(wl);
if (ret < 0)
return ret;
ret = wl12xx_acx_sg_cfg(wl);
if (ret < 0)
return ret;
return 0;
}
int wl12xx_hw_init_energy_detection(struct wl12xx *wl)
{
int ret;
ret = wl12xx_acx_cca_threshold(wl);
if (ret < 0)
return ret;
return 0;
}
int wl12xx_hw_init_beacon_broadcast(struct wl12xx *wl)
{
int ret;
ret = wl12xx_acx_bcn_dtim_options(wl);
if (ret < 0)
return ret;
return 0;
}
int wl12xx_hw_init_power_auth(struct wl12xx *wl)
{
return wl12xx_acx_sleep_auth(wl, WL12XX_PSM_CAM);
}
/*
* This file is part of wl12xx
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_INIT_H__
#define __WL12XX_INIT_H__
#include "wl12xx.h"
int wl12xx_hw_init_hwenc_config(struct wl12xx *wl);
int wl12xx_hw_init_templates_config(struct wl12xx *wl);
int wl12xx_hw_init_mem_config(struct wl12xx *wl);
int wl12xx_hw_init_rx_config(struct wl12xx *wl, u32 config, u32 filter);
int wl12xx_hw_init_phy_config(struct wl12xx *wl);
int wl12xx_hw_init_beacon_filter(struct wl12xx *wl);
int wl12xx_hw_init_pta(struct wl12xx *wl);
int wl12xx_hw_init_energy_detection(struct wl12xx *wl);
int wl12xx_hw_init_beacon_broadcast(struct wl12xx *wl);
int wl12xx_hw_init_power_auth(struct wl12xx *wl);
#endif
This diff is collapsed.
/*
* This file is part of wl12xx
*
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "reg.h"
#include "ps.h"
#include "spi.h"
#define WL12XX_WAKEUP_TIMEOUT 2000
/* Routines to toggle sleep mode while in ELP */
void wl12xx_ps_elp_sleep(struct wl12xx *wl)
{
if (wl->elp || !wl->psm)
return;
wl12xx_debug(DEBUG_PSM, "chip to elp");
wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
wl->elp = true;
}
int wl12xx_ps_elp_wakeup(struct wl12xx *wl)
{
unsigned long timeout;
u32 elp_reg;
if (!wl->elp)
return 0;
wl12xx_debug(DEBUG_PSM, "waking up chip from elp");
timeout = jiffies + msecs_to_jiffies(WL12XX_WAKEUP_TIMEOUT);
wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
elp_reg = wl12xx_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
/*
* FIXME: we should wait for irq from chip but, as a temporary
* solution to simplify locking, let's poll instead
*/
while (!(elp_reg & ELPCTRL_WLAN_READY)) {
if (time_after(jiffies, timeout)) {
wl12xx_error("elp wakeup timeout");
return -ETIMEDOUT;
}
msleep(1);
elp_reg = wl12xx_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
}
wl12xx_debug(DEBUG_PSM, "wakeup time: %u ms",
jiffies_to_msecs(jiffies) -
(jiffies_to_msecs(timeout) - WL12XX_WAKEUP_TIMEOUT));
wl->elp = false;
return 0;
}
static int wl12xx_ps_set_elp(struct wl12xx *wl, bool enable)
{
int ret;
if (enable) {
wl12xx_debug(DEBUG_PSM, "sleep auth psm/elp");
/*
* FIXME: we should PSM_ELP, but because of firmware wakeup
* problems let's use only PSM_PS
*/
ret = wl12xx_acx_sleep_auth(wl, WL12XX_PSM_PS);
if (ret < 0)
return ret;
wl12xx_ps_elp_sleep(wl);
} else {
wl12xx_debug(DEBUG_PSM, "sleep auth cam");
/*
* When the target is in ELP, we can only
* access the ELP control register. Thus,
* we have to wake the target up before
* changing the power authorization.
*/
wl12xx_ps_elp_wakeup(wl);
ret = wl12xx_acx_sleep_auth(wl, WL12XX_PSM_CAM);
if (ret < 0)
return ret;
}
return 0;
}
int wl12xx_ps_set_mode(struct wl12xx *wl, enum acx_ps_mode mode)
{
int ret;
switch (mode) {
case STATION_POWER_SAVE_MODE:
wl12xx_debug(DEBUG_PSM, "entering psm");
ret = wl12xx_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
if (ret < 0)
return ret;
ret = wl12xx_ps_set_elp(wl, true);
if (ret < 0)
return ret;
wl->psm = 1;
break;
case STATION_ACTIVE_MODE:
default:
wl12xx_debug(DEBUG_PSM, "leaving psm");
ret = wl12xx_ps_set_elp(wl, false);
if (ret < 0)
return ret;
ret = wl12xx_cmd_ps_mode(wl, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
wl->psm = 0;
break;
}
return ret;
}
#ifndef __WL12XX_PS_H__
#define __WL12XX_PS_H__
/*
* This file is part of wl12xx
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "wl12xx.h"
#include "acx.h"
int wl12xx_ps_set_mode(struct wl12xx *wl, enum acx_ps_mode mode);
void wl12xx_ps_elp_sleep(struct wl12xx *wl);
int wl12xx_ps_elp_wakeup(struct wl12xx *wl);
#endif /* __WL12XX_PS_H__ */
This diff is collapsed.
This diff is collapsed.
/*
* This file is part of wl12xx
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_RX_H__
#define __WL12XX_RX_H__
#include <linux/bitops.h>
/*
* RX PATH
*
* The Rx path uses a double buffer and an rx_contro structure, each located
* at a fixed address in the device memory. The host keeps track of which
* buffer is available and alternates between them on a per packet basis.
* The size of each of the two buffers is large enough to hold the longest
* 802.3 packet.
* The RX path goes like that:
* 1) The target generates an interrupt each time a new packet is received.
* There are 2 RX interrupts, one for each buffer.
* 2) The host reads the received packet from one of the double buffers.
* 3) The host triggers a target interrupt.
* 4) The target prepares the next RX packet.
*/
#define WL12XX_RX_MAX_RSSI -30
#define WL12XX_RX_MIN_RSSI -95
#define WL12XX_RX_ALIGN_TO 4
#define WL12XX_RX_ALIGN(len) (((len) + WL12XX_RX_ALIGN_TO - 1) & \
~(WL12XX_RX_ALIGN_TO - 1))
#define SHORT_PREAMBLE_BIT BIT(0)
#define OFDM_RATE_BIT BIT(6)
#define PBCC_RATE_BIT BIT(7)
#define PLCP_HEADER_LENGTH 8
#define RX_DESC_PACKETID_SHIFT 11
#define RX_MAX_PACKET_ID 3
#define RX_DESC_VALID_FCS 0x0001
#define RX_DESC_MATCH_RXADDR1 0x0002
#define RX_DESC_MCAST 0x0004
#define RX_DESC_STAINTIM 0x0008
#define RX_DESC_VIRTUAL_BM 0x0010
#define RX_DESC_BCAST 0x0020
#define RX_DESC_MATCH_SSID 0x0040
#define RX_DESC_MATCH_BSSID 0x0080
#define RX_DESC_ENCRYPTION_MASK 0x0300
#define RX_DESC_MEASURMENT 0x0400
#define RX_DESC_SEQNUM_MASK 0x1800
#define RX_DESC_MIC_FAIL 0x2000
#define RX_DESC_DECRYPT_FAIL 0x4000
struct wl12xx_rx_descriptor {
u32 timestamp; /* In microseconds */
u16 length; /* Paylod length, including headers */
u16 flags;
/*
* 0 - 802.11
* 1 - 802.3
* 2 - IP
* 3 - Raw Codec
*/
u8 type;
/*
* Recevied Rate:
* 0x0A - 1MBPS
* 0x14 - 2MBPS
* 0x37 - 5_5MBPS
* 0x0B - 6MBPS
* 0x0F - 9MBPS
* 0x6E - 11MBPS
* 0x0A - 12MBPS
* 0x0E - 18MBPS
* 0xDC - 22MBPS
* 0x09 - 24MBPS
* 0x0D - 36MBPS
* 0x08 - 48MBPS
* 0x0C - 54MBPS
*/
u8 rate;
u8 mod_pre; /* Modulation and preamble */
u8 channel;
/*
* 0 - 2.4 Ghz
* 1 - 5 Ghz
*/
u8 band;
s8 rssi; /* in dB */
u8 rcpi; /* in dB */
u8 snr; /* in dB */
} __attribute__ ((packed));
void wl12xx_rx(struct wl12xx *wl);
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment