imx8mm_thermal.c (5523B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright 2020 NXP. 4 * 5 * Author: Anson Huang <Anson.Huang@nxp.com> 6 */ 7 8#include <linux/bitfield.h> 9#include <linux/clk.h> 10#include <linux/err.h> 11#include <linux/io.h> 12#include <linux/module.h> 13#include <linux/of.h> 14#include <linux/of_device.h> 15#include <linux/platform_device.h> 16#include <linux/thermal.h> 17 18#include "thermal_core.h" 19 20#define TER 0x0 /* TMU enable */ 21#define TPS 0x4 22#define TRITSR 0x20 /* TMU immediate temp */ 23 24#define TER_ADC_PD BIT(30) 25#define TER_EN BIT(31) 26#define TRITSR_TEMP0_VAL_MASK 0xff 27#define TRITSR_TEMP1_VAL_MASK 0xff0000 28 29#define PROBE_SEL_ALL GENMASK(31, 30) 30 31#define probe_status_offset(x) (30 + x) 32#define SIGN_BIT BIT(7) 33#define TEMP_VAL_MASK GENMASK(6, 0) 34 35#define VER1_TEMP_LOW_LIMIT 10000 36#define VER2_TEMP_LOW_LIMIT -40000 37#define VER2_TEMP_HIGH_LIMIT 125000 38 39#define TMU_VER1 0x1 40#define TMU_VER2 0x2 41 42struct thermal_soc_data { 43 u32 num_sensors; 44 u32 version; 45 int (*get_temp)(void *, int *); 46}; 47 48struct tmu_sensor { 49 struct imx8mm_tmu *priv; 50 u32 hw_id; 51 struct thermal_zone_device *tzd; 52}; 53 54struct imx8mm_tmu { 55 void __iomem *base; 56 struct clk *clk; 57 const struct thermal_soc_data *socdata; 58 struct tmu_sensor sensors[]; 59}; 60 61static int imx8mm_tmu_get_temp(void *data, int *temp) 62{ 63 struct tmu_sensor *sensor = data; 64 struct imx8mm_tmu *tmu = sensor->priv; 65 u32 val; 66 67 val = readl_relaxed(tmu->base + TRITSR) & TRITSR_TEMP0_VAL_MASK; 68 *temp = val * 1000; 69 if (*temp < VER1_TEMP_LOW_LIMIT) 70 return -EAGAIN; 71 72 return 0; 73} 74 75static int imx8mp_tmu_get_temp(void *data, int *temp) 76{ 77 struct tmu_sensor *sensor = data; 78 struct imx8mm_tmu *tmu = sensor->priv; 79 unsigned long val; 80 bool ready; 81 82 val = readl_relaxed(tmu->base + TRITSR); 83 ready = test_bit(probe_status_offset(sensor->hw_id), &val); 84 if (!ready) 85 return -EAGAIN; 86 87 val = sensor->hw_id ? FIELD_GET(TRITSR_TEMP1_VAL_MASK, val) : 88 FIELD_GET(TRITSR_TEMP0_VAL_MASK, val); 89 if (val & SIGN_BIT) /* negative */ 90 val = (~(val & TEMP_VAL_MASK) + 1); 91 92 *temp = val * 1000; 93 if (*temp < VER2_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT) 94 return -EAGAIN; 95 96 return 0; 97} 98 99static int tmu_get_temp(void *data, int *temp) 100{ 101 struct tmu_sensor *sensor = data; 102 struct imx8mm_tmu *tmu = sensor->priv; 103 104 return tmu->socdata->get_temp(data, temp); 105} 106 107static struct thermal_zone_of_device_ops tmu_tz_ops = { 108 .get_temp = tmu_get_temp, 109}; 110 111static void imx8mm_tmu_enable(struct imx8mm_tmu *tmu, bool enable) 112{ 113 u32 val; 114 115 val = readl_relaxed(tmu->base + TER); 116 val = enable ? (val | TER_EN) : (val & ~TER_EN); 117 if (tmu->socdata->version == TMU_VER2) 118 val = enable ? (val & ~TER_ADC_PD) : (val | TER_ADC_PD); 119 writel_relaxed(val, tmu->base + TER); 120} 121 122static void imx8mm_tmu_probe_sel_all(struct imx8mm_tmu *tmu) 123{ 124 u32 val; 125 126 val = readl_relaxed(tmu->base + TPS); 127 val |= PROBE_SEL_ALL; 128 writel_relaxed(val, tmu->base + TPS); 129} 130 131static int imx8mm_tmu_probe(struct platform_device *pdev) 132{ 133 const struct thermal_soc_data *data; 134 struct imx8mm_tmu *tmu; 135 int ret; 136 int i; 137 138 data = of_device_get_match_data(&pdev->dev); 139 140 tmu = devm_kzalloc(&pdev->dev, struct_size(tmu, sensors, 141 data->num_sensors), GFP_KERNEL); 142 if (!tmu) 143 return -ENOMEM; 144 145 tmu->socdata = data; 146 147 tmu->base = devm_platform_ioremap_resource(pdev, 0); 148 if (IS_ERR(tmu->base)) 149 return PTR_ERR(tmu->base); 150 151 tmu->clk = devm_clk_get(&pdev->dev, NULL); 152 if (IS_ERR(tmu->clk)) 153 return dev_err_probe(&pdev->dev, PTR_ERR(tmu->clk), 154 "failed to get tmu clock\n"); 155 156 ret = clk_prepare_enable(tmu->clk); 157 if (ret) { 158 dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret); 159 return ret; 160 } 161 162 /* disable the monitor during initialization */ 163 imx8mm_tmu_enable(tmu, false); 164 165 for (i = 0; i < data->num_sensors; i++) { 166 tmu->sensors[i].priv = tmu; 167 tmu->sensors[i].tzd = 168 devm_thermal_zone_of_sensor_register(&pdev->dev, i, 169 &tmu->sensors[i], 170 &tmu_tz_ops); 171 if (IS_ERR(tmu->sensors[i].tzd)) { 172 ret = PTR_ERR(tmu->sensors[i].tzd); 173 dev_err(&pdev->dev, 174 "failed to register thermal zone sensor[%d]: %d\n", 175 i, ret); 176 goto disable_clk; 177 } 178 tmu->sensors[i].hw_id = i; 179 } 180 181 platform_set_drvdata(pdev, tmu); 182 183 /* enable all the probes for V2 TMU */ 184 if (tmu->socdata->version == TMU_VER2) 185 imx8mm_tmu_probe_sel_all(tmu); 186 187 /* enable the monitor */ 188 imx8mm_tmu_enable(tmu, true); 189 190 return 0; 191 192disable_clk: 193 clk_disable_unprepare(tmu->clk); 194 return ret; 195} 196 197static int imx8mm_tmu_remove(struct platform_device *pdev) 198{ 199 struct imx8mm_tmu *tmu = platform_get_drvdata(pdev); 200 201 /* disable TMU */ 202 imx8mm_tmu_enable(tmu, false); 203 204 clk_disable_unprepare(tmu->clk); 205 platform_set_drvdata(pdev, NULL); 206 207 return 0; 208} 209 210static struct thermal_soc_data imx8mm_tmu_data = { 211 .num_sensors = 1, 212 .version = TMU_VER1, 213 .get_temp = imx8mm_tmu_get_temp, 214}; 215 216static struct thermal_soc_data imx8mp_tmu_data = { 217 .num_sensors = 2, 218 .version = TMU_VER2, 219 .get_temp = imx8mp_tmu_get_temp, 220}; 221 222static const struct of_device_id imx8mm_tmu_table[] = { 223 { .compatible = "fsl,imx8mm-tmu", .data = &imx8mm_tmu_data, }, 224 { .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, }, 225 { }, 226}; 227MODULE_DEVICE_TABLE(of, imx8mm_tmu_table); 228 229static struct platform_driver imx8mm_tmu = { 230 .driver = { 231 .name = "i.mx8mm_thermal", 232 .of_match_table = imx8mm_tmu_table, 233 }, 234 .probe = imx8mm_tmu_probe, 235 .remove = imx8mm_tmu_remove, 236}; 237module_platform_driver(imx8mm_tmu); 238 239MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 240MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver"); 241MODULE_LICENSE("GPL v2");