// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2018 NXP. * */ #include #include #include #include #include #include #include #include #include #include #include "thermal_core.h" #define TER 0x0 /* TMU enable */ #define TSR 0x4 /* TMU status */ #define TIER 0x8 /* TMU interrupt enable */ #define TIDR 0xc /* TMU interrupt detect */ #define TMHTITR 0x10 /* TMU high immediate threshold */ #define TMHTATR 0x14 /* TMU high average threshold */ #define TMHTCATR 0x18 /* TMU high average critical threshold */ #define TSCR 0x1c /* TMU sensor val(raw, no calibration) */ #define TRITSR 0x20 /* TMU immediate temp */ #define TRATSR 0x24 /* TMU average temp */ #define TER_EN BIT(31) #define TRITSR_VALID BIT(31) #define TEMP_VAL_MASK 0xff #define TEMP_LOW_LIMIT 10 #define IMX_TEMP_PASSIVE_COOL_DELTA 10000 struct imx8mm_tmu { struct thermal_zone_device *tzd; struct thermal_cooling_device *cdev; struct clk *clk; void __iomem *tmu_base; bool enabled; int temp_passive; int temp_critical; }; /* The driver support 1 passive trip point and 1 critical trip point */ enum imx_thermal_trip { IMX_TRIP_PASSIVE, IMX_TRIP_CRITICAL, IMX_TRIP_NUM, }; static int tmu_get_temp(void *data, int *temp) { struct imx8mm_tmu *tmu = data; u32 val; /* the temp sensor need about 1ms to finish the measurement */ msleep(1); /* read the calibrated temp value */ val = readl_relaxed(tmu->tmu_base + TRITSR) & TEMP_VAL_MASK; /* check if the temp in the sensor's range */ if (val < TEMP_LOW_LIMIT) return -EAGAIN; *temp = val * 1000; return 0; } static int tmu_get_trend(void *p, int trip, enum thermal_trend *trend) { int trip_temp; struct imx8mm_tmu *tmu = p; if (!tmu->tzd) return 0; trip_temp = (trip == IMX_TRIP_PASSIVE) ? tmu->temp_passive : tmu->temp_critical; if (tmu->tzd->temperature >= (trip_temp - IMX_TEMP_PASSIVE_COOL_DELTA)) *trend = THERMAL_TREND_RAISE_FULL; else *trend = THERMAL_TREND_DROP_FULL; return 0; } static int tmu_set_trip_temp(void *p, int trip, int temp) { struct imx8mm_tmu *tmu = p; if (trip == IMX_TRIP_CRITICAL) tmu->temp_critical = temp; if (trip == IMX_TRIP_PASSIVE) tmu->temp_passive = temp; return 0; } static struct thermal_zone_of_device_ops tmu_tz_ops = { .get_temp = tmu_get_temp, .get_trend = tmu_get_trend, .set_trip_temp = tmu_set_trip_temp, }; static const struct of_device_id imx8mm_tmu_table[] = { { .compatible = "fsl,imx8mm-tmu", }, { }, }; static int imx8mm_tmu_probe(struct platform_device *pdev) { int ret; u32 val; struct imx8mm_tmu *tmu; const struct thermal_trip *trips; struct device_node *np = pdev->dev.of_node; if (!np) { dev_err(&pdev->dev, "device node NOT found\n"); return -ENODEV; } tmu = devm_kzalloc(&pdev->dev, sizeof(*tmu), GFP_KERNEL); if (!tmu) return -ENOMEM; platform_set_drvdata(pdev, tmu); tmu->tmu_base = of_iomap(np, 0); if (!tmu->tmu_base) { dev_err(&pdev->dev, "Failed to map the memory\n"); return -ENODEV; } tmu->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(tmu->clk)) { ret = PTR_ERR(tmu->clk); dev_err(&pdev->dev, "Failed to get the tmu clk\n"); goto err; } /* register the thermal zone sensor */ tmu->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, tmu, &tmu_tz_ops); if (IS_ERR(tmu->tzd)) { ret = PTR_ERR(tmu->tzd); dev_err(&pdev->dev, "Failed to register thermal zone sensor %d\n", ret); goto err; } tmu->cdev = devfreq_cooling_register(); if (IS_ERR(tmu->cdev)) { ret = PTR_ERR(tmu->cdev); if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "failed to register devfreq cooling device %d\n", ret); goto err; } ret = thermal_zone_bind_cooling_device(tmu->tzd, IMX_TRIP_PASSIVE, tmu->cdev, THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, THERMAL_WEIGHT_DEFAULT); if (ret) { dev_err(&pdev->dev, "binding zone %s with cdev %s failed:%d\n", tmu->tzd->type, tmu->cdev->type, ret); devfreq_cooling_unregister(tmu->cdev); goto err; } trips = of_thermal_get_trip_points(tmu->tzd); /* get the thermal trip temp */ tmu->temp_passive = trips[0].temperature; tmu->temp_critical = trips[1].temperature; /* enable the tmu clock */ ret = clk_prepare_enable(tmu->clk); if (ret) { dev_warn(&pdev->dev, "tmu clock enable failed:%d\n", ret); thermal_zone_unbind_cooling_device(tmu->tzd, IMX_TRIP_PASSIVE, tmu->cdev); devfreq_cooling_unregister(tmu->cdev); goto err; } /* enable the monitor */ val = readl_relaxed(tmu->tmu_base + TER); val |= TER_EN; writel_relaxed(val, tmu->tmu_base + TER); return 0; err: iounmap(tmu->tmu_base); return ret; } static int imx8mm_tmu_remove(struct platform_device *pdev) { u32 val; struct imx8mm_tmu *tmu = platform_get_drvdata(pdev); thermal_zone_unbind_cooling_device(tmu->tzd, IMX_TRIP_PASSIVE, tmu->cdev); devfreq_cooling_unregister(tmu->cdev); /* disable TMU */ val = readl_relaxed(tmu->tmu_base + TER); val &= ~TER_EN; writel_relaxed(val, tmu->tmu_base + TER); /* disable TMU clk */ clk_disable_unprepare(tmu->clk); iounmap(tmu->tmu_base); return 0; } static struct platform_driver imx8mm_tmu = { .driver = { .name = "i.mx8mm_thermal", .of_match_table = imx8mm_tmu_table, }, .probe = imx8mm_tmu_probe, .remove = imx8mm_tmu_remove, }; module_platform_driver(imx8mm_tmu); MODULE_AUTHOR("Jacky Bai "); MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver"); MODULE_LICENSE("GPL v2");