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