From e45d8e534b67580eedd9b4910ccc16d6dd3cceff Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Mon, 6 Apr 2009 15:50:56 -0700 Subject: libertas: add support for Marvell SD8688 chip libertas: add support for Marvell SD8688 chip Use RxPD->pkt_ptr to locate eth803 header in the packet received since SD8688/v10 firmware allows a gap between RxPD and eth803 header. Set SDIO block size to 256 for CMD53. The maximum block size for SD8688 WLAN function is set to 512 in TPLFE_MAX_BLK_SIZE. But using 512 as block size results upto 2K bytes data (4 blocks) being transferred and causes buffer overflow in firmware. Both changes above are backward compatible with earlier firmware versions for SD8385/SD8686. The SDIO_DEVICE_IDs for SD8688 chip are added in include/linux/mmc/sdio_ids.h Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/if_sdio.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/libertas/if_sdio.h') diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h index 533bdfbf5d2..37ada2c29aa 100644 --- a/drivers/net/wireless/libertas/if_sdio.h +++ b/drivers/net/wireless/libertas/if_sdio.h @@ -42,4 +42,6 @@ #define IF_SDIO_EVENT 0x80fc +#define IF_SDIO_BLOCK_SIZE 256 + #endif -- cgit v1.2.3-70-g09d2 From e70a5ac5d27166cfe5bfbe8f63017af3b09d72ce Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Tue, 19 May 2009 19:48:18 -0700 Subject: libertas: define macros for SDIO model numbers replace direct usages of SDIO model numbers with defined macros. Signed-off-by: Bing Zhao Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/if_sdio.c | 18 +++++++++--------- drivers/net/wireless/libertas/if_sdio.h | 4 ++++ 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/libertas/if_sdio.h') diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 55864c10f9f..69d7fd31319 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -66,19 +66,19 @@ struct if_sdio_model { static struct if_sdio_model if_sdio_models[] = { { /* 8385 */ - .model = 0x04, + .model = IF_SDIO_MODEL_8385, .helper = "sd8385_helper.bin", .firmware = "sd8385.bin", }, { /* 8686 */ - .model = 0x0B, + .model = IF_SDIO_MODEL_8686, .helper = "sd8686_helper.bin", .firmware = "sd8686.bin", }, { /* 8688 */ - .model = 0x10, + .model = IF_SDIO_MODEL_8688, .helper = "sd8688_helper.bin", .firmware = "sd8688.bin", }, @@ -118,7 +118,7 @@ static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) int ret, reg; u16 scratch; - if (card->model == 0x04) + if (card->model == IF_SDIO_MODEL_8385) reg = IF_SDIO_SCRATCH_OLD; else reg = IF_SDIO_SCRATCH; @@ -216,7 +216,7 @@ static int if_sdio_handle_event(struct if_sdio_card *card, lbs_deb_enter(LBS_DEB_SDIO); - if (card->model == 0x04) { + if (card->model == IF_SDIO_MODEL_8385) { event = sdio_readb(card->func, IF_SDIO_EVENT, &ret); if (ret) goto out; @@ -829,10 +829,10 @@ static int if_sdio_probe(struct sdio_func *func, if (sscanf(func->card->info[i], "ID: %x", &model) == 1) break; - if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { - model = 4; - break; - } + if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { + model = IF_SDIO_MODEL_8385; + break; + } } if (i == func->card->num_info) { diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h index 37ada2c29aa..cea343f9afd 100644 --- a/drivers/net/wireless/libertas/if_sdio.h +++ b/drivers/net/wireless/libertas/if_sdio.h @@ -12,6 +12,10 @@ #ifndef _LBS_IF_SDIO_H #define _LBS_IF_SDIO_H +#define IF_SDIO_MODEL_8385 0x04 +#define IF_SDIO_MODEL_8686 0x0b +#define IF_SDIO_MODEL_8688 0x10 + #define IF_SDIO_IOPORT 0x00 #define IF_SDIO_H_INT_MASK 0x04 -- cgit v1.2.3-70-g09d2 From b136a1414c94b4bda1057b4ffde26253d2527628 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Tue, 19 May 2009 19:48:19 -0700 Subject: libertas: get SD8688 rx length with one CMD52 Usually, the 16-bit rx length is read from scratch pad registers with two CMD52 transactions: SD8385: IF_SDIO_SCRATCH_OLD (0x80fe/0x80ff) SD8686/SD8688: IF_SDIO_SCRATCH (0x34/0x35) Alternatively, SD8688 firmware offers an enhanced method for driver to read an 8-bit rx length (in units) with a single CMD52: IF_SDIO_RX_UNIT 0x43 is read one time after firmware is ready. IF_SDIO_RX_LEN 0x42 is read every time when rx interrupt is received. Signed-off-by: Bing Zhao Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/if_sdio.c | 57 +++++++++++++++++++++++++++++++-- drivers/net/wireless/libertas/if_sdio.h | 3 ++ 2 files changed, 58 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/libertas/if_sdio.h') diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 69d7fd31319..e998c123e0c 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -107,6 +107,8 @@ struct if_sdio_card { struct workqueue_struct *workqueue; struct work_struct packet_worker; + + u8 rx_unit; }; /********************************************************************/ @@ -136,6 +138,46 @@ static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) return scratch; } +static u8 if_sdio_read_rx_unit(struct if_sdio_card *card) +{ + int ret; + u8 rx_unit; + + rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret); + + if (ret) + rx_unit = 0; + + return rx_unit; +} + +static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err) +{ + int ret; + u16 rx_len; + + switch (card->model) { + case IF_SDIO_MODEL_8385: + case IF_SDIO_MODEL_8686: + rx_len = if_sdio_read_scratch(card, &ret); + break; + case IF_SDIO_MODEL_8688: + default: /* for newer chipsets */ + rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret); + if (!ret) + rx_len <<= card->rx_unit; + else + rx_len = 0xffff; /* invalid length */ + + break; + } + + if (err) + *err = ret; + + return rx_len; +} + static int if_sdio_handle_cmd(struct if_sdio_card *card, u8 *buffer, unsigned size) { @@ -254,7 +296,7 @@ static int if_sdio_card_to_host(struct if_sdio_card *card) lbs_deb_enter(LBS_DEB_SDIO); - size = if_sdio_read_scratch(card, &ret); + size = if_sdio_read_rx_len(card, &ret); if (ret) goto out; @@ -923,10 +965,21 @@ static int if_sdio_probe(struct sdio_func *func, priv->fw_ready = 1; + sdio_claim_host(func); + + /* + * Get rx_unit if the chip is SD8688 or newer. + * SD8385 & SD8686 do not have rx_unit. + */ + if ((card->model != IF_SDIO_MODEL_8385) + && (card->model != IF_SDIO_MODEL_8686)) + card->rx_unit = if_sdio_read_rx_unit(card); + else + card->rx_unit = 0; + /* * Enable interrupts now that everything is set up */ - sdio_claim_host(func); sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); sdio_release_host(func); if (ret) diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h index cea343f9afd..d3a4fbe0769 100644 --- a/drivers/net/wireless/libertas/if_sdio.h +++ b/drivers/net/wireless/libertas/if_sdio.h @@ -44,6 +44,9 @@ #define IF_SDIO_SCRATCH_OLD 0x80fe #define IF_SDIO_FIRMWARE_OK 0xfedc +#define IF_SDIO_RX_LEN 0x42 +#define IF_SDIO_RX_UNIT 0x43 + #define IF_SDIO_EVENT 0x80fc #define IF_SDIO_BLOCK_SIZE 256 -- cgit v1.2.3-70-g09d2 From 2c7e57981f24e9f8b732ecf1c01e16111d21b7a5 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Thu, 21 May 2009 11:32:34 -0700 Subject: libertas: read SD8688 firmware status from new register The scratch pad register is used to store firmware status after firmware is downloaded and initialized. After firmware status is verified OK, the same register is used to store RX packet length. Hence the firmware status code is no longer valid afterwards. SD8688 firmware introduces a new register for firmware status which will never be overwritten. Also add scratch_reg variable to if_sdio_card structure and initialize it based on the model of the card during probe. Signed-off-by: Bing Zhao Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/if_sdio.c | 35 +++++++++++++++++++++++++-------- drivers/net/wireless/libertas/if_sdio.h | 1 + 2 files changed, 28 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/libertas/if_sdio.h') diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 478d766abf8..a7e3fc119b7 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -100,6 +100,7 @@ struct if_sdio_card { int model; unsigned long ioport; + unsigned int scratch_reg; const char *helper; const char *firmware; @@ -119,19 +120,21 @@ struct if_sdio_card { /* I/O */ /********************************************************************/ +/* + * For SD8385/SD8686, this function reads firmware status after + * the image is downloaded, or reads RX packet length when + * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received. + * For SD8688, this function reads firmware status only. + */ static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) { - int ret, reg; + int ret; u16 scratch; - if (card->model == IF_SDIO_MODEL_8385) - reg = IF_SDIO_SCRATCH_OLD; - else - reg = IF_SDIO_SCRATCH; - - scratch = sdio_readb(card->func, reg, &ret); + scratch = sdio_readb(card->func, card->scratch_reg, &ret); if (!ret) - scratch |= sdio_readb(card->func, reg + 1, &ret) << 8; + scratch |= sdio_readb(card->func, card->scratch_reg + 1, + &ret) << 8; if (err) *err = ret; @@ -706,6 +709,8 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card) if (ret) goto out; + lbs_deb_sdio("firmware status = %#x\n", scratch); + if (scratch == IF_SDIO_FIRMWARE_OK) { lbs_deb_sdio("firmware already loaded\n"); goto success; @@ -893,6 +898,20 @@ static int if_sdio_probe(struct sdio_func *func, card->func = func; card->model = model; + + switch (card->model) { + case IF_SDIO_MODEL_8385: + card->scratch_reg = IF_SDIO_SCRATCH_OLD; + break; + case IF_SDIO_MODEL_8686: + card->scratch_reg = IF_SDIO_SCRATCH; + break; + case IF_SDIO_MODEL_8688: + default: /* for newer chipsets */ + card->scratch_reg = IF_SDIO_FW_STATUS; + break; + } + spin_lock_init(&card->lock); card->workqueue = create_workqueue("libertas_sdio"); INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h index d3a4fbe0769..60c9b2fcef0 100644 --- a/drivers/net/wireless/libertas/if_sdio.h +++ b/drivers/net/wireless/libertas/if_sdio.h @@ -42,6 +42,7 @@ #define IF_SDIO_SCRATCH 0x34 #define IF_SDIO_SCRATCH_OLD 0x80fe +#define IF_SDIO_FW_STATUS 0x40 #define IF_SDIO_FIRMWARE_OK 0xfedc #define IF_SDIO_RX_LEN 0x42 -- cgit v1.2.3-70-g09d2