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