/**************************************************************************** * 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_drv.h" #include "xgi_regs.h" #include /* * irq functions */ #define STALL_INTERRUPT_RESET_THRESHOLD 0xffff static unsigned int s_invalid_begin = 0; static bool xgi_validate_signal(struct drm_map * map) { if (le32_to_cpu(DRM_READ32(map, 0x2800) & 0x001c0000)) { u16 check; /* Check Read back status */ DRM_WRITE8(map, 0x235c, 0x80); check = le16_to_cpu(DRM_READ16(map, 0x2360)); if ((check & 0x3f) != ((check & 0x3f00) >> 8)) { return false; } /* Check RO channel */ DRM_WRITE8(map, 0x235c, 0x83); check = le16_to_cpu(DRM_READ16(map, 0x2360)); if ((check & 0x0f) != ((check & 0xf0) >> 4)) { return false; } /* Check RW channel */ DRM_WRITE8(map, 0x235c, 0x88); check = le16_to_cpu(DRM_READ16(map, 0x2360)); if ((check & 0x0f) != ((check & 0xf0) >> 4)) { return false; } /* Check RO channel outstanding */ DRM_WRITE8(map, 0x235c, 0x8f); check = le16_to_cpu(DRM_READ16(map, 0x2360)); if (0 != (check & 0x3ff)) { return false; } /* Check RW channel outstanding */ DRM_WRITE8(map, 0x235c, 0x90); check = le16_to_cpu(DRM_READ16(map, 0x2360)); if (0 != (check & 0x3ff)) { return false; } /* No pending PCIE request. GE stall. */ } return true; } static void xgi_ge_hang_reset(struct drm_map * map) { int time_out = 0xffff; DRM_WRITE8(map, 0xb057, 8); while (0 != le32_to_cpu(DRM_READ32(map, 0x2800) & 0xf0000000)) { while (0 != ((--time_out) & 0xfff)) /* empty */ ; if (0 == time_out) { u8 old_3ce; u8 old_3cf; u8 old_index; u8 old_36; DRM_INFO("Can not reset back 0x%x!\n", le32_to_cpu(DRM_READ32(map, 0x2800))); DRM_WRITE8(map, 0xb057, 0); /* Have to use 3x5.36 to reset. */ /* Save and close dynamic gating */ old_3ce = DRM_READ8(map, 0x3ce); DRM_WRITE8(map, 0x3ce, 0x2a); old_3cf = DRM_READ8(map, 0x3cf); DRM_WRITE8(map, 0x3cf, old_3cf & 0xfe); /* Reset GE */ old_index = DRM_READ8(map, 0x3d4); DRM_WRITE8(map, 0x3d4, 0x36); old_36 = DRM_READ8(map, 0x3d5); DRM_WRITE8(map, 0x3d5, old_36 | 0x10); while (0 != ((--time_out) & 0xfff)) /* empty */ ; DRM_WRITE8(map, 0x3d5, old_36); DRM_WRITE8(map, 0x3d4, old_index); /* Restore dynamic gating */ DRM_WRITE8(map, 0x3cf, old_3cf); DRM_WRITE8(map, 0x3ce, old_3ce); break; } } DRM_WRITE8(map, 0xb057, 0); } bool xgi_ge_irq_handler(struct xgi_info * info) { const u32 int_status = le32_to_cpu(DRM_READ32(info->mmio_map, 0x2810)); bool is_support_auto_reset = false; /* Check GE on/off */ if (0 == (0xffffc0f0 & int_status)) { if (0 != (0x1000 & int_status)) { /* We got GE stall interrupt. */ DRM_WRITE32(info->mmio_map, 0x2810, cpu_to_le32(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(info->mmio_map)) { /* 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. */ DRM_INFO("Reset GE!\n"); xgi_ge_hang_reset(info->mmio_map); } } } } else if (0 != (0x1 & int_status)) { s_invalid_begin++; DRM_WRITE32(info->mmio_map, 0x2810, cpu_to_le32((int_status & ~0x01) | 0x04000000)); } return true; } return false; } bool xgi_crt_irq_handler(struct xgi_info * info) { bool ret = false; u8 save_3ce = DRM_READ8(info->mmio_map, 0x3ce); /* CRT1 interrupt just happened */ if (IN3CFB(info->mmio_map, 0x37) & 0x01) { u8 op3cf_3d; u8 op3cf_37; /* What happened? */ op3cf_37 = IN3CFB(info->mmio_map, 0x37); /* Clear CRT interrupt */ op3cf_3d = IN3CFB(info->mmio_map, 0x3d); OUT3CFB(info->mmio_map, 0x3d, (op3cf_3d | 0x04)); OUT3CFB(info->mmio_map, 0x3d, (op3cf_3d & ~0x04)); ret = true; } DRM_WRITE8(info->mmio_map, 0x3ce, save_3ce); return (ret); } bool xgi_dvi_irq_handler(struct xgi_info * info) { bool ret = false; const u8 save_3ce = DRM_READ8(info->mmio_map, 0x3ce); /* DVI interrupt just happened */ if (IN3CFB(info->mmio_map, 0x38) & 0x20) { const u8 save_3x4 = DRM_READ8(info->mmio_map, 0x3d4); u8 op3cf_39; u8 op3cf_37; u8 op3x5_5a; /* What happened? */ op3cf_37 = IN3CFB(info->mmio_map, 0x37); /* Notify BIOS that DVI plug/unplug happened */ op3x5_5a = IN3X5B(info->mmio_map, 0x5a); OUT3X5B(info->mmio_map, 0x5a, op3x5_5a & 0xf7); DRM_WRITE8(info->mmio_map, 0x3d4, save_3x4); /* Clear DVI interrupt */ op3cf_39 = IN3CFB(info->mmio_map, 0x39); OUT3C5B(info->mmio_map, 0x39, (op3cf_39 & ~0x01)); OUT3C5B(info->mmio_map, 0x39, (op3cf_39 | 0x01)); ret = true; } DRM_WRITE8(info->mmio_map, 0x3ce, save_3ce); return (ret); } static void dump_reg_header(unsigned regbase) { printk("\n=====xgi_dump_register========0x%x===============\n", regbase); printk(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); } static void dump_indexed_reg(struct xgi_info * info, unsigned regbase) { unsigned i, j; u8 temp; dump_reg_header(regbase); for (i = 0; i < 0x10; i++) { printk("%1x ", i); for (j = 0; j < 0x10; j++) { DRM_WRITE8(info->mmio_map, regbase - 1, (i * 0x10) + j); temp = DRM_READ8(info->mmio_map, regbase); printk("%3x", temp); } printk("\n"); } } static void dump_reg(struct xgi_info * info, unsigned regbase, unsigned range) { unsigned i, j; dump_reg_header(regbase); for (i = 0; i < range; i++) { printk("%1x ", i); for (j = 0; j < 0x10; j++) { u8 temp = DRM_READ8(info->mmio_map, regbase + (i * 0x10) + j); printk("%3x", temp); } printk("\n"); } } void xgi_dump_register(struct xgi_info * info) { dump_indexed_reg(info, 0x3c5); dump_indexed_reg(info, 0x3d5); dump_indexed_reg(info, 0x3cf); dump_reg(info, 0xB000, 0x05); dump_reg(info, 0x2200, 0x0B); dump_reg(info, 0x2300, 0x07); dump_reg(info, 0x2400, 0x10); dump_reg(info, 0x2800, 0x10); } #define WHOLD_GE_STATUS 0x2800 /* Test everything except the "whole GE busy" bit, the "master engine busy" * bit, and the reserved bits [26:21]. */ #define IDLE_MASK ~((1U<<31) | (1U<<28) | (0x3f<<21)) void xgi_waitfor_pci_idle(struct xgi_info * info) { unsigned int idleCount = 0; u32 old_status = 0; unsigned int same_count = 0; while (idleCount < 5) { const u32 status = DRM_READ32(info->mmio_map, WHOLD_GE_STATUS) & IDLE_MASK; if (status == old_status) { same_count++; if ((same_count % 100) == 0) { DRM_ERROR("GE status stuck at 0x%08x for %u iterations!\n", old_status, same_count); } } else { old_status = status; same_count = 0; } if (status != 0) { msleep(1); idleCount = 0; } else { idleCount++; } } } void xgi_enable_mmio(struct xgi_info * info) { u8 protect = 0; u8 temp; /* Unprotect registers */ DRM_WRITE8(info->mmio_map, 0x3C4, 0x11); protect = DRM_READ8(info->mmio_map, 0x3C5); DRM_WRITE8(info->mmio_map, 0x3C5, 0x92); DRM_WRITE8(info->mmio_map, 0x3D4, 0x3A); temp = DRM_READ8(info->mmio_map, 0x3D5); DRM_WRITE8(info->mmio_map, 0x3D5, temp | 0x20); /* Enable MMIO */ DRM_WRITE8(info->mmio_map, 0x3D4, 0x39); temp = DRM_READ8(info->mmio_map, 0x3D5); DRM_WRITE8(info->mmio_map, 0x3D5, temp | 0x01); /* Protect registers */ OUT3C5B(info->mmio_map, 0x11, protect); } void xgi_disable_mmio(struct xgi_info * info) { u8 protect = 0; u8 temp; /* Unprotect registers */ DRM_WRITE8(info->mmio_map, 0x3C4, 0x11); protect = DRM_READ8(info->mmio_map, 0x3C5); DRM_WRITE8(info->mmio_map, 0x3C5, 0x92); /* Disable MMIO access */ DRM_WRITE8(info->mmio_map, 0x3D4, 0x39); temp = DRM_READ8(info->mmio_map, 0x3D5); DRM_WRITE8(info->mmio_map, 0x3D5, temp & 0xFE); /* Protect registers */ OUT3C5B(info->mmio_map, 0x11, protect); } void xgi_enable_ge(struct xgi_info * info) { u8 bOld3cf2a; int wait = 0; OUT3C5B(info->mmio_map, 0x11, 0x92); /* Save and close dynamic gating */ bOld3cf2a = IN3CFB(info->mmio_map, XGI_MISC_CTRL); OUT3CFB(info->mmio_map, XGI_MISC_CTRL, bOld3cf2a & ~EN_GEPWM); /* Enable 2D and 3D GE */ OUT3X5B(info->mmio_map, XGI_GE_CNTL, (GE_ENABLE | GE_ENABLE_3D)); wait = 10; while (wait--) { DRM_READ8(info->mmio_map, 0x36); } /* Reset both 3D and 2D engine */ OUT3X5B(info->mmio_map, XGI_GE_CNTL, (GE_ENABLE | GE_RESET | GE_ENABLE_3D)); wait = 10; while (wait--) { DRM_READ8(info->mmio_map, 0x36); } OUT3X5B(info->mmio_map, XGI_GE_CNTL, (GE_ENABLE | GE_ENABLE_3D)); wait = 10; while (wait--) { DRM_READ8(info->mmio_map, 0x36); } /* Enable 2D engine only */ OUT3X5B(info->mmio_map, XGI_GE_CNTL, GE_ENABLE); /* Enable 2D+3D engine */ OUT3X5B(info->mmio_map, XGI_GE_CNTL, (GE_ENABLE | GE_ENABLE_3D)); /* Restore dynamic gating */ OUT3CFB(info->mmio_map, XGI_MISC_CTRL, bOld3cf2a); } void xgi_disable_ge(struct xgi_info * info) { int wait = 0; OUT3X5B(info->mmio_map, XGI_GE_CNTL, (GE_ENABLE | GE_ENABLE_3D)); wait = 10; while (wait--) { DRM_READ8(info->mmio_map, 0x36); } /* Reset both 3D and 2D engine */ OUT3X5B(info->mmio_map, XGI_GE_CNTL, (GE_ENABLE | GE_RESET | GE_ENABLE_3D)); wait = 10; while (wait--) { DRM_READ8(info->mmio_map, 0x36); } OUT3X5B(info->mmio_map, XGI_GE_CNTL, (GE_ENABLE | GE_ENABLE_3D)); wait = 10; while (wait--) { DRM_READ8(info->mmio_map, 0x36); } /* Disable 2D engine and 3D engine. */ OUT3X5B(info->mmio_map, XGI_GE_CNTL, 0); }