void kbasep_8401_workaround_term(kbase_device *kbdev)
{
	kbasep_js_device_data *js_devdata;
	int i;
	u16 restored_as;

	OSK_ASSERT(kbdev);
	OSK_ASSERT(kbdev->workaround_kctx);

	js_devdata = &kbdev->js_data;

	for(i = 0; i < KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT; i++)
	{
		osk_kunmap(kbdev->workaround_compute_job_pa[i], kbdev->workaround_compute_job_va[i]);
	}

	kbase_phy_pages_free(kbdev, &kbdev->workaround_kctx->pgd_allocator, KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT, kbdev->workaround_compute_job_pa);

	kbase_destroy_context(kbdev->workaround_kctx);
	kbdev->workaround_kctx = NULL;

	/* Free up the workaround address space */
	kbdev->nr_hw_address_spaces++;

	if ( kbdev->nr_user_address_spaces == (kbdev->nr_hw_address_spaces - 1) )
	{
		/* Only update nr_user_address_spaces if it was unchanged - to ensure
		 * HW workarounds that have modified this will still work */
		++(kbdev->nr_user_address_spaces);
	}
	OSK_ASSERT( kbdev->nr_user_address_spaces <= kbdev->nr_hw_address_spaces );

	/* Recalculate the free address spaces bit-pattern */
	restored_as = (1U << kbdev->nr_hw_address_spaces);
	js_devdata->as_free |= restored_as;
}
static base_jd_event_code kbase_dump_cpu_gpu_time(kbase_jd_atom *katom)
{
	kbase_va_region *reg;
	osk_phy_addr addr;
	u64 pfn;
	u32 offset;
	char *page;
	struct timespec ts;
	base_dump_cpu_gpu_counters data;
	u64 system_time;
	u64 cycle_counter;
	mali_addr64 jc = katom->jc;
	kbase_context *kctx = katom->kctx;

	u32 hi1, hi2;

	memset(&data, 0, sizeof(data));

	kbase_pm_context_active(kctx->kbdev);

	/* Read hi, lo, hi to ensure that overflow from lo to hi is handled correctly */
	do {
		hi1 = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(CYCLE_COUNT_HI), NULL);
		cycle_counter = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(CYCLE_COUNT_LO), NULL);
		hi2 = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(CYCLE_COUNT_HI), NULL);
		cycle_counter |= (((u64)hi1) << 32);
	} while (hi1 != hi2);

	/* Read hi, lo, hi to ensure that overflow from lo to hi is handled correctly */
	do {
		hi1 = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(TIMESTAMP_HI), NULL);
		system_time = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(TIMESTAMP_LO), NULL);
		hi2 = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(TIMESTAMP_HI), NULL);
		system_time |= (((u64)hi1) << 32);
	} while (hi1 != hi2);

	/* Record the CPU's idea of current time */
	getnstimeofday(&ts);

	kbase_pm_context_idle(kctx->kbdev);

	data.sec = ts.tv_sec;
	data.usec = ts.tv_nsec / 1000;
	data.system_time = system_time;
	data.cycle_counter = cycle_counter;

	pfn = jc >> 12;
	offset = jc & 0xFFF;

	if (offset > 0x1000-sizeof(data))
	{
		/* Wouldn't fit in the page */
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	reg = kbase_region_tracker_find_region_enclosing_address(kctx, jc);
	if (!reg)
	{
		return BASE_JD_EVENT_JOB_CANCELLED;
	}
	
	if (! (reg->flags & KBASE_REG_GPU_WR) )
	{
		/* Region is not writable by GPU so we won't write to it either */
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	if (!reg->phy_pages)
	{
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	addr = reg->phy_pages[pfn - reg->start_pfn];
	if (!addr)
	{
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	page = osk_kmap(addr);
	if (!page)
	{
		return BASE_JD_EVENT_JOB_CANCELLED;
	}
	memcpy(page+offset, &data, sizeof(data));
	osk_sync_to_cpu(addr+offset, page+offset, sizeof(data));
	osk_kunmap(addr, page);

	return BASE_JD_EVENT_DONE;
}
mali_error kbasep_8401_workaround_init(kbase_device *kbdev)
{
	kbase_context *workaround_kctx;
	u32 count;
	int i;

	OSK_ASSERT(kbdev);
	OSK_ASSERT(kbdev->workaround_kctx == NULL);

	/* For this workaround we reserve one address space to allow us to
	 * submit a special job independent of other contexts */
	kbdev->nr_address_spaces--;

	workaround_kctx = kbase_create_context(kbdev);
	if(!workaround_kctx)
	{
		return MALI_ERROR_FUNCTION_FAILED;
	}

	/* Allocate the pages required to contain the job */
	count = kbase_phy_pages_alloc(workaround_kctx->kbdev,
	                              &workaround_kctx->pgd_allocator,
	                              KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT,
	                              kbdev->workaround_compute_job_pa);
	if(count < KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT)
	{
		goto page_release;
	}

	/* Get virtual address of mapped memory and write a compute job for each page */
	for(i = 0; i < KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT; i++)
	{
		kbdev->workaround_compute_job_va[i] = osk_kmap(kbdev->workaround_compute_job_pa[i]);
		if(NULL == kbdev->workaround_compute_job_va[i])
		{
			goto page_free;
		}

		/* Generate the compute job data */
		kbasep_8401_workaround_update_job_pointers((u32*)kbdev->workaround_compute_job_va[i], i);
	}

	/* Insert pages to the gpu mmu. */
	kbase_mmu_insert_pages(workaround_kctx,
	                       /* vpfn = page number */
	                       (u64)WORKAROUND_PAGE_OFFSET,
	                       /* physical address */
	                       kbdev->workaround_compute_job_pa,
	                       /* number of pages */
	                       KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT,
	                       /* flags */
	                       KBASE_REG_CPU_RW|KBASE_REG_GPU_RW);

	kbdev->workaround_kctx = workaround_kctx;
	return MALI_ERROR_NONE;
page_free:
	while(i--)
	{
		osk_kunmap(kbdev->workaround_compute_job_pa[i], kbdev->workaround_compute_job_va[i]);
	}
page_release:
	kbase_phy_pages_free(kbdev, &workaround_kctx->pgd_allocator, count, kbdev->workaround_compute_job_pa);
	kbase_destroy_context(workaround_kctx);

	return MALI_ERROR_FUNCTION_FAILED;
}
mali_error kbasep_8401_workaround_init(kbase_device *kbdev)
{
	kbasep_js_device_data *js_devdata;
	kbase_context *workaround_kctx;
	u32 count;
	int i;
	u16 as_present_mask;

	OSK_ASSERT(kbdev);
	OSK_ASSERT(kbdev->workaround_kctx == NULL);

	js_devdata = &kbdev->js_data;

	/* For this workaround we reserve one address space to allow us to
	 * submit a special job independent of other contexts */
	--(kbdev->nr_hw_address_spaces);

	if ( kbdev->nr_user_address_spaces == (kbdev->nr_hw_address_spaces + 1) )
	{
		/* Only update nr_user_address_spaces if it was unchanged - to ensure
		 * HW workarounds that have modified this will still work */
		--(kbdev->nr_user_address_spaces);
	}
	OSK_ASSERT( kbdev->nr_user_address_spaces <= kbdev->nr_hw_address_spaces );

	/* Recalculate the free address spaces bit-pattern */
	as_present_mask = (1U << kbdev->nr_hw_address_spaces) - 1;
	js_devdata->as_free &= as_present_mask;

	workaround_kctx = kbase_create_context(kbdev);
	if(!workaround_kctx)
	{
		return MALI_ERROR_FUNCTION_FAILED;
	}

	/* Allocate the pages required to contain the job */
	count = kbase_phy_pages_alloc(workaround_kctx->kbdev,
	                              &workaround_kctx->pgd_allocator,
	                              KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT,
	                              kbdev->workaround_compute_job_pa);
	if(count < KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT)
	{
		goto page_release;
	}

	/* Get virtual address of mapped memory and write a compute job for each page */
	for(i = 0; i < KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT; i++)
	{
		kbdev->workaround_compute_job_va[i] = osk_kmap(kbdev->workaround_compute_job_pa[i]);
		if(NULL == kbdev->workaround_compute_job_va[i])
		{
			goto page_free;
		}

		/* Generate the compute job data */
		kbasep_8401_workaround_update_job_pointers((u32*)kbdev->workaround_compute_job_va[i], i);
	}

	/* Insert pages to the gpu mmu. */
	kbase_mmu_insert_pages(workaround_kctx,
	                       /* vpfn = page number */
	                       (u64)WORKAROUND_PAGE_OFFSET,
	                       /* physical address */
	                       kbdev->workaround_compute_job_pa,
	                       /* number of pages */
			       KBASE_8401_WORKAROUND_COMPUTEJOB_COUNT,
	                       /* flags */
	                       KBASE_REG_GPU_RD|KBASE_REG_CPU_RD|KBASE_REG_CPU_WR|KBASE_REG_GPU_WR);

	kbdev->workaround_kctx = workaround_kctx;
	return MALI_ERROR_NONE;
page_free:
	while(i--)
	{
		osk_kunmap(kbdev->workaround_compute_job_pa[i], kbdev->workaround_compute_job_va[i]);
	}
page_release:
	kbase_phy_pages_free(kbdev, &workaround_kctx->pgd_allocator, count, kbdev->workaround_compute_job_pa);
	kbase_destroy_context(workaround_kctx);

	return MALI_ERROR_FUNCTION_FAILED;
}
static base_jd_event_code kbase_dump_cpu_gpu_time(kbase_context *kctx, mali_addr64 jc)
{
	kbase_va_region *reg;
	osk_phy_addr addr;
	u64 pfn;
	u32 offset;
	char *page;
	osk_timeval tv;
	base_dump_cpu_gpu_counters data;
	u64 system_time;
	u64 cycle_counter;

	u32 hi1, hi2;

	OSK_MEMSET(&data, 0, sizeof(data));

	/* Read hi, lo, hi to ensure that overflow from lo to hi is handled correctly */
	do {
		hi1 = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(CYCLE_COUNT_HI), NULL);
		cycle_counter = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(CYCLE_COUNT_LO), NULL);
		hi2 = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(CYCLE_COUNT_HI), NULL);
		cycle_counter |= (((u64)hi1) << 32);
	} while (hi1 != hi2);

	/* Read hi, lo, hi to ensure that overflow from lo to hi is handled correctly */
	do {
		hi1 = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(TIMESTAMP_HI), NULL);
		system_time = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(TIMESTAMP_LO), NULL);
		hi2 = kbase_reg_read(kctx->kbdev, GPU_CONTROL_REG(TIMESTAMP_HI), NULL);
		system_time |= (((u64)hi1) << 32);
	} while (hi1 != hi2);

	/* Record the CPU's idea of current time */
	osk_gettimeofday(&tv);

	data.sec = tv.tv_sec;
	data.usec = tv.tv_usec;
	data.system_time = system_time;
	data.cycle_counter = cycle_counter;

	pfn = jc >> 12;
	offset = jc & 0xFFF;

	if (offset > 0x1000-sizeof(data))
	{
		/* Wouldn't fit in the page */
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	reg = kbase_region_lookup(kctx, jc);
	if (!reg)
	{
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	if (! (reg->flags & KBASE_REG_GPU_RW) )
	{
		/* Region is not writable by GPU so we won't write to it either */
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	if (!reg->phy_pages)
	{
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	addr = reg->phy_pages[pfn - reg->start_pfn];
	if (!addr)
	{
		return BASE_JD_EVENT_JOB_CANCELLED;
	}

	page = osk_kmap(addr);
	if (!page)
	{
		return BASE_JD_EVENT_JOB_CANCELLED;
	}
	memcpy(page+offset, &data, sizeof(data));
	osk_kunmap(addr, page);

	return BASE_JD_EVENT_DONE;
}