linux/drivers/thermal/qoriq_thermal.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright 2016 Freescale Semiconductor, Inc.
   4
   5#include <linux/module.h>
   6#include <linux/platform_device.h>
   7#include <linux/err.h>
   8#include <linux/io.h>
   9#include <linux/of.h>
  10#include <linux/of_address.h>
  11#include <linux/thermal.h>
  12
  13#include "thermal_core.h"
  14
  15#define SITES_MAX       16
  16
  17/*
  18 * QorIQ TMU Registers
  19 */
  20struct qoriq_tmu_site_regs {
  21        u32 tritsr;             /* Immediate Temperature Site Register */
  22        u32 tratsr;             /* Average Temperature Site Register */
  23        u8 res0[0x8];
  24};
  25
  26struct qoriq_tmu_regs {
  27        u32 tmr;                /* Mode Register */
  28#define TMR_DISABLE     0x0
  29#define TMR_ME          0x80000000
  30#define TMR_ALPF        0x0c000000
  31        u32 tsr;                /* Status Register */
  32        u32 tmtmir;             /* Temperature measurement interval Register */
  33#define TMTMIR_DEFAULT  0x0000000f
  34        u8 res0[0x14];
  35        u32 tier;               /* Interrupt Enable Register */
  36#define TIER_DISABLE    0x0
  37        u32 tidr;               /* Interrupt Detect Register */
  38        u32 tiscr;              /* Interrupt Site Capture Register */
  39        u32 ticscr;             /* Interrupt Critical Site Capture Register */
  40        u8 res1[0x10];
  41        u32 tmhtcrh;            /* High Temperature Capture Register */
  42        u32 tmhtcrl;            /* Low Temperature Capture Register */
  43        u8 res2[0x8];
  44        u32 tmhtitr;            /* High Temperature Immediate Threshold */
  45        u32 tmhtatr;            /* High Temperature Average Threshold */
  46        u32 tmhtactr;   /* High Temperature Average Crit Threshold */
  47        u8 res3[0x24];
  48        u32 ttcfgr;             /* Temperature Configuration Register */
  49        u32 tscfgr;             /* Sensor Configuration Register */
  50        u8 res4[0x78];
  51        struct qoriq_tmu_site_regs site[SITES_MAX];
  52        u8 res5[0x9f8];
  53        u32 ipbrr0;             /* IP Block Revision Register 0 */
  54        u32 ipbrr1;             /* IP Block Revision Register 1 */
  55        u8 res6[0x310];
  56        u32 ttr0cr;             /* Temperature Range 0 Control Register */
  57        u32 ttr1cr;             /* Temperature Range 1 Control Register */
  58        u32 ttr2cr;             /* Temperature Range 2 Control Register */
  59        u32 ttr3cr;             /* Temperature Range 3 Control Register */
  60};
  61
  62struct qoriq_tmu_data;
  63
  64/*
  65 * Thermal zone data
  66 */
  67struct qoriq_sensor {
  68        struct thermal_zone_device      *tzd;
  69        struct qoriq_tmu_data           *qdata;
  70        int                             id;
  71};
  72
  73struct qoriq_tmu_data {
  74        struct qoriq_tmu_regs __iomem *regs;
  75        bool little_endian;
  76        struct qoriq_sensor     *sensor[SITES_MAX];
  77};
  78
  79static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
  80{
  81        if (p->little_endian)
  82                iowrite32(val, addr);
  83        else
  84                iowrite32be(val, addr);
  85}
  86
  87static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
  88{
  89        if (p->little_endian)
  90                return ioread32(addr);
  91        else
  92                return ioread32be(addr);
  93}
  94
  95static int tmu_get_temp(void *p, int *temp)
  96{
  97        struct qoriq_sensor *qsensor = p;
  98        struct qoriq_tmu_data *qdata = qsensor->qdata;
  99        u32 val;
 100
 101        val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr);
 102        *temp = (val & 0xff) * 1000;
 103
 104        return 0;
 105}
 106
 107static const struct thermal_zone_of_device_ops tmu_tz_ops = {
 108        .get_temp = tmu_get_temp,
 109};
 110
 111static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev)
 112{
 113        struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev);
 114        int id, sites = 0;
 115
 116        for (id = 0; id < SITES_MAX; id++) {
 117                qdata->sensor[id] = devm_kzalloc(&pdev->dev,
 118                                sizeof(struct qoriq_sensor), GFP_KERNEL);
 119                if (!qdata->sensor[id])
 120                        return -ENOMEM;
 121
 122                qdata->sensor[id]->id = id;
 123                qdata->sensor[id]->qdata = qdata;
 124                qdata->sensor[id]->tzd = devm_thermal_zone_of_sensor_register(
 125                                &pdev->dev, id, qdata->sensor[id], &tmu_tz_ops);
 126                if (IS_ERR(qdata->sensor[id]->tzd)) {
 127                        if (PTR_ERR(qdata->sensor[id]->tzd) == -ENODEV)
 128                                continue;
 129                        else
 130                                return PTR_ERR(qdata->sensor[id]->tzd);
 131                }
 132
 133                sites |= 0x1 << (15 - id);
 134        }
 135
 136        /* Enable monitoring */
 137        if (sites != 0)
 138                tmu_write(qdata, sites | TMR_ME | TMR_ALPF, &qdata->regs->tmr);
 139
 140        return 0;
 141}
 142
 143static int qoriq_tmu_calibration(struct platform_device *pdev)
 144{
 145        int i, val, len;
 146        u32 range[4];
 147        const u32 *calibration;
 148        struct device_node *np = pdev->dev.of_node;
 149        struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
 150
 151        if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
 152                dev_err(&pdev->dev, "missing calibration range.\n");
 153                return -ENODEV;
 154        }
 155
 156        /* Init temperature range registers */
 157        tmu_write(data, range[0], &data->regs->ttr0cr);
 158        tmu_write(data, range[1], &data->regs->ttr1cr);
 159        tmu_write(data, range[2], &data->regs->ttr2cr);
 160        tmu_write(data, range[3], &data->regs->ttr3cr);
 161
 162        calibration = of_get_property(np, "fsl,tmu-calibration", &len);
 163        if (calibration == NULL || len % 8) {
 164                dev_err(&pdev->dev, "invalid calibration data.\n");
 165                return -ENODEV;
 166        }
 167
 168        for (i = 0; i < len; i += 8, calibration += 2) {
 169                val = of_read_number(calibration, 1);
 170                tmu_write(data, val, &data->regs->ttcfgr);
 171                val = of_read_number(calibration + 1, 1);
 172                tmu_write(data, val, &data->regs->tscfgr);
 173        }
 174
 175        return 0;
 176}
 177
 178static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
 179{
 180        /* Disable interrupt, using polling instead */
 181        tmu_write(data, TIER_DISABLE, &data->regs->tier);
 182
 183        /* Set update_interval */
 184        tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
 185
 186        /* Disable monitoring */
 187        tmu_write(data, TMR_DISABLE, &data->regs->tmr);
 188}
 189
 190static int qoriq_tmu_probe(struct platform_device *pdev)
 191{
 192        int ret;
 193        struct qoriq_tmu_data *data;
 194        struct device_node *np = pdev->dev.of_node;
 195
 196        data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
 197                            GFP_KERNEL);
 198        if (!data)
 199                return -ENOMEM;
 200
 201        platform_set_drvdata(pdev, data);
 202
 203        data->little_endian = of_property_read_bool(np, "little-endian");
 204
 205        data->regs = of_iomap(np, 0);
 206        if (!data->regs) {
 207                dev_err(&pdev->dev, "Failed to get memory region\n");
 208                ret = -ENODEV;
 209                goto err_iomap;
 210        }
 211
 212        qoriq_tmu_init_device(data);    /* TMU initialization */
 213
 214        ret = qoriq_tmu_calibration(pdev);      /* TMU calibration */
 215        if (ret < 0)
 216                goto err_tmu;
 217
 218        ret = qoriq_tmu_register_tmu_zone(pdev);
 219        if (ret < 0) {
 220                dev_err(&pdev->dev, "Failed to register sensors\n");
 221                ret = -ENODEV;
 222                goto err_iomap;
 223        }
 224
 225        return 0;
 226
 227err_tmu:
 228        iounmap(data->regs);
 229
 230err_iomap:
 231        platform_set_drvdata(pdev, NULL);
 232
 233        return ret;
 234}
 235
 236static int qoriq_tmu_remove(struct platform_device *pdev)
 237{
 238        struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
 239
 240        /* Disable monitoring */
 241        tmu_write(data, TMR_DISABLE, &data->regs->tmr);
 242
 243        iounmap(data->regs);
 244        platform_set_drvdata(pdev, NULL);
 245
 246        return 0;
 247}
 248
 249#ifdef CONFIG_PM_SLEEP
 250static int qoriq_tmu_suspend(struct device *dev)
 251{
 252        u32 tmr;
 253        struct qoriq_tmu_data *data = dev_get_drvdata(dev);
 254
 255        /* Disable monitoring */
 256        tmr = tmu_read(data, &data->regs->tmr);
 257        tmr &= ~TMR_ME;
 258        tmu_write(data, tmr, &data->regs->tmr);
 259
 260        return 0;
 261}
 262
 263static int qoriq_tmu_resume(struct device *dev)
 264{
 265        u32 tmr;
 266        struct qoriq_tmu_data *data = dev_get_drvdata(dev);
 267
 268        /* Enable monitoring */
 269        tmr = tmu_read(data, &data->regs->tmr);
 270        tmr |= TMR_ME;
 271        tmu_write(data, tmr, &data->regs->tmr);
 272
 273        return 0;
 274}
 275#endif
 276
 277static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
 278                         qoriq_tmu_suspend, qoriq_tmu_resume);
 279
 280static const struct of_device_id qoriq_tmu_match[] = {
 281        { .compatible = "fsl,qoriq-tmu", },
 282        { .compatible = "fsl,imx8mq-tmu", },
 283        {},
 284};
 285MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
 286
 287static struct platform_driver qoriq_tmu = {
 288        .driver = {
 289                .name           = "qoriq_thermal",
 290                .pm             = &qoriq_tmu_pm_ops,
 291                .of_match_table = qoriq_tmu_match,
 292        },
 293        .probe  = qoriq_tmu_probe,
 294        .remove = qoriq_tmu_remove,
 295};
 296module_platform_driver(qoriq_tmu);
 297
 298MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
 299MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
 300MODULE_LICENSE("GPL v2");
 301