cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

amlogic_thermal.c (8646B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Amlogic Thermal Sensor Driver
      4 *
      5 * Copyright (C) 2017 Huan Biao <huan.biao@amlogic.com>
      6 * Copyright (C) 2019 Guillaume La Roque <glaroque@baylibre.com>
      7 *
      8 * Register value to celsius temperature formulas:
      9 *	Read_Val	    m * U
     10 * U = ---------, Uptat = ---------
     11 *	2^16		  1 + n * U
     12 *
     13 * Temperature = A * ( Uptat + u_efuse / 2^16 )- B
     14 *
     15 *  A B m n : calibration parameters
     16 *  u_efuse : fused calibration value, it's a signed 16 bits value
     17 */
     18
     19#include <linux/bitfield.h>
     20#include <linux/clk.h>
     21#include <linux/io.h>
     22#include <linux/mfd/syscon.h>
     23#include <linux/module.h>
     24#include <linux/of.h>
     25#include <linux/of_address.h>
     26#include <linux/of_device.h>
     27#include <linux/platform_device.h>
     28#include <linux/regmap.h>
     29#include <linux/thermal.h>
     30
     31#include "thermal_core.h"
     32#include "thermal_hwmon.h"
     33
     34#define TSENSOR_CFG_REG1			0x4
     35	#define TSENSOR_CFG_REG1_RSET_VBG	BIT(12)
     36	#define TSENSOR_CFG_REG1_RSET_ADC	BIT(11)
     37	#define TSENSOR_CFG_REG1_VCM_EN		BIT(10)
     38	#define TSENSOR_CFG_REG1_VBG_EN		BIT(9)
     39	#define TSENSOR_CFG_REG1_OUT_CTL	BIT(6)
     40	#define TSENSOR_CFG_REG1_FILTER_EN	BIT(5)
     41	#define TSENSOR_CFG_REG1_DEM_EN		BIT(3)
     42	#define TSENSOR_CFG_REG1_CH_SEL		GENMASK(1, 0)
     43	#define TSENSOR_CFG_REG1_ENABLE		\
     44		(TSENSOR_CFG_REG1_FILTER_EN |	\
     45		 TSENSOR_CFG_REG1_VCM_EN |	\
     46		 TSENSOR_CFG_REG1_VBG_EN |	\
     47		 TSENSOR_CFG_REG1_DEM_EN |	\
     48		 TSENSOR_CFG_REG1_CH_SEL)
     49
     50#define TSENSOR_STAT0			0x40
     51
     52#define TSENSOR_STAT9			0x64
     53
     54#define TSENSOR_READ_TEMP_MASK		GENMASK(15, 0)
     55#define TSENSOR_TEMP_MASK		GENMASK(11, 0)
     56
     57#define TSENSOR_TRIM_SIGN_MASK		BIT(15)
     58#define TSENSOR_TRIM_TEMP_MASK		GENMASK(14, 0)
     59#define TSENSOR_TRIM_VERSION_MASK	GENMASK(31, 24)
     60
     61#define TSENSOR_TRIM_VERSION(_version)	\
     62	FIELD_GET(TSENSOR_TRIM_VERSION_MASK, _version)
     63
     64#define TSENSOR_TRIM_CALIB_VALID_MASK	(GENMASK(3, 2) | BIT(7))
     65
     66#define TSENSOR_CALIB_OFFSET	1
     67#define TSENSOR_CALIB_SHIFT	4
     68
     69/**
     70 * struct amlogic_thermal_soc_calib_data
     71 * @A: calibration parameters
     72 * @B: calibration parameters
     73 * @m: calibration parameters
     74 * @n: calibration parameters
     75 *
     76 * This structure is required for configuration of amlogic thermal driver.
     77 */
     78struct amlogic_thermal_soc_calib_data {
     79	int A;
     80	int B;
     81	int m;
     82	int n;
     83};
     84
     85/**
     86 * struct amlogic_thermal_data
     87 * @u_efuse_off: register offset to read fused calibration value
     88 * @calibration_parameters: calibration parameters structure pointer
     89 * @regmap_config: regmap config for the device
     90 * This structure is required for configuration of amlogic thermal driver.
     91 */
     92struct amlogic_thermal_data {
     93	int u_efuse_off;
     94	const struct amlogic_thermal_soc_calib_data *calibration_parameters;
     95	const struct regmap_config *regmap_config;
     96};
     97
     98struct amlogic_thermal {
     99	struct platform_device *pdev;
    100	const struct amlogic_thermal_data *data;
    101	struct regmap *regmap;
    102	struct regmap *sec_ao_map;
    103	struct clk *clk;
    104	struct thermal_zone_device *tzd;
    105	u32 trim_info;
    106};
    107
    108/*
    109 * Calculate a temperature value from a temperature code.
    110 * The unit of the temperature is degree milliCelsius.
    111 */
    112static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata,
    113						int temp_code)
    114{
    115	const struct amlogic_thermal_soc_calib_data *param =
    116					pdata->data->calibration_parameters;
    117	int temp;
    118	s64 factor, Uptat, uefuse;
    119
    120	uefuse = pdata->trim_info & TSENSOR_TRIM_SIGN_MASK ?
    121			     ~(pdata->trim_info & TSENSOR_TRIM_TEMP_MASK) + 1 :
    122			     (pdata->trim_info & TSENSOR_TRIM_TEMP_MASK);
    123
    124	factor = param->n * temp_code;
    125	factor = div_s64(factor, 100);
    126
    127	Uptat = temp_code * param->m;
    128	Uptat = div_s64(Uptat, 100);
    129	Uptat = Uptat * BIT(16);
    130	Uptat = div_s64(Uptat, BIT(16) + factor);
    131
    132	temp = (Uptat + uefuse) * param->A;
    133	temp = div_s64(temp, BIT(16));
    134	temp = (temp - param->B) * 100;
    135
    136	return temp;
    137}
    138
    139static int amlogic_thermal_initialize(struct amlogic_thermal *pdata)
    140{
    141	int ret = 0;
    142	int ver;
    143
    144	regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off,
    145		    &pdata->trim_info);
    146
    147	ver = TSENSOR_TRIM_VERSION(pdata->trim_info);
    148
    149	if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) {
    150		ret = -EINVAL;
    151		dev_err(&pdata->pdev->dev,
    152			"tsensor thermal calibration not supported: 0x%x!\n",
    153			ver);
    154	}
    155
    156	return ret;
    157}
    158
    159static int amlogic_thermal_enable(struct amlogic_thermal *data)
    160{
    161	int ret;
    162
    163	ret = clk_prepare_enable(data->clk);
    164	if (ret)
    165		return ret;
    166
    167	regmap_update_bits(data->regmap, TSENSOR_CFG_REG1,
    168			   TSENSOR_CFG_REG1_ENABLE, TSENSOR_CFG_REG1_ENABLE);
    169
    170	return 0;
    171}
    172
    173static int amlogic_thermal_disable(struct amlogic_thermal *data)
    174{
    175	regmap_update_bits(data->regmap, TSENSOR_CFG_REG1,
    176			   TSENSOR_CFG_REG1_ENABLE, 0);
    177	clk_disable_unprepare(data->clk);
    178
    179	return 0;
    180}
    181
    182static int amlogic_thermal_get_temp(void *data, int *temp)
    183{
    184	unsigned int tval;
    185	struct amlogic_thermal *pdata = data;
    186
    187	if (!data)
    188		return -EINVAL;
    189
    190	regmap_read(pdata->regmap, TSENSOR_STAT0, &tval);
    191	*temp =
    192	   amlogic_thermal_code_to_millicelsius(pdata,
    193						tval & TSENSOR_READ_TEMP_MASK);
    194
    195	return 0;
    196}
    197
    198static const struct thermal_zone_of_device_ops amlogic_thermal_ops = {
    199	.get_temp	= amlogic_thermal_get_temp,
    200};
    201
    202static const struct regmap_config amlogic_thermal_regmap_config_g12a = {
    203	.reg_bits = 8,
    204	.val_bits = 32,
    205	.reg_stride = 4,
    206	.max_register = TSENSOR_STAT9,
    207};
    208
    209static const struct amlogic_thermal_soc_calib_data amlogic_thermal_g12a = {
    210	.A = 9411,
    211	.B = 3159,
    212	.m = 424,
    213	.n = 324,
    214};
    215
    216static const struct amlogic_thermal_data amlogic_thermal_g12a_cpu_param = {
    217	.u_efuse_off = 0x128,
    218	.calibration_parameters = &amlogic_thermal_g12a,
    219	.regmap_config = &amlogic_thermal_regmap_config_g12a,
    220};
    221
    222static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = {
    223	.u_efuse_off = 0xf0,
    224	.calibration_parameters = &amlogic_thermal_g12a,
    225	.regmap_config = &amlogic_thermal_regmap_config_g12a,
    226};
    227
    228static const struct of_device_id of_amlogic_thermal_match[] = {
    229	{
    230		.compatible = "amlogic,g12a-ddr-thermal",
    231		.data = &amlogic_thermal_g12a_ddr_param,
    232	},
    233	{
    234		.compatible = "amlogic,g12a-cpu-thermal",
    235		.data = &amlogic_thermal_g12a_cpu_param,
    236	},
    237	{ /* sentinel */ }
    238};
    239MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match);
    240
    241static int amlogic_thermal_probe(struct platform_device *pdev)
    242{
    243	struct amlogic_thermal *pdata;
    244	struct device *dev = &pdev->dev;
    245	void __iomem *base;
    246	int ret;
    247
    248	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
    249	if (!pdata)
    250		return -ENOMEM;
    251
    252	pdata->data = of_device_get_match_data(dev);
    253	pdata->pdev = pdev;
    254	platform_set_drvdata(pdev, pdata);
    255
    256	base = devm_platform_ioremap_resource(pdev, 0);
    257	if (IS_ERR(base))
    258		return PTR_ERR(base);
    259
    260	pdata->regmap = devm_regmap_init_mmio(dev, base,
    261					      pdata->data->regmap_config);
    262	if (IS_ERR(pdata->regmap))
    263		return PTR_ERR(pdata->regmap);
    264
    265	pdata->clk = devm_clk_get(dev, NULL);
    266	if (IS_ERR(pdata->clk)) {
    267		if (PTR_ERR(pdata->clk) != -EPROBE_DEFER)
    268			dev_err(dev, "failed to get clock\n");
    269		return PTR_ERR(pdata->clk);
    270	}
    271
    272	pdata->sec_ao_map = syscon_regmap_lookup_by_phandle
    273		(pdev->dev.of_node, "amlogic,ao-secure");
    274	if (IS_ERR(pdata->sec_ao_map)) {
    275		dev_err(dev, "syscon regmap lookup failed.\n");
    276		return PTR_ERR(pdata->sec_ao_map);
    277	}
    278
    279	pdata->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
    280							  0,
    281							  pdata,
    282							  &amlogic_thermal_ops);
    283	if (IS_ERR(pdata->tzd)) {
    284		ret = PTR_ERR(pdata->tzd);
    285		dev_err(dev, "Failed to register tsensor: %d\n", ret);
    286		return ret;
    287	}
    288
    289	if (devm_thermal_add_hwmon_sysfs(pdata->tzd))
    290		dev_warn(&pdev->dev, "Failed to add hwmon sysfs attributes\n");
    291
    292	ret = amlogic_thermal_initialize(pdata);
    293	if (ret)
    294		return ret;
    295
    296	ret = amlogic_thermal_enable(pdata);
    297
    298	return ret;
    299}
    300
    301static int amlogic_thermal_remove(struct platform_device *pdev)
    302{
    303	struct amlogic_thermal *data = platform_get_drvdata(pdev);
    304
    305	return amlogic_thermal_disable(data);
    306}
    307
    308static int __maybe_unused amlogic_thermal_suspend(struct device *dev)
    309{
    310	struct amlogic_thermal *data = dev_get_drvdata(dev);
    311
    312	return amlogic_thermal_disable(data);
    313}
    314
    315static int __maybe_unused amlogic_thermal_resume(struct device *dev)
    316{
    317	struct amlogic_thermal *data = dev_get_drvdata(dev);
    318
    319	return amlogic_thermal_enable(data);
    320}
    321
    322static SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops,
    323			 amlogic_thermal_suspend, amlogic_thermal_resume);
    324
    325static struct platform_driver amlogic_thermal_driver = {
    326	.driver = {
    327		.name		= "amlogic_thermal",
    328		.pm		= &amlogic_thermal_pm_ops,
    329		.of_match_table = of_amlogic_thermal_match,
    330	},
    331	.probe	= amlogic_thermal_probe,
    332	.remove	= amlogic_thermal_remove,
    333};
    334
    335module_platform_driver(amlogic_thermal_driver);
    336
    337MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
    338MODULE_DESCRIPTION("Amlogic thermal driver");
    339MODULE_LICENSE("GPL v2");