| /* |
| * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 |
| * Copyright (c) 2008-2009 QUALCOMM USA, INC. |
| * |
| * All source code in this file is licensed under the following license |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can find it at http://www.fsf.org |
| */ |
| #include <linux/delay.h> |
| #include <linux/uaccess.h> |
| #include <linux/io.h> |
| #include <linux/irq.h> |
| #include <linux/sched.h> |
| #include <linux/wait.h> |
| |
| #include "kgsl.h" |
| #include "kgsl_log.h" |
| #include "kgsl_pm4types.h" |
| #include "kgsl_cmdstream.h" |
| |
| #include "yamato_reg.h" |
| |
| #define GSL_RBBM_INT_MASK \ |
| (RBBM_INT_CNTL__RDERR_INT_MASK | \ |
| RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) |
| |
| #define GSL_SQ_INT_MASK \ |
| (SQ_INT_CNTL__PS_WATCHDOG_MASK | \ |
| SQ_INT_CNTL__VS_WATCHDOG_MASK) |
| |
| /* Yamato MH arbiter config*/ |
| #define KGSL_CFG_YAMATO_MHARB \ |
| (0x10 \ |
| | (0 << MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT) \ |
| | (0 << MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT) \ |
| | (0 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT) \ |
| | (0x8 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT) \ |
| | (1 << MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT)) |
| |
| void kgsl_register_dump(struct kgsl_device *device) |
| { |
| unsigned int regValue; |
| |
| kgsl_yamato_regread(device, REG_RBBM_STATUS, ®Value); |
| KGSL_CMD_ERR("RBBM_STATUS = %8.8X\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_RB_BASE, ®Value); |
| KGSL_CMD_ERR("CP_RB_BASE = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_RB_CNTL, ®Value); |
| KGSL_CMD_ERR("CP_RB_CNTL = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_RB_RPTR_ADDR, ®Value); |
| KGSL_CMD_ERR("CP_RB_RPTR_ADDR = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_RB_RPTR, ®Value); |
| KGSL_CMD_ERR("CP_RB_RPTR = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_RB_WPTR, ®Value); |
| KGSL_CMD_ERR("CP_RB_WPTR = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_RB_RPTR_WR, ®Value); |
| KGSL_CMD_ERR("CP_RB_RPTR_WR = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_INT_CNTL, ®Value); |
| KGSL_CMD_ERR("CP_INT_CNTL = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_INT_STATUS, ®Value); |
| KGSL_CMD_ERR("CP_INT_STATUS = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_ME_CNTL, ®Value); |
| KGSL_CMD_ERR("CP_ME_CNTL = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_ME_STATUS, ®Value); |
| KGSL_CMD_ERR("CP_ME_STATUS = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, ®Value); |
| KGSL_CMD_ERR("RBBM_PM_OVERRIDE1 = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE2, ®Value); |
| KGSL_CMD_ERR("RBBM_PM_OVERRIDE2 = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_RBBM_INT_CNTL, ®Value); |
| KGSL_CMD_ERR("RBBM_INT_CNTL = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_RBBM_INT_STATUS, ®Value); |
| KGSL_CMD_ERR("RBBM_INT_STATUS = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_MASTER_INT_SIGNAL, ®Value); |
| KGSL_CMD_ERR("MASTER_INT_SIGNAL = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_IB1_BASE, ®Value); |
| KGSL_CMD_ERR("CP_IB1_BASE = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_IB1_BUFSZ, ®Value); |
| KGSL_CMD_ERR("CP_IB1_BUFSZ = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_IB2_BASE, ®Value); |
| KGSL_CMD_ERR("CP_IB2_BASE = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_IB2_BUFSZ, ®Value); |
| KGSL_CMD_ERR("CP_IB2_BUFSZ = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_CP_STAT, ®Value); |
| KGSL_CMD_ERR("CP_STAT = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_SCRATCH_REG0, ®Value); |
| KGSL_CMD_ERR("SCRATCH_REG0 = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_COHER_SIZE_PM4, ®Value); |
| KGSL_CMD_ERR("COHER_SIZE_PM4 = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_COHER_BASE_PM4, ®Value); |
| KGSL_CMD_ERR("COHER_BASE_PM4 = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_COHER_STATUS_PM4, ®Value); |
| KGSL_CMD_ERR("COHER_STATUS_PM4 = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_RBBM_READ_ERROR, ®Value); |
| KGSL_CMD_ERR("RBBM_READ_ERROR = %08x\n", regValue); |
| kgsl_yamato_regread(device, REG_MH_AXI_ERROR, ®Value); |
| KGSL_CMD_ERR("MH_AXI_ERROR = %08x\n", regValue); |
| } |
| |
| static int kgsl_yamato_gmeminit(struct kgsl_device *device) |
| { |
| union reg_rb_edram_info rb_edram_info; |
| unsigned int gmem_size; |
| unsigned int edram_value = 0; |
| |
| /* make sure edram range is aligned to size */ |
| BUG_ON(device->gmemspace.gpu_base & (device->gmemspace.sizebytes - 1)); |
| |
| /* get edram_size value equivalent */ |
| gmem_size = (device->gmemspace.sizebytes >> 14); |
| while (gmem_size >>= 1) |
| edram_value++; |
| |
| rb_edram_info.val = 0; |
| |
| rb_edram_info.f.edram_size = edram_value; |
| rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */ |
| /* must be aligned to size */ |
| rb_edram_info.f.edram_range = (device->gmemspace.gpu_base >> 14); |
| |
| kgsl_yamato_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val); |
| |
| return 0; |
| } |
| |
| static int kgsl_yamato_gmemclose(struct kgsl_device *device) |
| { |
| kgsl_yamato_regwrite(device, REG_RB_EDRAM_INFO, 0x00000000); |
| |
| return 0; |
| } |
| |
| void kgsl_yamato_rbbm_intrcallback(struct kgsl_device *device) |
| { |
| unsigned int status = 0; |
| unsigned int rderr = 0; |
| |
| KGSL_DRV_VDBG("enter (device=%p)\n", device); |
| |
| kgsl_yamato_regread(device, REG_RBBM_INT_STATUS, &status); |
| |
| if (status & RBBM_INT_CNTL__RDERR_INT_MASK) { |
| kgsl_yamato_regread(device, REG_RBBM_READ_ERROR, &rderr); |
| KGSL_DRV_FATAL("rbbm read error interrupt: %08x\n", rderr); |
| } else if (status & RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) { |
| KGSL_DRV_DBG("rbbm display update interrupt\n"); |
| } else if (status & RBBM_INT_CNTL__GUI_IDLE_INT_MASK) { |
| KGSL_DRV_DBG("rbbm gui idle interrupt\n"); |
| } else { |
| KGSL_CMD_DBG("bad bits in REG_CP_INT_STATUS %08x\n", status); |
| } |
| |
| status &= GSL_RBBM_INT_MASK; |
| kgsl_yamato_regwrite(device, REG_RBBM_INT_ACK, status); |
| |
| KGSL_DRV_VDBG("return\n"); |
| } |
| |
| void kgsl_yamato_sq_intrcallback(struct kgsl_device *device) |
| { |
| unsigned int status = 0; |
| |
| KGSL_DRV_VDBG("enter (device=%p)\n", device); |
| |
| kgsl_yamato_regread(device, REG_SQ_INT_STATUS, &status); |
| |
| if (status & SQ_INT_CNTL__PS_WATCHDOG_MASK) |
| KGSL_DRV_DBG("sq ps watchdog interrupt\n"); |
| else if (status & SQ_INT_CNTL__VS_WATCHDOG_MASK) |
| KGSL_DRV_DBG("sq vs watchdog interrupt\n"); |
| else |
| KGSL_DRV_DBG("bad bits in REG_SQ_INT_STATUS %08x\n", status); |
| |
| |
| status &= GSL_SQ_INT_MASK; |
| kgsl_yamato_regwrite(device, REG_SQ_INT_ACK, status); |
| |
| KGSL_DRV_VDBG("return\n"); |
| } |
| |
| irqreturn_t kgsl_yamato_isr(int irq, void *data) |
| { |
| irqreturn_t result = IRQ_NONE; |
| |
| struct kgsl_device *device = &kgsl_driver.yamato_device; |
| unsigned int status; |
| |
| kgsl_yamato_regread(device, REG_MASTER_INT_SIGNAL, &status); |
| |
| if (status & MASTER_INT_SIGNAL__MH_INT_STAT) { |
| kgsl_mh_intrcallback(device); |
| result = IRQ_HANDLED; |
| } |
| |
| if (status & MASTER_INT_SIGNAL__CP_INT_STAT) { |
| kgsl_cp_intrcallback(device); |
| result = IRQ_HANDLED; |
| } |
| |
| if (status & MASTER_INT_SIGNAL__RBBM_INT_STAT) { |
| kgsl_yamato_rbbm_intrcallback(device); |
| result = IRQ_HANDLED; |
| } |
| |
| if (status & MASTER_INT_SIGNAL__SQ_INT_STAT) { |
| kgsl_yamato_sq_intrcallback(device); |
| result = IRQ_HANDLED; |
| } |
| |
| |
| return result; |
| } |
| |
| int kgsl_yamato_cleanup_pt(struct kgsl_device *device, |
| struct kgsl_pagetable *pagetable) |
| { |
| kgsl_mmu_unmap(pagetable, device->ringbuffer.buffer_desc.gpuaddr, |
| device->ringbuffer.buffer_desc.size); |
| |
| kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr, |
| device->ringbuffer.memptrs_desc.size); |
| |
| kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr, |
| device->memstore.size); |
| |
| kgsl_mmu_unmap(pagetable, device->mmu.dummyspace.gpuaddr, |
| device->mmu.dummyspace.size); |
| |
| return 0; |
| } |
| |
| int kgsl_yamato_setup_pt(struct kgsl_device *device, |
| struct kgsl_pagetable *pagetable) |
| { |
| int result = 0; |
| unsigned int gpuaddr; |
| |
| BUG_ON(device->ringbuffer.buffer_desc.physaddr == 0); |
| BUG_ON(device->ringbuffer.memptrs_desc.physaddr == 0); |
| BUG_ON(device->memstore.physaddr == 0); |
| BUG_ON(device->mmu.dummyspace.physaddr == 0); |
| |
| result = kgsl_mmu_map(pagetable, |
| device->ringbuffer.buffer_desc.physaddr, |
| device->ringbuffer.buffer_desc.size, |
| GSL_PT_PAGE_RV, &gpuaddr, |
| KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); |
| |
| if (result) |
| goto error; |
| |
| if (device->ringbuffer.buffer_desc.gpuaddr == 0) |
| device->ringbuffer.buffer_desc.gpuaddr = gpuaddr; |
| BUG_ON(device->ringbuffer.buffer_desc.gpuaddr != gpuaddr); |
| |
| result = kgsl_mmu_map(pagetable, |
| device->ringbuffer.memptrs_desc.physaddr, |
| device->ringbuffer.memptrs_desc.size, |
| GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, |
| KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); |
| if (result) |
| goto unmap_buffer_desc; |
| |
| if (device->ringbuffer.memptrs_desc.gpuaddr == 0) |
| device->ringbuffer.memptrs_desc.gpuaddr = gpuaddr; |
| BUG_ON(device->ringbuffer.memptrs_desc.gpuaddr != gpuaddr); |
| |
| result = kgsl_mmu_map(pagetable, device->memstore.physaddr, |
| device->memstore.size, |
| GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, |
| KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); |
| if (result) |
| goto unmap_memptrs_desc; |
| |
| if (device->memstore.gpuaddr == 0) |
| device->memstore.gpuaddr = gpuaddr; |
| BUG_ON(device->memstore.gpuaddr != gpuaddr); |
| |
| result = kgsl_mmu_map(pagetable, |
| device->mmu.dummyspace.physaddr, |
| device->mmu.dummyspace.size, |
| GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, |
| KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); |
| |
| if (result) |
| goto unmap_memstore_desc; |
| |
| if (device->mmu.dummyspace.gpuaddr == 0) |
| device->mmu.dummyspace.gpuaddr = gpuaddr; |
| BUG_ON(device->mmu.dummyspace.gpuaddr != gpuaddr); |
| |
| return result; |
| |
| unmap_memstore_desc: |
| kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr, |
| device->memstore.size); |
| |
| unmap_memptrs_desc: |
| kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr, |
| device->ringbuffer.memptrs_desc.size); |
| unmap_buffer_desc: |
| kgsl_mmu_unmap(pagetable, device->ringbuffer.buffer_desc.gpuaddr, |
| device->ringbuffer.buffer_desc.size); |
| error: |
| return result; |
| |
| } |
| |
| #ifdef CONFIG_MSM_KGSL_MMU |
| int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) |
| { |
| unsigned int link[32]; |
| unsigned int *cmds = &link[0]; |
| int sizedwords = 0; |
| unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ |
| |
| KGSL_MEM_DBG("device %p ctxt %p pt %p\n", |
| device, |
| device->drawctxt_active, |
| device->mmu.hwpagetable); |
| /* if possible, set via command stream, |
| * otherwise set via direct register writes |
| */ |
| if (device->drawctxt_active) { |
| KGSL_MEM_DBG("cmds\n"); |
| if (flags & KGSL_MMUFLAGS_PTUPDATE) { |
| /* wait for graphics pipe to be idle */ |
| *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); |
| *cmds++ = 0x00000000; |
| |
| /* set page table base */ |
| *cmds++ = pm4_type0_packet(REG_MH_MMU_PT_BASE, 1); |
| *cmds++ = device->mmu.hwpagetable->base.gpuaddr; |
| sizedwords += 4; |
| } |
| |
| if (flags & KGSL_MMUFLAGS_TLBFLUSH) { |
| *cmds++ = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); |
| *cmds++ = mh_mmu_invalidate; |
| sizedwords += 2; |
| } |
| |
| if (flags & KGSL_MMUFLAGS_PTUPDATE) { |
| /* HW workaround: to resolve MMU page fault interrupts |
| * caused by the VGT.It prevents the CP PFP from filling |
| * the VGT DMA request fifo too early,thereby ensuring |
| * that the VGT will not fetch vertex/bin data until |
| * after the page table base register has been updated. |
| * |
| * Two null DRAW_INDX_BIN packets are inserted right |
| * after the page table base update, followed by a |
| * wait for idle. The null packets will fill up the |
| * VGT DMA request fifo and prevent any further |
| * vertex/bin updates from occurring until the wait |
| * has finished. */ |
| *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); |
| *cmds++ = (0x4 << 16) | |
| (REG_PA_SU_SC_MODE_CNTL - 0x2000); |
| *cmds++ = 0; /* disable faceness generation */ |
| *cmds++ = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); |
| *cmds++ = device->mmu.dummyspace.gpuaddr; |
| *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); |
| *cmds++ = 0; /* viz query info */ |
| *cmds++ = 0x0003C004; /* draw indicator */ |
| *cmds++ = 0; /* bin base */ |
| *cmds++ = 3; /* bin size */ |
| *cmds++ = device->mmu.dummyspace.gpuaddr; /* dma base */ |
| *cmds++ = 6; /* dma size */ |
| *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); |
| *cmds++ = 0; /* viz query info */ |
| *cmds++ = 0x0003C004; /* draw indicator */ |
| *cmds++ = 0; /* bin base */ |
| *cmds++ = 3; /* bin size */ |
| /* dma base */ |
| *cmds++ = device->mmu.dummyspace.gpuaddr; |
| *cmds++ = 6; /* dma size */ |
| *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); |
| *cmds++ = 0x00000000; |
| sizedwords += 21; |
| } |
| |
| if (flags & (KGSL_MMUFLAGS_PTUPDATE | KGSL_MMUFLAGS_TLBFLUSH)) { |
| *cmds++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); |
| *cmds++ = 0x7fff; /* invalidate all base pointers */ |
| sizedwords += 2; |
| } |
| |
| kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, |
| &link[0], sizedwords); |
| } else { |
| KGSL_MEM_DBG("regs\n"); |
| |
| if (flags & KGSL_MMUFLAGS_PTUPDATE) { |
| kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); |
| kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, |
| device->mmu.hwpagetable->base.gpuaddr); |
| } |
| |
| if (flags & KGSL_MMUFLAGS_TLBFLUSH) { |
| kgsl_yamato_regwrite(device, REG_MH_MMU_INVALIDATE, |
| mh_mmu_invalidate); |
| } |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static unsigned int |
| kgsl_yamato_getchipid(struct kgsl_device *device) |
| { |
| unsigned int chipid; |
| unsigned int coreid, majorid, minorid, patchid, revid; |
| |
| /* YDX */ |
| kgsl_yamato_regread(device, REG_RBBM_PERIPHID1, &coreid); |
| coreid &= 0xF; |
| |
| kgsl_yamato_regread(device, REG_RBBM_PERIPHID2, &majorid); |
| majorid = (majorid >> 4) & 0xF; |
| |
| kgsl_yamato_regread(device, REG_RBBM_PATCH_RELEASE, &revid); |
| /* this is a 16bit field, but extremely unlikely it would ever get |
| * this high |
| */ |
| minorid = ((revid >> 0) & 0xFF); |
| |
| |
| patchid = ((revid >> 16) & 0xFF); |
| |
| chipid = ((coreid << 24) | (majorid << 16) | |
| (minorid << 8) | (patchid << 0)); |
| |
| /* Hardware revision 211 (8650) returns the wrong chip ID */ |
| if (chipid == KGSL_CHIPID_YAMATODX_REV21) |
| chipid = KGSL_CHIPID_YAMATODX_REV211; |
| |
| return chipid; |
| } |
| |
| int kgsl_yamato_init(struct kgsl_device *device, struct kgsl_devconfig *config) |
| { |
| int status = -EINVAL; |
| int init_reftimestamp = 0x7fffffff; |
| struct kgsl_memregion *regspace = &device->regspace; |
| unsigned int memflags = KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS; |
| |
| KGSL_DRV_VDBG("enter (device=%p, config=%p)\n", device, config); |
| |
| if (device->flags & KGSL_FLAGS_INITIALIZED) { |
| KGSL_DRV_VDBG("return %d\n", 0); |
| return 0; |
| } |
| memset(device, 0, sizeof(*device)); |
| |
| init_waitqueue_head(&device->ib1_wq); |
| |
| memcpy(regspace, &config->regspace, sizeof(device->regspace)); |
| if (regspace->mmio_phys_base == 0 || regspace->sizebytes == 0) { |
| KGSL_DRV_ERR("dev %d invalid regspace\n", device->id); |
| goto error; |
| } |
| if (!request_mem_region(regspace->mmio_phys_base, |
| regspace->sizebytes, DRIVER_NAME)) { |
| KGSL_DRV_ERR("request_mem_region failed for register memory\n"); |
| status = -ENODEV; |
| goto error; |
| } |
| |
| regspace->mmio_virt_base = ioremap(regspace->mmio_phys_base, |
| regspace->sizebytes); |
| KGSL_MEM_INFO("ioremap(regs) = %p\n", regspace->mmio_virt_base); |
| if (regspace->mmio_virt_base == NULL) { |
| KGSL_DRV_ERR("ioremap failed for register memory\n"); |
| status = -ENODEV; |
| goto error_release_mem; |
| } |
| |
| KGSL_DRV_INFO("dev %d regs phys 0x%08x size 0x%08x virt %p\n", |
| device->id, regspace->mmio_phys_base, |
| regspace->sizebytes, regspace->mmio_virt_base); |
| |
| memcpy(&device->gmemspace, &config->gmemspace, |
| sizeof(device->gmemspace)); |
| |
| device->id = KGSL_DEVICE_YAMATO; |
| |
| if (config->mmu_config) { |
| device->mmu.config = config->mmu_config; |
| device->mmu.mpu_base = config->mpu_base; |
| device->mmu.mpu_range = config->mpu_range; |
| device->mmu.va_base = config->va_base; |
| device->mmu.va_range = config->va_range; |
| } |
| |
| device->chip_id = kgsl_yamato_getchipid(device); |
| |
| /*We need to make sure all blocks are powered up and clocked before |
| *issuing a soft reset. The overrides will be turned off (set to 0) |
| *later in kgsl_yamato_start. |
| */ |
| kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe); |
| kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff); |
| |
| kgsl_yamato_regwrite(device, REG_RBBM_SOFT_RESET, 0xFFFFFFFF); |
| msleep(50); |
| kgsl_yamato_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000); |
| |
| kgsl_yamato_regwrite(device, REG_RBBM_CNTL, 0x00004442); |
| |
| kgsl_yamato_regwrite(device, REG_MH_ARBITER_CONFIG, |
| KGSL_CFG_YAMATO_MHARB); |
| |
| kgsl_yamato_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000); |
| kgsl_yamato_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000); |
| |
| |
| status = kgsl_mmu_init(device); |
| if (status != 0) { |
| status = -ENODEV; |
| goto error_iounmap; |
| } |
| |
| status = kgsl_cmdstream_init(device); |
| if (status != 0) { |
| status = -ENODEV; |
| goto error_close_mmu; |
| } |
| |
| status = kgsl_sharedmem_alloc(memflags, sizeof(device->memstore), |
| &device->memstore); |
| if (status != 0) { |
| status = -ENODEV; |
| goto error_close_cmdstream; |
| } |
| kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); |
| |
| kgsl_sharedmem_write(&device->memstore, |
| KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), |
| &init_reftimestamp, 4); |
| |
| kgsl_yamato_regwrite(device, REG_RBBM_DEBUG, 0x00080000); |
| pr_info("msm_kgsl: initilized dev=%d mmu=%s\n", device->id, |
| kgsl_mmu_isenabled(&device->mmu) ? "on" : "off"); |
| |
| device->flags |= KGSL_FLAGS_INITIALIZED; |
| return 0; |
| |
| error_close_cmdstream: |
| kgsl_cmdstream_close(device); |
| error_close_mmu: |
| kgsl_mmu_close(device); |
| error_iounmap: |
| iounmap(regspace->mmio_virt_base); |
| regspace->mmio_virt_base = NULL; |
| error_release_mem: |
| release_mem_region(regspace->mmio_phys_base, regspace->sizebytes); |
| error: |
| return status; |
| } |
| |
| int kgsl_yamato_close(struct kgsl_device *device) |
| { |
| struct kgsl_memregion *regspace = &device->regspace; |
| |
| if (device->memstore.hostptr) |
| kgsl_sharedmem_free(&device->memstore); |
| |
| kgsl_mmu_close(device); |
| |
| kgsl_cmdstream_close(device); |
| |
| if (regspace->mmio_virt_base != NULL) { |
| KGSL_MEM_INFO("iounmap(regs) = %p\n", regspace->mmio_virt_base); |
| iounmap(regspace->mmio_virt_base); |
| regspace->mmio_virt_base = NULL; |
| release_mem_region(regspace->mmio_phys_base, |
| regspace->sizebytes); |
| } |
| |
| KGSL_DRV_VDBG("return %d\n", 0); |
| device->flags &= ~KGSL_FLAGS_INITIALIZED; |
| return 0; |
| } |
| |
| int kgsl_yamato_start(struct kgsl_device *device, uint32_t flags) |
| { |
| int status = -EINVAL; |
| |
| KGSL_DRV_VDBG("enter (device=%p)\n", device); |
| |
| if (!(device->flags & KGSL_FLAGS_INITIALIZED)) { |
| KGSL_DRV_ERR("Trying to start uninitialized device.\n"); |
| return -EINVAL; |
| } |
| |
| device->refcnt++; |
| |
| if (device->flags & KGSL_FLAGS_STARTED) { |
| KGSL_DRV_VDBG("already started"); |
| return 0; |
| } |
| |
| kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0); |
| kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0); |
| |
| KGSL_DRV_DBG("enabling RBBM interrupts mask 0x%08lx\n", |
| GSL_RBBM_INT_MASK); |
| kgsl_yamato_regwrite(device, REG_RBBM_INT_CNTL, GSL_RBBM_INT_MASK); |
| |
| /* make sure SQ interrupts are disabled */ |
| kgsl_yamato_regwrite(device, REG_SQ_INT_CNTL, 0); |
| |
| kgsl_yamato_gmeminit(device); |
| |
| status = kgsl_ringbuffer_init(device); |
| if (status != 0) { |
| kgsl_yamato_stop(device); |
| return status; |
| } |
| |
| status = kgsl_drawctxt_init(device); |
| if (status != 0) { |
| kgsl_yamato_stop(device); |
| return status; |
| } |
| |
| device->flags |= KGSL_FLAGS_STARTED; |
| |
| KGSL_DRV_VDBG("return %d\n", status); |
| return status; |
| } |
| |
| int kgsl_yamato_stop(struct kgsl_device *device) |
| { |
| if (device->flags & KGSL_FLAGS_STARTED) { |
| |
| kgsl_yamato_regwrite(device, REG_RBBM_INT_CNTL, 0); |
| |
| kgsl_yamato_regwrite(device, REG_SQ_INT_CNTL, 0); |
| |
| kgsl_drawctxt_close(device); |
| |
| kgsl_ringbuffer_close(&device->ringbuffer); |
| |
| kgsl_yamato_gmemclose(device); |
| |
| device->flags &= ~KGSL_FLAGS_STARTED; |
| } |
| |
| return 0; |
| } |
| |
| int kgsl_yamato_getproperty(struct kgsl_device *device, |
| enum kgsl_property_type type, |
| void *value, |
| unsigned int sizebytes) |
| { |
| int status = -EINVAL; |
| |
| switch (type) { |
| case KGSL_PROP_DEVICE_INFO: |
| { |
| struct kgsl_devinfo devinfo; |
| |
| if (sizebytes != sizeof(devinfo)) { |
| status = -EINVAL; |
| break; |
| } |
| |
| memset(&devinfo, 0, sizeof(devinfo)); |
| devinfo.device_id = device->id; |
| devinfo.chip_id = device->chip_id; |
| devinfo.mmu_enabled = kgsl_mmu_isenabled(&device->mmu); |
| devinfo.gmem_hostbaseaddr = |
| (unsigned int)device->gmemspace.mmio_virt_base; |
| devinfo.gmem_gpubaseaddr = device->gmemspace.gpu_base; |
| devinfo.gmem_sizebytes = device->gmemspace.sizebytes; |
| |
| if (copy_to_user(value, &devinfo, sizeof(devinfo)) != |
| 0) { |
| status = -EFAULT; |
| break; |
| } |
| status = 0; |
| } |
| break; |
| case KGSL_PROP_DEVICE_SHADOW: |
| { |
| struct kgsl_shadowprop shadowprop; |
| |
| if (sizebytes != sizeof(shadowprop)) { |
| status = -EINVAL; |
| break; |
| } |
| memset(&shadowprop, 0, sizeof(shadowprop)); |
| if (device->memstore.hostptr) { |
| /*NOTE: with mmu enabled, gpuaddr doesn't mean |
| * anything to mmap(). |
| */ |
| shadowprop.gpuaddr = device->memstore.physaddr; |
| shadowprop.size = device->memstore.size; |
| shadowprop.flags = KGSL_FLAGS_INITIALIZED; |
| } |
| if (copy_to_user(value, &shadowprop, |
| sizeof(shadowprop))) { |
| status = -EFAULT; |
| break; |
| } |
| status = 0; |
| } |
| break; |
| case KGSL_PROP_MMU_ENABLE: |
| { |
| #ifdef CONFIG_MSM_KGSL_MMU |
| int mmuProp = 1; |
| #else |
| int mmuProp = 0; |
| #endif |
| if (sizebytes != sizeof(int)) { |
| status = -EINVAL; |
| break; |
| } |
| if (copy_to_user(value, &mmuProp, sizeof(mmuProp))) { |
| status = -EFAULT; |
| break; |
| } |
| status = 0; |
| } |
| break; |
| case KGSL_PROP_INTERRUPT_WAITS: |
| { |
| int int_waits = 1; |
| if (sizebytes != sizeof(int)) { |
| status = -EINVAL; |
| break; |
| } |
| if (copy_to_user(value, &int_waits, sizeof(int))) { |
| status = -EFAULT; |
| break; |
| } |
| status = 0; |
| } |
| break; |
| default: |
| status = -EINVAL; |
| } |
| |
| return status; |
| } |
| |
| /* Note: This is either called from the standby timer, or while holding the |
| * driver mutex. |
| * |
| * The reader may obseve that this function may be called without holding the |
| * driver mutex (in the timer), which can cause the ringbuffer write pointer |
| * to change, when a user submits a command. However, the user must be holding |
| * the driver mutex when doing so, and then must |
| * have canceled the timer. If the timer was executing at the time of |
| * cancellation, the active flag would have been cleared, which the user |
| * ioctl checks for after cancelling the timer. |
| */ |
| bool kgsl_yamato_is_idle(struct kgsl_device *device) |
| { |
| struct kgsl_ringbuffer *rb = &device->ringbuffer; |
| unsigned int rbbm_status; |
| |
| BUG_ON(!(rb->flags & KGSL_FLAGS_STARTED)); |
| |
| GSL_RB_GET_READPTR(rb, &rb->rptr); |
| if (rb->rptr == rb->wptr) { |
| kgsl_yamato_regread(device, REG_RBBM_STATUS, &rbbm_status); |
| if (rbbm_status == 0x110) |
| return true; |
| } |
| return false; |
| } |
| |
| int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout) |
| { |
| int status = -EINVAL; |
| struct kgsl_ringbuffer *rb = &device->ringbuffer; |
| struct kgsl_mmu_debug mmu_dbg; |
| unsigned int rbbm_status; |
| int idle_count = 0; |
| #define IDLE_COUNT_MAX 1000000 |
| |
| KGSL_DRV_VDBG("enter (device=%p, timeout=%d)\n", device, timeout); |
| |
| (void)timeout; |
| |
| /* first, wait until the CP has consumed all the commands in |
| * the ring buffer |
| */ |
| if (rb->flags & KGSL_FLAGS_STARTED) { |
| do { |
| idle_count++; |
| GSL_RB_GET_READPTR(rb, &rb->rptr); |
| |
| } while (rb->rptr != rb->wptr && idle_count < IDLE_COUNT_MAX); |
| if (idle_count == IDLE_COUNT_MAX) |
| goto err; |
| } |
| /* now, wait for the GPU to finish its operations */ |
| for (idle_count = 0; idle_count < IDLE_COUNT_MAX; idle_count++) { |
| kgsl_yamato_regread(device, REG_RBBM_STATUS, &rbbm_status); |
| |
| if (rbbm_status == 0x110) { |
| status = 0; |
| goto done; |
| } |
| } |
| |
| err: |
| KGSL_DRV_ERR("spun too long waiting for RB to idle\n"); |
| kgsl_register_dump(device); |
| kgsl_ringbuffer_dump(rb); |
| kgsl_mmu_debug(&device->mmu, &mmu_dbg); |
| BUG(); |
| |
| done: |
| KGSL_DRV_VDBG("return %d\n", status); |
| |
| return status; |
| } |
| |
| int kgsl_yamato_regread(struct kgsl_device *device, unsigned int offsetwords, |
| unsigned int *value) |
| { |
| unsigned int *reg; |
| |
| if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) { |
| KGSL_DRV_ERR("invalid offset %d\n", offsetwords); |
| return -ERANGE; |
| } |
| |
| reg = (unsigned int *)(device->regspace.mmio_virt_base |
| + (offsetwords << 2)); |
| *value = readl(reg); |
| |
| return 0; |
| } |
| |
| int kgsl_yamato_regwrite(struct kgsl_device *device, unsigned int offsetwords, |
| unsigned int value) |
| { |
| unsigned int *reg; |
| |
| if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) { |
| KGSL_DRV_ERR("invalid offset %d\n", offsetwords); |
| return -ERANGE; |
| } |
| |
| reg = (unsigned int *)(device->regspace.mmio_virt_base |
| + (offsetwords << 2)); |
| writel(value, reg); |
| |
| return 0; |
| } |
| |
| static inline int _wait_timestamp(struct kgsl_device *device, |
| unsigned int timestamp, |
| unsigned int msecs) |
| { |
| long status; |
| |
| status = wait_event_interruptible_timeout(device->ib1_wq, |
| kgsl_cmdstream_check_timestamp(device, timestamp), |
| msecs_to_jiffies(msecs)); |
| |
| if (status > 0) |
| status = 0; |
| else if (status == 0) { |
| if (!kgsl_cmdstream_check_timestamp(device, timestamp)) { |
| status = -ETIMEDOUT; |
| kgsl_register_dump(device); |
| } |
| } |
| |
| return (int)status; |
| } |
| |
| /* MUST be called with the kgsl_driver.mutex held */ |
| int kgsl_yamato_waittimestamp(struct kgsl_device *device, |
| unsigned int timestamp, |
| unsigned int msecs) |
| { |
| long status = 0; |
| uint32_t ref_ts; |
| int enableflag = 1; |
| unsigned int cmd[2]; |
| |
| KGSL_DRV_INFO("enter (device=%p,timestamp=%d,timeout=0x%08x)\n", |
| device, timestamp, msecs); |
| |
| if (!kgsl_cmdstream_check_timestamp(device, timestamp)) { |
| kgsl_sharedmem_read(&device->memstore, &ref_ts, |
| KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), 4); |
| if (timestamp_cmp(ref_ts, timestamp)) { |
| kgsl_sharedmem_write(&device->memstore, |
| KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), |
| ×tamp, 4); |
| } |
| |
| cmd[0] = pm4_type3_packet(PM4_INTERRUPT, 1); |
| cmd[1] = CP_INT_CNTL__IB1_INT_MASK; |
| kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NO_TS_CMP, |
| cmd, 2); |
| kgsl_sharedmem_write(&device->memstore, |
| KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), |
| &enableflag, 4); |
| |
| mutex_unlock(&kgsl_driver.mutex); |
| status = _wait_timestamp(device, timestamp, msecs); |
| mutex_lock(&kgsl_driver.mutex); |
| } |
| |
| KGSL_DRV_INFO("return %ld\n", status); |
| return (int)status; |
| } |
| |
| int kgsl_yamato_runpending(struct kgsl_device *device) |
| { |
| if (device->flags & KGSL_FLAGS_INITIALIZED) |
| kgsl_cmdstream_memqueue_drain(device); |
| return 0; |
| } |
| |
| int __init kgsl_yamato_config(struct kgsl_devconfig *devconfig, |
| struct platform_device *pdev) |
| { |
| int result = 0; |
| struct resource *res = NULL; |
| |
| memset(devconfig, 0, sizeof(*devconfig)); |
| |
| /*find memory regions */ |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
| "kgsl_reg_memory"); |
| if (res == NULL) { |
| KGSL_DRV_ERR("platform_get_resource_byname failed\n"); |
| result = -EINVAL; |
| goto done; |
| } |
| KGSL_DRV_DBG("registers at %08x to %08x\n", res->start, res->end); |
| devconfig->regspace.mmio_phys_base = res->start; |
| devconfig->regspace.sizebytes = resource_size(res); |
| |
| devconfig->gmemspace.gpu_base = 0; |
| devconfig->gmemspace.sizebytes = SZ_256K; |
| |
| /*note: for all of these behavior masks: |
| * 0 = do not translate |
| * 1 = translate within va_range, otherwise use physical |
| * 2 = translate within va_range, otherwise fault |
| */ |
| devconfig->mmu_config = 1 /* mmu enable */ |
| | (2 << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) |
| | (2 << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT); |
| |
| /*TODO: these should probably be configurable from platform device |
| * stuff */ |
| devconfig->va_base = 0x66000000; |
| devconfig->va_range = SZ_128M; |
| |
| /* turn off memory protection unit by setting acceptable physical |
| * address range to include all pages. Apparrently MPU causing |
| * problems. |
| */ |
| devconfig->mpu_base = 0x00000000; |
| devconfig->mpu_range = 0xFFFFF000; |
| |
| result = 0; |
| done: |
| return result; |
| } |