diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx')
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1251_main.c | 65 | ||||
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1251_reg.h | 7 | ||||
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1251_rx.c | 6 | ||||
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1251_sdio.c | 96 | ||||
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_acx.c | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_boot.c | 8 | ||||
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_cmd.c | 14 | ||||
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_conf.h | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/wl12xx/wl1271_main.c | 98 | 
9 files changed, 226 insertions, 72 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 4d479708158..00b24282fc7 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -857,6 +857,7 @@ out:  }  static int wl1251_op_hw_scan(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif,  			     struct cfg80211_scan_request *req)  {  	struct wl1251 *wl = hw->priv; @@ -1196,6 +1197,66 @@ static const struct ieee80211_ops wl1251_ops = {  	.conf_tx = wl1251_op_conf_tx,  }; +static int wl1251_read_eeprom_byte(struct wl1251 *wl, off_t offset, u8 *data) +{ +	unsigned long timeout; + +	wl1251_reg_write32(wl, EE_ADDR, offset); +	wl1251_reg_write32(wl, EE_CTL, EE_CTL_READ); + +	/* EE_CTL_READ clears when data is ready */ +	timeout = jiffies + msecs_to_jiffies(100); +	while (1) { +		if (!(wl1251_reg_read32(wl, EE_CTL) & EE_CTL_READ)) +			break; + +		if (time_after(jiffies, timeout)) +			return -ETIMEDOUT; + +		msleep(1); +	} + +	*data = wl1251_reg_read32(wl, EE_DATA); +	return 0; +} + +static int wl1251_read_eeprom(struct wl1251 *wl, off_t offset, +			      u8 *data, size_t len) +{ +	size_t i; +	int ret; + +	wl1251_reg_write32(wl, EE_START, 0); + +	for (i = 0; i < len; i++) { +		ret = wl1251_read_eeprom_byte(wl, offset + i, &data[i]); +		if (ret < 0) +			return ret; +	} + +	return 0; +} + +static int wl1251_read_eeprom_mac(struct wl1251 *wl) +{ +	u8 mac[ETH_ALEN]; +	int i, ret; + +	wl1251_set_partition(wl, 0, 0, REGISTERS_BASE, REGISTERS_DOWN_SIZE); + +	ret = wl1251_read_eeprom(wl, 0x1c, mac, sizeof(mac)); +	if (ret < 0) { +		wl1251_warning("failed to read MAC address from EEPROM"); +		return ret; +	} + +	/* MAC is stored in reverse order */ +	for (i = 0; i < ETH_ALEN; i++) +		wl->mac_addr[i] = mac[ETH_ALEN - i - 1]; + +	return 0; +} +  static int wl1251_register_hw(struct wl1251 *wl)  {  	int ret; @@ -1231,7 +1292,6 @@ int wl1251_init_ieee80211(struct wl1251 *wl)  	wl->hw->channel_change_time = 10000;  	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | -		IEEE80211_HW_NOISE_DBM |  		IEEE80211_HW_SUPPORTS_PS |  		IEEE80211_HW_BEACON_FILTER |  		IEEE80211_HW_SUPPORTS_UAPSD; @@ -1242,6 +1302,9 @@ int wl1251_init_ieee80211(struct wl1251 *wl)  	wl->hw->queues = 4; +	if (wl->use_eeprom) +		wl1251_read_eeprom_mac(wl); +  	ret = wl1251_register_hw(wl);  	if (ret)  		goto out; diff --git a/drivers/net/wireless/wl12xx/wl1251_reg.h b/drivers/net/wireless/wl12xx/wl1251_reg.h index 0ca3b432605..d16edd9bf06 100644 --- a/drivers/net/wireless/wl12xx/wl1251_reg.h +++ b/drivers/net/wireless/wl12xx/wl1251_reg.h @@ -46,7 +46,14 @@  #define SOR_CFG                        (REGISTERS_BASE + 0x0800)  #define ECPU_CTRL                      (REGISTERS_BASE + 0x0804)  #define HI_CFG                         (REGISTERS_BASE + 0x0808) + +/* EEPROM registers */  #define EE_START                       (REGISTERS_BASE + 0x080C) +#define EE_CTL                         (REGISTERS_BASE + 0x2000) +#define EE_DATA                        (REGISTERS_BASE + 0x2004) +#define EE_ADDR                        (REGISTERS_BASE + 0x2008) + +#define EE_CTL_READ                   2  #define CHIP_ID_B                      (REGISTERS_BASE + 0x5674) diff --git a/drivers/net/wireless/wl12xx/wl1251_rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c index 6f229e0990f..af5c67b4da9 100644 --- a/drivers/net/wireless/wl12xx/wl1251_rx.c +++ b/drivers/net/wireless/wl12xx/wl1251_rx.c @@ -74,12 +74,6 @@ static void wl1251_rx_status(struct wl1251 *wl,  	status->signal = desc->rssi; -	/* -	 * FIXME: guessing that snr needs to be divided by two, otherwise -	 * the values don't make any sense -	 */ -	status->noise = desc->rssi - desc->snr / 2; -  	status->freq = ieee80211_channel_to_frequency(desc->channel);  	status->flag |= RX_FLAG_TSFT; diff --git a/drivers/net/wireless/wl12xx/wl1251_sdio.c b/drivers/net/wireless/wl12xx/wl1251_sdio.c index 2051ef06e9e..d234285c2c8 100644 --- a/drivers/net/wireless/wl12xx/wl1251_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1251_sdio.c @@ -23,6 +23,9 @@  #include <linux/mod_devicetable.h>  #include <linux/mmc/sdio_func.h>  #include <linux/mmc/sdio_ids.h> +#include <linux/platform_device.h> +#include <linux/spi/wl12xx.h> +#include <linux/irq.h>  #include "wl1251.h" @@ -34,6 +37,8 @@  #define SDIO_DEVICE_ID_TI_WL1251	0x9066  #endif +static struct wl12xx_platform_data *wl12xx_board_data; +  static struct sdio_func *wl_to_func(struct wl1251 *wl)  {  	return wl->if_priv; @@ -130,18 +135,60 @@ static void wl1251_sdio_disable_irq(struct wl1251 *wl)  	sdio_release_host(func);  } +/* Interrupts when using dedicated WLAN_IRQ pin */ +static irqreturn_t wl1251_line_irq(int irq, void *cookie) +{ +	struct wl1251 *wl = cookie; + +	ieee80211_queue_work(wl->hw, &wl->irq_work); + +	return IRQ_HANDLED; +} + +static void wl1251_enable_line_irq(struct wl1251 *wl) +{ +	return enable_irq(wl->irq); +} + +static void wl1251_disable_line_irq(struct wl1251 *wl) +{ +	return disable_irq(wl->irq); +} +  static void wl1251_sdio_set_power(bool enable)  {  } -static const struct wl1251_if_operations wl1251_sdio_ops = { +static struct wl1251_if_operations wl1251_sdio_ops = {  	.read = wl1251_sdio_read,  	.write = wl1251_sdio_write,  	.write_elp = wl1251_sdio_write_elp,  	.read_elp = wl1251_sdio_read_elp,  	.reset = wl1251_sdio_reset, -	.enable_irq = wl1251_sdio_enable_irq, -	.disable_irq = wl1251_sdio_disable_irq, +}; + +static int wl1251_platform_probe(struct platform_device *pdev) +{ +	if (pdev->id != -1) { +		wl1251_error("can only handle single device"); +		return -ENODEV; +	} + +	wl12xx_board_data = pdev->dev.platform_data; +	return 0; +} + +/* + * Dummy platform_driver for passing platform_data to this driver, + * until we have a way to pass this through SDIO subsystem or + * some other way. + */ +static struct platform_driver wl1251_platform_driver = { +	.driver = { +		.name	= "wl1251_data", +		.owner	= THIS_MODULE, +	}, +	.probe	= wl1251_platform_probe,  };  static int wl1251_sdio_probe(struct sdio_func *func, @@ -163,20 +210,50 @@ static int wl1251_sdio_probe(struct sdio_func *func,  		goto release;  	sdio_set_block_size(func, 512); +	sdio_release_host(func);  	SET_IEEE80211_DEV(hw, &func->dev);  	wl->if_priv = func;  	wl->if_ops = &wl1251_sdio_ops;  	wl->set_power = wl1251_sdio_set_power; -	sdio_release_host(func); +	if (wl12xx_board_data != NULL) { +		wl->set_power = wl12xx_board_data->set_power; +		wl->irq = wl12xx_board_data->irq; +		wl->use_eeprom = wl12xx_board_data->use_eeprom; +	} + +	if (wl->irq) { +		ret = request_irq(wl->irq, wl1251_line_irq, 0, "wl1251", wl); +		if (ret < 0) { +			wl1251_error("request_irq() failed: %d", ret); +			goto disable; +		} + +		set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); +		disable_irq(wl->irq); + +		wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq; +		wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq; + +		wl1251_info("using dedicated interrupt line"); +	} else { +		wl1251_sdio_ops.enable_irq = wl1251_sdio_enable_irq; +		wl1251_sdio_ops.disable_irq = wl1251_sdio_disable_irq; + +		wl1251_info("using SDIO interrupt"); +	} +  	ret = wl1251_init_ieee80211(wl);  	if (ret) -		goto disable; +		goto out_free_irq;  	sdio_set_drvdata(func, wl);  	return ret; +out_free_irq: +	if (wl->irq) +		free_irq(wl->irq, wl);  disable:  	sdio_claim_host(func);  	sdio_disable_func(func); @@ -189,6 +266,8 @@ static void __devexit wl1251_sdio_remove(struct sdio_func *func)  {  	struct wl1251 *wl = sdio_get_drvdata(func); +	if (wl->irq) +		free_irq(wl->irq, wl);  	wl1251_free_hw(wl);  	sdio_claim_host(func); @@ -208,6 +287,12 @@ static int __init wl1251_sdio_init(void)  {  	int err; +	err = platform_driver_register(&wl1251_platform_driver); +	if (err) { +		wl1251_error("failed to register platform driver: %d", err); +		return err; +	} +  	err = sdio_register_driver(&wl1251_sdio_driver);  	if (err)  		wl1251_error("failed to register sdio driver: %d", err); @@ -217,6 +302,7 @@ static int __init wl1251_sdio_init(void)  static void __exit wl1251_sdio_exit(void)  {  	sdio_unregister_driver(&wl1251_sdio_driver); +	platform_driver_unregister(&wl1251_platform_driver);  	wl1251_notice("unloaded");  } diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index 2ad086efe06..e19e2f8f1e5 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -590,7 +590,7 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl)  	/* BT-WLAN coext parameters */  	for (i = 0; i < CONF_SG_PARAMS_MAX; i++) -		param->params[i] = c->params[i]; +		param->params[i] = cpu_to_le32(c->params[i]);  	param->param_idx = CONF_SG_PARAMS_ALL;  	ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index 8087dc17f29..acb1d9e6b7d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -351,7 +351,7 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl)  static int wl1271_boot_run_firmware(struct wl1271 *wl)  {  	int loop, ret; -	u32 chip_id, interrupt; +	u32 chip_id, intr;  	wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); @@ -368,15 +368,15 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)  	loop = 0;  	while (loop++ < INIT_LOOP) {  		udelay(INIT_LOOP_DELAY); -		interrupt = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); +		intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); -		if (interrupt == 0xffffffff) { +		if (intr == 0xffffffff) {  			wl1271_error("error reading hardware complete "  				     "init indication");  			return -EIO;  		}  		/* check that ACX_INTR_INIT_COMPLETE is enabled */ -		else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) { +		else if (intr & WL1271_ACX_INTR_INIT_COMPLETE) {  			wl1271_write32(wl, ACX_REG_INTERRUPT_ACK,  				       WL1271_ACX_INTR_INIT_COMPLETE);  			break; diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 6b5ba8ec94c..62c11af1d8e 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -37,7 +37,7 @@  #include "wl1271_cmd.h"  #include "wl1271_event.h" -#define WL1271_CMD_POLL_COUNT       5 +#define WL1271_CMD_FAST_POLL_COUNT       50  /*   * send command to firmware @@ -77,11 +77,11 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,  			goto out;  		} -		udelay(10);  		poll_count++; -		if (poll_count == WL1271_CMD_POLL_COUNT) -			wl1271_info("cmd polling took over %d cycles", -				    poll_count); +		if (poll_count < WL1271_CMD_FAST_POLL_COUNT) +			udelay(10); +		else +			msleep(1);  		intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);  	} @@ -318,7 +318,7 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)  	join->rx_config_options = cpu_to_le32(wl->rx_config);  	join->rx_filter_options = cpu_to_le32(wl->rx_filter);  	join->bss_type = bss_type; -	join->basic_rate_set = wl->basic_rate_set; +	join->basic_rate_set = cpu_to_le32(wl->basic_rate_set);  	if (wl->band == IEEE80211_BAND_5GHZ)  		join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ; @@ -615,7 +615,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,  	params->params.scan_options = cpu_to_le16(scan_options);  	params->params.num_probe_requests = probe_requests; -	params->params.tx_rate = rate; +	params->params.tx_rate = cpu_to_le32(rate);  	params->params.tid_trigger = 0;  	params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h index c44307c4bcf..d046d044b5b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_conf.h +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -401,7 +401,7 @@ enum {  };  struct conf_sg_settings { -	__le32 params[CONF_SG_PARAMS_MAX]; +	u32 params[CONF_SG_PARAMS_MAX];  	u8 state;  }; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 814f300c3f1..62e544041d0 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1118,14 +1118,13 @@ static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)  	}  } -static int wl1271_join_channel(struct wl1271 *wl, int channel) +static int wl1271_dummy_join(struct wl1271 *wl)  {  	int ret = 0;  	/* we need to use a dummy BSSID for now */  	static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,  						  0xad, 0xbe, 0xef }; -	wl->channel = channel;  	memcpy(wl->bssid, dummy_bssid, ETH_ALEN);  	/* pass through frames from all BSS */ @@ -1141,7 +1140,47 @@ out:  	return ret;  } -static int wl1271_unjoin_channel(struct wl1271 *wl) +static int wl1271_join(struct wl1271 *wl) +{ +	int ret; + +	ret = wl1271_cmd_join(wl, wl->set_bss_type); +	if (ret < 0) +		goto out; + +	set_bit(WL1271_FLAG_JOINED, &wl->flags); + +	if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) +		goto out; + +	/* +	 * The join command disable the keep-alive mode, shut down its process, +	 * and also clear the template config, so we need to reset it all after +	 * the join. The acx_aid starts the keep-alive process, and the order +	 * of the commands below is relevant. +	 */ +	ret = wl1271_acx_keep_alive_mode(wl, true); +	if (ret < 0) +		goto out; + +	ret = wl1271_acx_aid(wl, wl->aid); +	if (ret < 0) +		goto out; + +	ret = wl1271_cmd_build_klv_null_data(wl); +	if (ret < 0) +		goto out; + +	ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA, +					   ACX_KEEP_ALIVE_TPL_VALID); +	if (ret < 0) +		goto out; + +out: +	return ret; +} + +static int wl1271_unjoin(struct wl1271 *wl)  {  	int ret; @@ -1231,7 +1270,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)  				       "failed %d", ret);  		if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) { -			ret = wl1271_cmd_join(wl, wl->set_bss_type); +			ret = wl1271_join(wl);  			if (ret < 0)  				wl1271_warning("cmd join to update channel "  					       "failed %d", ret); @@ -1241,9 +1280,9 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)  	if (changed & IEEE80211_CONF_CHANGE_IDLE) {  		if (conf->flags & IEEE80211_CONF_IDLE &&  		    test_bit(WL1271_FLAG_JOINED, &wl->flags)) -			wl1271_unjoin_channel(wl); +			wl1271_unjoin(wl);  		else if (!(conf->flags & IEEE80211_CONF_IDLE)) -			wl1271_join_channel(wl, channel); +			wl1271_dummy_join(wl);  		if (conf->flags & IEEE80211_CONF_IDLE) {  			wl->rate_set = wl1271_min_rate_get(wl); @@ -1311,7 +1350,6 @@ static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,  	struct wl1271_filter_params *fp;  	struct netdev_hw_addr *ha;  	struct wl1271 *wl = hw->priv; -	int i;  	if (unlikely(wl->state == WL1271_STATE_OFF))  		return 0; @@ -1520,6 +1558,7 @@ out:  }  static int wl1271_op_hw_scan(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif,  			     struct cfg80211_scan_request *req)  {  	struct wl1271 *wl = hw->priv; @@ -1608,7 +1647,6 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,  	enum wl1271_cmd_ps_mode mode;  	struct wl1271 *wl = hw->priv;  	bool do_join = false; -	bool do_keepalive = false;  	int ret;  	wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -1703,6 +1741,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,  			if (ret < 0)  				goto out_sleep; +			ret = wl1271_build_qos_null_data(wl); +			if (ret < 0) +				goto out_sleep; +  			/* filter out all packets not from this BSSID */  			wl1271_configure_filters(wl, 0); @@ -1747,19 +1789,6 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,  			ret = wl1271_cmd_build_probe_req(wl, NULL, 0,  							 NULL, 0, wl->band); -			/* Enable the keep-alive feature */ -			ret = wl1271_acx_keep_alive_mode(wl, true); -			if (ret < 0) -				goto out_sleep; - -			/* -			 * This is awkward. The keep-alive configs must be done -			 * *after* the join command, because otherwise it will -			 * not work, but it must only be done *once* because -			 * otherwise the firmware will start complaining. -			 */ -			do_keepalive = true; -  			/* enable the connection monitoring feature */  			ret = wl1271_acx_conn_monit_params(wl, true);  			if (ret < 0) @@ -1827,35 +1856,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,  	}  	if (do_join) { -		ret = wl1271_cmd_join(wl, wl->set_bss_type); +		ret = wl1271_join(wl);  		if (ret < 0) {  			wl1271_warning("cmd join failed %d", ret);  			goto out_sleep;  		} -		set_bit(WL1271_FLAG_JOINED, &wl->flags); -	} - -	/* -	 * The JOIN operation shuts down the firmware keep-alive as a side -	 * effect, and the ACX_AID will start the keep-alive as a side effect. -	 * Hence, for non-IBSS, the ACX_AID must always happen *after* the -	 * JOIN operation, and the template config after the ACX_AID. -	 */ -	if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { -		ret = wl1271_acx_aid(wl, wl->aid); -		if (ret < 0) -			goto out_sleep; -	} - -	if (do_keepalive) { -		ret = wl1271_cmd_build_klv_null_data(wl); -		if (ret < 0) -			goto out_sleep; -		ret = wl1271_acx_keep_alive_config( -			wl, CMD_TEMPL_KLV_IDX_NULL_DATA, -			ACX_KEEP_ALIVE_TPL_VALID); -		if (ret < 0) -			goto out_sleep;  	}  out_sleep: @@ -2266,7 +2271,6 @@ int wl1271_init_ieee80211(struct wl1271 *wl)  	wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;  	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | -		IEEE80211_HW_NOISE_DBM |  		IEEE80211_HW_BEACON_FILTER |  		IEEE80211_HW_SUPPORTS_PS |  		IEEE80211_HW_SUPPORTS_UAPSD |  |