diff -ruN linux/arch/arm/configs/odroidx_defconfig linux-tmu/arch/arm/configs/odroidx_defconfig --- linux/arch/arm/configs/odroidx_defconfig 2012-10-10 09:50:53.149487413 -0300 +++ linux-tmu/arch/arm/configs/odroidx_defconfig 2012-10-12 19:02:06.761366308 -0300 @@ -570,15 +570,15 @@ CONFIG_CPU_FREQ_TABLE=y CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_STAT_DETAILS=y -CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=m CONFIG_CPU_FREQ_GOV_POWERSAVE=m CONFIG_CPU_FREQ_GOV_USERSPACE=m -CONFIG_CPU_FREQ_GOV_ONDEMAND=m +CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m # @@ -587,6 +587,9 @@ CONFIG_ARM_EXYNOS_CPUFREQ=y # CONFIG_ARM_EXYNOS4210_CPUFREQ is not set CONFIG_ARM_EXYNOS4X12_CPUFREQ=y +# CONFIG_EXYNOS4X12_1600MHZ_SUPPORT is not set +# CONFIG_EXYNOS4X12_1704MHZ_SUPPORT is not set +# CONFIG_EXYNOS4X12_1800MHZ_SUPPORT is not set # CONFIG_ARM_EXYNOS5250_CPUFREQ is not set CONFIG_CPU_IDLE=y CONFIG_CPU_IDLE_GOV_LADDER=y @@ -2833,6 +2836,7 @@ # CONFIG_MCP4725 is not set CONFIG_PWM=y CONFIG_PWM_SAMSUNG=y +CONFIG_HKDK4412_TMU=y # # File systems diff -ruN linux/arch/arm/mach-exynos/mach-hkdk4412.c linux-tmu/arch/arm/mach-exynos/mach-hkdk4412.c --- linux/arch/arm/mach-exynos/mach-hkdk4412.c 2012-10-10 09:50:53.225487412 -0300 +++ linux-tmu/arch/arm/mach-exynos/mach-hkdk4412.c 2012-10-12 17:11:12.000000000 -0300 @@ -186,7 +186,7 @@ .constraints = { .name = "VDD_ARM_1.3V", .min_uV = 800000, - .max_uV = 1350000, + .max_uV = 1400000, .always_on = 1, .boot_on = 1, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, diff -ruN linux/drivers/cpufreq/exynos4x12-cpufreq.c linux-tmu/drivers/cpufreq/exynos4x12-cpufreq.c --- linux/drivers/cpufreq/exynos4x12-cpufreq.c 2012-10-10 09:50:54.693487379 -0300 +++ linux-tmu/drivers/cpufreq/exynos4x12-cpufreq.c 2012-10-12 18:20:24.000000000 -0300 @@ -20,7 +20,7 @@ #include #include -#define CPUFREQ_LEVEL_END (L13 + 1) +#define CPUFREQ_LEVEL_END (L16 + 1) static int max_support_idx; static int min_support_idx = (CPUFREQ_LEVEL_END - 1); @@ -39,20 +39,23 @@ static unsigned int exynos4x12_volt_table[CPUFREQ_LEVEL_END]; static struct cpufreq_frequency_table exynos4x12_freq_table[] = { - {L0, 1500 * 1000}, - {L1, 1400 * 1000}, - {L2, 1300 * 1000}, - {L3, 1200 * 1000}, - {L4, 1100 * 1000}, - {L5, 1000 * 1000}, - {L6, 900 * 1000}, - {L7, 800 * 1000}, - {L8, 700 * 1000}, - {L9, 600 * 1000}, - {L10, 500 * 1000}, - {L11, 400 * 1000}, - {L12, 300 * 1000}, - {L13, 200 * 1000}, + {L0, 1800 * 1000}, + {L1, 1704 * 1000}, + {L2, 1600 * 1000}, + {L3, 1500 * 1000}, + {L4, 1400 * 1000}, + {L5, 1300 * 1000}, + {L6, 1200 * 1000}, + {L7, 1100 * 1000}, + {L8, 1000 * 1000}, + {L9, 900 * 1000}, + {L10, 800 * 1000}, + {L11, 700 * 1000}, + {L12, 600 * 1000}, + {L13, 500 * 1000}, + {L14, 400 * 1000}, + {L15, 300 * 1000}, + {L16, 200 * 1000}, {0, CPUFREQ_TABLE_END}, }; @@ -64,46 +67,55 @@ * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH, * DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 } */ - /* ARM L0: 1500 MHz */ + /* ARM L0: 1800 Mhz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L1: 1704 Mhz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L2: 1600 Mhz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L3: 1500 MHz */ { 0, 3, 7, 0, 6, 1, 2, 0 }, - /* ARM L1: 1400 MHz */ + /* ARM L4: 1400 MHz */ { 0, 3, 7, 0, 6, 1, 2, 0 }, - /* ARM L2: 1300 MHz */ + /* ARM L5: 1300 MHz */ { 0, 3, 7, 0, 5, 1, 2, 0 }, - /* ARM L3: 1200 MHz */ + /* ARM L6: 1200 MHz */ { 0, 3, 7, 0, 5, 1, 2, 0 }, - /* ARM L4: 1100 MHz */ + /* ARM L7: 1100 MHz */ { 0, 3, 6, 0, 4, 1, 2, 0 }, - /* ARM L5: 1000 MHz */ + /* ARM L8: 1000 MHz */ { 0, 2, 5, 0, 4, 1, 1, 0 }, - /* ARM L6: 900 MHz */ + /* ARM L9: 900 MHz */ { 0, 2, 5, 0, 3, 1, 1, 0 }, - /* ARM L7: 800 MHz */ + /* ARM L10: 800 MHz */ { 0, 2, 5, 0, 3, 1, 1, 0 }, - /* ARM L8: 700 MHz */ + /* ARM L11: 700 MHz */ { 0, 2, 4, 0, 3, 1, 1, 0 }, - /* ARM L9: 600 MHz */ + /* ARM L12: 600 MHz */ { 0, 2, 4, 0, 3, 1, 1, 0 }, - /* ARM L10: 500 MHz */ + /* ARM L13: 500 MHz */ { 0, 2, 4, 0, 3, 1, 1, 0 }, - /* ARM L11: 400 MHz */ + /* ARM L14: 400 MHz */ { 0, 2, 4, 0, 3, 1, 1, 0 }, - /* ARM L12: 300 MHz */ + /* ARM L15: 300 MHz */ { 0, 2, 4, 0, 2, 1, 1, 0 }, - /* ARM L13: 200 MHz */ + /* ARM L16: 200 MHz */ { 0, 1, 3, 0, 1, 1, 1, 0 }, }; @@ -113,46 +125,56 @@ * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH, * DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 } */ - /* ARM L0: 1500 MHz */ + + /* ARM L0: 1800 Mhz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L1: 1704 Mhz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L2: 1600 Mhz */ + { 0, 3, 7, 0, 6, 1, 2, 0 }, + + /* ARM L3: 1500 MHz */ { 0, 3, 7, 0, 6, 1, 2, 0 }, - /* ARM L1: 1400 MHz */ + /* ARM L4: 1400 MHz */ { 0, 3, 7, 0, 6, 1, 2, 0 }, - /* ARM L2: 1300 MHz */ + /* ARM L5: 1300 MHz */ { 0, 3, 7, 0, 5, 1, 2, 0 }, - /* ARM L3: 1200 MHz */ + /* ARM L6: 1200 MHz */ { 0, 3, 7, 0, 5, 1, 2, 0 }, - /* ARM L4: 1100 MHz */ + /* ARM L7: 1100 MHz */ { 0, 3, 6, 0, 4, 1, 2, 0 }, - /* ARM L5: 1000 MHz */ + /* ARM L8: 1000 MHz */ { 0, 2, 5, 0, 4, 1, 1, 0 }, - /* ARM L6: 900 MHz */ + /* ARM L9: 900 MHz */ { 0, 2, 5, 0, 3, 1, 1, 0 }, - /* ARM L7: 800 MHz */ + /* ARM L10: 800 MHz */ { 0, 2, 5, 0, 3, 1, 1, 0 }, - /* ARM L8: 700 MHz */ + /* ARM L11: 700 MHz */ { 0, 2, 4, 0, 3, 1, 1, 0 }, - /* ARM L9: 600 MHz */ + /* ARM L12: 600 MHz */ { 0, 2, 4, 0, 3, 1, 1, 0 }, - /* ARM L10: 500 MHz */ + /* ARM L13: 500 MHz */ { 0, 2, 4, 0, 3, 1, 1, 0 }, - /* ARM L11: 400 MHz */ + /* ARM L14: 400 MHz */ { 0, 2, 4, 0, 3, 1, 1, 0 }, - /* ARM L12: 300 MHz */ + /* ARM L15: 300 MHz */ { 0, 2, 4, 0, 2, 1, 1, 0 }, - /* ARM L13: 200 MHz */ + /* ARM L16: 200 MHz */ { 0, 1, 3, 0, 1, 1, 1, 0 }, }; @@ -160,46 +182,55 @@ /* Clock divider value for following * { DIVCOPY, DIVHPM } */ - /* ARM L0: 1500 MHz */ + /* ARM L0: 1800 Mhz */ + { 6, 0 }, + + /* ARM L1: 1704 Mhz */ + { 6, 0 }, + + /* ARM L2: 1600 Mhz */ + { 6, 0 }, + + /* ARM L3: 1500 MHz */ { 6, 0 }, - /* ARM L1: 1400 MHz */ + /* ARM L4: 1400 MHz */ { 6, 0 }, - /* ARM L2: 1300 MHz */ + /* ARM L5: 1300 MHz */ { 5, 0 }, - /* ARM L3: 1200 MHz */ + /* ARM L6: 1200 MHz */ { 5, 0 }, - /* ARM L4: 1100 MHz */ + /* ARM L7: 1100 MHz */ { 4, 0 }, - /* ARM L5: 1000 MHz */ + /* ARM L8: 1000 MHz */ { 4, 0 }, - /* ARM L6: 900 MHz */ + /* ARM L9: 900 MHz */ { 3, 0 }, - /* ARM L7: 800 MHz */ + /* ARM L10: 800 MHz */ { 3, 0 }, - /* ARM L8: 700 MHz */ + /* ARM L11: 700 MHz */ { 3, 0 }, - /* ARM L9: 600 MHz */ + /* ARM L12: 600 MHz */ { 3, 0 }, - /* ARM L10: 500 MHz */ + /* ARM L13: 500 MHz */ { 3, 0 }, - /* ARM L11: 400 MHz */ + /* ARM L14: 400 MHz */ { 3, 0 }, - /* ARM L12: 300 MHz */ + /* ARM L15: 300 MHz */ { 3, 0 }, - /* ARM L13: 200 MHz */ + /* ARM L16: 200 MHz */ { 3, 0 }, }; @@ -207,96 +238,130 @@ /* Clock divider value for following * { DIVCOPY, DIVHPM, DIVCORES } */ - /* ARM L0: 1500 MHz */ + /* ARM L0: 1800 Mhz */ + { 6, 0, 7 }, + + /* ARM L1: 1704 Mhz */ + { 6, 0, 7 }, + + /* ARM L2: 1600 Mhz */ + { 6, 0, 7 }, + + /* ARM L3: 1500 MHz */ { 6, 0, 7 }, - /* ARM L1: 1400 MHz */ + /* ARM L4: 1400 MHz */ { 6, 0, 6 }, - /* ARM L2: 1300 MHz */ + /* ARM L5: 1300 MHz */ { 5, 0, 6 }, - /* ARM L3: 1200 MHz */ + /* ARM L6: 1200 MHz */ { 5, 0, 5 }, - /* ARM L4: 1100 MHz */ + /* ARM L7: 1100 MHz */ { 4, 0, 5 }, - /* ARM L5: 1000 MHz */ + /* ARM L8: 1000 MHz */ { 4, 0, 4 }, - /* ARM L6: 900 MHz */ + /* ARM L9: 900 MHz */ { 3, 0, 4 }, - /* ARM L7: 800 MHz */ + /* ARM L10: 800 MHz */ { 3, 0, 3 }, - /* ARM L8: 700 MHz */ + /* ARM L11: 700 MHz */ { 3, 0, 3 }, - /* ARM L9: 600 MHz */ + /* ARM L12: 600 MHz */ { 3, 0, 2 }, - /* ARM L10: 500 MHz */ + /* ARM L13: 500 MHz */ { 3, 0, 2 }, - /* ARM L11: 400 MHz */ + /* ARM L14: 400 MHz */ { 3, 0, 1 }, - /* ARM L12: 300 MHz */ + /* ARM L15: 300 MHz */ { 3, 0, 1 }, - /* ARM L13: 200 MHz */ + /* ARM L16: 200 MHz */ { 3, 0, 0 }, }; static unsigned int exynos4x12_apll_pms_table[CPUFREQ_LEVEL_END] = { - /* APLL FOUT L0: 1500 MHz */ + /* APLL FOUT L0: 1800 Mhz */ + ((300 << 16) | (4 << 8) | (0x0)), + + /* APLL FOUT L1: 1704 Mhz */ + ((213 << 16) | (3 << 8) | (0x0)), + + /* APLL FOUT L2: 1600 Mhz */ + ((200 << 16) | (3 << 8) | (0x0)), + + /* APLL FOUT L3: 1500 MHz */ ((250 << 16) | (4 << 8) | (0x0)), - /* APLL FOUT L1: 1400 MHz */ + /* APLL FOUT L4: 1400 MHz */ ((175 << 16) | (3 << 8) | (0x0)), - /* APLL FOUT L2: 1300 MHz */ + /* APLL FOUT L5: 1300 MHz */ ((325 << 16) | (6 << 8) | (0x0)), - /* APLL FOUT L3: 1200 MHz */ + /* APLL FOUT L6: 1200 MHz */ ((200 << 16) | (4 << 8) | (0x0)), - /* APLL FOUT L4: 1100 MHz */ + /* APLL FOUT L7: 1100 MHz */ ((275 << 16) | (6 << 8) | (0x0)), - /* APLL FOUT L5: 1000 MHz */ + /* APLL FOUT L8: 1000 MHz */ ((125 << 16) | (3 << 8) | (0x0)), - /* APLL FOUT L6: 900 MHz */ + /* APLL FOUT L9: 900 MHz */ ((150 << 16) | (4 << 8) | (0x0)), - /* APLL FOUT L7: 800 MHz */ + /* APLL FOUT L10: 800 MHz */ ((100 << 16) | (3 << 8) | (0x0)), - /* APLL FOUT L8: 700 MHz */ + /* APLL FOUT L11: 700 MHz */ ((175 << 16) | (3 << 8) | (0x1)), - /* APLL FOUT L9: 600 MHz */ + /* APLL FOUT L12: 600 MHz */ ((200 << 16) | (4 << 8) | (0x1)), - /* APLL FOUT L10: 500 MHz */ + /* APLL FOUT L13: 500 MHz */ ((125 << 16) | (3 << 8) | (0x1)), - /* APLL FOUT L11 400 MHz */ + /* APLL FOUT L14: 400 MHz */ ((100 << 16) | (3 << 8) | (0x1)), - /* APLL FOUT L12: 300 MHz */ + /* APLL FOUT L15: 300 MHz */ ((200 << 16) | (4 << 8) | (0x2)), - /* APLL FOUT L13: 200 MHz */ + /* APLL FOUT L16: 200 MHz */ ((100 << 16) | (3 << 8) | (0x2)), }; static const unsigned int asv_voltage_4x12[CPUFREQ_LEVEL_END] = { - 1350000, 1287500, 1250000, 1187500, 1137500, 1087500, 1037500, - 1000000, 987500, 975000, 950000, 925000, 900000, 900000 + 1400000, // L0 + 1375000, // L1 + 1300000, // L2 + 1300000, // L3 + 1225000, // L4 + 1150000, // L5 + 1100000, // L6 + 1050000, // L7 + 1012500, // L8 + 987500, // L9 + 975000, // L10 + 962500, // L11 + 950000, // L12 + 925000, // L13 + 900000, // L14 + 900000, // L15 + 800000, // L16 + }; static void exynos4x12_set_clkdiv(unsigned int div_index) @@ -420,11 +485,23 @@ { unsigned int i; - max_support_idx = L1; - - /* Not supported */ - exynos4x12_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID; - + #ifdef CONFIG_EXYNOS4X12_1800MHZ_SUPPORT + max_support_idx = L0; + #elif defined(CONFIG_EXYNOS4X12_1704MHZ_SUPPORT) + max_support_idx = L1; + exynos4x12_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID; + #elif defined(CONFIG_EXYNOS4X12_1600MHZ_SUPPORT) + max_support_idx = L2; + exynos4x12_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID; + exynos4x12_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID; + #else + max_support_idx = L4; + exynos4x12_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID; + exynos4x12_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID; + exynos4x12_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID; + exynos4x12_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID; + #endif + for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++) exynos4x12_volt_table[i] = asv_voltage_4x12[i]; } diff -ruN linux/drivers/cpufreq/Kconfig.arm linux-tmu/drivers/cpufreq/Kconfig.arm --- linux/drivers/cpufreq/Kconfig.arm 2012-10-10 09:50:54.693487379 -0300 +++ linux-tmu/drivers/cpufreq/Kconfig.arm 2012-10-12 18:18:36.000000000 -0300 @@ -71,6 +71,21 @@ This adds the CPUFreq driver for Samsung EXYNOS4X12 SoC (EXYNOS4212 or EXYNOS4412). +config EXYNOS4X12_1600MHZ_SUPPORT + bool "Max 1600MHz CPUFREQ LEVEL" + help + Max 1.6Ghz support + +config EXYNOS4X12_1704MHZ_SUPPORT + bool "Max 1704MHz CPUFREQ LEVEL" + help + Max 1.7Ghz support + +config EXYNOS4X12_1800MHZ_SUPPORT + bool "Max 1800MHz CPUFREQ LEVEL" + help + Max 1.8Ghz support + config ARM_EXYNOS5250_CPUFREQ def_bool SOC_EXYNOS5250 help diff -ruN linux/drivers/hardkernel/hkdk_tmu.c linux-tmu/drivers/hardkernel/hkdk_tmu.c --- linux/drivers/hardkernel/hkdk_tmu.c 1969-12-31 21:00:00.000000000 -0300 +++ linux-tmu/drivers/hardkernel/hkdk_tmu.c 2012-10-12 16:21:08.000000000 -0300 @@ -0,0 +1,287 @@ +#include "hkdk_tmu.h" + +static ssize_t hkdk_tmu_sysfs_curr_temp(struct device *dev, struct device_attribute *attr, char *buf) { + + struct s_hkdk_tmu *hkdk_tmu = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", hkdk_tmu->curr_temp); +} +static DEVICE_ATTR(curr_temp, S_IRUGO, hkdk_tmu_sysfs_curr_temp, NULL); + +static void get_cur_temp(struct s_hkdk_tmu *hkdk_tmu) { + + unsigned char current_temperature_uncompensated; + unsigned char current_temperature; + + current_temperature_uncompensated = __raw_readl(hkdk_tmu->tmu_base + E_CURRENT_TEMP) & 0xff; + + current_temperature = current_temperature_uncompensated - hkdk_tmu->te1 + TMU_DC_VALUE; + + hkdk_tmu->curr_temp = (int) current_temperature; + +} + +static void tmu_work_out(struct work_struct *work) { + struct delayed_work *dw = to_delayed_work(work); + + struct s_hkdk_tmu *hkdk_tmu = container_of(dw, struct s_hkdk_tmu, tmu_work); + + enable_irq(hkdk_tmu->irq); + get_cur_temp(hkdk_tmu); + if (hkdk_tmu->TMU_TEMP1 == false && hkdk_tmu->curr_temp >= TMU_TEMP1_START) { + pr_info("HKDK Stage 1 Kick In\n"); + hkdk_tmu->TMU_TEMP1 = true; + hack_setcpu_frequency(TMU_TEMP1_CPU_SPEED); + goto out; + } + if (hkdk_tmu->TMU_TEMP1 == true && hkdk_tmu->curr_temp <= TMU_TEMP1_STOP) { + pr_info("HKDK Stage 1 Kick out\n"); + hkdk_tmu->TMU_TEMP1 = false; + hack_setcpu_frequency(TMU_MAX_FREQ); + goto out; + } + if (hkdk_tmu->TMU_TEMP2 == false && hkdk_tmu->curr_temp >= TMU_TEMP2_START) { + pr_info("HKDK Stage 2 Kick in\n"); + hkdk_tmu->TMU_TEMP2 = true; + hack_setcpu_frequency(TMU_TEMP2_CPU_SPEED); + goto out; + } + if (hkdk_tmu->TMU_TEMP2 == true && hkdk_tmu->curr_temp <= TMU_TEMP2_STOP) { + pr_info("HKDK Stage 2 Kick out\n"); + hkdk_tmu->TMU_TEMP2 = false; + hack_setcpu_frequency(TMU_TEMP1_CPU_SPEED); + goto out; + } + + goto out; + out: + disable_irq_nosync(hkdk_tmu->irq); + queue_delayed_work_on(0, tmu_wq, &hkdk_tmu->tmu_work, usecs_to_jiffies(1500 * 1000)); + +} + +static irqreturn_t tmu_irq(int irq, void *id) { + struct s_hkdk_tmu *info = id; + + unsigned int status; + + disable_irq_nosync(irq); + + status = __raw_readl(info->tmu_base + INTSTAT); + pr_emerg("HKDK TMU: IRQ STATUS -> %d", status); + + if (status & INTSTAT_RISE0) { + pr_emerg("Throttling interrupt occured!!!!\n"); + __raw_writel(INTCLEAR_RISE0, info->tmu_base + INTCLEAR); + info->tmu_state = TMU_STATUS_THROTTLED; + queue_delayed_work_on(0, tmu_wq, &info->tmu_work, usecs_to_jiffies(1500 * 1000)); + } else if (status & INTSTAT_RISE1) { + pr_emerg("Warning interrupt occured!!!!\n"); + __raw_writel(INTCLEAR_RISE1, info->tmu_base + INTCLEAR); + info->tmu_state = TMU_STATUS_WARNING; + queue_delayed_work_on(0, tmu_wq, &info->tmu_work, usecs_to_jiffies(1500 * 1000)); + } else if (status & INTSTAT_RISE2) { + pr_emerg("Tripping interrupt occured!!!!\n"); + info->tmu_state = TMU_STATUS_TRIPPED; + __raw_writel(INTCLEAR_RISE2, info->tmu_base + INTCLEAR); + } else { + pr_err("%s: TMU interrupt error\n", __func__); + return -ENODEV; + } + + return IRQ_HANDLED; +}; + +static int hkdk_tmu_start(struct s_hkdk_tmu *info) { + + int te_temp, throttle_temp, warning_temp, trip_temp, cooling_temp = 0, rising_value, reg_info, con; + + // Reload efuse falue + __raw_writel(TRIMINFO_RELOAD, info->tmu_base + TRIMINFO_CON); + + // get the compensation parameter + te_temp = __raw_readl(info->tmu_base + TRIMINFO); + pr_emerg("HKDK TMU: Compensation Parameter is: %d", te_temp); + + info->te1 = te_temp & TRIM_INFO_MASK; + info->te2 = ((te_temp >> 8) & TRIM_INFO_MASK); + + if ((EFUSE_MIN_VALUE > info->te1) || (info->te1 > EFUSE_MAX_VALUE) || (info->te2 != 0)) + info->te1 = info->efuse_value; + + throttle_temp = TMU_TEMP1_START + info->te1 - TMU_DC_VALUE; + warning_temp = TMU_TEMP2_STOP + info->te1 - TMU_DC_VALUE; + trip_temp = TMU_TEMP2_START + info->te1 - TMU_DC_VALUE; + + cooling_temp = 0; + + rising_value = (throttle_temp | (warning_temp << 8) | (trip_temp << 16)); + + __raw_writel(rising_value, info->tmu_base + THD_TEMP_RISE); + __raw_writel(cooling_temp, info->tmu_base + THD_TEMP_FALL); + + /* Say Hello to Exynos */ + __raw_writel(info->slope, info->tmu_base + TMU_CON); + + pr_emerg("HKDK TMU: CPU Registers Initialization Complete"); + + reg_info = __raw_readl(info->tmu_base + THD_TEMP_RISE); + pr_emerg("HKDK TMU: Rising Threshold: %x", reg_info); + + __raw_writel(INTCLEARALL, info->tmu_base + INTCLEAR); + + con = __raw_readl(info->tmu_base + TMU_CON); + con |= (MUX_ADDR_VALUE << 20 | CORE_EN); + + __raw_writel(con, info->tmu_base + TMU_CON); + + // Enable Interrupts + __raw_writel(INTEN_RISE0 | INTEN_RISE1 | INTEN_RISE2, + info->tmu_base + INTEN); + + return 0; +} + +static int hkdk_tmu_initialize(struct platform_device *pdev) { + + struct s_hkdk_tmu *info = platform_get_drvdata(pdev); + int en; + + // pr_emerg("HKDK TMU: TMU Base: %d TMU_STATUS: %d\n", info->tmu_base, TMU_STATUS); + + en = (__raw_readl(info->tmu_base + TMU_STATUS) & 0x1); + + if (!en) { + pr_emerg("HKDK TMU: Failed to start the TMU driver\n"); + return -ENOENT; + } + + return hkdk_tmu_start(info); +} + +static int hkdk_tmu_probe(struct platform_device *pdev) { + + int ret, ret2; + struct resource *res; + struct s_hkdk_tmu *hkdk_tmu; + + pr_emerg("HKDK TMU: Loaded\n"); + + hkdk_tmu = kzalloc(sizeof(struct s_hkdk_tmu), GFP_KERNEL); + + if (!hkdk_tmu) { + pr_emerg("HKDK TMU: Failed to alloc memory to the structure\n"); + return -ENOMEM; + } + + hkdk_tmu->dev = &pdev->dev; + + pr_emerg("HKDK TMU: Pdev\n"); + + hkdk_tmu->efuse_value = 55; + hkdk_tmu->slope = 0x10008802; + hkdk_tmu->mode = 0; + hkdk_tmu->te1 = 55; + hkdk_tmu->TMU_TEMP1 = false; + hkdk_tmu->TMU_TEMP2 = false; + hkdk_tmu->curr_temp = 10; + hkdk_tmu->tmu_state = 0; + + pr_emerg("HKDK TMU: MAX CPU Frequency: %s", TMU_MAX_FREQ); + pr_emerg("HKDK TMU: Set TMU Status\n"); + + hkdk_tmu->irq = platform_get_irq(pdev, 0); + pr_emerg("HKDK TMU: Got IRQ"); + + if (hkdk_tmu->irq < 0) { + pr_emerg("HKDK TMU: No IRQ for TMU -> tmu irq: %d\n", hkdk_tmu->irq); + return -ENOMEM; + } + + ret = request_irq(hkdk_tmu->irq, tmu_irq, IRQF_DISABLED, "tmu interrupt", hkdk_tmu); + pr_emerg("HKDK TMU: Request IRQ\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL ) { + pr_emerg("HKDK TMU: Failed to get memory resources\n"); + return -ENOENT; + } + + hkdk_tmu->ioarea = request_mem_region(res->start, res->end-res->start+1, pdev->name); + pr_emerg("HKDK TMU: Request IO Memory\n"); + if (!(hkdk_tmu->ioarea)) + pr_emerg("HKDK TMU: Something wrong at requesting the memory region\n"); + + hkdk_tmu->tmu_base = ioremap(res->start, (res->end - res->start) + 1); + pr_emerg("HKDK TMU: Remap IO Memory\n"); + if (!(hkdk_tmu->tmu_base)) + pr_emerg("HKDK TMU: Something wrong at the io remap\n"); + + platform_set_drvdata(pdev, hkdk_tmu); + + ret2 = device_create_file(&pdev->dev, &dev_attr_curr_temp); + + hkdk_tmu_initialize(pdev); + pr_emerg("HKDK TMU: Initialized\n"); + + tmu_wq = create_freezable_workqueue("hkdk_tmu_wq"); + pr_emerg("HKDK TMU: Created Queue\n"); + + INIT_DELAYED_WORK_DEFERRABLE(&hkdk_tmu->tmu_work, tmu_work_out); + pr_emerg("HKDK TMU: INIT Queue\n"); + + queue_delayed_work_on(0, tmu_wq, &hkdk_tmu->tmu_work, usecs_to_jiffies(1500 * 1000)); + pr_emerg("HKDK TMU: Delay Work created on the queue\n"); + + return 0; +} + +static void hkdk_tmu_remove(void) { + pr_emerg("HKDK TMU Unloaded\n"); + destroy_workqueue(tmu_wq); +} + +static struct platform_driver hkdk_tmu_driver = { + .probe = hkdk_tmu_probe, + .remove = hkdk_tmu_remove, + .driver = { + .name = "hkdk_tmu", + }, +}; + +static struct resource hkdk_tmu_resource[] = { + [0] = { + .start = EXYNOS4_PA_TMU, + .end = EXYNOS4_PA_TMU + 0xFFFF -1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TMU, + .end = IRQ_TMU, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device hkdk_device_tmu = { + .name = "hkdk_tmu", + .id = -1, + .num_resources = ARRAY_SIZE(hkdk_tmu_resource), + .resource = hkdk_tmu_resource, +}; + + +static int __init tmu_driver_init(void) { + pr_emerg("HKDK TMU: Platform Device Init\n"); + platform_device_register(&hkdk_device_tmu); + pr_emerg("HKDK TMU: Platform Driver Init\n"); + return platform_driver_register(&hkdk_tmu_driver); +} + +static void __exit tmu_driver_exit(void) { + platform_driver_unregister(&hkdk_tmu_driver); +} + +module_init(tmu_driver_init); +module_exit(tmu_driver_exit); + +MODULE_DESCRIPTION("TMU hack for ODROID-X"); +MODULE_LICENSE("GPL"); diff -ruN linux/drivers/hardkernel/hkdk_tmu.h linux-tmu/drivers/hardkernel/hkdk_tmu.h --- linux/drivers/hardkernel/hkdk_tmu.h 1969-12-31 21:00:00.000000000 -0300 +++ linux-tmu/drivers/hardkernel/hkdk_tmu.h 2012-10-12 18:21:10.000000000 -0300 @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "samsung_info.h" + + +#define TMU_TEMP1_CPU_SPEED "800000" +#define TMU_TEMP2_CPU_SPEED "200000" + +#define TMU_TEMP1_START 80 +#define TMU_TEMP1_STOP 70 + +#define TMU_TEMP2_START 100 +#define TMU_TEMP2_STOP 90 + +#define ONLINE_FILE_PATH "/sys/devices/system/cpu/cpu0/online" +#define CPUFREQ_FILE_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" + +#ifdef CONFIG_EXYNOS4X12_1800MHZ_SUPPORT + #define TMU_MAX_FREQ "1800000" +#elif defined(CONFIG_EXYNOS4X12_1704MHZ_SUPPORT) + #define TMU_MAX_FREQ "1704000" +#elif defined(CONFIG_EXYNOS4X12_1600MHZ_SUPPORT) + #define TMU_MAX_FREQ "1600000" +#else + #define TMU_MAX_FREQ "1400000" +#endif + + +struct workqueue_struct *tmu_wq; + + +char *hkdk_change_cpu(char *str, const char *cpuid) { + static char buffer[50]; + strcpy(buffer, str); + buffer[27] = *cpuid; + return buffer; +} + +int hkdk_write_to_file(char *aa_text, const char *file_name) { + + struct file *fp = NULL; + char *buffer[10] = { 0 }; + mm_segment_t oldfs = { 0 }; + + memset(buffer, 0x00, sizeof(buffer)); + + sprintf(buffer, "%s", aa_text); + + fp = filp_open(file_name, O_RDWR, 0777); + if(IS_ERR(fp)) { + pr_emerg("Error while opening the file"); + return 0; + } + + oldfs = get_fs(); + set_fs(get_ds()); + + if (fp->f_mode & FMODE_WRITE ) { + int fret = fp->f_op->write(fp, (const char *)buffer, sizeof(buffer), &fp->f_pos); + return fret; + } + + set_fs(oldfs); + + if (fp) { + filp_close(fp, NULL ); + } + return 0; +} + +void hack_setcpu_frequency(char *frequency) { + /* first run, cpu0 */ + hkdk_write_to_file(frequency, (const char*) CPUFREQ_FILE_PATH); + + /* run for cpu1 */ + hkdk_write_to_file("1", hkdk_change_cpu(ONLINE_FILE_PATH, "1")); + hkdk_write_to_file(frequency, hkdk_change_cpu(CPUFREQ_FILE_PATH, "1")); + + /* run for cpu2 */ + hkdk_write_to_file("1", hkdk_change_cpu(ONLINE_FILE_PATH, "2")); + hkdk_write_to_file(frequency, hkdk_change_cpu(CPUFREQ_FILE_PATH, "2")); + + /* run for cpu3 */ + hkdk_write_to_file("1", hkdk_change_cpu(ONLINE_FILE_PATH, "3")); + hkdk_write_to_file(frequency, hkdk_change_cpu(CPUFREQ_FILE_PATH, "3")); + +} diff -ruN linux/drivers/hardkernel/Kconfig linux-tmu/drivers/hardkernel/Kconfig --- linux/drivers/hardkernel/Kconfig 1969-12-31 21:00:00.000000000 -0300 +++ linux-tmu/drivers/hardkernel/Kconfig 2012-10-12 01:08:53.967369920 -0300 @@ -0,0 +1,8 @@ +config HKDK4412_TMU + bool "Hardkernel's ODROID(X/Q) TMU Hack" + default y + help + This is hack to support TMU on ODROID-X/Q running Ubuntu. + Its strongly recommended to Enable this. + Otherwise you'll experience system freezes due to high cpu + temperature diff -ruN linux/drivers/hardkernel/Makefile linux-tmu/drivers/hardkernel/Makefile --- linux/drivers/hardkernel/Makefile 1969-12-31 21:00:00.000000000 -0300 +++ linux-tmu/drivers/hardkernel/Makefile 2012-10-12 01:08:27.747364664 -0300 @@ -0,0 +1 @@ +obj-$(CONFIG_HKDK4412_TMU) += hkdk_tmu.o diff -ruN linux/drivers/hardkernel/samsung_info.h linux-tmu/drivers/hardkernel/samsung_info.h --- linux/drivers/hardkernel/samsung_info.h 1969-12-31 21:00:00.000000000 -0300 +++ linux-tmu/drivers/hardkernel/samsung_info.h 2012-10-12 15:59:49.000000000 -0300 @@ -0,0 +1,83 @@ +/* + * samsung_info.h + * + * Created on: Oct 2, 2012 + * Author: mdrjr + */ + + +#define EXYNOS_IRQ_OFFSET (32) +#define HKDK_IRQ_Q(x) ((x) + EXYNOS_IRQ_OFFSET) +#define SPI_IRQ(x) HKDK_IRQ_Q(x+32) +#define GROUP_COMBINER(x) ((x) * 8 + SPI_IRQ(128)) +#define IRQ_COMBINER(x, y) (GROUP_COMBINER(x) + y) + +#define E_CURRENT_TEMP (0x40) +#define TMU_SAVE_NUM 10 +#define TMU_DC_VALUE 25 +#define EFUSE_MIN_VALUE 40 +#define EFUSE_MAX_VALUE 100 +#define TRIMINFO_RELOAD (1) +#define TRIMINFO_CON (0x14) +#define TRIM_INFO_MASK (0xFF) +#define INTSTAT (0x74) +#define INTSTAT_RISE0 (1) +#define INTSTAT_RISE1 (1<<4) +#define INTSTAT_RISE2 (1<<8) +#define INTSTAT_FALL0 (1<<16) +#define INTSTAT_FALL1 (1<<20) +#define INTSTAT_FALL2 (1<<24) +#define INTCLEAR_RISE0 (1) +#define INTCLEAR_RISE1 (1<<4) +#define INTCLEAR_RISE2 (1<<8) +#define INTCLEAR_FALL0 (1<<16) +#define INTCLEAR_FALL1 (1<<20) +#define INTCLEAR_FALL2 (1<<24) +#define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | INTCLEAR_RISE2 | INTCLEAR_FALL0 | INTCLEAR_FALL1 | INTCLEAR_FALL2) + +#define INTCLEAR (0x78) +#define TMU_STATUS (0x28) +#define TRIMINFO (0x0) +#define THD_TEMP_RISE (0x50) +#define THD_TEMP_FALL (0x54) +#define TMU_CON (0x20) +#define MUX_ADDR_VALUE 6 +#define CORE_EN (1) +#define INTEN_RISE0 (1) +#define INTEN_RISE1 (1<<4) +#define INTEN_RISE2 (1<<8) +#define INTEN (0x70) +#define IRQ_TMU IRQ_COMBINER(2, 4) +#define EXYNOS4_PA_TMU 0x100C0000 + +enum tmu_status_t { + TMU_STATUS_INIT = 0, + TMU_STATUS_NORMAL, + TMU_STATUS_THROTTLED, + TMU_STATUS_WARNING, + TMU_STATUS_TRIPPED, +}; + +struct s_hkdk_tmu { + int id; + void __iomem *tmu_base; + struct device *dev; + struct resource *ioarea; + int irq; + unsigned int efuse_value; + unsigned int slope; + int mode; + unsigned int te1; + unsigned int te2; + int tmu_state; + + struct delayed_work tmu_work; + + bool TMU_TEMP1; + bool TMU_TEMP2; + + int curr_temp; +}; + + + diff -ruN linux/drivers/Kconfig linux-tmu/drivers/Kconfig --- linux/drivers/Kconfig 2012-10-10 09:50:54.533487383 -0300 +++ linux-tmu/drivers/Kconfig 2012-10-12 01:02:09.457288826 -0300 @@ -152,4 +152,6 @@ source "drivers/pwm/Kconfig" +source "drivers/hardkernel/Kconfig" + endmenu diff -ruN linux/drivers/Makefile linux-tmu/drivers/Makefile --- linux/drivers/Makefile 2012-10-10 09:50:54.533487383 -0300 +++ linux-tmu/drivers/Makefile 2012-10-12 01:10:47.082392597 -0300 @@ -139,3 +139,6 @@ obj-$(CONFIG_MEMORY) += memory/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_VME_BUS) += vme/ + +# Hardkernel HKDK 4412 TMU +obj-$(CONFIG_HKDK4412_TMU) += hardkernel/ \ No newline at end of file