diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/thermal/sprd_board_sensor.c | |
Diffstat (limited to 'drivers/thermal/sprd_board_sensor.c')
| -rw-r--r-- | drivers/thermal/sprd_board_sensor.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/drivers/thermal/sprd_board_sensor.c b/drivers/thermal/sprd_board_sensor.c new file mode 100644 index 00000000..ed730e1f --- /dev/null +++ b/drivers/thermal/sprd_board_sensor.c @@ -0,0 +1,535 @@ +/* + * 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 <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 <soc/sprd/arch_misc.h> +#include <soc/sprd/hardware.h> +#include "thermal_core.h" +#include <soc/sprd/adc.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/thermal.h> +#include <linux/slab.h> + +//#define SPRD_THM_BOARD_DEBUG +#ifdef SPRD_THM_BOARD_DEBUG +#define THM_BOARD_DEBUG(format, arg...) printk("sprd board thm: " "@@@" format, ## arg) +#else +#define THM_BOARD_DEBUG(format, arg...) +#endif + +#define THM_SENOR_NUM 8 +extern int sprd_thermal_init(struct sprd_thermal_zone *pzone); +extern void sprd_thermal_remove(struct sprd_thermal_zone *pzone); + + +struct sprd_board_sensor_config *pthm_config; +//extern uint16_t sprdchg_bat_adc_to_vol(uint16_t adcvalue); + +static int sprdthm_read_temp_adc(void) +{ +#define SAMPLE_NUM 15 + int cnt = pthm_config->temp_adc_sample_cnt; + + if (cnt > SAMPLE_NUM) { + cnt = SAMPLE_NUM; + } else if (cnt < 1) { + cnt = 1; + } + + if (pthm_config->temp_support) { + int ret, i, j, temp; + int adc_val[cnt]; + struct adc_sample_data data = { + .channel_id = pthm_config->temp_adc_ch, + .channel_type = 0, /*sw */ + .hw_channel_delay = 0, /*reserved */ + .scale = pthm_config->temp_adc_scale, /*small scale */ + .pbuf = &adc_val[0], + .sample_num = cnt, + .sample_bits = 1, + .sample_speed = 0, /*quick mode */ + .signal_mode = 0, /*resistance path */ + }; + + ret = sci_adc_get_values(&data); + WARN_ON(0 != ret); + + for (j = 1; j <= cnt - 1; j++) { + for (i = 0; i < cnt - j; i++) { + if (adc_val[i] > adc_val[i + 1]) { + temp = adc_val[i]; + adc_val[i] = adc_val[i + 1]; + adc_val[i + 1] = temp; + } + } + } + THM_BOARD_DEBUG("sprdthm: channel:%d,sprdthm_read_temp_adc:%d\n", + data.channel_id, adc_val[cnt / 2]); + return adc_val[cnt / 2]; + } else { + return 3000; + } +} + +static int sprdthm_interpolate(int x, int n, struct sprdboard_table_data *tab) +{ + int index; + int y; + + if (x >= tab[0].x) + y = tab[0].y; + else if (x <= tab[n - 1].x) + y = tab[n - 1].y; + else { + /* find interval */ + for (index = 1; index < n; index++) + if (x > tab[index].x) + break; + /* interpolate */ + y = (tab[index - 1].y - tab[index].y) * (x - tab[index].x) + * 2 / (tab[index - 1].x - tab[index].x); + y = (y + 1) / 2; + y += tab[index].y; + } + return y; +} +static uint16_t sprdthm_adc_to_vol(uint16_t channel, int scale, + uint16_t adcvalue) +{ + uint32_t result; + uint32_t vthm_vol = adc2vbat(adcvalue,scale); + uint32_t m, n; + uint32_t thm_numerators, thm_denominators; + uint32_t numerators, denominators; + + sci_adc_get_vol_ratio(ADC_CHANNEL_VBAT, 0, &thm_numerators, + &thm_denominators); + sci_adc_get_vol_ratio(channel, scale, &numerators, &denominators); + + ///v1 = vbat_vol*0.268 = vol_bat_m * r2 /(r1+r2) + n = thm_denominators * numerators; + m = vthm_vol * thm_numerators * (denominators); + result = (m + n / 2) / n; + return result; +} + +static int sprdthm_search_temp_tab(int val) +{ + return sprdthm_interpolate(val, pthm_config->temp_tab_size, + pthm_config->temp_tab); +} + +static int sprd_board_thm_get_reg_base(struct sprd_thermal_zone *pzone ,struct resource *regs) +{ + return 0; +} +static int sprd_board_thm_set_active_trip(struct sprd_thermal_zone *pzone, int trip ) +{ + return 0; +} + +static unsigned long sprd_board_thm_temp_read(struct sprd_thermal_zone *pzone) +{ + int temp; + if (pthm_config->temp_support) { + int val = sprdthm_read_temp_adc(); + //voltage mode + if (pthm_config->temp_table_mode) { + val =sprdthm_adc_to_vol(pthm_config->temp_adc_ch, + pthm_config->temp_adc_scale, val); + THM_BOARD_DEBUG("sprdthm: sprdthm_read_temp voltage:%d\n", val); + } + THM_BOARD_DEBUG("sprd_board_thm_temp_read y=%d\n",sprdthm_search_temp_tab(val)); + temp = sprdthm_search_temp_tab(val); + printk("sensor id:%d,rawdata:0x%x, temp:%lu\n", pzone->sensor_id, val, temp*1000); + return temp*1000; + } else { + return -35000; + } + +} + +static int sprd_board_thm_hw_init(struct sprd_thermal_zone *pzone) +{ + THM_BOARD_DEBUG("sprd_board_thm_hw_init\n"); + pthm_config=pzone->sensor_config; + + return 0; +} + + +static int sprd_board_thm_hw_suspend(struct sprd_thermal_zone *pzone) +{ + THM_BOARD_DEBUG("sprd_board_thm_hw_suspend\n"); + + return 0; +} +static int sprd_board_thm_hw_resume(struct sprd_thermal_zone *pzone) +{ + + THM_BOARD_DEBUG("sprd_board_thm_hw_resume\n"); + + return 0; +} +#ifdef THM_TEST +static int sprd_board_thm_trip_set(struct sprd_thermal_zone *pzone,int trip) +{ + THM_BOARD_DEBUG("sprd_thm_trip_set trip=%d, temp=%ld,lowoff =%ld\n", + trip,pzone->trip_tab->trip_points[trip].temp,pzone->trip_tab->trip_points[trip].lowoff); + return 0; +} +#endif + +static int sprd_board_thm_get_trend(struct sprd_thermal_zone *pzone, int trip, enum thermal_trend *ptrend) +{ + *ptrend = pzone->trend_val; + return 0; +} + +static int sprd_board_thm_get_hyst(struct sprd_thermal_zone *pzone, int trip, unsigned long *physt) +{ + struct sprd_thm_platform_data *trip_tab = pzone->trip_tab; + if (trip >= trip_tab->num_trips - 2){ + *physt = 0; + }else{ + *physt = trip_tab->trip_points[trip].temp - trip_tab->trip_points[trip + 1].lowoff; + } + return 0; +} + +struct thm_handle_ops sprd_boardthm_ops = +{ + .hw_init = sprd_board_thm_hw_init, + .get_reg_base = sprd_board_thm_get_reg_base, + .read_temp = sprd_board_thm_temp_read, + .get_trend = sprd_board_thm_get_trend, + .get_hyst = sprd_board_thm_get_hyst, + .suspend = sprd_board_thm_hw_suspend, + .resume = sprd_board_thm_hw_resume, + .trip_debug_set = sprd_board_thm_trip_set, +}; + +#ifdef CONFIG_OF +static struct sprd_thm_platform_data *thermal_detect_parse_dt( + struct device *dev) +{ + struct sprd_thm_platform_data *pdata; + struct device_node *np = dev->of_node; + u32 trip_points_critical,trip_num; + char prop_name[32]; + const char *tmp_str; + u32 tmp_data,tmp_lowoff; + int ret,i ,j = 0; + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for platform data\n"); + return NULL; + } + ret = of_property_read_u32(np, "trip-points-critical", &trip_points_critical); + if(ret){ + dev_err(dev, "fail to get trip_points_critical\n"); + goto fail; + } + ret = of_property_read_u32(np, "trip-num", &trip_num); + if(ret){ + dev_err(dev, "fail to get trip_num\n"); + goto fail; + } + for (i = 0; i <trip_num-1; ++i) { + sprintf(prop_name, "trip%d-temp-active", i); + if (of_property_read_u32(np, prop_name, &tmp_data)){ + dev_err(dev, "fail to get trip%d-temp-active\n",i); + goto fail; + } + + pdata->trip_points[i].temp = tmp_data; + sprintf(prop_name, "trip%d-temp-lowoff", i); + if (of_property_read_u32(np, prop_name, &tmp_lowoff)){ + dev_err(dev, "fail to get trip%d-temp-lowoff\n",i); + goto fail; + } + pdata->trip_points[i].lowoff = tmp_lowoff; + sprintf(prop_name, "trip%d-type", i); + if (of_property_read_string(np, prop_name, &tmp_str)) + goto fail; + + if (!strcmp(tmp_str, "active")) + pdata->trip_points[i].type = THERMAL_TRIP_ACTIVE; + else if (!strcmp(tmp_str, "passive")) + pdata->trip_points[i].type = THERMAL_TRIP_PASSIVE; + else if (!strcmp(tmp_str, "hot")) + pdata->trip_points[i].type = THERMAL_TRIP_HOT; + else if (!strcmp(tmp_str, "critical")) + pdata->trip_points[i].type = THERMAL_TRIP_CRITICAL; + else + goto fail; + + sprintf(prop_name, "trip%d-cdev-num", i); + if (of_property_read_u32(np, prop_name, &tmp_data)) + goto fail; + for (j = 0; j < tmp_data; j++) { + sprintf(prop_name, "trip%d-cdev-name%d", i, j); + if (of_property_read_string(np, prop_name, &tmp_str)) + goto fail; + strcpy(pdata->trip_points[i].cdev_name[j], tmp_str); + dev_info(dev,"cdev name: %s \n", pdata->trip_points[i].cdev_name[j]); + } + dev_info(dev, "trip[%d] temp: %lu lowoff: %lu\n", + i, pdata->trip_points[i].temp, pdata->trip_points[i].lowoff); + } + pdata->trip_points[i].temp = trip_points_critical; + pdata->trip_points[i].type = THERMAL_TRIP_CRITICAL; + dev_info(dev, "trip[%d] temp: %lu \n", + i, pdata->trip_points[i].temp); + pdata->num_trips = trip_num; + + return pdata; + +fail: + kfree(pdata); + return NULL; + +} + +static struct sprd_board_sensor_config *sprdboard_thermal_parse_dt( + struct device *dev) +{ + struct sprd_board_sensor_config *pconfig; + struct device_node *np = dev->of_node; + int ret,i; + int temp; + int temp_adc_ch,temp_adc_scale,temp_adc_sample_cnt; + int temp_table_mode,temp_tab_size,temp_support; + pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); + if (!pconfig) { + dev_err(dev, "could not allocate memory for platform data\n"); + return NULL; + } + ret =of_property_read_u32(np, "temp-adc-ch",&temp_adc_ch); + if(ret){ + dev_err(dev, "sprd_thermal_probe No temp-adc-ch\n"); + goto fail; + } + ret =of_property_read_u32(np, "temp-adc-scale",&temp_adc_scale); + if(ret){ + dev_err(dev, "sprd_thermal_probe No temp_adc_scale\n"); + goto fail; + } + ret =of_property_read_u32(np, "temp-adc-sample-cnt",&temp_adc_sample_cnt); + if(ret){ + dev_err(dev, "fail to get temp_adc_sample_cnt\n"); + goto fail; + } + ret =of_property_read_u32(np, "temp-table-mode",&temp_table_mode); + if(ret){ + dev_err(dev, "fail to get temp_table_mode\n"); + goto fail; + } + ret =of_property_read_u32(np, "temp-tab-size",&temp_tab_size); + if(ret){ + dev_err(dev, "fail to get temp_tab_size\n"); + goto fail; + } + ret =of_property_read_u32(np, "temp-support",&temp_support); + if(ret){ + dev_err(dev, "fail to get temp_support\n"); + goto fail; + } + pconfig->temp_adc_ch=temp_adc_ch; + pconfig->temp_adc_sample_cnt=temp_adc_sample_cnt; + pconfig->temp_adc_scale=temp_adc_scale; + pconfig->temp_table_mode=temp_table_mode; + pconfig->temp_support=temp_support; + pconfig->temp_tab_size=temp_tab_size; + pconfig->temp_tab = kzalloc(sizeof(struct sprdboard_table_data) * + pconfig->temp_tab_size-1, GFP_KERNEL); + for (i = 0; i < pconfig->temp_tab_size-1; i++) { + ret = of_property_read_u32_index(np, "temp-tab-val", i, + &pconfig->temp_tab[i].x); + if(ret){ + dev_err(dev, "fail to get temp-tab-va\n"); + goto fail; + } + ret = of_property_read_u32_index(np, "temp-tab-temp", i, &temp); + if(ret){ + dev_err(dev, "fail to get temp-tab-temp\n"); + goto fail; + } + pconfig->temp_tab[i].y = temp - 1000; + } + return pconfig; +fail: + kfree(pconfig); + return NULL; +} + +#endif +static int sprd_board_thm_probe(struct platform_device *pdev) +{ + struct sprd_thermal_zone *pzone = NULL; + struct sprd_thm_platform_data *ptrips = NULL; + struct sprd_board_sensor_config *pconfig = NULL; + const char *thm_name; + int ret,temp_interval,sensor_id; + printk("sprd_thermal_probe---------start\n"); +#ifdef CONFIG_OF + struct device_node *np = pdev->dev.of_node; +#endif + +#ifdef CONFIG_OF + if (!np) { + dev_err(&pdev->dev, "device node not found\n"); + return -EINVAL; + } + pconfig = sprdboard_thermal_parse_dt(&pdev->dev); + if (!pconfig){ + dev_err(&pdev->dev, "not found ptrips\n"); + return -EINVAL; + } + ptrips = thermal_detect_parse_dt(&pdev->dev); +#else + ptrips = dev_get_platdata(&pdev->dev); +#endif + if (!ptrips){ + dev_err(&pdev->dev, "not found ptrips\n"); + return -EINVAL; + } + pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL); + if (!pzone){ + kfree(pconfig); + kfree(ptrips); + return -ENOMEM; + } + mutex_init(&pzone->th_lock); + mutex_lock(&pzone->th_lock); + printk("sprd_ddie_thm_probe start\n"); +#if 0 + pdev->id = of_alias_get_id(np, "thmzone"); + printk(KERN_INFO " sprd_thermal_probe id:%d\n", pdev->id); + if (unlikely(pdev->id < 0 || pdev->id >= THM_SENOR_NUM)) { + dev_err(&pdev->dev, "does not support id %d\n", pdev->id); + return -ENXIO; + } +#endif + ret = of_property_read_u32(np, "temp-inteval", &temp_interval); + if(ret){ + dev_err(&pdev->dev, "fail to get temp-inteval\n"); + return -EINVAL; + } + ret = of_property_read_u32(np, "id", &sensor_id); + if (ret) { + dev_err(&pdev->dev, "fail to get id\n"); + return -EINVAL; + } + of_property_read_string(np, "thermal-name",&thm_name); + strcpy(pzone->thermal_zone_name, thm_name); + pzone->trip_tab = ptrips; + pzone->mode = THERMAL_DEVICE_DISABLED; + pzone->trip_tab = ptrips; + pzone->sensor_config= pconfig; + pzone->trend_val = THERMAL_TREND_STABLE; + pzone->temp_inteval = temp_interval; + pzone->sensor_id = sensor_id; + pzone->ops = &sprd_boardthm_ops; + ret = sprd_board_thm_hw_init(pzone); + if(ret){ + dev_err(&pdev->dev, " pzone hw init error id =%d\n",pzone->sensor_id); + return -ENODEV; + } + ret = sprd_thermal_init(pzone); + if(ret){ + dev_err(&pdev->dev, " pzone sw init error id =%d\n",pzone->sensor_id); + return -ENODEV; + } + platform_set_drvdata(pdev, pzone); + printk("sprd_ddie_thm_probe end\n"); + mutex_unlock(&pzone->th_lock); + return 0; +} + +static int sprd_board_thm_remove(struct platform_device *pdev) +{ + + struct sprd_thermal_zone *pzone = platform_get_drvdata(pdev); + sprd_thermal_remove(pzone); + return 0; +} +static int sprd_board_thm_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct sprd_thermal_zone *pzone = platform_get_drvdata(pdev); + //flush_delayed_work(&pzone->thm_logtime_work); + flush_delayed_work(&pzone->thm_read_work); + flush_delayed_work(&pzone->resume_delay_work); + pzone->ops->suspend(pzone); + return 0; +} +static int sprd_board_thm_resume(struct platform_device *pdev) +{ + + struct sprd_thermal_zone *pzone = platform_get_drvdata(pdev); + schedule_delayed_work(&pzone->resume_delay_work, (HZ * 1)); + schedule_delayed_work(&pzone->thm_read_work, (HZ * 5)); + //schedule_delayed_work(&pzone->thm_logtime_work, (HZ * 7)); + //pzone->ops->resume(pzone); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id thermal_of_match[] = { + { .compatible = "sprd,board-thermal", }, + {} +}; +#endif + +static struct platform_driver sprd_thermal_driver = { + .probe = sprd_board_thm_probe, + .suspend = sprd_board_thm_suspend, + .resume = sprd_board_thm_resume, + .remove = sprd_board_thm_remove, + .driver = { + .owner = THIS_MODULE, + .name = "board-thermal", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(thermal_of_match), +#endif + }, +}; +static int __init sprd_board_thermal_init(void) +{ + return platform_driver_register(&sprd_thermal_driver); +} + +static void __exit sprd_board_thermal_exit(void) +{ + platform_driver_unregister(&sprd_thermal_driver); +} + +device_initcall_sync(sprd_board_thermal_init); +module_exit(sprd_board_thermal_exit); +MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>"); +MODULE_DESCRIPTION("sprd thermal driver"); +MODULE_LICENSE("GPL"); + |
