summaryrefslogtreecommitdiff
path: root/linux-core/xgi_misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core/xgi_misc.c')
-rw-r--r--linux-core/xgi_misc.c571
1 files changed, 571 insertions, 0 deletions
diff --git a/linux-core/xgi_misc.c b/linux-core/xgi_misc.c
new file mode 100644
index 00000000..2d310a2f
--- /dev/null
+++ b/linux-core/xgi_misc.c
@@ -0,0 +1,571 @@
+
+/****************************************************************************
+ * 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_linux.h"
+#include "xgi_drv.h"
+#include "xgi_regs.h"
+#include "xgi_pcie.h"
+
+void xgi_get_device_info(struct xgi_info * info, struct xgi_chip_info * req)
+{
+ req->device_id = info->dev->device;
+ req->device_name[0] = 'x';
+ req->device_name[1] = 'g';
+ req->device_name[2] = '4';
+ req->device_name[3] = '7';
+ req->vendor_id = info->dev->vendor;
+ 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(struct xgi_info * info, struct xgi_mmio_info * req)
+{
+ req->mmio_base = info->mmio.base;
+ req->size = info->mmio.size;
+}
+
+void xgi_put_screen_info(struct xgi_info * info, struct xgi_screen_info * 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(struct xgi_info * info, struct xgi_screen_info * 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(struct xgi_info * info)
+{
+ xgi_disable_ge(info);
+ xgi_enable_ge(info);
+}
+
+void xgi_sarea_info(struct xgi_info * info, struct xgi_sarea_info * 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 unsigned int s_invalid_begin = 0;
+
+static bool xgi_validate_signal(volatile u8 *mmio_vbase)
+{
+ volatile u32 *const ge_3d_status =
+ (volatile u32 *)(mmio_vbase + 0x2800);
+ const u32 old_ge_status = ge_3d_status[0x00];
+
+ if (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)) {
+ return FALSE;
+ }
+
+ /* Check RO channel */
+ *(mmio_vbase + 0x235c) = 0x83;
+ check = *((volatile u16 *)(mmio_vbase + 0x2360));
+ if ((check & 0x0f) != ((check & 0xf0) >> 4)) {
+ return FALSE;
+ }
+
+ /* Check RW channel */
+ *(mmio_vbase + 0x235c) = 0x88;
+ check = *((volatile u16 *)(mmio_vbase + 0x2360));
+ if ((check & 0x0f) != ((check & 0xf0) >> 4)) {
+ return FALSE;
+ }
+
+ /* Check RO channel outstanding */
+ *(mmio_vbase + 0x235c) = 0x8f;
+ check = *((volatile u16 *)(mmio_vbase + 0x2360));
+ if (0 != (check & 0x3ff)) {
+ return FALSE;
+ }
+
+ /* Check RW channel outstanding */
+ *(mmio_vbase + 0x235c) = 0x90;
+ check = *((volatile u16 *)(mmio_vbase + 0x2360));
+ if (0 != (check & 0x3ff)) {
+ return FALSE;
+ }
+
+ /* No pending PCIE request. GE stall. */
+ }
+
+ return TRUE;
+}
+
+
+static void xgi_ge_hang_reset(volatile u8 *mmio_vbase)
+{
+ volatile u32 *const ge_3d_status =
+ (volatile u32 *)(mmio_vbase + 0x2800);
+ int time_out = 0xffff;
+
+ *(mmio_vbase + 0xb057) = 8;
+ while (0 != (ge_3d_status[0x00] & 0xf0000000)) {
+ while (0 != ((--time_out) & 0xfff))
+ /* empty */ ;
+
+ if (0 == time_out) {
+ u8 old_3ce;
+ u8 old_3cf;
+ u8 old_index;
+ u8 old_36;
+
+ XGI_INFO("Can not reset back 0x%x!\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))
+ /* empty */ ;
+
+ *(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;
+}
+
+
+bool xgi_ge_irq_handler(struct xgi_info * info)
+{
+ volatile u8 *const mmio_vbase = info->mmio.vbase;
+ volatile u32 *const ge_3d_status =
+ (volatile u32 *)(mmio_vbase + 0x2800);
+ const u32 int_status = ge_3d_status[4];
+ bool is_support_auto_reset = FALSE;
+
+ /* Check GE on/off */
+ if (0 == (0xffffc0f0 & int_status)) {
+ 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 (is_support_auto_reset) {
+ static cycles_t last_tick;
+ static unsigned continue_int_count = 0;
+
+ /* OE II is busy. */
+
+ if (!xgi_validate_signal(mmio_vbase)) {
+ /* Nothing but skip. */
+ } else if (0 == continue_int_count++) {
+ last_tick = get_cycles();
+ } else {
+ const cycles_t new_tick = get_cycles();
+ if ((new_tick - last_tick) >
+ STALL_INTERRUPT_RESET_THRESHOLD) {
+ continue_int_count = 0;
+ } else if (continue_int_count >= 3) {
+ continue_int_count = 0;
+
+ /* GE Hung up, need reset. */
+ XGI_INFO("Reset GE!\n");
+
+ xgi_ge_hang_reset(mmio_vbase);
+ }
+ }
+ }
+ } 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(struct xgi_info * 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(struct xgi_info * 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(struct xgi_info * 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(struct xgi_info * info)
+{
+ bOut3x5(0x13, 0);
+ bOut3x5(0x8b, 2);
+}
+
+void xgi_waitfor_pci_idle(struct xgi_info * 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++;
+ }
+ }
+}
+
+
+/*memory collect function*/
+extern struct list_head xgi_mempid_list;
+void xgi_mem_collect(struct xgi_info * info, unsigned int *pcnt)
+{
+ struct xgi_mem_pid *block;
+ struct xgi_mem_pid *next;
+ struct task_struct *p, *find;
+ unsigned int cnt = 0;
+
+ list_for_each_entry_safe(block, next, &xgi_mempid_list, list) {
+
+ find = NULL;
+ XGI_SCAN_PROCESS(p) {
+ if (p->pid == block->pid) {
+ XGI_INFO
+ ("[!]Find active pid:%ld state:%ld location:%d addr:0x%lx! \n",
+ block->pid, p->state,
+ block->location,
+ block->bus_addr);
+ find = p;
+ if (block->bus_addr == 0xFFFFFFFF)
+ ++cnt;
+ break;
+ }
+ }
+ if (!find) {
+ if (block->location == XGI_MEMLOC_LOCAL) {
+ XGI_INFO
+ ("Memory ProcessID free fb and delete one block pid:%ld addr:0x%lx successfully! \n",
+ block->pid, block->bus_addr);
+ xgi_fb_free(info, block->bus_addr);
+ } else if (block->bus_addr != 0xFFFFFFFF) {
+ XGI_INFO
+ ("Memory ProcessID free pcie and delete one block pid:%ld addr:0x%lx successfully! \n",
+ block->pid, block->bus_addr);
+ xgi_pcie_free(info, block->bus_addr);
+ } else {
+ /*only delete the memory block */
+ list_del(&block->list);
+ XGI_INFO
+ ("Memory ProcessID delete one pcie block pid:%ld successfully! \n",
+ block->pid);
+ kfree(block);
+ }
+ }
+ }
+ *pcnt = cnt;
+}