|  | /* | 
|  |  | 
|  | Driver for the Marvell 8385 based compact flash WLAN cards. | 
|  |  | 
|  | (C) 2007 by Holger Schurig <[email protected]> | 
|  |  | 
|  | 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. | 
|  |  | 
|  | */ | 
|  |  | 
|  | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|  |  | 
|  | #include <linux/module.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/moduleparam.h> | 
|  | #include <linux/firmware.h> | 
|  | #include <linux/netdevice.h> | 
|  |  | 
|  | #include <pcmcia/cistpl.h> | 
|  | #include <pcmcia/ds.h> | 
|  |  | 
|  | #include <linux/io.h> | 
|  |  | 
|  | #define DRV_NAME "libertas_cs" | 
|  |  | 
|  | #include "decl.h" | 
|  | #include "defs.h" | 
|  | #include "dev.h" | 
|  |  | 
|  |  | 
|  | /********************************************************************/ | 
|  | /* Module stuff                                                     */ | 
|  | /********************************************************************/ | 
|  |  | 
|  | MODULE_AUTHOR("Holger Schurig <[email protected]>"); | 
|  | 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) | 
|  | return; | 
|  |  | 
|  | /* 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"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /********************************************************************/ | 
|  | /* 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); | 
|  | ret = -ENODEV; | 
|  | 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, | 
|  | }; | 
|  | module_pcmcia_driver(lbs_driver); |