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 42 struct thermal_soc_data { 43 u32 num_sensors; 44 u32 version; 45 int (*get_temp)(void *, int *); 46 }; 47 48 struct tmu_sensor { 49 struct imx8mm_tmu *priv; 50 u32 hw_id; 51 struct thermal_zone_device *tzd; 52 }; 53 54 struct imx8mm_tmu { 55 void __iomem *base; 56 struct clk *clk; 57 const struct thermal_soc_data *socdata; 58 struct tmu_sensor sensors[]; 59 }; 60 61 static 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 75 static 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 99 static int tmu_get_temp(struct thermal_zone_device *tz, int *temp) 100 { 101 struct tmu_sensor *sensor = tz->devdata; 102 struct imx8mm_tmu *tmu = sensor->priv; 103 104 return tmu->socdata->get_temp(sensor, temp); 105 } 106 107 static const struct thermal_zone_device_ops tmu_tz_ops = { 108 .get_temp = tmu_get_temp, 109 }; 110 111 static 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 122 static 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 131 static 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_of_zone_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 192 disable_clk: 193 clk_disable_unprepare(tmu->clk); 194 return ret; 195 } 196 197 static 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 210 static struct thermal_soc_data imx8mm_tmu_data = { 211 .num_sensors = 1, 212 .version = TMU_VER1, 213 .get_temp = imx8mm_tmu_get_temp, 214 }; 215 216 static struct thermal_soc_data imx8mp_tmu_data = { 217 .num_sensors = 2, 218 .version = TMU_VER2, 219 .get_temp = imx8mp_tmu_get_temp, 220 }; 221 222 static 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 }; 227 MODULE_DEVICE_TABLE(of, imx8mm_tmu_table); 228 229 static 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 }; 237 module_platform_driver(imx8mm_tmu); 238 239 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 240 MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver"); 241 MODULE_LICENSE("GPL v2"); 242