608 lines
14 KiB
C
608 lines
14 KiB
C
|
|
/****************************************************************************
|
|
* Copyright (C) 2003-2006 by XGI Technology, Taiwan.
|
|
* *
|
|
* All Rights Reserved. *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation on the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
* *
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NON-INFRINGEMENT. IN NO EVENT SHALL XGI AND/OR
|
|
* ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
***************************************************************************/
|
|
|
|
#include "xgi_types.h"
|
|
#include "xgi_linux.h"
|
|
#include "xgi_drv.h"
|
|
#include "xgi_regs.h"
|
|
#include "xgi_pcie.h"
|
|
|
|
void xgi_get_device_info(xgi_info_t * info, xgi_chip_info_t * req)
|
|
{
|
|
req->device_id = info->device_id;
|
|
req->device_name[0] = 'x';
|
|
req->device_name[1] = 'g';
|
|
req->device_name[2] = '4';
|
|
req->device_name[3] = '7';
|
|
req->vendor_id = info->vendor_id;
|
|
req->curr_display_mode = 0;
|
|
req->fb_size = info->fb.size;
|
|
req->sarea_bus_addr = info->sarea_info.bus_addr;
|
|
req->sarea_size = info->sarea_info.size;
|
|
}
|
|
|
|
void xgi_get_mmio_info(xgi_info_t * info, xgi_mmio_info_t * req)
|
|
{
|
|
req->mmioBase = (void *)info->mmio.base;
|
|
req->size = info->mmio.size;
|
|
}
|
|
|
|
void xgi_put_screen_info(xgi_info_t * info, xgi_screen_info_t * req)
|
|
{
|
|
info->scrn_info.scrn_start = req->scrn_start;
|
|
info->scrn_info.scrn_xres = req->scrn_xres;
|
|
info->scrn_info.scrn_yres = req->scrn_yres;
|
|
info->scrn_info.scrn_bpp = req->scrn_bpp;
|
|
info->scrn_info.scrn_pitch = req->scrn_pitch;
|
|
|
|
XGI_INFO("info->scrn_info.scrn_start: 0x%lx"
|
|
"info->scrn_info.scrn_xres: 0x%lx"
|
|
"info->scrn_info.scrn_yres: 0x%lx"
|
|
"info->scrn_info.scrn_bpp: 0x%lx"
|
|
"info->scrn_info.scrn_pitch: 0x%lx\n",
|
|
info->scrn_info.scrn_start,
|
|
info->scrn_info.scrn_xres,
|
|
info->scrn_info.scrn_yres,
|
|
info->scrn_info.scrn_bpp, info->scrn_info.scrn_pitch);
|
|
}
|
|
|
|
void xgi_get_screen_info(xgi_info_t * info, xgi_screen_info_t * req)
|
|
{
|
|
req->scrn_start = info->scrn_info.scrn_start;
|
|
req->scrn_xres = info->scrn_info.scrn_xres;
|
|
req->scrn_yres = info->scrn_info.scrn_yres;
|
|
req->scrn_bpp = info->scrn_info.scrn_bpp;
|
|
req->scrn_pitch = info->scrn_info.scrn_pitch;
|
|
|
|
XGI_INFO("req->scrn_start: 0x%lx"
|
|
"req->scrn_xres: 0x%lx"
|
|
"req->scrn_yres: 0x%lx"
|
|
"req->scrn_bpp: 0x%lx"
|
|
"req->scrn_pitch: 0x%lx\n",
|
|
req->scrn_start,
|
|
req->scrn_xres,
|
|
req->scrn_yres, req->scrn_bpp, req->scrn_pitch);
|
|
}
|
|
|
|
void xgi_ge_reset(xgi_info_t * info)
|
|
{
|
|
xgi_disable_ge(info);
|
|
xgi_enable_ge(info);
|
|
}
|
|
|
|
void xgi_sarea_info(xgi_info_t * info, xgi_sarea_info_t * req)
|
|
{
|
|
info->sarea_info.bus_addr = req->bus_addr;
|
|
info->sarea_info.size = req->size;
|
|
XGI_INFO("info->sarea_info.bus_addr: 0x%lx"
|
|
"info->sarea_info.size: 0x%lx\n",
|
|
info->sarea_info.bus_addr, info->sarea_info.size);
|
|
}
|
|
|
|
/*
|
|
* irq functions
|
|
*/
|
|
#define STALL_INTERRUPT_RESET_THRESHOLD 0xffff
|
|
|
|
static U32 s_invalid_begin = 0;
|
|
|
|
BOOL xgi_ge_irq_handler(xgi_info_t * info)
|
|
{
|
|
volatile U8 *mmio_vbase = info->mmio.vbase;
|
|
volatile U32 *ge_3d_status = (volatile U32 *)(mmio_vbase + 0x2800);
|
|
U32 int_status = ge_3d_status[4]; // interrupt status
|
|
U32 auto_reset_count = 0;
|
|
BOOL is_support_auto_reset = FALSE;
|
|
|
|
// Check GE on/off
|
|
if (0 == (0xffffc0f0 & int_status)) {
|
|
U32 old_ge_status = ge_3d_status[0x00];
|
|
U32 old_pcie_cmd_fetch_Addr = ge_3d_status[0x0a];
|
|
if (0 != (0x1000 & int_status)) {
|
|
// We got GE stall interrupt.
|
|
ge_3d_status[0x04] = int_status | 0x04000000;
|
|
|
|
if (TRUE == is_support_auto_reset) {
|
|
BOOL is_wrong_signal = FALSE;
|
|
static U32 last_int_tick_low,
|
|
last_int_tick_high;
|
|
static U32 new_int_tick_low;
|
|
static U32 continoue_int_count = 0;
|
|
// OE II is busy.
|
|
while (old_ge_status & 0x001c0000) {
|
|
U16 check;
|
|
// Check Read back status
|
|
*(mmio_vbase + 0x235c) = 0x80;
|
|
check =
|
|
*((volatile U16 *)(mmio_vbase +
|
|
0x2360));
|
|
if ((check & 0x3f) !=
|
|
((check & 0x3f00) >> 8)) {
|
|
is_wrong_signal = TRUE;
|
|
break;
|
|
}
|
|
// Check RO channel
|
|
*(mmio_vbase + 0x235c) = 0x83;
|
|
check =
|
|
*((volatile U16 *)(mmio_vbase +
|
|
0x2360));
|
|
if ((check & 0x0f) !=
|
|
((check & 0xf0) >> 4)) {
|
|
is_wrong_signal = TRUE;
|
|
break;
|
|
}
|
|
// Check RW channel
|
|
*(mmio_vbase + 0x235c) = 0x88;
|
|
check =
|
|
*((volatile U16 *)(mmio_vbase +
|
|
0x2360));
|
|
if ((check & 0x0f) !=
|
|
((check & 0xf0) >> 4)) {
|
|
is_wrong_signal = TRUE;
|
|
break;
|
|
}
|
|
// Check RO channel outstanding
|
|
*(mmio_vbase + 0x235c) = 0x8f;
|
|
check =
|
|
*((volatile U16 *)(mmio_vbase +
|
|
0x2360));
|
|
if (0 != (check & 0x3ff)) {
|
|
is_wrong_signal = TRUE;
|
|
break;
|
|
}
|
|
// Check RW channel outstanding
|
|
*(mmio_vbase + 0x235c) = 0x90;
|
|
check =
|
|
*((volatile U16 *)(mmio_vbase +
|
|
0x2360));
|
|
if (0 != (check & 0x3ff)) {
|
|
is_wrong_signal = TRUE;
|
|
break;
|
|
}
|
|
// No pending PCIE request. GE stall.
|
|
break;
|
|
}
|
|
|
|
if (is_wrong_signal) {
|
|
// Nothing but skip.
|
|
} else if (0 == continoue_int_count++) {
|
|
rdtsc(last_int_tick_low,
|
|
last_int_tick_high);
|
|
} else {
|
|
rdtscl(new_int_tick_low);
|
|
if ((new_int_tick_low -
|
|
last_int_tick_low) >
|
|
STALL_INTERRUPT_RESET_THRESHOLD) {
|
|
continoue_int_count = 0;
|
|
} else if (continoue_int_count >= 3) {
|
|
int time_out;
|
|
|
|
continoue_int_count = 0;
|
|
|
|
// GE Hung up, need reset.
|
|
XGI_INFO("Reset GE!\n");
|
|
|
|
*(mmio_vbase + 0xb057) = 8;
|
|
time_out = 0xffff;
|
|
while (0 !=
|
|
(ge_3d_status[0x00] &
|
|
0xf0000000)) {
|
|
while (0 !=
|
|
((--time_out) &
|
|
0xfff)) ;
|
|
if (0 == time_out) {
|
|
U8 old_3ce;
|
|
U8 old_3cf;
|
|
U8 old_index;
|
|
U8 old_36;
|
|
|
|
XGI_INFO
|
|
("Can not reset back 0x%lx!\n",
|
|
ge_3d_status
|
|
[0x00]);
|
|
*(mmio_vbase +
|
|
0xb057) = 0;
|
|
// Have to use 3x5.36 to reset.
|
|
// Save and close dynamic gating
|
|
old_3ce =
|
|
*(mmio_vbase
|
|
+ 0x3ce);
|
|
*(mmio_vbase +
|
|
0x3ce) = 0x2a;
|
|
old_3cf =
|
|
*(mmio_vbase
|
|
+ 0x3cf);
|
|
*(mmio_vbase +
|
|
0x3cf) =
|
|
old_3cf & 0xfe;
|
|
// Reset GE
|
|
old_index =
|
|
*(mmio_vbase
|
|
+ 0x3d4);
|
|
*(mmio_vbase +
|
|
0x3d4) = 0x36;
|
|
old_36 =
|
|
*(mmio_vbase
|
|
+ 0x3d5);
|
|
*(mmio_vbase +
|
|
0x3d5) =
|
|
old_36 | 0x10;
|
|
while (0 !=
|
|
((--time_out) & 0xfff)) ;
|
|
*(mmio_vbase +
|
|
0x3d5) =
|
|
old_36;
|
|
*(mmio_vbase +
|
|
0x3d4) =
|
|
old_index;
|
|
// Restore dynamic gating
|
|
*(mmio_vbase +
|
|
0x3cf) =
|
|
old_3cf;
|
|
*(mmio_vbase +
|
|
0x3ce) =
|
|
old_3ce;
|
|
break;
|
|
}
|
|
}
|
|
*(mmio_vbase + 0xb057) = 0;
|
|
|
|
// Increase Reset counter
|
|
auto_reset_count++;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
} else if (0 != (0x1 & int_status)) {
|
|
s_invalid_begin++;
|
|
ge_3d_status[0x04] = (int_status & ~0x01) | 0x04000000;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL xgi_crt_irq_handler(xgi_info_t * info)
|
|
{
|
|
BOOL ret = FALSE;
|
|
U8 save_3ce = bReadReg(0x3ce);
|
|
|
|
if (bIn3cf(0x37) & 0x01) // CRT1 interrupt just happened
|
|
{
|
|
U8 op3cf_3d;
|
|
U8 op3cf_37;
|
|
|
|
// What happened?
|
|
op3cf_37 = bIn3cf(0x37);
|
|
|
|
// Clear CRT interrupt
|
|
op3cf_3d = bIn3cf(0x3d);
|
|
bOut3cf(0x3d, (op3cf_3d | 0x04));
|
|
bOut3cf(0x3d, (op3cf_3d & ~0x04));
|
|
ret = TRUE;
|
|
}
|
|
bWriteReg(0x3ce, save_3ce);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
BOOL xgi_dvi_irq_handler(xgi_info_t * info)
|
|
{
|
|
BOOL ret = FALSE;
|
|
U8 save_3ce = bReadReg(0x3ce);
|
|
|
|
if (bIn3cf(0x38) & 0x20) // DVI interrupt just happened
|
|
{
|
|
U8 op3cf_39;
|
|
U8 op3cf_37;
|
|
U8 op3x5_5a;
|
|
U8 save_3x4 = bReadReg(0x3d4);;
|
|
|
|
// What happened?
|
|
op3cf_37 = bIn3cf(0x37);
|
|
|
|
//Notify BIOS that DVI plug/unplug happened
|
|
op3x5_5a = bIn3x5(0x5a);
|
|
bOut3x5(0x5a, op3x5_5a & 0xf7);
|
|
|
|
bWriteReg(0x3d4, save_3x4);
|
|
|
|
// Clear DVI interrupt
|
|
op3cf_39 = bIn3cf(0x39);
|
|
bOut3c5(0x39, (op3cf_39 & ~0x01)); //Set 3cf.39 bit 0 to 0
|
|
bOut3c5(0x39, (op3cf_39 | 0x01)); //Set 3cf.39 bit 0 to 1
|
|
|
|
ret = TRUE;
|
|
}
|
|
bWriteReg(0x3ce, save_3ce);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
void xgi_dump_register(xgi_info_t * info)
|
|
{
|
|
int i, j;
|
|
unsigned char temp;
|
|
|
|
// 0x3C5
|
|
printk("\r\n=====xgi_dump_register========0x%x===============\r\n",
|
|
0x3C5);
|
|
|
|
for (i = 0; i < 0x10; i++) {
|
|
if (i == 0) {
|
|
printk("%5x", i);
|
|
} else {
|
|
printk("%3x", i);
|
|
}
|
|
}
|
|
printk("\r\n");
|
|
|
|
for (i = 0; i < 0x10; i++) {
|
|
printk("%1x ", i);
|
|
|
|
for (j = 0; j < 0x10; j++) {
|
|
temp = bIn3c5(i * 0x10 + j);
|
|
printk("%3x", temp);
|
|
}
|
|
printk("\r\n");
|
|
}
|
|
|
|
// 0x3D5
|
|
printk("\r\n====xgi_dump_register=========0x%x===============\r\n",
|
|
0x3D5);
|
|
for (i = 0; i < 0x10; i++) {
|
|
if (i == 0) {
|
|
printk("%5x", i);
|
|
} else {
|
|
printk("%3x", i);
|
|
}
|
|
}
|
|
printk("\r\n");
|
|
|
|
for (i = 0; i < 0x10; i++) {
|
|
printk("%1x ", i);
|
|
|
|
for (j = 0; j < 0x10; j++) {
|
|
temp = bIn3x5(i * 0x10 + j);
|
|
printk("%3x", temp);
|
|
}
|
|
printk("\r\n");
|
|
}
|
|
|
|
// 0x3CF
|
|
printk("\r\n=========xgi_dump_register====0x%x===============\r\n",
|
|
0x3CF);
|
|
for (i = 0; i < 0x10; i++) {
|
|
if (i == 0) {
|
|
printk("%5x", i);
|
|
} else {
|
|
printk("%3x", i);
|
|
}
|
|
}
|
|
printk("\r\n");
|
|
|
|
for (i = 0; i < 0x10; i++) {
|
|
printk("%1x ", i);
|
|
|
|
for (j = 0; j < 0x10; j++) {
|
|
temp = bIn3cf(i * 0x10 + j);
|
|
printk("%3x", temp);
|
|
}
|
|
printk("\r\n");
|
|
}
|
|
|
|
printk("\r\n=====xgi_dump_register======0x%x===============\r\n",
|
|
0xB000);
|
|
for (i = 0; i < 0x10; i++) {
|
|
if (i == 0) {
|
|
printk("%5x", i);
|
|
} else {
|
|
printk("%3x", i);
|
|
}
|
|
}
|
|
printk("\r\n");
|
|
|
|
for (i = 0; i < 0x5; i++) {
|
|
printk("%1x ", i);
|
|
|
|
for (j = 0; j < 0x10; j++) {
|
|
temp = bReadReg(0xB000 + i * 0x10 + j);
|
|
printk("%3x", temp);
|
|
}
|
|
printk("\r\n");
|
|
}
|
|
|
|
printk("\r\n==================0x%x===============\r\n", 0x2200);
|
|
for (i = 0; i < 0x10; i++) {
|
|
if (i == 0) {
|
|
printk("%5x", i);
|
|
} else {
|
|
printk("%3x", i);
|
|
}
|
|
}
|
|
printk("\r\n");
|
|
|
|
for (i = 0; i < 0xB; i++) {
|
|
printk("%1x ", i);
|
|
|
|
for (j = 0; j < 0x10; j++) {
|
|
temp = bReadReg(0x2200 + i * 0x10 + j);
|
|
printk("%3x", temp);
|
|
}
|
|
printk("\r\n");
|
|
}
|
|
|
|
printk("\r\n==================0x%x===============\r\n", 0x2300);
|
|
for (i = 0; i < 0x10; i++) {
|
|
if (i == 0) {
|
|
printk("%5x", i);
|
|
} else {
|
|
printk("%3x", i);
|
|
}
|
|
}
|
|
printk("\r\n");
|
|
|
|
for (i = 0; i < 0x7; i++) {
|
|
printk("%1x ", i);
|
|
|
|
for (j = 0; j < 0x10; j++) {
|
|
temp = bReadReg(0x2300 + i * 0x10 + j);
|
|
printk("%3x", temp);
|
|
}
|
|
printk("\r\n");
|
|
}
|
|
|
|
printk("\r\n==================0x%x===============\r\n", 0x2400);
|
|
for (i = 0; i < 0x10; i++) {
|
|
if (i == 0) {
|
|
printk("%5x", i);
|
|
} else {
|
|
printk("%3x", i);
|
|
}
|
|
}
|
|
printk("\r\n");
|
|
|
|
for (i = 0; i < 0x10; i++) {
|
|
printk("%1x ", i);
|
|
|
|
for (j = 0; j < 0x10; j++) {
|
|
temp = bReadReg(0x2400 + i * 0x10 + j);
|
|
printk("%3x", temp);
|
|
}
|
|
printk("\r\n");
|
|
}
|
|
|
|
printk("\r\n==================0x%x===============\r\n", 0x2800);
|
|
for (i = 0; i < 0x10; i++) {
|
|
if (i == 0) {
|
|
printk("%5x", i);
|
|
} else {
|
|
printk("%3x", i);
|
|
}
|
|
}
|
|
printk("\r\n");
|
|
|
|
for (i = 0; i < 0x10; i++) {
|
|
printk("%1x ", i);
|
|
|
|
for (j = 0; j < 0x10; j++) {
|
|
temp = bReadReg(0x2800 + i * 0x10 + j);
|
|
printk("%3x", temp);
|
|
}
|
|
printk("\r\n");
|
|
}
|
|
}
|
|
|
|
void xgi_restore_registers(xgi_info_t * info)
|
|
{
|
|
bOut3x5(0x13, 0);
|
|
bOut3x5(0x8b, 2);
|
|
}
|
|
|
|
void xgi_waitfor_pci_idle(xgi_info_t * info)
|
|
{
|
|
#define WHOLD_GE_STATUS 0x2800
|
|
#define IDLE_MASK ~0x90200000
|
|
|
|
int idleCount = 0;
|
|
while (idleCount < 5) {
|
|
if (dwReadReg(WHOLD_GE_STATUS) & IDLE_MASK) {
|
|
idleCount = 0;
|
|
} else {
|
|
idleCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
int xgi_get_cpu_id(struct cpu_info_s *arg)
|
|
{
|
|
int op = arg->_eax;
|
|
__asm__("cpuid":"=a"(arg->_eax),
|
|
"=b"(arg->_ebx),
|
|
"=c"(arg->_ecx), "=d"(arg->_edx)
|
|
: "0"(op));
|
|
|
|
XGI_INFO
|
|
("opCode = 0x%x, eax = 0x%x, ebx = 0x%x, ecx = 0x%x, edx = 0x%x \n",
|
|
op, arg->_eax, arg->_ebx, arg->_ecx, arg->_edx);
|
|
}
|
|
|
|
/*memory collect function*/
|
|
extern struct list_head xgi_mempid_list;
|
|
void xgi_mem_collect(xgi_info_t * info, unsigned int *pcnt)
|
|
{
|
|
xgi_mem_pid_t *mempid_block;
|
|
struct list_head *mempid_list;
|
|
struct task_struct *p, *find;
|
|
unsigned int cnt = 0;
|
|
|
|
mempid_list = xgi_mempid_list.next;
|
|
|
|
while (mempid_list != &xgi_mempid_list) {
|
|
mempid_block =
|
|
list_entry(mempid_list, struct xgi_mem_pid_s, list);
|
|
mempid_list = mempid_list->next;
|
|
|
|
find = NULL;
|
|
XGI_SCAN_PROCESS(p) {
|
|
if (p->pid == mempid_block->pid) {
|
|
XGI_INFO
|
|
("[!]Find active pid:%ld state:%ld location:%d addr:0x%lx! \n",
|
|
mempid_block->pid, p->state,
|
|
mempid_block->location,
|
|
mempid_block->bus_addr);
|
|
find = p;
|
|
if (mempid_block->bus_addr == 0xFFFFFFFF)
|
|
++cnt;
|
|
break;
|
|
}
|
|
}
|
|
if (!find) {
|
|
if (mempid_block->location == LOCAL) {
|
|
XGI_INFO
|
|
("Memory ProcessID free fb and delete one block pid:%ld addr:0x%lx successfully! \n",
|
|
mempid_block->pid, mempid_block->bus_addr);
|
|
xgi_fb_free(info, mempid_block->bus_addr);
|
|
} else if (mempid_block->bus_addr != 0xFFFFFFFF) {
|
|
XGI_INFO
|
|
("Memory ProcessID free pcie and delete one block pid:%ld addr:0x%lx successfully! \n",
|
|
mempid_block->pid, mempid_block->bus_addr);
|
|
xgi_pcie_free(info, mempid_block->bus_addr);
|
|
} else {
|
|
/*only delete the memory block */
|
|
list_del(&mempid_block->list);
|
|
XGI_INFO
|
|
("Memory ProcessID delete one pcie block pid:%ld successfully! \n",
|
|
mempid_block->pid);
|
|
kfree(mempid_block);
|
|
}
|
|
}
|
|
}
|
|
*pcnt = cnt;
|
|
}
|