linux/drivers/pci/pcie/portdrv_core.c
<<
>>
Prefs
   1/*
   2 * File:        portdrv_core.c
   3 * Purpose:     PCI Express Port Bus Driver's Core Functions
   4 *
   5 * Copyright (C) 2004 Intel
   6 * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/pci.h>
  11#include <linux/kernel.h>
  12#include <linux/errno.h>
  13#include <linux/pm.h>
  14#include <linux/string.h>
  15#include <linux/slab.h>
  16#include <linux/pcieport_if.h>
  17
  18#include "portdrv.h"
  19
  20extern int pcie_mch_quirk;      /* MSI-quirk Indicator */
  21
  22static int pcie_port_probe_service(struct device *dev)
  23{
  24        struct pcie_device *pciedev;
  25        struct pcie_port_service_driver *driver;
  26        int status;
  27
  28        if (!dev || !dev->driver)
  29                return -ENODEV;
  30
  31        driver = to_service_driver(dev->driver);
  32        if (!driver || !driver->probe)
  33                return -ENODEV;
  34
  35        pciedev = to_pcie_device(dev);
  36        status = driver->probe(pciedev, driver->id_table);
  37        if (!status) {
  38                dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n",
  39                        driver->name);
  40                get_device(dev);
  41        }
  42        return status;
  43}
  44
  45static int pcie_port_remove_service(struct device *dev)
  46{
  47        struct pcie_device *pciedev;
  48        struct pcie_port_service_driver *driver;
  49
  50        if (!dev || !dev->driver)
  51                return 0;
  52
  53        pciedev = to_pcie_device(dev);
  54        driver = to_service_driver(dev->driver);
  55        if (driver && driver->remove) { 
  56                dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n",
  57                        driver->name);
  58                driver->remove(pciedev);
  59                put_device(dev);
  60        }
  61        return 0;
  62}
  63
  64static void pcie_port_shutdown_service(struct device *dev) {}
  65
  66static int pcie_port_suspend_service(struct device *dev, pm_message_t state)
  67{
  68        struct pcie_device *pciedev;
  69        struct pcie_port_service_driver *driver;
  70
  71        if (!dev || !dev->driver)
  72                return 0;
  73
  74        pciedev = to_pcie_device(dev);
  75        driver = to_service_driver(dev->driver);
  76        if (driver && driver->suspend)
  77                driver->suspend(pciedev, state);
  78        return 0;
  79}
  80
  81static int pcie_port_resume_service(struct device *dev)
  82{
  83        struct pcie_device *pciedev;
  84        struct pcie_port_service_driver *driver;
  85
  86        if (!dev || !dev->driver)
  87                return 0;
  88
  89        pciedev = to_pcie_device(dev);
  90        driver = to_service_driver(dev->driver);
  91
  92        if (driver && driver->resume)
  93                driver->resume(pciedev);
  94        return 0;
  95}
  96
  97/*
  98 * release_pcie_device
  99 *      
 100 *      Being invoked automatically when device is being removed 
 101 *      in response to device_unregister(dev) call.
 102 *      Release all resources being claimed.
 103 */
 104static void release_pcie_device(struct device *dev)
 105{
 106        dev_printk(KERN_DEBUG, dev, "free port service\n");
 107        kfree(to_pcie_device(dev));                     
 108}
 109
 110static int is_msi_quirked(struct pci_dev *dev)
 111{
 112        int port_type, quirk = 0;
 113        u16 reg16;
 114
 115        pci_read_config_word(dev, 
 116                pci_find_capability(dev, PCI_CAP_ID_EXP) + 
 117                PCIE_CAPABILITIES_REG, &reg16);
 118        port_type = (reg16 >> 4) & PORT_TYPE_MASK;
 119        switch(port_type) {
 120        case PCIE_RC_PORT:
 121                if (pcie_mch_quirk == 1)
 122                        quirk = 1;
 123                break;
 124        case PCIE_SW_UPSTREAM_PORT:
 125        case PCIE_SW_DOWNSTREAM_PORT:
 126        default:
 127                break;  
 128        }
 129        return quirk;
 130}
 131        
 132static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
 133{
 134        int i, pos, nvec, status = -EINVAL;
 135        int interrupt_mode = PCIE_PORT_INTx_MODE;
 136
 137        /* Set INTx as default */
 138        for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
 139                if (mask & (1 << i)) 
 140                        nvec++;
 141                vectors[i] = dev->irq;
 142        }
 143        
 144        /* Check MSI quirk */
 145        if (is_msi_quirked(dev))
 146                return interrupt_mode;
 147
 148        /* Select MSI-X over MSI if supported */                
 149        pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
 150        if (pos) {
 151                struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = 
 152                        {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
 153                dev_info(&dev->dev, "found MSI-X capability\n");
 154                status = pci_enable_msix(dev, msix_entries, nvec);
 155                if (!status) {
 156                        int j = 0;
 157
 158                        interrupt_mode = PCIE_PORT_MSIX_MODE;
 159                        for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
 160                                if (mask & (1 << i)) 
 161                                        vectors[i] = msix_entries[j++].vector;
 162                        }
 163                }
 164        } 
 165        if (status) {
 166                pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
 167                if (pos) {
 168                        dev_info(&dev->dev, "found MSI capability\n");
 169                        status = pci_enable_msi(dev);
 170                        if (!status) {
 171                                interrupt_mode = PCIE_PORT_MSI_MODE;
 172                                for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++)
 173                                        vectors[i] = dev->irq;
 174                        }
 175                }
 176        } 
 177        return interrupt_mode;
 178}
 179
 180static int get_port_device_capability(struct pci_dev *dev)
 181{
 182        int services = 0, pos;
 183        u16 reg16;
 184        u32 reg32;
 185
 186        pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
 187        pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
 188        /* Hot-Plug Capable */
 189        if (reg16 & PORT_TO_SLOT_MASK) {
 190                pci_read_config_dword(dev, 
 191                        pos + PCIE_SLOT_CAPABILITIES_REG, &reg32);
 192                if (reg32 & SLOT_HP_CAPABLE_MASK)
 193                        services |= PCIE_PORT_SERVICE_HP;
 194        } 
 195        /* PME Capable - root port capability */
 196        if (((reg16 >> 4) & PORT_TYPE_MASK) == PCIE_RC_PORT)
 197                services |= PCIE_PORT_SERVICE_PME;
 198
 199        if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
 200                services |= PCIE_PORT_SERVICE_AER;
 201        if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 202                services |= PCIE_PORT_SERVICE_VC;
 203
 204        return services;
 205}
 206
 207static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, 
 208        int port_type, int service_type, int irq, int irq_mode)
 209{
 210        struct device *device;
 211
 212        dev->port = parent;
 213        dev->interrupt_mode = irq_mode;
 214        dev->irq = irq;
 215        dev->id.vendor = parent->vendor;
 216        dev->id.device = parent->device;
 217        dev->id.port_type = port_type;
 218        dev->id.service_type = (1 << service_type);
 219
 220        /* Initialize generic device interface */
 221        device = &dev->device;
 222        memset(device, 0, sizeof(struct device));
 223        device->bus = &pcie_port_bus_type;
 224        device->driver = NULL;
 225        device->driver_data = NULL;
 226        device->release = release_pcie_device;  /* callback to free pcie dev */
 227        snprintf(device->bus_id, sizeof(device->bus_id), "%s:pcie%02x",
 228                 pci_name(parent), get_descriptor_id(port_type, service_type));
 229        device->parent = &parent->dev;
 230}
 231
 232static struct pcie_device* alloc_pcie_device(struct pci_dev *parent,
 233        int port_type, int service_type, int irq, int irq_mode)
 234{
 235        struct pcie_device *device;
 236
 237        device = kzalloc(sizeof(struct pcie_device), GFP_KERNEL);
 238        if (!device)
 239                return NULL;
 240
 241        pcie_device_init(parent, device, port_type, service_type, irq,irq_mode);
 242        dev_printk(KERN_DEBUG, &device->device, "allocate port service\n");
 243        return device;
 244}
 245
 246int pcie_port_device_probe(struct pci_dev *dev)
 247{
 248        int pos, type;
 249        u16 reg;
 250
 251        if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP)))
 252                return -ENODEV;
 253
 254        pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg);
 255        type = (reg >> 4) & PORT_TYPE_MASK;
 256        if (    type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT ||
 257                type == PCIE_SW_DOWNSTREAM_PORT )
 258                return 0;
 259
 260        return -ENODEV;
 261}
 262
 263int pcie_port_device_register(struct pci_dev *dev)
 264{
 265        struct pcie_port_device_ext *p_ext;
 266        int status, type, capabilities, irq_mode, i;
 267        int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
 268        u16 reg16;
 269
 270        /* Allocate port device extension */
 271        if (!(p_ext = kmalloc(sizeof(struct pcie_port_device_ext), GFP_KERNEL)))
 272                return -ENOMEM;
 273
 274        pci_set_drvdata(dev, p_ext);
 275
 276        /* Get port type */
 277        pci_read_config_word(dev,
 278                pci_find_capability(dev, PCI_CAP_ID_EXP) +
 279                PCIE_CAPABILITIES_REG, &reg16);
 280        type = (reg16 >> 4) & PORT_TYPE_MASK;
 281
 282        /* Now get port services */
 283        capabilities = get_port_device_capability(dev);
 284        irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
 285        p_ext->interrupt_mode = irq_mode;
 286
 287        /* Allocate child services if any */
 288        for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
 289                struct pcie_device *child;
 290
 291                if (capabilities & (1 << i)) {
 292                        child = alloc_pcie_device(
 293                                dev,            /* parent */
 294                                type,           /* port type */
 295                                i,              /* service type */
 296                                vectors[i],     /* irq */
 297                                irq_mode        /* interrupt mode */);
 298                        if (child) {
 299                                status = device_register(&child->device);
 300                                if (status) {
 301                                        kfree(child);
 302                                        continue;
 303                                }
 304                                get_device(&child->device);
 305                        }
 306                }
 307        }
 308        return 0;
 309}
 310
 311#ifdef CONFIG_PM
 312static int suspend_iter(struct device *dev, void *data)
 313{
 314        struct pcie_port_service_driver *service_driver;
 315        pm_message_t state = * (pm_message_t *) data;
 316
 317        if ((dev->bus == &pcie_port_bus_type) &&
 318            (dev->driver)) {
 319                service_driver = to_service_driver(dev->driver);
 320                if (service_driver->suspend)
 321                        service_driver->suspend(to_pcie_device(dev), state);
 322        }
 323        return 0;
 324}
 325
 326int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state)
 327{
 328        return device_for_each_child(&dev->dev, &state, suspend_iter);
 329}
 330
 331static int resume_iter(struct device *dev, void *data)
 332{
 333        struct pcie_port_service_driver *service_driver;
 334
 335        if ((dev->bus == &pcie_port_bus_type) &&
 336            (dev->driver)) {
 337                service_driver = to_service_driver(dev->driver);
 338                if (service_driver->resume)
 339                        service_driver->resume(to_pcie_device(dev));
 340        }
 341        return 0;
 342}
 343
 344int pcie_port_device_resume(struct pci_dev *dev)
 345{
 346        return device_for_each_child(&dev->dev, NULL, resume_iter);
 347}
 348#endif
 349
 350static int remove_iter(struct device *dev, void *data)
 351{
 352        struct pcie_port_service_driver *service_driver;
 353
 354        if (dev->bus == &pcie_port_bus_type) {
 355                if (dev->driver) {
 356                        service_driver = to_service_driver(dev->driver);
 357                        if (service_driver->remove)
 358                                service_driver->remove(to_pcie_device(dev));
 359                }
 360                *(unsigned long*)data = (unsigned long)dev;
 361                return 1;
 362        }
 363        return 0;
 364}
 365
 366void pcie_port_device_remove(struct pci_dev *dev)
 367{
 368        struct device *device;
 369        unsigned long device_addr;
 370        int interrupt_mode = PCIE_PORT_INTx_MODE;
 371        int status;
 372
 373        do {
 374                status = device_for_each_child(&dev->dev, &device_addr, remove_iter);
 375                if (status) {
 376                        device = (struct device*)device_addr;
 377                        interrupt_mode = (to_pcie_device(device))->interrupt_mode;
 378                        put_device(device);
 379                        device_unregister(device);
 380                }
 381        } while (status);
 382        /* Switch to INTx by default if MSI enabled */
 383        if (interrupt_mode == PCIE_PORT_MSIX_MODE)
 384                pci_disable_msix(dev);
 385        else if (interrupt_mode == PCIE_PORT_MSI_MODE)
 386                pci_disable_msi(dev);
 387}
 388
 389int pcie_port_bus_register(void)
 390{
 391        return bus_register(&pcie_port_bus_type);
 392}
 393
 394void pcie_port_bus_unregister(void)
 395{
 396        bus_unregister(&pcie_port_bus_type);
 397}
 398
 399int pcie_port_service_register(struct pcie_port_service_driver *new)
 400{
 401        new->driver.name = (char *)new->name;
 402        new->driver.bus = &pcie_port_bus_type;
 403        new->driver.probe = pcie_port_probe_service;
 404        new->driver.remove = pcie_port_remove_service;
 405        new->driver.shutdown = pcie_port_shutdown_service;
 406        new->driver.suspend = pcie_port_suspend_service;
 407        new->driver.resume = pcie_port_resume_service;
 408
 409        return driver_register(&new->driver);
 410}
 411
 412void pcie_port_service_unregister(struct pcie_port_service_driver *new)
 413{
 414        driver_unregister(&new->driver);
 415}
 416
 417EXPORT_SYMBOL(pcie_port_service_register);
 418EXPORT_SYMBOL(pcie_port_service_unregister);
 419
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.