diff options
Diffstat (limited to 'arch/arm/mach-exynos/busfreq_opp_4210.c')
-rw-r--r-- | arch/arm/mach-exynos/busfreq_opp_4210.c | 298 |
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; +} |