Linux Audio

Check our new training course

Open-source upstreaming

Need help get the support for your hardware in upstream Linux?
Loading...
Note: File does not exist in v3.1.
  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/nvmem-consumer.h>
 14#include <linux/of.h>
 15#include <linux/of_device.h>
 16#include <linux/platform_device.h>
 17#include <linux/slab.h>
 18#include <linux/thermal.h>
 19
 20#include "thermal_core.h"
 21#include "thermal_hwmon.h"
 22
 23#define TER			0x0	/* TMU enable */
 24#define TPS			0x4
 25#define TRITSR			0x20	/* TMU immediate temp */
 26/* TMU calibration data registers */
 27#define TASR			0x28
 28#define TASR_BUF_SLOPE_MASK	GENMASK(19, 16)
 29#define TASR_BUF_VREF_MASK	GENMASK(4, 0)	/* TMU_V1 */
 30#define TASR_BUF_VERF_SEL_MASK	GENMASK(1, 0)	/* TMU_V2 */
 31#define TCALIV(n)		(0x30 + ((n) * 4))
 32#define TCALIV_EN		BIT(31)
 33#define TCALIV_HR_MASK		GENMASK(23, 16)	/* TMU_V1 */
 34#define TCALIV_RT_MASK		GENMASK(7, 0)	/* TMU_V1 */
 35#define TCALIV_SNSR105C_MASK	GENMASK(27, 16)	/* TMU_V2 */
 36#define TCALIV_SNSR25C_MASK	GENMASK(11, 0)	/* TMU_V2 */
 37#define TRIM			0x3c
 38#define TRIM_BJT_CUR_MASK	GENMASK(23, 20)
 39#define TRIM_BGR_MASK		GENMASK(31, 28)
 40#define TRIM_VLSB_MASK		GENMASK(15, 12)
 41#define TRIM_EN_CH		BIT(7)
 42
 43#define TER_ADC_PD		BIT(30)
 44#define TER_EN			BIT(31)
 45#define TRITSR_TEMP0_VAL_MASK	GENMASK(7, 0)
 46#define TRITSR_TEMP1_VAL_MASK	GENMASK(23, 16)
 47
 48#define PROBE_SEL_ALL		GENMASK(31, 30)
 49
 50#define probe_status_offset(x)	(30 + x)
 51#define SIGN_BIT		BIT(7)
 52#define TEMP_VAL_MASK		GENMASK(6, 0)
 53
 54/* TMU OCOTP calibration data bitfields */
 55#define ANA0_EN			BIT(25)
 56#define ANA0_BUF_VREF_MASK	GENMASK(24, 20)
 57#define ANA0_BUF_SLOPE_MASK	GENMASK(19, 16)
 58#define ANA0_HR_MASK		GENMASK(15, 8)
 59#define ANA0_RT_MASK		GENMASK(7, 0)
 60#define TRIM2_VLSB_MASK		GENMASK(23, 20)
 61#define TRIM2_BGR_MASK		GENMASK(19, 16)
 62#define TRIM2_BJT_CUR_MASK	GENMASK(15, 12)
 63#define TRIM2_BUF_SLOP_SEL_MASK	GENMASK(11, 8)
 64#define TRIM2_BUF_VERF_SEL_MASK	GENMASK(7, 6)
 65#define TRIM3_TCA25_0_LSB_MASK	GENMASK(31, 28)
 66#define TRIM3_TCA40_0_MASK	GENMASK(27, 16)
 67#define TRIM4_TCA40_1_MASK	GENMASK(31, 20)
 68#define TRIM4_TCA105_0_MASK	GENMASK(19, 8)
 69#define TRIM4_TCA25_0_MSB_MASK	GENMASK(7, 0)
 70#define TRIM5_TCA105_1_MASK	GENMASK(23, 12)
 71#define TRIM5_TCA25_1_MASK	GENMASK(11, 0)
 72
 73#define VER1_TEMP_LOW_LIMIT	10000
 74#define VER2_TEMP_LOW_LIMIT	-40000
 75#define VER2_TEMP_HIGH_LIMIT	125000
 76
 77#define TMU_VER1		0x1
 78#define TMU_VER2		0x2
 79
 80struct thermal_soc_data {
 81	u32 num_sensors;
 82	u32 version;
 83	int (*get_temp)(void *, int *);
 84};
 85
 86struct tmu_sensor {
 87	struct imx8mm_tmu *priv;
 88	u32 hw_id;
 89	struct thermal_zone_device *tzd;
 90};
 91
 92struct imx8mm_tmu {
 93	void __iomem *base;
 94	struct clk *clk;
 95	const struct thermal_soc_data *socdata;
 96	struct tmu_sensor sensors[];
 97};
 98
 99static int imx8mm_tmu_get_temp(void *data, int *temp)
100{
101	struct tmu_sensor *sensor = data;
102	struct imx8mm_tmu *tmu = sensor->priv;
103	u32 val;
104
105	val = readl_relaxed(tmu->base + TRITSR) & TRITSR_TEMP0_VAL_MASK;
106
107	/*
108	 * Do not validate against the V bit (bit 31) due to errata
109	 * ERR051272: TMU: Bit 31 of registers TMU_TSCR/TMU_TRITSR/TMU_TRATSR invalid
110	 */
111
112	*temp = val * 1000;
113	if (*temp < VER1_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT)
114		return -EAGAIN;
115
116	return 0;
117}
118
119static int imx8mp_tmu_get_temp(void *data, int *temp)
120{
121	struct tmu_sensor *sensor = data;
122	struct imx8mm_tmu *tmu = sensor->priv;
123	unsigned long val;
124	bool ready;
125
126	val = readl_relaxed(tmu->base + TRITSR);
127	ready = test_bit(probe_status_offset(sensor->hw_id), &val);
128	if (!ready)
129		return -EAGAIN;
130
131	val = sensor->hw_id ? FIELD_GET(TRITSR_TEMP1_VAL_MASK, val) :
132	      FIELD_GET(TRITSR_TEMP0_VAL_MASK, val);
133	if (val & SIGN_BIT) /* negative */
134		val = (~(val & TEMP_VAL_MASK) + 1);
135
136	*temp = val * 1000;
137	if (*temp < VER2_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT)
138		return -EAGAIN;
139
140	return 0;
141}
142
143static int tmu_get_temp(struct thermal_zone_device *tz, int *temp)
144{
145	struct tmu_sensor *sensor = tz->devdata;
146	struct imx8mm_tmu *tmu = sensor->priv;
147
148	return tmu->socdata->get_temp(sensor, temp);
149}
150
151static const struct thermal_zone_device_ops tmu_tz_ops = {
152	.get_temp = tmu_get_temp,
153};
154
155static void imx8mm_tmu_enable(struct imx8mm_tmu *tmu, bool enable)
156{
157	u32 val;
158
159	val = readl_relaxed(tmu->base + TER);
160	val = enable ? (val | TER_EN) : (val & ~TER_EN);
161	if (tmu->socdata->version == TMU_VER2)
162		val = enable ? (val & ~TER_ADC_PD) : (val | TER_ADC_PD);
163	writel_relaxed(val, tmu->base + TER);
164}
165
166static void imx8mm_tmu_probe_sel_all(struct imx8mm_tmu *tmu)
167{
168	u32 val;
169
170	val = readl_relaxed(tmu->base + TPS);
171	val |= PROBE_SEL_ALL;
172	writel_relaxed(val, tmu->base + TPS);
173}
174
175static int imx8mm_tmu_probe_set_calib_v1(struct platform_device *pdev,
176					 struct imx8mm_tmu *tmu)
177{
178	struct device *dev = &pdev->dev;
179	u32 ana0;
180	int ret;
181
182	ret = nvmem_cell_read_u32(&pdev->dev, "calib", &ana0);
183	if (ret) {
184		dev_warn(dev, "Failed to read OCOTP nvmem cell (%d).\n", ret);
185		return ret;
186	}
187
188	writel(FIELD_PREP(TASR_BUF_VREF_MASK,
189			  FIELD_GET(ANA0_BUF_VREF_MASK, ana0)) |
190	       FIELD_PREP(TASR_BUF_SLOPE_MASK,
191			  FIELD_GET(ANA0_BUF_SLOPE_MASK, ana0)),
192	       tmu->base + TASR);
193
194	writel(FIELD_PREP(TCALIV_RT_MASK, FIELD_GET(ANA0_RT_MASK, ana0)) |
195	       FIELD_PREP(TCALIV_HR_MASK, FIELD_GET(ANA0_HR_MASK, ana0)) |
196	       ((ana0 & ANA0_EN) ? TCALIV_EN : 0),
197	       tmu->base + TCALIV(0));
198
199	return 0;
200}
201
202static int imx8mm_tmu_probe_set_calib_v2(struct platform_device *pdev,
203					 struct imx8mm_tmu *tmu)
204{
205	struct device *dev = &pdev->dev;
206	struct nvmem_cell *cell;
207	u32 trim[4] = { 0 };
208	size_t len;
209	void *buf;
210
211	cell = nvmem_cell_get(dev, "calib");
212	if (IS_ERR(cell))
213		return PTR_ERR(cell);
214
215	buf = nvmem_cell_read(cell, &len);
216	nvmem_cell_put(cell);
217
218	if (IS_ERR(buf))
219		return PTR_ERR(buf);
220
221	memcpy(trim, buf, min(len, sizeof(trim)));
222	kfree(buf);
223
224	if (len != 16) {
225		dev_err(dev,
226			"OCOTP nvmem cell length is %zu, must be 16.\n", len);
227		return -EINVAL;
228	}
229
230	/* Blank sample hardware */
231	if (!trim[0] && !trim[1] && !trim[2] && !trim[3]) {
232		/* Use a default 25C binary codes */
233		writel(FIELD_PREP(TCALIV_SNSR25C_MASK, 0x63c),
234		       tmu->base + TCALIV(0));
235		writel(FIELD_PREP(TCALIV_SNSR25C_MASK, 0x63c),
236		       tmu->base + TCALIV(1));
237		return 0;
238	}
239
240	writel(FIELD_PREP(TASR_BUF_VERF_SEL_MASK,
241			  FIELD_GET(TRIM2_BUF_VERF_SEL_MASK, trim[0])) |
242	       FIELD_PREP(TASR_BUF_SLOPE_MASK,
243			  FIELD_GET(TRIM2_BUF_SLOP_SEL_MASK, trim[0])),
244	       tmu->base + TASR);
245
246	writel(FIELD_PREP(TRIM_BJT_CUR_MASK,
247			  FIELD_GET(TRIM2_BJT_CUR_MASK, trim[0])) |
248	       FIELD_PREP(TRIM_BGR_MASK, FIELD_GET(TRIM2_BGR_MASK, trim[0])) |
249	       FIELD_PREP(TRIM_VLSB_MASK, FIELD_GET(TRIM2_VLSB_MASK, trim[0])) |
250	       TRIM_EN_CH,
251	       tmu->base + TRIM);
252
253	writel(FIELD_PREP(TCALIV_SNSR25C_MASK,
254			  FIELD_GET(TRIM3_TCA25_0_LSB_MASK, trim[1]) |
255			  (FIELD_GET(TRIM4_TCA25_0_MSB_MASK, trim[2]) << 4)) |
256	       FIELD_PREP(TCALIV_SNSR105C_MASK,
257			  FIELD_GET(TRIM4_TCA105_0_MASK, trim[2])),
258	       tmu->base + TCALIV(0));
259
260	writel(FIELD_PREP(TCALIV_SNSR25C_MASK,
261			  FIELD_GET(TRIM5_TCA25_1_MASK, trim[3])) |
262	       FIELD_PREP(TCALIV_SNSR105C_MASK,
263			  FIELD_GET(TRIM5_TCA105_1_MASK, trim[3])),
264	       tmu->base + TCALIV(1));
265
266	writel(FIELD_PREP(TCALIV_SNSR25C_MASK,
267			  FIELD_GET(TRIM3_TCA40_0_MASK, trim[1])) |
268	       FIELD_PREP(TCALIV_SNSR105C_MASK,
269			  FIELD_GET(TRIM4_TCA40_1_MASK, trim[2])),
270	       tmu->base + TCALIV(2));
271
272	return 0;
273}
274
275static int imx8mm_tmu_probe_set_calib(struct platform_device *pdev,
276				      struct imx8mm_tmu *tmu)
277{
278	struct device *dev = &pdev->dev;
279
280	/*
281	 * Lack of calibration data OCOTP reference is not considered
282	 * fatal to retain compatibility with old DTs. It is however
283	 * strongly recommended to update such old DTs to get correct
284	 * temperature compensation values for each SoC.
285	 */
286	if (!of_find_property(pdev->dev.of_node, "nvmem-cells", NULL)) {
287		dev_warn(dev,
288			 "No OCOTP nvmem reference found, SoC-specific calibration not loaded. Please update your DT.\n");
289		return 0;
290	}
291
292	if (tmu->socdata->version == TMU_VER1)
293		return imx8mm_tmu_probe_set_calib_v1(pdev, tmu);
294
295	return imx8mm_tmu_probe_set_calib_v2(pdev, tmu);
296}
297
298static int imx8mm_tmu_probe(struct platform_device *pdev)
299{
300	const struct thermal_soc_data *data;
301	struct imx8mm_tmu *tmu;
302	int ret;
303	int i;
304
305	data = of_device_get_match_data(&pdev->dev);
306
307	tmu = devm_kzalloc(&pdev->dev, struct_size(tmu, sensors,
308			   data->num_sensors), GFP_KERNEL);
309	if (!tmu)
310		return -ENOMEM;
311
312	tmu->socdata = data;
313
314	tmu->base = devm_platform_ioremap_resource(pdev, 0);
315	if (IS_ERR(tmu->base))
316		return PTR_ERR(tmu->base);
317
318	tmu->clk = devm_clk_get(&pdev->dev, NULL);
319	if (IS_ERR(tmu->clk))
320		return dev_err_probe(&pdev->dev, PTR_ERR(tmu->clk),
321				     "failed to get tmu clock\n");
322
323	ret = clk_prepare_enable(tmu->clk);
324	if (ret) {
325		dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret);
326		return ret;
327	}
328
329	/* disable the monitor during initialization */
330	imx8mm_tmu_enable(tmu, false);
331
332	for (i = 0; i < data->num_sensors; i++) {
333		tmu->sensors[i].priv = tmu;
334		tmu->sensors[i].tzd =
335			devm_thermal_of_zone_register(&pdev->dev, i,
336						      &tmu->sensors[i],
337						      &tmu_tz_ops);
338		if (IS_ERR(tmu->sensors[i].tzd)) {
339			ret = PTR_ERR(tmu->sensors[i].tzd);
340			dev_err(&pdev->dev,
341				"failed to register thermal zone sensor[%d]: %d\n",
342				i, ret);
343			goto disable_clk;
344		}
345		tmu->sensors[i].hw_id = i;
346
347		if (devm_thermal_add_hwmon_sysfs(tmu->sensors[i].tzd))
348			dev_warn(&pdev->dev, "failed to add hwmon sysfs attributes\n");
349	}
350
351	platform_set_drvdata(pdev, tmu);
352
353	ret = imx8mm_tmu_probe_set_calib(pdev, tmu);
354	if (ret)
355		goto disable_clk;
356
357	/* enable all the probes for V2 TMU */
358	if (tmu->socdata->version == TMU_VER2)
359		imx8mm_tmu_probe_sel_all(tmu);
360
361	/* enable the monitor */
362	imx8mm_tmu_enable(tmu, true);
363
364	return 0;
365
366disable_clk:
367	clk_disable_unprepare(tmu->clk);
368	return ret;
369}
370
371static int imx8mm_tmu_remove(struct platform_device *pdev)
372{
373	struct imx8mm_tmu *tmu = platform_get_drvdata(pdev);
374
375	/* disable TMU */
376	imx8mm_tmu_enable(tmu, false);
377
378	clk_disable_unprepare(tmu->clk);
379	platform_set_drvdata(pdev, NULL);
380
381	return 0;
382}
383
384static struct thermal_soc_data imx8mm_tmu_data = {
385	.num_sensors = 1,
386	.version = TMU_VER1,
387	.get_temp = imx8mm_tmu_get_temp,
388};
389
390static struct thermal_soc_data imx8mp_tmu_data = {
391	.num_sensors = 2,
392	.version = TMU_VER2,
393	.get_temp = imx8mp_tmu_get_temp,
394};
395
396static const struct of_device_id imx8mm_tmu_table[] = {
397	{ .compatible = "fsl,imx8mm-tmu", .data = &imx8mm_tmu_data, },
398	{ .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, },
399	{ },
400};
401MODULE_DEVICE_TABLE(of, imx8mm_tmu_table);
402
403static struct platform_driver imx8mm_tmu = {
404	.driver = {
405		.name	= "i.mx8mm_thermal",
406		.of_match_table = imx8mm_tmu_table,
407	},
408	.probe = imx8mm_tmu_probe,
409	.remove = imx8mm_tmu_remove,
410};
411module_platform_driver(imx8mm_tmu);
412
413MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
414MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver");
415MODULE_LICENSE("GPL v2");