wl1251: fix ELP_CTRL register reads
authorGrazvydas Ignotas <notasas@gmail.com>
Tue, 8 Jun 2010 11:33:31 +0000 (14:33 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 8 Jun 2010 13:31:21 +0000 (09:31 -0400)
Reading the ELP_CTRL register with sdio_readb causes problems because
hardware seems to be performing a write using stuff bits in the request
(those bits contain write data in write request). This indicates that it
actually expects RAW (read after write) type of request, so perform that
when reading ELP_CTRL instead. Also cache last written value so we know
what to write when doing RAW request.

Because of the above it was not possible to wake the chip from ELP power
saving mode, PM had to be disabled to have the driver usable in SDIO
mode. After this patch PM is functional.

For backporting to 2.6.34 or earlier, this patch depends on
6c1f716e8154ee9315534782b9b1eedea0559a24, which adds the
required SDIO funcion.

Signed-off-by: Grazvydas Ignotas <notasas@gmail.com>
Acked-by: Kalle Valo <kvalo@adurom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/wl12xx/wl1251_sdio.c

index c561332e70092b18280fff1843f948b13603289a..b901b6135654f0ce00974411ad4f69628963f596 100644 (file)
 #define SDIO_DEVICE_ID_TI_WL1251       0x9066
 #endif
 
+struct wl1251_sdio {
+       struct sdio_func *func;
+       u32 elp_val;
+};
+
 static struct wl12xx_platform_data *wl12xx_board_data;
 
 static struct sdio_func *wl_to_func(struct wl1251 *wl)
 {
-       return wl->if_priv;
+       struct wl1251_sdio *wl_sdio = wl->if_priv;
+       return wl_sdio->func;
 }
 
 static void wl1251_sdio_interrupt(struct sdio_func *func)
@@ -90,10 +96,17 @@ static void wl1251_sdio_write(struct wl1251 *wl, int addr,
 static void wl1251_sdio_read_elp(struct wl1251 *wl, int addr, u32 *val)
 {
        int ret = 0;
-       struct sdio_func *func = wl_to_func(wl);
-
+       struct wl1251_sdio *wl_sdio = wl->if_priv;
+       struct sdio_func *func = wl_sdio->func;
+
+       /*
+        * The hardware only supports RAW (read after write) access for
+        * reading, regular sdio_readb won't work here (it interprets
+        * the unused bits of CMD52 as write data even if we send read
+        * request).
+        */
        sdio_claim_host(func);
-       *val = sdio_readb(func, addr, &ret);
+       *val = sdio_writeb_readb(func, wl_sdio->elp_val, addr, &ret);
        sdio_release_host(func);
 
        if (ret)
@@ -103,7 +116,8 @@ static void wl1251_sdio_read_elp(struct wl1251 *wl, int addr, u32 *val)
 static void wl1251_sdio_write_elp(struct wl1251 *wl, int addr, u32 val)
 {
        int ret = 0;
-       struct sdio_func *func = wl_to_func(wl);
+       struct wl1251_sdio *wl_sdio = wl->if_priv;
+       struct sdio_func *func = wl_sdio->func;
 
        sdio_claim_host(func);
        sdio_writeb(func, val, addr, &ret);
@@ -111,6 +125,8 @@ static void wl1251_sdio_write_elp(struct wl1251 *wl, int addr, u32 val)
 
        if (ret)
                wl1251_error("sdio_writeb failed (%d)", ret);
+       else
+               wl_sdio->elp_val = val;
 }
 
 static void wl1251_sdio_reset(struct wl1251 *wl)
@@ -197,6 +213,7 @@ static int wl1251_sdio_probe(struct sdio_func *func,
        int ret;
        struct wl1251 *wl;
        struct ieee80211_hw *hw;
+       struct wl1251_sdio *wl_sdio;
 
        hw = wl1251_alloc_hw();
        if (IS_ERR(hw))
@@ -204,6 +221,12 @@ static int wl1251_sdio_probe(struct sdio_func *func,
 
        wl = hw->priv;
 
+       wl_sdio = kzalloc(sizeof(*wl_sdio), GFP_KERNEL);
+       if (wl_sdio == NULL) {
+               ret = -ENOMEM;
+               goto out_free_hw;
+       }
+
        sdio_claim_host(func);
        ret = sdio_enable_func(func);
        if (ret)
@@ -213,7 +236,8 @@ static int wl1251_sdio_probe(struct sdio_func *func,
        sdio_release_host(func);
 
        SET_IEEE80211_DEV(hw, &func->dev);
-       wl->if_priv = func;
+       wl_sdio->func = func;
+       wl->if_priv = wl_sdio;
        wl->if_ops = &wl1251_sdio_ops;
        wl->set_power = wl1251_sdio_set_power;
 
@@ -259,6 +283,8 @@ disable:
        sdio_disable_func(func);
 release:
        sdio_release_host(func);
+       kfree(wl_sdio);
+out_free_hw:
        wl1251_free_hw(wl);
        return ret;
 }
@@ -266,9 +292,11 @@ release:
 static void __devexit wl1251_sdio_remove(struct sdio_func *func)
 {
        struct wl1251 *wl = sdio_get_drvdata(func);
+       struct wl1251_sdio *wl_sdio = wl->if_priv;
 
        if (wl->irq)
                free_irq(wl->irq, wl);
+       kfree(wl_sdio);
        wl1251_free_hw(wl);
 
        sdio_claim_host(func);