cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets

Signed-off-by: Solomon Peachy <pizza@shaftnet.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Solomon Peachy
2013-05-29 15:26:40 -04:00
committed by John W. Linville
parent 5f07d15a77
commit a910e4a94f
34 changed files with 15040 additions and 0 deletions
+5
View File
@@ -2299,6 +2299,11 @@ M: Jaya Kumar <jayakumar.alsa@gmail.com>
S: Maintained
F: sound/pci/cs5535audio/
CW1200 WLAN driver
M: Solomon Peachy <pizza@shaftnet.org>
S: Maintained
F: drivers/net/wireless/cw1200/
CX18 VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers)
+1
View File
@@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/mwifiex/Kconfig"
source "drivers/net/wireless/cw1200/Kconfig"
endif # WLAN
+2
View File
@@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/
obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/
obj-$(CONFIG_CW1200) += cw1200/
+46
View File
@@ -0,0 +1,46 @@
config CW1200
tristate "CW1200 WLAN support"
depends on MAC80211 && CFG80211
help
This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets.
This option just enables the driver core, see below for
specific bus support.
if CW1200
config CW1200_WLAN_SDIO
tristate "Support SDIO platforms"
depends on CW1200 && MMC
help
Enable support for the CW1200 connected via an SDIO bus.
config CW1200_WLAN_SPI
tristate "Support SPI platforms"
depends on CW1200 && SPI
help
Enables support for the CW1200 connected via a SPI bus.
config CW1200_WLAN_SAGRAD
tristate "Support Sagrad SG901-1091/1098 modules"
depends on CW1200_WLAN_SDIO
help
This provides the platform data glue to support the
Sagrad SG901-1091/1098 modules in their standard SDIO EVK.
It also includes example SPI platform data.
menu "Driver debug features"
depends on CW1200 && DEBUG_FS
config CW1200_ETF
bool "Enable CW1200 Engineering Test Framework hooks"
help
If you don't know what this is, just say N.
config CW1200_ITP
bool "Enable ITP access"
help
If you don't know what this is, just say N.
endmenu
endif
+24
View File
@@ -0,0 +1,24 @@
cw1200_core-y := \
fwio.o \
txrx.o \
main.o \
queue.o \
hwio.o \
bh.o \
wsm.o \
sta.o \
scan.o \
pm.o \
debug.o
cw1200_core-$(CONFIG_CW1200_ITP) += itp.o
# CFLAGS_sta.o += -DDEBUG
cw1200_wlan_sdio-y := cw1200_sdio.o
cw1200_wlan_spi-y := cw1200_spi.o
cw1200_wlan_sagrad-y := cw1200_sagrad.o
obj-$(CONFIG_CW1200) += cw1200_core.o
obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o
obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o
obj-$(CONFIG_CW1200_WLAN_SAGRAD) += cw1200_wlan_sagrad.o
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
/*
* Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* 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.
*/
#ifndef CW1200_BH_H
#define CW1200_BH_H
/* extern */ struct cw1200_common;
int cw1200_register_bh(struct cw1200_common *priv);
void cw1200_unregister_bh(struct cw1200_common *priv);
void cw1200_irq_handler(struct cw1200_common *priv);
void cw1200_bh_wakeup(struct cw1200_common *priv);
int cw1200_bh_suspend(struct cw1200_common *priv);
int cw1200_bh_resume(struct cw1200_common *priv);
/* Must be called from BH thread. */
void cw1200_enable_powersave(struct cw1200_common *priv,
bool enable);
int wsm_release_tx_buffer(struct cw1200_common *priv, int count);
#endif /* CW1200_BH_H */
+332
View File
@@ -0,0 +1,332 @@
/*
* Common private data for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on the mac80211 Prism54 code, which is
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
*
* Based on the islsm (softmac prism54) driver, which is:
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
*
* 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.
*/
#ifndef CW1200_H
#define CW1200_H
#include <linux/wait.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <net/mac80211.h>
#include "queue.h"
#include "wsm.h"
#include "scan.h"
#include "txrx.h"
#include "pm.h"
/* Forward declarations */
struct sbus_ops;
struct task_struct;
struct cw1200_debug_priv;
struct firmware;
#ifdef CONFIG_CW1200_ETF
extern int etf_mode;
extern char *etf_firmware;
#endif
#define CW1200_MAX_CTRL_FRAME_LEN (0x1000)
#define CW1200_MAX_STA_IN_AP_MODE (5)
#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1)
#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2)
#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3)
#define CW1200_MAX_REQUEUE_ATTEMPTS (5)
#define CW1200_MAX_TID (8)
#define CW1200_BLOCK_ACK_CNT (30)
#define CW1200_BLOCK_ACK_THLD (800)
#define CW1200_BLOCK_ACK_HIST (3)
#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST)
#define CW1200_JOIN_TIMEOUT (1 * HZ)
#define CW1200_AUTH_TIMEOUT (5 * HZ)
struct cw1200_ht_info {
struct ieee80211_sta_ht_cap ht_cap;
enum nl80211_channel_type channel_type;
u16 operation_mode;
};
/* Please keep order */
enum cw1200_join_status {
CW1200_JOIN_STATUS_PASSIVE = 0,
CW1200_JOIN_STATUS_MONITOR,
CW1200_JOIN_STATUS_JOINING,
CW1200_JOIN_STATUS_PRE_STA,
CW1200_JOIN_STATUS_STA,
CW1200_JOIN_STATUS_IBSS,
CW1200_JOIN_STATUS_AP,
};
enum cw1200_link_status {
CW1200_LINK_OFF,
CW1200_LINK_RESERVE,
CW1200_LINK_SOFT,
CW1200_LINK_HARD,
CW1200_LINK_RESET,
CW1200_LINK_RESET_REMAP,
};
extern int cw1200_power_mode;
extern const char * const cw1200_fw_types[];
struct cw1200_link_entry {
unsigned long timestamp;
enum cw1200_link_status status;
enum cw1200_link_status prev_status;
u8 mac[ETH_ALEN];
u8 buffered[CW1200_MAX_TID];
struct sk_buff_head rx_queue;
};
struct cw1200_common {
/* interfaces to the rest of the stack */
struct ieee80211_hw *hw;
struct ieee80211_vif *vif;
struct device *pdev;
/* Statistics */
struct ieee80211_low_level_stats stats;
/* Our macaddr */
u8 mac_addr[ETH_ALEN];
/* Hardware interface */
const struct sbus_ops *sbus_ops;
struct sbus_priv *sbus_priv;
/* Hardware information */
enum {
HIF_9000_SILICON_VERSATILE = 0,
HIF_8601_VERSATILE,
HIF_8601_SILICON,
} hw_type;
enum {
CW1200_HW_REV_CUT10 = 10,
CW1200_HW_REV_CUT11 = 11,
CW1200_HW_REV_CUT20 = 20,
CW1200_HW_REV_CUT22 = 22,
CW1X60_HW_REV = 40,
} hw_revision;
int hw_refclk;
bool hw_have_5ghz;
const struct firmware *sdd;
char *sdd_path;
struct cw1200_debug_priv *debug;
struct workqueue_struct *workqueue;
struct mutex conf_mutex;
struct cw1200_queue tx_queue[4];
struct cw1200_queue_stats tx_queue_stats;
int tx_burst_idx;
/* firmware/hardware info */
unsigned int tx_hdr_len;
/* Radio data */
int output_power;
/* BBP/MAC state */
struct ieee80211_rate *rates;
struct ieee80211_rate *mcs_rates;
struct ieee80211_channel *channel;
struct wsm_edca_params edca;
struct wsm_tx_queue_params tx_queue_params;
struct wsm_mib_association_mode association_mode;
struct wsm_set_bss_params bss_params;
struct cw1200_ht_info ht_info;
struct wsm_set_pm powersave_mode;
struct wsm_set_pm firmware_ps_mode;
int cqm_rssi_thold;
unsigned cqm_rssi_hyst;
bool cqm_use_rssi;
int cqm_beacon_loss_count;
int channel_switch_in_progress;
wait_queue_head_t channel_switch_done;
u8 long_frame_max_tx_count;
u8 short_frame_max_tx_count;
int mode;
bool enable_beacon;
int beacon_int;
bool listening;
struct wsm_rx_filter rx_filter;
struct wsm_mib_multicast_filter multicast_filter;
bool has_multicast_subscription;
bool disable_beacon_filter;
struct work_struct update_filtering_work;
struct work_struct set_beacon_wakeup_period_work;
u8 ba_rx_tid_mask;
u8 ba_tx_tid_mask;
struct cw1200_pm_state pm_state;
struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo;
struct wsm_uapsd_info uapsd_info;
bool setbssparams_done;
bool bt_present;
u8 conf_listen_interval;
u32 listen_interval;
u32 erp_info;
u32 rts_threshold;
/* BH */
atomic_t bh_rx;
atomic_t bh_tx;
atomic_t bh_term;
atomic_t bh_suspend;
struct workqueue_struct *bh_workqueue;
struct work_struct bh_work;
int bh_error;
wait_queue_head_t bh_wq;
wait_queue_head_t bh_evt_wq;
u8 buf_id_tx;
u8 buf_id_rx;
u8 wsm_rx_seq;
u8 wsm_tx_seq;
int hw_bufs_used;
bool powersave_enabled;
bool device_can_sleep;
/* Scan status */
struct cw1200_scan scan;
/* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid
* FW issue with sleeping/waking up. */
atomic_t recent_scan;
struct delayed_work clear_recent_scan_work;
/* WSM */
struct wsm_startup_ind wsm_caps;
struct mutex wsm_cmd_mux;
struct wsm_buf wsm_cmd_buf;
struct wsm_cmd wsm_cmd;
wait_queue_head_t wsm_cmd_wq;
wait_queue_head_t wsm_startup_done;
int firmware_ready;
atomic_t tx_lock;
/* WSM debug */
int wsm_enable_wsm_dumps;
/* WSM Join */
enum cw1200_join_status join_status;
u32 pending_frame_id;
bool join_pending;
struct delayed_work join_timeout;
struct work_struct unjoin_work;
struct work_struct join_complete_work;
int join_complete_status;
int join_dtim_period;
bool delayed_unjoin;
/* TX/RX and security */
s8 wep_default_key_id;
struct work_struct wep_key_work;
u32 key_map;
struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1];
/* AP powersave */
u32 link_id_map;
struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE];
struct work_struct link_id_work;
struct delayed_work link_id_gc_work;
u32 sta_asleep_mask;
u32 pspoll_mask;
bool aid0_bit_set;
spinlock_t ps_state_lock; /* Protect power save state */
bool buffered_multicasts;
bool tx_multicast;
struct work_struct set_tim_work;
struct work_struct set_cts_work;
struct work_struct multicast_start_work;
struct work_struct multicast_stop_work;
struct timer_list mcast_timeout;
/* WSM events and CQM implementation */
spinlock_t event_queue_lock; /* Protect event queue */
struct list_head event_queue;
struct work_struct event_handler;
struct delayed_work bss_loss_work;
spinlock_t bss_loss_lock; /* Protect BSS loss state */
int bss_loss_state;
int bss_loss_confirm_id;
int delayed_link_loss;
struct work_struct bss_params_work;
/* TX rate policy cache */
struct tx_policy_cache tx_policy_cache;
struct work_struct tx_policy_upload_work;
/* legacy PS mode switch in suspend */
int ps_mode_switch_in_progress;
wait_queue_head_t ps_mode_switch_done;
/* Workaround for WFD testcase 6.1.10*/
struct work_struct linkid_reset_work;
u8 action_frame_sa[ETH_ALEN];
u8 action_linkid;
#ifdef CONFIG_CW1200_ETF
struct sk_buff_head etf_q;
#endif
};
struct cw1200_sta_priv {
int link_id;
};
/* interfaces for the drivers */
int cw1200_core_probe(const struct sbus_ops *sbus_ops,
struct sbus_priv *sbus,
struct device *pdev,
struct cw1200_common **pself,
int ref_clk, const u8 *macaddr,
const char *sdd_path, bool have_5ghz);
void cw1200_core_release(struct cw1200_common *self);
#define FWLOAD_BLOCK_SIZE (1024)
static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info)
{
return ht_info->channel_type != NL80211_CHAN_NO_HT;
}
static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info)
{
return cw1200_is_ht(ht_info) &&
(ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
!(ht_info->operation_mode &
IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
}
static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info)
{
if (!cw1200_is_ht(ht_info))
return 0;
return ht_info->ht_cap.ampdu_density;
}
#endif /* CW1200_H */
+145
View File
@@ -0,0 +1,145 @@
/*
* Platform glue data for ST-Ericsson CW1200 driver
*
* Copyright (c) 2013, Sagrad, Inc
* Author: Solomon Peachy <speachy@sagrad.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.
*/
#include <linux/module.h>
#include <linux/cw1200_platform.h>
MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
MODULE_DESCRIPTION("ST-Ericsson CW1200 Platform glue driver");
MODULE_LICENSE("GPL");
/* Define just one of these. Feel free to customize as needed */
#define SAGRAD_1091_1098_EVK_SDIO
/* #define SAGRAD_1091_1098_EVK_SPI */
#ifdef SAGRAD_1091_1098_EVK_SDIO
#if 0
static struct resource cw1200_href_resources[] = {
{
.start = 215, /* fix me as appropriate */
.end = 215, /* ditto */
.flags = IORESOURCE_IO,
.name = "cw1200_wlan_reset",
},
{
.start = 216, /* fix me as appropriate */
.end = 216, /* ditto */
.flags = IORESOURCE_IO,
.name = "cw1200_wlan_powerup",
},
{
.start = NOMADIK_GPIO_TO_IRQ(216), /* fix me as appropriate */
.end = NOMADIK_GPIO_TO_IRQ(216), /* ditto */
.flags = IORESOURCE_IRQ,
.name = "cw1200_wlan_irq",
},
};
#endif
static int cw1200_power_ctrl(const struct cw1200_platform_data_sdio *pdata,
bool enable)
{
/* Control 3v3 and 1v8 to hardware as appropriate */
/* Note this is not needed if it's controlled elsewhere or always on */
/* May require delay for power to stabilize */
return 0;
}
static int cw1200_clk_ctrl(const struct cw1200_platform_data_sdio *pdata,
bool enable)
{
/* Turn CLK_32K off and on as appropriate. */
/* Note this is not needed if it's always on */
/* May require delay for clock to stabilize */
return 0;
}
static struct cw1200_platform_data_sdio cw1200_platform_data = {
.ref_clk = 38400,
.have_5ghz = false,
#if 0
.reset = &cw1200_href_resources[0],
.powerup = &cw1200_href_resources[1],
.irq = &cw1200_href_resources[2],
#endif
.power_ctrl = cw1200_power_ctrl,
.clk_ctrl = cw1200_clk_ctrl,
/* .macaddr = ??? */
.sdd_file = "sdd_sagrad_1091_1098.bin",
};
#endif
#ifdef SAGRAD_1091_1098_EVK_SPI
/* Note that this is an example of integrating into your board support file */
static struct resource cw1200_href_resources[] = {
{
.start = GPIO_RF_RESET,
.end = GPIO_RF_RESET,
.flags = IORESOURCE_IO,
.name = "cw1200_wlan_reset",
},
{
.start = GPIO_RF_POWERUP,
.end = GPIO_RF_POWERUP,
.flags = IORESOURCE_IO,
.name = "cw1200_wlan_powerup",
},
};
static int cw1200_power_ctrl(const struct cw1200_platform_data_spi *pdata,
bool enable)
{
/* Control 3v3 and 1v8 to hardware as appropriate */
/* Note this is not needed if it's controlled elsewhere or always on */
/* May require delay for power to stabilize */
return 0;
}
static int cw1200_clk_ctrl(const struct cw1200_platform_data_spi *pdata,
bool enable)
{
/* Turn CLK_32K off and on as appropriate. */
/* Note this is not needed if it's always on */
/* May require delay for clock to stabilize */
return 0;
}
static struct cw1200_platform_data_spi cw1200_platform_data = {
.ref_clk = 38400,
.spi_bits_per_word = 16,
.reset = &cw1200_href_resources[0],
.powerup = &cw1200_href_resources[1],
.power_ctrl = cw1200_power_ctrl,
.clk_ctrl = cw1200_clk_ctrl,
/* .macaddr = ??? */
.sdd_file = "sdd_sagrad_1091_1098.bin",
};
static struct spi_board_info myboard_spi_devices[] __initdata = {
{
.modalias = "cw1200_wlan_spi",
.max_speed_hz = 10000000, /* 52MHz Max */
.bus_num = 0,
.irq = WIFI_IRQ,
.platform_data = &cw1200_platform_data,
.chip_select = 0,
},
};
#endif
const void *cw1200_get_platform_data(void)
{
return &cw1200_platform_data;
}
EXPORT_SYMBOL_GPL(cw1200_get_platform_data);
+409
View File
@@ -0,0 +1,409 @@
/*
* Mac80211 SDIO driver for ST-Ericsson CW1200 device
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* 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.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <net/mac80211.h>
#include "cw1200.h"
#include "sbus.h"
#include <linux/cw1200_platform.h>
#include "hwio.h"
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
MODULE_LICENSE("GPL");
#define SDIO_BLOCK_SIZE (512)
struct sbus_priv {
struct sdio_func *func;
struct cw1200_common *core;
const struct cw1200_platform_data_sdio *pdata;
};
#ifndef SDIO_VENDOR_ID_STE
#define SDIO_VENDOR_ID_STE 0x0020
#endif
#ifndef SDIO_DEVICE_ID_STE_CW1200
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
#endif
static const struct sdio_device_id cw1200_sdio_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
{ /* end: all zeroes */ },
};
/* sbus_ops implemetation */
static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self,
unsigned int addr,
void *dst, int count)
{
return sdio_memcpy_fromio(self->func, dst, addr, count);
}
static int cw1200_sdio_memcpy_toio(struct sbus_priv *self,
unsigned int addr,
const void *src, int count)
{
return sdio_memcpy_toio(self->func, addr, (void *)src, count);
}
static void cw1200_sdio_lock(struct sbus_priv *self)
{
sdio_claim_host(self->func);
}
static void cw1200_sdio_unlock(struct sbus_priv *self)
{
sdio_release_host(self->func);
}
static void cw1200_sdio_irq_handler(struct sdio_func *func)
{
struct sbus_priv *self = sdio_get_drvdata(func);
/* note: sdio_host already claimed here. */
if (self->core)
cw1200_irq_handler(self->core);
}
static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
{
return IRQ_WAKE_THREAD;
}
static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
{
struct sbus_priv *self = dev_id;
if (self->core) {
sdio_claim_host(self->func);
cw1200_irq_handler(self->core);
sdio_release_host(self->func);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
}
static int cw1200_request_irq(struct sbus_priv *self)
{
int ret;
const struct resource *irq = self->pdata->irq;
u8 cccr;
cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
if (WARN_ON(ret))
goto err;
/* Master interrupt enable ... */
cccr |= BIT(0);
/* ... for our function */
cccr |= BIT(self->func->num);
sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
if (WARN_ON(ret))
goto err;
ret = enable_irq_wake(irq->start);
if (WARN_ON(ret))
goto err;
/* Request the IRQ */
ret = request_threaded_irq(irq->start, cw1200_gpio_hardirq,
cw1200_gpio_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
irq->name, self);
if (WARN_ON(ret))
goto err;
return 0;
err:
return ret;
}
static int cw1200_sdio_irq_subscribe(struct sbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ subscribe\n");
sdio_claim_host(self->func);
if (self->pdata->irq)
ret = cw1200_request_irq(self);
else
ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
sdio_release_host(self->func);
return ret;
}
static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ unsubscribe\n");
if (self->pdata->irq) {
disable_irq_wake(self->pdata->irq->start);
free_irq(self->pdata->irq->start, self);
} else {
sdio_claim_host(self->func);
ret = sdio_release_irq(self->func);
sdio_release_host(self->func);
}
return ret;
}
static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
{
const struct resource *reset = pdata->reset;
if (reset) {
gpio_set_value(reset->start, 0);
msleep(30); /* Min is 2 * CLK32K cycles */
gpio_free(reset->start);
}
if (pdata->power_ctrl)
pdata->power_ctrl(pdata, false);
if (pdata->clk_ctrl)
pdata->clk_ctrl(pdata, false);
return 0;
}
static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
{
const struct resource *reset = pdata->reset;
const struct resource *powerup = pdata->reset;
/* Ensure I/Os are pulled low */
if (reset) {
gpio_request(reset->start, reset->name);
gpio_direction_output(reset->start, 0);
}
if (powerup) {
gpio_request(powerup->start, powerup->name);
gpio_direction_output(powerup->start, 0);
}
if (reset || powerup)
msleep(50); /* Settle time */
/* Enable 3v3 and 1v8 to hardware */
if (pdata->power_ctrl) {
if (pdata->power_ctrl(pdata, true)) {
pr_err("power_ctrl() failed!\n");
return -1;
}
}
/* Enable CLK32K */
if (pdata->clk_ctrl) {
if (pdata->clk_ctrl(pdata, true)) {
pr_err("clk_ctrl() failed!\n");
return -1;
}
msleep(10); /* Delay until clock is stable for 2 cycles */
}
/* Enable POWERUP signal */
if (powerup) {
gpio_set_value(powerup->start, 1);
msleep(250); /* or more..? */
}
/* Enable RSTn signal */
if (reset) {
gpio_set_value(reset->start, 1);
msleep(50); /* Or more..? */
}
return 0;
}
static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size)
{
if (self->pdata->no_nptb)
size = round_up(size, SDIO_BLOCK_SIZE);
else
size = sdio_align_size(self->func, size);
return size;
}
static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend)
{
int ret = 0;
if (self->pdata->irq)
ret = irq_set_irq_wake(self->pdata->irq->start, suspend);
return ret;
}
static struct sbus_ops cw1200_sdio_sbus_ops = {
.sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio,
.sbus_memcpy_toio = cw1200_sdio_memcpy_toio,
.lock = cw1200_sdio_lock,
.unlock = cw1200_sdio_unlock,
.align_size = cw1200_sdio_align_size,
.power_mgmt = cw1200_sdio_pm,
};
/* Probe Function to be called by SDIO stack when device is discovered */
static int cw1200_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct sbus_priv *self;
int status;
pr_info("cw1200_wlan_sdio: Probe called\n");
/* We are only able to handle the wlan function */
if (func->num != 0x01)
return -ENODEV;
self = kzalloc(sizeof(*self), GFP_KERNEL);
if (!self) {
pr_err("Can't allocate SDIO sbus_priv.\n");
return -ENOMEM;
}
func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
self->pdata = cw1200_get_platform_data();
self->func = func;
sdio_set_drvdata(func, self);
sdio_claim_host(func);
sdio_enable_func(func);
sdio_release_host(func);
status = cw1200_sdio_irq_subscribe(self);
status = cw1200_core_probe(&cw1200_sdio_sbus_ops,
self, &func->dev, &self->core,
self->pdata->ref_clk,
self->pdata->macaddr,
self->pdata->sdd_file,
self->pdata->have_5ghz);
if (status) {
cw1200_sdio_irq_unsubscribe(self);
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
sdio_set_drvdata(func, NULL);
kfree(self);
}
return status;
}
/* Disconnect Function to be called by SDIO stack when
* device is disconnected */
static void cw1200_sdio_disconnect(struct sdio_func *func)
{
struct sbus_priv *self = sdio_get_drvdata(func);
if (self) {
cw1200_sdio_irq_unsubscribe(self);
if (self->core) {
cw1200_core_release(self->core);
self->core = NULL;
}
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
sdio_set_drvdata(func, NULL);
kfree(self);
}
}
static int cw1200_sdio_suspend(struct device *dev)
{
int ret;
struct sdio_func *func = dev_to_sdio_func(dev);
struct sbus_priv *self = sdio_get_drvdata(func);
if (!cw1200_can_suspend(self->core))
return -EAGAIN;
/* Notify SDIO that CW1200 will remain powered during suspend */
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret)
pr_err("Error setting SDIO pm flags: %i\n", ret);
return ret;
}
static int cw1200_sdio_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops cw1200_pm_ops = {
.suspend = cw1200_sdio_suspend,
.resume = cw1200_sdio_resume,
};
static struct sdio_driver sdio_driver = {
.name = "cw1200_wlan_sdio",
.id_table = cw1200_sdio_ids,
.probe = cw1200_sdio_probe,
.remove = cw1200_sdio_disconnect,
.drv = {
.pm = &cw1200_pm_ops,
}
};
/* Init Module function -> Called by insmod */
static int __init cw1200_sdio_init(void)
{
const struct cw1200_platform_data_sdio *pdata;
int ret;
pdata = cw1200_get_platform_data();
if (cw1200_sdio_on(pdata)) {
ret = -1;
goto err;
}
ret = sdio_register_driver(&sdio_driver);
if (ret)
goto err;
return 0;
err:
cw1200_sdio_off(pdata);
return ret;
}
/* Called at Driver Unloading */
static void __exit cw1200_sdio_exit(void)
{
const struct cw1200_platform_data_sdio *pdata;
pdata = cw1200_get_platform_data();
sdio_unregister_driver(&sdio_driver);
cw1200_sdio_off(pdata);
}
module_init(cw1200_sdio_init);
module_exit(cw1200_sdio_exit);
+480
View File
@@ -0,0 +1,480 @@
/*
* Mac80211 SPI driver for ST-Ericsson CW1200 device
*
* Copyright (c) 2011, Sagrad Inc.
* Author: Solomon Peachy <speachy@sagrad.com>
*
* Based on cw1200_sdio.c
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* 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.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <net/mac80211.h>
#include <linux/spi/spi.h>
#include <linux/device.h>
#include "cw1200.h"
#include "sbus.h"
#include <linux/cw1200_platform.h>
#include "hwio.h"
MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:cw1200_wlan_spi");
/* #define SPI_DEBUG */
struct sbus_priv {
struct spi_device *func;
struct cw1200_common *core;
const struct cw1200_platform_data_spi *pdata;
spinlock_t lock; /* Serialize all bus operations */
int claimed;
};
#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
#define SET_WRITE 0x7FFF /* usage: and operation */
#define SET_READ 0x8000 /* usage: or operation */
/*
Notes on byte ordering:
LE: B0 B1 B2 B3
BE: B3 B2 B1 B0
Hardware expects 32-bit data to be written as 16-bit BE words:
B1 B0 B3 B2
*/
static int cw1200_spi_memcpy_fromio(struct sbus_priv *self,
unsigned int addr,
void *dst, int count)
{
int ret, i;
uint16_t regaddr;
struct spi_message m;
struct spi_transfer t_addr = {
.tx_buf = &regaddr,
.len = sizeof(regaddr),
};
struct spi_transfer t_msg = {
.rx_buf = dst,
.len = count,
};
regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
regaddr |= SET_READ;
regaddr |= (count>>1);
regaddr = cpu_to_le16(regaddr);
#ifdef SPI_DEBUG
pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr,
le16_to_cpu(regaddr));
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
regaddr = swab16(regaddr);
spi_message_init(&m);
spi_message_add_tail(&t_addr, &m);
spi_message_add_tail(&t_msg, &m);
ret = spi_sync(self->func, &m);
#ifdef SPI_DEBUG
pr_info("READ : ");
for (i = 0; i < t_addr.len; i++)
printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
printk(" : ");
for (i = 0; i < t_msg.len; i++)
printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
printk("\n");
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)dst;
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
return ret;
}
static int cw1200_spi_memcpy_toio(struct sbus_priv *self,
unsigned int addr,
const void *src, int count)
{
int rval, i;
uint16_t regaddr;
struct spi_transfer t_addr = {
.tx_buf = &regaddr,
.len = sizeof(regaddr),
};
struct spi_transfer t_msg = {
.tx_buf = src,
.len = count,
};
struct spi_message m;
regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
regaddr &= SET_WRITE;
regaddr |= (count>>1);
regaddr = cpu_to_le16(regaddr);
#ifdef SPI_DEBUG
pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr,
le16_to_cpu(regaddr));
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)src;
regaddr = swab16(regaddr);
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
#ifdef SPI_DEBUG
pr_info("WRITE: ");
for (i = 0; i < t_addr.len; i++)
printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
printk(" : ");
for (i = 0; i < t_msg.len; i++)
printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
printk("\n");
#endif
spi_message_init(&m);
spi_message_add_tail(&t_addr, &m);
spi_message_add_tail(&t_msg, &m);
rval = spi_sync(self->func, &m);
#ifdef SPI_DEBUG
pr_info("WROTE: %d\n", m.actual_length);
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)src;
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
return rval;
}
static void cw1200_spi_lock(struct sbus_priv *self)
{
unsigned long flags;
might_sleep();
spin_lock_irqsave(&self->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (!self->claimed)
break;
spin_unlock_irqrestore(&self->lock, flags);
schedule();
spin_lock_irqsave(&self->lock, flags);
}
set_current_state(TASK_RUNNING);
self->claimed = 1;
spin_unlock_irqrestore(&self->lock, flags);
return;
}
static void cw1200_spi_unlock(struct sbus_priv *self)
{
unsigned long flags;
spin_lock_irqsave(&self->lock, flags);
self->claimed = 0;
spin_unlock_irqrestore(&self->lock, flags);
return;
}
static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
{
struct sbus_priv *self = dev_id;
if (self->core) {
cw1200_irq_handler(self->core);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
}
static int cw1200_spi_irq_subscribe(struct sbus_priv *self)
{
int ret;
pr_debug("SW IRQ subscribe\n");
ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler,
IRQF_TRIGGER_HIGH,
"cw1200_wlan_irq", self);
if (WARN_ON(ret < 0))
goto exit;
ret = enable_irq_wake(self->func->irq);
if (WARN_ON(ret))
goto free_irq;
return 0;
free_irq:
free_irq(self->func->irq, self);
exit:
return ret;
}
static int cw1200_spi_irq_unsubscribe(struct sbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ unsubscribe\n");
disable_irq_wake(self->func->irq);
free_irq(self->func->irq, self);
return ret;
}
static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
{
const struct resource *reset = pdata->reset;
if (reset) {
gpio_set_value(reset->start, 0);
msleep(30); /* Min is 2 * CLK32K cycles */
gpio_free(reset->start);
}
if (pdata->power_ctrl)
pdata->power_ctrl(pdata, false);
if (pdata->clk_ctrl)
pdata->clk_ctrl(pdata, false);
return 0;
}
static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
{
const struct resource *reset = pdata->reset;
const struct resource *powerup = pdata->reset;
/* Ensure I/Os are pulled low */
if (reset) {
gpio_request(reset->start, reset->name);
gpio_direction_output(reset->start, 0);
}
if (powerup) {
gpio_request(powerup->start, powerup->name);
gpio_direction_output(powerup->start, 0);
}
if (reset || powerup)
msleep(10); /* Settle time? */
/* Enable 3v3 and 1v8 to hardware */
if (pdata->power_ctrl) {
if (pdata->power_ctrl(pdata, true)) {
pr_err("power_ctrl() failed!\n");
return -1;
}
}
/* Enable CLK32K */
if (pdata->clk_ctrl) {
if (pdata->clk_ctrl(pdata, true)) {
pr_err("clk_ctrl() failed!\n");
return -1;
}
msleep(10); /* Delay until clock is stable for 2 cycles */
}
/* Enable POWERUP signal */
if (powerup) {
gpio_set_value(powerup->start, 1);
msleep(250); /* or more..? */
}
/* Enable RSTn signal */
if (reset) {
gpio_set_value(reset->start, 1);
msleep(50); /* Or more..? */
}
return 0;
}
static size_t cw1200_spi_align_size(struct sbus_priv *self, size_t size)
{
return size & 1 ? size + 1 : size;
}
static int cw1200_spi_pm(struct sbus_priv *self, bool suspend)
{
return irq_set_irq_wake(self->func->irq, suspend);
}
static struct sbus_ops cw1200_spi_sbus_ops = {
.sbus_memcpy_fromio = cw1200_spi_memcpy_fromio,
.sbus_memcpy_toio = cw1200_spi_memcpy_toio,
.lock = cw1200_spi_lock,
.unlock = cw1200_spi_unlock,
.align_size = cw1200_spi_align_size,
.power_mgmt = cw1200_spi_pm,
};
/* Probe Function to be called by SPI stack when device is discovered */
static int cw1200_spi_probe(struct spi_device *func)
{
const struct cw1200_platform_data_spi *plat_data =
func->dev.platform_data;
struct sbus_priv *self;
int status;
/* Sanity check speed */
if (func->max_speed_hz > 52000000)
func->max_speed_hz = 52000000;
if (func->max_speed_hz < 1000000)
func->max_speed_hz = 1000000;
/* Fix up transfer size */
if (plat_data->spi_bits_per_word)
func->bits_per_word = plat_data->spi_bits_per_word;
if (!func->bits_per_word)
func->bits_per_word = 16;
/* And finally.. */
func->mode = SPI_MODE_0;
pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
func->chip_select, func->mode, func->bits_per_word,
func->max_speed_hz);
if (cw1200_spi_on(plat_data)) {
pr_err("spi_on() failed!\n");
return -1;
}
if (spi_setup(func)) {
pr_err("spi_setup() failed!\n");
return -1;
}
self = kzalloc(sizeof(*self), GFP_KERNEL);
if (!self) {
pr_err("Can't allocate SPI sbus_priv.");
return -ENOMEM;
}
self->pdata = plat_data;
self->func = func;
spin_lock_init(&self->lock);
spi_set_drvdata(func, self);
status = cw1200_spi_irq_subscribe(self);
status = cw1200_core_probe(&cw1200_spi_sbus_ops,
self, &func->dev, &self->core,
self->pdata->ref_clk,
self->pdata->macaddr,
self->pdata->sdd_file,
self->pdata->have_5ghz);
if (status) {
cw1200_spi_irq_unsubscribe(self);
cw1200_spi_off(plat_data);
kfree(self);
}
return status;
}
/* Disconnect Function to be called by SPI stack when device is disconnected */
static int cw1200_spi_disconnect(struct spi_device *func)
{
struct sbus_priv *self = spi_get_drvdata(func);
if (self) {
cw1200_spi_irq_unsubscribe(self);
if (self->core) {
cw1200_core_release(self->core);
self->core = NULL;
}
kfree(self);
}
cw1200_spi_off(func->dev.platform_data);
return 0;
}
static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
{
struct sbus_priv *self = spi_get_drvdata(to_spi_device(dev));
if (!cw1200_can_suspend(self->core))
return -EAGAIN;
/* XXX notify host that we have to keep CW1200 powered on? */
return 0;
}
static int cw1200_spi_resume(struct device *dev)
{
return 0;
}
static struct spi_driver spi_driver = {
.probe = cw1200_spi_probe,
.remove = cw1200_spi_disconnect,
.driver = {
.name = "cw1200_wlan_spi",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.suspend = cw1200_spi_suspend,
.resume = cw1200_spi_resume,
},
};
/* Init Module function -> Called by insmod */
static int __init cw1200_spi_init(void)
{
return spi_register_driver(&spi_driver);
}
/* Called at Driver Unloading */
static void __exit cw1200_spi_exit(void)
{
spi_unregister_driver(&spi_driver);
}
module_init(cw1200_spi_init);
module_exit(cw1200_spi_exit);
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
/*
* DebugFS code for ST-Ericsson CW1200 mac80211 driver
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* 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.
*/
#ifndef CW1200_DEBUG_H_INCLUDED
#define CW1200_DEBUG_H_INCLUDED
#include "itp.h"
struct cw1200_debug_priv {
struct dentry *debugfs_phy;
int tx;
int tx_agg;
int rx;
int rx_agg;
int tx_multi;
int tx_multi_frames;
int tx_cache_miss;
int tx_align;
int tx_ttl;
int tx_burst;
int ba_cnt;
int ba_acc;
int ba_cnt_rx;
int ba_acc_rx;
#ifdef CONFIG_CW1200_ITP
struct cw1200_itp itp;
#endif /* CONFIG_CW1200_ITP */
};
int cw1200_debug_init(struct cw1200_common *priv);
void cw1200_debug_release(struct cw1200_common *priv);
static inline void cw1200_debug_txed(struct cw1200_common *priv)
{
++priv->debug->tx;
}
static inline void cw1200_debug_txed_agg(struct cw1200_common *priv)
{
++priv->debug->tx_agg;
}
static inline void cw1200_debug_txed_multi(struct cw1200_common *priv,
int count)
{
++priv->debug->tx_multi;
priv->debug->tx_multi_frames += count;
}
static inline void cw1200_debug_rxed(struct cw1200_common *priv)
{
++priv->debug->rx;
}
static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv)
{
++priv->debug->rx_agg;
}
static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv)
{
++priv->debug->tx_cache_miss;
}
static inline void cw1200_debug_tx_align(struct cw1200_common *priv)
{
++priv->debug->tx_align;
}
static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv)
{
++priv->debug->tx_ttl;
}
static inline void cw1200_debug_tx_burst(struct cw1200_common *priv)
{
++priv->debug->tx_burst;
}
static inline void cw1200_debug_ba(struct cw1200_common *priv,
int ba_cnt, int ba_acc,
int ba_cnt_rx, int ba_acc_rx)
{
priv->debug->ba_cnt = ba_cnt;
priv->debug->ba_acc = ba_acc;
priv->debug->ba_cnt_rx = ba_cnt_rx;
priv->debug->ba_acc_rx = ba_acc_rx;
}
#endif /* CW1200_DEBUG_H_INCLUDED */
File diff suppressed because it is too large Load Diff
+39
View File
@@ -0,0 +1,39 @@
/*
* Firmware API for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.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.
*/
#ifndef FWIO_H_INCLUDED
#define FWIO_H_INCLUDED
#define BOOTLOADER_CW1X60 "boot_cw1x60.bin"
#define FIRMWARE_CW1X60 "wsm_cw1x60.bin"
#define FIRMWARE_CUT22 "wsm_22.bin"
#define FIRMWARE_CUT20 "wsm_20.bin"
#define FIRMWARE_CUT11 "wsm_11.bin"
#define FIRMWARE_CUT10 "wsm_10.bin"
#define SDD_FILE_CW1X60 "sdd_cw1x60.bin"
#define SDD_FILE_22 "sdd_22.bin"
#define SDD_FILE_20 "sdd_20.bin"
#define SDD_FILE_11 "sdd_11.bin"
#define SDD_FILE_10 "sdd_10.bin"
int cw1200_load_firmware(struct cw1200_common *priv);
/* SDD definitions */
#define SDD_PTA_CFG_ELT_ID 0xEB
#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5
u32 cw1200_dpll_from_clk(u16 clk);
#endif
+311
View File
@@ -0,0 +1,311 @@
/*
* Low-level device IO routines for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver, which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@lockless.no>
*
* 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.
*/
#include <linux/types.h>
#include "cw1200.h"
#include "hwio.h"
#include "sbus.h"
/* Sdio addr is 4*spi_addr */
#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
((((buf_id) & 0x1F) << 7) \
| (((mpf) & 1) << 6) \
| (((rfu) & 1) << 5) \
| (((reg_id_ofs) & 0x1F) << 0))
#define MAX_RETRY 3
static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr,
void *buf, size_t buf_len, int buf_id)
{
u16 addr_sdio;
u32 sdio_reg_addr_17bit;
/* Check if buffer is aligned to 4 byte boundary */
if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
pr_err("buffer is not aligned.\n");
return -EINVAL;
}
/* Convert to SDIO Register Address */
addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
return priv->sbus_ops->sbus_memcpy_fromio(priv->sbus_priv,
sdio_reg_addr_17bit,
buf, buf_len);
}
static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr,
const void *buf, size_t buf_len, int buf_id)
{
u16 addr_sdio;
u32 sdio_reg_addr_17bit;
/* Convert to SDIO Register Address */
addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
return priv->sbus_ops->sbus_memcpy_toio(priv->sbus_priv,
sdio_reg_addr_17bit,
buf, buf_len);
}
static inline int __cw1200_reg_read_32(struct cw1200_common *priv,
u16 addr, u32 *val)
{
int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
*val = le32_to_cpu(*val);
return i;
}
static inline int __cw1200_reg_write_32(struct cw1200_common *priv,
u16 addr, u32 val)
{
val = cpu_to_le32(val);
return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
}
static inline int __cw1200_reg_read_16(struct cw1200_common *priv,
u16 addr, u16 *val)
{
int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
*val = le16_to_cpu(*val);
return i;
}
static inline int __cw1200_reg_write_16(struct cw1200_common *priv,
u16 addr, u16 val)
{
val = cpu_to_le16(val);
return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
}
int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf,
size_t buf_len)
{
int ret;
priv->sbus_ops->lock(priv->sbus_priv);
ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
size_t buf_len)
{
int ret;
priv->sbus_ops->lock(priv->sbus_priv);
ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len)
{
int ret, retry = 1;
int buf_id_rx = priv->buf_id_rx;
priv->sbus_ops->lock(priv->sbus_priv);
while (retry <= MAX_RETRY) {
ret = __cw1200_reg_read(priv,
ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
buf_len, buf_id_rx + 1);
if (!ret) {
buf_id_rx = (buf_id_rx + 1) & 3;
priv->buf_id_rx = buf_id_rx;
break;
} else {
retry++;
mdelay(1);
pr_err("error :[%d]\n", ret);
}
}
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_data_write(struct cw1200_common *priv, const void *buf,
size_t buf_len)
{
int ret, retry = 1;
int buf_id_tx = priv->buf_id_tx;
priv->sbus_ops->lock(priv->sbus_priv);
while (retry <= MAX_RETRY) {
ret = __cw1200_reg_write(priv,
ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
buf_len, buf_id_tx);
if (!ret) {
buf_id_tx = (buf_id_tx + 1) & 31;
priv->buf_id_tx = buf_id_tx;
break;
} else {
retry++;
mdelay(1);
pr_err("error :[%d]\n", ret);
}
}
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
size_t buf_len, u32 prefetch, u16 port_addr)
{
u32 val32 = 0;
int i, ret;
if ((buf_len / 2) >= 0x1000) {
pr_err("Can't read more than 0xfff words.\n");
return -EINVAL;
goto out;
}
priv->sbus_ops->lock(priv->sbus_priv);
/* Write address */
ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
if (ret < 0) {
pr_err("Can't write address register.\n");
goto out;
}
/* Read CONFIG Register Value - We will read 32 bits */
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto out;
}
/* Set PREFETCH bit */
ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID,
val32 | prefetch);
if (ret < 0) {
pr_err("Can't write prefetch bit.\n");
goto out;
}
/* Check for PRE-FETCH bit to be cleared */
for (i = 0; i < 20; i++) {
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't check prefetch bit.\n");
goto out;
}
if (!(val32 & prefetch))
break;
mdelay(i);
}
if (val32 & prefetch) {
pr_err("Prefetch bit is not cleared.\n");
goto out;
}
/* Read data port */
ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0);
if (ret < 0) {
pr_err("Can't read data port.\n");
goto out;
}
out:
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
size_t buf_len)
{
int ret;
if ((buf_len / 2) >= 0x1000) {
pr_err("Can't write more than 0xfff words.\n");
return -EINVAL;
}
priv->sbus_ops->lock(priv->sbus_priv);
/* Write address */
ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
if (ret < 0) {
pr_err("Can't write address register.\n");
goto out;
}
/* Write data port */
ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID,
buf, buf_len, 0);
if (ret < 0) {
pr_err("Can't write data port.\n");
goto out;
}
out:
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
{
u32 val32;
u16 val16;
int ret;
if (HIF_8601_SILICON == priv->hw_type) {
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
return ret;
}
if (enable)
val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE;
else
val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE;
ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32);
if (ret < 0) {
pr_err("Can't write config register.\n");
return ret;
}
} else {
ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16);
if (ret < 0) {
pr_err("Can't read control register.\n");
return ret;
}
if (enable)
val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE;
else
val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE;
ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16);
if (ret < 0) {
pr_err("Can't write control register.\n");
return ret;
}
}
return 0;
}
+247
View File
@@ -0,0 +1,247 @@
/*
* Low-level API for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.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.
*/
#ifndef CW1200_HWIO_H_INCLUDED
#define CW1200_HWIO_H_INCLUDED
/* extern */ struct cw1200_common;
#define CW1200_CUT_11_ID_STR (0x302E3830)
#define CW1200_CUT_22_ID_STR1 (0x302e3132)
#define CW1200_CUT_22_ID_STR2 (0x32302e30)
#define CW1200_CUT_22_ID_STR3 (0x3335)
#define CW1200_CUT_ID_ADDR (0xFFF17F90)
#define CW1200_CUT2_ID_ADDR (0xFFF1FF90)
/* Download control area */
/* boot loader start address in SRAM */
#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000)
/* 32K, 0x4000 to 0xDFFF */
#define DOWNLOAD_FIFO_OFFSET (0x00004000)
/* 32K */
#define DOWNLOAD_FIFO_SIZE (0x00008000)
/* 128 bytes, 0xFF80 to 0xFFFF */
#define DOWNLOAD_CTRL_OFFSET (0x0000FF80)
#define DOWNLOAD_CTRL_DATA_DWORDS (32-6)
struct download_cntl_t {
/* size of whole firmware file (including Cheksum), host init */
u32 image_size;
/* downloading flags */
u32 flags;
/* No. of bytes put into the download, init & updated by host */
u32 put;
/* last traced program counter, last ARM reg_pc */
u32 trace_pc;
/* No. of bytes read from the download, host init, device updates */
u32 get;
/* r0, boot losader status, host init to pending, device updates */
u32 status;
/* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS];
};
#define DOWNLOAD_IMAGE_SIZE_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size))
#define DOWNLOAD_FLAGS_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags))
#define DOWNLOAD_PUT_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put))
#define DOWNLOAD_TRACE_PC_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc))
#define DOWNLOAD_GET_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get))
#define DOWNLOAD_STATUS_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status))
#define DOWNLOAD_DEBUG_DATA_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data))
#define DOWNLOAD_DEBUG_DATA_LEN (108)
#define DOWNLOAD_BLOCK_SIZE (1024)
/* For boot loader detection */
#define DOWNLOAD_ARE_YOU_HERE (0x87654321)
#define DOWNLOAD_I_AM_HERE (0x12345678)
/* Download error code */
#define DOWNLOAD_PENDING (0xFFFFFFFF)
#define DOWNLOAD_SUCCESS (0)
#define DOWNLOAD_EXCEPTION (1)
#define DOWNLOAD_ERR_MEM_1 (2)
#define DOWNLOAD_ERR_MEM_2 (3)
#define DOWNLOAD_ERR_SOFTWARE (4)
#define DOWNLOAD_ERR_FILE_SIZE (5)
#define DOWNLOAD_ERR_CHECKSUM (6)
#define DOWNLOAD_ERR_OVERFLOW (7)
#define DOWNLOAD_ERR_IMAGE (8)
#define DOWNLOAD_ERR_HOST (9)
#define DOWNLOAD_ERR_ABORT (10)
#define SYS_BASE_ADDR_SILICON (0)
#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000)
#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON)
#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr))
/* ***************************************************************
*Device register definitions
*************************************************************** */
/* WBF - SPI Register Addresses */
#define ST90TDS_ADDR_ID_BASE (0x0000)
/* 16/32 bits */
#define ST90TDS_CONFIG_REG_ID (0x0000)
/* 16/32 bits */
#define ST90TDS_CONTROL_REG_ID (0x0001)
/* 16 bits, Q mode W/R */
#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002)
/* 32 bits, AHB bus R/W */
#define ST90TDS_AHB_DPORT_REG_ID (0x0003)
/* 16/32 bits */
#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004)
/* 32 bits, APB bus R/W */
#define ST90TDS_SRAM_DPORT_REG_ID (0x0005)
/* 32 bits, t_settle/general */
#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006)
/* 16 bits, Q mode read, no length */
#define ST90TDS_FRAME_OUT_REG_ID (0x0007)
#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID)
/* WBF - Control register bit set */
/* next o/p length, bit 11 to 0 */
#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF)
#define ST90TDS_CONT_WUP_BIT (BIT(12))
#define ST90TDS_CONT_RDY_BIT (BIT(13))
#define ST90TDS_CONT_IRQ_ENABLE (BIT(14))
#define ST90TDS_CONT_RDY_ENABLE (BIT(15))
#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15))
/* SPI Config register bit set */
#define ST90TDS_CONFIG_FRAME_BIT (BIT(2))
#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4))
#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3))
#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4))
#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5))
#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6))
#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7))
/* TBD: Sure??? */
#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7))
#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8))
#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9))
/* QueueM */
#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
/* AHB bus */
#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11))
#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
/* APB bus */
#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13))
/* cpu reset */
#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14))
#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15))
/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */
#define ST90TDS_CONF_IRQ_ENABLE (BIT(16))
#define ST90TDS_CONF_RDY_ENABLE (BIT(17))
#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17))
int cw1200_data_read(struct cw1200_common *priv,
void *buf, size_t buf_len);
int cw1200_data_write(struct cw1200_common *priv,
const void *buf, size_t buf_len);
int cw1200_reg_read(struct cw1200_common *priv, u16 addr,
void *buf, size_t buf_len);
int cw1200_reg_write(struct cw1200_common *priv, u16 addr,
const void *buf, size_t buf_len);
static inline int cw1200_reg_read_16(struct cw1200_common *priv,
u16 addr, u16 *val)
{
u32 tmp;
int i;
i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
tmp = le32_to_cpu(tmp);
*val = tmp & 0xffff;
return i;
}
static inline int cw1200_reg_write_16(struct cw1200_common *priv,
u16 addr, u16 val)
{
u32 tmp = val;
tmp = cpu_to_le32(tmp);
return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp));
}
static inline int cw1200_reg_read_32(struct cw1200_common *priv,
u16 addr, u32 *val)
{
int i = cw1200_reg_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
static inline int cw1200_reg_write_32(struct cw1200_common *priv,
u16 addr, u32 val)
{
val = cpu_to_le32(val);
return cw1200_reg_write(priv, addr, &val, sizeof(val));
}
int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
size_t buf_len, u32 prefetch, u16 port_addr);
int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
size_t buf_len);
static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr,
void *buf, size_t buf_len)
{
return cw1200_indirect_read(priv, addr, buf, buf_len,
ST90TDS_CONFIG_PRFETCH_BIT,
ST90TDS_SRAM_DPORT_REG_ID);
}
static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr,
void *buf, size_t buf_len)
{
return cw1200_indirect_read(priv, addr, buf, buf_len,
ST90TDS_CONFIG_AHB_PRFETCH_BIT,
ST90TDS_AHB_DPORT_REG_ID);
}
static inline int cw1200_apb_read_32(struct cw1200_common *priv,
u32 addr, u32 *val)
{
int i = cw1200_apb_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
static inline int cw1200_apb_write_32(struct cw1200_common *priv,
u32 addr, u32 val)
{
val = cpu_to_le32(val);
return cw1200_apb_write(priv, addr, &val, sizeof(val));
}
static inline int cw1200_ahb_read_32(struct cw1200_common *priv,
u32 addr, u32 *val)
{
int i = cw1200_ahb_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
#endif /* CW1200_HWIO_H_INCLUDED */
File diff suppressed because it is too large Load Diff
+144
View File
@@ -0,0 +1,144 @@
/*
* ITP code for ST-Ericsson CW1200 mac80211 driver
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* 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.
*/
#ifndef CW1200_ITP_H_INCLUDED
#define CW1200_ITP_H_INCLUDED
struct cw200_common;
struct wsm_tx_confirm;
struct dentry;
#ifdef CONFIG_CW1200_ITP
/*extern*/ struct ieee80211_channel;
#define TEST_MODE_NO_TEST (0)
#define TEST_MODE_RX_TEST (1)
#define TEST_MODE_TX_TEST (2)
#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
#define ITP_MIN_DATA_SIZE 6
#define ITP_MAX_DATA_SIZE 1600
#define ITP_TIME_THRES_US 10000
#define ITP_US_TO_MS(x) ((x)/1000)
#define ITP_MS_TO_US(x) ((x)*1000)
#define ITP_BUF_SIZE 255
enum cw1200_itp_data_modes {
ITP_DATA_ZEROS,
ITP_DATA_ONES,
ITP_DATA_ZERONES,
ITP_DATA_RANDOM,
ITP_DATA_MAX_MODE,
};
enum cw1200_itp_version_type {
ITP_CHIP_ID,
ITP_FW_VER,
};
enum cw1200_itp_preamble_type {
ITP_PREAMBLE_LONG,
ITP_PREAMBLE_SHORT,
ITP_PREAMBLE_OFDM,
ITP_PREAMBLE_MIXED,
ITP_PREAMBLE_GREENFIELD,
ITP_PREAMBLE_MAX,
};
struct cw1200_itp {
struct cw1200_common *priv;
atomic_t open_count;
atomic_t awaiting_confirm;
struct sk_buff_head log_queue;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
wait_queue_head_t close_wait;
struct ieee80211_channel *saved_channel;
atomic_t stop_tx;
struct delayed_work tx_work;
struct delayed_work tx_finish;
spinlock_t tx_lock;
struct timespec last_sent;
atomic_t test_mode;
int rx_cnt;
long rx_rssi;
int rx_rssi_max;
int rx_rssi_min;
unsigned band;
unsigned ch;
unsigned rate;
unsigned preamble;
unsigned int number;
unsigned data_mode;
int interval_us;
int power;
u8 *data;
int hdr_len;
int data_len;
};
int cw1200_itp_init(struct cw1200_common *priv);
void cw1200_itp_release(struct cw1200_common *priv);
bool cw1200_is_itp(struct cw1200_common *priv);
bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb);
void cw1200_itp_wake_up_tx(struct cw1200_common *priv);
int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data,
size_t *tx_len, int *burst);
bool cw1200_itp_tx_running(struct cw1200_common *priv);
#else /* CONFIG_CW1200_ITP */
static inline int cw1200_itp_init(struct cw1200_common *priv)
{
return 0;
}
static inline void cw1200_itp_release(struct cw1200_common *priv)
{
}
static inline bool cw1200_is_itp(struct cw1200_common *priv)
{
return false;
}
static inline bool cw1200_itp_rxed(struct cw1200_common *priv,
struct sk_buff *skb)
{
return false;
}
static inline void cw1200_itp_consume_txed(struct cw1200_common *priv)
{
}
static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv)
{
}
static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data,
size_t *tx_len, int *burst)
{
return 0;
}
static inline bool cw1200_itp_tx_running(struct cw1200_common *priv)
{
return false;
}
#endif /* CONFIG_CW1200_ITP */
#endif /* CW1200_ITP_H_INCLUDED */
File diff suppressed because it is too large Load Diff

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