linux/drivers/thermal/sun8i_thermal.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Thermal sensor driver for Allwinner SOC
   4 * Copyright (C) 2019 Yangtao Li
   5 *
   6 * Based on the work of Icenowy Zheng <icenowy@aosc.io>
   7 * Based on the work of Ondrej Jirman <megous@megous.com>
   8 * Based on the work of Josef Gajdusek <atx@atx.name>
   9 */
  10
  11#include <linux/bitmap.h>
  12#include <linux/clk.h>
  13#include <linux/device.h>
  14#include <linux/interrupt.h>
  15#include <linux/module.h>
  16#include <linux/nvmem-consumer.h>
  17#include <linux/of_device.h>
  18#include <linux/platform_device.h>
  19#include <linux/regmap.h>
  20#include <linux/reset.h>
  21#include <linux/slab.h>
  22#include <linux/thermal.h>
  23
  24#include "thermal_hwmon.h"
  25
  26#define MAX_SENSOR_NUM  4
  27
  28#define FT_TEMP_MASK                            GENMASK(11, 0)
  29#define TEMP_CALIB_MASK                         GENMASK(11, 0)
  30#define CALIBRATE_DEFAULT                       0x800
  31
  32#define SUN8I_THS_CTRL0                         0x00
  33#define SUN8I_THS_CTRL2                         0x40
  34#define SUN8I_THS_IC                            0x44
  35#define SUN8I_THS_IS                            0x48
  36#define SUN8I_THS_MFC                           0x70
  37#define SUN8I_THS_TEMP_CALIB                    0x74
  38#define SUN8I_THS_TEMP_DATA                     0x80
  39
  40#define SUN50I_THS_CTRL0                        0x00
  41#define SUN50I_H6_THS_ENABLE                    0x04
  42#define SUN50I_H6_THS_PC                        0x08
  43#define SUN50I_H6_THS_DIC                       0x10
  44#define SUN50I_H6_THS_DIS                       0x20
  45#define SUN50I_H6_THS_MFC                       0x30
  46#define SUN50I_H6_THS_TEMP_CALIB                0xa0
  47#define SUN50I_H6_THS_TEMP_DATA                 0xc0
  48
  49#define SUN8I_THS_CTRL0_T_ACQ0(x)               (GENMASK(15, 0) & (x))
  50#define SUN8I_THS_CTRL2_T_ACQ1(x)               ((GENMASK(15, 0) & (x)) << 16)
  51#define SUN8I_THS_DATA_IRQ_STS(x)               BIT(x + 8)
  52
  53#define SUN50I_THS_CTRL0_T_ACQ(x)               ((GENMASK(15, 0) & (x)) << 16)
  54#define SUN50I_THS_FILTER_EN                    BIT(2)
  55#define SUN50I_THS_FILTER_TYPE(x)               (GENMASK(1, 0) & (x))
  56#define SUN50I_H6_THS_PC_TEMP_PERIOD(x)         ((GENMASK(19, 0) & (x)) << 12)
  57#define SUN50I_H6_THS_DATA_IRQ_STS(x)           BIT(x)
  58
  59/* millidegree celsius */
  60
  61struct tsensor {
  62        struct ths_device               *tmdev;
  63        struct thermal_zone_device      *tzd;
  64        int                             id;
  65};
  66
  67struct ths_thermal_chip {
  68        bool            has_mod_clk;
  69        bool            has_bus_clk_reset;
  70        int             sensor_num;
  71        int             offset;
  72        int             scale;
  73        int             ft_deviation;
  74        int             temp_data_base;
  75        int             (*calibrate)(struct ths_device *tmdev,
  76                                     u16 *caldata, int callen);
  77        int             (*init)(struct ths_device *tmdev);
  78        unsigned long   (*irq_ack)(struct ths_device *tmdev);
  79        int             (*calc_temp)(struct ths_device *tmdev,
  80                                     int id, int reg);
  81};
  82
  83struct ths_device {
  84        const struct ths_thermal_chip           *chip;
  85        struct device                           *dev;
  86        struct regmap                           *regmap;
  87        struct reset_control                    *reset;
  88        struct clk                              *bus_clk;
  89        struct clk                              *mod_clk;
  90        struct tsensor                          sensor[MAX_SENSOR_NUM];
  91};
  92
  93/* Temp Unit: millidegree Celsius */
  94static int sun8i_ths_calc_temp(struct ths_device *tmdev,
  95                               int id, int reg)
  96{
  97        return tmdev->chip->offset - (reg * tmdev->chip->scale / 10);
  98}
  99
 100static int sun50i_h5_calc_temp(struct ths_device *tmdev,
 101                               int id, int reg)
 102{
 103        if (reg >= 0x500)
 104                return -1191 * reg / 10 + 223000;
 105        else if (!id)
 106                return -1452 * reg / 10 + 259000;
 107        else
 108                return -1590 * reg / 10 + 276000;
 109}
 110
 111static int sun8i_ths_get_temp(void *data, int *temp)
 112{
 113        struct tsensor *s = data;
 114        struct ths_device *tmdev = s->tmdev;
 115        int val = 0;
 116
 117        regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
 118                    0x4 * s->id, &val);
 119
 120        /* ths have no data yet */
 121        if (!val)
 122                return -EAGAIN;
 123
 124        *temp = tmdev->chip->calc_temp(tmdev, s->id, val);
 125        /*
 126         * According to the original sdk, there are some platforms(rarely)
 127         * that add a fixed offset value after calculating the temperature
 128         * value. We can't simply put it on the formula for calculating the
 129         * temperature above, because the formula for calculating the
 130         * temperature above is also used when the sensor is calibrated. If
 131         * do this, the correct calibration formula is hard to know.
 132         */
 133        *temp += tmdev->chip->ft_deviation;
 134
 135        return 0;
 136}
 137
 138static const struct thermal_zone_of_device_ops ths_ops = {
 139        .get_temp = sun8i_ths_get_temp,
 140};
 141
 142static const struct regmap_config config = {
 143        .reg_bits = 32,
 144        .val_bits = 32,
 145        .reg_stride = 4,
 146        .fast_io = true,
 147        .max_register = 0xfc,
 148};
 149
 150static unsigned long sun8i_h3_irq_ack(struct ths_device *tmdev)
 151{
 152        unsigned long irq_bitmap = 0;
 153        int i, state;
 154
 155        regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
 156
 157        for (i = 0; i < tmdev->chip->sensor_num; i++) {
 158                if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
 159                        regmap_write(tmdev->regmap, SUN8I_THS_IS,
 160                                     SUN8I_THS_DATA_IRQ_STS(i));
 161                        bitmap_set(&irq_bitmap, i, 1);
 162                }
 163        }
 164
 165        return irq_bitmap;
 166}
 167
 168static unsigned long sun50i_h6_irq_ack(struct ths_device *tmdev)
 169{
 170        unsigned long irq_bitmap = 0;
 171        int i, state;
 172
 173        regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
 174
 175        for (i = 0; i < tmdev->chip->sensor_num; i++) {
 176                if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
 177                        regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
 178                                     SUN50I_H6_THS_DATA_IRQ_STS(i));
 179                        bitmap_set(&irq_bitmap, i, 1);
 180                }
 181        }
 182
 183        return irq_bitmap;
 184}
 185
 186static irqreturn_t sun8i_irq_thread(int irq, void *data)
 187{
 188        struct ths_device *tmdev = data;
 189        unsigned long irq_bitmap = tmdev->chip->irq_ack(tmdev);
 190        int i;
 191
 192        for_each_set_bit(i, &irq_bitmap, tmdev->chip->sensor_num) {
 193                thermal_zone_device_update(tmdev->sensor[i].tzd,
 194                                           THERMAL_EVENT_UNSPECIFIED);
 195        }
 196
 197        return IRQ_HANDLED;
 198}
 199
 200static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
 201                                  u16 *caldata, int callen)
 202{
 203        int i;
 204
 205        if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num)
 206                return -EINVAL;
 207
 208        for (i = 0; i < tmdev->chip->sensor_num; i++) {
 209                int offset = (i % 2) << 4;
 210
 211                regmap_update_bits(tmdev->regmap,
 212                                   SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
 213                                   0xfff << offset,
 214                                   caldata[i] << offset);
 215        }
 216
 217        return 0;
 218}
 219
 220static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
 221                                   u16 *caldata, int callen)
 222{
 223        struct device *dev = tmdev->dev;
 224        int i, ft_temp;
 225
 226        if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
 227                return -EINVAL;
 228
 229        /*
 230         * efuse layout:
 231         *
 232         *      0   11  16       32
 233         *      +-------+-------+-------+
 234         *      |temp|  |sensor0|sensor1|
 235         *      +-------+-------+-------+
 236         *
 237         * The calibration data on the H6 is the ambient temperature and
 238         * sensor values that are filled during the factory test stage.
 239         *
 240         * The unit of stored FT temperature is 0.1 degreee celusis.
 241         *
 242         * We need to calculate a delta between measured and caluclated
 243         * register values and this will become a calibration offset.
 244         */
 245        ft_temp = (caldata[0] & FT_TEMP_MASK) * 100;
 246
 247        for (i = 0; i < tmdev->chip->sensor_num; i++) {
 248                int sensor_reg = caldata[i + 1] & TEMP_CALIB_MASK;
 249                int cdata, offset;
 250                int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg);
 251
 252                /*
 253                 * Calibration data is CALIBRATE_DEFAULT - (calculated
 254                 * temperature from sensor reading at factory temperature
 255                 * minus actual factory temperature) * 14.88 (scale from
 256                 * temperature to register values)
 257                 */
 258                cdata = CALIBRATE_DEFAULT -
 259                        ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale);
 260                if (cdata & ~TEMP_CALIB_MASK) {
 261                        /*
 262                         * Calibration value more than 12-bit, but calibration
 263                         * register is 12-bit. In this case, ths hardware can
 264                         * still work without calibration, although the data
 265                         * won't be so accurate.
 266                         */
 267                        dev_warn(dev, "sensor%d is not calibrated.\n", i);
 268                        continue;
 269                }
 270
 271                offset = (i % 2) * 16;
 272                regmap_update_bits(tmdev->regmap,
 273                                   SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
 274                                   0xfff << offset,
 275                                   cdata << offset);
 276        }
 277
 278        return 0;
 279}
 280
 281static int sun8i_ths_calibrate(struct ths_device *tmdev)
 282{
 283        struct nvmem_cell *calcell;
 284        struct device *dev = tmdev->dev;
 285        u16 *caldata;
 286        size_t callen;
 287        int ret = 0;
 288
 289        calcell = devm_nvmem_cell_get(dev, "calibration");
 290        if (IS_ERR(calcell)) {
 291                if (PTR_ERR(calcell) == -EPROBE_DEFER)
 292                        return -EPROBE_DEFER;
 293                /*
 294                 * Even if the external calibration data stored in sid is
 295                 * not accessible, the THS hardware can still work, although
 296                 * the data won't be so accurate.
 297                 *
 298                 * The default value of calibration register is 0x800 for
 299                 * every sensor, and the calibration value is usually 0x7xx
 300                 * or 0x8xx, so they won't be away from the default value
 301                 * for a lot.
 302                 *
 303                 * So here we do not return error if the calibration data is
 304                 * not available, except the probe needs deferring.
 305                 */
 306                goto out;
 307        }
 308
 309        caldata = nvmem_cell_read(calcell, &callen);
 310        if (IS_ERR(caldata)) {
 311                ret = PTR_ERR(caldata);
 312                goto out;
 313        }
 314
 315        tmdev->chip->calibrate(tmdev, caldata, callen);
 316
 317        kfree(caldata);
 318out:
 319        return ret;
 320}
 321
 322static int sun8i_ths_resource_init(struct ths_device *tmdev)
 323{
 324        struct device *dev = tmdev->dev;
 325        struct platform_device *pdev = to_platform_device(dev);
 326        void __iomem *base;
 327        int ret;
 328
 329        base = devm_platform_ioremap_resource(pdev, 0);
 330        if (IS_ERR(base))
 331                return PTR_ERR(base);
 332
 333        tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
 334        if (IS_ERR(tmdev->regmap))
 335                return PTR_ERR(tmdev->regmap);
 336
 337        if (tmdev->chip->has_bus_clk_reset) {
 338                tmdev->reset = devm_reset_control_get(dev, NULL);
 339                if (IS_ERR(tmdev->reset))
 340                        return PTR_ERR(tmdev->reset);
 341
 342                tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus");
 343                if (IS_ERR(tmdev->bus_clk))
 344                        return PTR_ERR(tmdev->bus_clk);
 345        }
 346
 347        if (tmdev->chip->has_mod_clk) {
 348                tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod");
 349                if (IS_ERR(tmdev->mod_clk))
 350                        return PTR_ERR(tmdev->mod_clk);
 351        }
 352
 353        ret = reset_control_deassert(tmdev->reset);
 354        if (ret)
 355                return ret;
 356
 357        ret = clk_prepare_enable(tmdev->bus_clk);
 358        if (ret)
 359                goto assert_reset;
 360
 361        ret = clk_set_rate(tmdev->mod_clk, 24000000);
 362        if (ret)
 363                goto bus_disable;
 364
 365        ret = clk_prepare_enable(tmdev->mod_clk);
 366        if (ret)
 367                goto bus_disable;
 368
 369        ret = sun8i_ths_calibrate(tmdev);
 370        if (ret)
 371                goto mod_disable;
 372
 373        return 0;
 374
 375mod_disable:
 376        clk_disable_unprepare(tmdev->mod_clk);
 377bus_disable:
 378        clk_disable_unprepare(tmdev->bus_clk);
 379assert_reset:
 380        reset_control_assert(tmdev->reset);
 381
 382        return ret;
 383}
 384
 385static int sun8i_h3_thermal_init(struct ths_device *tmdev)
 386{
 387        int val;
 388
 389        /* average over 4 samples */
 390        regmap_write(tmdev->regmap, SUN8I_THS_MFC,
 391                     SUN50I_THS_FILTER_EN |
 392                     SUN50I_THS_FILTER_TYPE(1));
 393        /*
 394         * clkin = 24MHz
 395         * filter_samples = 4
 396         * period = 0.25s
 397         *
 398         * x = period * clkin / 4096 / filter_samples - 1
 399         *   = 365
 400         */
 401        val = GENMASK(7 + tmdev->chip->sensor_num, 8);
 402        regmap_write(tmdev->regmap, SUN8I_THS_IC,
 403                     SUN50I_H6_THS_PC_TEMP_PERIOD(365) | val);
 404        /*
 405         * T_acq = 20us
 406         * clkin = 24MHz
 407         *
 408         * x = T_acq * clkin - 1
 409         *   = 479
 410         */
 411        regmap_write(tmdev->regmap, SUN8I_THS_CTRL0,
 412                     SUN8I_THS_CTRL0_T_ACQ0(479));
 413        val = GENMASK(tmdev->chip->sensor_num - 1, 0);
 414        regmap_write(tmdev->regmap, SUN8I_THS_CTRL2,
 415                     SUN8I_THS_CTRL2_T_ACQ1(479) | val);
 416
 417        return 0;
 418}
 419
 420/*
 421 * Without this undocumented value, the returned temperatures would
 422 * be higher than real ones by about 20C.
 423 */
 424#define SUN50I_H6_CTRL0_UNK 0x0000002f
 425
 426static int sun50i_h6_thermal_init(struct ths_device *tmdev)
 427{
 428        int val;
 429
 430        /*
 431         * T_acq = 20us
 432         * clkin = 24MHz
 433         *
 434         * x = T_acq * clkin - 1
 435         *   = 479
 436         */
 437        regmap_write(tmdev->regmap, SUN50I_THS_CTRL0,
 438                     SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479));
 439        /* average over 4 samples */
 440        regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC,
 441                     SUN50I_THS_FILTER_EN |
 442                     SUN50I_THS_FILTER_TYPE(1));
 443        /*
 444         * clkin = 24MHz
 445         * filter_samples = 4
 446         * period = 0.25s
 447         *
 448         * x = period * clkin / 4096 / filter_samples - 1
 449         *   = 365
 450         */
 451        regmap_write(tmdev->regmap, SUN50I_H6_THS_PC,
 452                     SUN50I_H6_THS_PC_TEMP_PERIOD(365));
 453        /* enable sensor */
 454        val = GENMASK(tmdev->chip->sensor_num - 1, 0);
 455        regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val);
 456        /* thermal data interrupt enable */
 457        val = GENMASK(tmdev->chip->sensor_num - 1, 0);
 458        regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val);
 459
 460        return 0;
 461}
 462
 463static int sun8i_ths_register(struct ths_device *tmdev)
 464{
 465        int i;
 466
 467        for (i = 0; i < tmdev->chip->sensor_num; i++) {
 468                tmdev->sensor[i].tmdev = tmdev;
 469                tmdev->sensor[i].id = i;
 470                tmdev->sensor[i].tzd =
 471                        devm_thermal_zone_of_sensor_register(tmdev->dev,
 472                                                             i,
 473                                                             &tmdev->sensor[i],
 474                                                             &ths_ops);
 475                if (IS_ERR(tmdev->sensor[i].tzd))
 476                        return PTR_ERR(tmdev->sensor[i].tzd);
 477
 478                if (devm_thermal_add_hwmon_sysfs(tmdev->sensor[i].tzd))
 479                        dev_warn(tmdev->dev,
 480                                 "Failed to add hwmon sysfs attributes\n");
 481        }
 482
 483        return 0;
 484}
 485
 486static int sun8i_ths_probe(struct platform_device *pdev)
 487{
 488        struct ths_device *tmdev;
 489        struct device *dev = &pdev->dev;
 490        int ret, irq;
 491
 492        tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
 493        if (!tmdev)
 494                return -ENOMEM;
 495
 496        tmdev->dev = dev;
 497        tmdev->chip = of_device_get_match_data(&pdev->dev);
 498        if (!tmdev->chip)
 499                return -EINVAL;
 500
 501        platform_set_drvdata(pdev, tmdev);
 502
 503        ret = sun8i_ths_resource_init(tmdev);
 504        if (ret)
 505                return ret;
 506
 507        irq = platform_get_irq(pdev, 0);
 508        if (irq < 0)
 509                return irq;
 510
 511        ret = tmdev->chip->init(tmdev);
 512        if (ret)
 513                return ret;
 514
 515        ret = sun8i_ths_register(tmdev);
 516        if (ret)
 517                return ret;
 518
 519        /*
 520         * Avoid entering the interrupt handler, the thermal device is not
 521         * registered yet, we deffer the registration of the interrupt to
 522         * the end.
 523         */
 524        ret = devm_request_threaded_irq(dev, irq, NULL,
 525                                        sun8i_irq_thread,
 526                                        IRQF_ONESHOT, "ths", tmdev);
 527        if (ret)
 528                return ret;
 529
 530        return 0;
 531}
 532
 533static int sun8i_ths_remove(struct platform_device *pdev)
 534{
 535        struct ths_device *tmdev = platform_get_drvdata(pdev);
 536
 537        clk_disable_unprepare(tmdev->mod_clk);
 538        clk_disable_unprepare(tmdev->bus_clk);
 539        reset_control_assert(tmdev->reset);
 540
 541        return 0;
 542}
 543
 544static const struct ths_thermal_chip sun8i_a83t_ths = {
 545        .sensor_num = 3,
 546        .scale = 705,
 547        .offset = 191668,
 548        .temp_data_base = SUN8I_THS_TEMP_DATA,
 549        .calibrate = sun8i_h3_ths_calibrate,
 550        .init = sun8i_h3_thermal_init,
 551        .irq_ack = sun8i_h3_irq_ack,
 552        .calc_temp = sun8i_ths_calc_temp,
 553};
 554
 555static const struct ths_thermal_chip sun8i_h3_ths = {
 556        .sensor_num = 1,
 557        .scale = 1211,
 558        .offset = 217000,
 559        .has_mod_clk = true,
 560        .has_bus_clk_reset = true,
 561        .temp_data_base = SUN8I_THS_TEMP_DATA,
 562        .calibrate = sun8i_h3_ths_calibrate,
 563        .init = sun8i_h3_thermal_init,
 564        .irq_ack = sun8i_h3_irq_ack,
 565        .calc_temp = sun8i_ths_calc_temp,
 566};
 567
 568static const struct ths_thermal_chip sun8i_r40_ths = {
 569        .sensor_num = 2,
 570        .offset = 251086,
 571        .scale = 1130,
 572        .has_mod_clk = true,
 573        .has_bus_clk_reset = true,
 574        .temp_data_base = SUN8I_THS_TEMP_DATA,
 575        .calibrate = sun8i_h3_ths_calibrate,
 576        .init = sun8i_h3_thermal_init,
 577        .irq_ack = sun8i_h3_irq_ack,
 578        .calc_temp = sun8i_ths_calc_temp,
 579};
 580
 581static const struct ths_thermal_chip sun50i_a64_ths = {
 582        .sensor_num = 3,
 583        .offset = 260890,
 584        .scale = 1170,
 585        .has_mod_clk = true,
 586        .has_bus_clk_reset = true,
 587        .temp_data_base = SUN8I_THS_TEMP_DATA,
 588        .calibrate = sun8i_h3_ths_calibrate,
 589        .init = sun8i_h3_thermal_init,
 590        .irq_ack = sun8i_h3_irq_ack,
 591        .calc_temp = sun8i_ths_calc_temp,
 592};
 593
 594static const struct ths_thermal_chip sun50i_a100_ths = {
 595        .sensor_num = 3,
 596        .has_bus_clk_reset = true,
 597        .ft_deviation = 8000,
 598        .offset = 187744,
 599        .scale = 672,
 600        .temp_data_base = SUN50I_H6_THS_TEMP_DATA,
 601        .calibrate = sun50i_h6_ths_calibrate,
 602        .init = sun50i_h6_thermal_init,
 603        .irq_ack = sun50i_h6_irq_ack,
 604        .calc_temp = sun8i_ths_calc_temp,
 605};
 606
 607static const struct ths_thermal_chip sun50i_h5_ths = {
 608        .sensor_num = 2,
 609        .has_mod_clk = true,
 610        .has_bus_clk_reset = true,
 611        .temp_data_base = SUN8I_THS_TEMP_DATA,
 612        .calibrate = sun8i_h3_ths_calibrate,
 613        .init = sun8i_h3_thermal_init,
 614        .irq_ack = sun8i_h3_irq_ack,
 615        .calc_temp = sun50i_h5_calc_temp,
 616};
 617
 618static const struct ths_thermal_chip sun50i_h6_ths = {
 619        .sensor_num = 2,
 620        .has_bus_clk_reset = true,
 621        .ft_deviation = 7000,
 622        .offset = 187744,
 623        .scale = 672,
 624        .temp_data_base = SUN50I_H6_THS_TEMP_DATA,
 625        .calibrate = sun50i_h6_ths_calibrate,
 626        .init = sun50i_h6_thermal_init,
 627        .irq_ack = sun50i_h6_irq_ack,
 628        .calc_temp = sun8i_ths_calc_temp,
 629};
 630
 631static const struct of_device_id of_ths_match[] = {
 632        { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
 633        { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
 634        { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
 635        { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
 636        { .compatible = "allwinner,sun50i-a100-ths", .data = &sun50i_a100_ths },
 637        { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
 638        { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
 639        { /* sentinel */ },
 640};
 641MODULE_DEVICE_TABLE(of, of_ths_match);
 642
 643static struct platform_driver ths_driver = {
 644        .probe = sun8i_ths_probe,
 645        .remove = sun8i_ths_remove,
 646        .driver = {
 647                .name = "sun8i-thermal",
 648                .of_match_table = of_ths_match,
 649        },
 650};
 651module_platform_driver(ths_driver);
 652
 653MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
 654MODULE_LICENSE("GPL v2");
 655