Copyright (c) 2002, Thomas Kurschel
Part of Radeon kernel driver
PCI GART.
Currently, we use PCI DMA. Changing to AGP would
only affect this file, but AGP-GART is specific to
the chipset of the motherboard, and as DMA is really
overkill for 2D, I cannot bother writing a dozen
of AGP drivers just to gain little extra speedup.
*/
#include "radeon_driver.h"
#include "mmio.h"
#include "buscntrl_regs.h"
#include "memcntrl_regs.h"
#include "cp_regs.h"
#include <image.h>
#include <stdlib.h>
#include <string.h>
#if 1
static status_t
createGARTBuffer(GART_info *gart, size_t size)
{
SHOW_FLOW0( 3, "" );
gart->buffer.size = size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
gart->buffer.area = create_area("Radeon PCI GART buffer",
&gart->buffer.ptr, B_ANY_KERNEL_ADDRESS,
size, B_FULL_LOCK,
B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
if (gart->buffer.area < 0) {
SHOW_ERROR(1, "cannot create PCI GART buffer (%s)",
strerror(gart->buffer.area));
return gart->buffer.area;
}
gart->buffer.unaligned_area = -1;
memset( gart->buffer.ptr, 0, size );
return B_OK;
}
#else
static status_t createGARTBuffer( GART_info *gart, size_t size )
{
physical_entry map[1];
void *unaligned_addr, *aligned_phys;
SHOW_FLOW0( 3, "" );
gart->buffer.size = size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
gart->buffer.unaligned_area = create_area( "Radeon PCI GART buffer",
&unaligned_addr, B_ANY_KERNEL_ADDRESS,
2 * size, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA );
if (gart->buffer.unaligned_area < 0) {
SHOW_ERROR( 1, "cannot create PCI GART buffer (%s)",
strerror( gart->buffer.unaligned_area ));
return gart->buffer.unaligned_area;
}
get_memory_map( unaligned_addr, B_PAGE_SIZE, map, 1 );
aligned_phys =
(void **)((map[0].address + size - 1) & ~(size - 1));
SHOW_FLOW( 3, "aligned_phys=%p", aligned_phys );
gart->buffer.area = map_physical_memory( "Radeon aligned PCI GART buffer",
(addr_t)aligned_phys,
size, B_ANY_KERNEL_BLOCK_ADDRESS | B_WRITE_COMBINING_MEMORY,
B_READ_AREA | B_WRITE_AREA, &gart->buffer.ptr );
if( gart->buffer.area < 0 ) {
SHOW_ERROR0( 3, "cannot map buffer with WC" );
gart->buffer.area = map_physical_memory( "Radeon aligned PCI GART buffer",
(addr_t)aligned_phys,
size, B_ANY_KERNEL_BLOCK_ADDRESS,
B_READ_AREA | B_WRITE_AREA, &gart->buffer.ptr );
}
if( gart->buffer.area < 0 ) {
SHOW_ERROR0( 1, "cannot map GART buffer" );
delete_area( gart->buffer.unaligned_area );
gart->buffer.unaligned_area = -1;
return gart->buffer.area;
}
memset( gart->buffer.ptr, 0, size );
return B_OK;
}
#endif
static status_t initGATT( GART_info *gart )
{
area_id map_area;
uint32 map_area_size;
physical_entry *map;
physical_entry PTB_map[1];
size_t map_count;
uint32 i;
uint32 *gatt_entry;
size_t num_pages;
SHOW_FLOW0( 3, "" );
num_pages = (gart->buffer.size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
gart->GATT.area = create_area("Radeon GATT", (void **)&gart->GATT.ptr,
B_ANY_KERNEL_ADDRESS,
(num_pages * sizeof( uint32 ) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1),
B_32_BIT_CONTIGUOUS,
B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
if (gart->GATT.area < 0) {
SHOW_ERROR(1, "cannot create GATT table (%s)",
strerror(gart->GATT.area));
return gart->GATT.area;
}
get_memory_map(gart->GATT.ptr, B_PAGE_SIZE, PTB_map, 1);
gart->GATT.phys = PTB_map[0].address;
SHOW_INFO(3, "GATT_ptr=%p, GATT_phys=%#" B_PRIx32, gart->GATT.ptr, gart->GATT.phys);
memset(gart->GATT.ptr, 0, num_pages * sizeof(uint32));
map_count = num_pages + 1;
map_area_size = map_count * sizeof(physical_entry);
if ((map_area_size / B_PAGE_SIZE) * B_PAGE_SIZE != map_area_size)
map_area_size = ((map_area_size / B_PAGE_SIZE) + 1) * B_PAGE_SIZE;
map_area = create_area("pci_gart_map_area", (void **)&map, B_ANY_ADDRESS,
map_area_size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
dprintf("pci_gart_map_area: %" B_PRId32 "\n", map_area);
get_memory_map( gart->buffer.ptr, gart->buffer.size, map, map_count );
gatt_entry = gart->GATT.ptr;
for( i = 0; i < map_count; ++i ) {
phys_addr_t addr = map[i].address;
size_t size = map[i].size;
if( size == 0 )
break;
while( size > 0 ) {
*gatt_entry++ = addr;
addr += ATI_PCIGART_PAGE_SIZE;
size -= ATI_PCIGART_PAGE_SIZE;
}
}
delete_area(map_area);
if( i == map_count ) {
SHOW_ERROR0( 0, "memory map of GART buffer too large!" );
delete_area( gart->GATT.area );
gart->GATT.area = -1;
return B_ERROR;
}
clear_caches( gart->GATT.ptr, num_pages * sizeof( uint32 ),
B_FLUSH_DCACHE );
#if defined(__i386__)
asm volatile ( "wbinvd" ::: "memory" );
#elif defined(__POWERPC__)
#endif
return B_OK;
}
static void destroyGARTBuffer( GART_info *gart )
{
if( gart->buffer.area > 0 )
delete_area( gart->buffer.area );
if( gart->buffer.unaligned_area > 0 )
delete_area( gart->buffer.unaligned_area );
gart->buffer.area = gart->buffer.unaligned_area = -1;
}
static void destroyGATT( GART_info *gart )
{
if( gart->GATT.area > 0 )
delete_area( gart->GATT.area );
gart->GATT.area = -1;
}
status_t Radeon_InitPCIGART( device_info *di )
{
status_t result;
result = createGARTBuffer( &di->pci_gart, PCI_GART_SIZE );
if( result < 0 )
goto err1;
result = initGATT( &di->pci_gart );
if( result < 0 )
goto err2;
return B_OK;
err2:
destroyGARTBuffer( &di->pci_gart );
err1:
return result;
}
void Radeon_CleanupPCIGART( device_info *di )
{
vuint8 *regs = di->regs;
SHOW_FLOW0( 3, "" );
OUTREG( regs, RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS );
INREG( regs, RADEON_CP_CSQ_CNTL );
OUTREGP( regs, RADEON_BUS_CNTL, RADEON_BUS_MASTER_DIS, ~RADEON_BUS_MASTER_DIS );
OUTREGP( regs, RADEON_AIC_CNTL, 0, ~RADEON_PCIGART_TRANSLATE_EN );
destroyGATT( &di->pci_gart );
destroyGARTBuffer( &di->pci_gart );
}