···282282 ---help---283283 A driver for Marvell Libertas 8385 CompactFlash devices.284284285285+config LIBERTAS_SDIO286286+ tristate "Marvell Libertas 8385 and 8686 SDIO 802.11b/g cards"287287+ depends on LIBERTAS && MMC288288+ ---help---289289+ A driver for Marvell Libertas 8385 and 8686 SDIO devices.290290+285291config LIBERTAS_DEBUG286292 bool "Enable full debugging output in the Libertas module."287293 depends on LIBERTAS
···11+/*22+ * linux/drivers/net/wireless/libertas/if_sdio.c33+ *44+ * Copyright 2007 Pierre Ossman55+ *66+ * Inspired by if_cs.c, Copyright 2007 Holger Schurig77+ *88+ * This program is free software; you can redistribute it and/or modify99+ * it under the terms of the GNU General Public License as published by1010+ * the Free Software Foundation; either version 2 of the License, or (at1111+ * your option) any later version.1212+ *1313+ * This hardware has more or less no CMD53 support, so all registers1414+ * must be accessed using sdio_readb()/sdio_writeb().1515+ *1616+ * Transfers must be in one transaction or the firmware goes bonkers.1717+ * This means that the transfer must either be small enough to do a1818+ * byte based transfer or it must be padded to a multiple of the1919+ * current block size.2020+ *2121+ * As SDIO is still new to the kernel, it is unfortunately common with2222+ * bugs in the host controllers related to that. One such bug is that 2323+ * controllers cannot do transfers that aren't a multiple of 4 bytes.2424+ * If you don't have time to fix the host controller driver, you can2525+ * work around the problem by modifying if_sdio_host_to_card() and2626+ * if_sdio_card_to_host() to pad the data.2727+ */2828+2929+#include <linux/moduleparam.h>3030+#include <linux/firmware.h>3131+#include <linux/netdevice.h>3232+#include <linux/delay.h>3333+#include <linux/mmc/card.h>3434+#include <linux/mmc/sdio_func.h>3535+#include <linux/mmc/sdio_ids.h>3636+3737+#include "host.h"3838+#include "decl.h"3939+#include "defs.h"4040+#include "dev.h"4141+#include "if_sdio.h"4242+4343+static char *libertas_helper_name = NULL;4444+module_param_named(helper_name, libertas_helper_name, charp, 0644);4545+4646+static char *libertas_fw_name = NULL;4747+module_param_named(fw_name, libertas_fw_name, charp, 0644);4848+4949+static const struct sdio_device_id if_sdio_ids[] = {5050+ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_LIBERTAS) },5151+ { /* end: all zeroes */ },5252+};5353+5454+MODULE_DEVICE_TABLE(sdio, if_sdio_ids);5555+5656+struct if_sdio_model {5757+ int model;5858+ const char *helper;5959+ const char *firmware;6060+};6161+6262+static struct if_sdio_model if_sdio_models[] = {6363+ {6464+ /* 8385 */6565+ .model = 0x04,6666+ .helper = "sd8385_helper.bin",6767+ .firmware = "sd8385.bin",6868+ },6969+ {7070+ /* 8686 */7171+ .model = 0x0B,7272+ .helper = "sd8686_helper.bin",7373+ .firmware = "sd8686.bin",7474+ },7575+};7676+7777+struct if_sdio_packet {7878+ struct if_sdio_packet *next;7979+ u16 nb;8080+ u8 buffer[0] __attribute__((aligned(4)));8181+};8282+8383+struct if_sdio_card {8484+ struct sdio_func *func;8585+ wlan_private *priv;8686+8787+ int model;8888+ unsigned long ioport;8989+9090+ const char *helper;9191+ const char *firmware;9292+9393+ u8 buffer[65536];9494+ u8 int_cause;9595+ u32 event;9696+9797+ spinlock_t lock;9898+ struct if_sdio_packet *packets;9999+ struct work_struct packet_worker;100100+};101101+102102+/********************************************************************/103103+/* I/O */104104+/********************************************************************/105105+106106+static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)107107+{108108+ int ret, reg;109109+ u16 scratch;110110+111111+ if (card->model == 0x04)112112+ reg = IF_SDIO_SCRATCH_OLD;113113+ else114114+ reg = IF_SDIO_SCRATCH;115115+116116+ scratch = sdio_readb(card->func, reg, &ret);117117+ if (!ret)118118+ scratch |= sdio_readb(card->func, reg + 1, &ret) << 8;119119+120120+ if (err)121121+ *err = ret;122122+123123+ if (ret)124124+ return 0xffff;125125+126126+ return scratch;127127+}128128+129129+static int if_sdio_handle_cmd(struct if_sdio_card *card,130130+ u8 *buffer, unsigned size)131131+{132132+ int ret;133133+ unsigned long flags;134134+135135+ lbs_deb_enter(LBS_DEB_SDIO);136136+137137+ spin_lock_irqsave(&card->priv->adapter->driver_lock, flags);138138+139139+ if (!card->priv->adapter->cur_cmd) {140140+ lbs_deb_sdio("discarding spurious response\n");141141+ ret = 0;142142+ goto out;143143+ }144144+145145+ if (size > MRVDRV_SIZE_OF_CMD_BUFFER) {146146+ lbs_deb_sdio("response packet too large (%d bytes)\n",147147+ (int)size);148148+ ret = -E2BIG;149149+ goto out;150150+ }151151+152152+ memcpy(card->priv->adapter->cur_cmd->bufvirtualaddr, buffer, size);153153+ card->priv->upld_len = size;154154+155155+ card->int_cause |= MRVDRV_CMD_UPLD_RDY;156156+157157+ libertas_interrupt(card->priv->dev);158158+159159+ ret = 0;160160+161161+out:162162+ spin_unlock_irqrestore(&card->priv->adapter->driver_lock, flags);163163+164164+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);165165+166166+ return ret;167167+}168168+169169+static int if_sdio_handle_data(struct if_sdio_card *card,170170+ u8 *buffer, unsigned size)171171+{172172+ int ret;173173+ struct sk_buff *skb;174174+ char *data;175175+176176+ lbs_deb_enter(LBS_DEB_SDIO);177177+178178+ if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {179179+ lbs_deb_sdio("response packet too large (%d bytes)\n",180180+ (int)size);181181+ ret = -E2BIG;182182+ goto out;183183+ }184184+185185+ skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);186186+ if (!skb) {187187+ ret = -ENOMEM;188188+ goto out;189189+ }190190+191191+ data = skb_put(skb, size);192192+193193+ memcpy(data, buffer, size);194194+195195+ libertas_process_rxed_packet(card->priv, skb);196196+197197+ ret = 0;198198+199199+out:200200+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);201201+202202+ return ret;203203+}204204+205205+static int if_sdio_handle_event(struct if_sdio_card *card,206206+ u8 *buffer, unsigned size)207207+{208208+ int ret;209209+ unsigned long flags;210210+ u32 event;211211+212212+ lbs_deb_enter(LBS_DEB_SDIO);213213+214214+ if (card->model == 0x04) {215215+ event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);216216+ if (ret)217217+ goto out;218218+ } else {219219+ if (size < 4) {220220+ lbs_deb_sdio("event packet too small (%d bytes)\n",221221+ (int)size);222222+ ret = -EINVAL;223223+ goto out;224224+ }225225+ event = buffer[3] << 24;226226+ event |= buffer[2] << 16;227227+ event |= buffer[1] << 8;228228+ event |= buffer[0] << 0;229229+ event <<= SBI_EVENT_CAUSE_SHIFT;230230+ }231231+232232+ spin_lock_irqsave(&card->priv->adapter->driver_lock, flags);233233+234234+ card->event = event;235235+ card->int_cause |= MRVDRV_CARDEVENT;236236+237237+ libertas_interrupt(card->priv->dev);238238+239239+ spin_unlock_irqrestore(&card->priv->adapter->driver_lock, flags);240240+241241+ ret = 0;242242+243243+out:244244+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);245245+246246+ return ret;247247+}248248+249249+static int if_sdio_card_to_host(struct if_sdio_card *card)250250+{251251+ int ret;252252+ u8 status;253253+ u16 size, type, chunk;254254+ unsigned long timeout;255255+256256+ lbs_deb_enter(LBS_DEB_SDIO);257257+258258+ size = if_sdio_read_scratch(card, &ret);259259+ if (ret)260260+ goto out;261261+262262+ if (size < 4) {263263+ lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n",264264+ (int)size);265265+ ret = -EINVAL;266266+ goto out;267267+ }268268+269269+ timeout = jiffies + HZ;270270+ while (1) {271271+ status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);272272+ if (ret)273273+ goto out;274274+ if (status & IF_SDIO_IO_RDY)275275+ break;276276+ if (time_after(jiffies, timeout)) {277277+ ret = -ETIMEDOUT;278278+ goto out;279279+ }280280+ mdelay(1);281281+ }282282+283283+ /*284284+ * The transfer must be in one transaction or the firmware285285+ * goes suicidal.286286+ */287287+ chunk = size;288288+ if ((chunk > card->func->cur_blksize) || (chunk > 512)) {289289+ chunk = (chunk + card->func->cur_blksize - 1) /290290+ card->func->cur_blksize * card->func->cur_blksize;291291+ }292292+293293+ ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);294294+ if (ret)295295+ goto out;296296+297297+ chunk = card->buffer[0] | (card->buffer[1] << 8);298298+ type = card->buffer[2] | (card->buffer[3] << 8);299299+300300+ lbs_deb_sdio("packet of type %d and size %d bytes\n",301301+ (int)type, (int)chunk);302302+303303+ if (chunk > size) {304304+ lbs_deb_sdio("packet fragment (%d > %d)\n",305305+ (int)chunk, (int)size);306306+ ret = -EINVAL;307307+ goto out;308308+ }309309+310310+ if (chunk < size) {311311+ lbs_deb_sdio("packet fragment (%d < %d)\n",312312+ (int)chunk, (int)size);313313+ }314314+315315+ switch (type) {316316+ case MVMS_CMD:317317+ ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4);318318+ if (ret)319319+ goto out;320320+ break;321321+ case MVMS_DAT:322322+ ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4);323323+ if (ret)324324+ goto out;325325+ break;326326+ case MVMS_EVENT:327327+ ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4);328328+ if (ret)329329+ goto out;330330+ break;331331+ default:332332+ lbs_deb_sdio("invalid type (%d) from firmware\n",333333+ (int)type);334334+ ret = -EINVAL;335335+ goto out;336336+ }337337+338338+out:339339+ if (ret)340340+ lbs_pr_err("problem fetching packet from firmware\n");341341+342342+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);343343+344344+ return ret;345345+}346346+347347+static void if_sdio_host_to_card_worker(struct work_struct *work)348348+{349349+ struct if_sdio_card *card;350350+ struct if_sdio_packet *packet;351351+ unsigned long timeout;352352+ u8 status;353353+ int ret;354354+ unsigned long flags;355355+356356+ lbs_deb_enter(LBS_DEB_SDIO);357357+358358+ card = container_of(work, struct if_sdio_card, packet_worker);359359+360360+ while (1) {361361+ spin_lock_irqsave(&card->lock, flags);362362+ packet = card->packets;363363+ if (packet)364364+ card->packets = packet->next;365365+ spin_unlock_irqrestore(&card->lock, flags);366366+367367+ if (!packet)368368+ break;369369+370370+ sdio_claim_host(card->func);371371+372372+ timeout = jiffies + HZ;373373+ while (1) {374374+ status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);375375+ if (ret)376376+ goto release;377377+ if (status & IF_SDIO_IO_RDY)378378+ break;379379+ if (time_after(jiffies, timeout)) {380380+ ret = -ETIMEDOUT;381381+ goto release;382382+ }383383+ mdelay(1);384384+ }385385+386386+ ret = sdio_writesb(card->func, card->ioport,387387+ packet->buffer, packet->nb);388388+ if (ret)389389+ goto release;390390+release:391391+ sdio_release_host(card->func);392392+393393+ kfree(packet);394394+ }395395+396396+ lbs_deb_leave(LBS_DEB_SDIO);397397+}398398+399399+/********************************************************************/400400+/* Firmware */401401+/********************************************************************/402402+403403+static int if_sdio_prog_helper(struct if_sdio_card *card)404404+{405405+ int ret;406406+ u8 status;407407+ const struct firmware *fw;408408+ unsigned long timeout;409409+ u8 *chunk_buffer;410410+ u32 chunk_size;411411+ u8 *firmware;412412+ size_t size;413413+414414+ lbs_deb_enter(LBS_DEB_SDIO);415415+416416+ ret = request_firmware(&fw, card->helper, &card->func->dev);417417+ if (ret) {418418+ lbs_pr_err("can't load helper firmware\n");419419+ goto out;420420+ }421421+422422+ chunk_buffer = kzalloc(64, GFP_KERNEL);423423+ if (!chunk_buffer) {424424+ ret = -ENOMEM;425425+ goto release_fw;426426+ }427427+428428+ sdio_claim_host(card->func);429429+430430+ ret = sdio_set_block_size(card->func, 32);431431+ if (ret)432432+ goto release;433433+434434+ firmware = fw->data;435435+ size = fw->size;436436+437437+ while (size) {438438+ timeout = jiffies + HZ;439439+ while (1) {440440+ status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);441441+ if (ret)442442+ goto release;443443+ if ((status & IF_SDIO_IO_RDY) &&444444+ (status & IF_SDIO_DL_RDY))445445+ break;446446+ if (time_after(jiffies, timeout)) {447447+ ret = -ETIMEDOUT;448448+ goto release;449449+ }450450+ mdelay(1);451451+ }452452+453453+ chunk_size = min(size, (size_t)60);454454+455455+ *((u32*)chunk_buffer) = cpu_to_le32(chunk_size);456456+ memcpy(chunk_buffer + 4, firmware, chunk_size);457457+/*458458+ lbs_deb_sdio("sending %d bytes chunk\n", chunk_size);459459+*/460460+ ret = sdio_writesb(card->func, card->ioport,461461+ chunk_buffer, 64);462462+ if (ret)463463+ goto release;464464+465465+ firmware += chunk_size;466466+ size -= chunk_size;467467+ }468468+469469+ /* an empty block marks the end of the transfer */470470+ memset(chunk_buffer, 0, 4);471471+ ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64);472472+ if (ret)473473+ goto release;474474+475475+ lbs_deb_sdio("waiting for helper to boot...\n");476476+477477+ /* wait for the helper to boot by looking at the size register */478478+ timeout = jiffies + HZ;479479+ while (1) {480480+ u16 req_size;481481+482482+ req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret);483483+ if (ret)484484+ goto release;485485+486486+ req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8;487487+ if (ret)488488+ goto release;489489+490490+ if (req_size != 0)491491+ break;492492+493493+ if (time_after(jiffies, timeout)) {494494+ ret = -ETIMEDOUT;495495+ goto release;496496+ }497497+498498+ msleep(10);499499+ }500500+501501+ ret = 0;502502+503503+release:504504+ sdio_set_block_size(card->func, 0);505505+ sdio_release_host(card->func);506506+ kfree(chunk_buffer);507507+release_fw:508508+ release_firmware(fw);509509+510510+out:511511+ if (ret)512512+ lbs_pr_err("failed to load helper firmware\n");513513+514514+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);515515+516516+ return ret;517517+}518518+519519+static int if_sdio_prog_real(struct if_sdio_card *card)520520+{521521+ int ret;522522+ u8 status;523523+ const struct firmware *fw;524524+ unsigned long timeout;525525+ u8 *chunk_buffer;526526+ u32 chunk_size;527527+ u8 *firmware;528528+ size_t size, req_size;529529+530530+ lbs_deb_enter(LBS_DEB_SDIO);531531+532532+ ret = request_firmware(&fw, card->firmware, &card->func->dev);533533+ if (ret) {534534+ lbs_pr_err("can't load firmware\n");535535+ goto out;536536+ }537537+538538+ chunk_buffer = kzalloc(512, GFP_KERNEL);539539+ if (!chunk_buffer) {540540+ ret = -ENOMEM;541541+ goto release_fw;542542+ }543543+544544+ sdio_claim_host(card->func);545545+546546+ ret = sdio_set_block_size(card->func, 32);547547+ if (ret)548548+ goto release;549549+550550+ firmware = fw->data;551551+ size = fw->size;552552+553553+ while (size) {554554+ timeout = jiffies + HZ;555555+ while (1) {556556+ status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);557557+ if (ret)558558+ goto release;559559+ if ((status & IF_SDIO_IO_RDY) &&560560+ (status & IF_SDIO_DL_RDY))561561+ break;562562+ if (time_after(jiffies, timeout)) {563563+ ret = -ETIMEDOUT;564564+ goto release;565565+ }566566+ mdelay(1);567567+ }568568+569569+ req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret);570570+ if (ret)571571+ goto release;572572+573573+ req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8;574574+ if (ret)575575+ goto release;576576+/*577577+ lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size);578578+*/579579+ if (req_size == 0) {580580+ lbs_deb_sdio("firmware helper gave up early\n");581581+ ret = -EIO;582582+ goto release;583583+ }584584+585585+ if (req_size & 0x01) {586586+ lbs_deb_sdio("firmware helper signalled error\n");587587+ ret = -EIO;588588+ goto release;589589+ }590590+591591+ if (req_size > size)592592+ req_size = size;593593+594594+ while (req_size) {595595+ chunk_size = min(req_size, (size_t)512);596596+597597+ memcpy(chunk_buffer, firmware, chunk_size);598598+/*599599+ lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n",600600+ chunk_size, (chunk_size + 31) / 32 * 32);601601+*/602602+ ret = sdio_writesb(card->func, card->ioport,603603+ chunk_buffer, (chunk_size + 31) / 32 * 32);604604+ if (ret)605605+ goto release;606606+607607+ firmware += chunk_size;608608+ size -= chunk_size;609609+ req_size -= chunk_size;610610+ }611611+ }612612+613613+ ret = 0;614614+615615+ lbs_deb_sdio("waiting for firmware to boot...\n");616616+617617+ /* wait for the firmware to boot */618618+ timeout = jiffies + HZ;619619+ while (1) {620620+ u16 scratch;621621+622622+ scratch = if_sdio_read_scratch(card, &ret);623623+ if (ret)624624+ goto release;625625+626626+ if (scratch == IF_SDIO_FIRMWARE_OK)627627+ break;628628+629629+ if (time_after(jiffies, timeout)) {630630+ ret = -ETIMEDOUT;631631+ goto release;632632+ }633633+634634+ msleep(10);635635+ }636636+637637+ ret = 0;638638+639639+release:640640+ sdio_set_block_size(card->func, 0);641641+ sdio_release_host(card->func);642642+ kfree(chunk_buffer);643643+release_fw:644644+ release_firmware(fw);645645+646646+out:647647+ if (ret)648648+ lbs_pr_err("failed to load firmware\n");649649+650650+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);651651+652652+ return ret;653653+}654654+655655+static int if_sdio_prog_firmware(struct if_sdio_card *card)656656+{657657+ int ret;658658+ u16 scratch;659659+660660+ lbs_deb_enter(LBS_DEB_SDIO);661661+662662+ sdio_claim_host(card->func);663663+ scratch = if_sdio_read_scratch(card, &ret);664664+ sdio_release_host(card->func);665665+666666+ if (ret)667667+ goto out;668668+669669+ if (scratch == IF_SDIO_FIRMWARE_OK) {670670+ lbs_deb_sdio("firmware already loaded\n");671671+ goto success;672672+ }673673+674674+ ret = if_sdio_prog_helper(card);675675+ if (ret)676676+ goto out;677677+678678+ ret = if_sdio_prog_real(card);679679+ if (ret)680680+ goto out;681681+682682+success:683683+ ret = 0;684684+685685+out:686686+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);687687+688688+ return ret;689689+}690690+691691+/*******************************************************************/692692+/* Libertas callbacks */693693+/*******************************************************************/694694+695695+static int if_sdio_host_to_card(wlan_private *priv, u8 type, u8 *buf, u16 nb)696696+{697697+ int ret;698698+ struct if_sdio_card *card;699699+ struct if_sdio_packet *packet, *cur;700700+ u16 size;701701+ unsigned long flags;702702+703703+ lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb);704704+705705+ card = priv->card;706706+707707+ if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) {708708+ ret = -EINVAL;709709+ goto out;710710+ }711711+712712+ /*713713+ * The transfer must be in one transaction or the firmware714714+ * goes suicidal.715715+ */716716+ size = nb + 4;717717+ if ((size > card->func->cur_blksize) || (size > 512)) {718718+ size = (size + card->func->cur_blksize - 1) /719719+ card->func->cur_blksize * card->func->cur_blksize;720720+ }721721+722722+ packet = kzalloc(sizeof(struct if_sdio_packet) + size,723723+ GFP_ATOMIC);724724+ if (!packet) {725725+ ret = -ENOMEM;726726+ goto out;727727+ }728728+729729+ packet->next = NULL;730730+ packet->nb = size;731731+732732+ /*733733+ * SDIO specific header.734734+ */735735+ packet->buffer[0] = (nb + 4) & 0xff;736736+ packet->buffer[1] = ((nb + 4) >> 8) & 0xff;737737+ packet->buffer[2] = type;738738+ packet->buffer[3] = 0;739739+740740+ memcpy(packet->buffer + 4, buf, nb);741741+742742+ spin_lock_irqsave(&card->lock, flags);743743+744744+ if (!card->packets)745745+ card->packets = packet;746746+ else {747747+ cur = card->packets;748748+ while (cur->next)749749+ cur = cur->next;750750+ cur->next = packet;751751+ }752752+753753+ switch (type) {754754+ case MVMS_CMD:755755+ priv->dnld_sent = DNLD_CMD_SENT;756756+ break;757757+ case MVMS_DAT:758758+ priv->dnld_sent = DNLD_DATA_SENT;759759+ break;760760+ default:761761+ lbs_deb_sdio("unknown packet type %d\n", (int)type);762762+ }763763+764764+ spin_unlock_irqrestore(&card->lock, flags);765765+766766+ schedule_work(&card->packet_worker);767767+768768+ ret = 0;769769+770770+out:771771+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);772772+773773+ return ret;774774+}775775+776776+static int if_sdio_get_int_status(wlan_private *priv, u8 *ireg)777777+{778778+ struct if_sdio_card *card;779779+780780+ lbs_deb_enter(LBS_DEB_SDIO);781781+782782+ card = priv->card;783783+784784+ *ireg = card->int_cause;785785+ card->int_cause = 0;786786+787787+ lbs_deb_leave(LBS_DEB_SDIO);788788+789789+ return 0;790790+}791791+792792+static int if_sdio_read_event_cause(wlan_private *priv)793793+{794794+ struct if_sdio_card *card;795795+796796+ lbs_deb_enter(LBS_DEB_SDIO);797797+798798+ card = priv->card;799799+800800+ priv->adapter->eventcause = card->event;801801+802802+ lbs_deb_leave(LBS_DEB_SDIO);803803+804804+ return 0;805805+}806806+807807+/*******************************************************************/808808+/* SDIO callbacks */809809+/*******************************************************************/810810+811811+static void if_sdio_interrupt(struct sdio_func *func)812812+{813813+ int ret;814814+ struct if_sdio_card *card;815815+ u8 cause;816816+817817+ lbs_deb_enter(LBS_DEB_SDIO);818818+819819+ card = sdio_get_drvdata(func);820820+821821+ cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret);822822+ if (ret)823823+ goto out;824824+825825+ lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause);826826+827827+ sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret);828828+ if (ret)829829+ goto out;830830+831831+ /*832832+ * Ignore the define name, this really means the card has833833+ * successfully received the command.834834+ */835835+ if (cause & IF_SDIO_H_INT_DNLD) {836836+ if ((card->priv->dnld_sent == DNLD_DATA_SENT) &&837837+ (card->priv->adapter->connect_status == LIBERTAS_CONNECTED))838838+ netif_wake_queue(card->priv->dev);839839+ card->priv->dnld_sent = DNLD_RES_RECEIVED;840840+ }841841+842842+ if (cause & IF_SDIO_H_INT_UPLD) {843843+ ret = if_sdio_card_to_host(card);844844+ if (ret)845845+ goto out;846846+ }847847+848848+ ret = 0;849849+850850+out:851851+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);852852+}853853+854854+static int if_sdio_probe(struct sdio_func *func,855855+ const struct sdio_device_id *id)856856+{857857+ struct if_sdio_card *card;858858+ wlan_private *priv;859859+ int ret, i;860860+ unsigned int model;861861+ struct if_sdio_packet *packet;862862+863863+ lbs_deb_enter(LBS_DEB_SDIO);864864+865865+ for (i = 0;i < func->card->num_info;i++) {866866+ if (sscanf(func->card->info[i],867867+ "802.11 SDIO ID: %x", &model) == 1)868868+ break;869869+ if (sscanf(func->card->info[i],870870+ "ID: %x", &model) == 1)871871+ break;872872+ }873873+874874+ if (i == func->card->num_info) {875875+ lbs_pr_err("unable to identify card model\n");876876+ return -ENODEV;877877+ }878878+879879+ card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL);880880+ if (!card)881881+ return -ENOMEM;882882+883883+ card->func = func;884884+ card->model = model;885885+ spin_lock_init(&card->lock);886886+ INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);887887+888888+ for (i = 0;i < ARRAY_SIZE(if_sdio_models);i++) {889889+ if (card->model == if_sdio_models[i].model)890890+ break;891891+ }892892+893893+ if (i == ARRAY_SIZE(if_sdio_models)) {894894+ lbs_pr_err("unkown card model 0x%x\n", card->model);895895+ ret = -ENODEV;896896+ goto free;897897+ }898898+899899+ card->helper = if_sdio_models[i].helper;900900+ card->firmware = if_sdio_models[i].firmware;901901+902902+ if (libertas_helper_name) {903903+ lbs_deb_sdio("overriding helper firmware: %s\n",904904+ libertas_helper_name);905905+ card->helper = libertas_helper_name;906906+ }907907+908908+ if (libertas_fw_name) {909909+ lbs_deb_sdio("overriding firmware: %s\n", libertas_fw_name);910910+ card->firmware = libertas_fw_name;911911+ }912912+913913+ sdio_claim_host(func);914914+915915+ ret = sdio_enable_func(func);916916+ if (ret)917917+ goto release;918918+919919+ ret = sdio_claim_irq(func, if_sdio_interrupt);920920+ if (ret)921921+ goto disable;922922+923923+ card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);924924+ if (ret)925925+ goto release_int;926926+927927+ card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;928928+ if (ret)929929+ goto release_int;930930+931931+ card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;932932+ if (ret)933933+ goto release_int;934934+935935+ sdio_release_host(func);936936+937937+ sdio_set_drvdata(func, card);938938+939939+ lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "940940+ "device = 0x%X, model = 0x%X, ioport = 0x%X\n",941941+ func->class, func->vendor, func->device,942942+ model, (unsigned)card->ioport);943943+944944+ ret = if_sdio_prog_firmware(card);945945+ if (ret)946946+ goto reclaim;947947+948948+ priv = libertas_add_card(card, &func->dev);949949+ if (!priv) {950950+ ret = -ENOMEM;951951+ goto reclaim;952952+ }953953+954954+ card->priv = priv;955955+956956+ priv->card = card;957957+ priv->hw_host_to_card = if_sdio_host_to_card;958958+ priv->hw_get_int_status = if_sdio_get_int_status;959959+ priv->hw_read_event_cause = if_sdio_read_event_cause;960960+961961+ priv->adapter->fw_ready = 1;962962+963963+ /*964964+ * Enable interrupts now that everything is set up965965+ */966966+ sdio_claim_host(func);967967+ sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);968968+ sdio_release_host(func);969969+ if (ret)970970+ goto reclaim;971971+972972+ ret = libertas_start_card(priv);973973+ if (ret)974974+ goto err_activate_card;975975+976976+out:977977+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);978978+979979+ return ret;980980+981981+err_activate_card:982982+ flush_scheduled_work();983983+ free_netdev(priv->dev);984984+ kfree(priv->adapter);985985+reclaim:986986+ sdio_claim_host(func);987987+release_int:988988+ sdio_release_irq(func);989989+disable:990990+ sdio_disable_func(func);991991+release:992992+ sdio_release_host(func);993993+free:994994+ while (card->packets) {995995+ packet = card->packets;996996+ card->packets = card->packets->next;997997+ kfree(packet);998998+ }999999+10001000+ kfree(card);10011001+10021002+ goto out;10031003+}10041004+10051005+static void if_sdio_remove(struct sdio_func *func)10061006+{10071007+ struct if_sdio_card *card;10081008+ struct if_sdio_packet *packet;10091009+10101010+ lbs_deb_enter(LBS_DEB_SDIO);10111011+10121012+ card = sdio_get_drvdata(func);10131013+10141014+ card->priv->adapter->surpriseremoved = 1;10151015+10161016+ lbs_deb_sdio("call remove card\n");10171017+ libertas_stop_card(card->priv);10181018+ libertas_remove_card(card->priv);10191019+10201020+ flush_scheduled_work();10211021+10221022+ sdio_claim_host(func);10231023+ sdio_release_irq(func);10241024+ sdio_disable_func(func);10251025+ sdio_release_host(func);10261026+10271027+ while (card->packets) {10281028+ packet = card->packets;10291029+ card->packets = card->packets->next;10301030+ kfree(packet);10311031+ }10321032+10331033+ kfree(card);10341034+10351035+ lbs_deb_leave(LBS_DEB_SDIO);10361036+}10371037+10381038+static struct sdio_driver if_sdio_driver = {10391039+ .name = "libertas_sdio",10401040+ .id_table = if_sdio_ids,10411041+ .probe = if_sdio_probe,10421042+ .remove = if_sdio_remove,10431043+};10441044+10451045+/*******************************************************************/10461046+/* Module functions */10471047+/*******************************************************************/10481048+10491049+static int if_sdio_init_module(void)10501050+{10511051+ int ret = 0;10521052+10531053+ lbs_deb_enter(LBS_DEB_SDIO);10541054+10551055+ printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n");10561056+ printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n");10571057+10581058+ ret = sdio_register_driver(&if_sdio_driver);10591059+10601060+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);10611061+10621062+ return ret;10631063+}10641064+10651065+static void if_sdio_exit_module(void)10661066+{10671067+ lbs_deb_enter(LBS_DEB_SDIO);10681068+10691069+ sdio_unregister_driver(&if_sdio_driver);10701070+10711071+ lbs_deb_leave(LBS_DEB_SDIO);10721072+}10731073+10741074+module_init(if_sdio_init_module);10751075+module_exit(if_sdio_exit_module);10761076+10771077+MODULE_DESCRIPTION("Libertas SDIO WLAN Driver");10781078+MODULE_AUTHOR("Pierre Ossman");10791079+MODULE_LICENSE("GPL");
+45
drivers/net/wireless/libertas/if_sdio.h
···11+/*22+ * linux/drivers/net/wireless/libertas/if_sdio.h33+ *44+ * Copyright 2007 Pierre Ossman55+ *66+ * This program is free software; you can redistribute it and/or modify77+ * it under the terms of the GNU General Public License as published by88+ * the Free Software Foundation; either version 2 of the License, or (at99+ * your option) any later version.1010+ */1111+1212+#ifndef LIBERTAS_IF_SDIO_H1313+#define LIBERTAS_IF_SDIO_H1414+1515+#define IF_SDIO_IOPORT 0x001616+1717+#define IF_SDIO_H_INT_MASK 0x041818+#define IF_SDIO_H_INT_OFLOW 0x081919+#define IF_SDIO_H_INT_UFLOW 0x042020+#define IF_SDIO_H_INT_DNLD 0x022121+#define IF_SDIO_H_INT_UPLD 0x012222+2323+#define IF_SDIO_H_INT_STATUS 0x052424+#define IF_SDIO_H_INT_RSR 0x062525+#define IF_SDIO_H_INT_STATUS2 0x072626+2727+#define IF_SDIO_RD_BASE 0x102828+2929+#define IF_SDIO_STATUS 0x203030+#define IF_SDIO_IO_RDY 0x083131+#define IF_SDIO_CIS_RDY 0x043232+#define IF_SDIO_UL_RDY 0x023333+#define IF_SDIO_DL_RDY 0x013434+3535+#define IF_SDIO_C_INT_MASK 0x243636+#define IF_SDIO_C_INT_STATUS 0x283737+#define IF_SDIO_C_INT_RSR 0x2C3838+3939+#define IF_SDIO_SCRATCH 0x344040+#define IF_SDIO_SCRATCH_OLD 0x80fe4141+#define IF_SDIO_FIRMWARE_OK 0xfedc4242+4343+#define IF_SDIO_EVENT 0x80fc4444+4545+#endif