summaryrefslogtreecommitdiff
path: root/drivers/thermal/sprd_2713_thm.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/thermal/sprd_2713_thm.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/thermal/sprd_2713_thm.c')
-rw-r--r--drivers/thermal/sprd_2713_thm.c500
1 files changed, 500 insertions, 0 deletions
diff --git a/drivers/thermal/sprd_2713_thm.c b/drivers/thermal/sprd_2713_thm.c
new file mode 100644
index 00000000..cdb96ae9
--- /dev/null
+++ b/drivers/thermal/sprd_2713_thm.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * 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/kernel.h>
+#include <linux/err.h>
+#include <mach/hardware.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <linux/io.h>
+#include <soc/sprd/adi.h>
+#include "thm.h"
+#include <linux/sprd_thm.h>
+#include <mach/arch_misc.h>
+
+#define SPRD_THM_DEBUG
+#ifdef SPRD_THM_DEBUG
+#define THM_DEBUG(format, arg...) printk("sprd thm: " "@@@" format, ## arg)
+#else
+#define THM_DEBUG(format, arg...)
+#endif
+
+#define THM_CTRL (0x0000)
+#define THM_INT_CTRL (0x0004)
+#define SENSOR_CTRL (0x0020)
+#define SENSOR_DET_PERI (0x0024)
+#define SENSOR_INT_CTRL (0x0028)
+#define SENSOR_INT_STS (0x002C)
+#define SENSOR_INT_RAW_STS (0x0030)
+#define SENSOR_INT_CLR (0x0034)
+#define SENSOR_OVERHEAT_HOT_THRES (0X0040)
+#define SENSOR_HOT2NOR__HIGHOFF_THRES (0X0044)
+#define SENSOR_LOWOFF__COLD_THRES (0X0048)
+#define SENSOR_TEMPER0_READ (0x0058)
+
+#define SEN_OVERHEAT_INT_BIT (1 << 5)
+#define SEN_HOT_INT_BIT (1 << 4)
+#define SEN_HOT2NOR_INT_BIT (1 << 3)
+#define SEN_HIGHOFF_BIT (1 << 2)
+#define SEN_LOWOFF_INT_BIT (1 << 1)
+
+#define RAW_TEMP_OFFSET 8
+#define RAW_TEMP_RANGE_MSK 0x7F
+
+#define HIGH_BITS_OFFSET 4
+
+#define HIGH_TAB_SZ 8
+#define LOW_TAB_SZ 16
+
+#define HOT2NOR_RANGE 15
+#define LOCAL_SENSOR_ADDR_OFF 0x100
+#define DELAY_TEMPERATURE 3
+#define INTOFFSET 3
+
+#define TSMC_DOLPHINW4T_CHIP_ID_1 0x7715A001
+#define TSMC_DOLPHINW4T_CHIP_ID_2 0x7715A003
+#define TSMC_DOLPHINWT4T_CHIP_ID_1 0x8815A001
+
+static const short temp_search_high_40nm[HIGH_TAB_SZ] =
+ { -41, -14, 14, 41, 68, 95, 122, 150 };
+static const short temp_search_low_40nm[LOW_TAB_SZ] =
+ { 0, 2, 4, 5, 7, 8, 10, 12, 14, 15, 17, 19, 20, 22, 24, 26 };
+static const short temp_search_high_152nm[HIGH_TAB_SZ] =
+ { -45, -19, 7, 33, 59, 85, 111, 137 };
+static const short temp_search_low_152nm[LOW_TAB_SZ] =
+ { 0, 2, 3, 5, 6, 8, 10, 11, 13, 14, 16, 18, 19, 21, 22, 24 };
+
+static u32 current_trip_num = 0;
+static int arm_sen_cal_offset = 0;
+static int pmic_sen_cal_offset = 0;
+
+extern unsigned long SPRD_THM_BASE;
+extern unsigned int SPRD_THM_SIZE;
+
+static inline int __thm_reg_write(u32 reg, u16 bits, u16 clear_msk);
+static inline u32 __thm_reg_read(u32 reg);
+int sprd_thm_set_active_trip(struct sprd_thermal_zone *pzone, int trip );
+u32 sprd_thm_temp2rawdata(u32 sensor, int temp);
+
+static inline int __thm_reg_write(u32 reg, u16 bits, u16 clear_msk)
+{
+ if (reg >= SPRD_THM_BASE && reg <= (SPRD_THM_BASE + SPRD_THM_SIZE)) {
+ __raw_writel(((__raw_readl((volatile void *)reg) & ~clear_msk) | bits), (volatile void *)(reg));
+ } else if (reg >= ANA_THM_BASE && reg <= (ANA_THM_BASE + SPRD_THM_SIZE)) {
+ sci_adi_write(reg, bits, clear_msk);
+ } else {
+ printk(KERN_ERR "error thm reg0x:%x \n", reg);
+ }
+ return 0;
+}
+
+static inline u32 __thm_reg_read(u32 reg)
+{
+ if (reg >= SPRD_THM_BASE && reg <= (SPRD_THM_BASE + SPRD_THM_SIZE)) {
+ return __raw_readl((volatile void *)reg);
+ } else if (reg >= ANA_THM_BASE && reg <= (ANA_THM_BASE + SPRD_THM_SIZE)) {
+ return sci_adi_read(reg);
+ } else {
+ printk(KERN_ERR "error thm reg0x:%x \n", reg);
+ }
+ return 0;
+}
+
+int sprd_thm_set_active_trip(struct sprd_thermal_zone *pzone, int trip )
+{
+ u32 raw_temp = 0;
+ u32 local_sen_id = 0;
+ u32 local_sensor_addr = 0;
+ struct sprd_thm_platform_data *trip_tab = pzone->trip_tab;
+
+ THM_DEBUG("thm sensor id:%d, trip:%d \n", pzone->sensor_id, trip);
+ if (trip < 0 || trip > (trip_tab->num_trips - 1))
+ return -1;
+ if (trip_tab->trip_points[trip].type != THERMAL_TRIP_ACTIVE)
+ return -1;
+
+ local_sen_id = pzone->sensor_id;
+ local_sensor_addr =
+ (u32) pzone->reg_base + local_sen_id * LOCAL_SENSOR_ADDR_OFF;
+
+ //Disable sensor int
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CTRL),0,(0x7F & (~SEN_OVERHEAT_INT_BIT)));
+
+ //set hot int temp value
+ THM_DEBUG("thm sensor trip:%d, temperature:%d \n", trip, trip_tab->trip_points[trip].temp);
+ raw_temp =
+ sprd_thm_temp2rawdata(pzone->sensor_id,
+ trip_tab->trip_points[trip].temp - pmic_sen_cal_offset - INTOFFSET);
+ if (raw_temp < RAW_TEMP_RANGE_MSK) {
+ raw_temp++;
+ }
+ __thm_reg_write((local_sensor_addr + SENSOR_OVERHEAT_HOT_THRES),
+ raw_temp,
+ RAW_TEMP_RANGE_MSK);
+
+ //set Hot2Normal int temp value
+ raw_temp =
+ sprd_thm_temp2rawdata(pzone->sensor_id, trip_tab->trip_points[trip].lowoff - INTOFFSET - pmic_sen_cal_offset);
+ __thm_reg_write((local_sensor_addr + SENSOR_HOT2NOR__HIGHOFF_THRES),
+ raw_temp << RAW_TEMP_OFFSET,
+ RAW_TEMP_RANGE_MSK << RAW_TEMP_OFFSET);
+
+ //set cold int temp value
+ raw_temp =
+ sprd_thm_temp2rawdata(pzone->sensor_id, trip_tab->trip_points[trip].lowoff - INTOFFSET - pmic_sen_cal_offset);
+ __thm_reg_write((local_sensor_addr + SENSOR_LOWOFF__COLD_THRES),
+ raw_temp << RAW_TEMP_OFFSET,
+ RAW_TEMP_RANGE_MSK << RAW_TEMP_OFFSET);
+
+ THM_DEBUG("thm OVERHEAT_HOT:0x%x, HOT2NOR__HIGHOFF:0x%x, LOWOFF__COLD:0x%x\n", __thm_reg_read(local_sensor_addr + SENSOR_OVERHEAT_HOT_THRES),
+ __thm_reg_read(local_sensor_addr + SENSOR_HOT2NOR__HIGHOFF_THRES), __thm_reg_read(local_sensor_addr + SENSOR_LOWOFF__COLD_THRES));
+
+ // Restart sensor to enable new paramter
+ __thm_reg_write((local_sensor_addr + SENSOR_CTRL), 0x9, 0x9);
+
+ if (trip > 0)
+ {
+ //enable Hot int and Lowoff int
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CTRL),
+ SEN_HOT_INT_BIT | SEN_LOWOFF_INT_BIT,
+ SEN_HOT_INT_BIT | SEN_LOWOFF_INT_BIT);
+ }
+ else
+ {
+ //enable Hot int and disable LOWOFF int
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CTRL),
+ SEN_HOT_INT_BIT,
+ SEN_HOT_INT_BIT | SEN_LOWOFF_INT_BIT);
+ }
+
+ return 0;
+}
+
+u32 sprd_thm_temp2rawdata(u32 sensor, int temp)
+{
+ u32 high_bits, low_bits;
+ int i;
+ const short *high_tab;
+ const short *low_tab;
+
+ if (SPRD_ARM_SENSOR == sensor) {
+ high_tab = temp_search_high_40nm;
+ low_tab = temp_search_low_40nm;
+ } else if (SPRD_PMIC_SENSOR == sensor) {
+ high_tab = temp_search_high_152nm;
+ low_tab = temp_search_low_152nm;
+ } else {
+ printk(KERN_ERR "error thm sensor id:%d \n", sensor);
+ return 0;
+ }
+
+ if (temp < high_tab[0]) {
+ printk(KERN_ERR "temp over low temp:%d \n", temp);
+ return 0;
+ }
+
+ for (i = HIGH_TAB_SZ - 1; i >= 0; i--) {
+ if (high_tab[i] <= temp)
+ break;
+ }
+ if (i < 0) {
+ i = 0;
+ }
+ temp -= high_tab[i];
+ high_bits = i;
+
+ for (i = LOW_TAB_SZ - 1; i >= 0; i--) {
+ if (low_tab[i] <= temp)
+ break;
+ }
+ if (i < 0) {
+ i = 0;
+ }
+ low_bits = i;
+ return ((high_bits << HIGH_BITS_OFFSET) | low_bits);
+
+}
+
+int sprd_thm_rawdata2temp(u32 sensor, int rawdata)
+{
+ const short *high_tab;
+ const short *low_tab;
+
+ if (SPRD_ARM_SENSOR == sensor) {
+ high_tab = temp_search_high_40nm;
+ low_tab = temp_search_low_40nm;
+ } else if (SPRD_PMIC_SENSOR == sensor) {
+ high_tab = temp_search_high_152nm;
+ low_tab = temp_search_low_152nm;
+ } else {
+ printk(KERN_ERR "error thm sensor id:%d \n", sensor);
+ return 0;
+ }
+
+ return high_tab[(rawdata >> HIGH_BITS_OFFSET) & 0x07] +
+ low_tab[rawdata & 0x0F];
+}
+
+int sprd_thm_temp_read(struct sprd_thermal_zone *pzone)
+{
+ u32 rawdata = 0;
+ int cal_offset = 0;
+ u32 sensor = pzone->sensor_id;
+
+ if (SPRD_ARM_SENSOR == sensor) {
+ rawdata = __thm_reg_read((SPRD_THM_BASE + SENSOR_TEMPER0_READ));
+ cal_offset = arm_sen_cal_offset;
+ } else if (SPRD_PMIC_SENSOR == sensor) {
+ rawdata = __thm_reg_read(ANA_THM_BASE + SENSOR_TEMPER0_READ);
+ cal_offset = pmic_sen_cal_offset;
+ } else {
+ printk(KERN_ERR "error thm sensor id:%d \n", sensor);
+ return 0;
+ }
+ THM_DEBUG("thm sensor id:%d, cal_offset:%d, rawdata:0x%x\n", sensor,
+ cal_offset, rawdata);
+ return sprd_thm_rawdata2temp(sensor, rawdata) + cal_offset;
+}
+int sprd_thm_chip_id_check(void)
+{
+ u32 chip_id_tmp;
+
+ chip_id_tmp = sci_get_chip_id();
+
+#if !defined(CONFIG_ARCH_SCX15)
+ return 0;
+#else
+ if ((TSMC_DOLPHINW4T_CHIP_ID_1 == chip_id_tmp) || (TSMC_DOLPHINW4T_CHIP_ID_2 == chip_id_tmp) ||
+ (TSMC_DOLPHINWT4T_CHIP_ID_1 == chip_id_tmp)) {
+ //printk("Sprd thm the chip support thermal CHIP_ID:0x%x \n",chip_id_tmp);
+ return 0;
+ } else {
+ printk("Sprd thm the chip don't support thermal CHIP_ID:0x%x \n",chip_id_tmp);
+ return -1;
+ }
+#endif
+}
+int sprd_thm_hw_init(struct sprd_thermal_zone *pzone)
+{
+ u32 local_sensor_addr, base_addr = 0;
+ u32 local_sen_id = 0;
+ u32 raw_temp = 0;
+ int cal_offset = 0;
+ struct sprd_thm_platform_data *trip_tab = pzone->trip_tab;
+
+ base_addr = (u32) pzone->reg_base;
+
+ printk(KERN_NOTICE "sprd_thm_hw_init 2713_thm id:%d,base 0x%x\n",
+ pzone->sensor_id, base_addr);
+
+ if (SPRD_ARM_SENSOR == pzone->sensor_id) {
+ //ddie thm en
+ sci_glb_set(REG_AON_APB_APB_EB1, BIT_THM_EB);
+ sci_glb_set(REG_AON_APB_APB_RTC_EB,
+ (BIT_THM_RTC_EB | BIT_ARM_THMA_RTC_EB |
+ BIT_ARM_THMA_RTC_AUTO_EN));
+ cal_offset = arm_sen_cal_offset = 0;
+ local_sen_id = 0;
+ } else if (SPRD_PMIC_SENSOR == pzone->sensor_id) {
+ //adie thm en
+ sci_adi_set(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_THM_EN);
+ sci_adi_set(ANA_REG_GLB_RTC_CLK_EN,
+ BIT_RTC_THMA_AUTO_EN | BIT_RTC_THMA_EN |
+ BIT_RTC_THM_EN);
+ cal_offset = pmic_sen_cal_offset = 0;
+ local_sen_id = 0;
+ } else {
+ printk(KERN_ERR "error thm sensor id:%d \n", pzone->sensor_id);
+ return -EINVAL;
+ }
+ __thm_reg_write((base_addr + THM_CTRL), 0x1 >> local_sen_id, 0);
+ __thm_reg_write((base_addr + THM_INT_CTRL), 0x3, 0); //enable top int
+
+ local_sensor_addr = base_addr + local_sen_id * LOCAL_SENSOR_ADDR_OFF;
+
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CTRL), 0, ~0); //disable all int
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CLR), ~0, 0); //clr all int
+
+ //set int
+ if (trip_tab->num_trips > 0) {
+#if 0
+ //set hot
+ if (trip_tab->trip_points[0].type == THERMAL_TRIP_ACTIVE) {
+ raw_temp =
+ sprd_thm_temp2rawdata(pzone->sensor_id,
+ trip_tab->trip_points[0].
+ temp - cal_offset);
+ if (raw_temp < RAW_TEMP_RANGE_MSK) {
+ raw_temp++;
+ }
+ //set hot int temp value
+ __thm_reg_write((local_sensor_addr +
+ SENSOR_OVERHEAD_HOT_THRES), raw_temp,
+ RAW_TEMP_RANGE_MSK);
+ raw_temp =
+ sprd_thm_temp2rawdata(pzone->sensor_id,
+ trip_tab->trip_points[0].
+ temp - HOT2NOR_RANGE -
+ cal_offset);
+ //set hot2nor int temp value
+ __thm_reg_write((local_sensor_addr +
+ SENSOR_HOT2NOR__HIGHOFF_THRES),
+ raw_temp << RAW_TEMP_OFFSET,
+ RAW_TEMP_RANGE_MSK << RAW_TEMP_OFFSET);
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CTRL), //enable int
+ SEN_HOT2NOR_INT_BIT | SEN_HOT_INT_BIT,
+ 0);
+ }
+#else
+ current_trip_num = 0;
+ sprd_thm_set_active_trip(pzone,current_trip_num);
+#endif
+ //set overheat
+ if (trip_tab->trip_points[trip_tab->num_trips - 1].type ==
+ THERMAL_TRIP_CRITICAL) {
+ raw_temp =
+ sprd_thm_temp2rawdata(pzone->sensor_id,
+ trip_tab->trip_points
+ [trip_tab->num_trips -
+ 1].temp - cal_offset);
+ //set overheat int temp value
+ __thm_reg_write((local_sensor_addr +
+ SENSOR_OVERHEAT_HOT_THRES),
+ raw_temp << RAW_TEMP_OFFSET,
+ RAW_TEMP_RANGE_MSK << RAW_TEMP_OFFSET);
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CTRL), //enable int
+ SEN_OVERHEAT_INT_BIT, 0);
+ }
+ }
+
+ printk(KERN_NOTICE "sprd_thm_hw_init addr 0x:%x,int ctrl 0x%x\n",
+ local_sensor_addr,
+ __thm_reg_read((local_sensor_addr + SENSOR_INT_CTRL)));
+
+ // Set sensor det period is 2.25S
+ __thm_reg_write((local_sensor_addr + SENSOR_DET_PERI), 0x2000, 0x2000);
+ __thm_reg_write((local_sensor_addr + SENSOR_CTRL), 0x101, 0x101);
+
+ // Start the sensor by set Sen_set_rdy(bit3)
+ __thm_reg_write((local_sensor_addr + SENSOR_CTRL), 0x8, 0x8);
+
+ return 0;
+
+}
+
+int sprd_thm_hw_disable_sensor(struct sprd_thermal_zone *pzone)
+{
+ int ret = 0;
+ // Sensor minitor disable
+ if(SPRD_ARM_SENSOR == pzone->sensor_id){
+ __thm_reg_write((u32)(pzone->reg_base + SENSOR_CTRL), 0x0, 0x8);
+ __thm_reg_write((u32)(pzone->reg_base + SENSOR_CTRL), 0x00, 0x01);
+ }
+ return ret;
+
+}
+
+int sprd_thm_hw_enable_sensor(struct sprd_thermal_zone *pzone)
+{
+ int ret = 0;
+ // Sensor minitor enable
+ THM_DEBUG("sprd_2713_thm enable sensor sensor_ID:0x%x \n",pzone->sensor_id);
+ if(SPRD_ARM_SENSOR == pzone->sensor_id){
+ __thm_reg_write((u32)(pzone->reg_base + SENSOR_CTRL), 0x01, 0x01);
+ __thm_reg_write((u32)(pzone->reg_base + SENSOR_CTRL), 0x8, 0x8);
+ }
+ return ret;
+
+}
+
+
+u16 int_ctrl_reg[SPRD_MAX_SENSOR];
+int sprd_thm_hw_suspend(struct sprd_thermal_zone *pzone)
+{
+ u32 local_sen_id = 0;
+ u32 local_sensor_addr;
+ int ret = 0;
+
+ local_sensor_addr =
+ (u32) pzone->reg_base + local_sen_id * LOCAL_SENSOR_ADDR_OFF;
+ int_ctrl_reg[pzone->sensor_id] = __thm_reg_read((local_sensor_addr + SENSOR_INT_CTRL));
+
+ sprd_thm_hw_disable_sensor(pzone);
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CTRL), 0, ~0); //disable all int
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CLR), ~0, 0); //clr all int
+ return ret;
+}
+int sprd_thm_hw_resume(struct sprd_thermal_zone *pzone)
+{
+ u32 local_sen_id = 0;
+ u32 local_sensor_addr;
+ int ret = 0;
+
+ local_sensor_addr =
+ (u32) pzone->reg_base + local_sen_id * LOCAL_SENSOR_ADDR_OFF;
+
+ sprd_thm_hw_enable_sensor(pzone);
+ if(SPRD_ARM_SENSOR == pzone->sensor_id){
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CLR), ~0, 0); //clr all int
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CTRL), int_ctrl_reg[pzone->sensor_id], ~0); //enable int of saved
+ __thm_reg_write((local_sensor_addr + SENSOR_CTRL), 0x9, 0);
+ }
+ return ret;
+}
+
+int sprd_thm_hw_irq_handle(struct sprd_thermal_zone *pzone)
+{
+ u32 local_sen_id = 0;
+ u32 local_sensor_addr;
+ u32 int_sts;
+ int ret = 0;
+ u32 overhead_hot_tem_cur = 0;
+ struct sprd_thm_platform_data *trip_tab = pzone->trip_tab;
+ int temp;
+
+ local_sensor_addr =
+ (u32) pzone->reg_base + local_sen_id * LOCAL_SENSOR_ADDR_OFF;
+ int_sts = __thm_reg_read(local_sensor_addr + SENSOR_INT_STS);
+
+ __thm_reg_write((local_sensor_addr + SENSOR_INT_CLR), int_sts, ~0); //CLR INT
+ temp = sprd_thm_temp_read(pzone->sensor_id);
+
+ printk("sprd_thm_hw_irq_handle --------@@@------id:%d, int_sts :0x%x \n",
+ pzone->sensor_id, int_sts);
+ printk("sprd_thm_hw_irq_handle ------$$$--------temp:%d\n", temp);
+
+ overhead_hot_tem_cur = __thm_reg_read((local_sensor_addr + SENSOR_OVERHEAT_HOT_THRES))
+ & RAW_TEMP_RANGE_MSK;
+
+ if (int_sts & SEN_HOT_INT_BIT){
+ if ((current_trip_num) >= (trip_tab->num_trips - 2)){
+ current_trip_num = trip_tab->num_trips - 2;
+ return ret;
+ }
+ if (temp >= trip_tab->trip_points[current_trip_num].temp - INTOFFSET){
+ current_trip_num++;
+ sprd_thm_set_active_trip(pzone,current_trip_num);
+ }
+ }else if (int_sts & SEN_LOWOFF_INT_BIT){
+ if (temp < trip_tab->trip_points[current_trip_num].lowoff + INTOFFSET){
+ current_trip_num--;
+ sprd_thm_set_active_trip(pzone,current_trip_num);
+ }
+ }else{
+ THM_DEBUG("sprd_thm_hw_irq_handle NOT a HOT or LOWOFF interrupt \n");
+ return ret;
+ }
+ return ret;
+}