/* Driver for the Marvell 8385 based compact flash WLAN cards. (C) 2007 by Holger Schurig This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #define DRV_NAME "libertas_cs" #include "decl.h" #include "defs.h" #include "dev.h" /********************************************************************/ /* Module stuff */ /********************************************************************/ MODULE_AUTHOR("Holger Schurig "); MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); MODULE_LICENSE("GPL"); /********************************************************************/ /* Data structures */ /********************************************************************/ struct if_cs_card { struct pcmcia_device *p_dev; struct lbs_private *priv; void __iomem *iobase; bool align_regs; u32 model; }; enum { MODEL_UNKNOWN = 0x00, MODEL_8305 = 0x01, MODEL_8381 = 0x02, MODEL_8385 = 0x03 }; static const struct lbs_fw_table fw_table[] = { { MODEL_8305, "libertas/cf8305.bin", NULL }, { MODEL_8305, "libertas_cs_helper.fw", NULL }, { MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" }, { MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" }, { MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" }, { MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" }, { 0, NULL, NULL } }; MODULE_FIRMWARE("libertas/cf8305.bin"); MODULE_FIRMWARE("libertas/cf8381_helper.bin"); MODULE_FIRMWARE("libertas/cf8381.bin"); MODULE_FIRMWARE("libertas/cf8385_helper.bin"); MODULE_FIRMWARE("libertas/cf8385.bin"); MODULE_FIRMWARE("libertas_cs_helper.fw"); MODULE_FIRMWARE("libertas_cs.fw"); /********************************************************************/ /* Hardware access */ /********************************************************************/ /* This define enables wrapper functions which allow you to dump all register accesses. You normally won't this, except for development */ /* #define DEBUG_IO */ #ifdef DEBUG_IO static int debug_output = 0; #else /* This way the compiler optimizes the printk's away */ #define debug_output 0 #endif static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg) { unsigned int val = ioread8(card->iobase + reg); if (debug_output) printk(KERN_INFO "inb %08x<%02x\n", reg, val); return val; } static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) { unsigned int val = ioread16(card->iobase + reg); if (debug_output) printk(KERN_INFO "inw %08x<%04x\n", reg, val); return val; } static inline void if_cs_read16_rep( struct if_cs_card *card, uint reg, void *buf, unsigned long count) { if (debug_output) printk(KERN_INFO "insw %08x<(0x%lx words)\n", reg, count); ioread16_rep(card->iobase + reg, buf, count); } static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) { if (debug_output) printk(KERN_INFO "outb %08x>%02x\n", reg, val); iowrite8(val, card->iobase + reg); } static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) { if (debug_output) printk(KERN_INFO "outw %08x>%04x\n", reg, val); iowrite16(val, card->iobase + reg); } static inline void if_cs_write16_rep( struct if_cs_card *card, uint reg, const void *buf, unsigned long count) { if (debug_output) printk(KERN_INFO "outsw %08x>(0x%lx words)\n", reg, count); iowrite16_rep(card->iobase + reg, buf, count); } /* * I know that polling/delaying is frowned upon. However, this procedure * with polling is needed while downloading the firmware. At this stage, * the hardware does unfortunately not create any interrupts. * * Fortunately, this function is never used once the firmware is in * the card. :-) * * As a reference, see the "Firmware Specification v5.1", page 18 * and 19. I did not follow their suggested timing to the word, * but this works nice & fast anyway. */ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg) { int i; for (i = 0; i < 100000; i++) { u8 val = if_cs_read8(card, addr); if (val == reg) return 0; udelay(5); } return -ETIME; } /* * First the bitmasks for the host/card interrupt/status registers: */ #define IF_CS_BIT_TX 0x0001 #define IF_CS_BIT_RX 0x0002 #define IF_CS_BIT_COMMAND 0x0004 #define IF_CS_BIT_RESP 0x0008 #define IF_CS_BIT_EVENT 0x0010 #define IF_CS_BIT_MASK 0x001f /* * It's not really clear to me what the host status register is for. It * needs to be set almost in union with "host int cause". The following * bits from above are used: * * IF_CS_BIT_TX driver downloaded a data packet * IF_CS_BIT_RX driver got a data packet * IF_CS_BIT_COMMAND driver downloaded a command * IF_CS_BIT_RESP not used (has some meaning with powerdown) * IF_CS_BIT_EVENT driver read a host event */ #define IF_CS_HOST_STATUS 0x00000000 /* * With the host int cause register can the host (that is, Linux) cause * an interrupt in the firmware, to tell the firmware about those events: * * IF_CS_BIT_TX a data packet has been downloaded * IF_CS_BIT_RX a received data packet has retrieved * IF_CS_BIT_COMMAND a firmware block or a command has been downloaded * IF_CS_BIT_RESP not used (has some meaning with powerdown) * IF_CS_BIT_EVENT a host event (link lost etc) has been retrieved */ #define IF_CS_HOST_INT_CAUSE 0x00000002 /* * The host int mask register is used to enable/disable interrupt. However, * I have the suspicion that disabled interrupts are lost. */ #define IF_CS_HOST_INT_MASK 0x00000004 /* * Used to send or receive data packets: */ #define IF_CS_WRITE 0x00000016 #define IF_CS_WRITE_LEN 0x00000014 #define IF_CS_READ 0x00000010 #define IF_CS_READ_LEN 0x00000024 /* * Used to send commands (and to send firmware block) and to * receive command responses: */ #define IF_CS_CMD 0x0000001A #define IF_CS_CMD_LEN 0x00000018 #define IF_CS_RESP 0x00000012 #define IF_CS_RESP_LEN 0x00000030 /* * The card status registers shows what the card/firmware actually * accepts: * * IF_CS_BIT_TX you may send a data packet * IF_CS_BIT_RX you may retrieve a data packet * IF_CS_BIT_COMMAND you may send a command * IF_CS_BIT_RESP you may retrieve a command response * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) * * When reading this register several times, you will get back the same * results --- with one exception: the IF_CS_BIT_EVENT clear itself * automatically. * * Not that we don't rely on BIT_RX,_BIT_RESP or BIT_EVENT because * we handle this via the card int cause register. */ #define IF_CS_CARD_STATUS 0x00000020 #define IF_CS_CARD_STATUS_MASK 0x7f00 /* * The card int cause register is used by the card/firmware to notify us * about the following events: * * IF_CS_BIT_TX a data packet has successfully been sentx * IF_CS_BIT_RX a data packet has been received and can be retrieved * IF_CS_BIT_COMMAND not used * IF_CS_BIT_RESP the firmware has a command response for us * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) */ #define IF_CS_CARD_INT_CAUSE 0x00000022 /* * This is used to for handshaking with the card's bootloader/helper image * to synchronize downloading of firmware blocks. */ #define IF_CS_SQ_READ_LOW 0x00000028 #define IF_CS_SQ_HELPER_OK 0x10 /* * The scratch register tells us ... * * IF_CS_SCRATCH_BOOT_OK the bootloader runs * IF_CS_SCRATCH_HELPER_OK the helper firmware already runs */ #define IF_CS_SCRATCH 0x0000003F #define IF_CS_SCRATCH_BOOT_OK 0x00 #define IF_CS_SCRATCH_HELPER_OK 0x5a /* * Used to detect ancient chips: */ #define IF_CS_PRODUCT_ID 0x0000001C #define IF_CS_CF8385_B1_REV 0x12 #define IF_CS_CF8381_B3_REV 0x04 #define IF_CS_CF8305_B1_REV 0x03 /* * Used to detect other cards than CF8385 since their revisions of silicon * doesn't match those from CF8385, eg. CF8381 B3 works with this driver. */ #define CF8305_MANFID 0x02db #define CF8305_CARDID 0x8103 #define CF8381_MANFID 0x02db #define CF8381_CARDID 0x6064 #define CF8385_MANFID 0x02df #define CF8385_CARDID 0x8103 /* * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when * that gets fixed. Currently there's no way to access it from the probe hook. */ static inline u32 get_model(u16 manf_id, u16 card_id) { /* NOTE: keep in sync with if_cs_ids */ if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID) return MODEL_8305; else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID) return MODEL_8381; else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID) return MODEL_8385; return MODEL_UNKNOWN; } /********************************************************************/ /* I/O and interrupt handling */ /********************************************************************/ static inline void if_cs_enable_ints(struct if_cs_card *card) { lbs_deb_enter(LBS_DEB_CS); if_cs_write16(card, IF_CS_HOST_INT_MASK, 0); } static inline void if_cs_disable_ints(struct if_cs_card *card) { lbs_deb_enter(LBS_DEB_CS); if_cs_write16(card, IF_CS_HOST_INT_MASK, IF_CS_BIT_MASK); } /* * Called from if_cs_host_to_card to send a command to the hardware */ static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb) { struct if_cs_card *card = (struct if_cs_card *)priv->card; int ret = -1; int loops = 0; lbs_deb_enter(LBS_DEB_CS); if_cs_disable_ints(card); /* Is hardware ready? */ while (1) { u16 status = if_cs_read16(card, IF_CS_CARD_STATUS); if (status & IF_CS_BIT_COMMAND) break; if (++loops > 100) { netdev_err(priv->dev, "card not ready for commands\n"); goto done; } mdelay(1); } if_cs_write16(card, IF_CS_CMD_LEN, nb); if_cs_write16_rep(card, IF_CS_CMD, buf, nb / 2); /* Are we supposed to transfer an odd amount of bytes? */ if (nb & 1) if_cs_write8(card, IF_CS_CMD, buf[nb-1]); /* "Assert the download over interrupt command in the Host * status register" */ if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); /* "Assert the download over interrupt command in the Card * interrupt case register" */ if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); ret = 0; done: if_cs_enable_ints(card); lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } /* * Called from if_cs_host_to_card to send a data to the hardware */ static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) { struct if_cs_card *card = (struct if_cs_card *)priv->card; u16 status; lbs_deb_enter(LBS_DEB_CS); if_cs_disable_ints(card); status = if_cs_read16(card, IF_CS_CARD_STATUS); BUG_ON((status & IF_CS_BIT_TX) == 0); if_cs_write16(card, IF_CS_WRITE_LEN, nb); /* write even number of bytes, then odd byte if necessary */ if_cs_write16_rep(card, IF_CS_WRITE, buf, nb / 2); if (nb & 1) if_cs_write8(card, IF_CS_WRITE, buf[nb-1]); if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX); if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX); if_cs_enable_ints(card); lbs_deb_leave(LBS_DEB_CS); } /* * Get the command result out of the card. */ static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) { unsigned long flags; int ret = -1; u16 status; lbs_deb_enter(LBS_DEB_CS); /* is hardware ready? */ status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); if ((status & IF_CS_BIT_RESP) == 0) { netdev_err(priv->dev, "no cmd response in card\n"); *len = 0; goto out; } *len = if_cs_read16(priv->card, IF_CS_RESP_LEN); if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) { netdev_err(priv->dev, "card cmd buffer has invalid # of bytes (%d)\n", *len); goto out; } /* read even number of bytes, then odd byte if necessary */ if_cs_read16_rep(priv->card, IF_CS_RESP, data, *len/sizeof(u16)); if (*len & 1) data[*len-1] = if_cs_read8(priv->card, IF_CS_RESP); /* This is a workaround for a firmware that reports too much * bytes */ *len -= 8; ret = 0; /* Clear this flag again */ spin_lock_irqsave(&priv->driver_lock, flags); priv->dnld_sent = DNLD_RES_RECEIVED; spin_unlock_irqrestore(&priv->driver_lock, flags); out: lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); return ret; } static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) { struct sk_buff *skb = NULL; u16 len; u8 *data; lbs_deb_enter(LBS_DEB_CS); len = if_cs_read16(priv->card, IF_CS_READ_LEN); if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { netdev_err(priv->dev, "card data buffer has invalid # of bytes (%d)\n", len); priv->dev->stats.rx_dropped++; goto dat_err; } skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2); if (!skb) goto out; skb_put(skb, len); skb_reserve(skb, 2);/* 16 byte align */ data = skb->data; /* read even number of bytes, then odd byte if necessary */ if_cs_read16_rep(priv->card, IF_CS_READ, data, len/sizeof(u16)); if (len & 1) data[len-1] = if_cs_read8(priv->card, IF_CS_READ); dat_err: if_cs_write16(priv->card, IF_CS_HOST_STATUS, IF_CS_BIT_RX); if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_RX); out: lbs_deb_leave_args(LBS_DEB_CS, "ret %p", skb); return skb; } static irqreturn_t if_cs_interrupt(int irq, void *data) { struct if_cs_card *card = data; struct lbs_private *priv = card->priv; u16 cause; lbs_deb_enter(LBS_DEB_CS); /* Ask card interrupt cause register if there is something for us */ cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE); lbs_deb_cs("cause 0x%04x\n", cause); if (cause == 0) { /* Not for us */ return IRQ_NONE; } if (cause == 0xffff) { /* Read in junk, the card has probably been removed */ card->priv->surpriseremoved = 1; return IRQ_HANDLED; } if (cause & IF_CS_BIT_RX) { struct sk_buff *skb; lbs_deb_cs("rx packet\n"); skb = if_cs_receive_data(priv); if (skb) lbs_process_rxed_packet(priv, skb); } if (cause & IF_CS_BIT_TX) { lbs_deb_cs("tx done\n"); lbs_host_to_card_done(priv); } if (cause & IF_CS_BIT_RESP) { unsigned long flags; u8 i; lbs_deb_cs("cmd resp\n"); spin_lock_irqsave(&priv->driver_lock, flags); i = (priv->resp_idx == 0) ? 1 : 0; spin_unlock_irqrestore(&priv->driver_lock, flags); BUG_ON(priv->resp_len[i]); if_cs_receive_cmdres(priv, priv->resp_buf[i], &priv->resp_len[i]); spin_lock_irqsave(&priv->driver_lock, flags); lbs_notify_command_response(priv, i); spin_unlock_irqrestore(&priv->driver_lock, flags); } if (cause & IF_CS_BIT_EVENT) { u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_EVENT); lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8); } /* Clear interrupt cause */ if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK); lbs_deb_leave(LBS_DEB_CS); return IRQ_HANDLED; } /********************************************************************/ /* Firmware */ /********************************************************************/ /* * Tries to program the helper firmware. * * Return 0 on success */ static int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw) { int ret = 0; int sent = 0; u8 scratch; lbs_deb_enter(LBS_DEB_CS); /* * This is the only place where an unaligned register access happens on * the CF8305 card, therefore for the sake of speed of the driver, we do * the alignment correction here. */ if (card->align_regs) scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8; else scratch = if_cs_read8(card, IF_CS_SCRATCH); /* "If the value is 0x5a, the firmware is already * downloaded successfully" */ if (scratch == IF_CS_SCRATCH_HELPER_OK) goto done; /* "If the value is != 00, it is invalid value of register */ if (scratch != IF_CS_SCRATCH_BOOT_OK) { ret = -ENODEV; goto done; } lbs_deb_cs("helper size %td\n", fw->size); /* "Set the 5 bytes of the helper image to 0" */ /* Not needed, this contains an ARM branch instruction */ for (;;) { /* "the number of bytes to send is 256" */ int count = 256; int remain = fw->size - sent; if (remain < count) count = remain; /* * "write the number of bytes to be sent to the I/O Command * write length register" */ if_cs_write16(card, IF_CS_CMD_LEN, count); /* "write this to I/O Command port register as 16 bit writes */ if (count) if_cs_write16_rep(card, IF_CS_CMD, &fw->data[sent], count >> 1); /* * "Assert the download over interrupt command in the Host * status register" */ if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); /* * "Assert the download over interrupt command in the Card * interrupt case register" */ if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); /* * "The host polls the Card Status register ... for 50 ms before * declaring a failure" */ ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, IF_CS_BIT_COMMAND); if (ret < 0) { pr_err("can't download helper at 0x%x, ret %d\n", sent, ret); goto done; } if (count == 0) break; sent += count; } done: lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } static int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw) { int ret = 0; int retry = 0; int len = 0; int sent; lbs_deb_enter(LBS_DEB_CS); lbs_deb_cs("fw size %td\n", fw->size); ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW, IF_CS_SQ_HELPER_OK); if (ret < 0) { pr_err("helper firmware doesn't answer\n"); goto done; } for (sent = 0; sent < fw->size; sent += len) { len = if_cs_read16(card, IF_CS_SQ_READ_LOW); if (len & 1) { retry++; pr_info("odd, need to retry this firmware block\n"); } else { retry = 0; } if (retry > 20) { pr_err("could not download firmware\n"); ret = -ENODEV; goto done; } if (retry) { sent -= len; } if_cs_write16(card, IF_CS_CMD_LEN, len); if_cs_write16_rep(card, IF_CS_CMD, &fw->data[sent], (len+1) >> 1); if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, IF_CS_BIT_COMMAND); if (ret < 0) { pr_err("can't download firmware at 0x%x\n", sent); goto done; } } ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a); if (ret < 0) pr_err("firmware download failed\n"); done: lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } static void if_cs_prog_firmware(struct lbs_private *priv, int ret, const struct firmware *helper, const struct firmware *mainfw) { struct if_cs_card *card = priv->card; if (ret) { pr_err("failed to find firmware (%d)\n", ret); return; } /* Load the firmware */ ret = if_cs_prog_helper(card, helper); if (ret == 0 && (card->model != MODEL_8305)) ret = if_cs_prog_real(card, mainfw); if (ret) goto out; /* Now actually get the IRQ */ ret = request_irq(card->p_dev->irq, if_cs_interrupt, IRQF_SHARED, DRV_NAME, card); if (ret) { pr_err("error in request_irq\n"); goto out; } /* * Clear any interrupt cause that happened while sending * firmware/initializing card */ if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK); if_cs_enable_ints(card); /* And finally bring the card up */ priv->fw_ready = 1; if (lbs_start_card(priv) != 0) { pr_err("could not activate card\n"); free_irq(card->p_dev->irq, card); } out: release_firmware(helper); release_firmware(mainfw); } /********************************************************************/ /* Callback functions for libertas.ko */ /********************************************************************/ /* Send commands or data packets to the card */ static int if_cs_host_to_card(struct lbs_private *priv, u8 type, u8 *buf, u16 nb) { int ret = -1; lbs_deb_enter_args(LBS_DEB_CS, "type %d, bytes %d", type, nb); switch (type) { case MVMS_DAT: priv->dnld_sent = DNLD_DATA_SENT; if_cs_send_data(priv, buf, nb); ret = 0; break; case MVMS_CMD: priv->dnld_sent = DNLD_CMD_SENT; ret = if_cs_send_cmd(priv, buf, nb); break; default: netdev_err(priv->dev, "%s: unsupported type %d\n", __func__, type); } lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } static void if_cs_release(struct pcmcia_device *p_dev) { struct if_cs_card *card = p_dev->priv; lbs_deb_enter(LBS_DEB_CS); free_irq(p_dev->irq, card); pcmcia_disable_device(p_dev); if (card->iobase) ioport_unmap(card->iobase); lbs_deb_leave(LBS_DEB_CS); } static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data) { p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; if (p_dev->resource[1]->end) { pr_err("wrong CIS (check number of IO windows)\n"); return -ENODEV; } /* This reserves IO space but doesn't actually enable it */ return pcmcia_request_io(p_dev); } static int if_cs_probe(struct pcmcia_device *p_dev) { int ret = -ENOMEM; unsigned int prod_id; struct lbs_private *priv; struct if_cs_card *card; lbs_deb_enter(LBS_DEB_CS); card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL); if (!card) goto out; card->p_dev = p_dev; p_dev->priv = card; p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { pr_err("error in pcmcia_loop_config\n"); goto out1; } /* * Allocate an interrupt line. Note that this does not assign * a handler to the interrupt, unless the 'Handler' member of * the irq structure is initialized. */ if (!p_dev->irq) goto out1; /* Initialize io access */ card->iobase = ioport_map(p_dev->resource[0]->start, resource_size(p_dev->resource[0])); if (!card->iobase) { pr_err("error in ioport_map\n"); ret = -EIO; goto out1; } ret = pcmcia_enable_device(p_dev); if (ret) { pr_err("error in pcmcia_enable_device\n"); goto out2; } /* Finally, report what we've done */ lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]); /* * Most of the libertas cards can do unaligned register access, but some * weird ones cannot. That's especially true for the CF8305 card. */ card->align_regs = false; card->model = get_model(p_dev->manf_id, p_dev->card_id); if (card->model == MODEL_UNKNOWN) { pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n", p_dev->manf_id, p_dev->card_id); goto out2; } /* Check if we have a current silicon */ prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); if (card->model == MODEL_8305) { card->align_regs = true; if (prod_id < IF_CS_CF8305_B1_REV) { pr_err("8305 rev B0 and older are not supported\n"); ret = -ENODEV; goto out2; } } if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) { pr_err("8381 rev B2 and older are not supported\n"); ret = -ENODEV; goto out2; } if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) { pr_err("8385 rev B0 and older are not supported\n"); ret = -ENODEV; goto out2; } /* Make this card known to the libertas driver */ priv = lbs_add_card(card, &p_dev->dev); if (!priv) { ret = -ENOMEM; goto out2; } /* Set up fields in lbs_private */ card->priv = priv; priv->card = card; priv->hw_host_to_card = if_cs_host_to_card; priv->enter_deep_sleep = NULL; priv->exit_deep_sleep = NULL; priv->reset_deep_sleep_wakeup = NULL; /* Get firmware */ ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table, if_cs_prog_firmware); if (ret) { pr_err("failed to find firmware (%d)\n", ret); goto out3; } goto out; out3: lbs_remove_card(priv); out2: ioport_unmap(card->iobase); out1: pcmcia_disable_device(p_dev); out: lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } static void if_cs_detach(struct pcmcia_device *p_dev) { struct if_cs_card *card = p_dev->priv; lbs_deb_enter(LBS_DEB_CS); lbs_stop_card(card->priv); lbs_remove_card(card->priv); if_cs_disable_ints(card); if_cs_release(p_dev); kfree(card); lbs_deb_leave(LBS_DEB_CS); } /********************************************************************/ /* Module initialization */ /********************************************************************/ static const struct pcmcia_device_id if_cs_ids[] = { PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID), PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID), PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID), /* NOTE: keep in sync with get_model() */ PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); static struct pcmcia_driver lbs_driver = { .owner = THIS_MODULE, .name = DRV_NAME, .probe = if_cs_probe, .remove = if_cs_detach, .id_table = if_cs_ids, }; static int __init if_cs_init(void) { int ret; lbs_deb_enter(LBS_DEB_CS); ret = pcmcia_register_driver(&lbs_driver); lbs_deb_leave(LBS_DEB_CS); return ret; } static void __exit if_cs_exit(void) { lbs_deb_enter(LBS_DEB_CS); pcmcia_unregister_driver(&lbs_driver); lbs_deb_leave(LBS_DEB_CS); } module_init(if_cs_init); module_exit(if_cs_exit);