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 <notasas@gmail.com>
bool elp;
struct delayed_work elp_work;
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;
/* we can be in psm, but not in elp, we have to differentiate */
bool psm;
int wl1251_init_ieee80211(struct wl1251 *wl);
void wl1251_enable_interrupts(struct wl1251 *wl);
void wl1251_disable_interrupts(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
#define DEFAULT_HW_GEN_MODULATION_TYPE CCK_LONG /* Long Preamble */
#define DEFAULT_HW_GEN_TX_RATE RATE_2MBPS
mutex_unlock(&wl->mutex);
}
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)
{
static void wl1251_convert_filters(struct wl1251 *wl,
unsigned int mac80211_filters)
{
int wl1251_ps_elp_wakeup(struct wl1251 *wl)
{
int wl1251_ps_elp_wakeup(struct wl1251 *wl)
{
- unsigned long timeout, start;
+ DECLARE_COMPLETION_ONSTACK(compl);
+ unsigned long start;
if (delayed_work_pending(&wl->elp_work))
cancel_delayed_work(&wl->elp_work);
if (delayed_work_pending(&wl->elp_work))
cancel_delayed_work(&wl->elp_work);
wl1251_debug(DEBUG_PSM, "waking up chip from elp");
start = jiffies;
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);
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;
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)
}
int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
/* ELP register commands */
#define ELPCTRL_WAKE_UP 0x1
/* 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
#define ELPCTRL_SLEEP 0x0
/* ELP WLAN_READY bit */
#define ELPCTRL_WLAN_READY 0x2
{
struct wl1251 *wl = sdio_get_drvdata(func);
{
struct wl1251 *wl = sdio_get_drvdata(func);
- wl1251_debug(DEBUG_IRQ, "IRQ");
-
/* FIXME should be synchronous for sdio */
/* FIXME should be synchronous for sdio */
- ieee80211_queue_work(wl->hw, &wl->irq_work);
}
static const struct sdio_device_id wl1251_devices[] = {
}
static const struct sdio_device_id wl1251_devices[] = {
{
struct wl1251 *wl = cookie;
{
struct wl1251 *wl = cookie;
- ieee80211_queue_work(wl->hw, &wl->irq_work);
#include "wl1251_reg.h"
#include "wl1251_spi.h"
#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);
wl->use_eeprom = pdata->use_eeprom;
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;
if (ret < 0) {
wl1251_error("request_irq() failed: %d", ret);
goto out_free;