Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2020 NXP.
0004  *
0005  * Author: Anson Huang <Anson.Huang@nxp.com>
0006  */
0007 
0008 #include <linux/bitfield.h>
0009 #include <linux/clk.h>
0010 #include <linux/err.h>
0011 #include <linux/io.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/of_device.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/thermal.h>
0017 
0018 #include "thermal_core.h"
0019 
0020 #define TER         0x0 /* TMU enable */
0021 #define TPS         0x4
0022 #define TRITSR          0x20    /* TMU immediate temp */
0023 
0024 #define TER_ADC_PD      BIT(30)
0025 #define TER_EN          BIT(31)
0026 #define TRITSR_TEMP0_VAL_MASK   0xff
0027 #define TRITSR_TEMP1_VAL_MASK   0xff0000
0028 
0029 #define PROBE_SEL_ALL       GENMASK(31, 30)
0030 
0031 #define probe_status_offset(x)  (30 + x)
0032 #define SIGN_BIT        BIT(7)
0033 #define TEMP_VAL_MASK       GENMASK(6, 0)
0034 
0035 #define VER1_TEMP_LOW_LIMIT 10000
0036 #define VER2_TEMP_LOW_LIMIT -40000
0037 #define VER2_TEMP_HIGH_LIMIT    125000
0038 
0039 #define TMU_VER1        0x1
0040 #define TMU_VER2        0x2
0041 
0042 struct thermal_soc_data {
0043     u32 num_sensors;
0044     u32 version;
0045     int (*get_temp)(void *, int *);
0046 };
0047 
0048 struct tmu_sensor {
0049     struct imx8mm_tmu *priv;
0050     u32 hw_id;
0051     struct thermal_zone_device *tzd;
0052 };
0053 
0054 struct imx8mm_tmu {
0055     void __iomem *base;
0056     struct clk *clk;
0057     const struct thermal_soc_data *socdata;
0058     struct tmu_sensor sensors[];
0059 };
0060 
0061 static int imx8mm_tmu_get_temp(void *data, int *temp)
0062 {
0063     struct tmu_sensor *sensor = data;
0064     struct imx8mm_tmu *tmu = sensor->priv;
0065     u32 val;
0066 
0067     val = readl_relaxed(tmu->base + TRITSR) & TRITSR_TEMP0_VAL_MASK;
0068     *temp = val * 1000;
0069     if (*temp < VER1_TEMP_LOW_LIMIT)
0070         return -EAGAIN;
0071 
0072     return 0;
0073 }
0074 
0075 static int imx8mp_tmu_get_temp(void *data, int *temp)
0076 {
0077     struct tmu_sensor *sensor = data;
0078     struct imx8mm_tmu *tmu = sensor->priv;
0079     unsigned long val;
0080     bool ready;
0081 
0082     val = readl_relaxed(tmu->base + TRITSR);
0083     ready = test_bit(probe_status_offset(sensor->hw_id), &val);
0084     if (!ready)
0085         return -EAGAIN;
0086 
0087     val = sensor->hw_id ? FIELD_GET(TRITSR_TEMP1_VAL_MASK, val) :
0088           FIELD_GET(TRITSR_TEMP0_VAL_MASK, val);
0089     if (val & SIGN_BIT) /* negative */
0090         val = (~(val & TEMP_VAL_MASK) + 1);
0091 
0092     *temp = val * 1000;
0093     if (*temp < VER2_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT)
0094         return -EAGAIN;
0095 
0096     return 0;
0097 }
0098 
0099 static int tmu_get_temp(void *data, int *temp)
0100 {
0101     struct tmu_sensor *sensor = data;
0102     struct imx8mm_tmu *tmu = sensor->priv;
0103 
0104     return tmu->socdata->get_temp(data, temp);
0105 }
0106 
0107 static struct thermal_zone_of_device_ops tmu_tz_ops = {
0108     .get_temp = tmu_get_temp,
0109 };
0110 
0111 static void imx8mm_tmu_enable(struct imx8mm_tmu *tmu, bool enable)
0112 {
0113     u32 val;
0114 
0115     val = readl_relaxed(tmu->base + TER);
0116     val = enable ? (val | TER_EN) : (val & ~TER_EN);
0117     if (tmu->socdata->version == TMU_VER2)
0118         val = enable ? (val & ~TER_ADC_PD) : (val | TER_ADC_PD);
0119     writel_relaxed(val, tmu->base + TER);
0120 }
0121 
0122 static void imx8mm_tmu_probe_sel_all(struct imx8mm_tmu *tmu)
0123 {
0124     u32 val;
0125 
0126     val = readl_relaxed(tmu->base + TPS);
0127     val |= PROBE_SEL_ALL;
0128     writel_relaxed(val, tmu->base + TPS);
0129 }
0130 
0131 static int imx8mm_tmu_probe(struct platform_device *pdev)
0132 {
0133     const struct thermal_soc_data *data;
0134     struct imx8mm_tmu *tmu;
0135     int ret;
0136     int i;
0137 
0138     data = of_device_get_match_data(&pdev->dev);
0139 
0140     tmu = devm_kzalloc(&pdev->dev, struct_size(tmu, sensors,
0141                data->num_sensors), GFP_KERNEL);
0142     if (!tmu)
0143         return -ENOMEM;
0144 
0145     tmu->socdata = data;
0146 
0147     tmu->base = devm_platform_ioremap_resource(pdev, 0);
0148     if (IS_ERR(tmu->base))
0149         return PTR_ERR(tmu->base);
0150 
0151     tmu->clk = devm_clk_get(&pdev->dev, NULL);
0152     if (IS_ERR(tmu->clk))
0153         return dev_err_probe(&pdev->dev, PTR_ERR(tmu->clk),
0154                      "failed to get tmu clock\n");
0155 
0156     ret = clk_prepare_enable(tmu->clk);
0157     if (ret) {
0158         dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret);
0159         return ret;
0160     }
0161 
0162     /* disable the monitor during initialization */
0163     imx8mm_tmu_enable(tmu, false);
0164 
0165     for (i = 0; i < data->num_sensors; i++) {
0166         tmu->sensors[i].priv = tmu;
0167         tmu->sensors[i].tzd =
0168             devm_thermal_zone_of_sensor_register(&pdev->dev, i,
0169                                  &tmu->sensors[i],
0170                                  &tmu_tz_ops);
0171         if (IS_ERR(tmu->sensors[i].tzd)) {
0172             ret = PTR_ERR(tmu->sensors[i].tzd);
0173             dev_err(&pdev->dev,
0174                 "failed to register thermal zone sensor[%d]: %d\n",
0175                 i, ret);
0176             goto disable_clk;
0177         }
0178         tmu->sensors[i].hw_id = i;
0179     }
0180 
0181     platform_set_drvdata(pdev, tmu);
0182 
0183     /* enable all the probes for V2 TMU */
0184     if (tmu->socdata->version == TMU_VER2)
0185         imx8mm_tmu_probe_sel_all(tmu);
0186 
0187     /* enable the monitor */
0188     imx8mm_tmu_enable(tmu, true);
0189 
0190     return 0;
0191 
0192 disable_clk:
0193     clk_disable_unprepare(tmu->clk);
0194     return ret;
0195 }
0196 
0197 static int imx8mm_tmu_remove(struct platform_device *pdev)
0198 {
0199     struct imx8mm_tmu *tmu = platform_get_drvdata(pdev);
0200 
0201     /* disable TMU */
0202     imx8mm_tmu_enable(tmu, false);
0203 
0204     clk_disable_unprepare(tmu->clk);
0205     platform_set_drvdata(pdev, NULL);
0206 
0207     return 0;
0208 }
0209 
0210 static struct thermal_soc_data imx8mm_tmu_data = {
0211     .num_sensors = 1,
0212     .version = TMU_VER1,
0213     .get_temp = imx8mm_tmu_get_temp,
0214 };
0215 
0216 static struct thermal_soc_data imx8mp_tmu_data = {
0217     .num_sensors = 2,
0218     .version = TMU_VER2,
0219     .get_temp = imx8mp_tmu_get_temp,
0220 };
0221 
0222 static const struct of_device_id imx8mm_tmu_table[] = {
0223     { .compatible = "fsl,imx8mm-tmu", .data = &imx8mm_tmu_data, },
0224     { .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, },
0225     { },
0226 };
0227 MODULE_DEVICE_TABLE(of, imx8mm_tmu_table);
0228 
0229 static struct platform_driver imx8mm_tmu = {
0230     .driver = {
0231         .name   = "i.mx8mm_thermal",
0232         .of_match_table = imx8mm_tmu_table,
0233     },
0234     .probe = imx8mm_tmu_probe,
0235     .remove = imx8mm_tmu_remove,
0236 };
0237 module_platform_driver(imx8mm_tmu);
0238 
0239 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
0240 MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver");
0241 MODULE_LICENSE("GPL v2");