/*
 * AM33XX Clock data
 *
 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
 * Vaibhav Hiremath <hvaibhav@ti.com>
 *
 * 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 version 2.
 *
 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
 * kind, whether express or implied; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/clk-private.h>
#include <linux/clkdev.h>
#include <linux/io.h>

#include "am33xx.h"
#include "soc.h"
#include "iomap.h"
#include "clock.h"
#include "control.h"
#include "cm.h"
#include "cm33xx.h"
#include "cm-regbits-33xx.h"
#include "prm.h"

/* Modulemode control */
#define AM33XX_MODULEMODE_HWCTRL_SHIFT		0
#define AM33XX_MODULEMODE_SWCTRL_SHIFT		1

/*LIST_HEAD(clocks);*/

/* Root clocks */

/* RTC 32k */
DEFINE_CLK_FIXED_RATE(clk_32768_ck, CLK_IS_ROOT, 32768, 0x0);

/* On-Chip 32KHz RC OSC */
DEFINE_CLK_FIXED_RATE(clk_rc32k_ck, CLK_IS_ROOT, 32000, 0x0);

/* Crystal input clks */
DEFINE_CLK_FIXED_RATE(virt_19200000_ck, CLK_IS_ROOT, 19200000, 0x0);

DEFINE_CLK_FIXED_RATE(virt_24000000_ck, CLK_IS_ROOT, 24000000, 0x0);

DEFINE_CLK_FIXED_RATE(virt_25000000_ck, CLK_IS_ROOT, 25000000, 0x0);

DEFINE_CLK_FIXED_RATE(virt_26000000_ck, CLK_IS_ROOT, 26000000, 0x0);

/* Oscillator clock */
/* 19.2, 24, 25 or 26 MHz */
static const char *sys_clkin_ck_parents[] = {
	"virt_19200000_ck", "virt_24000000_ck", "virt_25000000_ck",
	"virt_26000000_ck",
};

/*
 * sys_clk in: input to the dpll and also used as funtional clock for,
 *   adc_tsc, smartreflex0-1, timer1-7, mcasp0-1, dcan0-1, cefuse
 *
 */
DEFINE_CLK_MUX(sys_clkin_ck, sys_clkin_ck_parents, NULL, 0x0,
	       AM33XX_CTRL_REGADDR(AM33XX_CONTROL_STATUS),
	       AM33XX_CONTROL_STATUS_SYSBOOT1_SHIFT,
	       AM33XX_CONTROL_STATUS_SYSBOOT1_WIDTH,
	       0, NULL);

/* External clock - 12 MHz */
DEFINE_CLK_FIXED_RATE(tclkin_ck, CLK_IS_ROOT, 12000000, 0x0);

/* Module clocks and DPLL outputs */

/* DPLL_CORE */
static struct dpll_data dpll_core_dd = {
	.mult_div1_reg	= AM33XX_CM_CLKSEL_DPLL_CORE,
	.clk_bypass	= &sys_clkin_ck,
	.clk_ref	= &sys_clkin_ck,
	.control_reg	= AM33XX_CM_CLKMODE_DPLL_CORE,
	.modes		= (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
	.idlest_reg	= AM33XX_CM_IDLEST_DPLL_CORE,
	.mult_mask	= AM33XX_DPLL_MULT_MASK,
	.div1_mask	= AM33XX_DPLL_DIV_MASK,
	.enable_mask	= AM33XX_DPLL_EN_MASK,
	.idlest_mask	= AM33XX_ST_DPLL_CLK_MASK,
	.max_multiplier	= 2047,
	.max_divider	= 128,
	.min_divider	= 1,
};

/* CLKDCOLDO output */
static const char *dpll_core_ck_parents[] = {
	"sys_clkin_ck",
};

static struct clk dpll_core_ck;

static const struct clk_ops dpll_core_ck_ops = {
	.recalc_rate	= &omap3_dpll_recalc,
	.get_parent	= &omap2_init_dpll_parent,
};

static struct clk_hw_omap dpll_core_ck_hw = {
	.hw	= {
		.clk	= &dpll_core_ck,
	},
	.dpll_data	= &dpll_core_dd,
	.ops		= &clkhwops_omap3_dpll,
};

DEFINE_STRUCT_CLK(dpll_core_ck, dpll_core_ck_parents, dpll_core_ck_ops);

static const char *dpll_core_x2_ck_parents[] = {
	"dpll_core_ck",
};

static struct clk dpll_core_x2_ck;

static const struct clk_ops dpll_x2_ck_ops = {
	.recalc_rate	= &omap3_clkoutx2_recalc,
};

static struct clk_hw_omap dpll_core_x2_ck_hw = {
	.hw	= {
		.clk	= &dpll_core_x2_ck,
	},
	.flags		= CLOCK_CLKOUTX2,
};

DEFINE_STRUCT_CLK(dpll_core_x2_ck, dpll_core_x2_ck_parents, dpll_x2_ck_ops);

DEFINE_CLK_DIVIDER(dpll_core_m4_ck, "dpll_core_x2_ck", &dpll_core_x2_ck,
		   0x0, AM33XX_CM_DIV_M4_DPLL_CORE,
		   AM33XX_HSDIVIDER_CLKOUT1_DIV_SHIFT,
		   AM33XX_HSDIVIDER_CLKOUT1_DIV_WIDTH, CLK_DIVIDER_ONE_BASED,
		   NULL);

DEFINE_CLK_DIVIDER(dpll_core_m5_ck, "dpll_core_x2_ck", &dpll_core_x2_ck,
		   0x0, AM33XX_CM_DIV_M5_DPLL_CORE,
		   AM33XX_HSDIVIDER_CLKOUT2_DIV_SHIFT,
		   AM33XX_HSDIVIDER_CLKOUT2_DIV_WIDTH,
		   CLK_DIVIDER_ONE_BASED, NULL);

DEFINE_CLK_DIVIDER(dpll_core_m6_ck, "dpll_core_x2_ck", &dpll_core_x2_ck,
		   0x0, AM33XX_CM_DIV_M6_DPLL_CORE,
		   AM33XX_HSDIVIDER_CLKOUT3_DIV_SHIFT,
		   AM33XX_HSDIVIDER_CLKOUT3_DIV_WIDTH,
		   CLK_DIVIDER_ONE_BASED, NULL);


/* DPLL_MPU */
static struct dpll_data dpll_mpu_dd = {
	.mult_div1_reg	= AM33XX_CM_CLKSEL_DPLL_MPU,
	.clk_bypass	= &sys_clkin_ck,
	.clk_ref	= &sys_clkin_ck,
	.control_reg	= AM33XX_CM_CLKMODE_DPLL_MPU,
	.modes		= (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
	.idlest_reg	= AM33XX_CM_IDLEST_DPLL_MPU,
	.mult_mask	= AM33XX_DPLL_MULT_MASK,
	.div1_mask	= AM33XX_DPLL_DIV_MASK,
	.enable_mask	= AM33XX_DPLL_EN_MASK,
	.idlest_mask	= AM33XX_ST_DPLL_CLK_MASK,
	.max_multiplier	= 2047,
	.max_divider	= 128,
	.min_divider	= 1,
};

/* CLKOUT: fdpll/M2 */
static struct clk dpll_mpu_ck;

static const struct clk_ops dpll_mpu_ck_ops = {
	.enable		= &omap3_noncore_dpll_enable,
	.disable	= &omap3_noncore_dpll_disable,
	.recalc_rate	= &omap3_dpll_recalc,
	.round_rate	= &omap2_dpll_round_rate,
	.set_rate	= &omap3_noncore_dpll_set_rate,
	.get_parent	= &omap2_init_dpll_parent,
};

static struct clk_hw_omap dpll_mpu_ck_hw = {
	.hw = {
		.clk	= &dpll_mpu_ck,
	},
	.dpll_data	= &dpll_mpu_dd,
	.ops		= &clkhwops_omap3_dpll,
};

DEFINE_STRUCT_CLK(dpll_mpu_ck, dpll_core_ck_parents, dpll_mpu_ck_ops);

/*
 * TODO: Add clksel here (sys_clkin, CORE_CLKOUTM6, PER_CLKOUTM2
 * and ALT_CLK1/2)
 */
DEFINE_CLK_DIVIDER(dpll_mpu_m2_ck, "dpll_mpu_ck", &dpll_mpu_ck,
		   0x0, AM33XX_CM_DIV_M2_DPLL_MPU, AM33XX_DPLL_CLKOUT_DIV_SHIFT,
		   AM33XX_DPLL_CLKOUT_DIV_WIDTH, CLK_DIVIDER_ONE_BASED, NULL);

/* DPLL_DDR */
static struct dpll_data dpll_ddr_dd = {
	.mult_div1_reg	= AM33XX_CM_CLKSEL_DPLL_DDR,
	.clk_bypass	= &sys_clkin_ck,
	.clk_ref	= &sys_clkin_ck,
	.control_reg	= AM33XX_CM_CLKMODE_DPLL_DDR,
	.modes		= (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
	.idlest_reg	= AM33XX_CM_IDLEST_DPLL_DDR,
	.mult_mask	= AM33XX_DPLL_MULT_MASK,
	.div1_mask	= AM33XX_DPLL_DIV_MASK,
	.enable_mask	= AM33XX_DPLL_EN_MASK,
	.idlest_mask	= AM33XX_ST_DPLL_CLK_MASK,
	.max_multiplier	= 2047,
	.max_divider	= 128,
	.min_divider	= 1,
};

/* CLKOUT: fdpll/M2 */
static struct clk dpll_ddr_ck;

static const struct clk_ops dpll_ddr_ck_ops = {
	.recalc_rate	= &omap3_dpll_recalc,
	.get_parent	= &omap2_init_dpll_parent,
	.round_rate	= &omap2_dpll_round_rate,
	.set_rate	= &omap3_noncore_dpll_set_rate,
};

static struct clk_hw_omap dpll_ddr_ck_hw = {
	.hw = {
		.clk	= &dpll_ddr_ck,
	},
	.dpll_data	= &dpll_ddr_dd,
	.ops		= &clkhwops_omap3_dpll,
};

DEFINE_STRUCT_CLK(dpll_ddr_ck, dpll_core_ck_parents, dpll_ddr_ck_ops);

/*
 * TODO: Add clksel here (sys_clkin, CORE_CLKOUTM6, PER_CLKOUTM2
 * and ALT_CLK1/2)
 */
DEFINE_CLK_DIVIDER(dpll_ddr_m2_ck, "dpll_ddr_ck", &dpll_ddr_ck,
		   0x0, AM33XX_CM_DIV_M2_DPLL_DDR,
		   AM33XX_DPLL_CLKOUT_DIV_SHIFT, AM33XX_DPLL_CLKOUT_DIV_WIDTH,
		   CLK_DIVIDER_ONE_BASED, NULL);

/* emif_fck functional clock */
DEFINE_CLK_FIXED_FACTOR(dpll_ddr_m2_div2_ck, "dpll_ddr_m2_ck", &dpll_ddr_m2_ck,
			0x0, 1, 2);

/* DPLL_DISP */
static struct dpll_data dpll_disp_dd = {
	.mult_div1_reg	= AM33XX_CM_CLKSEL_DPLL_DISP,
	.clk_bypass	= &sys_clkin_ck,
	.clk_ref	= &sys_clkin_ck,
	.control_reg	= AM33XX_CM_CLKMODE_DPLL_DISP,
	.modes		= (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
	.idlest_reg	= AM33XX_CM_IDLEST_DPLL_DISP,
	.mult_mask	= AM33XX_DPLL_MULT_MASK,
	.div1_mask	= AM33XX_DPLL_DIV_MASK,
	.enable_mask	= AM33XX_DPLL_EN_MASK,
	.idlest_mask	= AM33XX_ST_DPLL_CLK_MASK,
	.max_multiplier	= 2047,
	.max_divider	= 128,
	.min_divider	= 1,
};

/* CLKOUT: fdpll/M2 */
static struct clk dpll_disp_ck;

static struct clk_hw_omap dpll_disp_ck_hw = {
	.hw = {
		.clk	= &dpll_disp_ck,
	},
	.dpll_data	= &dpll_disp_dd,
	.ops		= &clkhwops_omap3_dpll,
};

DEFINE_STRUCT_CLK(dpll_disp_ck, dpll_core_ck_parents, dpll_ddr_ck_ops);

/*
 * TODO: Add clksel here (sys_clkin, CORE_CLKOUTM6, PER_CLKOUTM2
 * and ALT_CLK1/2)
 */
DEFINE_CLK_DIVIDER(dpll_disp_m2_ck, "dpll_disp_ck", &dpll_disp_ck, 0x0,
		   AM33XX_CM_DIV_M2_DPLL_DISP, AM33XX_DPLL_CLKOUT_DIV_SHIFT,
		   AM33XX_DPLL_CLKOUT_DIV_WIDTH, CLK_DIVIDER_ONE_BASED, NULL);

/* DPLL_PER */
static struct dpll_data dpll_per_dd = {
	.mult_div1_reg	= AM33XX_CM_CLKSEL_DPLL_PERIPH,
	.clk_bypass	= &sys_clkin_ck,
	.clk_ref	= &sys_clkin_ck,
	.control_reg	= AM33XX_CM_CLKMODE_DPLL_PER,
	.modes		= (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
	.idlest_reg	= AM33XX_CM_IDLEST_DPLL_PER,
	.mult_mask	= AM33XX_DPLL_MULT_PERIPH_MASK,
	.div1_mask	= AM33XX_DPLL_PER_DIV_MASK,
	.enable_mask	= AM33XX_DPLL_EN_MASK,
	.idlest_mask	= AM33XX_ST_DPLL_CLK_MASK,
	.max_multiplier	= 2047,
	.max_divider	= 128,
	.min_divider	= 1,
	.flags		= DPLL_J_TYPE,
};

/* CLKDCOLDO */
static struct clk dpll_per_ck;

static struct clk_hw_omap dpll_per_ck_hw = {
	.hw	= {
		.clk	= &dpll_per_ck,
	},
	.dpll_data	= &dpll_per_dd,
	.ops		= &clkhwops_omap3_dpll,
};

DEFINE_STRUCT_CLK(dpll_per_ck, dpll_core_ck_parents, dpll_ddr_ck_ops);

/* CLKOUT: fdpll/M2 */
DEFINE_CLK_DIVIDER(dpll_per_m2_ck, "dpll_per_ck", &dpll_per_ck, 0x0,
		   AM33XX_CM_DIV_M2_DPLL_PER, AM33XX_DPLL_CLKOUT_DIV_SHIFT,
		   AM33XX_DPLL_CLKOUT_DIV_WIDTH, CLK_DIVIDER_ONE_BASED,
		   NULL);

DEFINE_CLK_FIXED_FACTOR(dpll_per_m2_div4_wkupdm_ck, "dpll_per_m2_ck",
			&dpll_per_m2_ck, 0x0, 1, 4);

DEFINE_CLK_FIXED_FACTOR(dpll_per_m2_div4_ck, "dpll_per_m2_ck",
			&dpll_per_m2_ck, 0x0, 1, 4);

DEFINE_CLK_FIXED_FACTOR(dpll_core_m4_div2_ck, "dpll_core_m4_ck",
			&dpll_core_m4_ck, 0x0, 1, 2);

DEFINE_CLK_FIXED_FACTOR(l4_rtc_gclk, "dpll_core_m4_ck", &dpll_core_m4_ck, 0x0,
			1, 2);

DEFINE_CLK_FIXED_FACTOR(clk_24mhz, "dpll_per_m2_ck", &dpll_per_m2_ck, 0x0, 1,
			8);

/*
 * Below clock nodes describes clockdomains derived out
 * of core clock.
