linux/drivers/net/wireless/intersil/hostap/hostap_pci.c
<<
>>
Prefs
   1#define PRISM2_PCI
   2
   3/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on
   4 * driver patches from Reyk Floeter <reyk@vantronix.net> and
   5 * Andy Warner <andyw@pobox.com> */
   6
   7#include <linux/module.h>
   8#include <linux/if.h>
   9#include <linux/skbuff.h>
  10#include <linux/netdevice.h>
  11#include <linux/slab.h>
  12#include <linux/workqueue.h>
  13#include <linux/wireless.h>
  14#include <net/iw_handler.h>
  15
  16#include <linux/ioport.h>
  17#include <linux/pci.h>
  18#include <asm/io.h>
  19
  20#include "hostap_wlan.h"
  21
  22
  23static char *dev_info = "hostap_pci";
  24
  25
  26MODULE_AUTHOR("Jouni Malinen");
  27MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN "
  28                   "PCI cards.");
  29MODULE_LICENSE("GPL");
  30
  31
  32/* struct local_info::hw_priv */
  33struct hostap_pci_priv {
  34        void __iomem *mem_start;
  35};
  36
  37
  38/* FIX: do we need mb/wmb/rmb with memory operations? */
  39
  40
  41static const struct pci_device_id prism2_pci_id_table[] = {
  42        /* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */
  43        { 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID },
  44        /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */
  45        { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID },
  46        /* Samsung MagicLAN SWL-2210P */
  47        { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID },
  48        { 0 }
  49};
  50
  51
  52#ifdef PRISM2_IO_DEBUG
  53
  54static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
  55{
  56        struct hostap_interface *iface;
  57        struct hostap_pci_priv *hw_priv;
  58        local_info_t *local;
  59        unsigned long flags;
  60
  61        iface = netdev_priv(dev);
  62        local = iface->local;
  63        hw_priv = local->hw_priv;
  64
  65        spin_lock_irqsave(&local->lock, flags);
  66        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
  67        writeb(v, hw_priv->mem_start + a);
  68        spin_unlock_irqrestore(&local->lock, flags);
  69}
  70
  71static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
  72{
  73        struct hostap_interface *iface;
  74        struct hostap_pci_priv *hw_priv;
  75        local_info_t *local;
  76        unsigned long flags;
  77        u8 v;
  78
  79        iface = netdev_priv(dev);
  80        local = iface->local;
  81        hw_priv = local->hw_priv;
  82
  83        spin_lock_irqsave(&local->lock, flags);
  84        v = readb(hw_priv->mem_start + a);
  85        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
  86        spin_unlock_irqrestore(&local->lock, flags);
  87        return v;
  88}
  89
  90static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
  91{
  92        struct hostap_interface *iface;
  93        struct hostap_pci_priv *hw_priv;
  94        local_info_t *local;
  95        unsigned long flags;
  96
  97        iface = netdev_priv(dev);
  98        local = iface->local;
  99        hw_priv = local->hw_priv;
 100
 101        spin_lock_irqsave(&local->lock, flags);
 102        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
 103        writew(v, hw_priv->mem_start + a);
 104        spin_unlock_irqrestore(&local->lock, flags);
 105}
 106
 107static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
 108{
 109        struct hostap_interface *iface;
 110        struct hostap_pci_priv *hw_priv;
 111        local_info_t *local;
 112        unsigned long flags;
 113        u16 v;
 114
 115        iface = netdev_priv(dev);
 116        local = iface->local;
 117        hw_priv = local->hw_priv;
 118
 119        spin_lock_irqsave(&local->lock, flags);
 120        v = readw(hw_priv->mem_start + a);
 121        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
 122        spin_unlock_irqrestore(&local->lock, flags);
 123        return v;
 124}
 125
 126#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
 127#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
 128#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
 129#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
 130#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), le16_to_cpu((v)))
 131#define HFA384X_INW_DATA(a) cpu_to_le16(hfa384x_inw_debug(dev, (a)))
 132
 133#else /* PRISM2_IO_DEBUG */
 134
 135static inline void hfa384x_outb(struct net_device *dev, int a, u8 v)
 136{
 137        struct hostap_interface *iface;
 138        struct hostap_pci_priv *hw_priv;
 139        iface = netdev_priv(dev);
 140        hw_priv = iface->local->hw_priv;
 141        writeb(v, hw_priv->mem_start + a);
 142}
 143
 144static inline u8 hfa384x_inb(struct net_device *dev, int a)
 145{
 146        struct hostap_interface *iface;
 147        struct hostap_pci_priv *hw_priv;
 148        iface = netdev_priv(dev);
 149        hw_priv = iface->local->hw_priv;
 150        return readb(hw_priv->mem_start + a);
 151}
 152
 153static inline void hfa384x_outw(struct net_device *dev, int a, u16 v)
 154{
 155        struct hostap_interface *iface;
 156        struct hostap_pci_priv *hw_priv;
 157        iface = netdev_priv(dev);
 158        hw_priv = iface->local->hw_priv;
 159        writew(v, hw_priv->mem_start + a);
 160}
 161
 162static inline u16 hfa384x_inw(struct net_device *dev, int a)
 163{
 164        struct hostap_interface *iface;
 165        struct hostap_pci_priv *hw_priv;
 166        iface = netdev_priv(dev);
 167        hw_priv = iface->local->hw_priv;
 168        return readw(hw_priv->mem_start + a);
 169}
 170
 171#define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v))
 172#define HFA384X_INB(a) hfa384x_inb(dev, (a))
 173#define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v))
 174#define HFA384X_INW(a) hfa384x_inw(dev, (a))
 175#define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), le16_to_cpu((v)))
 176#define HFA384X_INW_DATA(a) cpu_to_le16(hfa384x_inw(dev, (a)))
 177
 178#endif /* PRISM2_IO_DEBUG */
 179
 180
 181static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
 182                            int len)
 183{
 184        u16 d_off;
 185        __le16 *pos;
 186
 187        d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
 188        pos = (__le16 *) buf;
 189
 190        for ( ; len > 1; len -= 2)
 191                *pos++ = HFA384X_INW_DATA(d_off);
 192
 193        if (len & 1)
 194                *((char *) pos) = HFA384X_INB(d_off);
 195
 196        return 0;
 197}
 198
 199
 200static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
 201{
 202        u16 d_off;
 203        __le16 *pos;
 204
 205        d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
 206        pos = (__le16 *) buf;
 207
 208        for ( ; len > 1; len -= 2)
 209                HFA384X_OUTW_DATA(*pos++, d_off);
 210
 211        if (len & 1)
 212                HFA384X_OUTB(*((char *) pos), d_off);
 213
 214        return 0;
 215}
 216
 217
 218/* FIX: This might change at some point.. */
 219#include "hostap_hw.c"
 220
 221static void prism2_pci_cor_sreset(local_info_t *local)
 222{
 223        struct net_device *dev = local->dev;
 224        u16 reg;
 225
 226        reg = HFA384X_INB(HFA384X_PCICOR_OFF);
 227        printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg);
 228
 229        /* linux-wlan-ng uses extremely long hold and settle times for
 230         * COR sreset. A comment in the driver code mentions that the long
 231         * delays appear to be necessary. However, at least IBM 22P6901 seems
 232         * to work fine with shorter delays.
 233         *
 234         * Longer delays can be configured by uncommenting following line: */
 235/* #define PRISM2_PCI_USE_LONG_DELAYS */
 236
 237#ifdef PRISM2_PCI_USE_LONG_DELAYS
 238        int i;
 239
 240        HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF);
 241        mdelay(250);
 242
 243        HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF);
 244        mdelay(500);
 245
 246        /* Wait for f/w to complete initialization (CMD:BUSY == 0) */
 247        i = 2000000 / 10;
 248        while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i)
 249                udelay(10);
 250
 251#else /* PRISM2_PCI_USE_LONG_DELAYS */
 252
 253        HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF);
 254        mdelay(2);
 255        HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF);
 256        mdelay(2);
 257
 258#endif /* PRISM2_PCI_USE_LONG_DELAYS */
 259
 260        if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) {
 261                printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name);
 262        }
 263}
 264
 265
 266static void prism2_pci_genesis_reset(local_info_t *local, int hcr)
 267{
 268        struct net_device *dev = local->dev;
 269
 270        HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF);
 271        mdelay(10);
 272        HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF);
 273        mdelay(10);
 274        HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF);
 275        mdelay(10);
 276}
 277
 278
 279static struct prism2_helper_functions prism2_pci_funcs =
 280{
 281        .card_present   = NULL,
 282        .cor_sreset     = prism2_pci_cor_sreset,
 283        .genesis_reset  = prism2_pci_genesis_reset,
 284        .hw_type        = HOSTAP_HW_PCI,
 285};
 286
 287
 288static int prism2_pci_probe(struct pci_dev *pdev,
 289                            const struct pci_device_id *id)
 290{
 291        unsigned long phymem;
 292        void __iomem *mem = NULL;
 293        local_info_t *local = NULL;
 294        struct net_device *dev = NULL;
 295        static int cards_found /* = 0 */;
 296        int irq_registered = 0;
 297        struct hostap_interface *iface;
 298        struct hostap_pci_priv *hw_priv;
 299
 300        hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
 301        if (hw_priv == NULL)
 302                return -ENOMEM;
 303
 304        if (pci_enable_device(pdev))
 305                goto err_out_free;
 306
 307        phymem = pci_resource_start(pdev, 0);
 308
 309        if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) {
 310                printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n");
 311                goto err_out_disable;
 312        }
 313
 314        mem = pci_ioremap_bar(pdev, 0);
 315        if (mem == NULL) {
 316                printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ;
 317                goto fail;
 318        }
 319
 320        dev = prism2_init_local_data(&prism2_pci_funcs, cards_found,
 321                                     &pdev->dev);
 322        if (dev == NULL)
 323                goto fail;
 324        iface = netdev_priv(dev);
 325        local = iface->local;
 326        local->hw_priv = hw_priv;
 327        cards_found++;
 328
 329        dev->irq = pdev->irq;
 330        hw_priv->mem_start = mem;
 331        dev->base_addr = (unsigned long) mem;
 332
 333        prism2_pci_cor_sreset(local);
 334
 335        pci_set_drvdata(pdev, dev);
 336
 337        if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
 338                        dev)) {
 339                printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
 340                goto fail;
 341        } else
 342                irq_registered = 1;
 343
 344        if (!local->pri_only && prism2_hw_config(dev, 1)) {
 345                printk(KERN_DEBUG "%s: hardware initialization failed\n",
 346                       dev_info);
 347                goto fail;
 348        }
 349
 350        printk(KERN_INFO "%s: Intersil Prism2.5 PCI: "
 351               "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq);
 352
 353        return hostap_hw_ready(dev);
 354
 355 fail:
 356        if (irq_registered && dev)
 357                free_irq(dev->irq, dev);
 358
 359        if (mem)
 360                iounmap(mem);
 361
 362        release_mem_region(phymem, pci_resource_len(pdev, 0));
 363
 364 err_out_disable:
 365        pci_disable_device(pdev);
 366        prism2_free_local_data(dev);
 367
 368 err_out_free:
 369        kfree(hw_priv);
 370
 371        return -ENODEV;
 372}
 373
 374
 375static void prism2_pci_remove(struct pci_dev *pdev)
 376{
 377        struct net_device *dev;
 378        struct hostap_interface *iface;
 379        void __iomem *mem_start;
 380        struct hostap_pci_priv *hw_priv;
 381
 382        dev = pci_get_drvdata(pdev);
 383        iface = netdev_priv(dev);
 384        hw_priv = iface->local->hw_priv;
 385
 386        /* Reset the hardware, and ensure interrupts are disabled. */
 387        prism2_pci_cor_sreset(iface->local);
 388        hfa384x_disable_interrupts(dev);
 389
 390        if (dev->irq)
 391                free_irq(dev->irq, dev);
 392
 393        mem_start = hw_priv->mem_start;
 394        prism2_free_local_data(dev);
 395        kfree(hw_priv);
 396
 397        iounmap(mem_start);
 398
 399        release_mem_region(pci_resource_start(pdev, 0),
 400                           pci_resource_len(pdev, 0));
 401        pci_disable_device(pdev);
 402}
 403
 404static int __maybe_unused prism2_pci_suspend(struct device *dev_d)
 405{
 406        struct net_device *dev = dev_get_drvdata(dev_d);
 407
 408        if (netif_running(dev)) {
 409                netif_stop_queue(dev);
 410                netif_device_detach(dev);
 411        }
 412        prism2_suspend(dev);
 413
 414        return 0;
 415}
 416
 417static int __maybe_unused prism2_pci_resume(struct device *dev_d)
 418{
 419        struct net_device *dev = dev_get_drvdata(dev_d);
 420
 421        prism2_hw_config(dev, 0);
 422        if (netif_running(dev)) {
 423                netif_device_attach(dev);
 424                netif_start_queue(dev);
 425        }
 426
 427        return 0;
 428}
 429
 430MODULE_DEVICE_TABLE(pci, prism2_pci_id_table);
 431
 432static SIMPLE_DEV_PM_OPS(prism2_pci_pm_ops,
 433                         prism2_pci_suspend,
 434                         prism2_pci_resume);
 435
 436static struct pci_driver prism2_pci_driver = {
 437        .name           = "hostap_pci",
 438        .id_table       = prism2_pci_id_table,
 439        .probe          = prism2_pci_probe,
 440        .remove         = prism2_pci_remove,
 441        .driver.pm      = &prism2_pci_pm_ops,
 442};
 443
 444module_pci_driver(prism2_pci_driver);
 445