Search Linux Wireless

[PATCH 2/4] wl1251: fix ELP_CTRL register accesses when using SDIO

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



For some unknown reason ELP_CTRL can't be accesed using
sdio_memcpy_* functions (any attemts to do so result in timeouts):

 wl1251: ERROR sdio write failed (-110)
 wl1251: ERROR sdio read failed (-110)
 wl1251: WARNING WLAN not ready

To fix this, add special IO functions for ELP_CTRL access that are
using sdio_readb/sdio_writeb. Similar handling is done in TI
reference driver from Android code drop.

Signed-off-by: Grazvydas Ignotas <notasas@xxxxxxxxx>
Cc: Bob Copeland <me@xxxxxxxxxxxxxxx>
---
 drivers/net/wireless/wl12xx/wl1251.h      |    2 ++
 drivers/net/wireless/wl12xx/wl1251_io.h   |   20 ++++++++++++++++++++
 drivers/net/wireless/wl12xx/wl1251_main.c |    4 ++--
 drivers/net/wireless/wl12xx/wl1251_ps.c   |    8 ++++----
 drivers/net/wireless/wl12xx/wl1251_sdio.c |   28 ++++++++++++++++++++++++++++
 5 files changed, 56 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h
index 37c61c1..4f5f02a 100644
--- a/drivers/net/wireless/wl12xx/wl1251.h
+++ b/drivers/net/wireless/wl12xx/wl1251.h
@@ -256,6 +256,8 @@ struct wl1251_debugfs {
 struct wl1251_if_operations {
 	void (*read)(struct wl1251 *wl, int addr, void *buf, size_t len);
 	void (*write)(struct wl1251 *wl, int addr, void *buf, size_t len);
+	void (*read_elp)(struct wl1251 *wl, int addr, u32 *val);
+	void (*write_elp)(struct wl1251 *wl, int addr, u32 val);
 	void (*reset)(struct wl1251 *wl);
 	void (*enable_irq)(struct wl1251 *wl);
 	void (*disable_irq)(struct wl1251 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1251_io.h b/drivers/net/wireless/wl12xx/wl1251_io.h
index b89d2ac..c545e9d 100644
--- a/drivers/net/wireless/wl12xx/wl1251_io.h
+++ b/drivers/net/wireless/wl12xx/wl1251_io.h
@@ -48,6 +48,26 @@ static inline void wl1251_write32(struct wl1251 *wl, int addr, u32 val)
 	wl->if_ops->write(wl, addr, &val, sizeof(u32));
 }
 
+static inline u32 wl1251_read_elp(struct wl1251 *wl, int addr)
+{
+	u32 response;
+
+	if (wl->if_ops->read_elp)
+		wl->if_ops->read_elp(wl, addr, &response);
+	else
+		wl->if_ops->read(wl, addr, &response, sizeof(u32));
+
+	return response;
+}
+
+static inline void wl1251_write_elp(struct wl1251 *wl, int addr, u32 val)
+{
+	if (wl->if_ops->write_elp)
+		wl->if_ops->write_elp(wl, addr, val);
+	else
+		wl->if_ops->write(wl, addr, &val, sizeof(u32));
+}
+
 /* Memory target IO, address is translated to partition 0 */
 void wl1251_mem_read(struct wl1251 *wl, int addr, void *buf, size_t len);
 void wl1251_mem_write(struct wl1251 *wl, int addr, void *buf, size_t len);
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index 24ae6a3..0155653 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -146,8 +146,8 @@ static void wl1251_fw_wakeup(struct wl1251 *wl)
 	u32 elp_reg;
 
 	elp_reg = ELPCTRL_WAKE_UP;
-	wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
-	elp_reg = wl1251_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
+	wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
+	elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
 
 	if (!(elp_reg & ELPCTRL_WLAN_READY))
 		wl1251_warning("WLAN not ready");
diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c
index 851dfb6..b55cb2b 100644
--- a/drivers/net/wireless/wl12xx/wl1251_ps.c
+++ b/drivers/net/wireless/wl12xx/wl1251_ps.c
@@ -45,7 +45,7 @@ void wl1251_elp_work(struct work_struct *work)
 		goto out;
 
 	wl1251_debug(DEBUG_PSM, "chip to elp");
-	wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
+	wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
 	wl->elp = true;
 
 out:
@@ -79,9 +79,9 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl)
 	start = jiffies;
 	timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT);
 
-	wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
+	wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
 
-	elp_reg = wl1251_read32(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
@@ -93,7 +93,7 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl)
 			return -ETIMEDOUT;
 		}
 		msleep(1);
-		elp_reg = wl1251_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
+		elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
 	}
 
 	wl1251_debug(DEBUG_PSM, "wakeup time: %u ms",
diff --git a/drivers/net/wireless/wl12xx/wl1251_sdio.c b/drivers/net/wireless/wl12xx/wl1251_sdio.c
index 0aceb91..4df1a20 100644
--- a/drivers/net/wireless/wl12xx/wl1251_sdio.c
+++ b/drivers/net/wireless/wl12xx/wl1251_sdio.c
@@ -81,6 +81,32 @@ static void wl1251_sdio_write(struct wl1251 *wl, int addr,
 	sdio_release_host(func);
 }
 
+static void wl1251_sdio_read_elp(struct wl1251 *wl, int addr, u32 *val)
+{
+	int ret = 0;
+	struct sdio_func *func = wl_to_func(wl);
+
+	sdio_claim_host(func);
+	*val = sdio_readb(func, addr, &ret);
+	sdio_release_host(func);
+
+	if (ret)
+		wl1251_error("sdio_readb failed (%d)", ret);
+}
+
+static void wl1251_sdio_write_elp(struct wl1251 *wl, int addr, u32 val)
+{
+	int ret = 0;
+	struct sdio_func *func = wl_to_func(wl);
+
+	sdio_claim_host(func);
+	sdio_writeb(func, val, addr, &ret);
+	sdio_release_host(func);
+
+	if (ret)
+		wl1251_error("sdio_writeb failed (%d)", ret);
+}
+
 static void wl1251_sdio_reset(struct wl1251 *wl)
 {
 }
@@ -110,6 +136,8 @@ static void wl1251_sdio_set_power(bool enable)
 static const 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,
-- 
1.6.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux