Initial commit
This commit is contained in:
commit
169c65d57e
51358 changed files with 23120455 additions and 0 deletions
3
drivers/thermal/sunxi_thermal/Makefile
Executable file
3
drivers/thermal/sunxi_thermal/Makefile
Executable file
|
@ -0,0 +1,3 @@
|
|||
obj-$(CONFIG_SUNXI_THERMAL) += sunxi_ths_combine.o
|
||||
obj-$(CONFIG_SUNXI_THERMAL) += sunxi_ths_core.o
|
||||
obj-$(CONFIG_SUNXI_THS_DRIVER) += sunxi_ths_driver.o
|
74
drivers/thermal/sunxi_thermal/sunxi_ths.h
Normal file
74
drivers/thermal/sunxi_thermal/sunxi_ths.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
*
|
||||
* Copyright(c) 2014-2016 Allwinnertech Co., Ltd.
|
||||
* http://www.allwinnertech.com
|
||||
*
|
||||
* Author: JiaRui Xiao <xiaojiarui@allwinnertech.com>
|
||||
*
|
||||
* allwinner sunxi thermal sensor driver data struct.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SUNXI_THS_H
|
||||
#define SUNXI_THS_H
|
||||
|
||||
#define MAX_CHN (4)
|
||||
#define THERMAL_DATA_DELAY (5000)
|
||||
|
||||
#define SUNXI_THS_NAME "sunxi_ths_controller"
|
||||
#define SUNXI_THS_COMBINE_NAME "sunxi_ths_combine"
|
||||
|
||||
struct sunxi_ths_controller;
|
||||
|
||||
enum combine_ths_temp_type {
|
||||
COMBINE_MAX_TEMP = 0,
|
||||
COMBINE_AVG_TMP,
|
||||
COMBINE_MIN_TMP,
|
||||
};
|
||||
|
||||
struct sunxi_ths_controller_ops {
|
||||
int (*suspend)(struct sunxi_ths_controller *);
|
||||
int (*resume)(struct sunxi_ths_controller *);
|
||||
int (*get_temp)(struct sunxi_ths_controller *, u32 id, int *temp);
|
||||
};
|
||||
|
||||
struct sunxi_ths_controller {
|
||||
struct device *dev;
|
||||
struct sunxi_ths_controller_ops *ops;
|
||||
atomic_t initialize;
|
||||
atomic_t usage;
|
||||
atomic_t is_suspend;
|
||||
struct mutex lock;
|
||||
struct list_head combine_list;
|
||||
struct list_head node;
|
||||
int combine_num;
|
||||
void *data; /*store the point of ths_data */
|
||||
};
|
||||
|
||||
struct sunxi_ths_combine_disc {
|
||||
u32 combine_ths_count;
|
||||
enum combine_ths_temp_type type;
|
||||
const char *combine_ths_type;
|
||||
u32 combine_ths_id[MAX_CHN];
|
||||
struct sunxi_ths_controller *controller;
|
||||
};
|
||||
|
||||
struct sunxi_ths_sensor {
|
||||
struct platform_device *pdev;
|
||||
u32 sensor_id; /* the combine sensor id */;
|
||||
int last_temp;
|
||||
struct sunxi_ths_combine_disc *combine;
|
||||
struct thermal_zone_device *tz;
|
||||
atomic_t is_suspend;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct sunxi_ths_controller *
|
||||
sunxi_ths_controller_register(struct device *dev ,struct sunxi_ths_controller_ops *ops, void *data);
|
||||
void sunxi_ths_controller_unregister(struct sunxi_ths_controller *controller);
|
||||
|
||||
#endif /* SUNXI_THS_H */
|
400
drivers/thermal/sunxi_thermal/sunxi_ths_combine.c
Executable file
400
drivers/thermal/sunxi_thermal/sunxi_ths_combine.c
Executable file
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
* drivers/thermal/sunxi_thermal/sunxi_ths_combine.c
|
||||
*
|
||||
* Copyright (C) 2013-2024 allwinner.
|
||||
* JiaRui Xiao<xiaojiarui@allwinnertech.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define NEED_DEBUG (0)
|
||||
|
||||
#if NEED_DEBUG
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#ifdef CONFIG_PM
|
||||
#include <linux/pm.h>
|
||||
#endif
|
||||
#include "sunxi_ths.h"
|
||||
#include "sunxi_ths_core.h"
|
||||
|
||||
#define thsprintk(fmt, arg...) \
|
||||
pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
||||
|
||||
static LIST_HEAD(controller_list);
|
||||
static DEFINE_MUTEX(controller_list_lock);
|
||||
|
||||
static const char * const combine_types[] = {
|
||||
[COMBINE_MAX_TEMP] = "max",
|
||||
[COMBINE_AVG_TMP] = "avg",
|
||||
[COMBINE_MIN_TMP] = "min",
|
||||
};
|
||||
|
||||
static enum combine_ths_temp_type get_combine_type(const char *t)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(combine_types); i++)
|
||||
if (!strcasecmp(t, combine_types[i])) {
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_combine_get_temp(void *data, int *temperature)
|
||||
{
|
||||
struct sunxi_ths_sensor *sensor = data;
|
||||
struct sunxi_ths_controller *controller = sensor->combine->controller;
|
||||
int i, ret, is_suspend;
|
||||
u32 sensor_id;
|
||||
int temp = 0, taget = 0;
|
||||
|
||||
is_suspend = atomic_read(&sensor->is_suspend);
|
||||
|
||||
if (!is_suspend) {
|
||||
switch (sensor->combine->type) {
|
||||
case COMBINE_MAX_TEMP:
|
||||
for (i = 0, taget = -40; i < sensor->combine->combine_ths_count; i++) {
|
||||
sensor_id = sensor->combine->combine_ths_id[i];
|
||||
ret = controller->ops->get_temp(controller, sensor_id, &temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (temp > taget)
|
||||
taget = temp;
|
||||
}
|
||||
break;
|
||||
case COMBINE_AVG_TMP:
|
||||
for (i = 0, taget = 0; i < sensor->combine->combine_ths_count; i++) {
|
||||
sensor_id = sensor->combine->combine_ths_id[i];
|
||||
ret = controller->ops->get_temp(controller, sensor_id, &temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
taget += temp;
|
||||
}
|
||||
do_div(taget, sensor->combine->combine_ths_count);
|
||||
break;
|
||||
case COMBINE_MIN_TMP:
|
||||
for (i = 0, taget = 180; i < sensor->combine->combine_ths_count; i++) {
|
||||
sensor_id = sensor->combine->combine_ths_id[i];
|
||||
ret = controller->ops->get_temp(controller, sensor_id, &temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (temp < taget)
|
||||
taget = temp;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*temperature = taget;
|
||||
sensor->last_temp = taget;
|
||||
}else{
|
||||
*temperature = sensor->last_temp;
|
||||
}
|
||||
thsprintk("get temp %d\n", (*temperature));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sunxi_ths_controller *
|
||||
combine_find_controller(struct device_node *np)
|
||||
{
|
||||
struct sunxi_ths_controller *controller;
|
||||
struct device_node *pnp = of_get_parent(np);
|
||||
if (IS_ERR_OR_NULL(pnp)) {
|
||||
pr_err("ths combine: get parent err\n");
|
||||
return NULL;
|
||||
}
|
||||
mutex_lock(&controller_list_lock);
|
||||
list_for_each_entry(controller, &controller_list, node){
|
||||
if(pnp == controller->dev->of_node){
|
||||
mutex_unlock(&controller_list_lock);
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&controller_list_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sunxi_ths_controller *
|
||||
sunxi_ths_controller_register(struct device *dev ,struct sunxi_ths_controller_ops *ops, void *data)
|
||||
{
|
||||
struct sunxi_ths_controller *controller = NULL;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *combine_np = NULL;
|
||||
struct platform_device *combine_dev = NULL;
|
||||
int i = 0, combine_num = 0;
|
||||
char combine_name[50];
|
||||
|
||||
controller = kzalloc(sizeof(*controller), GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(controller)) {
|
||||
pr_err("ths controller: not enough memory for controller data\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "combine_num", &combine_num)) {
|
||||
pr_err("%s: get combine_num failed\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
controller->dev = dev;
|
||||
controller->ops = ops;
|
||||
controller->data = data;
|
||||
controller->combine_num = combine_num;
|
||||
atomic_set(&controller->is_suspend, 0);
|
||||
atomic_set(&controller->usage, 0);
|
||||
mutex_init(&controller->lock);
|
||||
INIT_LIST_HEAD(&controller->combine_list);
|
||||
mutex_lock(&controller_list_lock);
|
||||
list_add_tail(&controller->node, &controller_list);
|
||||
mutex_unlock(&controller_list_lock);
|
||||
for_each_child_of_node(np, combine_np) {
|
||||
combine_dev = kzalloc(sizeof(*combine_dev), GFP_KERNEL);
|
||||
|
||||
if (IS_ERR_OR_NULL(combine_dev)) {
|
||||
pr_err("combine_dev: not enough memory for combine_dev data\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
combine_dev->dev.of_node = combine_np;
|
||||
sprintf(combine_name, "sunxi_ths_combine_%d", i);
|
||||
combine_dev->name = combine_name;
|
||||
combine_dev->id = PLATFORM_DEVID_NONE;
|
||||
platform_device_register(combine_dev);
|
||||
i++;
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
EXPORT_SYMBOL(sunxi_ths_controller_register);
|
||||
|
||||
void sunxi_ths_controller_unregister(struct sunxi_ths_controller *controller)
|
||||
{
|
||||
struct sunxi_ths_sensor *sensor;
|
||||
struct platform_device *pdev;
|
||||
list_for_each_entry(sensor, &controller->combine_list, node) {
|
||||
pdev = sensor->pdev;
|
||||
platform_device_unregister(pdev);
|
||||
kfree(pdev);
|
||||
}
|
||||
mutex_lock(&controller_list_lock);
|
||||
list_del(&controller->node);
|
||||
mutex_unlock(&controller_list_lock);
|
||||
kfree(controller);
|
||||
}
|
||||
EXPORT_SYMBOL(sunxi_ths_controller_unregister);
|
||||
|
||||
static int sunxi_combine_parse(struct sunxi_ths_sensor *sensor)
|
||||
{
|
||||
struct device_node *np = NULL;
|
||||
struct sunxi_ths_combine_disc *combine;
|
||||
const char *type = NULL;
|
||||
int combine_sensor_num = 0;
|
||||
int i = 0;
|
||||
|
||||
combine = kzalloc(sizeof(*combine), GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(combine)) {
|
||||
pr_err("ths combine: not enough memory for combine data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
np = sensor->pdev->dev.of_node;
|
||||
combine->controller = combine_find_controller(np);
|
||||
if (!combine->controller) {
|
||||
pr_err("sensor find no controller\n");
|
||||
goto parse_fail;
|
||||
}
|
||||
/* getting the combine sensor have sensor num*/
|
||||
if (of_property_read_u32(np, "combine_sensor_num", &combine_sensor_num)) {
|
||||
pr_err("%s: get sensor_num failed\n", __func__);
|
||||
return -EBUSY;
|
||||
} else {
|
||||
combine->combine_ths_count = combine_sensor_num;
|
||||
}
|
||||
|
||||
/* getting the combine sensor how to calcular all the sensor temp */
|
||||
if (of_property_read_string(np, "combine_sensor_temp_type", &type)) {
|
||||
pr_err("%s: get combine type failed\n", __func__);
|
||||
return -EBUSY;
|
||||
} else {
|
||||
combine->type = get_combine_type(type);
|
||||
}
|
||||
|
||||
/* getting the combine sensor where focus on */
|
||||
if (of_property_read_string(np, "combine_sensor_type", &combine->combine_ths_type)) {
|
||||
pr_err("%s: get sensor_num failed\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* getting the combine sensor include all sensor id */
|
||||
for (i = 0; i < combine->combine_ths_count; i++) {
|
||||
if (of_property_read_u32_index(np, "combine_sensor_id",
|
||||
i, &(combine->combine_ths_id[i]))) {
|
||||
pr_err("node combine chn get failed!\n");
|
||||
goto parse_fail;
|
||||
}
|
||||
}
|
||||
|
||||
sensor->combine = combine;
|
||||
list_add_tail(&sensor->node, &combine->controller->combine_list);
|
||||
return 0;
|
||||
parse_fail:
|
||||
kfree(combine);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int sunxi_combine_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err = 0, id = 0;
|
||||
struct sunxi_ths_sensor *sensor;
|
||||
struct sunxi_ths_data *ths_data;
|
||||
|
||||
thsprintk("sunxi ths sensor probe start !\n");
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
pr_err("sunxi ths sensor register err!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(sensor)) {
|
||||
pr_err("ths combine: not enough memory for sensor data\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
sensor->pdev = pdev;
|
||||
|
||||
err = sunxi_combine_parse(sensor);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
sscanf(pdev->name, "sunxi_ths_combine_%d", &id);
|
||||
sensor->sensor_id = id;
|
||||
atomic_add(1, &sensor->combine->controller->usage);
|
||||
sensor->tz = thermal_zone_of_sensor_register(&pdev->dev,
|
||||
id, sensor, sunxi_combine_get_temp, NULL);
|
||||
if (IS_ERR(sensor->tz)) {
|
||||
pr_err("sunxi ths sensor register err!\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
ths_data = (struct sunxi_ths_data *)sensor->combine->controller->data;
|
||||
ths_data->comb_sensor[id] = sensor;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, sensor);
|
||||
atomic_set(&sensor->is_suspend, 0);
|
||||
|
||||
if (sensor->tz->ops->set_mode)
|
||||
sensor->tz->ops->set_mode(sensor->tz, THERMAL_DEVICE_ENABLED);
|
||||
else
|
||||
thermal_zone_device_update(sensor->tz);
|
||||
|
||||
thsprintk("%s probe end!\n", pdev->name);
|
||||
return 0;
|
||||
fail1:
|
||||
kfree(sensor->combine);
|
||||
fail:
|
||||
kfree(sensor);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int sunxi_combine_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sunxi_ths_sensor *sensor;
|
||||
|
||||
sensor = dev_get_drvdata(&pdev->dev);
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tz);
|
||||
kfree(sensor->combine);
|
||||
kfree(sensor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sunxi_combine_suspend(struct device *dev)
|
||||
{
|
||||
struct sunxi_ths_sensor *sensor = dev_get_drvdata(dev);
|
||||
struct sunxi_ths_controller *controller = sensor->combine->controller;
|
||||
|
||||
thsprintk("enter: sunxi_ths_suspend.\n");
|
||||
atomic_set(&sensor->is_suspend, 1);
|
||||
if (atomic_sub_return(1, &controller->usage) == 0)
|
||||
if (controller->ops->suspend)
|
||||
return controller->ops->suspend(controller);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_combine_resume(struct device *dev)
|
||||
{
|
||||
struct sunxi_ths_sensor *sensor = dev_get_drvdata(dev);
|
||||
struct sunxi_ths_controller *controller = sensor->combine->controller;
|
||||
int max_combine;
|
||||
|
||||
thsprintk("enter: sunxi_ths_resume.\n");
|
||||
max_combine = sensor->combine->controller->combine_num;
|
||||
if (atomic_add_return(1, &controller->usage) == max_combine)
|
||||
if (controller->ops->resume)
|
||||
controller->ops->resume(controller);
|
||||
|
||||
atomic_set(&sensor->is_suspend, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sunxi_combine_pm_ops = {
|
||||
.suspend = sunxi_combine_suspend,
|
||||
.resume = sunxi_combine_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct of_device_id of_sunxi_combine_ths_match[] = {
|
||||
{ .compatible = "allwinner,ths_combine0" },
|
||||
{ .compatible = "allwinner,ths_combine1" },
|
||||
{ .compatible = "allwinner,ths_combine2" },
|
||||
{ /* end */ }
|
||||
};
|
||||
|
||||
static struct platform_driver sunxi_combine_driver = {
|
||||
.probe = sunxi_combine_probe,
|
||||
.remove = sunxi_combine_remove,
|
||||
.driver = {
|
||||
.name = SUNXI_THS_COMBINE_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_sunxi_combine_ths_match,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &sunxi_combine_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init sunxi_ths_combine_init(void)
|
||||
{
|
||||
return platform_driver_register(&sunxi_combine_driver);
|
||||
}
|
||||
|
||||
static void __exit sunxi_ths_combine_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sunxi_combine_driver);
|
||||
mutex_destroy(&controller_list_lock);
|
||||
}
|
||||
|
||||
subsys_initcall_sync(sunxi_ths_combine_init);
|
||||
module_exit(sunxi_ths_combine_exit);
|
||||
MODULE_DESCRIPTION("SUNXI combine thermal sensor driver");
|
||||
MODULE_AUTHOR("QIn");
|
||||
MODULE_LICENSE("GPL v2");
|
784
drivers/thermal/sunxi_thermal/sunxi_ths_core.c
Normal file
784
drivers/thermal/sunxi_thermal/sunxi_ths_core.c
Normal file
|
@ -0,0 +1,784 @@
|
|||
/*
|
||||
* drivers/thermal/sunxi_thermal/sunxi_ths_core.c
|
||||
*
|
||||
* Copyright (C) 2013-2024 allwinner.
|
||||
* JiaRui Xiao<xiaojiarui@allwinnertech.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#define NEED_DEBUG (0)
|
||||
|
||||
#if NEED_DEBUG
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/sunxi-sid.h>
|
||||
#include <linux/sunxi-smc.h>
|
||||
|
||||
#include "sunxi_ths.h"
|
||||
#include "sunxi_ths_core.h"
|
||||
#include "sunxi_ths_efuse.h"
|
||||
|
||||
#define DISABLE_EN_REG 0
|
||||
|
||||
static struct thermal_reg *status_reg;
|
||||
static struct thermal_reg *data_reg;
|
||||
static struct thermal_reg *enable_reg;
|
||||
|
||||
static ssize_t
|
||||
thermal_sensor_info_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sunxi_ths_data *ths_data = platform_get_drvdata(pdev);
|
||||
int indx;
|
||||
struct thermal_sensor_info *sensor_info =
|
||||
(struct thermal_sensor_info *)ths_data->data;
|
||||
|
||||
for (indx = 0; indx < ths_data->sensor_cnt; indx++) {
|
||||
sprintf(buf, "sensor_%d_%s_temp", indx, sensor_info[indx].ths_name);
|
||||
if (!strcmp(attr->attr.name, buf)) {
|
||||
return sprintf(buf, "sensor%d located in %s, temp is = %d\n",
|
||||
indx,
|
||||
sensor_info[indx].ths_name,
|
||||
sensor_info[indx].temp);
|
||||
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int ths_driver_create_sensor_info_attrs(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_sensor_info *sensor_info)
|
||||
{
|
||||
int indx, size;
|
||||
size = sizeof(struct ths_info_attr) * ths_data->sensor_cnt;
|
||||
|
||||
ths_data->ths_info_attrs = kzalloc(size, GFP_KERNEL);
|
||||
if (!ths_data->ths_info_attrs) {
|
||||
pr_err("ths_info_attrs: not enough memory for sensor_info\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
for (indx = 0; indx < ths_data->sensor_cnt; indx++) {
|
||||
/* create trip type attribute */
|
||||
snprintf(ths_data->ths_info_attrs[indx].name, THERMAL_ATTR_LENGTH,
|
||||
"sensor_%d_%s_temp", indx, sensor_info[indx].ths_name);
|
||||
|
||||
sysfs_attr_init(&ths_data->ths_info_attrs[indx].attr.attr);
|
||||
ths_data->ths_info_attrs[indx].attr.attr.name =
|
||||
ths_data->ths_info_attrs[indx].name;
|
||||
ths_data->ths_info_attrs[indx].attr.attr.mode = S_IRUGO;
|
||||
ths_data->ths_info_attrs[indx].attr.show = thermal_sensor_info_show;
|
||||
|
||||
device_create_file(&ths_data->pdev->dev,
|
||||
&ths_data->ths_info_attrs[indx].attr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ths_drvier_remove_trip_attrs(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
int indx;
|
||||
|
||||
for (indx = 0; indx < ths_data->sensor_cnt; indx++) {
|
||||
device_remove_file(&ths_data->pdev->dev,
|
||||
&ths_data->ths_info_attrs[indx].attr);
|
||||
|
||||
}
|
||||
kfree(ths_data->ths_info_attrs);
|
||||
}
|
||||
|
||||
/*
|
||||
* @interface name: reg_to_temp_version_1
|
||||
* @function: change the value what read from register to the temp for thermal
|
||||
* version 1.
|
||||
*/
|
||||
static int reg_to_temp_version_1(u32 reg_data, u16 id,
|
||||
struct temp_calculate_coefficent *para)
|
||||
{
|
||||
int t = 0;
|
||||
|
||||
t = (int)(reg_data * (para->nt_para[id].MUL_PARA)) - (int)(para->nt_para[id].MINU_PARA);
|
||||
t = t / (int)para->nt_para[id].DIV_PARA;
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* @interface name: reg_to_temp_version_2_3
|
||||
* @function: change the value what read from register to the temp for thermal
|
||||
* version 2 or 3.
|
||||
*/
|
||||
static int reg_to_temp_version_2_3(u32 reg_data, u16 id,
|
||||
struct temp_calculate_coefficent *para)
|
||||
{
|
||||
int t = 0;
|
||||
|
||||
t = (int)(para->nt_para[id].MINU_PARA) - (int)(reg_data * (para->nt_para[id].MUL_PARA));
|
||||
/* rounding for variable t */
|
||||
t += (int)para->nt_para[id].DIV_PARA/2;
|
||||
t = t / (int)para->nt_para[id].DIV_PARA;
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* ths_calculate_temp_x10_version_2_3() --using ths data reg to calculate temp
|
||||
* reg_data: ths data register
|
||||
* id: ths sensor id
|
||||
* para: calculate coefficent
|
||||
*
|
||||
* calculrate temp and return tempx10 to reduce deviation.
|
||||
*
|
||||
* return: real temp * 10,for ex:real-temp = 25.6, then return 256
|
||||
*/
|
||||
static int ths_calculate_temp_x10_version_2_3(u32 reg_data, u16 id,
|
||||
struct temp_calculate_coefficent *para)
|
||||
{
|
||||
int t = 0;
|
||||
|
||||
t = (int)(para->nt_para[id].MINU_PARA) -
|
||||
(int)(reg_data * (para->nt_para[id].MUL_PARA));
|
||||
t = (t * 10) / (int)para->nt_para[id].DIV_PARA;
|
||||
return t;
|
||||
}
|
||||
|
||||
u32 ths_driver_temp_to_reg(int temp, u16 id,
|
||||
struct temp_calculate_coefficent *para)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = (para->nt_para[id].MINU_PARA -
|
||||
(temp * para->nt_para[id].DIV_PARA));
|
||||
reg = reg / para->nt_para[id].MUL_PARA;
|
||||
return (u32) reg;
|
||||
}
|
||||
|
||||
static int get_ths_driver_version(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
return ths_data->ths_driver_version;
|
||||
}
|
||||
|
||||
void ths_driver_clk_cfg(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
unsigned long rate = 0;
|
||||
|
||||
if (ths_data->parent_clk == false) {
|
||||
rate = clk_get_rate(ths_data->pclk);
|
||||
thsprintk("get ths_clk_source rate %dHZ\n", (__u32) rate);
|
||||
return;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(ths_data->pclk);
|
||||
thsprintk("get ths_clk_source rate %dHZ\n", (__u32) rate);
|
||||
if (ths_data->ths_driver_version != 3) {
|
||||
if (clk_set_parent(ths_data->mclk, ths_data->pclk)) {
|
||||
pr_err("%s: set ths_clk parent to ths_clk_source failed!\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
rate = clk_get_rate(ths_data->mclk);
|
||||
thsprintk("get ths_clk rate %dHZ\n", (__u32) rate);
|
||||
}
|
||||
|
||||
/*if (clk_set_rate(ths_data->mclk, ths_clk))
|
||||
* pr_err("set ths clock freq to %uM failed!\n", ths_clk);
|
||||
*/
|
||||
if (clk_prepare_enable(ths_data->mclk))
|
||||
pr_err("try to enable ths_clk failed!\n");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void ths_driver_clk_uncfg(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
if (ths_data->parent_clk == true) {
|
||||
if (!(ths_data->mclk == NULL || IS_ERR(ths_data->mclk))) {
|
||||
clk_disable_unprepare(ths_data->mclk);
|
||||
clk_put(ths_data->mclk);
|
||||
ths_data->mclk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ths_data->pclk == NULL || IS_ERR(ths_data->pclk))) {
|
||||
clk_put(ths_data->pclk);
|
||||
ths_data->pclk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int ths_driver_startup(struct sunxi_ths_data *ths_data,
|
||||
struct device *dev)
|
||||
{
|
||||
struct device_node *np = NULL;
|
||||
|
||||
np = dev->of_node;
|
||||
|
||||
ths_data->base_addr = of_iomap(np, 0);
|
||||
if (ths_data->base_addr != NULL) {
|
||||
thsprintk("ths base: %p !\n", ths_data->base_addr);
|
||||
} else {
|
||||
pr_err("%s:Failed to ioremap() io memory region.\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ths_data->irq = irq_of_parse_and_map(np, 0);
|
||||
if (ths_data->irq != 0) {
|
||||
thsprintk("ths irq num: %d !\n", ths_data->irq);
|
||||
} else {
|
||||
pr_err("%s:Failed to map irq.\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "sensor_num", &ths_data->sensor_cnt)) {
|
||||
pr_err("%s: get sensor_num failed\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "combine_num", &ths_data->combine_cnt))
|
||||
pr_err("%s: get combine_num failed\n", __func__);
|
||||
|
||||
if (of_property_read_u32(np, "alarm_low_temp",
|
||||
&ths_data->alarm_low_temp)) {
|
||||
pr_err("%s: get alarm_low_temp failed\n", __func__);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "alarm_high_temp",
|
||||
&ths_data->alarm_high_temp)) {
|
||||
pr_err("%s: get alarm_high_temp failed\n", __func__);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "alarm_temp_hysteresis",
|
||||
&ths_data->alarm_temp_hysteresis)) {
|
||||
pr_err("%s: get alarm_temp_hysteresis failed\n", __func__);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "shut_temp", &ths_data->shut_temp)) {
|
||||
pr_err("%s: get int temp failed\n", __func__);
|
||||
ths_data->shut_temp = 120;
|
||||
}
|
||||
|
||||
thsprintk("ths have parent clk: %d, thermal version is: %d !\n",
|
||||
ths_data->parent_clk, ths_data->ths_driver_version);
|
||||
|
||||
ths_data->pclk = of_clk_get(np, 0);
|
||||
if (ths_data->pclk == NULL || IS_ERR(ths_data->pclk)) {
|
||||
pr_err("%s:Failed to get pclk.\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (ths_data->parent_clk == true) {
|
||||
ths_data->mclk = of_clk_get(np, 1);
|
||||
if (ths_data->mclk == NULL || IS_ERR(ths_data->mclk)) {
|
||||
pr_err("%s:Failed to get mclk.\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ths_driver_reg_to_temp(u32 reg_data, u16 id,
|
||||
u32 ths_driver_version,
|
||||
struct temp_calculate_coefficent *para)
|
||||
{
|
||||
int t = 0;
|
||||
|
||||
/**when reg_data bigger than before,
|
||||
* the real temp is smaller than before.
|
||||
*/
|
||||
if (reg_data > para->down_limit.split_reg) {
|
||||
pr_err("%s:Thermal sensor calculate reg to temp is error.\n",
|
||||
__func__);
|
||||
return OUT_THE_THERMAL_LIMIT;
|
||||
} else if ((reg_data <= para->down_limit.split_reg) &&
|
||||
(reg_data >= para->up_limit.split_reg)) {
|
||||
thsprintk("THIS :MUL_PARA=%d DIV_PARA=%d MINU_PARA=%d.\n",
|
||||
para->nt_para[id].MUL_PARA,
|
||||
para->nt_para[id].DIV_PARA,
|
||||
para->nt_para[id].MINU_PARA);
|
||||
if (ths_driver_version == 1)
|
||||
t = reg_to_temp_version_1(reg_data, id, para);
|
||||
else
|
||||
t = reg_to_temp_version_2_3(reg_data, id, para);
|
||||
} else if (reg_data < para->up_limit.split_reg) {
|
||||
thsprintk("NEXT:MUL_PARA=%d DIV_PARA=%d MINU_PARA=%d.\n",
|
||||
para->nt_para[id].MUL_PARA,
|
||||
para->nt_para[id].DIV_PARA,
|
||||
para->nt_para[id].MINU_PARA);
|
||||
t = ths_driver_reg_to_temp(reg_data, id, ths_driver_version, para + 1);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* @interface name: reg_to_temp_to_reg_efuse
|
||||
* @function: change the value what read from register to the temp for thermal
|
||||
* version 3. we use float type, because we need to calcula calibration efuse
|
||||
* info.
|
||||
*/
|
||||
static u32 reg_to_temp_to_reg_efuse(u32 reg_data, u16 id, u16 environment_temp, struct temp_calculate_coefficent *para)
|
||||
{
|
||||
u32 t = 0;
|
||||
u32 write_reg_data;
|
||||
s32 reg_to_temp;
|
||||
u16 envir_temp;
|
||||
|
||||
envir_temp = (u16)environment_temp;
|
||||
reg_to_temp = ths_calculate_temp_x10_version_2_3(reg_data, id, para);
|
||||
|
||||
t = ((reg_to_temp - envir_temp) * CONST_DIV) /
|
||||
SENSOR_CP_EUFSE_PER_REG_TO_TEMP;
|
||||
|
||||
write_reg_data = THS_EFUSE_DEFAULT_VALUE - t;
|
||||
if ((write_reg_data & (~THS_EFUSE_ENVIROMENT_MASK)) == 0) {
|
||||
thsprintk("efuse register data will write 0x%x", write_reg_data);
|
||||
return write_reg_data;
|
||||
} else {
|
||||
thsprintk("efuse register data is 12-bit but write 0x%x", write_reg_data);
|
||||
return WRONG_EFUSE_REG_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
int ths_driver_get_temp(struct sunxi_ths_data *ths_data, int id)
|
||||
{
|
||||
u32 reg_data;
|
||||
u32 reg_flag = 0;
|
||||
int ths_drv_ver;
|
||||
int t = 0;
|
||||
|
||||
if (id + 1 > ths_data->sensor_cnt) {
|
||||
thsprintk("the sensor id has writed wrong, beyond sensor %d\n",
|
||||
ths_data->sensor_cnt);
|
||||
return WRONG_SENSOR_ID;
|
||||
}
|
||||
|
||||
mutex_lock(&ths_data->ths_mutex_lock);
|
||||
reg_data = readl(ths_data->base_addr + status_reg->offset);
|
||||
|
||||
ths_drv_ver = get_ths_driver_version(ths_data);
|
||||
if (ths_drv_ver == 1)
|
||||
reg_flag = (0x40000 << id);
|
||||
else if (ths_drv_ver == 2)
|
||||
reg_flag = (0x100 << id);
|
||||
else if (ths_drv_ver == 3)
|
||||
reg_flag = (0x1 << id);
|
||||
|
||||
if (!(reg_data & reg_flag)) {
|
||||
thsprintk("reg_data=0x%x,reg_flag=0x%x\n", reg_data, reg_flag);
|
||||
mutex_unlock(&ths_data->ths_mutex_lock);
|
||||
return NO_DATA_INTTERUPT;
|
||||
}
|
||||
|
||||
|
||||
reg_data = readl(ths_data->base_addr
|
||||
+ data_reg->offset + id * 4);
|
||||
reg_data &= THERMAL_DATA_REG_MAX;
|
||||
t = ths_driver_reg_to_temp(reg_data, id, ths_data->ths_driver_version,
|
||||
ths_data->ths_coefficent->calcular_para);
|
||||
/* clear the status */
|
||||
writel(reg_flag, ths_data->base_addr + status_reg->offset);
|
||||
mutex_unlock(&ths_data->ths_mutex_lock);
|
||||
|
||||
if ((ths_data->ths_driver_version == 3) && (ths_data->cp_ft_flag == THS_CALIBRATION_IN_FT))
|
||||
t = t + FT_CALIBRATION_DEVIATION;
|
||||
|
||||
if (t >= ths_data->shut_temp)
|
||||
printk(KERN_EMERG "ths data[%d] = 0x%x, temp is %d\n",
|
||||
id,
|
||||
reg_data,
|
||||
t);
|
||||
|
||||
thsprintk("THS data[%d] = 0x%x, temp is %d\n",
|
||||
id,
|
||||
reg_data,
|
||||
t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/*this api can calcular power of the base */
|
||||
u32 unsigned_int_pow(u32 base, u32 power)
|
||||
{
|
||||
u32 value = 1;
|
||||
|
||||
if (power == 0)
|
||||
return 1;
|
||||
|
||||
while (power != 0) {
|
||||
value *= base;
|
||||
power--;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* return 0, is CP calibration.
|
||||
* return 1, is FT calibration.
|
||||
*/
|
||||
static u16 get_cp_ft_flag(u16 reg_value)
|
||||
{
|
||||
return (reg_value & THS_EFUSE_CP_FT_MASK) >> THS_EFUSE_CP_FT_BIT;
|
||||
}
|
||||
|
||||
/*
|
||||
* the new test method add CP calibration and
|
||||
* support the old FT calibration. but the version3
|
||||
* thermal divide the enviroment temp reg from the FT,
|
||||
* so the FT method different to version_1_2.
|
||||
*/
|
||||
static void write_efuse_to_reg_ver_3(struct sunxi_ths_data *ths_data,
|
||||
int cal_reg_num,
|
||||
struct thermal_reg *reg,
|
||||
u32 *ths_cal_data)
|
||||
{
|
||||
int i, calcu_times;
|
||||
u16 first_16bit;
|
||||
u16 environment_temp;
|
||||
u32 sensor_temp;
|
||||
u32 write_value = 0;
|
||||
u16 *efuse_sensor0_reg_data;
|
||||
|
||||
struct temp_calculate_coefficent *para = ths_data->ths_coefficent->calcular_para;
|
||||
|
||||
if (*ths_cal_data == 0)
|
||||
pr_info("%s:reading environment_temp is zero\n", __func__);
|
||||
else
|
||||
thsprintk("thermal sensor efuse fisrt 32-bit info 0x%x\n", *ths_cal_data);
|
||||
|
||||
first_16bit = (u16)*ths_cal_data;
|
||||
/* last 4-bit is CP or FT method in all of 16-bit */
|
||||
ths_data->cp_ft_flag = get_cp_ft_flag(first_16bit);
|
||||
/* first 12-bit is environment temp in all of 16-bit */
|
||||
environment_temp = first_16bit & THS_EFUSE_ENVIROMENT_MASK;
|
||||
|
||||
/* make the pointer point to the sensor0 efuse address */
|
||||
efuse_sensor0_reg_data = (u16 *)ths_cal_data;
|
||||
efuse_sensor0_reg_data++;
|
||||
thsprintk("efuse_sensor0_reg_data 0x%x\n", *efuse_sensor0_reg_data);
|
||||
|
||||
thsprintk("first_16 0x%x, environment_temp 0x%x, cp_ft_flag 0x%x\n", first_16bit, environment_temp, ths_data->cp_ft_flag);
|
||||
|
||||
for (i = 0, calcu_times = 0; i < ths_data->sensor_cnt; i++) {
|
||||
if (*(efuse_sensor0_reg_data + i) == 0) {
|
||||
pr_err("%s:reading calibration data%d is zero\n",
|
||||
__func__, i);
|
||||
return;
|
||||
}
|
||||
|
||||
sensor_temp = reg_to_temp_to_reg_efuse((u32)*(efuse_sensor0_reg_data + i), i,
|
||||
environment_temp, para);
|
||||
if (sensor_temp == WRONG_EFUSE_REG_DATA) {
|
||||
pr_err("%s:getting efuse data bit out of 12-bit limit\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* calcu_times is used to distinguish the operation
|
||||
* num which is odd number or even number.
|
||||
*/
|
||||
if (calcu_times == 0) {
|
||||
/* we calcular the sensor efuse when isn't last sensor*/
|
||||
if ((i + 1) < ths_data->sensor_cnt) {
|
||||
write_value = sensor_temp;
|
||||
thsprintk("sensor:%d efuse_reg[%d] =0x%x\n", i, i / 2, write_value);
|
||||
calcu_times++;
|
||||
continue;
|
||||
} else {
|
||||
write_value = sensor_temp;
|
||||
writel(write_value, ths_data->base_addr +
|
||||
reg->offset + (i / 2) * 4);
|
||||
thsprintk("sensor:%d efuse_reg[%d] =0x%x\n", i, i / 2, write_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* calcular the calibration efuse data high 16-bit and write to register */
|
||||
if (calcu_times == 1) {
|
||||
calcu_times = 0;
|
||||
write_value |= (sensor_temp << 16);
|
||||
writel(write_value, ths_data->base_addr +
|
||||
reg->offset + (i / 2) * 4);
|
||||
thsprintk("sensor:%d efuse_reg[%d] =0x%x\n", i, i / 2, write_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_efuse_to_reg_ver_1_2(struct sunxi_ths_data *ths_data,
|
||||
int cal_reg_num,
|
||||
struct thermal_reg *reg,
|
||||
u32 *ths_cal_data)
|
||||
{
|
||||
int i;
|
||||
u32 reg_data;
|
||||
|
||||
for (i = 0; i < cal_reg_num; i++) {
|
||||
if (*(ths_cal_data + i) == 0) {
|
||||
pr_err("%s:reading calibration data%d is zero\n",
|
||||
__func__, i);
|
||||
break;
|
||||
}
|
||||
thsprintk("ths_cal_data[%d] =0x%x\n",
|
||||
i, *(ths_cal_data + i));
|
||||
writel(*(ths_cal_data + i), ths_data->base_addr
|
||||
+ reg->offset + i * 4);
|
||||
reg_data = readl(ths_data->base_addr
|
||||
+ reg->offset + i * 4);
|
||||
if (reg_data != *(ths_cal_data + i))
|
||||
thsprintk("write efuse fail ths_cal_data[%d] =0x%x,reg_data = 0x%x\n",
|
||||
i, *(ths_cal_data + i), reg_data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void ths_driver_write_efuse_to_reg(struct sunxi_ths_data *ths_data,
|
||||
int cal_reg_num,
|
||||
struct thermal_reg *reg)
|
||||
{
|
||||
u32 *ths_cal_data;
|
||||
int ret;
|
||||
int ths_drv_ver;
|
||||
|
||||
ths_cal_data = kmalloc(sizeof(u32), GFP_KERNEL);
|
||||
ret = sunxi_efuse_readn(EFUSE_THM_SENSOR_NAME, (void *)(ths_cal_data),
|
||||
2 * sizeof(u32));
|
||||
if (ret) {
|
||||
thsprintk("%s:read the efuse key fail\n", __func__);
|
||||
} else {
|
||||
ths_drv_ver = get_ths_driver_version(ths_data);
|
||||
if (ths_drv_ver == 1 || ths_drv_ver == 2) {
|
||||
write_efuse_to_reg_ver_1_2(ths_data, cal_reg_num, reg,
|
||||
ths_cal_data);
|
||||
} else if (ths_drv_ver == 3) {
|
||||
write_efuse_to_reg_ver_3(ths_data, cal_reg_num, reg,
|
||||
ths_cal_data);
|
||||
}
|
||||
}
|
||||
kfree(ths_cal_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* old_reg need the mask to protect the data,
|
||||
* writed to reg before enbale sensor.
|
||||
*/
|
||||
static void ths_driver_enable_enreg(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_reg *reg)
|
||||
{
|
||||
u32 reg_value;
|
||||
u32 reg_enable_sensor_bit = (u32)0xffffffff;
|
||||
int ths_drv_ver;
|
||||
|
||||
ths_drv_ver = get_ths_driver_version(ths_data);
|
||||
reg_value = readl(ths_data->base_addr + reg->offset);
|
||||
if (ths_drv_ver == 1) {
|
||||
reg_enable_sensor_bit &=
|
||||
((unsigned_int_pow(2, ths_data->sensor_cnt) - 1) << 18);
|
||||
reg_value |= reg_enable_sensor_bit;
|
||||
} else if (ths_drv_ver == 2) {
|
||||
reg_enable_sensor_bit &=
|
||||
(unsigned_int_pow(2, ths_data->sensor_cnt) - 1);
|
||||
reg_value |= reg_enable_sensor_bit;
|
||||
|
||||
thsprintk("reg->name=%s,value=0x%x,reg_enable_sensor_bit=0x%x\n",
|
||||
reg->name, reg_value, reg_enable_sensor_bit);
|
||||
} else if (ths_drv_ver == 3) {
|
||||
reg_value = unsigned_int_pow(2, ths_data->sensor_cnt) - 1;
|
||||
}
|
||||
writel(reg_value, ths_data->base_addr + reg->offset);
|
||||
}
|
||||
|
||||
void ths_driver_reg_debug(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_sensor_coefficent *ths_coefficent)
|
||||
{
|
||||
struct thermal_reg *thermal_reg = NULL;
|
||||
|
||||
thermal_reg = ths_coefficent->reg_para;
|
||||
while (strcmp("", thermal_reg->name)) {
|
||||
thsprintk("reg_name=%s, value=0x%x\n",
|
||||
thermal_reg->name,
|
||||
readl(ths_data->base_addr +
|
||||
thermal_reg->offset));
|
||||
thermal_reg++;
|
||||
}
|
||||
}
|
||||
|
||||
static void ths_driver_init_old_shut_reg(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_reg *reg,
|
||||
struct temp_calculate_coefficent *cpara)
|
||||
{
|
||||
u32 reg_value;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ths_data->sensor_cnt; i++) {
|
||||
reg_value = ths_driver_temp_to_reg(ths_data->shut_temp, i, cpara);
|
||||
reg_value = (reg_value << 16);
|
||||
writel(reg_value, ths_data->base_addr + reg->offset + i * 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void ths_driver_init_new_shut_reg(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_reg *reg,
|
||||
struct temp_calculate_coefficent *cpara)
|
||||
{
|
||||
u32 write_value, reg_value_store;
|
||||
int i, calcu_times;
|
||||
|
||||
for (i = 0, calcu_times = 0; i < ths_data->sensor_cnt; i++) {
|
||||
reg_value_store =
|
||||
ths_driver_temp_to_reg(ths_data->shut_temp, i, cpara);
|
||||
|
||||
/**
|
||||
* calcu_times is used to distinguish the operation
|
||||
* num which is odd number or even number.
|
||||
*/
|
||||
if (calcu_times == 0) {
|
||||
/* we calcular the sensor efuse when isn't last sensor*/
|
||||
if ((i + 1) < ths_data->sensor_cnt) {
|
||||
write_value = (u32)reg_value_store;
|
||||
thsprintk("sensor:%d shut_temp_reg[%d] =0x%x\n", i, i / 2, write_value);
|
||||
calcu_times++;
|
||||
continue;
|
||||
} else {
|
||||
write_value = (u32)reg_value_store;
|
||||
|
||||
writel(write_value, ths_data->base_addr +
|
||||
reg->offset + (i / 2) * 4);
|
||||
thsprintk("sensor:%d shut_temp_reg[%d] =0x%x\n", i, i / 2, write_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* calcular the calibration efuse data high 16-bit and write to register */
|
||||
if (calcu_times == 1) {
|
||||
calcu_times = 0;
|
||||
write_value |= (reg_value_store << 16);
|
||||
writel(write_value, ths_data->base_addr +
|
||||
reg->offset + (i / 2) * 4);
|
||||
thsprintk("sensor:%d shut_temp_reg[%d] =0x%x\n", i, i / 2, write_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ths_driver_init_shut_temp_reg(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_reg *reg, struct temp_calculate_coefficent *para)
|
||||
{
|
||||
int ths_drv_ver;
|
||||
|
||||
ths_drv_ver = get_ths_driver_version(ths_data);
|
||||
if (ths_drv_ver == 2)
|
||||
ths_driver_init_old_shut_reg(ths_data, reg, para);
|
||||
else if (ths_drv_ver == 3)
|
||||
ths_driver_init_new_shut_reg(ths_data, reg, para);
|
||||
}
|
||||
|
||||
int ths_driver_init_reg(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_sensor_coefficent *ths_coefficent)
|
||||
{
|
||||
|
||||
int calibration_reg_num = 0;
|
||||
int interrupt_sta_reg_num = 0;
|
||||
struct thermal_reg *cali_reg = NULL;
|
||||
struct thermal_reg *thermal_reg = NULL;
|
||||
struct temp_calculate_coefficent *cpara = NULL;
|
||||
int ths_drv_ver;
|
||||
|
||||
status_reg = NULL;
|
||||
data_reg = NULL;
|
||||
enable_reg = NULL;
|
||||
cpara = ths_coefficent->calcular_para;
|
||||
thermal_reg = ths_coefficent->reg_para;
|
||||
ths_drv_ver = get_ths_driver_version(ths_data);
|
||||
|
||||
/*do the init until the name is nothing*/
|
||||
while (strcmp("", thermal_reg->name)) {
|
||||
|
||||
switch (thermal_reg->init_type) {
|
||||
case NO_INIT:
|
||||
break;
|
||||
|
||||
case NORMAL_REG:
|
||||
writel((thermal_reg->value),
|
||||
ths_data->base_addr + (thermal_reg->offset));
|
||||
break;
|
||||
|
||||
case ENABLE_REG:
|
||||
if ((ths_drv_ver == 1) || (ths_drv_ver == 2))
|
||||
writel(thermal_reg->value,
|
||||
ths_data->base_addr + thermal_reg->offset);
|
||||
enable_reg = thermal_reg;
|
||||
break;
|
||||
|
||||
case CDATA_REG:
|
||||
calibration_reg_num++;
|
||||
if (calibration_reg_num == 1)
|
||||
cali_reg = thermal_reg;
|
||||
break;
|
||||
|
||||
case INT_STA_REG:
|
||||
/**
|
||||
*Here clear interrupt status,and find the data
|
||||
*interrupt status reg.
|
||||
*/
|
||||
interrupt_sta_reg_num++;
|
||||
if (interrupt_sta_reg_num == 1)
|
||||
status_reg = thermal_reg;
|
||||
writel((thermal_reg->value),
|
||||
ths_data->base_addr + (thermal_reg->offset));
|
||||
break;
|
||||
|
||||
case SHT_TMP_REG:
|
||||
ths_driver_init_shut_temp_reg(ths_data, thermal_reg,
|
||||
cpara);
|
||||
break;
|
||||
|
||||
case TDATA_REG:
|
||||
if (!strcmp("THS_0_DATA_REG", thermal_reg->name))
|
||||
data_reg = thermal_reg;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
thsprintk("reg_name=%s, offset=0x%x, value=%x, reg_value=%x\n",
|
||||
thermal_reg->name,
|
||||
thermal_reg->offset,
|
||||
thermal_reg->value,
|
||||
readl(ths_data->base_addr + (thermal_reg->offset)));
|
||||
thermal_reg++;
|
||||
}
|
||||
|
||||
ths_driver_write_efuse_to_reg(ths_data, calibration_reg_num, cali_reg);
|
||||
ths_driver_enable_enreg(ths_data, enable_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ths_driver_disable_old_new_enreg(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_reg *reg)
|
||||
{
|
||||
writel(DISABLE_EN_REG, ths_data->base_addr + reg->offset);
|
||||
}
|
||||
|
||||
static void ths_driver_disable_chop_reg(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
writel(0, ths_data->base_addr + 0x04);
|
||||
}
|
||||
|
||||
void ths_driver_disable_reg(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
thsprintk("clear enbale config!\n");
|
||||
if (ths_data->ths_driver_version == 1)
|
||||
ths_driver_disable_chop_reg(ths_data);
|
||||
ths_driver_disable_old_new_enreg(ths_data, enable_reg);
|
||||
}
|
||||
|
153
drivers/thermal/sunxi_thermal/sunxi_ths_core.h
Normal file
153
drivers/thermal/sunxi_thermal/sunxi_ths_core.h
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* drivers/thermal/sunxi_ths_core.h
|
||||
*
|
||||
* Copyright (C) 2013-2024 allwinner.
|
||||
* JiaRui Xiao<xiaojiarui@allwinnertech.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __SUNXI_THS_CORE_H__
|
||||
#define __SUNXI_THS_CORE_H__
|
||||
|
||||
/* GET TMEP RETURN VALUE */
|
||||
#define NO_DATA_INTTERUPT (-41)
|
||||
#define OUT_THE_THERMAL_LIMIT (-42)
|
||||
#define WRONG_SENSOR_ID (-43)
|
||||
|
||||
#define MAX_SENSOR_NUM (5)
|
||||
#define REG_HAVE_SHUT_TEMP_NUM (2)
|
||||
#define THERMAL_ATTR_LENGTH (25)
|
||||
#define THERMAL_DATA_REG_MAX 0xfff
|
||||
|
||||
#define thsprintk(fmt, arg...) \
|
||||
pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
||||
|
||||
#define to_thermal_sensor_device(x) \
|
||||
container_of((x), struct sunxi_ths_data, pdev)
|
||||
|
||||
#define SETMASK(width, shift) ((width ? ((-1U) >> (32-width)) : 0) << (shift))
|
||||
#define CLRMASK(width, shift) (~(SETMASK(width, shift)))
|
||||
#define GET_BITS(shift, width, reg) \
|
||||
(((reg) & SETMASK(width, shift)) >> (shift))
|
||||
#define SET_BITS(shift, width, reg, val) \
|
||||
(((reg) & CLRMASK(width, shift)) | (val << (shift)))
|
||||
|
||||
struct ths_info_attr {
|
||||
struct device_attribute attr;
|
||||
char name[THERMAL_ATTR_LENGTH];
|
||||
};
|
||||
|
||||
struct sunxi_ths_data {
|
||||
void __iomem *base_addr;
|
||||
struct mutex ths_mutex_lock;
|
||||
struct platform_device *pdev;
|
||||
struct sunxi_ths_controller *ctrl;
|
||||
struct thermal_sensor_coefficent *ths_coefficent;
|
||||
struct clk *mclk;
|
||||
struct clk *pclk;
|
||||
u32 ths_driver_version;
|
||||
bool parent_clk;
|
||||
int irq;
|
||||
int shut_temp;
|
||||
int sensor_cnt;
|
||||
int combine_cnt;
|
||||
int alarm_low_temp;
|
||||
int alarm_high_temp;
|
||||
int alarm_temp_hysteresis;
|
||||
int cp_ft_flag; /* tell us what calibration they used. 00 cp, 01 ft */
|
||||
void *data; /* specific sensor data */
|
||||
struct ths_info_attr *ths_info_attrs;
|
||||
struct sunxi_ths_sensor **comb_sensor;
|
||||
};
|
||||
|
||||
/**
|
||||
* to recored the single sensor data about name and temp
|
||||
*/
|
||||
struct thermal_sensor_info {
|
||||
int id;
|
||||
char *ths_name;
|
||||
int temp;
|
||||
int alarm_irq_type;
|
||||
bool irq_enabled;
|
||||
};
|
||||
|
||||
/*
|
||||
* @NO_INIT: this thermal reg doesn't need to init.
|
||||
* @NORMAL_REG: this thermal reg just write the init value.
|
||||
* @ENABLE_REG: this thermal reg used to enable the thermal sensor.
|
||||
* @CDATA_OLD_REG: this thermal reg use old method to calibration thermal
|
||||
* sensor.
|
||||
* @CDATA_NEW_REG: this thermal reg use new method to calibration thermal
|
||||
* sensor.
|
||||
* @INT_STA_REG: this thermal reg show the thermal sensor status
|
||||
* SHT_TMP_REG:this thermal reg will compara the TDATA_REG about temp, and
|
||||
* if the TDATA_REG temp big than this reg will do interrput if you ennale.
|
||||
* @TDATA_REG:this thermal reg show the thermal sensor calcular temp.
|
||||
* };
|
||||
*/
|
||||
enum reg_type {
|
||||
NO_INIT = 0,
|
||||
NORMAL_REG,
|
||||
ENABLE_REG,
|
||||
CDATA_REG,
|
||||
INT_STA_REG,
|
||||
SHT_TMP_REG,
|
||||
TDATA_REG
|
||||
};
|
||||
|
||||
struct thermal_reg {
|
||||
char name[32];
|
||||
u32 offset;
|
||||
u32 value;
|
||||
u32 init_type;
|
||||
};
|
||||
|
||||
struct normal_temp_para {
|
||||
u32 MUL_PARA;
|
||||
u32 DIV_PARA;
|
||||
u32 MINU_PARA;
|
||||
};
|
||||
|
||||
struct split_temp_point {
|
||||
long split_temp;
|
||||
u32 split_reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* temperature = ( MINUPA - reg * MULPA) / DIVPA
|
||||
*/
|
||||
struct temp_calculate_coefficent {
|
||||
struct split_temp_point down_limit;
|
||||
struct split_temp_point up_limit;
|
||||
struct normal_temp_para nt_para[MAX_SENSOR_NUM];
|
||||
};
|
||||
|
||||
/**
|
||||
* this struct include all of the sensor driver init para
|
||||
*/
|
||||
struct thermal_sensor_coefficent {
|
||||
struct temp_calculate_coefficent *calcular_para;
|
||||
struct thermal_reg *reg_para;
|
||||
};
|
||||
|
||||
extern int ths_driver_init_reg(struct sunxi_ths_data *,
|
||||
struct thermal_sensor_coefficent *);
|
||||
extern void ths_driver_reg_debug(struct sunxi_ths_data *,
|
||||
struct thermal_sensor_coefficent *);
|
||||
extern int ths_driver_get_temp(struct sunxi_ths_data *,
|
||||
int);
|
||||
extern int ths_driver_startup(struct sunxi_ths_data *,
|
||||
struct device *);
|
||||
extern void ths_driver_disable_reg(struct sunxi_ths_data *);
|
||||
extern void ths_driver_clk_cfg(struct sunxi_ths_data *);
|
||||
extern void ths_driver_clk_uncfg(struct sunxi_ths_data *);
|
||||
extern void ths_drvier_remove_trip_attrs(struct sunxi_ths_data *);
|
||||
extern int ths_driver_create_sensor_info_attrs(struct sunxi_ths_data *,
|
||||
struct thermal_sensor_info *);
|
||||
extern u32 ths_driver_temp_to_reg(int temp, u16 id,
|
||||
struct temp_calculate_coefficent *para);
|
||||
|
||||
#endif
|
635
drivers/thermal/sunxi_thermal/sunxi_ths_driver.c
Normal file
635
drivers/thermal/sunxi_thermal/sunxi_ths_driver.c
Normal file
|
@ -0,0 +1,635 @@
|
|||
/*
|
||||
* drivers/thermal/sunxi_thermal/sunxi_ths_driver.c
|
||||
*
|
||||
* Copyright (C) 2013-2024 allwinner.
|
||||
* JiaRui Xiao<xiaojiarui@allwinnertech.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#define NEED_DEBUG (0)
|
||||
|
||||
#if NEED_DEBUG
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/sunxi-sid.h>
|
||||
#include "sunxi_ths.h"
|
||||
#include "sunxi_ths_core.h"
|
||||
#include "sunxi_ths_driver.h"
|
||||
|
||||
static struct sunxi_ths_controller *main_ctrl;
|
||||
#define THS_INFO(fmt, arg...) \
|
||||
pr_warn("[ths]: %s()%d - "fmt, __func__, __LINE__, ##arg)
|
||||
|
||||
/**
|
||||
*Init the thermal sensor and show them value in screen
|
||||
*/
|
||||
static void sunxi_ths_reg_init(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
ths_driver_init_reg(ths_data, ths_data->ths_coefficent);
|
||||
ths_driver_reg_debug(ths_data, ths_data->ths_coefficent);
|
||||
return;
|
||||
}
|
||||
|
||||
static void sunxi_ths_exit(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
ths_driver_disable_reg(ths_data);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SUNXI_THERMAL_SUPPORT_IRQ
|
||||
static void sunxi_ths_set_alarm_threshold_temp(struct sunxi_ths_data *ths_data,
|
||||
u32 id)
|
||||
{
|
||||
u32 reg, alarm_threshold_temp = 0, alarm_threshold_value = 0;
|
||||
struct thermal_sensor_info *sensor_info =
|
||||
(struct thermal_sensor_info *)ths_data->data;
|
||||
|
||||
if (sensor_info[id].alarm_irq_type == THS_LOW_TEMP_ALARM)
|
||||
alarm_threshold_temp = ths_data->alarm_low_temp;
|
||||
else if (sensor_info[id].alarm_irq_type == THS_HIGH_TEMP_ALARM)
|
||||
alarm_threshold_temp = ths_data->alarm_high_temp;
|
||||
|
||||
alarm_threshold_value = ths_driver_temp_to_reg(alarm_threshold_temp,
|
||||
id, ths_data->ths_coefficent->calcular_para);
|
||||
|
||||
reg = readl(ths_data->base_addr
|
||||
+ ths_hw_sensor[id].alarm->threshold.reg);
|
||||
reg = SET_BITS(ths_hw_sensor[id].alarm->threshold.shift,
|
||||
ths_hw_sensor[id].alarm->threshold.width,
|
||||
reg, alarm_threshold_value);
|
||||
writel(reg, ths_data->base_addr
|
||||
+ ths_hw_sensor[id].alarm->threshold.reg);
|
||||
|
||||
/* Don't need alarm off intterup, so set the alarm off temp 0 */
|
||||
alarm_threshold_value = ths_driver_temp_to_reg(0,
|
||||
id, ths_data->ths_coefficent->calcular_para);
|
||||
reg = readl(ths_data->base_addr
|
||||
+ ths_hw_sensor[id].alarm->threshold.reg);
|
||||
reg = SET_BITS(ths_hw_sensor[id].alarm_off->threshold.shift,
|
||||
ths_hw_sensor[id].alarm->threshold.width,
|
||||
reg, alarm_threshold_value);
|
||||
writel(reg, ths_data->base_addr
|
||||
+ ths_hw_sensor[id].alarm->threshold.reg);
|
||||
}
|
||||
|
||||
static int sunxi_ths_set_shut_threshold_temp(struct sunxi_ths_data *ths_data,
|
||||
u32 id)
|
||||
{
|
||||
u32 reg, shut_threshold_value = 0;
|
||||
|
||||
shut_threshold_value = ths_driver_temp_to_reg(ths_data->shut_temp,
|
||||
id, ths_data->ths_coefficent->calcular_para);
|
||||
|
||||
reg = readl(ths_data->base_addr
|
||||
+ ths_hw_sensor[id].shut->threshold.reg);
|
||||
reg = SET_BITS(ths_hw_sensor[id].shut->threshold.shift,
|
||||
ths_hw_sensor[id].shut->threshold.width,
|
||||
reg, shut_threshold_value);
|
||||
writel(reg, ths_data->base_addr
|
||||
+ ths_hw_sensor[id].shut->threshold.reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunxi_ths_clr_alarm_irq_pending(struct sunxi_ths_data *ths_data,
|
||||
u32 id)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(ths_data->base_addr +
|
||||
ths_hw_sensor[id].alarm->irq_status.reg);
|
||||
reg = SET_BITS(ths_hw_sensor[id].alarm->irq_status.shift, 1, reg, 0x1);
|
||||
writel(reg, ths_data->base_addr +
|
||||
ths_hw_sensor[id].alarm->irq_status.reg);
|
||||
|
||||
reg = readl(ths_data->base_addr +
|
||||
ths_hw_sensor[id].alarm_off->irq_status.reg);
|
||||
reg = SET_BITS(ths_hw_sensor[id].alarm_off->irq_status.shift, 1,
|
||||
reg, 0x1);
|
||||
writel(reg, ths_data->base_addr +
|
||||
ths_hw_sensor[id].alarm_off->irq_status.reg);
|
||||
}
|
||||
|
||||
static void sunxi_ths_disable_alarm_irq(struct sunxi_ths_data *ths_data, u32 id)
|
||||
{
|
||||
u32 reg;
|
||||
struct thermal_sensor_info *sensor_info =
|
||||
(struct thermal_sensor_info *)ths_data->data;
|
||||
|
||||
reg = readl(ths_data->base_addr +
|
||||
ths_hw_sensor[id].alarm->irq_enable.reg);
|
||||
reg = SET_BITS(ths_hw_sensor[id].alarm->irq_enable.shift, 1, reg, 0x0);
|
||||
writel(reg, ths_data->base_addr +
|
||||
ths_hw_sensor[id].alarm->irq_enable.reg);
|
||||
sensor_info[id].irq_enabled = false;
|
||||
|
||||
}
|
||||
|
||||
static void sunxi_ths_disable_shut_irq(struct sunxi_ths_data *ths_data, u32 id)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(ths_data->base_addr +
|
||||
ths_hw_sensor[id].shut->irq_enable.reg);
|
||||
reg = SET_BITS(ths_hw_sensor[id].shut->irq_enable.shift, 1, reg, 0x0);
|
||||
writel(reg, ths_data->base_addr +
|
||||
ths_hw_sensor[id].shut->irq_enable.reg);
|
||||
}
|
||||
|
||||
static void sunxi_ths_clr_shut_irq_pending(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
u32 reg, id;
|
||||
|
||||
/* read irq status,if the bit is 1, then write 1 to clear it */
|
||||
for (id = 0; id < ths_data->sensor_cnt; id++) {
|
||||
reg = readl(ths_data->base_addr +
|
||||
ths_hw_sensor[id].shut->irq_status.reg);
|
||||
writel(reg, ths_data->base_addr +
|
||||
ths_hw_sensor[id].shut->irq_status.reg);
|
||||
}
|
||||
}
|
||||
|
||||
static int sunxi_ths_enable_alarm_irq_sensor(struct sunxi_ths_data *data, u32 i)
|
||||
{
|
||||
u32 reg;
|
||||
struct thermal_sensor_info *sensor_info =
|
||||
(struct thermal_sensor_info *)data->data;
|
||||
|
||||
/* clear irq pending before enable irq */
|
||||
sunxi_ths_clr_alarm_irq_pending(data, i);
|
||||
|
||||
/*enable irq control*/
|
||||
reg = readl(data->base_addr + ths_hw_sensor[i].alarm->irq_enable.reg);
|
||||
|
||||
reg = SET_BITS(ths_hw_sensor[i].alarm->irq_enable.shift, 1, reg, 0x1);
|
||||
writel(reg, data->base_addr + ths_hw_sensor[i].alarm->irq_enable.reg);
|
||||
sensor_info[i].irq_enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init enable alarm irq ctrl */
|
||||
static void sunxi_ths_enable_alarm_irq_ctrl(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
u32 i;
|
||||
struct thermal_sensor_info *sensor_info =
|
||||
(struct thermal_sensor_info *)ths_data->data;
|
||||
|
||||
for (i = 0; i < ths_data->sensor_cnt; i++) {
|
||||
sensor_info[i].alarm_irq_type = THS_LOW_TEMP_ALARM;
|
||||
sunxi_ths_set_alarm_threshold_temp(ths_data, i);
|
||||
}
|
||||
for (i = 0; i < ths_data->sensor_cnt; i++)
|
||||
sunxi_ths_enable_alarm_irq_sensor(ths_data, i);
|
||||
}
|
||||
|
||||
static int sunxi_ths_enable_sensor_shut_irq(struct sunxi_ths_data *ths_data,
|
||||
u32 id)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
sunxi_ths_clr_shut_irq_pending(ths_data);
|
||||
reg = readl(ths_data->base_addr +
|
||||
ths_hw_sensor[id].shut->irq_enable.reg);
|
||||
reg = SET_BITS(ths_hw_sensor[id].alarm->irq_enable.shift, 1, reg, 0x1);
|
||||
writel(reg, ths_data->base_addr +
|
||||
ths_hw_sensor[id].shut->irq_enable.reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunxi_ths_enable_shut_irq_ctrl(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < ths_data->sensor_cnt; i++)
|
||||
sunxi_ths_set_shut_threshold_temp(ths_data, i);
|
||||
|
||||
for (i = 0; i < ths_data->sensor_cnt; i++)
|
||||
sunxi_ths_enable_sensor_shut_irq(ths_data, i);
|
||||
|
||||
}
|
||||
|
||||
static u32 sunxi_ths_query_alarmirq_pending(struct sunxi_ths_data *data, u32 id)
|
||||
{
|
||||
u32 reg, irq_mask;
|
||||
|
||||
irq_mask = 1 << ths_hw_sensor[id].alarm->irq_status.shift;
|
||||
reg = readl(data->base_addr +
|
||||
ths_hw_sensor[id].alarm->irq_status.reg);
|
||||
|
||||
return reg&irq_mask;
|
||||
}
|
||||
|
||||
static u32 sunxi_ths_query_shutirq_pending(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
u32 reg, i;
|
||||
|
||||
for (i = 0; i < ths_data->sensor_cnt; i++) {
|
||||
reg = readl(ths_data->base_addr +
|
||||
ths_hw_sensor[i].shut->irq_status.reg);
|
||||
if (reg)
|
||||
return reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunxi_ths_handle_irq_pending(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
u32 id;
|
||||
struct thermal_sensor_info *sensor_info =
|
||||
(struct thermal_sensor_info *)ths_data->data;
|
||||
|
||||
for (id = 0; id < ths_data->sensor_cnt; id++) {
|
||||
if (sensor_info[id].irq_enabled) {
|
||||
if (sunxi_ths_query_alarmirq_pending(ths_data, id)) {
|
||||
THS_INFO("sensor[%d]&irq-type=%d\n", id,
|
||||
sensor_info[id].alarm_irq_type);
|
||||
if (sensor_info[id].alarm_irq_type ==
|
||||
THS_LOW_TEMP_ALARM) {
|
||||
sensor_info[id].alarm_irq_type =
|
||||
THS_HIGH_TEMP_ALARM;
|
||||
sunxi_ths_set_alarm_threshold_temp(
|
||||
ths_data, id);
|
||||
} else
|
||||
sunxi_ths_disable_alarm_irq(
|
||||
ths_data, id);
|
||||
}
|
||||
}
|
||||
sunxi_ths_clr_alarm_irq_pending(ths_data, id);
|
||||
}
|
||||
|
||||
/* query shutdowm irq status and handle it*/
|
||||
if (sunxi_ths_query_shutirq_pending(ths_data)) {
|
||||
THS_INFO("thermal:shutdown intterupt!\n");
|
||||
for (id = 0; id < ths_data->sensor_cnt; id++)
|
||||
sunxi_ths_disable_shut_irq(ths_data, id);
|
||||
sunxi_ths_clr_shut_irq_pending(ths_data);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t sunxi_ths_alarm_irq(int irq, void *dev)
|
||||
{
|
||||
struct sunxi_ths_data *ths_data = dev;
|
||||
|
||||
THS_INFO("alarm_irq\n");
|
||||
disable_irq_nosync(irq);
|
||||
sunxi_ths_handle_irq_pending(ths_data);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t sunxi_ths_alarm_irq_thread(int irq, void *dev)
|
||||
{
|
||||
struct sunxi_ths_data *ths_data = dev;
|
||||
int i;
|
||||
struct thermal_sensor_info *sensor_info =
|
||||
(struct thermal_sensor_info *)ths_data->data;
|
||||
|
||||
for (i = 0; i < ths_data->combine_cnt; i++) {
|
||||
thermal_zone_device_update(ths_data->comb_sensor[i]->tz);
|
||||
THS_INFO("sensor[%d]_alarmtemp = %d\n", i, sensor_info[i].temp);
|
||||
}
|
||||
THS_INFO("alarm_irq_therad\n");
|
||||
enable_irq(irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sunxi_ths_get_sensor_combine_id(struct sunxi_ths_data *ths_data,
|
||||
u32 sensor_id)
|
||||
{
|
||||
u32 i, j;
|
||||
struct sunxi_ths_combine_disc *combine;
|
||||
|
||||
for (i = 0; i < ths_data->combine_cnt; i++) {
|
||||
combine = ths_data->comb_sensor[i]->combine;
|
||||
for (j = 0; j < combine->combine_ths_count; j++) {
|
||||
if (sensor_id == combine->combine_ths_id[j])
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
THS_INFO("ths get combind id failed!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#else
|
||||
static void sunxi_ths_enable_alarm_irq_ctrl(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void sunxi_ths_enable_shut_irq_ctrl(struct sunxi_ths_data *ths_data)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sunxi_ths_get_temp(struct sunxi_ths_controller *controller, u32 id,
|
||||
int *temp)
|
||||
{
|
||||
int t = 0;
|
||||
struct sunxi_ths_data *ths_data =
|
||||
(struct sunxi_ths_data *)controller->data;
|
||||
struct thermal_sensor_info *sensor_info =
|
||||
(struct thermal_sensor_info *)ths_data->data;
|
||||
#ifdef SUNXI_THERMAL_SUPPORT_IRQ
|
||||
int combine_id = sunxi_ths_get_sensor_combine_id(ths_data, id);
|
||||
enum thermal_device_mode mode;
|
||||
struct thermal_zone_device *tz = ths_data->comb_sensor[combine_id]->tz;
|
||||
#endif
|
||||
|
||||
if (ths_data->sensor_cnt <= id) {
|
||||
thsprintk("ths driver get wrong sensor num!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if sensor data is not ready, return the last time temp */
|
||||
t = ths_driver_get_temp(ths_data, id);
|
||||
|
||||
if (t == NO_DATA_INTTERUPT) {
|
||||
t = sensor_info[id].temp;
|
||||
}
|
||||
|
||||
if (-40 > t || 180 < t) {
|
||||
thsprintk("ths driver get unvalid temp!\n");
|
||||
return t;
|
||||
}
|
||||
|
||||
sensor_info[id].temp = t;
|
||||
*temp = t;
|
||||
|
||||
#ifdef SUNXI_THERMAL_SUPPORT_IRQ
|
||||
tz->ops->get_mode(tz, &mode);
|
||||
if (combine_id >= 0 && mode == THERMAL_DEVICE_PERFORMANCE) {
|
||||
sunxi_ths_disable_alarm_irq(ths_data, id);
|
||||
sunxi_ths_disable_shut_irq(ths_data, id);
|
||||
return 0;
|
||||
}
|
||||
if (t < (ths_data->alarm_high_temp - ths_data->alarm_temp_hysteresis)) {
|
||||
if (!sensor_info[id].irq_enabled) {
|
||||
sunxi_ths_enable_alarm_irq_sensor(ths_data, id);
|
||||
sunxi_ths_enable_sensor_shut_irq(ths_data, id);
|
||||
}
|
||||
|
||||
if (sensor_info[id].alarm_irq_type == THS_HIGH_TEMP_ALARM) {
|
||||
sensor_info[id].alarm_irq_type = THS_LOW_TEMP_ALARM;
|
||||
sunxi_ths_set_alarm_threshold_temp(ths_data, id);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunxi_ths_info_init(struct thermal_sensor_info *sensor_info,
|
||||
int sensor_num)
|
||||
{
|
||||
sensor_num -= 1;
|
||||
|
||||
while (sensor_num >= 0) {
|
||||
sensor_info[sensor_num].id = sensor_num;
|
||||
sensor_info[sensor_num].ths_name = id_name_mapping[sensor_num];
|
||||
sensor_info[sensor_num].temp = 0;
|
||||
sensor_info[sensor_num].alarm_irq_type = THS_LOW_TEMP_ALARM;
|
||||
sensor_info[sensor_num].irq_enabled = false;
|
||||
thsprintk("sensor_info:id=%d,name=%s,temp=%d!\n",
|
||||
sensor_info[sensor_num].id,
|
||||
sensor_info[sensor_num].ths_name,
|
||||
sensor_info[sensor_num].temp);
|
||||
sensor_num--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void sunxi_ths_coefficent_init(struct thermal_sensor_coefficent *ths_coe)
|
||||
{
|
||||
ths_coe->calcular_para = thermal_cal_coefficent;
|
||||
ths_coe->reg_para = thermal_reg_init;
|
||||
return;
|
||||
}
|
||||
|
||||
static void sunxi_ths_para_init(struct sunxi_ths_data *ths_data,
|
||||
struct thermal_sensor_info *sensor_info)
|
||||
{
|
||||
sunxi_ths_info_init(sensor_info, ths_data->sensor_cnt);
|
||||
sunxi_ths_coefficent_init(ths_data->ths_coefficent);
|
||||
ths_data->data = sensor_info;
|
||||
return;
|
||||
}
|
||||
|
||||
int sunxi_get_sensor_temp(u32 sensor_num, int *temperature)
|
||||
{
|
||||
return sunxi_ths_get_temp(main_ctrl, sensor_num, temperature);
|
||||
}
|
||||
EXPORT_SYMBOL(sunxi_get_sensor_temp);
|
||||
|
||||
static int sunxi_ths_suspend(struct sunxi_ths_controller *controller)
|
||||
{
|
||||
struct sunxi_ths_data *ths_data =
|
||||
(struct sunxi_ths_data *)controller->data;
|
||||
|
||||
thsprintk("enter: sunxi_ths_controller_suspend.\n");
|
||||
atomic_set(&controller->is_suspend, 1);
|
||||
sunxi_ths_exit(ths_data);
|
||||
if (ths_data->parent_clk == true)
|
||||
clk_disable_unprepare(ths_data->mclk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_ths_resume(struct sunxi_ths_controller *controller)
|
||||
{
|
||||
struct sunxi_ths_data *ths_data =
|
||||
(struct sunxi_ths_data *)controller->data;
|
||||
|
||||
thsprintk("enter: sunxi_ths_controller_resume.\n");
|
||||
if (ths_data->parent_clk == true)
|
||||
clk_prepare_enable(ths_data->mclk);
|
||||
sunxi_ths_reg_init(ths_data);
|
||||
#ifdef SUNXI_THERMAL_SUPPORT_IRQ
|
||||
sunxi_ths_enable_alarm_irq_ctrl(ths_data);
|
||||
sunxi_ths_enable_shut_irq_ctrl(ths_data);
|
||||
#endif
|
||||
atomic_set(&controller->is_suspend, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sunxi_ths_controller_ops sunxi_ths_ops = {
|
||||
.suspend = sunxi_ths_suspend,
|
||||
.resume = sunxi_ths_resume,
|
||||
.get_temp = sunxi_ths_get_temp,
|
||||
};
|
||||
|
||||
static int sunxi_ths_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err = 0;
|
||||
struct sunxi_ths_data *ths_data;
|
||||
struct sunxi_ths_controller *ctrl;
|
||||
struct thermal_sensor_info *sensor_info;
|
||||
|
||||
thsprintk("sunxi ths sensor probe start !\n");
|
||||
if (!pdev->dev.of_node) {
|
||||
pr_err("%s:sunxi ths device tree err!\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ths_data = kzalloc(sizeof(*ths_data), GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(ths_data)) {
|
||||
pr_err("ths_data: not enough memory for ths_data\n");
|
||||
err = -ENOMEM;
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
ths_data->ths_coefficent =
|
||||
kzalloc(sizeof(*ths_data->ths_coefficent), GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(ths_data->ths_coefficent)) {
|
||||
pr_err("ths_coe: not enough memory for ths_coe\n");
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
mutex_init(&ths_data->ths_mutex_lock);
|
||||
ths_data->parent_clk = ENABLE_CLK;
|
||||
ths_data->ths_driver_version = THERMAL_VERSION;
|
||||
ths_data->cp_ft_flag = 0;
|
||||
ths_data->pdev = pdev;
|
||||
|
||||
err = ths_driver_startup(ths_data, &pdev->dev);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
ths_data->comb_sensor = kzalloc(ths_data->combine_cnt *
|
||||
(sizeof(struct sunxi_ths_sensor *)), GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(ths_data->comb_sensor)) {
|
||||
pr_err("ths_comb_sensor: not enough memory to alloc!\n");
|
||||
err = -ENOMEM;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
#ifdef SUNXI_THERMAL_SUPPORT_IRQ
|
||||
err = devm_request_threaded_irq(&pdev->dev, ths_data->irq,
|
||||
sunxi_ths_alarm_irq,
|
||||
sunxi_ths_alarm_irq_thread,
|
||||
0, "sunxi_thermal", ths_data);
|
||||
if (err < 0) {
|
||||
pr_err("failed to request alarm irq: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
sensor_info =
|
||||
kcalloc(ths_data->sensor_cnt, sizeof(*sensor_info), GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(sensor_info)) {
|
||||
pr_err("sensor_info: not enough memory for sensor_info\n");
|
||||
err = -ENOMEM;
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
sunxi_ths_para_init(ths_data, sensor_info);
|
||||
platform_set_drvdata(pdev, ths_data);
|
||||
ths_driver_clk_cfg(ths_data);
|
||||
sunxi_ths_reg_init(ths_data);
|
||||
sunxi_ths_enable_alarm_irq_ctrl(ths_data);
|
||||
sunxi_ths_enable_shut_irq_ctrl(ths_data);
|
||||
|
||||
ths_driver_create_sensor_info_attrs(ths_data, sensor_info);
|
||||
|
||||
ctrl = sunxi_ths_controller_register(&pdev->dev,
|
||||
&sunxi_ths_ops, ths_data);
|
||||
if (!ctrl) {
|
||||
pr_err("ths_data: thermal controller register err.\n");
|
||||
err = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
ths_data->ctrl = ctrl;
|
||||
main_ctrl = ctrl;
|
||||
thsprintk("sunxi_ths_controller is ok.\n");
|
||||
|
||||
return 0;
|
||||
fail4:
|
||||
kfree(sensor_info);
|
||||
sensor_info = NULL;
|
||||
fail3:
|
||||
kfree(ths_data->comb_sensor);
|
||||
ths_data->comb_sensor = NULL;
|
||||
fail2:
|
||||
kfree(ths_data->ths_coefficent);
|
||||
ths_data->ths_coefficent = NULL;
|
||||
fail1:
|
||||
kfree(ths_data);
|
||||
ths_data = NULL;
|
||||
fail0:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sunxi_ths_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sunxi_ths_data *ths_data = platform_get_drvdata(pdev);
|
||||
sunxi_ths_controller_unregister(ths_data->ctrl);
|
||||
sunxi_ths_exit(ths_data);
|
||||
ths_driver_clk_uncfg(ths_data);
|
||||
ths_drvier_remove_trip_attrs(ths_data);
|
||||
kfree(ths_data->ths_coefficent);
|
||||
kfree(ths_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* Translate OpenFirmware node properties into platform_data */
|
||||
static struct of_device_id sunxi_ths_of_match[] = {
|
||||
{.compatible = "allwinner,thermal_sensor",},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, sunxi_ths_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver sunxi_ths_driver = {
|
||||
.probe = sunxi_ths_probe,
|
||||
.remove = sunxi_ths_remove,
|
||||
.driver = {
|
||||
.name = SUNXI_THS_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(sunxi_ths_of_match),
|
||||
},
|
||||
};
|
||||
static int __init sunxi_thermal_sensor_init(void)
|
||||
{
|
||||
return platform_driver_register(&sunxi_ths_driver);
|
||||
}
|
||||
|
||||
static void __exit sunxi_thermal_sensor_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sunxi_ths_driver);
|
||||
}
|
||||
|
||||
subsys_initcall_sync(sunxi_thermal_sensor_init);
|
||||
module_exit(sunxi_thermal_sensor_exit);
|
||||
|
||||
MODULE_DESCRIPTION("SUNXI thermal sensor driver");
|
||||
MODULE_AUTHOR("JRXiao");
|
||||
MODULE_LICENSE("GPL v2");
|
495
drivers/thermal/sunxi_thermal/sunxi_ths_driver.h
Normal file
495
drivers/thermal/sunxi_thermal/sunxi_ths_driver.h
Normal file
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
* drivers/thermal/sunxi_ths_driver.h
|
||||
*
|
||||
* Copyright (C) 2013-2024 allwinner.
|
||||
* JiaRui Xiao<xiaojiarui@allwinnertech.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_SENSOR_DRIVER_H
|
||||
#define THERMAL_SENSOR_DRIVER_H
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN8IW5)
|
||||
#define THERMAL_VERSION 1
|
||||
#elif defined(CONFIG_ARCH_SUN8IW10) || defined(CONFIG_ARCH_SUN8IW11) || \
|
||||
defined(CONFIG_ARCH_SUN50IW1) || defined(CONFIG_ARCH_SUN50IW2)
|
||||
#define THERMAL_VERSION 2
|
||||
#elif defined(CONFIG_ARCH_SUN50IW3) || defined(CONFIG_ARCH_SUN50IW6)
|
||||
#define THERMAL_VERSION 3
|
||||
#endif
|
||||
|
||||
enum alarm_irq_temp_type {
|
||||
THS_LOW_TEMP_ALARM = 0,
|
||||
THS_HIGH_TEMP_ALARM,
|
||||
};
|
||||
|
||||
struct ths_irq_enable {
|
||||
u32 reg;
|
||||
u8 shift;
|
||||
};
|
||||
|
||||
struct ths_irq_status {
|
||||
u32 reg;
|
||||
u8 shift;
|
||||
};
|
||||
|
||||
struct ths_threshold {
|
||||
u32 reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
};
|
||||
|
||||
struct sunxi_thermal_hw_ctrl {
|
||||
struct ths_irq_enable irq_enable;
|
||||
struct ths_irq_status irq_status;
|
||||
struct ths_threshold threshold;
|
||||
};
|
||||
|
||||
/*
|
||||
struct sunxi_thermal_hw_shut {
|
||||
struct ths_irq_enable shut_irq_enable;
|
||||
struct ths_irq_status shut_irq_status;
|
||||
struct ths_threshold shut_threshold;
|
||||
}
|
||||
|
||||
*/
|
||||
struct sunxi_thermal_hw_sensor {
|
||||
u8 sensor_id;
|
||||
struct sunxi_thermal_hw_ctrl *alarm;
|
||||
struct sunxi_thermal_hw_ctrl *shut;
|
||||
struct sunxi_thermal_hw_ctrl *alarm_off;
|
||||
};
|
||||
|
||||
#define SUNXI_THERMAL_HW_CTRL(name, irq_enable_reg, \
|
||||
irq_enable_shift, irq_sta_reg, irq_sta_shift, \
|
||||
thresh_reg, thresh_shift, thresh_width) \
|
||||
static struct sunxi_thermal_hw_ctrl sunxi_ths_hw_##name = { \
|
||||
.irq_enable = { \
|
||||
.reg = irq_enable_reg, \
|
||||
.shift = irq_enable_shift, \
|
||||
}, \
|
||||
.irq_status = { \
|
||||
.reg = irq_sta_reg, \
|
||||
.shift = irq_sta_shift, \
|
||||
}, \
|
||||
.threshold = { \
|
||||
.reg = thresh_reg, \
|
||||
.shift = thresh_shift, \
|
||||
.width = thresh_width, \
|
||||
}, \
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN8IW5)
|
||||
|
||||
#define ENABLE_CLK (false)
|
||||
|
||||
|
||||
/* temperature = -0.118*sensor + 256
|
||||
* = (256000 - 118*sensor)/1000
|
||||
* = (262144 - 120.832*sensor)/1024
|
||||
* = (268435456 - 123732*sensor)/1024/1024
|
||||
* = ( MINUPA - reg * MULPA) / 2^DIVPA
|
||||
* sensor value range:
|
||||
* = 0 - 0xffff,ffff/2/123732
|
||||
* = 0 - 17355
|
||||
*/
|
||||
|
||||
char *id_name_mapping[] = {
|
||||
"cpuc0"
|
||||
};
|
||||
|
||||
struct temp_calculate_coefficent thermal_cal_coefficent[] = {
|
||||
[0] = {
|
||||
.down_limit = {(-227), (0xfff)},
|
||||
.up_limit = {(256), (0x000)},
|
||||
.nt_para = {
|
||||
/* MUL_PARA DIV_PARA MINU_PARA */
|
||||
{(1000), (6180), (1662420)},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct thermal_reg thermal_reg_init[] = {
|
||||
/* name address value reg_type*/
|
||||
{"THS_CTRL0_REG", (0x00), (0x002000ff), (NORMAL_REG)},
|
||||
{"THS_CTRL1_REG", (0x04), (0x100), (NORMAL_REG)},
|
||||
{"THS_INT_CTRL_REG", (0x10), (0x0), (NORMAL_REG)},
|
||||
{"THS_INT_STA_REG", (0x14), (0x40000), (INT_STA_REG)},
|
||||
{"THS_PRO_CTRL_REG", (0x18), (0x1005f), (ENABLE_REG)},
|
||||
{"THS_0_DATA_REG", (0x20), (0x0), (TDATA_REG)},
|
||||
{"THS_0_CDATA_REG", (0x40), (0x0), (CDATA_REG)},
|
||||
{"", (0), (0), (0)}
|
||||
};
|
||||
|
||||
#endif /* CONFIG_ARCH_SUN8IW5 */
|
||||
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN8IW10)
|
||||
|
||||
#define ENABLE_CLK (true)
|
||||
|
||||
|
||||
/* temperature = -0.118*sensor + 256
|
||||
* = (256000 - 118*sensor)/1000
|
||||
* = (262144 - 120.832*sensor)/1024
|
||||
* = (268435456 - 123732*sensor)/1024/1024
|
||||
* = ( MINUPA - reg * MULPA) / 2^DIVPA
|
||||
* sensor value range:
|
||||
* = 0 - 0xffff,ffff/2/123732
|
||||
* = 0 - 17355
|
||||
*/
|
||||
|
||||
char *id_name_mapping[] = {
|
||||
"cpuc0",
|
||||
"dram"
|
||||
};
|
||||
|
||||
struct temp_calculate_coefficent thermal_cal_coefficent[] = {
|
||||
[0] = {
|
||||
.down_limit = {(-227), (0xfff)},
|
||||
.up_limit = {(256), (0x000)},
|
||||
.nt_para = {
|
||||
/* MUL_PARA DIV_PARA MINU_PARA */
|
||||
{(1180), (10000), (2560000)},
|
||||
{(1180), (10000), (2560000)}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct thermal_reg thermal_reg_init[] = {
|
||||
/* name address value reg_type*/
|
||||
{"THS_CTRL2_REG", (0x40), (0x01df0000), (ENABLE_REG)},
|
||||
{"THS_INT_CTRL_REG", (0x44), (0x3a030), (NORMAL_REG)},
|
||||
{"THS_INT_STA_REG", (0x48), (0x333), (INT_STA_REG)},
|
||||
{"THS_0_INT_ALM_TH_REG", (0x50), (0x0), (NO_INIT)},
|
||||
{"THS_1_INT_ALM_TH_REG", (0x54), (0x0), (NO_INIT)},
|
||||
{"THS_0_INT_SHUT_TH_REG", (0x60), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_1_INT_SHUT_TH_REG", (0x64), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_FILT_CTRL_REG", (0x70), (0x06), (NORMAL_REG)},
|
||||
{"THS_0_1_CDATA_REG", (0x74), (0x0), (CDATA_REG)},
|
||||
{"THS_0_DATA_REG", (0x80), (0x0), (TDATA_REG)},
|
||||
{"THS_1_DATA_REG", (0x84), (0x0), (TDATA_REG)},
|
||||
{"", (0), (0), (0)}
|
||||
};
|
||||
|
||||
#endif /* CONFIG_ARCH_SUN8IW10 */
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN8IW11)
|
||||
|
||||
#define ENABLE_CLK (true)
|
||||
|
||||
|
||||
|
||||
/* temperature = -0.1125*sensor + 250
|
||||
* = (2500000 - 1125*sensor)/10000
|
||||
* = (xxx - 117964.8*sensor)/1024/1024
|
||||
* = ( MINUPA - reg * MULPA) / 2^DIVPA
|
||||
* sensor value range:
|
||||
* = 0 - 0xffff,ffff/2/117964
|
||||
* = 0 - 18204
|
||||
*/
|
||||
char *id_name_mapping[] = {
|
||||
"cpuc0",
|
||||
"gpu"
|
||||
};
|
||||
|
||||
struct temp_calculate_coefficent thermal_cal_coefficent[] = {
|
||||
[0] = {
|
||||
.down_limit = {(-210), (0xfff)},
|
||||
.up_limit = {(250), (0x000)},
|
||||
/*MUL_PARA DIV_PARA MINU_PARA */
|
||||
.nt_para = {
|
||||
{(1125), (10000), (2500000)},
|
||||
{(1125), (10000), (2500000)}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @THS_INT_CTRL_VALUE:per sampling takes: 10ms
|
||||
* @THS_CTRL0_VALUE:acq time = 20us
|
||||
* @THS_FILT_CTRL_REG :got 1 data for per 8 sampling,
|
||||
* time = 10ms * 8 = 80ms
|
||||
*/
|
||||
struct thermal_reg thermal_reg_init[] = {
|
||||
/* name address value reg_type*/
|
||||
{"THS_CTRL0_REG", (0x00), (0x1df), (NORMAL_REG)},
|
||||
{"THS_CTRL1_REG", (0x04), (0x1<<17), (NORMAL_REG)},
|
||||
{"ADC_CDAT_REG", (0x14), (0x0), (NO_INIT)},
|
||||
{"THS_CTRL2_REG", (0x40), (0x01df0000), (ENABLE_REG)},
|
||||
{"THS_INT_CTRL_REG", (0x44), (0x3a030), (NORMAL_REG)},
|
||||
{"THS_INT_STA_REG", (0x48), (0x333), (INT_STA_REG)},
|
||||
{"THS_0_INT_ALM_TH_REG", (0x50), (0x0), (NO_INIT)},
|
||||
{"THS_1_INT_ALM_TH_REG", (0x54), (0x0), (NO_INIT)},
|
||||
{"THS_0_INT_SHUT_TH_REG", (0x60), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_1_INT_SHUT_TH_REG", (0x64), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_FILT_CTRL_REG", (0x70), (0x06), (NORMAL_REG)},
|
||||
{"THS_0_1_CDATA_REG", (0x74), (0x0), (CDATA_REG)},
|
||||
{"THS_0_DATA_REG", (0x80), (0x0), (TDATA_REG)},
|
||||
{"THS_1_DATA_REG", (0x84), (0x0), (TDATA_REG)},
|
||||
{"", (0), (0), (0)}
|
||||
};
|
||||
|
||||
#endif /* CONFIG_ARCH_SUN8IW11 */
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN50IW1)
|
||||
|
||||
#define ENABLE_CLK (true)
|
||||
|
||||
/* temperature = ( MINUPA - reg * MULPA) / DIVPA */
|
||||
|
||||
char *id_name_mapping[] = {
|
||||
"cpuc0",
|
||||
"cpuc1",
|
||||
"gpu"
|
||||
};
|
||||
|
||||
struct temp_calculate_coefficent thermal_cal_coefficent[] = {
|
||||
[0] = {
|
||||
.down_limit = {(-210), (0xfff)},
|
||||
.up_limit = {(250), (0x000)},
|
||||
.nt_para = {
|
||||
{(1000), (8560), (2170000)},
|
||||
{(1000), (8560), (2170000)},
|
||||
{(1000), (8560), (2170000)}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct thermal_reg thermal_reg_init[] = {
|
||||
/* name address value reg_type*/
|
||||
{"THS_CTRL0_REG", (0x00), (0x190), (NORMAL_REG)},
|
||||
{"THS_CTRL1_REG", (0x04), (0x1<<17), (NORMAL_REG)},
|
||||
{"ADC_CDAT_REG", (0x14), (0x0), (NO_INIT)},
|
||||
{"THS_CTRL2_REG", (0x40), (0x01900000), (ENABLE_REG)},
|
||||
{"THS_INT_CTRL_REG", (0x44), (0x18070), (NORMAL_REG)},
|
||||
{"THS_INT_STA_REG", (0x48), (0x777), (INT_STA_REG)},
|
||||
{"THS_0_INT_ALM_TH_REG", (0x50), (0x0), (NO_INIT)},
|
||||
{"THS_1_INT_ALM_TH_REG", (0x54), (0x0), (NO_INIT)},
|
||||
{"THS_2_INT_ALM_TH_REG", (0x58), (0x0), (NO_INIT)},
|
||||
{"THS_0_INT_SHUT_TH_REG", (0x60), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_1_INT_SHUT_TH_REG", (0x64), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_2_INT_SHUT_TH_REG", (0x68), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_FILT_CTRL_REG", (0x70), (0x06), (NORMAL_REG)},
|
||||
{"THS_0_1_CDATA_REG", (0x74), (0x0), (CDATA_REG)},
|
||||
{"THS_2_CDATA_REG", (0x78), (0x0), (CDATA_REG)},
|
||||
{"THS_0_DATA_REG", (0x80), (0x0), (TDATA_REG)},
|
||||
{"THS_1_DATA_REG", (0x84), (0x0), (TDATA_REG)},
|
||||
{"THS_2_DATA_REG", (0x88), (0x0), (TDATA_REG)},
|
||||
{"", (0), (0), (0)}
|
||||
};
|
||||
|
||||
|
||||
#endif /*CONFIG_ARCH_SUN50IW1 */
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN50IW2)
|
||||
|
||||
#define ENABLE_CLK (true)
|
||||
|
||||
|
||||
/* when temperature <= 70C (sample_data >= 0x500)
|
||||
* temperature = -0.1191*sensor + 223
|
||||
* = (2230000 - 1191*sensor)/10000
|
||||
* = (233832448 - 124885.4016*sensor)/1024/1024
|
||||
* = (233832448 - 124885*sensor)/1024/1024
|
||||
* = ( MINUPA - reg * MULPA) / 2^DIVPA
|
||||
* sensor value range:
|
||||
* = 0 - 0xffff,ffff/2/124885
|
||||
* = 0 - 17195
|
||||
*/
|
||||
char *id_name_mapping[] = {
|
||||
"cpuc0",
|
||||
"gpu"
|
||||
};
|
||||
|
||||
struct temp_calculate_coefficent thermal_cal_coefficent[] = {
|
||||
[0] = {
|
||||
.down_limit = {(-268), (0xfff)},
|
||||
.up_limit = {(70), (0x500)},
|
||||
.nt_para = {
|
||||
{(1191), (10000), (2230000)},
|
||||
{(1191), (10000), (2230000)}
|
||||
}
|
||||
},
|
||||
[1] = {
|
||||
.down_limit = {(70), (0x500)},
|
||||
.up_limit = {(223), (0x000)},
|
||||
.nt_para = {
|
||||
{(1452), (10000), (2590000)},
|
||||
{(1590), (10000), (2760000)}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* when temperature > 70C (sample_data < 0x500)
|
||||
* cpu_temperature = -0.1452*sensor + 259
|
||||
* = (2590000 - 1452*sensor)/10000
|
||||
* = (271581184 - 152253.2352*sensor)/1024/1024
|
||||
* = (271581184 - 152253*sensor)/1024/1024
|
||||
* = ( MINUPA - reg * MULPA) / 2^DIVPA
|
||||
* sensor value range:
|
||||
* = 0 - 0xffff,ffff/2/152253
|
||||
* = 0 - 14104
|
||||
*
|
||||
* gpu_temperature = -0.159*sensor + 276
|
||||
* = (2760000 - 1590*sensor)/10000
|
||||
* = (289406976 - 166723.5840*sensor)/1024/1024
|
||||
* = (289406976 - 166724*sensor)/1024/1024
|
||||
* = ( MINUPA - reg * MULPA) / 2^DIVPA
|
||||
* sensor value range:
|
||||
* = 0 - 0xffff,ffff/2/166724
|
||||
* = 0 - 2047
|
||||
*/
|
||||
|
||||
struct thermal_reg thermal_reg_init[] = {
|
||||
/* name address value reg_type*/
|
||||
{"THS_CTRL0_REG", (0x00), (0x1df), (NORMAL_REG)},
|
||||
{"THS_CTRL1_REG", (0x04), (0x1<<17), (NORMAL_REG)},
|
||||
{"ADC_CDAT_REG", (0x14), (0x0), (NO_INIT)},
|
||||
{"THS_CTRL2_REG", (0x40), (0x01df0000), (ENABLE_REG)},
|
||||
{"THS_INT_CTRL_REG", (0x44), (0x3a030), (NORMAL_REG)},
|
||||
{"THS_INT_STA_REG", (0x48), (0x333), (INT_STA_REG)},
|
||||
{"THS_0_INT_ALM_TH_REG", (0x50), (0x0), (NO_INIT)},
|
||||
{"THS_1_INT_ALM_TH_REG", (0x54), (0x0), (NO_INIT)},
|
||||
{"THS_0_INT_SHUT_TH_REG", (0x60), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_1_INT_SHUT_TH_REG", (0x64), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_FILT_CTRL_REG", (0x70), (0x06), (NORMAL_REG)},
|
||||
{"THS_0_1_CDATA_REG", (0x74), (0x0), (CDATA_REG)},
|
||||
{"THS_0_DATA_REG", (0x80), (0x0), (TDATA_REG)},
|
||||
{"THS_1_DATA_REG", (0x84), (0x0), (TDATA_REG)},
|
||||
{"", (0), (0), (0)}
|
||||
};
|
||||
|
||||
#endif /* CONFIG_ARCH_SUN50IW2 */
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN50IW3)
|
||||
|
||||
#define ENABLE_CLK (true)
|
||||
|
||||
struct temp_calculate_coefficent thermal_cal_coefficent[] = {
|
||||
[0] = {
|
||||
.down_limit = {(-210), (0xfff)},
|
||||
.up_limit = {(250), (0x000)},
|
||||
/*MUL_PARA DIV_PARA MINU_PARA */
|
||||
.nt_para = {
|
||||
{(1125), (10000), (2360000)},
|
||||
{(1125), (10000), (2360000)}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* SUN50IW3 Thermal Sensor Register
|
||||
*/
|
||||
char *id_name_mapping[] = {
|
||||
"cpuc0",
|
||||
"gpu",
|
||||
};
|
||||
|
||||
struct thermal_reg thermal_reg_init[] = {
|
||||
/* name address value reg_type*/
|
||||
{"THS_CTRL_REG", (0x00), (0x01df002f), (NORMAL_REG)},
|
||||
{"THS_EN_REG", (0x04), (0x3), (ENABLE_REG)},
|
||||
{"THS_PER_REG", (0x08), (0x3a), (NORMAL_REG)},
|
||||
{"THS_DATA_INTC_REG", (0x10), (0x0), (NO_INIT)},
|
||||
{"THS_SHUT_INTC_REG", (0x14), (0x3), (NORMAL_REG)},
|
||||
{"THS_ALARM_INTC_REG", (0x18), (0x0), (NO_INIT)},
|
||||
{"THS_DATA_INTS_REG", (0x20), (0x3), (INT_STA_REG)},
|
||||
{"THS_SHUT_INTS_REG", (0x24), (0x3), (INT_STA_REG)},
|
||||
{"THS_ALARMO_INTS_REG", (0x28), (0x3), (INT_STA_REG)},
|
||||
{"THS_ALARM_INTS_REG", (0x2C), (0x3), (INT_STA_REG)},
|
||||
{"THS_FILT_CTRL_REG", (0x30), (0x06), (NORMAL_REG)},
|
||||
{"THS_0_ALARM_CTRL_REG", (0x40), (0x0), (NO_INIT)},
|
||||
{"THS_1_ALARM_CTRL_REG", (0x44), (0x0), (NO_INIT)},
|
||||
{"THS_0_1_SHUT_CTRL_REG", (0x80), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_0_1_CDATA_REG", (0xA0), (0x0), (CDATA_REG)},
|
||||
{"THS_0_DATA_REG", (0xC0), (0x0), (TDATA_REG)},
|
||||
{"THS_1_DATA_REG", (0xC4), (0x0), (TDATA_REG)},
|
||||
{"", (0), (0), (0)}
|
||||
};
|
||||
|
||||
#endif /* defined(CONFIG_ARCH_SUN50IW3P1)*/
|
||||
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN50IW6)
|
||||
|
||||
#define SUNXI_THERMAL_SUPPORT_IRQ
|
||||
#define ENABLE_CLK (true)
|
||||
|
||||
struct temp_calculate_coefficent thermal_cal_coefficent[] = {
|
||||
[0] = {
|
||||
.down_limit = {(-210), (0xfff)},
|
||||
.up_limit = {(250), (0x000)},
|
||||
/*MUL_PARA DIV_PARA MINU_PARA */
|
||||
.nt_para = {
|
||||
{(1000), (14882), (2794000)},
|
||||
{(1000), (14882), (2794000)}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define THS_SHUT_INT_CTL 0x14
|
||||
#define THS_SHUT_INT_STATUS 0x24
|
||||
#define THS_ALARM_INT_CTL 0x18
|
||||
#define THS_ALARMOFF_INT_STATUS 0x28
|
||||
#define THS_ALARM_INT_STATUS 0x2C
|
||||
#define THS0_ALARM_THRESHOLD_CTL 0x40
|
||||
#define THS1_ALARM_THRESHOLD_CTL 0X44
|
||||
#define THS_SHUT_THRESHOLD_CTL 0x80
|
||||
|
||||
SUNXI_THERMAL_HW_CTRL(alarm0, THS_ALARM_INT_CTL, 0, THS_ALARM_INT_STATUS, 0,
|
||||
THS0_ALARM_THRESHOLD_CTL, 16, 12);
|
||||
SUNXI_THERMAL_HW_CTRL(alarm1, THS_ALARM_INT_CTL, 1, THS_ALARM_INT_STATUS, 1,
|
||||
THS1_ALARM_THRESHOLD_CTL, 16, 12);
|
||||
SUNXI_THERMAL_HW_CTRL(alarmoff0, THS_ALARM_INT_CTL, 0, THS_ALARMOFF_INT_STATUS,
|
||||
0, THS0_ALARM_THRESHOLD_CTL, 0, 12);
|
||||
SUNXI_THERMAL_HW_CTRL(alarmoff1, THS_ALARM_INT_CTL, 1, THS_ALARMOFF_INT_STATUS,
|
||||
1, THS1_ALARM_THRESHOLD_CTL, 0, 12);
|
||||
SUNXI_THERMAL_HW_CTRL(shut0, THS_SHUT_INT_CTL, 0, THS_SHUT_INT_STATUS, 0,
|
||||
THS_SHUT_THRESHOLD_CTL, 0, 12);
|
||||
SUNXI_THERMAL_HW_CTRL(shut1, THS_SHUT_INT_CTL, 1, THS_SHUT_INT_STATUS, 1,
|
||||
THS_SHUT_THRESHOLD_CTL, 16, 12);
|
||||
|
||||
struct sunxi_thermal_hw_sensor ths_hw_sensor[] = {
|
||||
{0, &sunxi_ths_hw_alarm0, &sunxi_ths_hw_shut0, &sunxi_ths_hw_alarmoff0},
|
||||
{1, &sunxi_ths_hw_alarm1, &sunxi_ths_hw_shut1, &sunxi_ths_hw_alarmoff1},
|
||||
};
|
||||
/**
|
||||
* SUN50IW3 Thermal Sensor Register
|
||||
*/
|
||||
char *id_name_mapping[] = {
|
||||
"cpuc0",
|
||||
"gpu",
|
||||
};
|
||||
|
||||
struct thermal_reg thermal_reg_init[] = {
|
||||
/* name address value reg_type*/
|
||||
{"THS_CTRL_REG", (0x00), (0x01df002f), (NORMAL_REG)},
|
||||
{"THS_EN_REG", (0x04), (0x3), (ENABLE_REG)},
|
||||
{"THS_PER_REG", (0x08), (0x3a000), (NORMAL_REG)},
|
||||
{"THS_DATA_INTC_REG", (0x10), (0x0), (NO_INIT)},
|
||||
{"THS_SHUT_INTC_REG", (0x14), (0x3), (NORMAL_REG)},
|
||||
{"THS_ALARM_INTC_REG", (0x18), (0x0), (NO_INIT)},
|
||||
{"THS_DATA_INTS_REG", (0x20), (0x3), (INT_STA_REG)},
|
||||
{"THS_SHUT_INTS_REG", (0x24), (0x3), (INT_STA_REG)},
|
||||
{"THS_ALARMO_INTS_REG", (0x28), (0x3), (INT_STA_REG)},
|
||||
{"THS_ALARM_INTS_REG", (0x2C), (0x3), (INT_STA_REG)},
|
||||
{"THS_FILT_CTRL_REG", (0x30), (0x06), (NORMAL_REG)},
|
||||
{"THS_0_ALARM_CTRL_REG", (0x40), (0x0), (NO_INIT)},
|
||||
{"THS_1_ALARM_CTRL_REG", (0x44), (0x0), (NO_INIT)},
|
||||
{"THS_0_1_SHUT_CTRL_REG", (0x80), (0x0), (SHT_TMP_REG)},
|
||||
{"THS_0_1_CDATA_REG", (0xA0), (0x0), (CDATA_REG)},
|
||||
{"THS_0_DATA_REG", (0xC0), (0x0), (TDATA_REG)},
|
||||
{"THS_1_DATA_REG", (0xC4), (0x0), (TDATA_REG)},
|
||||
{"", (0), (0), (0)}
|
||||
};
|
||||
|
||||
#endif /* defined(CONFIG_ARCH_SUN50IW6P1)*/
|
||||
|
||||
#endif /* THERMAL_SENSOR_DRIVER_H */
|
48
drivers/thermal/sunxi_thermal/sunxi_ths_efuse.h
Normal file
48
drivers/thermal/sunxi_thermal/sunxi_ths_efuse.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* drivers/thermal/sunxi_thermal/sunxi_ths_efuse.h
|
||||
*
|
||||
* Copyright (C) 2013-2024 allwinner.
|
||||
* JiaRui Xiao<xiaojiarui@allwinnertech.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __SUNXI_THS_EFUSE_H__
|
||||
#define __SUNXI_THS_EFUSE_H__
|
||||
|
||||
/* GET TMEP RETURN VALUE */
|
||||
#define WRONG_EFUSE_REG_DATA (0xffff)
|
||||
#define THS_EFUSE_DEFAULT_VALUE (0x800)
|
||||
#define THS_EFUSE_ENVIROMENT_MASK (0x0fff)
|
||||
#define THS_EFUSE_CP_FT_MASK (0b0011000000000000)
|
||||
#define THS_EFUSE_CP_FT_BIT (12)/* depend on ths calibration doc */
|
||||
#define THS_CALIBRATION_IN_FT (1)
|
||||
|
||||
|
||||
|
||||
#if defined(CONFIG_ARCH_SUN50IW3)
|
||||
|
||||
#define SENSOR_CP_EUFSE_PER_REG_TO_TEMP (672)/* this value is 0.0672 */
|
||||
#define CONST_MUL (10)
|
||||
#define CONST_DIV (1000)/* protect one decimal,so x10*/
|
||||
#define FT_CALIBRATION_DEVIATION (0)/* degrees celsius */
|
||||
|
||||
#elif defined(CONFIG_ARCH_SUN50IW6)
|
||||
|
||||
#define SENSOR_CP_EUFSE_PER_REG_TO_TEMP (672)/* this value is 0.0672 */
|
||||
#define CONST_MUL (10)
|
||||
#define CONST_DIV (1000)/* protect one decimal so x10*/
|
||||
#define FT_CALIBRATION_DEVIATION (2)/* degrees celsius */
|
||||
|
||||
#else
|
||||
/* doesn't exist these Macro */
|
||||
#define SENSOR_CP_EUFSE_PER_REG_TO_TEMP (-1UL)
|
||||
#define CONST_MUL (1)
|
||||
#define CONST_DIV (1*CONST_MUL)
|
||||
#define FT_CALIBRATION_DEVIATION (0)/* degrees celsius */
|
||||
#endif
|
||||
|
||||
#endif/* __SUNXI_THS_EFUSE_H__ */
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue