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[]; 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 return dev_err_probe(&pdev->dev, PTR_ERR(tmu->clk), 151 "failed to get tmu clock\n"); 152 153 ret = clk_prepare_enable(tmu->clk); 154 if (ret) { 155 dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret); 156 return ret; 157 } 158 159 /* disable the monitor during initialization */ 160 imx8mm_tmu_enable(tmu, false); 161 162 for (i = 0; i < data->num_sensors; i++) { 163 tmu->sensors[i].priv = tmu; 164 tmu->sensors[i].tzd = 165 devm_thermal_zone_of_sensor_register(&pdev->dev, i, 166 &tmu->sensors[i], 167 &tmu_tz_ops); 168 if (IS_ERR(tmu->sensors[i].tzd)) { 169 ret = PTR_ERR(tmu->sensors[i].tzd); 170 dev_err(&pdev->dev, 171 "failed to register thermal zone sensor[%d]: %d\n", 172 i, ret); 173 goto disable_clk; 174 } 175 tmu->sensors[i].hw_id = i; 176 } 177 178 platform_set_drvdata(pdev, tmu); 179 180 /* enable all the probes for V2 TMU */ 181 if (tmu->socdata->version == TMU_VER2) 182 imx8mm_tmu_probe_sel_all(tmu); 183 184 /* enable the monitor */ 185 imx8mm_tmu_enable(tmu, true); 186 187 return 0; 188 189 disable_clk: 190 clk_disable_unprepare(tmu->clk); 191 return ret; 192 } 193 194 static int imx8mm_tmu_remove(struct platform_device *pdev) 195 { 196 struct imx8mm_tmu *tmu = platform_get_drvdata(pdev); 197 198 /* disable TMU */ 199 imx8mm_tmu_enable(tmu, false); 200 201 clk_disable_unprepare(tmu->clk); 202 platform_set_drvdata(pdev, NULL); 203 204 return 0; 205 } 206 207 static struct thermal_soc_data imx8mm_tmu_data = { 208 .num_sensors = 1, 209 .version = TMU_VER1, 210 .get_temp = imx8mm_tmu_get_temp, 211 }; 212 213 static struct thermal_soc_data imx8mp_tmu_data = { 214 .num_sensors = 2, 215 .version = TMU_VER2, 216 .get_temp = imx8mp_tmu_get_temp, 217 }; 218 219 static const struct of_device_id imx8mm_tmu_table[] = { 220 { .compatible = "fsl,imx8mm-tmu", .data = &imx8mm_tmu_data, }, 221 { .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, }, 222 { }, 223 }; 224 MODULE_DEVICE_TABLE(of, imx8mm_tmu_table); 225 226 static struct platform_driver imx8mm_tmu = { 227 .driver = { 228 .name = "i.mx8mm_thermal", 229 .of_match_table = imx8mm_tmu_table, 230 }, 231 .probe = imx8mm_tmu_probe, 232 .remove = imx8mm_tmu_remove, 233 }; 234 module_platform_driver(imx8mm_tmu); 235 236 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 237 MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver"); 238 MODULE_LICENSE("GPL v2"); 239