1 /* orinoco_pci.c 2 * 3 * Driver for Prism 2.5/3 devices that have a direct PCI interface 4 * (i.e. these are not PCMCIA cards in a PCMCIA-to-PCI bridge). 5 * The card contains only one PCI region, which contains all the usual 6 * hermes registers, as well as the COR register. 7 * 8 * Current maintainers are: 9 * Pavel Roskin <proski AT gnu.org> 10 * and David Gibson <hermes AT gibson.dropbear.id.au> 11 * 12 * Some of this code is borrowed from orinoco_plx.c 13 * Copyright (C) 2001 Daniel Barlow <dan AT telent.net> 14 * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing 15 * has been copied from it. linux-wlan-ng-0.1.10 is originally : 16 * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. 17 * This file originally written by: 18 * Copyright (C) 2001 Jean Tourrilhes <jt AT hpl.hp.com> 19 * And is now maintained by: 20 * (C) Copyright David Gibson, IBM Corp. 2002-2003. 21 * 22 * The contents of this file are subject to the Mozilla Public License 23 * Version 1.1 (the "License"); you may not use this file except in 24 * compliance with the License. You may obtain a copy of the License 25 * at http://www.mozilla.org/MPL/ 26 * 27 * Software distributed under the License is distributed on an "AS IS" 28 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 29 * the License for the specific language governing rights and 30 * limitations under the License. 31 * 32 * Alternatively, the contents of this file may be used under the 33 * terms of the GNU General Public License version 2 (the "GPL"), in 34 * which case the provisions of the GPL are applicable instead of the 35 * above. If you wish to allow the use of your version of this file 36 * only under the terms of the GPL and not to allow others to use your 37 * version of this file under the MPL, indicate your decision by 38 * deleting the provisions above and replace them with the notice and 39 * other provisions required by the GPL. If you do not delete the 40 * provisions above, a recipient may use your version of this file 41 * under either the MPL or the GPL. 42 */ 43 44 #define DRIVER_NAME "orinoco_pci" 45 #define PFX DRIVER_NAME ": " 46 47 #include <linux/module.h> 48 #include <linux/kernel.h> 49 #include <linux/init.h> 50 #include <linux/delay.h> 51 #include <linux/pci.h> 52 53 #include "orinoco.h" 54 #include "orinoco_pci.h" 55 56 /* Offset of the COR register of the PCI card */ 57 #define HERMES_PCI_COR (0x26) 58 59 /* Bitmask to reset the card */ 60 #define HERMES_PCI_COR_MASK (0x0080) 61 62 /* Magic timeouts for doing the reset. 63 * Those times are straight from wlan-ng, and it is claimed that they 64 * are necessary. Alan will kill me. Take your time and grab a coffee. */ 65 #define HERMES_PCI_COR_ONT (250) /* ms */ 66 #define HERMES_PCI_COR_OFFT (500) /* ms */ 67 #define HERMES_PCI_COR_BUSYT (500) /* ms */ 68 69 /* 70 * Do a soft reset of the card using the Configuration Option Register 71 * We need this to get going... 72 * This is the part of the code that is strongly inspired from wlan-ng 73 * 74 * Note : This code is done with irq enabled. This mean that many 75 * interrupts will occur while we are there. This is why we use the 76 * jiffies to regulate time instead of a straight mdelay(). Usually we 77 * need only around 245 iteration of the loop to do 250 ms delay. 78 * 79 * Note bis : Don't try to access HERMES_CMD during the reset phase. 80 * It just won't work ! 81 */ 82 static int orinoco_pci_cor_reset(struct orinoco_private *priv) 83 { 84 struct hermes *hw = &priv->hw; 85 unsigned long timeout; 86 u16 reg; 87 88 /* Assert the reset until the card notices */ 89 hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK); 90 mdelay(HERMES_PCI_COR_ONT); 91 92 /* Give time for the card to recover from this hard effort */ 93 hermes_write_regn(hw, PCI_COR, 0x0000); 94 mdelay(HERMES_PCI_COR_OFFT); 95 96 /* The card is ready when it's no longer busy */ 97 timeout = jiffies + msecs_to_jiffies(HERMES_PCI_COR_BUSYT); 98 reg = hermes_read_regn(hw, CMD); 99 while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { 100 mdelay(1); 101 reg = hermes_read_regn(hw, CMD); 102 } 103 104 /* Still busy? */ 105 if (reg & HERMES_CMD_BUSY) { 106 printk(KERN_ERR PFX "Busy timeout\n"); 107 return -ETIMEDOUT; 108 } 109 110 return 0; 111 } 112 113 static int orinoco_pci_init_one(struct pci_dev *pdev, 114 const struct pci_device_id *ent) 115 { 116 int err; 117 struct orinoco_private *priv; 118 struct orinoco_pci_card *card; 119 void __iomem *hermes_io; 120 121 err = pci_enable_device(pdev); 122 if (err) { 123 printk(KERN_ERR PFX "Cannot enable PCI device\n"); 124 return err; 125 } 126 127 err = pci_request_regions(pdev, DRIVER_NAME); 128 if (err) { 129 printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); 130 goto fail_resources; 131 } 132 133 hermes_io = pci_iomap(pdev, 0, 0); 134 if (!hermes_io) { 135 printk(KERN_ERR PFX "Cannot remap chipset registers\n"); 136 err = -EIO; 137 goto fail_map_hermes; 138 } 139 140 /* Allocate network device */ 141 priv = alloc_orinocodev(sizeof(*card), &pdev->dev, 142 orinoco_pci_cor_reset, NULL); 143 if (!priv) { 144 printk(KERN_ERR PFX "Cannot allocate network device\n"); 145 err = -ENOMEM; 146 goto fail_alloc; 147 } 148 149 card = priv->card; 150 151 hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING); 152 153 err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, 154 DRIVER_NAME, priv); 155 if (err) { 156 printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); 157 err = -EBUSY; 158 goto fail_irq; 159 } 160 161 err = orinoco_pci_cor_reset(priv); 162 if (err) { 163 printk(KERN_ERR PFX "Initial reset failed\n"); 164 goto fail; 165 } 166 167 err = orinoco_init(priv); 168 if (err) { 169 printk(KERN_ERR PFX "orinoco_init() failed\n"); 170 goto fail; 171 } 172 173 err = orinoco_if_add(priv, 0, 0, NULL); 174 if (err) { 175 printk(KERN_ERR PFX "orinoco_if_add() failed\n"); 176 goto fail_wiphy; 177 } 178 179 pci_set_drvdata(pdev, priv); 180 181 return 0; 182 183 fail_wiphy: 184 wiphy_unregister(priv_to_wiphy(priv)); 185 fail: 186 free_irq(pdev->irq, priv); 187 188 fail_irq: 189 free_orinocodev(priv); 190 191 fail_alloc: 192 pci_iounmap(pdev, hermes_io); 193 194 fail_map_hermes: 195 pci_release_regions(pdev); 196 197 fail_resources: 198 pci_disable_device(pdev); 199 200 return err; 201 } 202 203 static void orinoco_pci_remove_one(struct pci_dev *pdev) 204 { 205 struct orinoco_private *priv = pci_get_drvdata(pdev); 206 207 orinoco_if_del(priv); 208 wiphy_unregister(priv_to_wiphy(priv)); 209 free_irq(pdev->irq, priv); 210 free_orinocodev(priv); 211 pci_iounmap(pdev, priv->hw.iobase); 212 pci_release_regions(pdev); 213 pci_disable_device(pdev); 214 } 215 216 static const struct pci_device_id orinoco_pci_id_table[] = { 217 /* Intersil Prism 3 */ 218 {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, 219 /* Intersil Prism 2.5 */ 220 {0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,}, 221 /* Samsung MagicLAN SWL-2210P */ 222 {0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID,}, 223 {0,}, 224 }; 225 226 MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table); 227 228 static struct pci_driver orinoco_pci_driver = { 229 .name = DRIVER_NAME, 230 .id_table = orinoco_pci_id_table, 231 .probe = orinoco_pci_init_one, 232 .remove = orinoco_pci_remove_one, 233 .suspend = orinoco_pci_suspend, 234 .resume = orinoco_pci_resume, 235 }; 236 237 static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION 238 " (Pavel Roskin <proski@gnu.org>," 239 " David Gibson <hermes@gibson.dropbear.id.au> &" 240 " Jean Tourrilhes <jt@hpl.hp.com>)"; 241 MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> &" 242 " David Gibson <hermes@gibson.dropbear.id.au>"); 243 MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface"); 244 MODULE_LICENSE("Dual MPL/GPL"); 245 246 static int __init orinoco_pci_init(void) 247 { 248 printk(KERN_DEBUG "%s\n", version); 249 return pci_register_driver(&orinoco_pci_driver); 250 } 251 252 static void __exit orinoco_pci_exit(void) 253 { 254 pci_unregister_driver(&orinoco_pci_driver); 255 } 256 257 module_init(orinoco_pci_init); 258 module_exit(orinoco_pci_exit); 259 260 /* 261 * Local variables: 262 * c-indent-level: 8 263 * c-basic-offset: 8 264 * tab-width: 8 265 * End: 266 */ 267