diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/sdio.c')
| -rw-r--r-- | drivers/net/wireless/wl12xx/sdio.c | 95 | 
1 files changed, 91 insertions, 4 deletions
diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index b1c7d031c39..536e5065454 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -51,6 +51,13 @@ static const struct sdio_device_id wl1271_devices[] = {  };  MODULE_DEVICE_TABLE(sdio, wl1271_devices); +static void wl1271_sdio_set_block_size(struct wl1271 *wl, unsigned int blksz) +{ +	sdio_claim_host(wl->if_priv); +	sdio_set_block_size(wl->if_priv, blksz); +	sdio_release_host(wl->if_priv); +} +  static inline struct sdio_func *wl_to_func(struct wl1271 *wl)  {  	return wl->if_priv; @@ -75,6 +82,16 @@ static irqreturn_t wl1271_hardirq(int irq, void *cookie)  		complete(wl->elp_compl);  		wl->elp_compl = NULL;  	} + +	if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) { +		/* don't enqueue a work right now. mark it as pending */ +		set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags); +		wl1271_debug(DEBUG_IRQ, "should not enqueue work"); +		disable_irq_nosync(wl->irq); +		pm_wakeup_event(wl1271_sdio_wl_to_dev(wl), 0); +		spin_unlock_irqrestore(&wl->wl_lock, flags); +		return IRQ_HANDLED; +	}  	spin_unlock_irqrestore(&wl->wl_lock, flags);  	return IRQ_WAKE_THREAD; @@ -203,7 +220,8 @@ static struct wl1271_if_operations sdio_ops = {  	.power		= wl1271_sdio_set_power,  	.dev		= wl1271_sdio_wl_to_dev,  	.enable_irq	= wl1271_sdio_enable_interrupts, -	.disable_irq	= wl1271_sdio_disable_interrupts +	.disable_irq	= wl1271_sdio_disable_interrupts, +	.set_block_size = wl1271_sdio_set_block_size,  };  static int __devinit wl1271_probe(struct sdio_func *func, @@ -212,6 +230,8 @@ static int __devinit wl1271_probe(struct sdio_func *func,  	struct ieee80211_hw *hw;  	const struct wl12xx_platform_data *wlan_data;  	struct wl1271 *wl; +	unsigned long irqflags; +	mmc_pm_flag_t mmcflags;  	int ret;  	/* We are only able to handle the wlan function */ @@ -230,6 +250,9 @@ static int __devinit wl1271_probe(struct sdio_func *func,  	/* Grab access to FN0 for ELP reg. */  	func->card->quirks |= MMC_QUIRK_LENIENT_FN0; +	/* Use block mode for transferring over one block size of data */ +	func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; +  	wlan_data = wl12xx_get_platform_data();  	if (IS_ERR(wlan_data)) {  		ret = PTR_ERR(wlan_data); @@ -239,17 +262,34 @@ static int __devinit wl1271_probe(struct sdio_func *func,  	wl->irq = wlan_data->irq;  	wl->ref_clock = wlan_data->board_ref_clock; +	wl->tcxo_clock = wlan_data->board_tcxo_clock; +	wl->platform_quirks = wlan_data->platform_quirks; + +	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) +		irqflags = IRQF_TRIGGER_RISING; +	else +		irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;  	ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, -				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT, +				   irqflags,  				   DRIVER_NAME, wl);  	if (ret < 0) {  		wl1271_error("request_irq() failed: %d", ret);  		goto out_free;  	} +	enable_irq_wake(wl->irq); +	device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1); +  	disable_irq(wl->irq); +	/* if sdio can keep power while host is suspended, enable wow */ +	mmcflags = sdio_get_host_pm_caps(func); +	wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags); + +	if (mmcflags & MMC_PM_KEEP_POWER) +		hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; +  	ret = wl1271_init_ieee80211(wl);  	if (ret)  		goto out_irq; @@ -284,19 +324,61 @@ static void __devexit wl1271_remove(struct sdio_func *func)  	pm_runtime_get_noresume(&func->dev);  	wl1271_unregister_hw(wl); +	device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0); +	disable_irq_wake(wl->irq);  	free_irq(wl->irq, wl);  	wl1271_free_hw(wl);  } +#ifdef CONFIG_PM  static int wl1271_suspend(struct device *dev)  {  	/* Tell MMC/SDIO core it's OK to power down the card  	 * (if it isn't already), but not to remove it completely */ -	return 0; +	struct sdio_func *func = dev_to_sdio_func(dev); +	struct wl1271 *wl = sdio_get_drvdata(func); +	mmc_pm_flag_t sdio_flags; +	int ret = 0; + +	wl1271_debug(DEBUG_MAC80211, "wl1271 suspend. wow_enabled: %d", +		     wl->wow_enabled); + +	/* check whether sdio should keep power */ +	if (wl->wow_enabled) { +		sdio_flags = sdio_get_host_pm_caps(func); + +		if (!(sdio_flags & MMC_PM_KEEP_POWER)) { +			wl1271_error("can't keep power while host " +				     "is suspended"); +			ret = -EINVAL; +			goto out; +		} + +		/* keep power while host suspended */ +		ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +		if (ret) { +			wl1271_error("error while trying to keep power"); +			goto out; +		} + +		/* release host */ +		sdio_release_host(func); +	} +out: +	return ret;  }  static int wl1271_resume(struct device *dev)  { +	struct sdio_func *func = dev_to_sdio_func(dev); +	struct wl1271 *wl = sdio_get_drvdata(func); + +	wl1271_debug(DEBUG_MAC80211, "wl1271 resume"); +	if (wl->wow_enabled) { +		/* claim back host */ +		sdio_claim_host(func); +	} +  	return 0;  } @@ -304,15 +386,18 @@ static const struct dev_pm_ops wl1271_sdio_pm_ops = {  	.suspend	= wl1271_suspend,  	.resume		= wl1271_resume,  }; +#endif  static struct sdio_driver wl1271_sdio_driver = {  	.name		= "wl1271_sdio",  	.id_table	= wl1271_devices,  	.probe		= wl1271_probe,  	.remove		= __devexit_p(wl1271_remove), +#ifdef CONFIG_PM  	.drv = {  		.pm = &wl1271_sdio_pm_ops,  	}, +#endif  };  static int __init wl1271_init(void) @@ -343,4 +428,6 @@ MODULE_LICENSE("GPL");  MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");  MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");  MODULE_FIRMWARE(WL1271_FW_NAME); -MODULE_FIRMWARE(WL1271_AP_FW_NAME); +MODULE_FIRMWARE(WL128X_FW_NAME); +MODULE_FIRMWARE(WL127X_AP_FW_NAME); +MODULE_FIRMWARE(WL128X_AP_FW_NAME);  |