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 69 /* 70 * Do not validate against the V bit (bit 31) due to errata 71 * ERR051272: TMU: Bit 31 of registers TMU_TSCR/TMU_TRITSR/TMU_TRATSR invalid 72 */ 73 74 *temp = val * 1000; 75 if (*temp < VER1_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT) 76 return -EAGAIN; 77 78 return 0; 79} 80 81static int imx8mp_tmu_get_temp(void *data, int *temp) 82{ 83 struct tmu_sensor *sensor = data; 84 struct imx8mm_tmu *tmu = sensor->priv; 85 unsigned long val; 86 bool ready; 87 88 val = readl_relaxed(tmu->base + TRITSR); 89 ready = test_bit(probe_status_offset(sensor->hw_id), &val); 90 if (!ready) 91 return -EAGAIN; 92 93 val = sensor->hw_id ? FIELD_GET(TRITSR_TEMP1_VAL_MASK, val) : 94 FIELD_GET(TRITSR_TEMP0_VAL_MASK, val); 95 if (val & SIGN_BIT) /* negative */ 96 val = (~(val & TEMP_VAL_MASK) + 1); 97 98 *temp = val * 1000; 99 if (*temp < VER2_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT) 100 return -EAGAIN; 101 102 return 0; 103} 104 105static int tmu_get_temp(void *data, int *temp) 106{ 107 struct tmu_sensor *sensor = data; 108 struct imx8mm_tmu *tmu = sensor->priv; 109 110 return tmu->socdata->get_temp(data, temp); 111} 112 113static struct thermal_zone_of_device_ops tmu_tz_ops = { 114 .get_temp = tmu_get_temp, 115}; 116 117static void imx8mm_tmu_enable(struct imx8mm_tmu *tmu, bool enable) 118{ 119 u32 val; 120 121 val = readl_relaxed(tmu->base + TER); 122 val = enable ? (val | TER_EN) : (val & ~TER_EN); 123 if (tmu->socdata->version == TMU_VER2) 124 val = enable ? (val & ~TER_ADC_PD) : (val | TER_ADC_PD); 125 writel_relaxed(val, tmu->base + TER); 126} 127 128static void imx8mm_tmu_probe_sel_all(struct imx8mm_tmu *tmu) 129{ 130 u32 val; 131 132 val = readl_relaxed(tmu->base + TPS); 133 val |= PROBE_SEL_ALL; 134 writel_relaxed(val, tmu->base + TPS); 135} 136 137static int imx8mm_tmu_probe(struct platform_device *pdev) 138{ 139 const struct thermal_soc_data *data; 140 struct imx8mm_tmu *tmu; 141 int ret; 142 int i; 143 144 data = of_device_get_match_data(&pdev->dev); 145 146 tmu = devm_kzalloc(&pdev->dev, struct_size(tmu, sensors, 147 data->num_sensors), GFP_KERNEL); 148 if (!tmu) 149 return -ENOMEM; 150 151 tmu->socdata = data; 152 153 tmu->base = devm_platform_ioremap_resource(pdev, 0); 154 if (IS_ERR(tmu->base)) 155 return PTR_ERR(tmu->base); 156 157 tmu->clk = devm_clk_get(&pdev->dev, NULL); 158 if (IS_ERR(tmu->clk)) 159 return dev_err_probe(&pdev->dev, PTR_ERR(tmu->clk), 160 "failed to get tmu clock\n"); 161 162 ret = clk_prepare_enable(tmu->clk); 163 if (ret) { 164 dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret); 165 return ret; 166 } 167 168 /* disable the monitor during initialization */ 169 imx8mm_tmu_enable(tmu, false); 170 171 for (i = 0; i < data->num_sensors; i++) { 172 tmu->sensors[i].priv = tmu; 173 tmu->sensors[i].tzd = 174 devm_thermal_zone_of_sensor_register(&pdev->dev, i, 175 &tmu->sensors[i], 176 &tmu_tz_ops); 177 if (IS_ERR(tmu->sensors[i].tzd)) { 178 dev_err(&pdev->dev, 179 "failed to register thermal zone sensor[%d]: %d\n", 180 i, ret); 181 return PTR_ERR(tmu->sensors[i].tzd); 182 } 183 tmu->sensors[i].hw_id = i; 184 } 185 186 platform_set_drvdata(pdev, tmu); 187 188 /* enable all the probes for V2 TMU */ 189 if (tmu->socdata->version == TMU_VER2) 190 imx8mm_tmu_probe_sel_all(tmu); 191 192 /* enable the monitor */ 193 imx8mm_tmu_enable(tmu, true); 194 195 return 0; 196} 197 198static int imx8mm_tmu_remove(struct platform_device *pdev) 199{ 200 struct imx8mm_tmu *tmu = platform_get_drvdata(pdev); 201 202 /* disable TMU */ 203 imx8mm_tmu_enable(tmu, false); 204 205 clk_disable_unprepare(tmu->clk); 206 platform_set_drvdata(pdev, NULL); 207 208 return 0; 209} 210 211static struct thermal_soc_data imx8mm_tmu_data = { 212 .num_sensors = 1, 213 .version = TMU_VER1, 214 .get_temp = imx8mm_tmu_get_temp, 215}; 216 217static struct thermal_soc_data imx8mp_tmu_data = { 218 .num_sensors = 2, 219 .version = TMU_VER2, 220 .get_temp = imx8mp_tmu_get_temp, 221}; 222 223static const struct of_device_id imx8mm_tmu_table[] = { 224 { .compatible = "fsl,imx8mm-tmu", .data = &imx8mm_tmu_data, }, 225 { .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, }, 226 { }, 227}; 228MODULE_DEVICE_TABLE(of, imx8mm_tmu_table); 229 230static struct platform_driver imx8mm_tmu = { 231 .driver = { 232 .name = "i.mx8mm_thermal", 233 .of_match_table = imx8mm_tmu_table, 234 }, 235 .probe = imx8mm_tmu_probe, 236 .remove = imx8mm_tmu_remove, 237}; 238module_platform_driver(imx8mm_tmu); 239 240MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 241MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver"); 242MODULE_LICENSE("GPL v2"); 243