agpgart.c revision b526f5afa61d2760d2c59cb604b57b8c7a1a4f95
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Portions Philip Brown phil@bolthole.com Dec 2001
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* agpgart driver
*
* This driver is primary targeted at providing memory support for INTEL
* AGP device, INTEL memory less video card, and AMD64 cpu GART devices.
* So there are four main architectures, ARC_IGD810, ARC_IGD830, ARC_INTELAGP,
* ARC_AMD64AGP to agpgart driver. However, the memory
* interfaces are the same for these architectures. The difference is how to
* manage the hardware GART table for them.
*
* For large memory allocation, this driver use direct mapping to userland
* application interface to save kernel virtual memory .
*/
#include <sys/ddidevmap.h>
/* Dynamic debug support */
int agp_debug_var = 0;
/* Driver global softstate handle */
static void *agpgart_glob_soft_handle;
#define MAX_INSTNUM 16
((type) == ARC_AMD64AGP))
#define agpinfo_default_to_32(v, v32) \
{ \
}
static ddi_dma_attr_t agpgart_dma_attr = {
0U, /* dma_attr_addr_lo */
0xffffffffU, /* dma_attr_addr_hi */
0xffffffffU, /* dma_attr_count_max */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffU, /* dma_attr_maxxfer */
0xffffffffU, /* dma_attr_seg */
1, /* dma_attr_sgllen, variable */
4, /* dma_attr_granular */
0 /* dma_attr_flags */
};
/*
* AMD64 supports gart table above 4G. See alloc_gart_table.
*/
static ddi_dma_attr_t garttable_dma_attr = {
0U, /* dma_attr_addr_lo */
0xffffffffU, /* dma_attr_addr_hi */
0xffffffffU, /* dma_attr_count_max */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffU, /* dma_attr_maxxfer */
0xffffffffU, /* dma_attr_seg */
1, /* dma_attr_sgllen, variable */
4, /* dma_attr_granular */
0 /* dma_attr_flags */
};
/*
* AGPGART table need a physical contiguous memory. To assure that
* each access to gart table is strongly ordered and uncachable,
* we use DDI_STRICTORDER_ACC.
*/
static ddi_device_acc_attr_t gart_dev_acc_attr = {
DDI_STRICTORDER_ACC /* must be DDI_STRICTORDER_ACC */
};
/*
* AGP memory is usually used as texture memory or for a framebuffer, so we
* can set the memory attribute to write combining. Video drivers will
* determine the frame buffer attributes, for example the memory is write
* combinging or non-cachable. However, the interface between Xorg and agpgart
* driver to support attribute selcetion doesn't exist yet. So we set agp memory
* to non-cachable by default now. This attribute might be overridden
* by MTTR in X86.
*/
static ddi_device_acc_attr_t mem_dev_acc_attr = {
DDI_STRICTORDER_ACC /* Can be DDI_MERGING_OK_ACC */
};
static keytable_ent_t *
static void
static void
void **new_devprivate2)
{
struct keytable_ent *mementry;
if (new_handle1 != NULL) {
}
if (new_handle2 != NULL) {
}
mementry =
mementry->kte_refcnt--;
mementry =
mementry->kte_refcnt++;
}
}
/*ARGSUSED*/
static int
{
int instance;
struct keytable_ent *mementry;
return (ENXIO);
}
mementry =
mementry->kte_refcnt++;
return (0);
}
/*ARGSUSED*/
{
struct keytable_ent *mementry;
mementry->kte_refcnt++;
return (0);
}
struct devmap_callback_ctl agp_devmap_cb = {
DEVMAP_OPS_REV, /* rev */
agp_devmap_map, /* map */
NULL, /* access */
agp_devmap_dup, /* dup */
agp_devmap_unmap, /* unmap */
};
/*
* agp_master_regis_byname()
*
* Description:
* Open the AGP master device node by device path name and
* register the device handle for later operations.
* We check all possible driver instance from 0
* to MAX_INSTNUM because the master device could be
* at any instance number. Only one AGP master is supported.
*
* Arguments:
* master_hdlp AGP master device LDI handle pointer
* agpgart_l AGPGART driver LDI identifier
*
* Returns:
* -1 failed
* 0 success
*/
static int
{
int i;
char buf[MAXPATHLEN];
/*
* Search all possible instance numbers for the agp master device.
* Only one master device is supported now, so the search ends
* when one master device is found.
*/
for (i = 0; i < MAX_INSTNUM; i++) {
continue;
"master device found: instance number=%d", i));
break;
}
/* AGP master device not found */
if (i == MAX_INSTNUM)
return (-1);
return (0);
}
/*
* agp_target_regis_byname()
*
* Description:
* This function opens agp bridge device node by
* device path name and registers the device handle
* for later operations.
* We check driver instance from 0 to MAX_INSTNUM
* because the master device could be at any instance
* number. Only one agp target is supported.
*
*
* Arguments:
* target_hdlp AGP target device LDI handle pointer
* agpgart_l AGPGART driver LDI identifier
*
* Returns:
* -1 failed
* 0 success
*/
static int
{
int i;
char buf[MAXPATHLEN];
for (i = 0; i < MAX_INSTNUM; i++) {
continue;
"bridge device found: instance number=%d", i));
break;
}
/* AGP bridge device not found */
if (i == MAX_INSTNUM) {
return (-1);
}
return (0);
}
/*
* amd64_gart_regis_byname()
*
* Description:
* Open all amd64 gart device nodes by deice path name and
* register the device handles for later operations. Each cpu
* has its own amd64 gart device.
*
* Arguments:
* cpu_garts cpu garts device list header
* agpgart_l AGPGART driver LDI identifier
*
* Returns:
* -1 failed
* 0 success
*/
static int
{
int i;
char buf[MAXPATHLEN];
int ret;
/*
* Search all possible instance numbers for the gart devices.
* There can be multiple on-cpu gart devices for Opteron server.
*/
for (i = 0; i < MAX_INSTNUM; i++) {
&gart_hdl, agpgart_li);
continue;
else if (ret != 0) { /* There was an error opening the device */
return (ret);
}
"amd64 gart device found: instance number=%d", i));
/* Add new item to the head of the gart device list */
}
if (cpu_garts->gart_device_num == 0)
return (ENODEV);
return (0);
}
/*
* Unregister agp master device handle
*/
static void
{
if (master_hdlp) {
*master_hdlp = NULL;
}
}
/*
* Unregister agp bridge device handle
*/
static void
{
if (target_hdlp) {
*target_hdlp = NULL;
}
}
/*
* Unregister all amd64 gart device handles
*/
static void
{
/* Free allocated memory */
}
cpu_garts->gart_device_num = 0;
}
/*
* lyr_detect_master_type()
*
* Description:
* This function gets agp master type by querying agp master device.
*
* Arguments:
* master_hdlp agp master device ldi handle pointer
*
* Returns:
* -1 unsupported device
* DEVICE_IS_I810 i810 series
* DEVICE_IS_I810 i830 series
* DEVICE_IS_AGP true agp master
*/
static int
{
int vtype;
int err;
/* ldi_ioctl(agpmaster) */
if (err) /* Unsupported graphics device */
return (-1);
return (vtype);
}
/*
* devtect_target_type()
*
* Description:
* This function gets the host bridge chipset type by querying the agp
* target device.
*
* Arguments:
* target_hdlp agp target device LDI handle pointer
*
* Returns:
* CHIP_IS_INTEL Intel agp chipsets
* CHIP_IS_AMD AMD agp chipset
* -1 unsupported chipset
*/
static int
{
int btype;
int err;
if (err) /* Unsupported bridge device */
return (-1);
return (btype);
}
/*
* lyr_init()
*
* Description:
* This function detects the graphics system architecture and
* registers all relative device handles in a global structure
* "agp_regdev". Then it stores the system arc type in driver
* soft state.
*
* Arguments:
* agp_regdev AGP devices registration struct pointer
* agpgart_l AGPGART driver LDI identifier
*
* Returns:
* 0 System arc supported and agp devices registration successed.
* -1 System arc not supported or device registration failed.
*/
int
{
int ret;
/*
* Register agp devices, assuming all instances attached, and
* detect which agp architucture this server belongs to. This
* must be done before the agpgart driver starts to use layered
* driver interfaces.
*/
/* Check whether the system is amd64 arc */
/* No amd64 gart devices */
"lyr_init: this is not an amd64 system"));
"lyr_init: register master device unsuccessful"));
goto err1;
}
"lyr_init: register target device unsuccessful"));
goto err2;
}
/*
* Detect system arc by master device. If it is a intel
* integrated device, finish the detection successfully.
*/
switch (card_type) {
case DEVICE_IS_I810: /* I810 likewise graphics */
"lyr_init: the system is Intel 810 arch"));
return (0);
case DEVICE_IS_I830: /* I830 likewise graphics */
"lyr_init: the system is Intel 830 arch"));
return (0);
case DEVICE_IS_AGP: /* AGP graphics */
break;
"lyr_init: non-supported master device"));
goto err3;
}
/* Continue to detect AGP arc by target device */
switch (chip_type) {
case CHIP_IS_INTEL: /* Intel chipset */
"lyr_init: Intel AGP arch detected"));
return (0);
case CHIP_IS_AMD: /* AMD chipset */
"lyr_init: no cpu gart, but have AMD64 chipsets"));
goto err3;
default: /* Non supported chipset */
"lyr_init: detection can not continue"));
goto err3;
}
}
if (ret)
return (-1); /* Errors in open amd64 cpu gart devices */
/*
* AMD64 cpu gart device exsits, continue detection
*/
goto err1;
}
"lyr_init: no AGP bridge"));
goto err2;
}
"lyr_init: the system is AMD64 AGP architecture"));
return (0); /* Finished successfully */
err3:
err2:
err1:
/* AMD64 CPU gart registered ? */
if (ret == 0) {
}
return (-1);
}
void
{
switch (agp_regdev->agprd_arctype) {
case ARC_IGD810:
case ARC_IGD830:
case ARC_INTELAGP:
return;
case ARC_AMD64AGP:
return;
default:
ASSERT(0);
return;
}
}
int
{
int err;
switch (agp_regdev->agprd_arctype) {
case ARC_IGD810:
if (err)
return (-1);
if (err)
return (-1);
break;
case ARC_IGD830:
if (err)
return (-1);
if (err)
return (-1);
/*
* Assume all units are kilobytes unless explicitly
* stated below:
* preallocated GTT memory = preallocated memory - GTT size
* - scratch page size
*
* scratch page size = 4
* GTT size (KB) = aperture size (MB)
* this algorithm came from Xorg source code
*/
else {
"pre-allocated memory too small, setting to zero"));
prealloc_size = 0;
}
"lyr_get_info: prealloc_size = %ldKB, apersize = %dMB",
break;
case ARC_INTELAGP:
case ARC_AMD64AGP:
/* AGP devices */
if (err)
return (-1);
if (err)
return (-1);
break;
default:
"lyr_get_info: function doesn't work for unknown arc"));
return (-1);
}
(info->agpki_apersize == 0) ||
(info->agpki_aperbase == 0)) {
"lyr_get_info: aperture is not programmed correctly!"));
return (-1);
}
return (0);
}
/*
* lyr_i8xx_add_to_gtt()
*
* Description:
* This function sets up the integrated video device gtt table
* via an ioclt to the AGP master driver.
*
* Arguments:
* pg_offset The start entry to be setup
* keyent Keytable entity pointer
* agp_regdev AGP devices registration struct pointer
*
* Returns:
* 0 success
* -1 invalid operations
*/
int
{
int err = 0;
int rval;
*addrp =
}
err = -1;
}
return (err);
}
/*
* lyr_i8xx_remove_from_gtt()
*
* Description:
* This function clears the integrated video device gtt table via
* an ioctl to the agp master device.
*
* Arguments:
* pg_offset The starting entry to be cleared
* npage The number of entries to be cleared
* agp_regdev AGP devices struct pointer
*
* Returns:
* 0 success
* -1 invalid operations
*/
int
{
int rval;
return (-1);
return (0);
}
/*
* lyr_set_gart_addr()
*
* Description:
* This function puts the gart table physical address in the
* gart base register.
* Please refer to gart and gtt table base register format for
* gart base register format in agpdefs.h.
*
* Arguments:
* phy_base The base physical address of gart table
* agp_regdev AGP devices registration struct pointer
*
* Returns:
* 0 success
* -1 failed
*
*/
int
{
int err = 0;
switch (agp_regdev->agprd_arctype) {
case ARC_IGD810:
{
break;
}
case ARC_INTELAGP:
{
break;
}
case ARC_AMD64AGP:
{
err = -1;
break;
}
}
break;
}
default:
err = -1;
}
if (err)
return (-1);
return (0);
}
int
{
return (-1);
return (-1);
return (0);
}
int
{
int rc = 0;
switch (agp_regdev->agprd_arctype) {
case ARC_IGD830:
case ARC_IGD810:
break;
case ARC_INTELAGP:
{
break;
}
case ARC_AMD64AGP:
{
/*
* BIOS always shadow registers such like Aperture Base
* register, Aperture Size Register from the AGP bridge
* to the AMD64 CPU host bridge. If future BIOSes are broken
* in this regard, we may need to shadow these registers
* in driver.
*/
rc = -1;
break;
}
}
break;
}
default:
rc = -1;
}
if (rc)
return (-1);
return (0);
}
int
{
int rc = 0;
switch (agp_regdev->agprd_arctype) {
case ARC_IGD830:
case ARC_IGD810:
{
break;
}
case ARC_INTELAGP:
{
break;
}
case ARC_AMD64AGP:
{
rc = -1;
break;
}
}
break;
}
default:
rc = -1;
}
if (rc)
return (-1);
return (0);
}
/*
* lyr_flush_gart_cache()
*
* Description:
* This function flushes the GART translation look-aside buffer. All
* GART translation caches will be flushed after this operation.
*
* Arguments:
* agp_regdev AGP devices struct pointer
*/
void
{
}
}
}
/*
* get_max_pages()
*
* Description:
* This function compute the total pages allowed for agp aperture
* based on the ammount of physical pages.
* The algorithm is: compare the aperture size with 1/4 of total
* physical pages, and use the smaller one to for the max available
* pages.
*
* Arguments:
* aper_size system agp aperture size (in MB)
*
* Returns:
* The max possible number of agp memory pages available to users
*/
static uint32_t
{
uint32_t i, j;
i = AGP_MB2PAGES(aper_size);
j = (physmem >> 2);
return ((i < j) ? i : j);
}
/*
* agp_fill_empty_keyent()
*
* Description:
* This function finds a empty key table slot and
* fills it with a new entity.
*
* Arguments:
* softsate driver soft state pointer
* entryp new entity data pointer
*
* Returns:
* NULL no key table slot available
* entryp the new entity slot pointer
*/
static keytable_ent_t *
{
int key;
break;
}
}
if (key >= AGP_MAXKEYS) {
"agp_fill_empty_keyent: key table exhausted"));
return (NULL);
}
return (newentryp);
}
/*
* agp_find_bound_keyent()
*
* Description:
* This function finds the key table entity by agp aperture page offset.
* Every keytable entity will have an agp aperture range after the binding
* operation.
*
* Arguments:
* softsate driver soft state pointer
* pg_offset agp aperture page offset
*
* Returns:
* NULL no such keytable entity
* pointer key table entity pointer found
*/
static keytable_ent_t *
{
int keycount;
continue;
}
continue;
continue;
return (entryp);
}
return (NULL);
}
/*
* agp_check_off()
*
* Description:
* This function checks whether an AGP aperture range to be bound
* overlaps with AGP offset already bound.
*
* Arguments:
* entryp key table start entry pointer
* pg_start AGP range start page offset
* pg_num pages number to be bound
*
* Returns:
* 0 Does not overlap
* -1 Overlaps
*/
static int
{
int key;
continue;
break;
}
if (key == AGP_MAXKEYS)
return (0);
else
return (-1);
}
static int
{
if (!st->asoft_acquired) {
"ioctl_agpgart_setup: gart not acquired"));
return (-1);
}
"ioctl_agpgart_release: not controlling process"));
return (-1);
}
return (0);
}
{
st->asoft_curpid = 0;
st->asoft_acquired = 0;
}
{
}
/*
* agp_remove_from_gart()
*
* Description:
* This function fills the gart table entries by a given page
* frame number array and setup the agp aperture page to physical
* memory page translation.
* Arguments:
* pg_offset Starting aperture page to be bound
* entries the number of pages to be bound
* acc_hdl GART table dma memory acc handle
* tablep GART table kernel virtual address
*/
static void
{
items++;
}
}
/*
* agp_unbind_key()
*
* Description:
* This function unbinds AGP memory from the gart table. It will clear
* all the gart entries related to this agp memory.
*
* Arguments:
* softstate driver soft state pointer
* entryp key table entity pointer
*
* Returns:
* EINVAL invalid key table entity pointer
* 0 success
*
*/
static int
{
int retval = 0;
"agp_unbind_key: key = 0x%x, not bound",
return (EINVAL);
}
if (entryp->kte_refcnt) {
"agp_unbind_key: memory is exported to users"));
return (EINVAL);
}
case ARC_IGD810:
case ARC_IGD830:
if (retval) {
"agp_unbind_key: Key = 0x%x, clear table error",
return (EIO);
}
break;
case ARC_INTELAGP:
case ARC_AMD64AGP:
/* Flush GTLB table */
break;
}
return (0);
}
/*
* agp_dealloc_kmem()
*
* Description:
* This function deallocates dma memory resources for userland
* applications.
*
* Arguments:
* entryp keytable entity pointer
*/
static void
{
}
/*
* agp_dealloc_mem()
*
* Description:
* This function deallocates physical memory resources allocated for
* userland applications.
*
* Arguments:
* st driver soft state pointer
* entryp key table entity pointer
*
* Returns:
* -1 not a valid memory type or the memory is mapped by
* user area applications
* 0 success
*/
static int
{
/* auto unbind here */
"agp_dealloc_mem: key=0x%x, auto unbind",
/*
* agp_dealloc_mem may be called indirectly by agp_detach.
* In the agp_detach function, agpgart_close is already
* called which will free the gart table. agp_unbind_key
* will panic if no valid gart table exists. So test if
* gart table exsits here.
*/
if (st->asoft_opened)
}
if (entryp->kte_refcnt) {
"agp_dealloc_mem: memory is exported to users"));
return (-1);
}
case AGP_NORMAL:
case AGP_PHYSICAL:
break;
default:
return (-1);
}
return (0);
}
/*
* agp_del_allkeys()
*
* Description:
* This function calls agp_dealloc_mem to release all the agp memory
* resource allocated.
*
* Arguments:
* softsate driver soft state pointer
* Returns:
* -1 can not free all agp memory
* 0 success
*
*/
static int
{
int key;
int ret = 0;
/*
* Check if we can free agp memory now.
* If agp memory is exported to user
* applications, agp_dealloc_mem will fail.
*/
if (agp_dealloc_mem(softstate,
ret = -1;
}
}
return (ret);
}
/*
* pfn2gartentry()
*
* Description:
* This function converts a physical address to GART entry.
* For AMD64, hardware only support addresses below 40bits,
* about 1024G physical address, so the largest pfn
* number is below 28 bits. Please refer to GART and GTT entry
* format table in agpdefs.h for entry format. Intel IGD only
* only supports GTT entry below 1G. Intel AGP only supports
* GART entry below 4G.
*
* Arguments:
* arc_type system agp arc type
* pfn page frame number
* itemv the entry item to be returned
* Returns:
* -1 not a invalid page frame
* 0 conversion success
*/
static int
{
switch (arc_type) {
case ARC_INTELAGP:
{
/* Only support 32-bit hardware address */
if ((paddr & AGP_INTEL_POINTER_MASK) != 0) {
"INTEL AGP Hardware only support 32 bits"));
return (-1);
}
break;
}
case ARC_AMD64AGP:
{
/* Physaddr should not exceed 40-bit */
if ((paddr & AMD64_POINTER_MASK) != 0) {
"AMD64 GART hardware only supoort 40 bits"));
return (-1);
}
value1 <<= 4;
break;
}
case ARC_IGD810:
if ((paddr & I810_POINTER_MASK) != 0) {
"Intel i810 only support 30 bits"));
return (-1);
}
break;
case ARC_IGD830:
if ((paddr & GTT_POINTER_MASK) != 0) {
"Intel IGD only support 32 bits"));
return (-1);
}
break;
default:
"pfn2gartentry: arc type = %d, not support", arc_type));
return (-1);
}
return (0);
}
/*
* Check allocated physical pages validity, only called in DEBUG
* mode.
*/
static int
{
int count;
break;
}
return (-1);
else
return (0);
}
/*
* kmem_getpfns()
*
* Description:
* This function gets page frame numbers from dma handle.
*
* Arguments:
* dma_handle dma hanle allocated by ddi_dma_alloc_handle
* dma_cookip dma cookie pointer
* cookies_num cookies number
* pfnarray array to store page frames
*
* Returns:
* 0 success
*/
static int
int cookies_num,
{
int num_cookies;
int index = 0;
while (num_cookies > 0) {
while (ck_startaddr < ck_end) {
index++;
}
num_cookies--;
if (num_cookies > 0) {
}
}
return (0);
}
static int
{
case ARC_IGD810:
case ARC_IGD830:
break;
case ARC_INTELAGP:
case ARC_AMD64AGP:
break;
default:
return (-1);
}
/*
* 64bit->32bit conversion possible
*/
return (0);
}
static uint32_t
{
/*
* tstatus: target device status
* mstatus: master device status
* mode: the agp mode to be sent
*/
/*
* RQ - Request Queue size
* set RQ to the min of mode and tstatus
* if mode set a RQ larger than hardware can support,
* use the max RQ which hardware can support.
* tstatus & AGPSTAT_RQ_MASK is the max RQ hardware can support
* Corelogic will enqueue agp transaction
*/
/*
* SBA - Sideband Addressing
*
* Sideband Addressing provides an additional bus to pass requests
* (address and command) to the target from the master.
*
* set SBA if all three support it
*/
& (mode & AGPSTAT_SBA);
/* set OVER4G if all three support it */
& (mode & AGPSTAT_OVER4G);
/*
* FW - fast write
*
* acceleration of memory write transactions from the corelogic to the
* A.G.P. master device acting like a PCI target.
*
* set FW if all three support it
*/
& (mode & AGPSTAT_FW);
/*
* figure out the max rate
* AGP v2 support: 4X, 2X, 1X speed
* status bit meaning
* ---------------------------------------------
* 7:3 others
* 3 0 stand for V2 support
* 0:2 001:1X, 010:2X, 100:4X
* ----------------------------------------------
*/
& (mode & AGPSTAT_RATE_MASK);
if (rate & AGP2_RATE_4X)
rate = AGP2_RATE_4X;
else if (rate & AGP2_RATE_2X)
rate = AGP2_RATE_2X;
else
rate = AGP2_RATE_1X;
/* enable agp mode */
cmd |= AGPCMD_AGPEN;
return (cmd);
}
static uint32_t
{
/*
* tstatus: target device status
* mstatus: master device status
* mode: the agp mode to be set
*/
/*
* RQ - Request Queue size
* Set RQ to the min of mode and tstatus
* If mode set a RQ larger than hardware can support,
* use the max RQ which hardware can support.
* tstatus & AGPSTAT_RQ_MASK is the max RQ hardware can support
* Corelogic will enqueue agp transaction;
*/
/*
* ARQSZ - Asynchronous Request Queue size
* Set the value equal to tstatus.
* Don't allow the mode register to override values
*/
/*
* CAL - Calibration cycle
* Set to the min of tstatus and mstatus
* Don't allow override by mode register
*/
/*
* SBA - Sideband Addressing
*
* Sideband Addressing provides an additional bus to pass requests
* (address and command) to the target from the master.
*
* SBA in agp v3.0 must be set
*/
sba = AGPCMD_SBAEN;
/* GART64B is not set since no hardware supports it now */
/* Set OVER4G if all three support it */
& (mode & AGPSTAT_OVER4G);
/*
* FW - fast write
*
* Acceleration of memory write transactions from the corelogic to the
* A.G.P. master device acting like a PCI target.
*
* Always set FW in AGP 3.0
*/
& (mode & AGPSTAT_FW);
/*
* Figure out the max rate
*
* AGP v3 support: 8X, 4X speed
*
* status bit meaning
* ---------------------------------------------
* 7:3 others
* 3 1 stand for V3 support
* 0:2 001:4X, 010:8X, 011:4X,8X
* ----------------------------------------------
*/
& (mode & AGPSTAT_RATE_MASK);
if (rate & AGP3_RATE_8X)
rate = AGP3_RATE_8X;
else
rate = AGP3_RATE_4X;
/* Enable AGP mode */
cmd |= AGPCMD_AGPEN;
return (cmd);
}
static int
{
/*
* There are three kinds of AGP mode. AGP mode 1.0, 2.0, 3.0
* AGP mode 2.0 is fully compatible with AGP mode 1.0, so we
* only check 2.0 and 3.0 mode. AGP 3.0 device can work in
* two AGP 2.0 or AGP 3.0 mode. By checking AGP status register,
* we can get which mode it is working at. The working mode of
* AGP master and AGP target must be consistent. That is, both
* of them must work on AGP 3.0 mode or AGP 2.0 mode.
*/
(tstatus & AGPSTAT_MODE3)) {
/* Master device should be 3.0 mode, too */
((mstatus & AGPSTAT_MODE3) == 0))
return (EIO);
/* Write to the AGPCMD register of target and master devices */
if (lyr_set_agp_cmd(agp_mode,
return (EIO);
return (0);
}
/*
* If agp taget device doesn't work in AGP 3.0 mode,
* it must work in AGP 2.0 mode. And make sure
* master device work in AGP 2.0 mode too
*/
(mstatus & AGPSTAT_MODE3))
return (EIO);
return (EIO);
return (0);
}
/*
* agp_alloc_kmem()
*
* Description:
* This function allocates physical memory for userland applications
* by ddi interfaces. This function can also be called to allocate
* small phsyical contiguous pages, usually tens of kilobytes.
*
* Arguments:
* softsate driver soft state pointer
* length memory size
*
* Returns:
* entryp new keytable entity pointer
* NULL no keytable slot available or no physical
* memory available
*/
static keytable_ent_t *
{
int ret;
/*
* Set dma_attr_sgllen to assure contiguous physical pages
*/
if (type == AGP_PHYSICAL)
else
/* 4k size pages */
"agp_alloc_kmem: ddi_dma_allco_hanlde error"));
goto err4;
}
if ((ret = ddi_dma_mem_alloc(
"agp_alloc_kmem: ddi_dma_mem_alloc error"));
goto err3;
}
NULL,
NULL,
/*
* Even dma_attr_sgllen = 1, ddi_dma_addr_bind_handle may return more
* than one cookie, we check this in the if statement.
*/
if ((ret != DDI_DMA_MAPPED) ||
"agp_alloc_kmem: can not alloc physical memory properly"));
goto err2;
}
if (kmem_getpfns(
keyentry.kte_pfnarray)) {
goto err1;
}
goto err1;
if (!entryp) {
"agp_alloc_kmem: agp_fill_empty_keyent error"));
goto err1;
}
return (entryp);
err1:
err2:
err3:
err4:
return (NULL);
}
/*
* agp_alloc_mem()
*
* Description:
* This function allocate physical memory for userland applications,
* in order to save kernel virtual space, we use the direct mapping
* memory interface if it is available.
*
* Arguments:
* st driver soft state pointer
* length memory size
* type AGP_NORMAL: normal agp memory, AGP_PHISYCAL: specical
* memory type for intel i810 IGD
*
* Returns:
* NULL Invalid memory type or can not allocate memory
* Keytable entry pointer returned by agp_alloc_kmem
*/
static keytable_ent_t *
{
/*
* AGP_PHYSICAL type require contiguous physical pages exported
* to X drivers, like i810 HW cursor, ARGB cursor. the number of
* pages needed is usuallysmall and contiguous, 4K, 16K. So we
* use DDI interface to allocated such memory. And X use xsvc
* drivers to map this memory into its own address space.
*/
switch (type) {
case AGP_NORMAL:
case AGP_PHYSICAL:
default:
return (NULL);
}
}
/*
* free_gart_table()
*
* Description:
* This function frees the gart table memory allocated by driver.
* Must disable gart table before calling this function.
*
* Arguments:
* softstate driver soft state pointer
*
*/
static void
{
return;
st->gart_vbase = 0;
}
/*
* alloc_gart_table()
*
* Description:
* This function allocates one physical continuous gart table.
* INTEL integrated video device except i810 have their special
* video bios; No need to allocate gart table for them.
*
* Arguments:
* st driver soft state pointer
*
* Returns:
* 0 success
* -1 can not allocate gart tabl
*/
static int
{
int num_pages;
int ret = DDI_SUCCESS;
/*
* Only 40-bit maximum physical memory is supported by today's
* AGP hardware (32-bit gart tables can hold 40-bit memory addresses).
* No one supports 64-bit gart entries now, so the size of gart
* entries defaults to 32-bit though AGP3.0 specifies the possibility
* of 64-bit gart entries.
*/
/*
* Only AMD64 can put gart table above 4G, 40 bits at maximum
*/
else
/* Allocate physical continuous page frame for gart table */
"alloc_gart_table: ddi_dma_alloc_handle failed"));
goto err3;
}
&st->gart_vbase,
&st->gart_dma_acc_handle)) {
"alloc_gart_table: ddi_dma_mem_alloc failed"));
goto err2;
}
&cookie, &num_cookies);
if (num_cookies > 1)
"alloc_gart_table: alloc contiguous phys memory failed"));
goto err1;
}
return (0);
err1:
err2:
err3:
st->gart_pbase = 0;
st->gart_vbase = 0;
return (-1);
}
/*
* agp_add_to_gart()
*
* Description:
* This function fills the gart table entries by a given page frame number
* array and set up the agp aperture page to physical memory page
* translation.
* Arguments:
* type valid sytem arc types ARC_AMD64AGP, ARC_INTELAGP,
* ARC_AMD64AGP
* pfnarray allocated physical page frame number array
* pg_offset agp aperture start page to be bound
* entries the number of pages to be bound
* dma_hdl gart table dma memory handle
* tablep gart table kernel virtual address
* Returns:
* -1 failed
* 0 success
*/
static int
{
int items = 0;
break;
items++;
}
return (-1);
return (0);
}
/*
* agp_bind_key()
*
* Description:
* This function will call low level gart table access functions to
* set up gart table translation. Also it will do some sanity
* checking on key table entry.
*
* Arguments:
* softstate driver soft state pointer
* keyent key table entity pointer to be bound
* pg_offset aperture start page to be bound
* Returns:
* EINVAL not a valid operation
*/
static int
{
int ret = 0;
"agp_bind_key: key=0x%x,exceed aper range",
return (EINVAL);
}
"agp_bind_key: pg_offset=0x%x, pages=0x%lx overlaped",
return (EINVAL);
}
case ARC_IGD810:
case ARC_IGD830:
if (ret)
return (EIO);
break;
case ARC_INTELAGP:
case ARC_AMD64AGP:
if (ret)
return (EINVAL);
/* Flush GTLB table */
break;
default:
"agp_bind_key: arc type = 0x%x unsupported",
return (EINVAL);
}
return (0);
}
static int
{
int instance;
if (cmd != DDI_ATTACH) {
"agpgart_attach: only attach op supported"));
return (DDI_FAILURE);
}
!= DDI_SUCCESS) {
"agpgart_attach: soft state zalloc failed"));
goto err1;
}
/*
* Allocate LDI identifier for agpgart driver
* Agpgart driver is the kernel consumer
*/
"agpgart_attach: LDI indentifier allcation failed"));
goto err2;
}
/* Install agp kstat */
if (agp_init_kstats(softstate)) {
goto err3;
}
/*
*/
DDI_NT_AGP_PSEUDO, 0)) {
"agpgart_attach: Can not create minor node"));
goto err4;
}
AGP_MAXKEYS * (sizeof (keytable_ent_t)),
KM_SLEEP);
return (DDI_SUCCESS);
err4:
err3:
err2:
err1:
return (DDI_FAILURE);
}
static int
{
int instance;
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
/*
* Caller should free all the memory allocated explicitly.
* We release the memory allocated by caller which is not
* properly freed. mutex_enter here make sure assertion on
* softstate mutex success in agp_dealloc_mem.
*/
if (agp_del_allkeys(st)) {
"you might free agp memory exported to your applications"));
return (DDI_FAILURE);
}
if (st->asoft_table) {
AGP_MAXKEYS * (sizeof (keytable_ent_t)));
st->asoft_table = 0;
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
void **resultp)
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
rval = DDI_SUCCESS;
} else
break;
case DDI_INFO_DEVT2INSTANCE:
rval = DDI_SUCCESS;
break;
default:
break;
}
return (rval);
}
/*
* agpgart_open()
*
* Description:
* This function is the driver open entry point. If it is the
* first time the agpgart driver is opened, the driver will
* open other agp related layered drivers and set up the agpgart
* table properly.
*
* Arguments:
* dev device number pointer
* openflags open flags
* otyp OTYP_BLK, OTYP_CHR
* credp user's credential's struct pointer
*
* Returns:
* ENXIO operation error
* EAGAIN resoure temporarily unvailable
* 0 success
*/
/*ARGSUSED*/
static int
{
int rc = 0;
return (ENXIO);
}
if (softstate->asoft_opened) {
return (0);
}
/*
* The driver is opened first time, so we initialize layered
* driver interface and softstate member here.
*/
softstate->asoft_pgused = 0;
return (EAGAIN);
}
/* Call into layered driver */
return (EIO);
}
/*
* BIOS already set up gtt table for ARC_IGD830
*/
"agpgart_open: lyr_config_devices error"));
return (EIO);
}
return (0);
}
/*
* Allocate physically contiguous pages for AGP arc or
* i810 arc. If failed, divide aper_size by 2 to
* reduce gart table size until 4 megabytes. This
* is just a workaround for systems with very few
* physically contiguous memory.
*/
if (rc) {
(alloc_gart_table(softstate))) {
}
rc = 0;
}
if (rc != 0) {
"agpgart_open: alloc gart table failed"));
return (EAGAIN);
}
/*
* BIOS doesn't initialize GTT for i810,
* So i810 GTT must be created by driver.
*
* Set up gart table and enable it.
*/
&softstate->asoft_devreg)) {
"agpgart_open: set gart table addr failed"));
return (EIO);
}
"agpgart_open: lyr_config_devices failed"));
return (EIO);
}
return (0);
}
/*
* agpgart_close()
*
* Description:
* agpgart_close will release resources allocated in the first open
* and close other open layered drivers. Also it frees the memory
* allocated by ioctls.
*
* Arguments:
* dev device number
* flag file status flag
* otyp OTYP_BLK, OTYP_CHR
* credp user's credential's struct pointer
*
* Returns:
* ENXIO not an error, to support "deferred attach"
* 0 success
*/
/*ARGSUSED*/
static int
{
return (ENXIO);
}
/*
* If the last process close this device is not the controlling
* process, also release the control over agpgart driver here if the
* the controlling process fails to release the control before it
* close the driver.
*/
"agpgart_close: auto release control over driver"));
}
"agpgart_close: lyr_unconfig_device error"));
return (EIO);
}
softstate->asoft_agpen = 0;
}
/*
* This statement must be positioned before agp_del_allkeys
* agp_dealloc_mem indirectly called by agp_del_allkeys
* will test this variable.
*/
softstate->asoft_opened = 0;
/*
* Free the memory allocated by user applications which
* was never deallocated.
*/
(void) agp_del_allkeys(softstate);
return (0);
}
static int
{
#ifdef _MULTI_DATAMODEL
#endif
#ifdef _MULTI_DATAMODEL
return (EINVAL);
sizeof (agp_info32_t), flags) != 0)
return (EFAULT);
return (0);
}
#endif /* _MULTI_DATAMODEL */
return (EINVAL);
return (EFAULT);
}
return (0);
}
static int
{
if (st->asoft_acquired) {
return (EBUSY);
}
return (0);
}
static int
{
if (is_controlling_proc(st) < 0) {
"ioctl_agpgart_release: not a controlling process"));
return (EPERM);
}
return (0);
}
static int
{
int rc = 0;
if (is_controlling_proc(st) < 0) {
"ioctl_agpgart_setup: not a controlling process"));
return (EPERM);
}
"ioctl_agpgart_setup: no true agp bridge"));
return (EINVAL);
}
return (EFAULT);
return (rc);
/* Store agp mode status for kstat */
return (0);
}
static int
{
if (is_controlling_proc(st) < 0) {
"ioctl_agpgart_alloc: not a controlling process"));
return (EPERM);
}
sizeof (agp_allocate_t), flags) != 0) {
return (EFAULT);
}
"ioctl_agpgart_alloc: exceeding the memory pages limit"));
"ioctl_agpgart_alloc: request %x pages failed",
"ioctl_agpgart_alloc: pages used %x total is %x",
return (EINVAL);
}
if (!entryp) {
"ioctl_agpgart_alloc: allocate 0x%lx bytes failed",
length));
return (ENOMEM);
}
}
/* Update the memory pagse used */
sizeof (agp_allocate_t), flags) != 0) {
return (EFAULT);
}
return (0);
}
static int
{
int key;
if (is_controlling_proc(st) < 0) {
"ioctl_agpgart_dealloc: not a controlling process"));
return (EPERM);
}
return (EINVAL);
}
if (!keyent->kte_memhdl) {
return (EINVAL);
}
return (EINVAL);
/* Update the memory pages used */
return (0);
}
static int
{
int key;
int retval = 0;
if (is_controlling_proc(st) < 0) {
"ioctl_agpgart_bind: not a controlling process"));
return (EPERM);
}
return (EFAULT);
}
return (EINVAL);
}
"ioctl_agpgart_bind: bind to prealloc area "
"pgstart = %dKB < presize = %ldKB",
return (EINVAL);
}
}
if (!keyent->kte_memhdl) {
"ioctl_agpgart_bind: Key = 0x%x can't get keyenty",
key));
return (EINVAL);
}
"ioctl_agpgart_bind: Key = 0x%x already bound",
key));
return (EINVAL);
}
if (retval == 0) {
}
return (retval);
}
static int
{
if (is_controlling_proc(st) < 0) {
"ioctl_agpgart_bind: not a controlling process"));
return (EPERM);
}
return (EFAULT);
}
return (EINVAL);
}
return (EINVAL);
}
return (retval);
return (0);
}
/*ARGSUSED*/
static int
{
int instance;
int retval = 0;
return (ENXIO);
}
return (EPERM);
}
switch (cmd) {
case AGPIOC_INFO:
break;
case AGPIOC_ACQUIRE:
break;
case AGPIOC_RELEASE:
break;
case AGPIOC_SETUP:
break;
case AGPIOC_ALLOCATE:
break;
case AGPIOC_DEALLOCATE:
break;
case AGPIOC_BIND:
break;
case AGPIOC_UNBIND:
break;
default:
break;
}
return (retval);
}
static int
{
struct agpgart_softstate *softstate;
int instance;
int rc = 0;
return (ENXIO);
}
if (!AGP_ALIGNED(len))
return (EINVAL);
/*
* Process must have gart map privilege or gart access privilege
* to map agp memory.
*/
if (secpolicy_gart_map(credp)) {
return (EPERM);
}
return (rc);
}
/*ARGSUSED*/
static int
{
struct agpgart_softstate *softstate;
struct keytable_ent *mementry;
return (ENXIO);
}
return (EINVAL);
}
/*
* Can not find any memory now, so fail.
*/
"agpgart_devmap: can not find the proper keyent"));
return (EINVAL);
}
}
case AGP_NORMAL:
} else {
"agpgart_devmap: not a valid memory type"));
return (EINVAL);
}
break;
default:
"agpgart_devmap: not a valid memory type"));
return (EINVAL);
}
if (status == 0) {
} else {
*mappedlen = 0;
"agpgart_devmap: devmap interface failed"));
return (EINVAL);
}
return (0);
}
static struct cb_ops agpgart_cb_ops = {
agpgart_open, /* open() */
agpgart_close, /* close() */
nodev, /* strategy() */
nodev, /* print routine */
nodev, /* no dump routine */
nodev, /* read() */
nodev, /* write() */
agpgart_ioctl, /* agpgart_ioctl */
agpgart_devmap, /* devmap routine */
nodev, /* no longer use mmap routine */
agpgart_segmap, /* system segmap routine */
nochpoll, /* no chpoll routine */
ddi_prop_op, /* system prop operations */
0, /* not a STREAMS driver */
CB_REV, /* cb_ops version? */
nodev, /* cb_aread() */
nodev, /* cb_awrite() */
};
static struct dev_ops agpgart_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
agpgart_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
agpgart_attach, /* devo_attach */
agpgart_detach, /* devo_detach */
nodev, /* devo_reset */
&agpgart_cb_ops, /* devo_cb_ops */
(struct bus_ops *)0, /* devo_bus_ops */
NULL, /* devo_power */
};
"AGP driver v1.9",
};
static struct modlinkage modlinkage = {
MODREV_1, /* MODREV_1 is indicated by manual */
};
static void *agpgart_glob_soft_handle;
int
_init(void)
{
int ret = DDI_SUCCESS;
sizeof (agpgart_softstate_t),
if (ret != 0) {
"_init: soft state init error code=0x%x", ret));
return (ret);
}
"_init: mod install error code=0x%x", ret));
return (ret);
}
return (DDI_SUCCESS);
}
int
{
}
int
_fini(void)
{
int ret;
}
return (ret);
}