Initial commit

This commit is contained in:
Ole André Vadla Ravnås 2022-05-07 01:01:45 +02:00
commit 169c65d57e
51358 changed files with 23120455 additions and 0 deletions

View 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

View 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 */

View 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");

View 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);
}

View 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

View 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");

View 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 */

View 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__ */