wl1271: add wl1271 driver files

This driver supports the wl1271 chipset from Texas Instruments based on the
WiLink(tm) 6.0 mobile platform.  Support for wl1273 should be relatively easy
to add.  This chipset is designed for embedded devices, with good powersaving
capabilities.  The wl1271 chipset is the successor of wl1251 and supports the
802.11b/g/n standards, but currently this driver supports only b/g.

More information about this chipset can be found here:
http://focus.ti.com/general/docs/wtbu/wtbuproductcontent.tsp?templateId=6123&navigationId=12762&contentId=29993

Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Luciano Coelho
2009-08-06 16:25:28 +03:00
committed by John W. Linville
parent b935df01ed
commit f5fc0f86b0
23 changed files with 9431 additions and 0 deletions
+407
View File
@@ -0,0 +1,407 @@
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 __WL1271_H__
#define __WL1271_H__
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/bitops.h>
#include <net/mac80211.h>
#define DRIVER_NAME "wl1271"
#define DRIVER_PREFIX DRIVER_NAME ": "
enum {
DEBUG_NONE = 0,
DEBUG_IRQ = BIT(0),
DEBUG_SPI = BIT(1),
DEBUG_BOOT = BIT(2),
DEBUG_MAILBOX = BIT(3),
DEBUG_NETLINK = BIT(4),
DEBUG_EVENT = BIT(5),
DEBUG_TX = BIT(6),
DEBUG_RX = BIT(7),
DEBUG_SCAN = BIT(8),
DEBUG_CRYPT = BIT(9),
DEBUG_PSM = BIT(10),
DEBUG_MAC80211 = BIT(11),
DEBUG_CMD = BIT(12),
DEBUG_ACX = BIT(13),
DEBUG_ALL = ~0,
};
#define DEBUG_LEVEL (DEBUG_NONE)
#define DEBUG_DUMP_LIMIT 1024
#define wl1271_error(fmt, arg...) \
printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg)
#define wl1271_warning(fmt, arg...) \
printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg)
#define wl1271_notice(fmt, arg...) \
printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg)
#define wl1271_info(fmt, arg...) \
printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg)
#define wl1271_debug(level, fmt, arg...) \
do { \
if (level & DEBUG_LEVEL) \
printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \
} while (0)
#define wl1271_dump(level, prefix, buf, len) \
do { \
if (level & DEBUG_LEVEL) \
print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
DUMP_PREFIX_OFFSET, 16, 1, \
buf, \
min_t(size_t, len, DEBUG_DUMP_LIMIT), \
0); \
} while (0)
#define wl1271_dump_ascii(level, prefix, buf, len) \
do { \
if (level & DEBUG_LEVEL) \
print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
DUMP_PREFIX_OFFSET, 16, 1, \
buf, \
min_t(size_t, len, DEBUG_DUMP_LIMIT), \
true); \
} while (0)
#define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \
CFG_BSSID_FILTER_EN)
#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \
CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \
CFG_RX_CTL_EN | CFG_RX_BCN_EN | \
CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN)
#define WL1271_FW_NAME "wl1271-fw.bin"
#define WL1271_NVS_NAME "wl1271-nvs.bin"
#define WL1271_BUSY_WORD_LEN 8
#define WL1271_ELP_HW_STATE_ASLEEP 0
#define WL1271_ELP_HW_STATE_IRQ 1
enum wl1271_state {
WL1271_STATE_OFF,
WL1271_STATE_ON,
WL1271_STATE_PLT,
};
enum wl1271_partition_type {
PART_DOWN,
PART_WORK,
PART_DRPW,
PART_TABLE_LEN
};
struct wl1271_partition {
u32 size;
u32 start;
};
struct wl1271_partition_set {
struct wl1271_partition mem;
struct wl1271_partition reg;
};
struct wl1271;
/* FIXME: I'm not sure about this structure name */
struct wl1271_chip {
u32 id;
char fw_ver[21];
};
struct wl1271_stats {
struct acx_statistics *fw_stats;
unsigned long fw_stats_update;
unsigned int retry_count;
unsigned int excessive_retries;
};
struct wl1271_debugfs {
struct dentry *rootdir;
struct dentry *fw_statistics;
struct dentry *tx_internal_desc_overflow;
struct dentry *rx_out_of_mem;
struct dentry *rx_hdr_overflow;
struct dentry *rx_hw_stuck;
struct dentry *rx_dropped;
struct dentry *rx_fcs_err;
struct dentry *rx_xfr_hint_trig;
struct dentry *rx_path_reset;
struct dentry *rx_reset_counter;
struct dentry *dma_rx_requested;
struct dentry *dma_rx_errors;
struct dentry *dma_tx_requested;
struct dentry *dma_tx_errors;
struct dentry *isr_cmd_cmplt;
struct dentry *isr_fiqs;
struct dentry *isr_rx_headers;
struct dentry *isr_rx_mem_overflow;
struct dentry *isr_rx_rdys;
struct dentry *isr_irqs;
struct dentry *isr_tx_procs;
struct dentry *isr_decrypt_done;
struct dentry *isr_dma0_done;
struct dentry *isr_dma1_done;
struct dentry *isr_tx_exch_complete;
struct dentry *isr_commands;
struct dentry *isr_rx_procs;
struct dentry *isr_hw_pm_mode_changes;
struct dentry *isr_host_acknowledges;
struct dentry *isr_pci_pm;
struct dentry *isr_wakeups;
struct dentry *isr_low_rssi;
struct dentry *wep_addr_key_count;
struct dentry *wep_default_key_count;
/* skipping wep.reserved */
struct dentry *wep_key_not_found;
struct dentry *wep_decrypt_fail;
struct dentry *wep_packets;
struct dentry *wep_interrupt;
struct dentry *pwr_ps_enter;
struct dentry *pwr_elp_enter;
struct dentry *pwr_missing_bcns;
struct dentry *pwr_wake_on_host;
struct dentry *pwr_wake_on_timer_exp;
struct dentry *pwr_tx_with_ps;
struct dentry *pwr_tx_without_ps;
struct dentry *pwr_rcvd_beacons;
struct dentry *pwr_power_save_off;
struct dentry *pwr_enable_ps;
struct dentry *pwr_disable_ps;
struct dentry *pwr_fix_tsf_ps;
/* skipping cont_miss_bcns_spread for now */
struct dentry *pwr_rcvd_awake_beacons;
struct dentry *mic_rx_pkts;
struct dentry *mic_calc_failure;
struct dentry *aes_encrypt_fail;
struct dentry *aes_decrypt_fail;
struct dentry *aes_encrypt_packets;
struct dentry *aes_decrypt_packets;
struct dentry *aes_encrypt_interrupt;
struct dentry *aes_decrypt_interrupt;
struct dentry *event_heart_beat;
struct dentry *event_calibration;
struct dentry *event_rx_mismatch;
struct dentry *event_rx_mem_empty;
struct dentry *event_rx_pool;
struct dentry *event_oom_late;
struct dentry *event_phy_transmit_error;
struct dentry *event_tx_stuck;
struct dentry *ps_pspoll_timeouts;
struct dentry *ps_upsd_timeouts;
struct dentry *ps_upsd_max_sptime;
struct dentry *ps_upsd_max_apturn;
struct dentry *ps_pspoll_max_apturn;
struct dentry *ps_pspoll_utilization;
struct dentry *ps_upsd_utilization;
struct dentry *rxpipe_rx_prep_beacon_drop;
struct dentry *rxpipe_descr_host_int_trig_rx_data;
struct dentry *rxpipe_beacon_buffer_thres_host_int_trig_rx_data;
struct dentry *rxpipe_missed_beacon_host_int_trig_rx_data;
struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data;
struct dentry *tx_queue_len;
struct dentry *retry_count;
struct dentry *excessive_retries;
};
#define NUM_TX_QUEUES 4
#define NUM_RX_PKT_DESC 8
/* FW status registers */
struct wl1271_fw_status {
u32 intr;
u8 fw_rx_counter;
u8 drv_rx_counter;
u8 reserved;
u8 tx_results_counter;
u32 rx_pkt_descs[NUM_RX_PKT_DESC];
u32 tx_released_blks[NUM_TX_QUEUES];
u32 fw_localtime;
u32 padding[2];
} __attribute__ ((packed));
struct wl1271_rx_mem_pool_addr {
u32 addr;
u32 addr_extra;
};
struct wl1271 {
struct ieee80211_hw *hw;
bool mac80211_registered;
struct spi_device *spi;
void (*set_power)(bool enable);
int irq;
spinlock_t wl_lock;
enum wl1271_state state;
struct mutex mutex;
int physical_mem_addr;
int physical_reg_addr;
int virtual_mem_addr;
int virtual_reg_addr;
struct wl1271_chip chip;
int cmd_box_addr;
int event_box_addr;
u8 *fw;
size_t fw_len;
u8 *nvs;
size_t nvs_len;
u8 bssid[ETH_ALEN];
u8 mac_addr[ETH_ALEN];
u8 bss_type;
u8 ssid[IW_ESSID_MAX_SIZE + 1];
u8 ssid_len;
u8 listen_int;
int channel;
struct wl1271_acx_mem_map *target_mem_map;
/* Accounting for allocated / available TX blocks on HW */
u32 tx_blocks_freed[NUM_TX_QUEUES];
u32 tx_blocks_available;
u8 tx_results_count;
/* Transmitted TX packets counter for chipset interface */
int tx_packets_count;
/* Time-offset between host and chipset clocks */
int time_offset;
/* Session counter for the chipset */
int session_counter;
/* Frames scheduled for transmission, not handled yet */
struct sk_buff_head tx_queue;
bool tx_queue_stopped;
struct work_struct tx_work;
struct work_struct filter_work;
/* Pending TX frames */
struct sk_buff *tx_frames[16];
/* FW Rx counter */
u32 rx_counter;
/* Rx memory pool address */
struct wl1271_rx_mem_pool_addr rx_mem_pool_addr;
/* The target interrupt mask */
struct work_struct irq_work;
/* The mbox event mask */
u32 event_mask;
/* Mailbox pointers */
u32 mbox_ptr[2];
/* Are we currently scanning */
bool scanning;
/* Our association ID */
u16 aid;
/* Default key (for WEP) */
u32 default_key;
unsigned int rx_config;
unsigned int rx_filter;
/* is firmware in elp mode */
bool elp;
struct completion *elp_compl;
/* we can be in psm, but not in elp, we have to differentiate */
bool psm;
/* PSM mode requested */
bool psm_requested;
/* in dBm */
int power_level;
struct wl1271_stats stats;
struct wl1271_debugfs debugfs;
u32 buffer_32;
u32 buffer_cmd;
u8 buffer_busyword[WL1271_BUSY_WORD_LEN];
struct wl1271_rx_descriptor *rx_descriptor;
struct wl1271_fw_status *fw_status;
struct wl1271_tx_hw_res_if *tx_res_if;
};
int wl1271_plt_start(struct wl1271 *wl);
int wl1271_plt_stop(struct wl1271 *wl);
#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
#define SESSION_COUNTER_MAX 7 /* maximum value for the session counter */
#define WL1271_DEFAULT_POWER_LEVEL 0
#define WL1271_TX_QUEUE_MAX_LENGTH 20
/* WL1271 needs a 200ms sleep after power on */
#define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+72
View File
@@ -0,0 +1,72 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 "wl1271.h"
int wl1271_boot(struct wl1271 *wl);
#define WL1271_NO_SUBBANDS 8
#define WL1271_NO_POWER_LEVELS 4
#define WL1271_FW_VERSION_MAX_LEN 20
struct wl1271_static_data {
u8 mac_address[ETH_ALEN];
u8 padding[2];
u8 fw_version[WL1271_FW_VERSION_MAX_LEN];
u32 hw_version;
u8 tx_power_table[WL1271_NO_SUBBANDS][WL1271_NO_POWER_LEVELS];
};
/* number of times we try to read the INIT interrupt */
#define INIT_LOOP 20000
/* delay between retries */
#define INIT_LOOP_DELAY 50
#define REF_CLOCK 2
#define WU_COUNTER_PAUSE_VAL 0x3FF
#define WELP_ARM_COMMAND_VAL 0x4
#define OCP_CMD_LOOP 32
#define OCP_CMD_WRITE 0x1
#define OCP_CMD_READ 0x2
#define OCP_READY_MASK BIT(18)
#define OCP_STATUS_MASK (BIT(16) | BIT(17))
#define OCP_STATUS_NO_RESP 0x00000
#define OCP_STATUS_OK 0x10000
#define OCP_STATUS_REQ_FAILED 0x20000
#define OCP_STATUS_RESP_ERROR 0x30000
#define OCP_REG_POLARITY 0x30032
#define CMD_MBOX_ADDRESS 0x407B4
#define POLARITY_LOW BIT(1)
#endif
File diff suppressed because it is too large Load Diff
+464
View File
@@ -0,0 +1,464 @@
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 __WL1271_CMD_H__
#define __WL1271_CMD_H__
#include "wl1271.h"
struct acx_header;
int wl1271_cmd_send(struct wl1271 *wl, u16 type, void *buf, size_t buf_len);
int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
u16 beacon_interval, u8 wait);
int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable);
int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len);
int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
u8 active_scan, u8 high_prio, u8 num_channels,
u8 probe_requests);
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
void *buf, size_t buf_len);
int wl1271_cmd_build_null_data(struct wl1271 *wl);
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len);
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, const u8 *addr);
enum wl1271_commands {
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_START_JOIN = 11,
CMD_SET_KEYS = 12,
CMD_READ_MEMORY = 13,
CMD_WRITE_MEMORY = 14,
CMD_SET_TEMPLATE = 19,
CMD_TEST = 23,
CMD_NOISE_HIST = 28,
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,
CMD_CONNECTION_SCAN_CFG = 48,
CMD_CONNECTION_SCAN_SSID_CFG = 49,
CMD_START_PERIODIC_SCAN = 50,
CMD_STOP_PERIODIC_SCAN = 51,
CMD_SET_STA_STATE = 52,
NUM_COMMANDS,
MAX_COMMAND_ID = 0xFFFF,
};
#define MAX_CMD_PARAMS 572
enum cmd_templ {
CMD_TEMPL_NULL_DATA = 0,
CMD_TEMPL_BEACON,
CMD_TEMPL_CFG_PROBE_REQ_2_4,
CMD_TEMPL_CFG_PROBE_REQ_5,
CMD_TEMPL_PROBE_RESPONSE,
CMD_TEMPL_QOS_NULL_DATA,
CMD_TEMPL_PS_POLL,
CMD_TEMPL_KLV,
CMD_TEMPL_DISCONNECT,
CMD_TEMPL_PROBE_REQ_2_4, /* for firmware internal use only */
CMD_TEMPL_PROBE_REQ_5, /* for firmware internal use only */
CMD_TEMPL_BAR, /* for firmware internal use only */
CMD_TEMPL_CTS, /*
* For CTS-to-self (FastCTS) mechanism
* for BT/WLAN coexistence (SoftGemini). */
CMD_TEMPL_MAX = 0xff
};
/* unit ms */
#define WL1271_COMMAND_TIMEOUT 2000
#define WL1271_CMD_TEMPL_MAX_SIZE 252
struct wl1271_cmd_header {
u16 id;
u16 status;
/* payload */
u8 data[0];
} __attribute__ ((packed));
#define WL1271_CMD_MAX_PARAMS 572
struct wl1271_command {
struct wl1271_cmd_header header;
u8 parameters[WL1271_CMD_MAX_PARAMS];
} __attribute__ ((packed));
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 {
struct wl1271_cmd_header header;
/* 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
enum {
BSS_TYPE_IBSS = 0,
BSS_TYPE_STA_BSS = 2,
BSS_TYPE_AP_BSS = 3,
MAX_BSS_TYPE = 0xFF
};
#define WL1271_JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */
#define WL1271_JOIN_CMD_TX_SESSION_OFFSET 1
struct wl1271_cmd_join {
struct wl1271_cmd_header header;
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).
*/
u32 basic_rate_set;
u8 dtim_interval;
/*
* 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 reserved[3];
} __attribute__ ((packed));
struct cmd_enabledisable_path {
struct wl1271_cmd_header header;
u8 channel;
u8 padding[3];
} __attribute__ ((packed));
struct wl1271_cmd_template_set {
struct wl1271_cmd_header header;
u16 len;
u8 template_type;
u8 index; /* relevant only for KLV_TEMPLATE type */
u32 enabled_rates;
u8 short_retry_limit;
u8 long_retry_limit;
u8 aflags;
u8 reserved;
u8 template_data[WL1271_CMD_TEMPL_MAX_SIZE];
} __attribute__ ((packed));
#define TIM_ELE_ID 5
#define PARTIAL_VBM_MAX 251
struct wl1271_tim {
u8 identity;
u8 length;
u8 dtim_count;
u8 dtim_period;
u8 bitmap_ctrl;
u8 pvb_field[PARTIAL_VBM_MAX]; /* Partial Virtual Bitmap */
} __attribute__ ((packed));
enum wl1271_cmd_ps_mode {
STATION_ACTIVE_MODE,
STATION_POWER_SAVE_MODE
};
struct wl1271_cmd_ps_params {
struct wl1271_cmd_header header;
u8 ps_mode; /* STATION_* */
u8 send_null_data; /* Do we have to send NULL data packet ? */
u8 retries; /* Number of retires for the initial NULL data packet */
/*
* TUs during which the target stays awake after switching
* to power save mode.
*/
u8 hang_over_period;
u32 null_data_rate;
} __attribute__ ((packed));
/* HW encryption keys */
#define NUM_ACCESS_CATEGORIES_COPY 4
#define MAX_KEY_SIZE 32
/* When set, disable HW encryption */
#define DF_ENCRYPTION_DISABLE 0x01
/* When set, disable HW decryption */
#define DF_SNIFF_MODE_ENABLE 0x80
enum wl1271_cmd_key_action {
KEY_ADD_OR_REPLACE = 1,
KEY_REMOVE = 2,
KEY_SET_ID = 3,
MAX_KEY_ACTION = 0xffff,
};
enum wl1271_cmd_key_type {
KEY_NONE = 0,
KEY_WEP = 1,
KEY_TKIP = 2,
KEY_AES = 3,
KEY_GEM = 4
};
/* FIXME: Add description for key-types */
struct wl1271_cmd_set_keys {
struct wl1271_cmd_header header;
/* Ignored for default WEP key */
u8 addr[ETH_ALEN];
/* key_action_e */
u16 key_action;
u16 reserved_1;
/* key size in bytes */
u8 key_size;
/* key_type_e */
u8 key_type;
u8 ssid_profile;
/*
* TKIP, AES: frame's key id field.
* For WEP default key: key id;
*/
u8 id;
u8 reserved_2[6];
u8 key[MAX_KEY_SIZE];
u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __attribute__ ((packed));
#define WL1271_SCAN_MAX_CHANNELS 24
#define WL1271_SCAN_DEFAULT_TAG 1
#define WL1271_SCAN_CURRENT_TX_PWR 0
#define WL1271_SCAN_OPT_ACTIVE 0
#define WL1271_SCAN_OPT_PASSIVE 1
#define WL1271_SCAN_OPT_PRIORITY_HIGH 4
#define WL1271_SCAN_CHAN_MIN_DURATION 30000 /* TU */
#define WL1271_SCAN_CHAN_MAX_DURATION 60000 /* TU */
struct basic_scan_params {
u32 rx_config_options;
u32 rx_filter_options;
/* Scan option flags (WL1271_SCAN_OPT_*) */
u16 scan_options;
/* Number of scan channels in the list (maximum 30) */
u8 num_channels;
/* This field indicates the number of probe requests to send
per channel for an active scan */
u8 num_probe_requests;
/* Rate bit field for sending the probes */
u32 tx_rate;
u8 tid_trigger;
u8 ssid_len;
/* in order to align */
u8 padding1[2];
u8 ssid[IW_ESSID_MAX_SIZE];
/* Band to scan */
u8 band;
u8 use_ssid_list;
u8 scan_tag;
u8 padding2;
} __attribute__ ((packed));
struct basic_scan_channel_params {
/* Duration in TU to wait for frames on a channel for active scan */
u32 min_duration;
u32 max_duration;
u32 bssid_lsb;
u16 bssid_msb;
u8 early_termination;
u8 tx_power_att;
u8 channel;
/* FW internal use only! */
u8 dfs_candidate;
u8 activity_detected;
u8 pad;
} __attribute__ ((packed));
struct wl1271_cmd_scan {
struct wl1271_cmd_header header;
struct basic_scan_params params;
struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
} __attribute__ ((packed));
struct wl1271_cmd_trigger_scan_to {
struct wl1271_cmd_header header;
u32 timeout;
};
struct wl1271_cmd_test_header {
u8 id;
u8 padding[3];
};
enum wl1271_channel_tune_bands {
WL1271_CHANNEL_TUNE_BAND_2_4,
WL1271_CHANNEL_TUNE_BAND_5,
WL1271_CHANNEL_TUNE_BAND_4_9
};
#define WL1271_PD_REFERENCE_POINT_BAND_B_G 0
#define TEST_CMD_P2G_CAL 0x02
#define TEST_CMD_CHANNEL_TUNE 0x0d
#define TEST_CMD_UPDATE_PD_REFERENCE_POINT 0x1d
struct wl1271_cmd_cal_channel_tune {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
u8 band;
u8 channel;
u16 radio_status;
} __attribute__ ((packed));
struct wl1271_cmd_cal_update_ref_point {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
s32 ref_power;
s32 ref_detector;
u8 sub_band;
u8 padding[3];
} __attribute__ ((packed));
#define MAX_TLV_LENGTH 400
#define MAX_NVS_VERSION_LENGTH 12
#define WL1271_CAL_P2G_BAND_B_G BIT(0)
struct wl1271_cmd_cal_p2g {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
u16 len;
u8 buf[MAX_TLV_LENGTH];
u8 type;
u8 padding;
s16 radio_status;
u8 nvs_version[MAX_NVS_VERSION_LENGTH];
u8 sub_band_mask;
u8 padding2;
} __attribute__ ((packed));
#endif /* __WL1271_CMD_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,33 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 WL1271_DEBUGFS_H
#define WL1271_DEBUGFS_H
#include "wl1271.h"
int wl1271_debugfs_init(struct wl1271 *wl);
void wl1271_debugfs_exit(struct wl1271 *wl);
void wl1271_debugfs_reset(struct wl1271 *wl);
#endif /* WL1271_DEBUGFS_H */
+125
View File
@@ -0,0 +1,125 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 "wl1271.h"
#include "wl1271_reg.h"
#include "wl1271_spi.h"
#include "wl1271_event.h"
#include "wl1271_ps.h"
static int wl1271_event_scan_complete(struct wl1271 *wl,
struct event_mailbox *mbox)
{
wl1271_debug(DEBUG_EVENT, "status: 0x%x",
mbox->scheduled_scan_status);
if (wl->scanning) {
mutex_unlock(&wl->mutex);
ieee80211_scan_completed(wl->hw, false);
mutex_lock(&wl->mutex);
wl->scanning = false;
}
return 0;
}
static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
{
wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
}
static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
{
int ret;
u32 vector;
wl1271_event_mbox_dump(mbox);
vector = mbox->events_vector & ~(mbox->events_mask);
wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) {
ret = wl1271_event_scan_complete(wl, mbox);
if (ret < 0)
return ret;
}
if (vector & BSS_LOSE_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
if (wl->psm_requested && wl->psm) {
ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
}
}
return 0;
}
int wl1271_event_unmask(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask));
if (ret < 0)
return ret;
return 0;
}
void wl1271_event_mbox_config(struct wl1271 *wl)
{
wl->mbox_ptr[0] = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]);
}
int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
{
struct event_mailbox mbox;
int ret;
wl1271_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num);
if (mbox_num > 1)
return -EINVAL;
/* first we read the mbox descriptor */
wl1271_spi_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox,
sizeof(struct event_mailbox));
/* process the descriptor */
ret = wl1271_event_process(wl, &mbox);
if (ret < 0)
return ret;
/* then we let the firmware know it can go on...*/
wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK);
return 0;
}
+110
View File
@@ -0,0 +1,110 @@
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 __WL1271_EVENT_H__
#define __WL1271_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 {
MEASUREMENT_START_EVENT_ID = BIT(8),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
SCAN_COMPLETE_EVENT_ID = BIT(10),
SCHEDULED_SCAN_COMPLETE_EVENT_ID = BIT(11),
AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12),
PS_REPORT_EVENT_ID = BIT(13),
PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14),
DISCONNECT_EVENT_COMPLETE_ID = BIT(15),
JOIN_EVENT_COMPLETE_ID = BIT(16),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19),
ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20),
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23),
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25),
DBG_EVENT_ID = BIT(26),
HEALTH_CHECK_REPLY_EVENT_ID = BIT(27),
PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28),
PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29),
BA_SESSION_TEAR_DOWN_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));
#define NUM_OF_RSSI_SNR_TRIGGERS 8
struct event_mailbox {
u32 events_vector;
u32 events_mask;
u32 reserved_1;
u32 reserved_2;
u8 dbg_event_id;
u8 num_relevant_params;
u16 reserved_3;
u32 event_report_p1;
u32 event_report_p2;
u32 event_report_p3;
u8 number_of_scan_results;
u8 scan_tag;
u8 reserved_4[2];
u32 compl_scheduled_scan_status;
u16 scheduled_scan_attended_channels;
u8 soft_gemini_sense_info;
u8 soft_gemini_protective_info;
s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
u8 channel_switch_status;
u8 scheduled_scan_status;
u8 ps_status;
u8 reserved_5[29];
} __attribute__ ((packed));
int wl1271_event_unmask(struct wl1271 *wl);
void wl1271_event_mbox_config(struct wl1271 *wl);
int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
#endif
+397
View File
@@ -0,0 +1,397 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 "wl1271_init.h"
#include "wl12xx_80211.h"
#include "wl1271_acx.h"
#include "wl1271_cmd.h"
#include "wl1271_reg.h"
static int wl1271_init_hwenc_config(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_feature_cfg(wl);
if (ret < 0) {
wl1271_warning("couldn't set feature config");
return ret;
}
ret = wl1271_cmd_set_default_wep_key(wl, wl->default_key);
if (ret < 0) {
wl1271_warning("couldn't set default key");
return ret;
}
return 0;
}
static int wl1271_init_templates_config(struct wl1271 *wl)
{
int ret;
/* send empty templates for fw memory reservation */
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
sizeof(struct wl12xx_probe_req_template));
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
sizeof(struct wl12xx_null_data_template));
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, NULL,
sizeof(struct wl12xx_ps_poll_template));
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL,
sizeof
(struct wl12xx_qos_null_data_template));
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL,
sizeof
(struct wl12xx_probe_resp_template));
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL,
sizeof
(struct wl12xx_beacon_template));
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter)
{
int ret;
ret = wl1271_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF);
if (ret < 0)
return ret;
ret = wl1271_acx_rx_config(wl, config, filter);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_phy_config(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_pd_threshold(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_slot(wl, DEFAULT_SLOT_TIME);
if (ret < 0)
return ret;
ret = wl1271_acx_group_address_tbl(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_service_period_timeout(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_rts_threshold(wl, RTS_THRESHOLD_DEF);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_beacon_filter(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_beacon_filter_opt(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_beacon_filter_table(wl);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_pta(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_sg_enable(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_sg_cfg(wl);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_energy_detection(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_cca_threshold(wl);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_bcn_dtim_options(wl);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_general_parms(struct wl1271 *wl)
{
struct wl1271_general_parms *gen_parms;
int ret;
gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL);
if (!gen_parms)
return -ENOMEM;
gen_parms->id = TEST_CMD_INI_FILE_GENERAL_PARAM;
gen_parms->ref_clk = REF_CLK_38_4_E;
/* FIXME: magic numbers */
gen_parms->settling_time = 5;
gen_parms->clk_valid_on_wakeup = 0;
gen_parms->dc2dcmode = 0;
gen_parms->single_dual_band = 0;
gen_parms->tx_bip_fem_autodetect = 1;
gen_parms->tx_bip_fem_manufacturer = 1;
gen_parms->settings = 1;
ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0);
if (ret < 0) {
wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
return ret;
}
kfree(gen_parms);
return 0;
}
static int wl1271_init_radio_parms(struct wl1271 *wl)
{
/*
* FIXME: All these magic numbers should be moved to some place where
* they can be configured (separate file?)
*/
struct wl1271_radio_parms *radio_parms;
int ret;
u8 compensation[] = { 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, 0xfc, 0x00,
0x08, 0x10, 0xf0, 0xf8, 0x00, 0x0a, 0x14 };
u8 tx_rate_limits_normal[] = { 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 };
u8 tx_rate_limits_degraded[] = { 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 };
u8 tx_channel_limits_11b[] = { 0x22, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x22, 0x50,
0x22, 0x50 };
u8 tx_channel_limits_ofdm[] = { 0x20, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x20, 0x50,
0x20, 0x50 };
u8 tx_pdv_rate_offsets[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
u8 tx_ibias[] = { 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 };
radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL);
if (!radio_parms)
return -ENOMEM;
radio_parms->id = TEST_CMD_INI_FILE_RADIO_PARAM;
/* Static radio parameters */
radio_parms->rx_trace_loss = 10;
radio_parms->tx_trace_loss = 10;
memcpy(radio_parms->rx_rssi_and_proc_compens, compensation,
sizeof(compensation));
/* We don't set the 5GHz -- N/A */
/* Dynamic radio parameters */
radio_parms->tx_ref_pd_voltage = cpu_to_le16(0x24e);
radio_parms->tx_ref_power = 0x78;
radio_parms->tx_offset_db = 0x0;
memcpy(radio_parms->tx_rate_limits_normal, tx_rate_limits_normal,
sizeof(tx_rate_limits_normal));
memcpy(radio_parms->tx_rate_limits_degraded, tx_rate_limits_degraded,
sizeof(tx_rate_limits_degraded));
memcpy(radio_parms->tx_channel_limits_11b, tx_channel_limits_11b,
sizeof(tx_channel_limits_11b));
memcpy(radio_parms->tx_channel_limits_ofdm, tx_channel_limits_ofdm,
sizeof(tx_channel_limits_ofdm));
memcpy(radio_parms->tx_pdv_rate_offsets, tx_pdv_rate_offsets,
sizeof(tx_pdv_rate_offsets));
memcpy(radio_parms->tx_ibias, tx_ibias,
sizeof(tx_ibias));
radio_parms->rx_fem_insertion_loss = 0x14;
ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0);
if (ret < 0)
wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed");
kfree(radio_parms);
return ret;
}
int wl1271_hw_init(struct wl1271 *wl)
{
int ret;
ret = wl1271_init_general_parms(wl);
if (ret < 0)
return ret;
ret = wl1271_init_radio_parms(wl);
if (ret < 0)
return ret;
/* Template settings */
ret = wl1271_init_templates_config(wl);
if (ret < 0)
return ret;
/* Default memory configuration */
ret = wl1271_acx_init_mem_config(wl);
if (ret < 0)
return ret;
/* RX config */
ret = wl1271_init_rx_config(wl,
RX_CFG_PROMISCUOUS | RX_CFG_TSF,
RX_FILTER_OPTION_DEF);
/* RX_CONFIG_OPTION_ANY_DST_ANY_BSS,
RX_FILTER_OPTION_FILTER_ALL); */
if (ret < 0)
goto out_free_memmap;
/* PHY layer config */
ret = wl1271_init_phy_config(wl);
if (ret < 0)
goto out_free_memmap;
/* Beacon filtering */
ret = wl1271_init_beacon_filter(wl);
if (ret < 0)
goto out_free_memmap;
/* Configure TX patch complete interrupt behavior */
ret = wl1271_acx_tx_config_options(wl);
if (ret < 0)
goto out_free_memmap;
/* RX complete interrupt pacing */
ret = wl1271_acx_init_rx_interrupt(wl);
if (ret < 0)
goto out_free_memmap;
/* Bluetooth WLAN coexistence */
ret = wl1271_init_pta(wl);
if (ret < 0)
goto out_free_memmap;
/* Energy detection */
ret = wl1271_init_energy_detection(wl);
if (ret < 0)
goto out_free_memmap;
/* Beacons and boradcast settings */
ret = wl1271_init_beacon_broadcast(wl);
if (ret < 0)
goto out_free_memmap;
/* Default fragmentation threshold */
ret = wl1271_acx_frag_threshold(wl);
if (ret < 0)
goto out_free_memmap;
/* Default TID configuration */
ret = wl1271_acx_tid_cfg(wl);
if (ret < 0)
goto out_free_memmap;
/* Default AC configuration */
ret = wl1271_acx_ac_cfg(wl);
if (ret < 0)
goto out_free_memmap;
/* Configure TX rate classes */
ret = wl1271_acx_rate_policies(wl);
if (ret < 0)
goto out_free_memmap;
/* Enable data path */
ret = wl1271_cmd_data_path(wl, wl->channel, 1);
if (ret < 0)
goto out_free_memmap;
/* Configure for ELP power saving */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
if (ret < 0)
goto out_free_memmap;
/* Configure HW encryption */
ret = wl1271_init_hwenc_config(wl);
if (ret < 0)
goto out_free_memmap;
return 0;
out_free_memmap:
kfree(wl->target_mem_map);
return ret;
}
+115
View File
@@ -0,0 +1,115 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 __WL1271_INIT_H__
#define __WL1271_INIT_H__
#include "wl1271.h"
int wl1271_hw_init_power_auth(struct wl1271 *wl);
int wl1271_hw_init(struct wl1271 *wl);
/* These are not really a TEST_CMD, but the ref driver uses them as such */
#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19
#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E
struct wl1271_general_parms {
u8 id;
u8 padding[3];
u8 ref_clk;
u8 settling_time;
u8 clk_valid_on_wakeup;
u8 dc2dcmode;
u8 single_dual_band;
u8 tx_bip_fem_autodetect;
u8 tx_bip_fem_manufacturer;
u8 settings;
} __attribute__ ((packed));
enum ref_clk_enum {
REF_CLK_19_2_E,
REF_CLK_26_E,
REF_CLK_38_4_E,
REF_CLK_52_E
};
#define RSSI_AND_PROCESS_COMPENSATION_SIZE 15
#define NUMBER_OF_SUB_BANDS_5 7
#define NUMBER_OF_RATE_GROUPS 6
#define NUMBER_OF_CHANNELS_2_4 14
#define NUMBER_OF_CHANNELS_5 35
struct wl1271_radio_parms {
u8 id;
u8 padding[3];
/* Static radio parameters */
/* 2.4GHz */
u8 rx_trace_loss;
u8 tx_trace_loss;
s8 rx_rssi_and_proc_compens[RSSI_AND_PROCESS_COMPENSATION_SIZE];
/* 5GHz */
u8 rx_trace_loss_5[NUMBER_OF_SUB_BANDS_5];
u8 tx_trace_loss_5[NUMBER_OF_SUB_BANDS_5];
s8 rx_rssi_and_proc_compens_5[RSSI_AND_PROCESS_COMPENSATION_SIZE];
/* Dynamic radio parameters */
/* 2.4GHz */
s16 tx_ref_pd_voltage;
s8 tx_ref_power;
s8 tx_offset_db;
s8 tx_rate_limits_normal[NUMBER_OF_RATE_GROUPS];
s8 tx_rate_limits_degraded[NUMBER_OF_RATE_GROUPS];
s8 tx_channel_limits_11b[NUMBER_OF_CHANNELS_2_4];
s8 tx_channel_limits_ofdm[NUMBER_OF_CHANNELS_2_4];
s8 tx_pdv_rate_offsets[NUMBER_OF_RATE_GROUPS];
u8 tx_ibias[NUMBER_OF_RATE_GROUPS];
u8 rx_fem_insertion_loss;
u8 padding2;
/* 5GHz */
s16 tx_ref_pd_voltage_5[NUMBER_OF_SUB_BANDS_5];
s8 tx_ref_power_5[NUMBER_OF_SUB_BANDS_5];
s8 tx_offset_db_5[NUMBER_OF_SUB_BANDS_5];
s8 tx_rate_limits_normal_5[NUMBER_OF_RATE_GROUPS];
s8 tx_rate_limits_degraded_5[NUMBER_OF_RATE_GROUPS];
s8 tx_channel_limits_ofdm_5[NUMBER_OF_CHANNELS_5];
s8 tx_pdv_rate_offsets_5[NUMBER_OF_RATE_GROUPS];
/* FIXME: this is inconsistent with the types for 2.4GHz */
s8 tx_ibias_5[NUMBER_OF_RATE_GROUPS];
s8 rx_fem_insertion_loss_5[NUMBER_OF_SUB_BANDS_5];
u8 padding3[2];
} __attribute__ ((packed));
#endif
File diff suppressed because it is too large Load Diff
+142
View File
@@ -0,0 +1,142 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 "wl1271_reg.h"
#include "wl1271_ps.h"
#include "wl1271_spi.h"
#define WL1271_WAKEUP_TIMEOUT 500
/* Routines to toggle sleep mode while in ELP */
void wl1271_ps_elp_sleep(struct wl1271 *wl)
{
/*
* FIXME: due to a problem in the firmware (causing a firmware
* crash), ELP entry is prevented below. Remove the "true" to
* re-enable ELP entry.
*/
if (true || wl->elp || !wl->psm)
return;
/*
* Go to ELP unless there is work already pending - pending work
* will immediately wakeup the chipset anyway.
*/
if (!work_pending(&wl->irq_work) && !work_pending(&wl->tx_work)) {
wl1271_debug(DEBUG_PSM, "chip to elp");
wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
wl->elp = true;
}
}
int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake)
{
DECLARE_COMPLETION_ONSTACK(compl);
unsigned long flags;
int ret;
u32 start_time = jiffies;
bool pending = false;
if (!wl->elp)
return 0;
wl1271_debug(DEBUG_PSM, "waking up chip from elp");
/*
* The spinlock is required here to synchronize both the work and
* the completion variable in one entity.
*/
spin_lock_irqsave(&wl->wl_lock, flags);
if (work_pending(&wl->irq_work) || chip_awake)
pending = true;
else
wl->elp_compl = &compl;
spin_unlock_irqrestore(&wl->wl_lock, flags);
wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
if (!pending) {
ret = wait_for_completion_timeout(
&compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
if (ret == 0) {
wl1271_error("ELP wakeup timeout!");
ret = -ETIMEDOUT;
goto err;
} else if (ret < 0) {
wl1271_error("ELP wakeup completion error.");
goto err;
}
}
wl->elp = false;
wl1271_debug(DEBUG_PSM, "wakeup time: %u ms",
jiffies_to_msecs(jiffies - start_time));
goto out;
err:
spin_lock_irqsave(&wl->wl_lock, flags);
wl->elp_compl = NULL;
spin_unlock_irqrestore(&wl->wl_lock, flags);
return ret;
out:
return 0;
}
int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
{
int ret;
switch (mode) {
case STATION_POWER_SAVE_MODE:
wl1271_debug(DEBUG_PSM, "entering psm");
ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
if (ret < 0)
return ret;
wl1271_ps_elp_sleep(wl);
if (ret < 0)
return ret;
wl->psm = 1;
break;
case STATION_ACTIVE_MODE:
default:
wl1271_debug(DEBUG_PSM, "leaving psm");
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
return ret;
ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
wl->psm = 0;
break;
}
return ret;
}
+35
View File
@@ -0,0 +1,35 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 __WL1271_PS_H__
#define __WL1271_PS_H__
#include "wl1271.h"
#include "wl1271_acx.h"
int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode);
void wl1271_ps_elp_sleep(struct wl1271 *wl);
int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake);
#endif /* __WL1271_PS_H__ */
File diff suppressed because it is too large Load Diff
+200
View File
@@ -0,0 +1,200 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 "wl1271.h"
#include "wl1271_acx.h"
#include "wl1271_reg.h"
#include "wl1271_rx.h"
#include "wl1271_spi.h"
static u8 wl1271_rx_get_mem_block(struct wl1271_fw_status *status,
u32 drv_rx_counter)
{
return status->rx_pkt_descs[drv_rx_counter] & RX_MEM_BLOCK_MASK;
}
static u32 wl1271_rx_get_buf_size(struct wl1271_fw_status *status,
u32 drv_rx_counter)
{
return (status->rx_pkt_descs[drv_rx_counter] & RX_BUF_SIZE_MASK) >>
RX_BUF_SIZE_SHIFT_DIV;
}
/* The values of this table must match the wl1271_rates[] array */
static u8 wl1271_rx_rate_to_idx[] = {
/* MCS rates are used only with 11n */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS7 */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS6 */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS5 */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS4 */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS3 */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS2 */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS1 */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS0 */
11, /* WL1271_RATE_54 */
10, /* WL1271_RATE_48 */
9, /* WL1271_RATE_36 */
8, /* WL1271_RATE_24 */
/* TI-specific rate */
WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_22 */
7, /* WL1271_RATE_18 */
6, /* WL1271_RATE_12 */
3, /* WL1271_RATE_11 */
5, /* WL1271_RATE_9 */
4, /* WL1271_RATE_6 */
2, /* WL1271_RATE_5_5 */
1, /* WL1271_RATE_2 */
0 /* WL1271_RATE_1 */
};
static void wl1271_rx_status(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct ieee80211_rx_status *status,
u8 beacon)
{
memset(status, 0, sizeof(struct ieee80211_rx_status));
if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG)
status->band = IEEE80211_BAND_2GHZ;
else
wl1271_warning("unsupported band 0x%x",
desc->flags & WL1271_RX_DESC_BAND_MASK);
/*
* FIXME: Add mactime handling. For IBSS (ad-hoc) we need to get the
* timestamp from the beacon (acx_tsf_info). In BSS mode (infra) we
* only need the mactime for monitor mode. For now the mactime is
* not valid, so RX_FLAG_TSFT should not be set
*/
status->signal = desc->rssi;
/* FIXME: Should this be optimized? */
status->qual = (desc->rssi - WL1271_RX_MIN_RSSI) * 100 /
(WL1271_RX_MAX_RSSI - WL1271_RX_MIN_RSSI);
status->qual = min(status->qual, 100);
status->qual = max(status->qual, 0);
/*
* FIXME: In wl1251, the SNR should be divided by two. In wl1271 we
* need to divide by two for now, but TI has been discussing about
* changing it. This needs to be rechecked.
*/
status->noise = desc->rssi - (desc->snr >> 1);
status->freq = ieee80211_channel_to_frequency(desc->channel);
if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) {
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
if (likely(!(desc->flags & WL1271_RX_DESC_DECRYPT_FAIL)))
status->flag |= RX_FLAG_DECRYPTED;
if (unlikely(desc->flags & WL1271_RX_DESC_MIC_FAIL))
status->flag |= RX_FLAG_MMIC_ERROR;
}
status->rate_idx = wl1271_rx_rate_to_idx[desc->rate];
if (status->rate_idx == WL1271_RX_RATE_UNSUPPORTED)
wl1271_warning("unsupported rate");
}
static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
{
struct ieee80211_rx_status rx_status;
struct wl1271_rx_descriptor *desc;
struct sk_buff *skb;
u16 *fc;
u8 *buf;
u8 beacon = 0;
skb = dev_alloc_skb(length);
if (!skb) {
wl1271_error("Couldn't allocate RX frame");
return;
}
buf = skb_put(skb, length);
wl1271_spi_reg_read(wl, WL1271_SLV_MEM_DATA, buf, length, true);
/* the data read starts with the descriptor */
desc = (struct wl1271_rx_descriptor *) buf;
/* now we pull the descriptor out of the buffer */
skb_pull(skb, sizeof(*desc));
fc = (u16 *)skb->data;
if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
beacon = 1;
wl1271_rx_status(wl, desc, &rx_status, beacon);
wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len,
beacon ? "beacon" : "");
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
ieee80211_rx(wl->hw, skb);
}
void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
{
struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
u32 buf_size;
u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
u32 mem_block;
while (drv_rx_counter != fw_rx_counter) {
mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter);
buf_size = wl1271_rx_get_buf_size(status, drv_rx_counter);
if (buf_size == 0) {
wl1271_warning("received empty data");
break;
}
wl->rx_mem_pool_addr.addr =
(mem_block << 8) + wl_mem_map->packet_memory_pool_start;
wl->rx_mem_pool_addr.addr_extra =
wl->rx_mem_pool_addr.addr + 4;
/* Choose the block we want to read */
wl1271_spi_reg_write(wl, WL1271_SLV_REG_DATA,
&wl->rx_mem_pool_addr,
sizeof(wl->rx_mem_pool_addr), false);
wl1271_rx_handle_data(wl, buf_size);
wl->rx_counter++;
drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
}
wl1271_reg_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
/* This is a workaround for some problems in the chip */
wl1271_reg_write32(wl, RX_DRIVER_DUMMY_WRITE_ADDRESS, 0x1);
}
+121
View File
@@ -0,0 +1,121 @@
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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 __WL1271_RX_H__
#define __WL1271_RX_H__
#include <linux/bitops.h>
#define WL1271_RX_MAX_RSSI -30
#define WL1271_RX_MIN_RSSI -95
#define WL1271_RX_ALIGN_TO 4
#define WL1271_RX_ALIGN(len) (((len) + WL1271_RX_ALIGN_TO - 1) & \
~(WL1271_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 NUM_RX_PKT_DESC_MOD_MASK 7
#define WL1271_RX_RATE_UNSUPPORTED 0xFF
#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
/*
* RX Descriptor flags:
*
* Bits 0-1 - band
* Bit 2 - STBC
* Bit 3 - A-MPDU
* Bit 4 - HT
* Bits 5-7 - encryption
*/
#define WL1271_RX_DESC_BAND_MASK 0x03
#define WL1271_RX_DESC_ENCRYPT_MASK 0xE0
#define WL1271_RX_DESC_BAND_BG 0x00
#define WL1271_RX_DESC_BAND_J 0x01
#define WL1271_RX_DESC_BAND_A 0x02
#define WL1271_RX_DESC_STBC BIT(2)
#define WL1271_RX_DESC_A_MPDU BIT(3)
#define WL1271_RX_DESC_HT BIT(4)
#define WL1271_RX_DESC_ENCRYPT_WEP 0x20
#define WL1271_RX_DESC_ENCRYPT_TKIP 0x40
#define WL1271_RX_DESC_ENCRYPT_AES 0x60
#define WL1271_RX_DESC_ENCRYPT_GEM 0x80
/*
* RX Descriptor status
*
* Bits 0-2 - status
* Bits 3-7 - reserved
*/
#define WL1271_RX_DESC_STATUS_MASK 0x07
#define WL1271_RX_DESC_SUCCESS 0x00
#define WL1271_RX_DESC_DECRYPT_FAIL 0x01
#define WL1271_RX_DESC_MIC_FAIL 0x02
#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03
#define RX_MEM_BLOCK_MASK 0xFF
#define RX_BUF_SIZE_MASK 0xFFF00
#define RX_BUF_SIZE_SHIFT_DIV 6
struct wl1271_rx_descriptor {
u16 length;
u8 status;
u8 flags;
u8 rate;
u8 channel;
s8 rssi;
u8 snr;
u32 timestamp;
u8 packet_class;
u8 process_id;
u8 pad_len;
u8 reserved;
} __attribute__ ((packed));
void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status);
#endif
+382
View File
@@ -0,0 +1,382 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@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/module.h>
#include <linux/platform_device.h>
#include <linux/crc7.h>
#include <linux/spi/spi.h>
#include "wl1271.h"
#include "wl12xx_80211.h"
#include "wl1271_spi.h"
static int wl1271_translate_reg_addr(struct wl1271 *wl, int addr)
{
return addr - wl->physical_reg_addr + wl->virtual_reg_addr;
}
static int wl1271_translate_mem_addr(struct wl1271 *wl, int addr)
{
return addr - wl->physical_mem_addr + wl->virtual_mem_addr;
}
void wl1271_spi_reset(struct wl1271 *wl)
{
u8 *cmd;
struct spi_transfer t;
struct spi_message m;
cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
if (!cmd) {
wl1271_error("could not allocate cmd for spi reset");
return;
}
memset(&t, 0, sizeof(t));
spi_message_init(&m);
memset(cmd, 0xff, WSPI_INIT_CMD_LEN);
t.tx_buf = cmd;
t.len = WSPI_INIT_CMD_LEN;
spi_message_add_tail(&t, &m);
spi_sync(wl->spi, &m);
wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN);
}
void wl1271_spi_init(struct wl1271 *wl)
{
u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd;
struct spi_transfer t;
struct spi_message m;
cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
if (!cmd) {
wl1271_error("could not allocate cmd for spi init");
return;
}
memset(crc, 0, sizeof(crc));
memset(&t, 0, sizeof(t));
spi_message_init(&m);
/*
* Set WSPI_INIT_COMMAND
* the data is being send from the MSB to LSB
*/
cmd[2] = 0xff;
cmd[3] = 0xff;
cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
cmd[0] = 0;
cmd[7] = 0;
cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
else
cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
| WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
crc[0] = cmd[1];
crc[1] = cmd[0];
crc[2] = cmd[7];
crc[3] = cmd[6];
crc[4] = cmd[5];
cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1;
cmd[4] |= WSPI_INIT_CMD_END;
t.tx_buf = cmd;
t.len = WSPI_INIT_CMD_LEN;
spi_message_add_tail(&t, &m);
spi_sync(wl->spi, &m);
wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN);
}
/* Set the SPI partitions to access the chip addresses
*
* There are two VIRTUAL (SPI) partitions (the memory partition and the
* registers partition), which are mapped to two different areas of the
* PHYSICAL (hardware) memory. This function also makes other checks to
* ensure that the partitions are not overlapping. In the diagram below, the
* memory partition comes before the register partition, but the opposite is
* also supported.
*
* PHYSICAL address
* space
*
* | |
* ...+----+--> mem_start
* VIRTUAL address ... | |
* space ... | | [PART_0]
* ... | |
* 0x00000000 <--+----+... ...+----+--> mem_start + mem_size
* | | ... | |
* |MEM | ... | |
* | | ... | |
* part_size <--+----+... | | {unused area)
* | | ... | |
* |REG | ... | |
* part_size | | ... | |
* + <--+----+... ...+----+--> reg_start
* reg_size ... | |
* ... | | [PART_1]
* ... | |
* ...+----+--> reg_start + reg_size
* | |
*
*/
int wl1271_set_partition(struct wl1271 *wl,
u32 mem_start, u32 mem_size,
u32 reg_start, u32 reg_size)
{
struct wl1271_partition *partition;
struct spi_transfer t;
struct spi_message m;
size_t len, cmd_len;
u32 *cmd;
int addr;
cmd_len = sizeof(u32) + 2 * sizeof(struct wl1271_partition);
cmd = kzalloc(cmd_len, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
spi_message_init(&m);
memset(&t, 0, sizeof(t));
partition = (struct wl1271_partition *) (cmd + 1);
addr = HW_ACCESS_PART0_SIZE_ADDR;
len = 2 * sizeof(struct wl1271_partition);
*cmd |= WSPI_CMD_WRITE;
*cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH;
*cmd |= addr & WSPI_CMD_BYTE_ADDR;
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
/* Make sure that the two partitions together don't exceed the
* address range */
if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) {
wl1271_debug(DEBUG_SPI, "Total size exceeds maximum virtual"
" address range. Truncating partition[0].");
mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size;
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
}
if ((mem_start < reg_start) &&
((mem_start + mem_size) > reg_start)) {
/* Guarantee that the memory partition doesn't overlap the
* registers partition */
wl1271_debug(DEBUG_SPI, "End of partition[0] is "
"overlapping partition[1]. Adjusted.");
mem_size = reg_start - mem_start;
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
} else if ((reg_start < mem_start) &&
((reg_start + reg_size) > mem_start)) {
/* Guarantee that the register partition doesn't overlap the
* memory partition */
wl1271_debug(DEBUG_SPI, "End of partition[1] is"
" overlapping partition[0]. Adjusted.");
reg_size = mem_start - reg_start;
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
}
partition[0].start = mem_start;
partition[0].size = mem_size;
partition[1].start = reg_start;
partition[1].size = reg_size;
wl->physical_mem_addr = mem_start;
wl->physical_reg_addr = reg_start;
wl->virtual_mem_addr = 0;
wl->virtual_reg_addr = mem_size;
t.tx_buf = cmd;
t.len = cmd_len;
spi_message_add_tail(&t, &m);
spi_sync(wl->spi, &m);
kfree(cmd);
return 0;
}
void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
struct spi_transfer t[3];
struct spi_message m;
u8 *busy_buf;
u32 *cmd;
cmd = &wl->buffer_cmd;
busy_buf = wl->buffer_busyword;
*cmd = 0;
*cmd |= WSPI_CMD_READ;
*cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH;
*cmd |= addr & WSPI_CMD_BYTE_ADDR;
if (fixed)
*cmd |= WSPI_CMD_FIXED;
spi_message_init(&m);
memset(t, 0, sizeof(t));
t[0].tx_buf = cmd;
t[0].len = 4;
spi_message_add_tail(&t[0], &m);
/* Busy and non busy words read */
t[1].rx_buf = busy_buf;
t[1].len = WL1271_BUSY_WORD_LEN;
spi_message_add_tail(&t[1], &m);
t[2].rx_buf = buf;
t[2].len = len;
spi_message_add_tail(&t[2], &m);
spi_sync(wl->spi, &m);
/* FIXME: check busy words */
wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd));
wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len);
}
void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
struct spi_transfer t[2];
struct spi_message m;
u32 *cmd;
cmd = &wl->buffer_cmd;
*cmd = 0;
*cmd |= WSPI_CMD_WRITE;
*cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH;
*cmd |= addr & WSPI_CMD_BYTE_ADDR;
if (fixed)
*cmd |= WSPI_CMD_FIXED;
spi_message_init(&m);
memset(t, 0, sizeof(t));
t[0].tx_buf = cmd;
t[0].len = sizeof(*cmd);
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = buf;
t[1].len = len;
spi_message_add_tail(&t[1], &m);
spi_sync(wl->spi, &m);
wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd));
wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len);
}
void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf,
size_t len)
{
int physical;
physical = wl1271_translate_mem_addr(wl, addr);
wl1271_spi_read(wl, physical, buf, len, false);
}
void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf,
size_t len)
{
int physical;
physical = wl1271_translate_mem_addr(wl, addr);
wl1271_spi_write(wl, physical, buf, len, false);
}
void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len,
bool fixed)
{
int physical;
physical = wl1271_translate_reg_addr(wl, addr);
wl1271_spi_read(wl, physical, buf, len, fixed);
}
void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len,
bool fixed)
{
int physical;
physical = wl1271_translate_reg_addr(wl, addr);
wl1271_spi_write(wl, physical, buf, len, fixed);
}
u32 wl1271_mem_read32(struct wl1271 *wl, int addr)
{
return wl1271_read32(wl, wl1271_translate_mem_addr(wl, addr));
}
void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val)
{
wl1271_write32(wl, wl1271_translate_mem_addr(wl, addr), val);
}
u32 wl1271_reg_read32(struct wl1271 *wl, int addr)
{
return wl1271_read32(wl, wl1271_translate_reg_addr(wl, addr));
}
void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val)
{
wl1271_write32(wl, wl1271_translate_reg_addr(wl, addr), val);
}

Some files were not shown because too many files have changed in this diff Show More