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