1280183Sdumbbell/* drm_pci.h -- PCI DMA memory management wrappers for DRM -*- linux-c -*- */ 2280183Sdumbbell/** 3280183Sdumbbell * \file drm_pci.c 4280183Sdumbbell * \brief Functions and ioctls to manage PCI memory 5280183Sdumbbell * 6280183Sdumbbell * \warning These interfaces aren't stable yet. 7280183Sdumbbell * 8280183Sdumbbell * \todo Implement the remaining ioctl's for the PCI pools. 9280183Sdumbbell * \todo The wrappers here are so thin that they would be better off inlined.. 10280183Sdumbbell * 11280183Sdumbbell * \author Jos�� Fonseca <jrfonseca@tungstengraphics.com> 12280183Sdumbbell * \author Leif Delgass <ldelgass@retinalburn.net> 13280183Sdumbbell */ 14280183Sdumbbell 15280183Sdumbbell/* 16280183Sdumbbell * Copyright 2003 Jos�� Fonseca. 17280183Sdumbbell * 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 34280183Sdumbbell * 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$"); 41235783Skib 42235783Skib#include <dev/drm2/drmP.h> 43235783Skib 44280183Sdumbbellstatic int drm_msi = 1; /* Enable by default. */ 45280183SdumbbellSYSCTL_NODE(_hw, OID_AUTO, drm, CTLFLAG_RW, NULL, "DRM device"); 46280183SdumbbellSYSCTL_INT(_hw_drm, OID_AUTO, msi, CTLFLAG_RDTUN, &drm_msi, 1, 47280183Sdumbbell "Enable MSI interrupts for drm devices"); 48280183Sdumbbell 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/** 66280183Sdumbbell * \brief Allocate a PCI consistent memory block, for DMA. 67235783Skib */ 68280183Sdumbbelldrm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, 69280183Sdumbbell 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 90279919Sjah ret = bus_dma_tag_create( 91280183Sdumbbell bus_get_dma_tag(dev->dev), /* parent */ 92279919Sjah 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 123280183SdumbbellEXPORT_SYMBOL(drm_pci_alloc); 124280183Sdumbbell 125235783Skib/** 126280183Sdumbbell * \brief Free a PCI consistent memory block without freeing its descriptor. 127280183Sdumbbell * 128280183Sdumbbell * This function is for internal use in the Linux-specific DRM core code. 129235783Skib */ 130280183Sdumbbellvoid __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) 131235783Skib{ 132235783Skib if (dmah == NULL) 133235783Skib return; 134235783Skib 135267446Sjhb bus_dmamap_unload(dmah->tag, dmah->map); 136235783Skib bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 137235783Skib bus_dma_tag_destroy(dmah->tag); 138280183Sdumbbell} 139235783Skib 140280183Sdumbbell/** 141280183Sdumbbell * \brief Free a PCI consistent memory block 142280183Sdumbbell */ 143280183Sdumbbellvoid drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) 144280183Sdumbbell{ 145280183Sdumbbell __drm_pci_free(dev, dmah); 146235783Skib free(dmah, DRM_MEM_DMA); 147235783Skib} 148235783Skib 149280183SdumbbellEXPORT_SYMBOL(drm_pci_free); 150254848Sdumbbell 151280183Sdumbbellstatic int drm_get_pci_domain(struct drm_device *dev) 152280183Sdumbbell{ 153280183Sdumbbell return dev->pci_domain; 154280183Sdumbbell} 155280183Sdumbbell 156280183Sdumbbellstatic int drm_pci_get_irq(struct drm_device *dev) 157280183Sdumbbell{ 158280183Sdumbbell 159280183Sdumbbell if (dev->irqr) 160280183Sdumbbell return (dev->irq); 161280183Sdumbbell 162280183Sdumbbell dev->irqr = bus_alloc_resource_any(dev->dev, SYS_RES_IRQ, 163280183Sdumbbell &dev->irqrid, RF_SHAREABLE); 164280183Sdumbbell if (!dev->irqr) { 165280183Sdumbbell dev_err(dev->dev, "Failed to allocate IRQ\n"); 166280183Sdumbbell return (0); 167280183Sdumbbell } 168280183Sdumbbell 169280183Sdumbbell dev->irq = (int) rman_get_start(dev->irqr); 170280183Sdumbbell 171280183Sdumbbell return (dev->irq); 172280183Sdumbbell} 173280183Sdumbbell 174280183Sdumbbellstatic void drm_pci_free_irq(struct drm_device *dev) 175280183Sdumbbell{ 176280183Sdumbbell if (dev->irqr == NULL) 177280183Sdumbbell return; 178280183Sdumbbell 179280183Sdumbbell bus_release_resource(dev->dev, SYS_RES_IRQ, 180280183Sdumbbell dev->irqrid, dev->irqr); 181280183Sdumbbell 182280183Sdumbbell dev->irqr = NULL; 183280183Sdumbbell dev->irq = 0; 184280183Sdumbbell} 185280183Sdumbbell 186280183Sdumbbellstatic const char *drm_pci_get_name(struct drm_device *dev) 187280183Sdumbbell{ 188280183Sdumbbell return dev->driver->name; 189280183Sdumbbell} 190280183Sdumbbell 191280183Sdumbbellint drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) 192280183Sdumbbell{ 193280183Sdumbbell int len, ret; 194280183Sdumbbell master->unique_len = 40; 195280183Sdumbbell master->unique_size = master->unique_len; 196280183Sdumbbell master->unique = malloc(master->unique_size, DRM_MEM_DRIVER, M_NOWAIT); 197280183Sdumbbell if (master->unique == NULL) 198280183Sdumbbell return -ENOMEM; 199280183Sdumbbell 200280183Sdumbbell 201280183Sdumbbell len = snprintf(master->unique, master->unique_len, 202280183Sdumbbell "pci:%04x:%02x:%02x.%d", 203280183Sdumbbell dev->pci_domain, 204280183Sdumbbell dev->pci_bus, 205280183Sdumbbell dev->pci_slot, 206280183Sdumbbell dev->pci_func); 207280183Sdumbbell 208280183Sdumbbell if (len >= master->unique_len) { 209280183Sdumbbell DRM_ERROR("buffer overflow"); 210280183Sdumbbell ret = -EINVAL; 211280183Sdumbbell goto err; 212280183Sdumbbell } else 213280183Sdumbbell master->unique_len = len; 214280183Sdumbbell 215280183Sdumbbell return 0; 216280183Sdumbbellerr: 217280183Sdumbbell return ret; 218280183Sdumbbell} 219280183Sdumbbell 220280183Sdumbbellint drm_pci_set_unique(struct drm_device *dev, 221280183Sdumbbell struct drm_master *master, 222280183Sdumbbell struct drm_unique *u) 223280183Sdumbbell{ 224280183Sdumbbell int domain, bus, slot, func, ret; 225280183Sdumbbell 226280183Sdumbbell master->unique_len = u->unique_len; 227280183Sdumbbell master->unique_size = u->unique_len + 1; 228293851Sdumbbell master->unique = malloc(master->unique_size, DRM_MEM_DRIVER, M_WAITOK); 229280183Sdumbbell if (!master->unique) { 230280183Sdumbbell ret = -ENOMEM; 231280183Sdumbbell goto err; 232280183Sdumbbell } 233280183Sdumbbell 234280183Sdumbbell if (copy_from_user(master->unique, u->unique, master->unique_len)) { 235280183Sdumbbell ret = -EFAULT; 236280183Sdumbbell goto err; 237280183Sdumbbell } 238280183Sdumbbell 239280183Sdumbbell master->unique[master->unique_len] = '\0'; 240280183Sdumbbell 241280183Sdumbbell /* Return error if the busid submitted doesn't match the device's actual 242280183Sdumbbell * busid. 243280183Sdumbbell */ 244280183Sdumbbell ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); 245280183Sdumbbell if (ret != 3) { 246280183Sdumbbell ret = -EINVAL; 247280183Sdumbbell goto err; 248280183Sdumbbell } 249280183Sdumbbell 250280183Sdumbbell domain = bus >> 8; 251280183Sdumbbell bus &= 0xff; 252280183Sdumbbell 253280183Sdumbbell if ((domain != dev->pci_domain) || 254280183Sdumbbell (bus != dev->pci_bus) || 255280183Sdumbbell (slot != dev->pci_slot) || 256280183Sdumbbell (func != dev->pci_func)) { 257280183Sdumbbell ret = -EINVAL; 258280183Sdumbbell goto err; 259280183Sdumbbell } 260280183Sdumbbell return 0; 261280183Sdumbbellerr: 262280183Sdumbbell return ret; 263280183Sdumbbell} 264280183Sdumbbell 265280183Sdumbbell 266280183Sdumbbellstatic int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) 267280183Sdumbbell{ 268280183Sdumbbell if ((p->busnum >> 8) != drm_get_pci_domain(dev) || 269280183Sdumbbell (p->busnum & 0xff) != dev->pci_bus || 270280183Sdumbbell p->devnum != dev->pci_slot || p->funcnum != dev->pci_func) 271280183Sdumbbell return -EINVAL; 272280183Sdumbbell 273280183Sdumbbell p->irq = dev->irq; 274280183Sdumbbell 275280183Sdumbbell DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum, 276280183Sdumbbell p->irq); 277280183Sdumbbell return 0; 278280183Sdumbbell} 279280183Sdumbbell 280280183Sdumbbellint drm_pci_agp_init(struct drm_device *dev) 281280183Sdumbbell{ 282280183Sdumbbell if (drm_core_has_AGP(dev)) { 283280183Sdumbbell if (drm_pci_device_is_agp(dev)) 284280183Sdumbbell dev->agp = drm_agp_init(dev); 285280183Sdumbbell if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP) 286280183Sdumbbell && (dev->agp == NULL)) { 287280183Sdumbbell DRM_ERROR("Cannot initialize the agpgart module.\n"); 288280183Sdumbbell return -EINVAL; 289280183Sdumbbell } 290280183Sdumbbell if (drm_core_has_MTRR(dev)) { 291280183Sdumbbell if (dev->agp && dev->agp->agp_info.ai_aperture_base != 0) { 292280183Sdumbbell if (drm_mtrr_add(dev->agp->agp_info.ai_aperture_base, 293280183Sdumbbell dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC) == 0) 294280183Sdumbbell dev->agp->agp_mtrr = 1; 295280183Sdumbbell else 296280183Sdumbbell dev->agp->agp_mtrr = -1; 297280183Sdumbbell } 298280183Sdumbbell } 299280183Sdumbbell } 300280183Sdumbbell return 0; 301280183Sdumbbell} 302280183Sdumbbell 303280183Sdumbbellstatic struct drm_bus drm_pci_bus = { 304280183Sdumbbell .bus_type = DRIVER_BUS_PCI, 305280183Sdumbbell .get_irq = drm_pci_get_irq, 306280183Sdumbbell .free_irq = drm_pci_free_irq, 307280183Sdumbbell .get_name = drm_pci_get_name, 308280183Sdumbbell .set_busid = drm_pci_set_busid, 309280183Sdumbbell .set_unique = drm_pci_set_unique, 310280183Sdumbbell .irq_by_busid = drm_pci_irq_by_busid, 311280183Sdumbbell .agp_init = drm_pci_agp_init, 312280183Sdumbbell}; 313280183Sdumbbell 314280183Sdumbbell/** 315280183Sdumbbell * Register. 316280183Sdumbbell * 317280183Sdumbbell * \param pdev - PCI device structure 318280183Sdumbbell * \param ent entry from the PCI ID table with device type flags 319280183Sdumbbell * \return zero on success or a negative number on failure. 320280183Sdumbbell * 321280183Sdumbbell * Attempt to gets inter module "drm" information. If we are first 322280183Sdumbbell * then register the character device and inter module information. 323280183Sdumbbell * Try and register, if we fail to register, backout previous work. 324280183Sdumbbell */ 325280183Sdumbbellint drm_get_pci_dev(device_t kdev, struct drm_device *dev, 326280183Sdumbbell struct drm_driver *driver) 327280183Sdumbbell{ 328280183Sdumbbell int ret; 329280183Sdumbbell 330280183Sdumbbell DRM_DEBUG("\n"); 331280183Sdumbbell 332280183Sdumbbell driver->bus = &drm_pci_bus; 333280183Sdumbbell 334280183Sdumbbell dev->dev = kdev; 335280183Sdumbbell 336280183Sdumbbell dev->pci_domain = pci_get_domain(dev->dev); 337280183Sdumbbell dev->pci_bus = pci_get_bus(dev->dev); 338280183Sdumbbell dev->pci_slot = pci_get_slot(dev->dev); 339280183Sdumbbell dev->pci_func = pci_get_function(dev->dev); 340280183Sdumbbell 341280183Sdumbbell dev->pci_vendor = pci_get_vendor(dev->dev); 342280183Sdumbbell dev->pci_device = pci_get_device(dev->dev); 343280183Sdumbbell dev->pci_subvendor = pci_get_subvendor(dev->dev); 344280183Sdumbbell dev->pci_subdevice = pci_get_subdevice(dev->dev); 345280183Sdumbbell 346280183Sdumbbell sx_xlock(&drm_global_mutex); 347280183Sdumbbell 348280183Sdumbbell if ((ret = drm_fill_in_dev(dev, driver))) { 349280183Sdumbbell DRM_ERROR("Failed to fill in dev: %d\n", ret); 350280183Sdumbbell goto err_g1; 351280183Sdumbbell } 352280183Sdumbbell 353280183Sdumbbell if (drm_core_check_feature(dev, DRIVER_MODESET)) { 354280183Sdumbbell ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL); 355280183Sdumbbell if (ret) 356280183Sdumbbell goto err_g2; 357280183Sdumbbell } 358280183Sdumbbell 359280183Sdumbbell if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY))) 360280183Sdumbbell goto err_g3; 361280183Sdumbbell 362280183Sdumbbell if (dev->driver->load) { 363280183Sdumbbell ret = dev->driver->load(dev, 364280183Sdumbbell dev->id_entry->driver_private); 365280183Sdumbbell if (ret) 366280183Sdumbbell goto err_g4; 367280183Sdumbbell } 368280183Sdumbbell 369280183Sdumbbell /* setup the grouping for the legacy output */ 370280183Sdumbbell if (drm_core_check_feature(dev, DRIVER_MODESET)) { 371280183Sdumbbell ret = drm_mode_group_init_legacy_group(dev, 372280183Sdumbbell &dev->primary->mode_group); 373280183Sdumbbell if (ret) 374280183Sdumbbell goto err_g5; 375280183Sdumbbell } 376280183Sdumbbell 377280183Sdumbbell#ifdef FREEBSD_NOTYET 378280183Sdumbbell list_add_tail(&dev->driver_item, &driver->device_list); 379280183Sdumbbell#endif /* FREEBSD_NOTYET */ 380280183Sdumbbell 381280183Sdumbbell DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", 382280183Sdumbbell driver->name, driver->major, driver->minor, driver->patchlevel, 383280183Sdumbbell driver->date, device_get_nameunit(dev->dev), dev->primary->index); 384280183Sdumbbell 385280183Sdumbbell sx_xunlock(&drm_global_mutex); 386280183Sdumbbell return 0; 387280183Sdumbbell 388280183Sdumbbellerr_g5: 389280183Sdumbbell if (dev->driver->unload) 390280183Sdumbbell dev->driver->unload(dev); 391280183Sdumbbellerr_g4: 392280183Sdumbbell drm_put_minor(&dev->primary); 393280183Sdumbbellerr_g3: 394280183Sdumbbell if (drm_core_check_feature(dev, DRIVER_MODESET)) 395280183Sdumbbell drm_put_minor(&dev->control); 396280183Sdumbbellerr_g2: 397280183Sdumbbell drm_cancel_fill_in_dev(dev); 398280183Sdumbbellerr_g1: 399280183Sdumbbell sx_xunlock(&drm_global_mutex); 400280183Sdumbbell return ret; 401280183Sdumbbell} 402280183SdumbbellEXPORT_SYMBOL(drm_get_pci_dev); 403280183Sdumbbell 404280183Sdumbbellint 405280183Sdumbbelldrm_pci_enable_msi(struct drm_device *dev) 406280183Sdumbbell{ 407280183Sdumbbell int msicount, ret; 408280183Sdumbbell 409280183Sdumbbell if (!drm_msi) 410280183Sdumbbell return (-ENOENT); 411280183Sdumbbell 412280183Sdumbbell msicount = pci_msi_count(dev->dev); 413280183Sdumbbell DRM_DEBUG("MSI count = %d\n", msicount); 414280183Sdumbbell if (msicount > 1) 415280183Sdumbbell msicount = 1; 416280183Sdumbbell 417280183Sdumbbell ret = pci_alloc_msi(dev->dev, &msicount); 418280183Sdumbbell if (ret == 0) { 419280183Sdumbbell DRM_INFO("MSI enabled %d message(s)\n", msicount); 420280183Sdumbbell dev->msi_enabled = 1; 421280183Sdumbbell dev->irqrid = 1; 422280183Sdumbbell } 423280183Sdumbbell 424280183Sdumbbell return (-ret); 425280183Sdumbbell} 426280183Sdumbbell 427280183Sdumbbellvoid 428280183Sdumbbelldrm_pci_disable_msi(struct drm_device *dev) 429280183Sdumbbell{ 430280183Sdumbbell 431280183Sdumbbell if (!dev->msi_enabled) 432280183Sdumbbell return; 433280183Sdumbbell 434280183Sdumbbell pci_release_msi(dev->dev); 435280183Sdumbbell dev->msi_enabled = 0; 436280183Sdumbbell dev->irqrid = 0; 437280183Sdumbbell} 438280183Sdumbbell 439254848Sdumbbellint drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) 440254848Sdumbbell{ 441254848Sdumbbell device_t root; 442254848Sdumbbell int pos; 443254848Sdumbbell u32 lnkcap = 0, lnkcap2 = 0; 444254848Sdumbbell 445254848Sdumbbell *mask = 0; 446280183Sdumbbell if (!drm_pci_device_is_pcie(dev)) 447254848Sdumbbell return -EINVAL; 448254848Sdumbbell 449258930Sdumbbell root = 450258930Sdumbbell device_get_parent( /* pcib */ 451258930Sdumbbell device_get_parent( /* `-- pci */ 452258930Sdumbbell device_get_parent( /* `-- vgapci */ 453280183Sdumbbell dev->dev))); /* `-- drmn */ 454254848Sdumbbell 455254848Sdumbbell pos = 0; 456254848Sdumbbell pci_find_cap(root, PCIY_EXPRESS, &pos); 457254848Sdumbbell if (!pos) 458254848Sdumbbell return -EINVAL; 459254848Sdumbbell 460254848Sdumbbell /* we've been informed via and serverworks don't make the cut */ 461254848Sdumbbell if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA || 462254848Sdumbbell pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS) 463254848Sdumbbell return -EINVAL; 464254848Sdumbbell 465254848Sdumbbell lnkcap = pci_read_config(root, pos + PCIER_LINK_CAP, 4); 466254848Sdumbbell lnkcap2 = pci_read_config(root, pos + PCIER_LINK_CAP2, 4); 467254848Sdumbbell 468254848Sdumbbell lnkcap &= PCIEM_LINK_CAP_MAX_SPEED; 469254848Sdumbbell lnkcap2 &= 0xfe; 470254848Sdumbbell 471254848Sdumbbell#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x02 /* Supported Link Speed 2.5GT/s */ 472254848Sdumbbell#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x04 /* Supported Link Speed 5.0GT/s */ 473254848Sdumbbell#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x08 /* Supported Link Speed 8.0GT/s */ 474254848Sdumbbell 475254848Sdumbbell if (lnkcap2) { /* PCIE GEN 3.0 */ 476254848Sdumbbell if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) 477254848Sdumbbell *mask |= DRM_PCIE_SPEED_25; 478254848Sdumbbell if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) 479254848Sdumbbell *mask |= DRM_PCIE_SPEED_50; 480254848Sdumbbell if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) 481254848Sdumbbell *mask |= DRM_PCIE_SPEED_80; 482254848Sdumbbell } else { 483254848Sdumbbell if (lnkcap & 1) 484254848Sdumbbell *mask |= DRM_PCIE_SPEED_25; 485254848Sdumbbell if (lnkcap & 2) 486254848Sdumbbell *mask |= DRM_PCIE_SPEED_50; 487254848Sdumbbell } 488254848Sdumbbell 489254848Sdumbbell DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", pci_get_vendor(root), pci_get_device(root), lnkcap, lnkcap2); 490254848Sdumbbell return 0; 491254848Sdumbbell} 492280183SdumbbellEXPORT_SYMBOL(drm_pcie_get_speed_cap_mask); 493