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 */ 82static 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 113static 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; 177 } 178 179 pci_set_drvdata(pdev, priv); 180 181 return 0; 182 183 fail: 184 free_irq(pdev->irq, priv); 185 186 fail_irq: 187 free_orinocodev(priv); 188 189 fail_alloc: 190 pci_iounmap(pdev, hermes_io); 191 192 fail_map_hermes: 193 pci_release_regions(pdev); 194 195 fail_resources: 196 pci_disable_device(pdev); 197 198 return err; 199} 200 201static void orinoco_pci_remove_one(struct pci_dev *pdev) 202{ 203 struct orinoco_private *priv = pci_get_drvdata(pdev); 204 205 orinoco_if_del(priv); 206 free_irq(pdev->irq, priv); 207 free_orinocodev(priv); 208 pci_iounmap(pdev, priv->hw.iobase); 209 pci_release_regions(pdev); 210 pci_disable_device(pdev); 211} 212 213static const struct pci_device_id orinoco_pci_id_table[] = { 214 /* Intersil Prism 3 */ 215 {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, 216 /* Intersil Prism 2.5 */ 217 {0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,}, 218 /* Samsung MagicLAN SWL-2210P */ 219 {0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID,}, 220 {0,}, 221}; 222 223MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table); 224 225static struct pci_driver orinoco_pci_driver = { 226 .name = DRIVER_NAME, 227 .id_table = orinoco_pci_id_table, 228 .probe = orinoco_pci_init_one, 229 .remove = orinoco_pci_remove_one, 230 .suspend = orinoco_pci_suspend, 231 .resume = orinoco_pci_resume, 232}; 233 234static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION 235 " (Pavel Roskin <proski@gnu.org>," 236 " David Gibson <hermes@gibson.dropbear.id.au> &" 237 " Jean Tourrilhes <jt@hpl.hp.com>)"; 238MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> &" 239 " David Gibson <hermes@gibson.dropbear.id.au>"); 240MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface"); 241MODULE_LICENSE("Dual MPL/GPL"); 242 243static int __init orinoco_pci_init(void) 244{ 245 printk(KERN_DEBUG "%s\n", version); 246 return pci_register_driver(&orinoco_pci_driver); 247} 248 249static void __exit orinoco_pci_exit(void) 250{ 251 pci_unregister_driver(&orinoco_pci_driver); 252} 253 254module_init(orinoco_pci_init); 255module_exit(orinoco_pci_exit); 256 257/* 258 * Local variables: 259 * c-indent-level: 8 260 * c-basic-offset: 8 261 * tab-width: 8 262 * End: 263 */ 264