linux/drivers/net/wireless/intersil/orinoco/orinoco_nortel.c
<<
>>
Prefs
   1/* orinoco_nortel.c
   2 *
   3 * Driver for Prism II devices which would usually be driven by orinoco_cs,
   4 * but are connected to the PCI bus by a PCI-to-PCMCIA adapter used in
   5 * Nortel emobility, Symbol LA-4113 and Symbol LA-4123.
   6 *
   7 * Copyright (C) 2002 Tobias Hoffmann
   8 *           (C) 2003 Christoph Jungegger <disdos@traum404.de>
   9 *
  10 * Some of this code is borrowed from orinoco_plx.c
  11 *      Copyright (C) 2001 Daniel Barlow
  12 * Some of this code is borrowed from orinoco_pci.c
  13 *  Copyright (C) 2001 Jean Tourrilhes
  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 *
  18 * The contents of this file are subject to the Mozilla Public License
  19 * Version 1.1 (the "License"); you may not use this file except in
  20 * compliance with the License. You may obtain a copy of the License
  21 * at http://www.mozilla.org/MPL/
  22 *
  23 * Software distributed under the License is distributed on an "AS IS"
  24 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  25 * the License for the specific language governing rights and
  26 * limitations under the License.
  27 *
  28 * Alternatively, the contents of this file may be used under the
  29 * terms of the GNU General Public License version 2 (the "GPL"), in
  30 * which case the provisions of the GPL are applicable instead of the
  31 * above.  If you wish to allow the use of your version of this file
  32 * only under the terms of the GPL and not to allow others to use your
  33 * version of this file under the MPL, indicate your decision by
  34 * deleting the provisions above and replace them with the notice and
  35 * other provisions required by the GPL.  If you do not delete the
  36 * provisions above, a recipient may use your version of this file
  37 * under either the MPL or the GPL.
  38 */
  39
  40#define DRIVER_NAME "orinoco_nortel"
  41#define PFX DRIVER_NAME ": "
  42
  43#include <linux/module.h>
  44#include <linux/kernel.h>
  45#include <linux/init.h>
  46#include <linux/delay.h>
  47#include <linux/pci.h>
  48#include <pcmcia/cisreg.h>
  49
  50#include "orinoco.h"
  51#include "orinoco_pci.h"
  52
  53#define COR_OFFSET    (0xe0)    /* COR attribute offset of Prism2 PC card */
  54#define COR_VALUE     (COR_LEVEL_REQ | COR_FUNC_ENA)    /* Enable PC card with interrupt in level trigger */
  55
  56
  57/*
  58 * Do a soft reset of the card using the Configuration Option Register
  59 * We need this to get going...
  60 * This is the part of the code that is strongly inspired from wlan-ng
  61 *
  62 * Note bis : Don't try to access HERMES_CMD during the reset phase.
  63 * It just won't work !
  64 */
  65static int orinoco_nortel_cor_reset(struct orinoco_private *priv)
  66{
  67        struct orinoco_pci_card *card = priv->card;
  68
  69        /* Assert the reset until the card notices */
  70        iowrite16(8, card->bridge_io + 2);
  71        ioread16(card->attr_io + COR_OFFSET);
  72        iowrite16(0x80, card->attr_io + COR_OFFSET);
  73        mdelay(1);
  74
  75        /* Give time for the card to recover from this hard effort */
  76        iowrite16(0, card->attr_io + COR_OFFSET);
  77        iowrite16(0, card->attr_io + COR_OFFSET);
  78        mdelay(1);
  79
  80        /* Set COR as usual */
  81        iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
  82        iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
  83        mdelay(1);
  84
  85        iowrite16(0x228, card->bridge_io + 2);
  86
  87        return 0;
  88}
  89
  90static int orinoco_nortel_hw_init(struct orinoco_pci_card *card)
  91{
  92        int i;
  93        u32 reg;
  94
  95        /* Setup bridge */
  96        if (ioread16(card->bridge_io) & 1) {
  97                printk(KERN_ERR PFX "brg1 answer1 wrong\n");
  98                return -EBUSY;
  99        }
 100        iowrite16(0x118, card->bridge_io + 2);
 101        iowrite16(0x108, card->bridge_io + 2);
 102        mdelay(30);
 103        iowrite16(0x8, card->bridge_io + 2);
 104        for (i = 0; i < 30; i++) {
 105                mdelay(30);
 106                if (ioread16(card->bridge_io) & 0x10)
 107                        break;
 108        }
 109        if (i == 30) {
 110                printk(KERN_ERR PFX "brg1 timed out\n");
 111                return -EBUSY;
 112        }
 113        if (ioread16(card->attr_io + COR_OFFSET) & 1) {
 114                printk(KERN_ERR PFX "brg2 answer1 wrong\n");
 115                return -EBUSY;
 116        }
 117        if (ioread16(card->attr_io + COR_OFFSET + 2) & 1) {
 118                printk(KERN_ERR PFX "brg2 answer2 wrong\n");
 119                return -EBUSY;
 120        }
 121        if (ioread16(card->attr_io + COR_OFFSET + 4) & 1) {
 122                printk(KERN_ERR PFX "brg2 answer3 wrong\n");
 123                return -EBUSY;
 124        }
 125
 126        /* Set the PCMCIA COR register */
 127        iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
 128        mdelay(1);
 129        reg = ioread16(card->attr_io + COR_OFFSET);
 130        if (reg != COR_VALUE) {
 131                printk(KERN_ERR PFX "Error setting COR value (reg=%x)\n",
 132                       reg);
 133                return -EBUSY;
 134        }
 135
 136        /* Set LEDs */
 137        iowrite16(1, card->bridge_io + 10);
 138        return 0;
 139}
 140
 141static int orinoco_nortel_init_one(struct pci_dev *pdev,
 142                                   const struct pci_device_id *ent)
 143{
 144        int err;
 145        struct orinoco_private *priv;
 146        struct orinoco_pci_card *card;
 147        void __iomem *hermes_io, *bridge_io, *attr_io;
 148
 149        err = pci_enable_device(pdev);
 150        if (err) {
 151                printk(KERN_ERR PFX "Cannot enable PCI device\n");
 152                return err;
 153        }
 154
 155        err = pci_request_regions(pdev, DRIVER_NAME);
 156        if (err) {
 157                printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
 158                goto fail_resources;
 159        }
 160
 161        bridge_io = pci_iomap(pdev, 0, 0);
 162        if (!bridge_io) {
 163                printk(KERN_ERR PFX "Cannot map bridge registers\n");
 164                err = -EIO;
 165                goto fail_map_bridge;
 166        }
 167
 168        attr_io = pci_iomap(pdev, 1, 0);
 169        if (!attr_io) {
 170                printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n");
 171                err = -EIO;
 172                goto fail_map_attr;
 173        }
 174
 175        hermes_io = pci_iomap(pdev, 2, 0);
 176        if (!hermes_io) {
 177                printk(KERN_ERR PFX "Cannot map chipset registers\n");
 178                err = -EIO;
 179                goto fail_map_hermes;
 180        }
 181
 182        /* Allocate network device */
 183        priv = alloc_orinocodev(sizeof(*card), &pdev->dev,
 184                                orinoco_nortel_cor_reset, NULL);
 185        if (!priv) {
 186                printk(KERN_ERR PFX "Cannot allocate network device\n");
 187                err = -ENOMEM;
 188                goto fail_alloc;
 189        }
 190
 191        card = priv->card;
 192        card->bridge_io = bridge_io;
 193        card->attr_io = attr_io;
 194
 195        hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
 196
 197        err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED,
 198                          DRIVER_NAME, priv);
 199        if (err) {
 200                printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq);
 201                err = -EBUSY;
 202                goto fail_irq;
 203        }
 204
 205        err = orinoco_nortel_hw_init(card);
 206        if (err) {
 207                printk(KERN_ERR PFX "Hardware initialization failed\n");
 208                goto fail;
 209        }
 210
 211        err = orinoco_nortel_cor_reset(priv);
 212        if (err) {
 213                printk(KERN_ERR PFX "Initial reset failed\n");
 214                goto fail;
 215        }
 216
 217        err = orinoco_init(priv);
 218        if (err) {
 219                printk(KERN_ERR PFX "orinoco_init() failed\n");
 220                goto fail;
 221        }
 222
 223        err = orinoco_if_add(priv, 0, 0, NULL);
 224        if (err) {
 225                printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 226                goto fail_wiphy;
 227        }
 228
 229        pci_set_drvdata(pdev, priv);
 230
 231        return 0;
 232
 233 fail_wiphy:
 234        wiphy_unregister(priv_to_wiphy(priv));
 235 fail:
 236        free_irq(pdev->irq, priv);
 237
 238 fail_irq:
 239        free_orinocodev(priv);
 240
 241 fail_alloc:
 242        pci_iounmap(pdev, hermes_io);
 243
 244 fail_map_hermes:
 245        pci_iounmap(pdev, attr_io);
 246
 247 fail_map_attr:
 248        pci_iounmap(pdev, bridge_io);
 249
 250 fail_map_bridge:
 251        pci_release_regions(pdev);
 252
 253 fail_resources:
 254        pci_disable_device(pdev);
 255
 256        return err;
 257}
 258
 259static void orinoco_nortel_remove_one(struct pci_dev *pdev)
 260{
 261        struct orinoco_private *priv = pci_get_drvdata(pdev);
 262        struct orinoco_pci_card *card = priv->card;
 263
 264        /* Clear LEDs */
 265        iowrite16(0, card->bridge_io + 10);
 266
 267        orinoco_if_del(priv);
 268        wiphy_unregister(priv_to_wiphy(priv));
 269        free_irq(pdev->irq, priv);
 270        free_orinocodev(priv);
 271        pci_iounmap(pdev, priv->hw.iobase);
 272        pci_iounmap(pdev, card->attr_io);
 273        pci_iounmap(pdev, card->bridge_io);
 274        pci_release_regions(pdev);
 275        pci_disable_device(pdev);
 276}
 277
 278static const struct pci_device_id orinoco_nortel_id_table[] = {
 279        /* Nortel emobility PCI */
 280        {0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,},
 281        /* Symbol LA-4123 PCI */
 282        {0x1562, 0x0001, PCI_ANY_ID, PCI_ANY_ID,},
 283        {0,},
 284};
 285
 286MODULE_DEVICE_TABLE(pci, orinoco_nortel_id_table);
 287
 288static struct pci_driver orinoco_nortel_driver = {
 289        .name           = DRIVER_NAME,
 290        .id_table       = orinoco_nortel_id_table,
 291        .probe          = orinoco_nortel_init_one,
 292        .remove         = orinoco_nortel_remove_one,
 293        .driver.pm      = &orinoco_pci_pm_ops,
 294};
 295
 296static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
 297        " (Tobias Hoffmann & Christoph Jungegger <disdos@traum404.de>)";
 298MODULE_AUTHOR("Christoph Jungegger <disdos@traum404.de>");
 299MODULE_DESCRIPTION("Driver for wireless LAN cards using the Nortel PCI bridge");
 300MODULE_LICENSE("Dual MPL/GPL");
 301
 302static int __init orinoco_nortel_init(void)
 303{
 304        printk(KERN_DEBUG "%s\n", version);
 305        return pci_register_driver(&orinoco_nortel_driver);
 306}
 307
 308static void __exit orinoco_nortel_exit(void)
 309{
 310        pci_unregister_driver(&orinoco_nortel_driver);
 311}
 312
 313module_init(orinoco_nortel_init);
 314module_exit(orinoco_nortel_exit);
 315