/*
 * mali_kbase_meson.c
 *
 * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 */

#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/of_address.h>
#include <linux/delay.h>
#include <mali_kbase.h>
#include "mali_kbase_meson.h"

int meson_gpu_reset(struct kbase_device *kbdev)
{
	struct meson_context *platform = kbdev->platform_context;
    void __iomem *reg_base_reset = platform->reg_base_reset;
    u32 value;

    //JOHNT
    // Level reset mail
 
    // Level reset mail
    //writel(~(0x1<<14), reg_base_reset + P_RESET2_MASK * 4);
    //writel(~(0x1<<14), reg_base_reset + P_RESET2_LEVEL * 4);

    //writel(0xffffffff, reg_base_reset + P_RESET2_LEVEL * 4);
    //writel(0xffffffff, reg_base_reset + P_RESET0_LEVEL * 4);

    MESON_PRINT("%s, %d\n", __func__, __LINE__);
    MESON_PRINT("reg_base=%p, reset0=%p\n", reg_base_reset, reg_base_reset + RESET0_MASK * 4);
    udelay(100);
#if 0
    value = readl(reg_base_reset + RESET0_REGISTER * 4);
    MESON_PRINT("line(%d), RESET0_REGISTER=%x\n", __LINE__, value);
#endif

    udelay(100);
    MESON_PRINT("%s, %d\n", __func__, __LINE__);
    value = readl(reg_base_reset + RESET0_MASK * 4);
    value = value & (~(0x1<<20));
    writel(value, reg_base_reset + RESET0_MASK * 4);

    udelay(100);
#if 0
    value = readl(reg_base_reset + RESET0_REGISTER * 4);
    MESON_PRINT("line(%d), RESET0_REGISTER=%x\n", __LINE__, value);
    udelay(100);
#endif

    value = readl(reg_base_reset + RESET0_LEVEL * 4);
    value = value & (~(0x1<<20));
    //MESON_PRINT("line(%d), value=%x\n", __LINE__, value);
    writel(value, reg_base_reset + RESET0_LEVEL * 4);
    udelay(100);

#if 0
    value = readl(reg_base_reset + RESET0_REGISTER * 4);
    MESON_PRINT("line(%d), RESET0_REGISTER=%x\n", __LINE__, value);
    udelay(100);
#endif

///////////////
#if 0
    value = readl(reg_base_reset + RESET2_REGISTER * 4);
    MESON_PRINT("line(%d), RESET2_REGISTER=%x\n", __LINE__, value);
#endif

    udelay(100);
    value = readl(reg_base_reset + RESET2_MASK * 4);
    value = value & (~(0x1<<14));
    //MESON_PRINT("line(%d), value=%x\n", __LINE__, value);
    writel(value, reg_base_reset + RESET2_MASK * 4);

#if 0
    value = readl(reg_base_reset + RESET2_REGISTER * 4);
    MESON_PRINT("line(%d), RESET2_REGISTER=%x\n", __LINE__, value);
#endif

    value = readl(reg_base_reset + RESET2_LEVEL * 4);
    value = value & (~(0x1<<14));
    //MESON_PRINT("line(%d), value=%x\n", __LINE__, value);
    writel(value, reg_base_reset + RESET2_LEVEL * 4);
    udelay(100);

#if 0
    value = readl(reg_base_reset + RESET2_REGISTER * 4);
    MESON_PRINT("line(%d), RESET2_REGISTER=%x\n", __LINE__, value);
#endif

    udelay(100);
    value = readl(reg_base_reset + RESET0_LEVEL * 4);
    value = value | ((0x1<<20));
    //MESON_PRINT("line(%d), value=%x\n", __LINE__, value);
    writel(value, reg_base_reset + RESET0_LEVEL * 4);
#if 0
    value = readl(reg_base_reset + RESET2_REGISTER * 4);
    MESON_PRINT("line(%d), RESET2_REGISTER=%x\n", __LINE__, value);
#endif

    udelay(100);
    value = readl(reg_base_reset + RESET2_LEVEL * 4);
    value = value | ((0x1<<14));
    //MESON_PRINT("line(%d), value=%x\n", __LINE__, value);
    writel(value, reg_base_reset + RESET2_LEVEL * 4);
#if 0
    value = readl(reg_base_reset + RESET2_REGISTER * 4);
    MESON_PRINT("line(%d), RESET2_REGISTER=%x\n", __LINE__, value);
#endif

    udelay(10); // OR POLL for reset done

    return 0;
}

void  meson_gpu_pwr_on(struct kbase_device *kbdev, u32  mask)
{
    u32    part1_done = 0;
    u32     value = 0;
    u32     count = 0;

    kbdev->pm.backend.gpu_powered = true;
    MESON_PRINT("%s, %d begin\n", __func__,__LINE__);

#if 0
    value = 0x10 | (0x1<<16);
#else
    value = 0xfff | (0x20<<16);
#endif
    while (part1_done != value) {
        Mali_WrReg(GPU_CONTROL_REG(PWR_KEY), 0x2968A819);
        Mali_WrReg(GPU_CONTROL_REG(PWR_OVERRIDE1), value);
        part1_done = Mali_RdReg(GPU_CONTROL_REG(PWR_OVERRIDE1));
        MESON_PRINT("write again, count=%d, overrider1=%x\n", count, part1_done);
        udelay(20);
        count ++;
        if (0 == (count %100)) MESON_PRINT("write again, count%d\n", count);
    }
    part1_done = Mali_RdReg(GPU_CONTROL_REG(PWR_OVERRIDE1));
    MESON_PRINT("write again, count=%d, overrider1=%x\n", count, part1_done);

    Mali_WrReg(0x0000024, 0xffffffff); // clear interrupts

    MESON_PRINT("%s, %d\n", __func__,__LINE__);
    if ((mask & 0x1) != 0 ) {
        MESON_PRINT("%s, %d\n", __func__,__LINE__);
        Mali_WrReg(0x00000190, 0xffffffff);    // Power on all cores
        Mali_WrReg(0x00000194, 0xffffffff);    // Power on all cores
        Mali_WrReg(0x000001a0, 0xffffffff);    // Power on all cores
        Mali_WrReg(0x000001a4, 0xffffffff);    // Power on all cores
    }
    MESON_PRINT("power on %d\n", __LINE__);

    if ( (mask >> 1) != 0 ) {
        Mali_WrReg(0x00000180, mask >> 1);    // Power on all cores
        Mali_WrReg(0x00000184, 0x0);    // Power on all cores
        MESON_PRINT("%s, %d\n", __func__,__LINE__);
    }

    MESON_PRINT("%s, %d\n", __func__,__LINE__);
    if ( mask != 0 ) {
        MESON_PRINT("%s, %d\n", __func__,__LINE__);
        udelay(10);
        part1_done = Mali_RdReg(0x0000020);
        while(part1_done ==0) {
            part1_done = Mali_RdReg(0x00000020);
            udelay(10);
        }

        MESON_PRINT("%s, %d\n", __func__,__LINE__);
        Mali_WrReg(0x0000024, 0xffffffff); // clear interrupts
    }
    MESON_PRINT("%s, %d end\n", __func__,__LINE__);
}

void  meson_gpu_pwr_off(struct kbase_device *kbdev, u32  mask)
{
#if 1
    u32 part1_done;
    part1_done = 0;
    Mali_WrReg(0x0000024, 0xffffffff); // clear interrupts

    if ( (mask >> 1) != 0 ) {
        Mali_WrReg(0x000001C0, mask >> 1);    // Power off all cores
        Mali_WrReg(0x000001C4, 0x0);          // Power off all cores
    }

    if (  (mask & 0x1) != 0 ) {
        Mali_WrReg(0x000001D0, 0xffffffff);    // Power off all cores
        Mali_WrReg(0x000001D4, 0xffffffff);    // Power off all cores
        Mali_WrReg(0x000001E0, 0xffffffff);    // Power off all cores
        Mali_WrReg(0x000001E4, 0xffffffff);    // Power off all cores
    }

    if ( mask != 0 ) {
        part1_done = Mali_RdReg(0x0000020);
        while((part1_done ==0)) { part1_done = Mali_RdReg(0x00000020); }
        MESON_PRINT("Mali_pwr_off:gpu_irq : %x\n", part1_done);
        Mali_WrReg(0x0000024, 0xffffffff); // clear interrupts
    }
#endif
}




static int kbase_platform_meson_init(struct kbase_device *kbdev)
{
#if 0
	int err;
#endif
	struct device_node *gpu_dn = kbdev->dev->of_node;
#ifdef CONFIG_MALI_MIDGARD_DVFS
	unsigned long flags;
#endif /* CONFIG_MALI_MIDGARD_DVFS */
	struct meson_context *platform;
    u32 part1_done = 0;

	platform = kmalloc(sizeof(struct meson_context), GFP_KERNEL);

	if (!platform)
		return -ENOMEM;

	memset(platform, 0, sizeof(struct meson_context));

	kbdev->platform_context = (void *) platform;

    platform->reg_base_reset = of_iomap(gpu_dn, 1);
	_dev_info(kbdev->dev, "reset io source  0x%p\n",platform->reg_base_reset);

	platform->reg_base_aobus = of_iomap(gpu_dn, 2);
	_dev_info(kbdev->dev, "ao io source  0x%p\n", platform->reg_base_aobus);

	platform->reg_base_hiubus = of_iomap(gpu_dn, 3);
	_dev_info(kbdev->dev, "hiu io source  0x%p\n", platform->reg_base_hiubus);

    platform->clk_mali = devm_clk_get(kbdev->dev, "gpu_mux");
	if (IS_ERR(platform->clk_mali)) {
		dev_err(kbdev->dev, "failed to get clock pointer\n");
	} else {
        clk_prepare_enable(platform->clk_mali);
        clk_set_rate(platform->clk_mali, 285000000);
    }
    MESON_PRINT("%s, %d begin\n", __func__, __LINE__);
    meson_gpu_reset(kbdev);
    meson_gpu_pwr_on(kbdev, 0xe);

    part1_done = Mali_RdReg(GPU_CONTROL_REG(PWR_OVERRIDE1));
    MESON_PRINT("line%d, overrider1=%x\n", __LINE__, part1_done);
    meson_gpu_pwr_off(kbdev, 0xe);
#if 1
    part1_done = Mali_RdReg(GPU_CONTROL_REG(PWR_OVERRIDE1));
    MESON_PRINT("line%d, overrider1=%x\n", __LINE__, part1_done);

    meson_gpu_reset(kbdev);
    meson_gpu_pwr_on(kbdev, 0xe);
    part1_done = Mali_RdReg(GPU_CONTROL_REG(PWR_OVERRIDE1));
    MESON_PRINT("line%d, overrider1=%x\n", __LINE__, part1_done);
#endif
#if 0
	platform->cmu_pmu_status = 0;
	platform->dvfs_wq = NULL;
	platform->polling_speed = 100;
	gpu_debug_level = DVFS_WARNING;
#endif

	mutex_init(&platform->gpu_clock_lock);
	mutex_init(&platform->gpu_dvfs_handler_lock);
	spin_lock_init(&platform->gpu_dvfs_spinlock);
#if 0
	err = gpu_control_module_init(kbdev);
	if (err)
		goto clock_init_fail;

	/* dvfs gobernor init*/
	gpu_dvfs_governor_init(kbdev, G3D_DVFS_GOVERNOR_DEFAULT);
#endif
#ifdef CONFIG_MALI_MIDGARD_DVFS
	spin_lock_irqsave(&platform->gpu_dvfs_spinlock, flags);
	platform->wakeup_lock = 0;
	spin_unlock_irqrestore(&platform->gpu_dvfs_spinlock, flags);
#endif /* CONFIG_MALI_MIDGARD_DVFS */
#if 0
	/* dvfs handler init*/
	gpu_dvfs_handler_init(kbdev);

	err = gpu_notifier_init(kbdev);
	if (err)
		goto notifier_init_fail;

	err = gpu_create_sysfs_file(kbdev->dev);
	if (err)
		goto sysfs_init_fail;
#endif

    MESON_PRINT("%s, %d end\n", __func__, __LINE__);
	return 0;
#if 0
clock_init_fail:
notifier_init_fail:
sysfs_init_fail:
	kfree(platform);

	return err;
#endif
}

/**
 ** Meson hardware specific termination
 **/
static void kbase_platform_meson_term(struct kbase_device *kbdev)
{
	struct meson_context *platform;
	platform = (struct meson_context *) kbdev->platform_context;
#if 0
	gpu_notifier_term();

#ifdef CONFIG_MALI_MIDGARD_DVFS
	gpu_dvfs_handler_deinit(kbdev);
#endif /* CONFIG_MALI_MIDGARD_DVFS */

	gpu_control_module_term(kbdev);
#endif

	kfree(kbdev->platform_context);
	kbdev->platform_context = 0;

#if 0
	gpu_remove_sysfs_file(kbdev->dev);
#endif
}

struct kbase_platform_funcs_conf platform_funcs = {
	.platform_init_func = &kbase_platform_meson_init,
	.platform_term_func = &kbase_platform_meson_term,
};

