xref: /openbmc/linux/drivers/net/wireless/intersil/orinoco/spectrum_cs.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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