1282199Sdumbbell/* drm_pci.h -- PCI DMA memory management wrappers for DRM -*- linux-c -*- */ 2282199Sdumbbell/** 3282199Sdumbbell * \file drm_pci.c 4282199Sdumbbell * \brief Functions and ioctls to manage PCI memory 5282199Sdumbbell * 6282199Sdumbbell * \warning These interfaces aren't stable yet. 7282199Sdumbbell * 8282199Sdumbbell * \todo Implement the remaining ioctl's for the PCI pools. 9282199Sdumbbell * \todo The wrappers here are so thin that they would be better off inlined.. 10282199Sdumbbell * 11282199Sdumbbell * \author Jos�� Fonseca <jrfonseca@tungstengraphics.com> 12282199Sdumbbell * \author Leif Delgass <ldelgass@retinalburn.net> 13282199Sdumbbell */ 14282199Sdumbbell 15282199Sdumbbell/* 16282199Sdumbbell * Copyright 2003 Jos�� Fonseca. 17282199Sdumbbell * Copyright 2003 Leif Delgass. 18235783Skib * All Rights Reserved. 19235783Skib * 20235783Skib * Permission is hereby granted, free of charge, to any person obtaining a 21235783Skib * copy of this software and associated documentation files (the "Software"), 22235783Skib * to deal in the Software without restriction, including without limitation 23235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense, 24235783Skib * and/or sell copies of the Software, and to permit persons to whom the 25235783Skib * Software is furnished to do so, subject to the following conditions: 26235783Skib * 27235783Skib * The above copyright notice and this permission notice (including the next 28235783Skib * paragraph) shall be included in all copies or substantial portions of the 29235783Skib * Software. 30235783Skib * 31235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34282199Sdumbbell * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 35235783Skib * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 36235783Skib * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37235783Skib */ 38235783Skib 39235783Skib#include <sys/cdefs.h> 40235783Skib__FBSDID("$FreeBSD: releng/10.2/sys/dev/drm2/drm_pci.c 282199 2015-04-28 19:35:05Z dumbbell $"); 41235783Skib 42235783Skib#include <dev/drm2/drmP.h> 43235783Skib 44282199Sdumbbellstatic int drm_msi = 1; /* Enable by default. */ 45282199SdumbbellSYSCTL_NODE(_hw, OID_AUTO, drm, CTLFLAG_RW, NULL, "DRM device"); 46282199SdumbbellSYSCTL_INT(_hw_drm, OID_AUTO, msi, CTLFLAG_RDTUN, &drm_msi, 1, 47282199Sdumbbell "Enable MSI interrupts for drm devices"); 48282199Sdumbbell 49235783Skib/**********************************************************************/ 50235783Skib/** \name PCI memory */ 51235783Skib/*@{*/ 52235783Skib 53235783Skibstatic void 54235783Skibdrm_pci_busdma_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 55235783Skib{ 56235783Skib drm_dma_handle_t *dmah = arg; 57235783Skib 58235783Skib if (error != 0) 59235783Skib return; 60235783Skib 61235783Skib KASSERT(nsegs == 1, ("drm_pci_busdma_callback: bad dma segment count")); 62235783Skib dmah->busaddr = segs[0].ds_addr; 63235783Skib} 64235783Skib 65235783Skib/** 66282199Sdumbbell * \brief Allocate a PCI consistent memory block, for DMA. 67235783Skib */ 68282199Sdumbbelldrm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, 69282199Sdumbbell size_t align, dma_addr_t maxaddr) 70235783Skib{ 71235783Skib drm_dma_handle_t *dmah; 72235783Skib int ret; 73235783Skib 74235783Skib /* Need power-of-two alignment, so fail the allocation if it isn't. */ 75235783Skib if ((align & (align - 1)) != 0) { 76235783Skib DRM_ERROR("drm_pci_alloc with non-power-of-two alignment %d\n", 77235783Skib (int)align); 78235783Skib return NULL; 79235783Skib } 80235783Skib 81235783Skib dmah = malloc(sizeof(drm_dma_handle_t), DRM_MEM_DMA, M_ZERO | M_NOWAIT); 82235783Skib if (dmah == NULL) 83235783Skib return NULL; 84235783Skib 85235783Skib /* Make sure we aren't holding mutexes here */ 86235783Skib mtx_assert(&dev->dma_lock, MA_NOTOWNED); 87235783Skib if (mtx_owned(&dev->dma_lock)) 88235783Skib DRM_ERROR("called while holding dma_lock\n"); 89235783Skib 90280353Sjah ret = bus_dma_tag_create( 91282199Sdumbbell bus_get_dma_tag(dev->dev), /* parent */ 92280353Sjah align, 0, /* align, boundary */ 93235783Skib maxaddr, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */ 94235783Skib NULL, NULL, /* filtfunc, filtfuncargs */ 95235783Skib size, 1, size, /* maxsize, nsegs, maxsegsize */ 96235783Skib 0, NULL, NULL, /* flags, lockfunc, lockfuncargs */ 97235783Skib &dmah->tag); 98235783Skib if (ret != 0) { 99235783Skib free(dmah, DRM_MEM_DMA); 100235783Skib return NULL; 101235783Skib } 102235783Skib 103235783Skib ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr, 104235783Skib BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_NOCACHE, &dmah->map); 105235783Skib if (ret != 0) { 106235783Skib bus_dma_tag_destroy(dmah->tag); 107235783Skib free(dmah, DRM_MEM_DMA); 108235783Skib return NULL; 109235783Skib } 110235783Skib 111235783Skib ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, size, 112235783Skib drm_pci_busdma_callback, dmah, BUS_DMA_NOWAIT); 113235783Skib if (ret != 0) { 114235783Skib bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 115235783Skib bus_dma_tag_destroy(dmah->tag); 116235783Skib free(dmah, DRM_MEM_DMA); 117235783Skib return NULL; 118235783Skib } 119235783Skib 120235783Skib return dmah; 121235783Skib} 122235783Skib 123282199SdumbbellEXPORT_SYMBOL(drm_pci_alloc); 124282199Sdumbbell 125235783Skib/** 126282199Sdumbbell * \brief Free a PCI consistent memory block without freeing its descriptor. 127282199Sdumbbell * 128282199Sdumbbell * This function is for internal use in the Linux-specific DRM core code. 129235783Skib */ 130282199Sdumbbellvoid __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) 131235783Skib{ 132235783Skib if (dmah == NULL) 133235783Skib return; 134235783Skib 135235783Skib bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 136235783Skib bus_dma_tag_destroy(dmah->tag); 137282199Sdumbbell} 138235783Skib 139282199Sdumbbell/** 140282199Sdumbbell * \brief Free a PCI consistent memory block 141282199Sdumbbell */ 142282199Sdumbbellvoid drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) 143282199Sdumbbell{ 144282199Sdumbbell __drm_pci_free(dev, dmah); 145235783Skib free(dmah, DRM_MEM_DMA); 146235783Skib} 147235783Skib 148282199SdumbbellEXPORT_SYMBOL(drm_pci_free); 149254848Sdumbbell 150282199Sdumbbellstatic int drm_get_pci_domain(struct drm_device *dev) 151282199Sdumbbell{ 152282199Sdumbbell return dev->pci_domain; 153282199Sdumbbell} 154282199Sdumbbell 155282199Sdumbbellstatic int drm_pci_get_irq(struct drm_device *dev) 156282199Sdumbbell{ 157282199Sdumbbell 158282199Sdumbbell if (dev->irqr) 159282199Sdumbbell return (dev->irq); 160282199Sdumbbell 161282199Sdumbbell dev->irqr = bus_alloc_resource_any(dev->dev, SYS_RES_IRQ, 162282199Sdumbbell &dev->irqrid, RF_SHAREABLE); 163282199Sdumbbell if (!dev->irqr) { 164282199Sdumbbell dev_err(dev->dev, "Failed to allocate IRQ\n"); 165282199Sdumbbell return (0); 166282199Sdumbbell } 167282199Sdumbbell 168282199Sdumbbell dev->irq = (int) rman_get_start(dev->irqr); 169282199Sdumbbell 170282199Sdumbbell return (dev->irq); 171282199Sdumbbell} 172282199Sdumbbell 173282199Sdumbbellstatic void drm_pci_free_irq(struct drm_device *dev) 174282199Sdumbbell{ 175282199Sdumbbell if (dev->irqr == NULL) 176282199Sdumbbell return; 177282199Sdumbbell 178282199Sdumbbell bus_release_resource(dev->dev, SYS_RES_IRQ, 179282199Sdumbbell dev->irqrid, dev->irqr); 180282199Sdumbbell 181282199Sdumbbell dev->irqr = NULL; 182282199Sdumbbell dev->irq = 0; 183282199Sdumbbell} 184282199Sdumbbell 185282199Sdumbbellstatic const char *drm_pci_get_name(struct drm_device *dev) 186282199Sdumbbell{ 187282199Sdumbbell return dev->driver->name; 188282199Sdumbbell} 189282199Sdumbbell 190282199Sdumbbellint drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) 191282199Sdumbbell{ 192282199Sdumbbell int len, ret; 193282199Sdumbbell master->unique_len = 40; 194282199Sdumbbell master->unique_size = master->unique_len; 195282199Sdumbbell master->unique = malloc(master->unique_size, DRM_MEM_DRIVER, M_NOWAIT); 196282199Sdumbbell if (master->unique == NULL) 197282199Sdumbbell return -ENOMEM; 198282199Sdumbbell 199282199Sdumbbell 200282199Sdumbbell len = snprintf(master->unique, master->unique_len, 201282199Sdumbbell "pci:%04x:%02x:%02x.%d", 202282199Sdumbbell dev->pci_domain, 203282199Sdumbbell dev->pci_bus, 204282199Sdumbbell dev->pci_slot, 205282199Sdumbbell dev->pci_func); 206282199Sdumbbell 207282199Sdumbbell if (len >= master->unique_len) { 208282199Sdumbbell DRM_ERROR("buffer overflow"); 209282199Sdumbbell ret = -EINVAL; 210282199Sdumbbell goto err; 211282199Sdumbbell } else 212282199Sdumbbell master->unique_len = len; 213282199Sdumbbell 214282199Sdumbbell return 0; 215282199Sdumbbellerr: 216282199Sdumbbell return ret; 217282199Sdumbbell} 218282199Sdumbbell 219282199Sdumbbellint drm_pci_set_unique(struct drm_device *dev, 220282199Sdumbbell struct drm_master *master, 221282199Sdumbbell struct drm_unique *u) 222282199Sdumbbell{ 223282199Sdumbbell int domain, bus, slot, func, ret; 224282199Sdumbbell 225282199Sdumbbell master->unique_len = u->unique_len; 226282199Sdumbbell master->unique_size = u->unique_len + 1; 227282199Sdumbbell master->unique = malloc(master->unique_size, DRM_MEM_DRIVER, M_WAITOK); 228282199Sdumbbell if (!master->unique) { 229282199Sdumbbell ret = -ENOMEM; 230282199Sdumbbell goto err; 231282199Sdumbbell } 232282199Sdumbbell 233282199Sdumbbell if (copy_from_user(master->unique, u->unique, master->unique_len)) { 234282199Sdumbbell ret = -EFAULT; 235282199Sdumbbell goto err; 236282199Sdumbbell } 237282199Sdumbbell 238282199Sdumbbell master->unique[master->unique_len] = '\0'; 239282199Sdumbbell 240282199Sdumbbell /* Return error if the busid submitted doesn't match the device's actual 241282199Sdumbbell * busid. 242282199Sdumbbell */ 243282199Sdumbbell ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); 244282199Sdumbbell if (ret != 3) { 245282199Sdumbbell ret = -EINVAL; 246282199Sdumbbell goto err; 247282199Sdumbbell } 248282199Sdumbbell 249282199Sdumbbell domain = bus >> 8; 250282199Sdumbbell bus &= 0xff; 251282199Sdumbbell 252282199Sdumbbell if ((domain != dev->pci_domain) || 253282199Sdumbbell (bus != dev->pci_bus) || 254282199Sdumbbell (slot != dev->pci_slot) || 255282199Sdumbbell (func != dev->pci_func)) { 256282199Sdumbbell ret = -EINVAL; 257282199Sdumbbell goto err; 258282199Sdumbbell } 259282199Sdumbbell return 0; 260282199Sdumbbellerr: 261282199Sdumbbell return ret; 262282199Sdumbbell} 263282199Sdumbbell 264282199Sdumbbell 265282199Sdumbbellstatic int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) 266282199Sdumbbell{ 267282199Sdumbbell if ((p->busnum >> 8) != drm_get_pci_domain(dev) || 268282199Sdumbbell (p->busnum & 0xff) != dev->pci_bus || 269282199Sdumbbell p->devnum != dev->pci_slot || p->funcnum != dev->pci_func) 270282199Sdumbbell return -EINVAL; 271282199Sdumbbell 272282199Sdumbbell p->irq = dev->irq; 273282199Sdumbbell 274282199Sdumbbell DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum, 275282199Sdumbbell p->irq); 276282199Sdumbbell return 0; 277282199Sdumbbell} 278282199Sdumbbell 279282199Sdumbbellint drm_pci_agp_init(struct drm_device *dev) 280282199Sdumbbell{ 281282199Sdumbbell if (drm_core_has_AGP(dev)) { 282282199Sdumbbell if (drm_pci_device_is_agp(dev)) 283282199Sdumbbell dev->agp = drm_agp_init(dev); 284282199Sdumbbell if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP) 285282199Sdumbbell && (dev->agp == NULL)) { 286282199Sdumbbell DRM_ERROR("Cannot initialize the agpgart module.\n"); 287282199Sdumbbell return -EINVAL; 288282199Sdumbbell } 289282199Sdumbbell if (drm_core_has_MTRR(dev)) { 290282199Sdumbbell if (dev->agp && dev->agp->agp_info.ai_aperture_base != 0) { 291282199Sdumbbell if (drm_mtrr_add(dev->agp->agp_info.ai_aperture_base, 292282199Sdumbbell dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC) == 0) 293282199Sdumbbell dev->agp->agp_mtrr = 1; 294282199Sdumbbell else 295282199Sdumbbell dev->agp->agp_mtrr = -1; 296282199Sdumbbell } 297282199Sdumbbell } 298282199Sdumbbell } 299282199Sdumbbell return 0; 300282199Sdumbbell} 301282199Sdumbbell 302282199Sdumbbellstatic struct drm_bus drm_pci_bus = { 303282199Sdumbbell .bus_type = DRIVER_BUS_PCI, 304282199Sdumbbell .get_irq = drm_pci_get_irq, 305282199Sdumbbell .free_irq = drm_pci_free_irq, 306282199Sdumbbell .get_name = drm_pci_get_name, 307282199Sdumbbell .set_busid = drm_pci_set_busid, 308282199Sdumbbell .set_unique = drm_pci_set_unique, 309282199Sdumbbell .irq_by_busid = drm_pci_irq_by_busid, 310282199Sdumbbell .agp_init = drm_pci_agp_init, 311282199Sdumbbell}; 312282199Sdumbbell 313282199Sdumbbell/** 314282199Sdumbbell * Register. 315282199Sdumbbell * 316282199Sdumbbell * \param pdev - PCI device structure 317282199Sdumbbell * \param ent entry from the PCI ID table with device type flags 318282199Sdumbbell * \return zero on success or a negative number on failure. 319282199Sdumbbell * 320282199Sdumbbell * Attempt to gets inter module "drm" information. If we are first 321282199Sdumbbell * then register the character device and inter module information. 322282199Sdumbbell * Try and register, if we fail to register, backout previous work. 323282199Sdumbbell */ 324282199Sdumbbellint drm_get_pci_dev(device_t kdev, struct drm_device *dev, 325282199Sdumbbell struct drm_driver *driver) 326282199Sdumbbell{ 327282199Sdumbbell int ret; 328282199Sdumbbell 329282199Sdumbbell DRM_DEBUG("\n"); 330282199Sdumbbell 331282199Sdumbbell driver->bus = &drm_pci_bus; 332282199Sdumbbell 333282199Sdumbbell dev->dev = kdev; 334282199Sdumbbell 335282199Sdumbbell dev->pci_domain = pci_get_domain(dev->dev); 336282199Sdumbbell dev->pci_bus = pci_get_bus(dev->dev); 337282199Sdumbbell dev->pci_slot = pci_get_slot(dev->dev); 338282199Sdumbbell dev->pci_func = pci_get_function(dev->dev); 339282199Sdumbbell 340282199Sdumbbell dev->pci_vendor = pci_get_vendor(dev->dev); 341282199Sdumbbell dev->pci_device = pci_get_device(dev->dev); 342282199Sdumbbell dev->pci_subvendor = pci_get_subvendor(dev->dev); 343282199Sdumbbell dev->pci_subdevice = pci_get_subdevice(dev->dev); 344282199Sdumbbell 345282199Sdumbbell sx_xlock(&drm_global_mutex); 346282199Sdumbbell 347282199Sdumbbell if ((ret = drm_fill_in_dev(dev, driver))) { 348282199Sdumbbell DRM_ERROR("Failed to fill in dev: %d\n", ret); 349282199Sdumbbell goto err_g1; 350282199Sdumbbell } 351282199Sdumbbell 352282199Sdumbbell if (drm_core_check_feature(dev, DRIVER_MODESET)) { 353282199Sdumbbell ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL); 354282199Sdumbbell if (ret) 355282199Sdumbbell goto err_g2; 356282199Sdumbbell } 357282199Sdumbbell 358282199Sdumbbell if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY))) 359282199Sdumbbell goto err_g3; 360282199Sdumbbell 361282199Sdumbbell if (dev->driver->load) { 362282199Sdumbbell ret = dev->driver->load(dev, 363282199Sdumbbell dev->id_entry->driver_private); 364282199Sdumbbell if (ret) 365282199Sdumbbell goto err_g4; 366282199Sdumbbell } 367282199Sdumbbell 368282199Sdumbbell /* setup the grouping for the legacy output */ 369282199Sdumbbell if (drm_core_check_feature(dev, DRIVER_MODESET)) { 370282199Sdumbbell ret = drm_mode_group_init_legacy_group(dev, 371282199Sdumbbell &dev->primary->mode_group); 372282199Sdumbbell if (ret) 373282199Sdumbbell goto err_g5; 374282199Sdumbbell } 375282199Sdumbbell 376282199Sdumbbell#ifdef FREEBSD_NOTYET 377282199Sdumbbell list_add_tail(&dev->driver_item, &driver->device_list); 378282199Sdumbbell#endif /* FREEBSD_NOTYET */ 379282199Sdumbbell 380282199Sdumbbell DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", 381282199Sdumbbell driver->name, driver->major, driver->minor, driver->patchlevel, 382282199Sdumbbell driver->date, device_get_nameunit(dev->dev), dev->primary->index); 383282199Sdumbbell 384282199Sdumbbell sx_xunlock(&drm_global_mutex); 385282199Sdumbbell return 0; 386282199Sdumbbell 387282199Sdumbbellerr_g5: 388282199Sdumbbell if (dev->driver->unload) 389282199Sdumbbell dev->driver->unload(dev); 390282199Sdumbbellerr_g4: 391282199Sdumbbell drm_put_minor(&dev->primary); 392282199Sdumbbellerr_g3: 393282199Sdumbbell if (drm_core_check_feature(dev, DRIVER_MODESET)) 394282199Sdumbbell drm_put_minor(&dev->control); 395282199Sdumbbellerr_g2: 396282199Sdumbbell drm_cancel_fill_in_dev(dev); 397282199Sdumbbellerr_g1: 398282199Sdumbbell sx_xunlock(&drm_global_mutex); 399282199Sdumbbell return ret; 400282199Sdumbbell} 401282199SdumbbellEXPORT_SYMBOL(drm_get_pci_dev); 402282199Sdumbbell 403282199Sdumbbellint 404282199Sdumbbelldrm_pci_enable_msi(struct drm_device *dev) 405282199Sdumbbell{ 406282199Sdumbbell int msicount, ret; 407282199Sdumbbell 408282199Sdumbbell if (!drm_msi) 409282199Sdumbbell return (-ENOENT); 410282199Sdumbbell 411282199Sdumbbell msicount = pci_msi_count(dev->dev); 412282199Sdumbbell DRM_DEBUG("MSI count = %d\n", msicount); 413282199Sdumbbell if (msicount > 1) 414282199Sdumbbell msicount = 1; 415282199Sdumbbell 416282199Sdumbbell ret = pci_alloc_msi(dev->dev, &msicount); 417282199Sdumbbell if (ret == 0) { 418282199Sdumbbell DRM_INFO("MSI enabled %d message(s)\n", msicount); 419282199Sdumbbell dev->msi_enabled = 1; 420282199Sdumbbell dev->irqrid = 1; 421282199Sdumbbell } 422282199Sdumbbell 423282199Sdumbbell return (-ret); 424282199Sdumbbell} 425282199Sdumbbell 426282199Sdumbbellvoid 427282199Sdumbbelldrm_pci_disable_msi(struct drm_device *dev) 428282199Sdumbbell{ 429282199Sdumbbell 430282199Sdumbbell if (!dev->msi_enabled) 431282199Sdumbbell return; 432282199Sdumbbell 433282199Sdumbbell pci_release_msi(dev->dev); 434282199Sdumbbell dev->msi_enabled = 0; 435282199Sdumbbell dev->irqrid = 0; 436282199Sdumbbell} 437282199Sdumbbell 438254848Sdumbbellint drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) 439254848Sdumbbell{ 440254848Sdumbbell device_t root; 441254848Sdumbbell int pos; 442254848Sdumbbell u32 lnkcap = 0, lnkcap2 = 0; 443254848Sdumbbell 444254848Sdumbbell *mask = 0; 445282199Sdumbbell if (!drm_pci_device_is_pcie(dev)) 446254848Sdumbbell return -EINVAL; 447254848Sdumbbell 448259237Sdumbbell root = 449259237Sdumbbell device_get_parent( /* pcib */ 450259237Sdumbbell device_get_parent( /* `-- pci */ 451259237Sdumbbell device_get_parent( /* `-- vgapci */ 452282199Sdumbbell dev->dev))); /* `-- drmn */ 453254848Sdumbbell 454254848Sdumbbell pos = 0; 455254848Sdumbbell pci_find_cap(root, PCIY_EXPRESS, &pos); 456254848Sdumbbell if (!pos) 457254848Sdumbbell return -EINVAL; 458254848Sdumbbell 459254848Sdumbbell /* we've been informed via and serverworks don't make the cut */ 460254848Sdumbbell if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA || 461254848Sdumbbell pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS) 462254848Sdumbbell return -EINVAL; 463254848Sdumbbell 464254848Sdumbbell lnkcap = pci_read_config(root, pos + PCIER_LINK_CAP, 4); 465254848Sdumbbell lnkcap2 = pci_read_config(root, pos + PCIER_LINK_CAP2, 4); 466254848Sdumbbell 467254848Sdumbbell lnkcap &= PCIEM_LINK_CAP_MAX_SPEED; 468254848Sdumbbell lnkcap2 &= 0xfe; 469254848Sdumbbell 470254848Sdumbbell#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x02 /* Supported Link Speed 2.5GT/s */ 471254848Sdumbbell#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x04 /* Supported Link Speed 5.0GT/s */ 472254848Sdumbbell#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x08 /* Supported Link Speed 8.0GT/s */ 473254848Sdumbbell 474254848Sdumbbell if (lnkcap2) { /* PCIE GEN 3.0 */ 475254848Sdumbbell if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) 476254848Sdumbbell *mask |= DRM_PCIE_SPEED_25; 477254848Sdumbbell if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) 478254848Sdumbbell *mask |= DRM_PCIE_SPEED_50; 479254848Sdumbbell if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) 480254848Sdumbbell *mask |= DRM_PCIE_SPEED_80; 481254848Sdumbbell } else { 482254848Sdumbbell if (lnkcap & 1) 483254848Sdumbbell *mask |= DRM_PCIE_SPEED_25; 484254848Sdumbbell if (lnkcap & 2) 485254848Sdumbbell *mask |= DRM_PCIE_SPEED_50; 486254848Sdumbbell } 487254848Sdumbbell 488254848Sdumbbell DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", pci_get_vendor(root), pci_get_device(root), lnkcap, lnkcap2); 489254848Sdumbbell return 0; 490254848Sdumbbell} 491282199SdumbbellEXPORT_SYMBOL(drm_pcie_get_speed_cap_mask); 492