aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/busfreq_opp_4210.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-exynos/busfreq_opp_4210.c')
-rw-r--r--arch/arm/mach-exynos/busfreq_opp_4210.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/busfreq_opp_4210.c b/arch/arm/mach-exynos/busfreq_opp_4210.c
new file mode 100644
index 0000000..d566fea
--- /dev/null
+++ b/arch/arm/mach-exynos/busfreq_opp_4210.c
@@ -0,0 +1,298 @@
+/* linux/arch/arm/mach-exynos/busfreq_opp_4210.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * EXYNOS4 - BUS clock frequency scaling support with OPP
+ *
+ * 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.
+*/
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/ktime.h>
+#include <linux/tick.h>
+#include <linux/kernel_stat.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/opp.h>
+#include <mach/busfreq.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/ppmu.h>
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <mach/gpio.h>
+#include <mach/regs-mem.h>
+#include <mach/cpufreq.h>
+#include <mach/dev.h>
+#include <mach/asv.h>
+
+#include <plat/map-s5p.h>
+#include <plat/gpio-cfg.h>
+
+enum busfreq_level_idx {
+ LV_0,
+ LV_1,
+ LV_2,
+ LV_END
+};
+
+static struct busfreq_table exynos4_busfreq_table[] = {
+ {LV_0, 400000, 1100000, 0, 0, 0},
+ {LV_1, 267000, 1000000, 0, 0, 0},
+ {LV_2, 133000, 950000, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0},
+};
+
+#define ASV_GROUP 5
+static unsigned int exynos4_asv_volt[ASV_GROUP][LV_END] = {
+ {1150000, 1050000, 1000000},
+ {1125000, 1025000, 1000000},
+ {1100000, 1000000, 975000},
+ {1075000, 975000, 950000},
+ {1050000, 950000, 950000},
+};
+
+static unsigned int clkdiv_dmc0[LV_END][8] = {
+ /*
+ * Clock divider value for following
+ * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
+ * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS }
+ */
+
+ /* DMC L0: 400MHz */
+ {3, 1, 1, 1, 1, 1, 3, 1},
+
+ /* DMC L1: 266.7MHz */
+ {4, 1, 1, 2, 1, 1, 3, 1},
+
+ /* DMC L2: 133MHz */
+ {5, 1, 1, 5, 1, 1, 3, 1},
+};
+
+static unsigned int clkdiv_top[LV_END][5] = {
+ /*
+ * Clock divider value for following
+ * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
+ */
+
+ /* ACLK200 L0: 200MHz */
+ {3, 7, 4, 5, 1},
+
+ /* ACLK200 L1: 160MHz */
+ {4, 7, 5, 6, 1},
+
+ /* ACLK200 L2: 133MHz */
+ {5, 7, 7, 7, 1},
+};
+
+static unsigned int clkdiv_lr_bus[LV_END][2] = {
+ /*
+ * Clock divider value for following
+ * { DIVGDL/R, DIVGPL/R }
+ */
+
+ /* ACLK_GDL/R L1: 200MHz */
+ {3, 1},
+
+ /* ACLK_GDL/R L2: 160MHz */
+ {4, 1},
+
+ /* ACLK_GDL/R L3: 133MHz */
+ {5, 1},
+};
+
+static void exynos4210_set_bus_volt(void)
+{
+ unsigned int asv_group;
+ unsigned int i;
+
+ asv_group = exynos_asv->asv_result & 0xF;
+
+ asv_group = 0;
+ printk(KERN_INFO "DVFS : VDD_INT Voltage table set with %d Group\n", asv_group);
+
+ for (i = 0 ; i < LV_END ; i++) {
+ switch (asv_group) {
+ case 0:
+ exynos4_busfreq_table[i].volt =
+ exynos4_asv_volt[0][i];
+ break;
+ case 1:
+ case 2:
+ exynos4_busfreq_table[i].volt =
+ exynos4_asv_volt[1][i];
+ break;
+ case 3:
+ case 4:
+ exynos4_busfreq_table[i].volt =
+ exynos4_asv_volt[2][i];
+ break;
+ case 5:
+ case 6:
+ exynos4_busfreq_table[i].volt =
+ exynos4_asv_volt[3][i];
+ break;
+ case 7:
+ exynos4_busfreq_table[i].volt =
+ exynos4_asv_volt[4][i];
+ break;
+ }
+ }
+
+ return;
+}
+
+unsigned int exynos4210_target(unsigned int div_index)
+{
+ unsigned int tmp;
+
+ /* Change Divider - DMC0 */
+ tmp = exynos4_busfreq_table[div_index].clk_dmc0div;
+
+ __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0);
+
+ do {
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0);
+ } while (tmp & 0x11111111);
+
+ /* Change Divider - TOP */
+ tmp = exynos4_busfreq_table[div_index].clk_topdiv;
+
+ __raw_writel(tmp, EXYNOS4_CLKDIV_TOP);
+
+ do {
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP);
+ } while (tmp & 0x11111);
+
+ /* Change Divider - LEFTBUS */
+ tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS);
+
+ tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK);
+
+ tmp |= ((clkdiv_lr_bus[div_index][0] << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) |
+ (clkdiv_lr_bus[div_index][1] << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT));
+
+ __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS);
+
+ do {
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS);
+ } while (tmp & 0x11);
+
+ /* Change Divider - RIGHTBUS */
+ tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS);
+
+ tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK);
+
+ tmp |= ((clkdiv_lr_bus[div_index][0] << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) |
+ (clkdiv_lr_bus[div_index][1] << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT));
+
+ __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS);
+
+ do {
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS);
+ } while (tmp & 0x11);
+
+ return div_index;
+}
+
+unsigned int exynos4210_get_table_index(struct opp *opp)
+{
+ unsigned int index;
+
+ for (index = LV_0; index < LV_END; index++)
+ if (opp_get_freq(opp) == exynos4_busfreq_table[index].mem_clk)
+ break;
+
+ return index;
+}
+
+int exynos4210_init(struct device *dev, struct busfreq_data *data)
+{
+ unsigned int i;
+ unsigned int tmp;
+ unsigned long maxfreq = UINT_MAX;
+ int ret;
+
+ tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0);
+
+ for (i = 0; i < LV_END; i++) {
+ tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK |
+ EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK |
+ EXYNOS4_CLKDIV_DMC0_DPHY_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMC_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMCD_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMCP_MASK |
+ EXYNOS4_CLKDIV_DMC0_COPY2_MASK |
+ EXYNOS4_CLKDIV_DMC0_CORETI_MASK);
+
+ tmp |= ((clkdiv_dmc0[i][0] << EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) |
+ (clkdiv_dmc0[i][1] << EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) |
+ (clkdiv_dmc0[i][2] << EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) |
+ (clkdiv_dmc0[i][3] << EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) |
+ (clkdiv_dmc0[i][4] << EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) |
+ (clkdiv_dmc0[i][5] << EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) |
+ (clkdiv_dmc0[i][6] << EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) |
+ (clkdiv_dmc0[i][7] << EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT));
+
+ exynos4_busfreq_table[i].clk_dmc0div = tmp;
+ }
+
+ tmp = __raw_readl(EXYNOS4_CLKDIV_TOP);
+
+ for (i = 0; i < LV_END; i++) {
+ tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK200_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK100_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK160_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK133_MASK |
+ EXYNOS4_CLKDIV_TOP_ONENAND_MASK);
+
+ tmp |= ((clkdiv_top[i][0] << EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) |
+ (clkdiv_top[i][1] << EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) |
+ (clkdiv_top[i][2] << EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) |
+ (clkdiv_top[i][3] << EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) |
+ (clkdiv_top[i][4] << EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT));
+
+ exynos4_busfreq_table[i].clk_topdiv = tmp;
+ }
+
+ exynos4210_set_bus_volt();
+
+ for (i = 0; i < LV_END; i++) {
+ ret = opp_add(dev, exynos4_busfreq_table[i].mem_clk,
+ exynos4_busfreq_table[i].volt);
+ if (ret) {
+ dev_err(dev, "Fail to add opp entries.\n");
+ return ret;
+ }
+ }
+
+ data->table = exynos4_busfreq_table;
+ data->table_size = LV_END;
+
+ /* Find max frequency */
+ data->max_opp = opp_find_freq_floor(dev, &maxfreq);
+
+ data->vdd_int = regulator_get(NULL, "vdd_int");
+ if (IS_ERR(data->vdd_int)) {
+ pr_err("failed to get resource %s\n", "vdd_int");
+ return -ENODEV;
+ }
+
+ data->vdd_mif = ERR_PTR(-ENODEV);
+
+ return 0;
+}