12be45b66SKalle Valo /*
22be45b66SKalle Valo * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as
32be45b66SKalle Valo * Symbol Wireless Networker LA4137, CompactFlash cards by Socket
42be45b66SKalle Valo * Communications and Intel PRO/Wireless 2011B.
52be45b66SKalle Valo *
62be45b66SKalle Valo * The driver implements Symbol firmware download. The rest is handled
72be45b66SKalle Valo * in hermes.c and main.c.
82be45b66SKalle Valo *
92be45b66SKalle Valo * Utilities for downloading the Symbol firmware are available at
102be45b66SKalle Valo * http://sourceforge.net/projects/orinoco/
112be45b66SKalle Valo *
122be45b66SKalle Valo * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
132be45b66SKalle Valo * Portions based on orinoco_cs.c:
142be45b66SKalle Valo * Copyright (C) David Gibson, Linuxcare Australia
152be45b66SKalle Valo * Portions based on Spectrum24tDnld.c from original spectrum24 driver:
162be45b66SKalle Valo * Copyright (C) Symbol Technologies.
172be45b66SKalle Valo *
182be45b66SKalle Valo * See copyright notice in file main.c.
192be45b66SKalle Valo */
202be45b66SKalle Valo
212be45b66SKalle Valo #define DRIVER_NAME "spectrum_cs"
222be45b66SKalle Valo #define PFX DRIVER_NAME ": "
232be45b66SKalle Valo
242be45b66SKalle Valo #include <linux/module.h>
252be45b66SKalle Valo #include <linux/kernel.h>
262be45b66SKalle Valo #include <linux/delay.h>
272be45b66SKalle Valo #include <pcmcia/cistpl.h>
282be45b66SKalle Valo #include <pcmcia/cisreg.h>
292be45b66SKalle Valo #include <pcmcia/ds.h>
302be45b66SKalle Valo
312be45b66SKalle Valo #include "orinoco.h"
322be45b66SKalle Valo
332be45b66SKalle Valo /********************************************************************/
342be45b66SKalle Valo /* Module stuff */
352be45b66SKalle Valo /********************************************************************/
362be45b66SKalle Valo
372be45b66SKalle Valo MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
382be45b66SKalle Valo MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader");
392be45b66SKalle Valo MODULE_LICENSE("Dual MPL/GPL");
402be45b66SKalle Valo
412be45b66SKalle Valo /* Module parameters */
422be45b66SKalle Valo
432be45b66SKalle Valo /* Some D-Link cards have buggy CIS. They do work at 5v properly, but
442be45b66SKalle Valo * don't have any CIS entry for it. This workaround it... */
452be45b66SKalle Valo static int ignore_cis_vcc; /* = 0 */
462be45b66SKalle Valo module_param(ignore_cis_vcc, int, 0);
472be45b66SKalle Valo MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
482be45b66SKalle Valo
492be45b66SKalle Valo /********************************************************************/
502be45b66SKalle Valo /* Data structures */
512be45b66SKalle Valo /********************************************************************/
522be45b66SKalle Valo
532be45b66SKalle Valo /* PCMCIA specific device information (goes in the card field of
542be45b66SKalle Valo * struct orinoco_private */
552be45b66SKalle Valo struct orinoco_pccard {
562be45b66SKalle Valo struct pcmcia_device *p_dev;
572be45b66SKalle Valo };
582be45b66SKalle Valo
592be45b66SKalle Valo /********************************************************************/
602be45b66SKalle Valo /* Function prototypes */
612be45b66SKalle Valo /********************************************************************/
622be45b66SKalle Valo
632be45b66SKalle Valo static int spectrum_cs_config(struct pcmcia_device *link);
642be45b66SKalle Valo static void spectrum_cs_release(struct pcmcia_device *link);
652be45b66SKalle Valo
662be45b66SKalle Valo /* Constants for the CISREG_CCSR register */
672be45b66SKalle Valo #define HCR_RUN 0x07 /* run firmware after reset */
682be45b66SKalle Valo #define HCR_IDLE 0x0E /* don't run firmware after reset */
692be45b66SKalle Valo #define HCR_MEM16 0x10 /* memory width bit, should be preserved */
702be45b66SKalle Valo
712be45b66SKalle Valo
722be45b66SKalle Valo /*
732be45b66SKalle Valo * Reset the card using configuration registers COR and CCSR.
742be45b66SKalle Valo * If IDLE is 1, stop the firmware, so that it can be safely rewritten.
752be45b66SKalle Valo */
762be45b66SKalle Valo static int
spectrum_reset(struct pcmcia_device * link,int idle)772be45b66SKalle Valo spectrum_reset(struct pcmcia_device *link, int idle)
782be45b66SKalle Valo {
792be45b66SKalle Valo int ret;
802be45b66SKalle Valo u8 save_cor;
812be45b66SKalle Valo u8 ccsr;
822be45b66SKalle Valo
832be45b66SKalle Valo /* Doing it if hardware is gone is guaranteed crash */
842be45b66SKalle Valo if (!pcmcia_dev_present(link))
852be45b66SKalle Valo return -ENODEV;
862be45b66SKalle Valo
872be45b66SKalle Valo /* Save original COR value */
882be45b66SKalle Valo ret = pcmcia_read_config_byte(link, CISREG_COR, &save_cor);
892be45b66SKalle Valo if (ret)
902be45b66SKalle Valo goto failed;
912be45b66SKalle Valo
922be45b66SKalle Valo /* Soft-Reset card */
932be45b66SKalle Valo ret = pcmcia_write_config_byte(link, CISREG_COR,
942be45b66SKalle Valo (save_cor | COR_SOFT_RESET));
952be45b66SKalle Valo if (ret)
962be45b66SKalle Valo goto failed;
972be45b66SKalle Valo udelay(1000);
982be45b66SKalle Valo
992be45b66SKalle Valo /* Read CCSR */
1002be45b66SKalle Valo ret = pcmcia_read_config_byte(link, CISREG_CCSR, &ccsr);
1012be45b66SKalle Valo if (ret)
1022be45b66SKalle Valo goto failed;
1032be45b66SKalle Valo
1042be45b66SKalle Valo /*
1052be45b66SKalle Valo * Start or stop the firmware. Memory width bit should be
1062be45b66SKalle Valo * preserved from the value we've just read.
1072be45b66SKalle Valo */
1082be45b66SKalle Valo ccsr = (idle ? HCR_IDLE : HCR_RUN) | (ccsr & HCR_MEM16);
1092be45b66SKalle Valo ret = pcmcia_write_config_byte(link, CISREG_CCSR, ccsr);
1102be45b66SKalle Valo if (ret)
1112be45b66SKalle Valo goto failed;
1122be45b66SKalle Valo udelay(1000);
1132be45b66SKalle Valo
1142be45b66SKalle Valo /* Restore original COR configuration index */
1152be45b66SKalle Valo ret = pcmcia_write_config_byte(link, CISREG_COR,
1162be45b66SKalle Valo (save_cor & ~COR_SOFT_RESET));
1172be45b66SKalle Valo if (ret)
1182be45b66SKalle Valo goto failed;
1192be45b66SKalle Valo udelay(1000);
1202be45b66SKalle Valo return 0;
1212be45b66SKalle Valo
1222be45b66SKalle Valo failed:
1232be45b66SKalle Valo return -ENODEV;
1242be45b66SKalle Valo }
1252be45b66SKalle Valo
1262be45b66SKalle Valo /********************************************************************/
1272be45b66SKalle Valo /* Device methods */
1282be45b66SKalle Valo /********************************************************************/
1292be45b66SKalle Valo
1302be45b66SKalle Valo static int
spectrum_cs_hard_reset(struct orinoco_private * priv)1312be45b66SKalle Valo spectrum_cs_hard_reset(struct orinoco_private *priv)
1322be45b66SKalle Valo {
1332be45b66SKalle Valo struct orinoco_pccard *card = priv->card;
1342be45b66SKalle Valo struct pcmcia_device *link = card->p_dev;
1352be45b66SKalle Valo
1362be45b66SKalle Valo /* Soft reset using COR and HCR */
1372be45b66SKalle Valo spectrum_reset(link, 0);
1382be45b66SKalle Valo
1392be45b66SKalle Valo return 0;
1402be45b66SKalle Valo }
1412be45b66SKalle Valo
1422be45b66SKalle Valo static int
spectrum_cs_stop_firmware(struct orinoco_private * priv,int idle)1432be45b66SKalle Valo spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
1442be45b66SKalle Valo {
1452be45b66SKalle Valo struct orinoco_pccard *card = priv->card;
1462be45b66SKalle Valo struct pcmcia_device *link = card->p_dev;
1472be45b66SKalle Valo
1482be45b66SKalle Valo return spectrum_reset(link, idle);
1492be45b66SKalle Valo }
1502be45b66SKalle Valo
1512be45b66SKalle Valo /********************************************************************/
1522be45b66SKalle Valo /* PCMCIA stuff */
1532be45b66SKalle Valo /********************************************************************/
1542be45b66SKalle Valo
1552be45b66SKalle Valo static int
spectrum_cs_probe(struct pcmcia_device * link)1562be45b66SKalle Valo spectrum_cs_probe(struct pcmcia_device *link)
1572be45b66SKalle Valo {
1582be45b66SKalle Valo struct orinoco_private *priv;
1592be45b66SKalle Valo struct orinoco_pccard *card;
160*92524432SChristophe JAILLET int ret;
1612be45b66SKalle Valo
1622be45b66SKalle Valo priv = alloc_orinocodev(sizeof(*card), &link->dev,
1632be45b66SKalle Valo spectrum_cs_hard_reset,
1642be45b66SKalle Valo spectrum_cs_stop_firmware);
1652be45b66SKalle Valo if (!priv)
1662be45b66SKalle Valo return -ENOMEM;
1672be45b66SKalle Valo card = priv->card;
1682be45b66SKalle Valo
1692be45b66SKalle Valo /* Link both structures together */
1702be45b66SKalle Valo card->p_dev = link;
1712be45b66SKalle Valo link->priv = priv;
1722be45b66SKalle Valo
173*92524432SChristophe JAILLET ret = spectrum_cs_config(link);
174*92524432SChristophe JAILLET if (ret)
175*92524432SChristophe JAILLET goto err_free_orinocodev;
176*92524432SChristophe JAILLET
177*92524432SChristophe JAILLET return 0;
178*92524432SChristophe JAILLET
179*92524432SChristophe JAILLET err_free_orinocodev:
180*92524432SChristophe JAILLET free_orinocodev(priv);
181*92524432SChristophe JAILLET return ret;
182*92524432SChristophe JAILLET }
1832be45b66SKalle Valo
spectrum_cs_detach(struct pcmcia_device * link)1842be45b66SKalle Valo static void spectrum_cs_detach(struct pcmcia_device *link)
1852be45b66SKalle Valo {
1862be45b66SKalle Valo struct orinoco_private *priv = link->priv;
1872be45b66SKalle Valo
1882be45b66SKalle Valo orinoco_if_del(priv);
1892be45b66SKalle Valo
1902be45b66SKalle Valo spectrum_cs_release(link);
1912be45b66SKalle Valo
1922be45b66SKalle Valo free_orinocodev(priv);
1932be45b66SKalle Valo } /* spectrum_cs_detach */
1942be45b66SKalle Valo
spectrum_cs_config_check(struct pcmcia_device * p_dev,void * priv_data)1952be45b66SKalle Valo static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
1962be45b66SKalle Valo void *priv_data)
1972be45b66SKalle Valo {
1982be45b66SKalle Valo if (p_dev->config_index == 0)
1992be45b66SKalle Valo return -EINVAL;
2002be45b66SKalle Valo
2012be45b66SKalle Valo return pcmcia_request_io(p_dev);
2022be45b66SKalle Valo };
2032be45b66SKalle Valo
2042be45b66SKalle Valo static int
spectrum_cs_config(struct pcmcia_device * link)2052be45b66SKalle Valo spectrum_cs_config(struct pcmcia_device *link)
2062be45b66SKalle Valo {
2072be45b66SKalle Valo struct orinoco_private *priv = link->priv;
2082be45b66SKalle Valo struct hermes *hw = &priv->hw;
2092be45b66SKalle Valo int ret;
2102be45b66SKalle Valo void __iomem *mem;
2112be45b66SKalle Valo
2122be45b66SKalle Valo link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC |
2132be45b66SKalle Valo CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
2142be45b66SKalle Valo if (ignore_cis_vcc)
2152be45b66SKalle Valo link->config_flags &= ~CONF_AUTO_CHECK_VCC;
2162be45b66SKalle Valo ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
2172be45b66SKalle Valo if (ret) {
2182be45b66SKalle Valo if (!ignore_cis_vcc)
2192be45b66SKalle Valo printk(KERN_ERR PFX "GetNextTuple(): No matching "
2202be45b66SKalle Valo "CIS configuration. Maybe you need the "
2212be45b66SKalle Valo "ignore_cis_vcc=1 parameter.\n");
2222be45b66SKalle Valo goto failed;
2232be45b66SKalle Valo }
2242be45b66SKalle Valo
2252be45b66SKalle Valo mem = ioport_map(link->resource[0]->start,
2262be45b66SKalle Valo resource_size(link->resource[0]));
2272be45b66SKalle Valo if (!mem)
2282be45b66SKalle Valo goto failed;
2292be45b66SKalle Valo
2302be45b66SKalle Valo /* We initialize the hermes structure before completing PCMCIA
2312be45b66SKalle Valo * configuration just in case the interrupt handler gets
2322be45b66SKalle Valo * called. */
2332be45b66SKalle Valo hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
2342be45b66SKalle Valo hw->eeprom_pda = true;
2352be45b66SKalle Valo
2362be45b66SKalle Valo ret = pcmcia_request_irq(link, orinoco_interrupt);
2372be45b66SKalle Valo if (ret)
2382be45b66SKalle Valo goto failed;
2392be45b66SKalle Valo
2402be45b66SKalle Valo ret = pcmcia_enable_device(link);
2412be45b66SKalle Valo if (ret)
2422be45b66SKalle Valo goto failed;
2432be45b66SKalle Valo
2442be45b66SKalle Valo /* Reset card */
2452be45b66SKalle Valo if (spectrum_cs_hard_reset(priv) != 0)
2462be45b66SKalle Valo goto failed;
2472be45b66SKalle Valo
2482be45b66SKalle Valo /* Initialise the main driver */
2492be45b66SKalle Valo if (orinoco_init(priv) != 0) {
2502be45b66SKalle Valo printk(KERN_ERR PFX "orinoco_init() failed\n");
2512be45b66SKalle Valo goto failed;
2522be45b66SKalle Valo }
2532be45b66SKalle Valo
2542be45b66SKalle Valo /* Register an interface with the stack */
2552be45b66SKalle Valo if (orinoco_if_add(priv, link->resource[0]->start,
2562be45b66SKalle Valo link->irq, NULL) != 0) {
2572be45b66SKalle Valo printk(KERN_ERR PFX "orinoco_if_add() failed\n");
2582be45b66SKalle Valo goto failed;
2592be45b66SKalle Valo }
2602be45b66SKalle Valo
2612be45b66SKalle Valo return 0;
2622be45b66SKalle Valo
2632be45b66SKalle Valo failed:
2642be45b66SKalle Valo spectrum_cs_release(link);
2652be45b66SKalle Valo return -ENODEV;
2662be45b66SKalle Valo } /* spectrum_cs_config */
2672be45b66SKalle Valo
2682be45b66SKalle Valo static void
spectrum_cs_release(struct pcmcia_device * link)2692be45b66SKalle Valo spectrum_cs_release(struct pcmcia_device *link)
2702be45b66SKalle Valo {
2712be45b66SKalle Valo struct orinoco_private *priv = link->priv;
2722be45b66SKalle Valo unsigned long flags;
2732be45b66SKalle Valo
2742be45b66SKalle Valo /* We're committed to taking the device away now, so mark the
2752be45b66SKalle Valo * hardware as unavailable */
2762be45b66SKalle Valo priv->hw.ops->lock_irqsave(&priv->lock, &flags);
2772be45b66SKalle Valo priv->hw_unavailable++;
2782be45b66SKalle Valo priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
2792be45b66SKalle Valo
2802be45b66SKalle Valo pcmcia_disable_device(link);
2812be45b66SKalle Valo if (priv->hw.iobase)
2822be45b66SKalle Valo ioport_unmap(priv->hw.iobase);
2832be45b66SKalle Valo } /* spectrum_cs_release */
2842be45b66SKalle Valo
2852be45b66SKalle Valo
2862be45b66SKalle Valo static int
spectrum_cs_suspend(struct pcmcia_device * link)2872be45b66SKalle Valo spectrum_cs_suspend(struct pcmcia_device *link)
2882be45b66SKalle Valo {
2892be45b66SKalle Valo struct orinoco_private *priv = link->priv;
2902be45b66SKalle Valo
2912be45b66SKalle Valo /* Mark the device as stopped, to block IO until later */
2922be45b66SKalle Valo orinoco_down(priv);
2932be45b66SKalle Valo
2947b9ae69dSJason Yan return 0;
2952be45b66SKalle Valo }
2962be45b66SKalle Valo
2972be45b66SKalle Valo static int
spectrum_cs_resume(struct pcmcia_device * link)2982be45b66SKalle Valo spectrum_cs_resume(struct pcmcia_device *link)
2992be45b66SKalle Valo {
3002be45b66SKalle Valo struct orinoco_private *priv = link->priv;
3012be45b66SKalle Valo int err = orinoco_up(priv);
3022be45b66SKalle Valo
3032be45b66SKalle Valo return err;
3042be45b66SKalle Valo }
3052be45b66SKalle Valo
3062be45b66SKalle Valo
3072be45b66SKalle Valo /********************************************************************/
3082be45b66SKalle Valo /* Module initialization */
3092be45b66SKalle Valo /********************************************************************/
3102be45b66SKalle Valo
3112be45b66SKalle Valo static const struct pcmcia_device_id spectrum_cs_ids[] = {
3122be45b66SKalle Valo PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
3132be45b66SKalle Valo PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
3142be45b66SKalle Valo PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
3152be45b66SKalle Valo PCMCIA_DEVICE_NULL,
3162be45b66SKalle Valo };
3172be45b66SKalle Valo MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids);
3182be45b66SKalle Valo
3192be45b66SKalle Valo static struct pcmcia_driver orinoco_driver = {
3202be45b66SKalle Valo .owner = THIS_MODULE,
3212be45b66SKalle Valo .name = DRIVER_NAME,
3222be45b66SKalle Valo .probe = spectrum_cs_probe,
3232be45b66SKalle Valo .remove = spectrum_cs_detach,
3242be45b66SKalle Valo .suspend = spectrum_cs_suspend,
3252be45b66SKalle Valo .resume = spectrum_cs_resume,
3262be45b66SKalle Valo .id_table = spectrum_cs_ids,
3272be45b66SKalle Valo };
3282be45b66SKalle Valo module_pcmcia_driver(orinoco_driver);
329