1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Misc module for AGP master device support 31 */ 32 33 #include <sys/modctl.h> 34 #include <sys/pci.h> 35 #include <sys/stat.h> 36 #include <sys/file.h> 37 #include <sys/types.h> 38 #include <sys/dditypes.h> 39 #include <sys/sunddi.h> 40 #include <sys/agpgart.h> 41 #include <sys/agp/agpdefs.h> 42 #include <sys/agp/agpmaster_io.h> 43 44 #define I8XX_MMIO_REGSET 2 45 #define I8XX_FB_REGSET 1 46 #define I8XX_PTE_OFFSET 0x10000 47 #define I8XX_PGTBL_CTL 0x2020 48 #define I915_GTTADDR_BAR 4 49 #define I915_FB_REGSET 3 50 51 #ifdef DEBUG 52 #define CONFIRM(value) ASSERT(value) 53 #else 54 #define CONFIRM(value) if (!(value)) return (EINVAL) 55 #endif 56 57 int agpm_debug = 0; 58 #define AGPM_DEBUG(args) if (agpm_debug >= 1) cmn_err args 59 60 /* 61 * Whether it is a Intel integrated graphics card 62 */ 63 #define IS_IGD(agpmaster) ((agpmaster->agpm_dev_type == DEVICE_IS_I810) || \ 64 (agpmaster->agpm_dev_type == DEVICE_IS_I830)) 65 66 67 #define IS_INTEL_9XX(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_910) || \ 68 (agpmaster->agpm_id == INTEL_IGD_910M) || \ 69 (agpmaster->agpm_id == INTEL_IGD_945) || \ 70 (agpmaster->agpm_id == INTEL_IGD_945GM)) 71 72 static struct modlmisc modlmisc = { 73 &mod_miscops, "AGP master interfaces v%I%" 74 }; 75 76 static struct modlinkage modlinkage = { 77 MODREV_1, (void *)&modlmisc, NULL 78 }; 79 80 static ddi_device_acc_attr_t i8xx_dev_access = { 81 DDI_DEVICE_ATTR_V0, 82 DDI_NEVERSWAP_ACC, 83 DDI_STRICTORDER_ACC 84 }; 85 86 static off_t agpmaster_cap_find(ddi_acc_handle_t); 87 static int detect_i8xx_device(agp_master_softc_t *); 88 static int detect_agp_devcice(agp_master_softc_t *, ddi_acc_handle_t); 89 static int i8xx_add_to_gtt(gtt_impl_t *, igd_gtt_seg_t); 90 static void i8xx_remove_from_gtt(gtt_impl_t *, igd_gtt_seg_t); 91 92 int 93 _init(void) 94 { 95 int err; 96 97 if ((err = mod_install(&modlinkage)) != 0) 98 return (err); 99 100 return (0); 101 } 102 103 int 104 _fini(void) 105 { 106 int err; 107 108 if ((err = mod_remove(&modlinkage)) != 0) 109 return (err); 110 111 return (0); 112 } 113 114 int 115 _info(struct modinfo *modinfop) 116 { 117 return (mod_info(&modlinkage, modinfop)); 118 } 119 120 /* 121 * Minor node is not removed here, since the caller (xx_attach) is 122 * responsible for removing all nodes. 123 */ 124 void 125 agpmaster_detach(agp_master_softc_t **master_softcp) 126 { 127 agp_master_softc_t *master_softc; 128 129 ASSERT(master_softcp); 130 master_softc = *master_softcp; 131 132 /* intel integrated device */ 133 if (IS_IGD(master_softc)) { 134 if (master_softc->agpm_data.agpm_gtt.gtt_mmio_handle != NULL) { 135 ddi_regs_map_free( 136 &master_softc->agpm_data.agpm_gtt.gtt_mmio_handle); 137 } 138 } 139 140 kmem_free(master_softc, sizeof (agp_master_softc_t)); 141 master_softc = NULL; 142 143 return; 144 145 } 146 147 /* 148 * Try to initialize agp master. 149 * 0 is returned if the device is successfully initialized. AGP master soft 150 * state is returned in master_softcp if needed. 151 * Otherwise -1 is returned and *master_softcp is set to NULL. 152 */ 153 int 154 agpmaster_attach(dev_info_t *devi, agp_master_softc_t **master_softcp, 155 ddi_acc_handle_t pci_acc_hdl, minor_t minor) 156 { 157 int instance; 158 int status; 159 agp_master_softc_t *agpmaster; 160 uint32_t value; 161 off_t reg_size; 162 char buf[80]; 163 164 165 ASSERT(pci_acc_hdl); 166 *master_softcp = NULL; 167 agpmaster = (agp_master_softc_t *) 168 kmem_zalloc(sizeof (agp_master_softc_t), KM_SLEEP); 169 170 agpmaster->agpm_id = 171 pci_config_get32(pci_acc_hdl, PCI_CONF_VENID); 172 agpmaster->agpm_acc_hdl = pci_acc_hdl; 173 174 if (!detect_i8xx_device(agpmaster)) { 175 /* map mmio register set */ 176 if (IS_INTEL_9XX(agpmaster)) { 177 status = ddi_regs_map_setup(devi, I915_GTTADDR_BAR, 178 &agpmaster->agpm_data.agpm_gtt.gtt_mmio_base, 179 0, 0, &i8xx_dev_access, 180 &agpmaster->agpm_data.agpm_gtt.gtt_mmio_handle); 181 } else { 182 status = ddi_regs_map_setup(devi, I8XX_MMIO_REGSET, 183 &agpmaster->agpm_data.agpm_gtt.gtt_mmio_base, 184 0, 0, &i8xx_dev_access, 185 &agpmaster->agpm_data.agpm_gtt.gtt_mmio_handle); 186 } 187 188 if (status != DDI_SUCCESS) { 189 AGPM_DEBUG((CE_WARN, 190 "agpmaster_attach: ddi_regs_map_setup failed")); 191 goto fail; 192 } 193 /* get GTT range base offset */ 194 if (IS_INTEL_9XX(agpmaster)) { 195 agpmaster->agpm_data.agpm_gtt.gtt_addr = 196 agpmaster->agpm_data.agpm_gtt.gtt_mmio_base; 197 } else 198 agpmaster->agpm_data.agpm_gtt.gtt_addr = 199 agpmaster->agpm_data.agpm_gtt.gtt_mmio_base + 200 I8XX_PTE_OFFSET; 201 202 /* get graphics memory size */ 203 if (IS_INTEL_9XX(agpmaster)) { 204 status = ddi_dev_regsize(devi, I915_FB_REGSET, 205 ®_size); 206 } else 207 status = ddi_dev_regsize(devi, I8XX_FB_REGSET, 208 ®_size); 209 /* 210 * if memory size is smaller than a certain value, it means 211 * the register set number for graphics memory range might 212 * be wrong 213 */ 214 if (status != DDI_SUCCESS || reg_size < 0x400000) { 215 AGPM_DEBUG((CE_WARN, 216 "agpmaster_attach: ddi_dev_regsize error")); 217 goto fail; 218 } 219 220 agpmaster->agpm_data.agpm_gtt.gtt_info.igd_apersize = 221 BYTES2MB(reg_size); 222 if (IS_INTEL_9XX(agpmaster)) { 223 value = pci_config_get32(pci_acc_hdl, 224 I915_CONF_GMADR); 225 } else 226 value = pci_config_get32(pci_acc_hdl, 227 I8XX_CONF_GMADR); 228 229 agpmaster->agpm_data.agpm_gtt.gtt_info.igd_aperbase = 230 value & GTT_BASE_MASK; 231 agpmaster->agpm_data.agpm_gtt.gtt_info.igd_devid = 232 agpmaster->agpm_id; 233 } else if (detect_agp_devcice(agpmaster, pci_acc_hdl)) { 234 /* 235 * non IGD or AGP devices, AMD64 gart 236 */ 237 AGPM_DEBUG((CE_WARN, 238 "agpmaster_attach: neither IGD or AGP devices exists")); 239 agpmaster_detach(&agpmaster); 240 return (0); 241 } 242 243 /* create minor node for IGD or AGP device */ 244 instance = ddi_get_instance(devi); 245 246 (void) sprintf(buf, "%s%d", AGPMASTER_NAME, instance); 247 status = ddi_create_minor_node(devi, buf, S_IFCHR, minor, 248 DDI_NT_AGP_MASTER, 0); 249 250 if (status != DDI_SUCCESS) { 251 AGPM_DEBUG((CE_WARN, 252 "agpmaster_attach: create agpmaster node failed")); 253 goto fail; 254 } 255 256 *master_softcp = agpmaster; 257 return (0); 258 fail: 259 agpmaster_detach(&agpmaster); 260 return (-1); 261 } 262 263 /* 264 * Currently, it handles ioctl requests related with agp master device for 265 * layered driver (agpgart) only. 266 */ 267 /*ARGSUSED*/ 268 int 269 agpmaster_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *cred, 270 int *rval, agp_master_softc_t *softc) 271 { 272 uint32_t base; 273 uint32_t addr; 274 igd_gtt_seg_t seg; 275 agp_info_t info; 276 uint32_t value; 277 off_t cap; 278 uint32_t command; 279 static char kernel_only[] = 280 "agpmaster_ioctl: %s is a kernel only ioctl"; 281 282 CONFIRM(softc); 283 284 switch (cmd) { 285 case DEVICE_DETECT: 286 if (!(mode & FKIOCTL)) { 287 AGPM_DEBUG((CE_CONT, kernel_only, "DEVICE_DETECT")); 288 return (ENXIO); 289 } 290 291 if (ddi_copyout(&softc->agpm_dev_type, 292 (void *)data, sizeof (int), mode)) 293 return (EFAULT); 294 break; 295 case AGP_MASTER_SETCMD: 296 if (!(mode & FKIOCTL)) { 297 AGPM_DEBUG((CE_CONT, kernel_only, "AGP_MASTER_SETCMD")); 298 return (ENXIO); 299 } 300 301 CONFIRM(softc->agpm_dev_type == DEVICE_IS_AGP); 302 CONFIRM(softc->agpm_data.agpm_acaptr); 303 304 if (ddi_copyin((void *)data, &command, 305 sizeof (uint32_t), mode)) 306 return (EFAULT); 307 308 pci_config_put32(softc->agpm_acc_hdl, 309 softc->agpm_data.agpm_acaptr + AGP_CONF_COMMAND, 310 command); 311 break; 312 case AGP_MASTER_GETINFO: 313 if (!(mode & FKIOCTL)) { 314 AGPM_DEBUG((CE_CONT, kernel_only, 315 "AGP_MASTER_GETINFO")); 316 return (ENXIO); 317 } 318 319 CONFIRM(softc->agpm_dev_type == DEVICE_IS_AGP); 320 CONFIRM(softc->agpm_data.agpm_acaptr); 321 322 cap = softc->agpm_data.agpm_acaptr; 323 value = pci_config_get32(softc->agpm_acc_hdl, cap); 324 info.agpi_version.agpv_major = (uint16_t)((value >> 20) & 0xf); 325 info.agpi_version.agpv_minor = (uint16_t)((value >> 16) & 0xf); 326 info.agpi_devid = softc->agpm_id; 327 info.agpi_mode = pci_config_get32( 328 softc->agpm_acc_hdl, cap + AGP_CONF_STATUS); 329 330 if (ddi_copyout(&info, (void *)data, 331 sizeof (agp_info_t), mode)) 332 return (EFAULT); 333 break; 334 case I810_SET_GTT_BASE: 335 if (!(mode & FKIOCTL)) { 336 AGPM_DEBUG((CE_CONT, kernel_only, "I810_SET_GTT_ADDR")); 337 return (ENXIO); 338 } 339 340 CONFIRM(softc->agpm_dev_type == DEVICE_IS_I810); 341 342 if (ddi_copyin((void *)data, &base, sizeof (uint32_t), mode)) 343 return (EFAULT); 344 345 /* enables page table */ 346 addr = (base & GTT_BASE_MASK) | GTT_TABLE_VALID; 347 348 ddi_put32(softc->agpm_data.agpm_gtt.gtt_mmio_handle, 349 (uint32_t *)(softc->agpm_data.agpm_gtt.gtt_mmio_base + 350 I8XX_PGTBL_CTL), 351 addr); 352 break; 353 case I8XX_GET_INFO: 354 if (!(mode & FKIOCTL)) { 355 AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_GET_INFO")); 356 return (ENXIO); 357 } 358 359 CONFIRM(IS_IGD(softc)); 360 361 if (ddi_copyout(&softc->agpm_data.agpm_gtt.gtt_info, 362 (void *)data, sizeof (igd_info_t), mode)) 363 return (EFAULT); 364 break; 365 case I8XX_ADD2GTT: 366 if (!(mode & FKIOCTL)) { 367 AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_ADD2GTT")); 368 return (ENXIO); 369 } 370 371 CONFIRM(IS_IGD(softc)); 372 373 if (ddi_copyin((void *)data, &seg, 374 sizeof (igd_gtt_seg_t), mode)) 375 return (EFAULT); 376 377 if (i8xx_add_to_gtt(&softc->agpm_data.agpm_gtt, seg)) 378 return (EINVAL); 379 break; 380 case I8XX_REM_GTT: 381 if (!(mode & FKIOCTL)) { 382 AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_REM_GTT")); 383 return (ENXIO); 384 } 385 386 CONFIRM(IS_IGD(softc)); 387 388 if (ddi_copyin((void *)data, &seg, 389 sizeof (igd_gtt_seg_t), mode)) 390 return (EFAULT); 391 392 i8xx_remove_from_gtt(&softc->agpm_data.agpm_gtt, seg); 393 break; 394 case I8XX_UNCONFIG: 395 if (!(mode & FKIOCTL)) { 396 AGPM_DEBUG((CE_CONT, kernel_only, "I8XX_UNCONFIG")); 397 return (ENXIO); 398 } 399 400 CONFIRM(IS_IGD(softc)); 401 402 if (softc->agpm_dev_type == DEVICE_IS_I810) 403 ddi_put32(softc->agpm_data.agpm_gtt.gtt_mmio_handle, 404 (uint32_t *) 405 (softc->agpm_data.agpm_gtt.gtt_mmio_base + 406 I8XX_PGTBL_CTL), 0); 407 /* 408 * may need to clear all gtt entries here for i830 series, 409 * but may not be necessary 410 */ 411 break; 412 } 413 return (0); 414 } 415 416 /* 417 * If AGP cap pointer is successfully found, none-zero value is returned. 418 * Otherwise 0 is returned. 419 */ 420 static off_t 421 agpmaster_cap_find(ddi_acc_handle_t acc_handle) 422 { 423 off_t nextcap; 424 uint32_t ncapid; 425 uint8_t value; 426 427 /* check if this device supports capibility pointer */ 428 value = (uint8_t)(pci_config_get16(acc_handle, PCI_CONF_STAT) 429 & PCI_CONF_CAP_MASK); 430 431 if (!value) 432 return (0); 433 /* get the offset of the first capability pointer from CAPPTR */ 434 nextcap = (off_t)(pci_config_get8(acc_handle, AGP_CONF_CAPPTR)); 435 436 /* check AGP capability from the first capability pointer */ 437 while (nextcap) { 438 ncapid = pci_config_get32(acc_handle, nextcap); 439 if ((ncapid & PCI_CONF_CAPID_MASK) 440 == AGP_CAP_ID) /* find AGP cap */ 441 break; 442 443 nextcap = (off_t)((ncapid & PCI_CONF_NCAPID_MASK) >> 8); 444 } 445 446 return (nextcap); 447 448 } 449 450 /* 451 * If i8xx device is successfully detected, 0 is returned. 452 * Otherwise -1 is returned. 453 */ 454 static int 455 detect_i8xx_device(agp_master_softc_t *master_softc) 456 { 457 458 switch (master_softc->agpm_id) { 459 case INTEL_IGD_810: 460 case INTEL_IGD_810DC: 461 case INTEL_IGD_810E: 462 case INTEL_IGD_815: 463 master_softc->agpm_dev_type = DEVICE_IS_I810; 464 break; 465 case INTEL_IGD_830M: 466 case INTEL_IGD_845G: 467 case INTEL_IGD_855GM: 468 case INTEL_IGD_865G: 469 case INTEL_IGD_910: 470 case INTEL_IGD_910M: 471 case INTEL_IGD_945: 472 case INTEL_IGD_945GM: 473 master_softc->agpm_dev_type = DEVICE_IS_I830; 474 break; 475 default: /* unknown id */ 476 return (-1); 477 } 478 479 return (0); 480 } 481 482 /* 483 * If agp master is succssfully detected, 0 is returned. 484 * Otherwise -1 is returned. 485 */ 486 static int 487 detect_agp_devcice(agp_master_softc_t *master_softc, 488 ddi_acc_handle_t acc_handle) 489 { 490 off_t cap; 491 492 cap = agpmaster_cap_find(acc_handle); 493 if (cap) { 494 master_softc->agpm_dev_type = DEVICE_IS_AGP; 495 master_softc->agpm_data.agpm_acaptr = cap; 496 return (0); 497 } else { 498 return (-1); 499 } 500 501 } 502 503 /* 504 * Please refer to GART and GTT entry format table in agpdefs.h for 505 * intel GTT entry format. 506 */ 507 static int 508 phys2entry(uint32_t type, uint32_t physaddr, uint32_t *entry) 509 { 510 uint32_t value; 511 512 switch (type) { 513 case AGP_PHYSICAL: 514 case AGP_NORMAL: 515 value = (physaddr & GTT_PTE_MASK) | GTT_PTE_VALID; 516 break; 517 default: 518 return (-1); 519 } 520 521 *entry = value; 522 523 return (0); 524 } 525 526 static int 527 i8xx_add_to_gtt(gtt_impl_t *gtt, igd_gtt_seg_t seg) 528 { 529 int i; 530 uint32_t *paddr; 531 uint32_t entry; 532 uint32_t maxpages; 533 534 maxpages = gtt->gtt_info.igd_apersize; 535 maxpages = GTT_MB_TO_PAGES(maxpages); 536 537 paddr = seg.igs_phyaddr; 538 539 /* check if gtt max page number is reached */ 540 if ((seg.igs_pgstart + seg.igs_npage) > maxpages) 541 return (-1); 542 543 paddr = seg.igs_phyaddr; 544 for (i = seg.igs_pgstart; i < (seg.igs_pgstart + seg.igs_npage); 545 i++, paddr++) { 546 if (phys2entry(seg.igs_type, *paddr, &entry)) 547 return (-1); 548 ddi_put32(gtt->gtt_mmio_handle, 549 (uint32_t *)(gtt->gtt_addr + i * sizeof (uint32_t)), 550 entry); 551 } 552 553 return (0); 554 } 555 556 static void 557 i8xx_remove_from_gtt(gtt_impl_t *gtt, igd_gtt_seg_t seg) 558 { 559 int i; 560 uint32_t maxpages; 561 562 maxpages = gtt->gtt_info.igd_apersize; 563 maxpages = GTT_MB_TO_PAGES(maxpages); 564 565 /* check if gtt max page number is reached */ 566 if ((seg.igs_pgstart + seg.igs_npage) > maxpages) 567 return; 568 569 for (i = seg.igs_pgstart; i < (seg.igs_pgstart + seg.igs_npage); i++) { 570 ddi_put32(gtt->gtt_mmio_handle, 571 (uint32_t *)(gtt->gtt_addr + 572 i * sizeof (uint32_t)), 573 0); 574 } 575 } 576