From: Grazvydas Ignotas Date: Wed, 27 Oct 2010 21:39:40 +0000 (+0300) Subject: wl1251: use ELP wakeup interrupt instead of polling X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-wifi.git;a=commitdiff_plain;h=9388be2a6aedd397b931b1beb9aadbaa85db0efd wl1251: use ELP wakeup interrupt instead of polling The chip can generate an interrupt on ELP wakeup, make use of it instead of polling the status register. This reduces wakeup time to ~7ms from ~8-20ms (the wait loop was sleeping, so could take up to 20ms depending on how system timer is configured). Note that DEBUG_PSM does not report this accurately as it only measures jiffies. This patch also moves atomic portion of interrupt processing to main.c to avoid code duplication. Signed-off-by: Grazvydas Ignotas --- diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index 46dd160..85e4640 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h @@ -359,6 +359,7 @@ struct wl1251 { bool elp; struct delayed_work elp_work; + struct completion *elp_compl; /* we can be in psm, but not in elp, we have to differentiate */ bool psm; @@ -394,6 +395,7 @@ int wl1251_free_hw(struct wl1251 *wl); int wl1251_init_ieee80211(struct wl1251 *wl); void wl1251_enable_interrupts(struct wl1251 *wl); void wl1251_disable_interrupts(struct wl1251 *wl); +void wl1251_irq(struct wl1251 *wl); #define DEFAULT_HW_GEN_MODULATION_TYPE CCK_LONG /* Long Preamble */ #define DEFAULT_HW_GEN_TX_RATE RATE_2MBPS diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 8b3e78e..fcc6829 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -323,6 +323,25 @@ out: mutex_unlock(&wl->mutex); } +/* Atomic irq handler */ +void wl1251_irq(struct wl1251 *wl) +{ + unsigned long flags; + + wl1251_debug(DEBUG_IRQ, "IRQ"); + + /* complete the ELP completion */ + spin_lock_irqsave(&wl->wl_lock, flags); + if (wl->elp_compl) { + complete(wl->elp_compl); + wl->elp_compl = NULL; + } + spin_unlock_irqrestore(&wl->wl_lock, flags); + + ieee80211_queue_work(wl->hw, &wl->irq_work); +} +EXPORT_SYMBOL_GPL(wl1251_irq); + static void wl1251_convert_filters(struct wl1251 *wl, unsigned int mac80211_filters) { diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c index f5b90a1..4e4c661 100644 --- a/drivers/net/wireless/wl12xx/wl1251_ps.c +++ b/drivers/net/wireless/wl12xx/wl1251_ps.c @@ -67,8 +67,10 @@ void wl1251_ps_elp_sleep(struct wl1251 *wl) int wl1251_ps_elp_wakeup(struct wl1251 *wl) { - unsigned long timeout, start; + DECLARE_COMPLETION_ONSTACK(compl); + unsigned long start; u32 elp_reg; + int ret; if (delayed_work_pending(&wl->elp_work)) cancel_delayed_work(&wl->elp_work); @@ -79,31 +81,42 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl) wl1251_debug(DEBUG_PSM, "waking up chip from elp"); start = jiffies; - timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT); - - wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); - /* - * FIXME: we should wait for irq from chip but, as a temporary - * solution to simplify locking, let's poll instead - */ - while (!(elp_reg & ELPCTRL_WLAN_READY)) { - if (time_after(jiffies, timeout)) { - wl1251_error("elp wakeup timeout"); - return -ETIMEDOUT; + wl->elp_compl = &compl; + wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, + ELPCTRL_WAKE_UP | ELPCTRL_IRQ_SRC); + + elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); + if (!(elp_reg & ELPCTRL_WLAN_READY)) { + ret = wait_for_completion_timeout( + &compl, msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT)); + if (ret == 0) { + wl1251_error("ELP wakeup timeout"); + ret = -ETIMEDOUT; + goto err; + } else if (ret < 0) { + wl1251_error("ELP wakeup completion error."); + goto err; } - msleep(1); - elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); } + wl->elp_compl = NULL; + + /* clear IRQ_SRC to switch irq source back from WLAN_RDY to HOST_IRQ */ + wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); + wl1251_debug(DEBUG_PSM, "wakeup time: %u ms", jiffies_to_msecs(jiffies - start)); wl->elp = false; return 0; + +err: + wl->elp_compl = NULL; + return ret; } int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) diff --git a/drivers/net/wireless/wl12xx/wl1251_reg.h b/drivers/net/wireless/wl12xx/wl1251_reg.h index d16edd9..005f115 100644 --- a/drivers/net/wireless/wl12xx/wl1251_reg.h +++ b/drivers/net/wireless/wl12xx/wl1251_reg.h @@ -37,7 +37,7 @@ /* ELP register commands */ #define ELPCTRL_WAKE_UP 0x1 -#define ELPCTRL_WAKE_UP_WLAN_READY 0x5 +#define ELPCTRL_IRQ_SRC 0x4 #define ELPCTRL_SLEEP 0x0 /* ELP WLAN_READY bit */ #define ELPCTRL_WLAN_READY 0x2 diff --git a/drivers/net/wireless/wl12xx/wl1251_sdio.c b/drivers/net/wireless/wl12xx/wl1251_sdio.c index 0228e97..f1dc7ad 100644 --- a/drivers/net/wireless/wl12xx/wl1251_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1251_sdio.c @@ -54,10 +54,8 @@ static void wl1251_sdio_interrupt(struct sdio_func *func) { struct wl1251 *wl = sdio_get_drvdata(func); - wl1251_debug(DEBUG_IRQ, "IRQ"); - /* FIXME should be synchronous for sdio */ - ieee80211_queue_work(wl->hw, &wl->irq_work); + wl1251_irq(wl); } static const struct sdio_device_id wl1251_devices[] = { @@ -170,7 +168,7 @@ static irqreturn_t wl1251_line_irq(int irq, void *cookie) { struct wl1251 *wl = cookie; - ieee80211_queue_work(wl->hw, &wl->irq_work); + wl1251_irq(wl); return IRQ_HANDLED; } diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c index 601bde2..113df61 100644 --- a/drivers/net/wireless/wl12xx/wl1251_spi.c +++ b/drivers/net/wireless/wl12xx/wl1251_spi.c @@ -34,15 +34,11 @@ #include "wl1251_reg.h" #include "wl1251_spi.h" -static irqreturn_t wl1251_irq(int irq, void *cookie) +static irqreturn_t wl1251_line_irq(int irq, void *cookie) { - struct wl1251 *wl; - - wl1251_debug(DEBUG_IRQ, "IRQ"); - - wl = cookie; + struct wl1251 *wl = cookie; - ieee80211_queue_work(wl->hw, &wl->irq_work); + wl1251_irq(wl); return IRQ_HANDLED; } @@ -275,7 +271,7 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi) wl->use_eeprom = pdata->use_eeprom; - ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl); + ret = request_irq(wl->irq, wl1251_line_irq, 0, DRIVER_NAME, wl); if (ret < 0) { wl1251_error("request_irq() failed: %d", ret); goto out_free;