linux/drivers/thermal/intel/intel_pch_thermal.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* intel_pch_thermal.c - Intel PCH Thermal driver
   3 *
   4 * Copyright (c) 2015, Intel Corporation.
   5 *
   6 * Authors:
   7 *     Tushar Dave <tushar.n.dave@intel.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/types.h>
  12#include <linux/init.h>
  13#include <linux/pci.h>
  14#include <linux/acpi.h>
  15#include <linux/thermal.h>
  16#include <linux/pm.h>
  17
  18/* Intel PCH thermal Device IDs */
  19#define PCH_THERMAL_DID_HSW_1   0x9C24 /* Haswell PCH */
  20#define PCH_THERMAL_DID_HSW_2   0x8C24 /* Haswell PCH */
  21#define PCH_THERMAL_DID_WPT     0x9CA4 /* Wildcat Point */
  22#define PCH_THERMAL_DID_SKL     0x9D31 /* Skylake PCH */
  23#define PCH_THERMAL_DID_SKL_H   0xA131 /* Skylake PCH 100 series */
  24#define PCH_THERMAL_DID_CNL     0x9Df9 /* CNL PCH */
  25#define PCH_THERMAL_DID_CNL_H   0xA379 /* CNL-H PCH */
  26#define PCH_THERMAL_DID_CML_H   0X06F9 /* CML-H PCH */
  27
  28/* Wildcat Point-LP  PCH Thermal registers */
  29#define WPT_TEMP        0x0000  /* Temperature */
  30#define WPT_TSC 0x04    /* Thermal Sensor Control */
  31#define WPT_TSS 0x06    /* Thermal Sensor Status */
  32#define WPT_TSEL        0x08    /* Thermal Sensor Enable and Lock */
  33#define WPT_TSREL       0x0A    /* Thermal Sensor Report Enable and Lock */
  34#define WPT_TSMIC       0x0C    /* Thermal Sensor SMI Control */
  35#define WPT_CTT 0x0010  /* Catastrophic Trip Point */
  36#define WPT_TAHV        0x0014  /* Thermal Alert High Value */
  37#define WPT_TALV        0x0018  /* Thermal Alert Low Value */
  38#define WPT_TL          0x00000040      /* Throttle Value */
  39#define WPT_PHL 0x0060  /* PCH Hot Level */
  40#define WPT_PHLC        0x62    /* PHL Control */
  41#define WPT_TAS 0x80    /* Thermal Alert Status */
  42#define WPT_TSPIEN      0x82    /* PCI Interrupt Event Enables */
  43#define WPT_TSGPEN      0x84    /* General Purpose Event Enables */
  44
  45/*  Wildcat Point-LP  PCH Thermal Register bit definitions */
  46#define WPT_TEMP_TSR    0x01ff  /* Temp TS Reading */
  47#define WPT_TSC_CPDE    0x01    /* Catastrophic Power-Down Enable */
  48#define WPT_TSS_TSDSS   0x10    /* Thermal Sensor Dynamic Shutdown Status */
  49#define WPT_TSS_GPES    0x08    /* GPE status */
  50#define WPT_TSEL_ETS    0x01    /* Enable TS */
  51#define WPT_TSEL_PLDB   0x80    /* TSEL Policy Lock-Down Bit */
  52#define WPT_TL_TOL      0x000001FF      /* T0 Level */
  53#define WPT_TL_T1L      0x1ff00000      /* T1 Level */
  54#define WPT_TL_TTEN     0x20000000      /* TT Enable */
  55
  56static char driver_name[] = "Intel PCH thermal driver";
  57
  58struct pch_thermal_device {
  59        void __iomem *hw_base;
  60        const struct pch_dev_ops *ops;
  61        struct pci_dev *pdev;
  62        struct thermal_zone_device *tzd;
  63        int crt_trip_id;
  64        unsigned long crt_temp;
  65        int hot_trip_id;
  66        unsigned long hot_temp;
  67        int psv_trip_id;
  68        unsigned long psv_temp;
  69        bool bios_enabled;
  70};
  71
  72#ifdef CONFIG_ACPI
  73
  74/*
  75 * On some platforms, there is a companion ACPI device, which adds
  76 * passive trip temperature using _PSV method. There is no specific
  77 * passive temperature setting in MMIO interface of this PCI device.
  78 */
  79static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
  80                                      int *nr_trips)
  81{
  82        struct acpi_device *adev;
  83
  84        ptd->psv_trip_id = -1;
  85
  86        adev = ACPI_COMPANION(&ptd->pdev->dev);
  87        if (adev) {
  88                unsigned long long r;
  89                acpi_status status;
  90
  91                status = acpi_evaluate_integer(adev->handle, "_PSV", NULL,
  92                                               &r);
  93                if (ACPI_SUCCESS(status)) {
  94                        unsigned long trip_temp;
  95
  96                        trip_temp = DECI_KELVIN_TO_MILLICELSIUS(r);
  97                        if (trip_temp) {
  98                                ptd->psv_temp = trip_temp;
  99                                ptd->psv_trip_id = *nr_trips;
 100                                ++(*nr_trips);
 101                        }
 102                }
 103        }
 104}
 105#else
 106static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
 107                                      int *nr_trips)
 108{
 109        ptd->psv_trip_id = -1;
 110
 111}
 112#endif
 113
 114static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
 115{
 116        u8 tsel;
 117        u16 trip_temp;
 118
 119        *nr_trips = 0;
 120
 121        /* Check if BIOS has already enabled thermal sensor */
 122        if (WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL)) {
 123                ptd->bios_enabled = true;
 124                goto read_trips;
 125        }
 126
 127        tsel = readb(ptd->hw_base + WPT_TSEL);
 128        /*
 129         * When TSEL's Policy Lock-Down bit is 1, TSEL become RO.
 130         * If so, thermal sensor cannot enable. Bail out.
 131         */
 132        if (tsel & WPT_TSEL_PLDB) {
 133                dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
 134                return -ENODEV;
 135        }
 136
 137        writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
 138        if (!(WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL))) {
 139                dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
 140                return -ENODEV;
 141        }
 142
 143read_trips:
 144        ptd->crt_trip_id = -1;
 145        trip_temp = readw(ptd->hw_base + WPT_CTT);
 146        trip_temp &= 0x1FF;
 147        if (trip_temp) {
 148                /* Resolution of 1/2 degree C and an offset of -50C */
 149                ptd->crt_temp = trip_temp * 1000 / 2 - 50000;
 150                ptd->crt_trip_id = 0;
 151                ++(*nr_trips);
 152        }
 153
 154        ptd->hot_trip_id = -1;
 155        trip_temp = readw(ptd->hw_base + WPT_PHL);
 156        trip_temp &= 0x1FF;
 157        if (trip_temp) {
 158                /* Resolution of 1/2 degree C and an offset of -50C */
 159                ptd->hot_temp = trip_temp * 1000 / 2 - 50000;
 160                ptd->hot_trip_id = *nr_trips;
 161                ++(*nr_trips);
 162        }
 163
 164        pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
 165
 166        return 0;
 167}
 168
 169static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
 170{
 171        u16 wpt_temp;
 172
 173        wpt_temp = WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP);
 174
 175        /* Resolution of 1/2 degree C and an offset of -50C */
 176        *temp = (wpt_temp * 1000 / 2 - 50000);
 177
 178        return 0;
 179}
 180
 181static int pch_wpt_suspend(struct pch_thermal_device *ptd)
 182{
 183        u8 tsel;
 184
 185        if (ptd->bios_enabled)
 186                return 0;
 187
 188        tsel = readb(ptd->hw_base + WPT_TSEL);
 189
 190        writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL);
 191
 192        return 0;
 193}
 194
 195static int pch_wpt_resume(struct pch_thermal_device *ptd)
 196{
 197        u8 tsel;
 198
 199        if (ptd->bios_enabled)
 200                return 0;
 201
 202        tsel = readb(ptd->hw_base + WPT_TSEL);
 203
 204        writeb(tsel | WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
 205
 206        return 0;
 207}
 208
 209struct pch_dev_ops {
 210        int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
 211        int (*get_temp)(struct pch_thermal_device *ptd, int *temp);
 212        int (*suspend)(struct pch_thermal_device *ptd);
 213        int (*resume)(struct pch_thermal_device *ptd);
 214};
 215
 216
 217/* dev ops for Wildcat Point */
 218static const struct pch_dev_ops pch_dev_ops_wpt = {
 219        .hw_init = pch_wpt_init,
 220        .get_temp = pch_wpt_get_temp,
 221        .suspend = pch_wpt_suspend,
 222        .resume = pch_wpt_resume,
 223};
 224
 225static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
 226{
 227        struct pch_thermal_device *ptd = tzd->devdata;
 228
 229        return  ptd->ops->get_temp(ptd, temp);
 230}
 231
 232static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
 233                             enum thermal_trip_type *type)
 234{
 235        struct pch_thermal_device *ptd = tzd->devdata;
 236
 237        if (ptd->crt_trip_id == trip)
 238                *type = THERMAL_TRIP_CRITICAL;
 239        else if (ptd->hot_trip_id == trip)
 240                *type = THERMAL_TRIP_HOT;
 241        else if (ptd->psv_trip_id == trip)
 242                *type = THERMAL_TRIP_PASSIVE;
 243        else
 244                return -EINVAL;
 245
 246        return 0;
 247}
 248
 249static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp)
 250{
 251        struct pch_thermal_device *ptd = tzd->devdata;
 252
 253        if (ptd->crt_trip_id == trip)
 254                *temp = ptd->crt_temp;
 255        else if (ptd->hot_trip_id == trip)
 256                *temp = ptd->hot_temp;
 257        else if (ptd->psv_trip_id == trip)
 258                *temp = ptd->psv_temp;
 259        else
 260                return -EINVAL;
 261
 262        return 0;
 263}
 264
 265static struct thermal_zone_device_ops tzd_ops = {
 266        .get_temp = pch_thermal_get_temp,
 267        .get_trip_type = pch_get_trip_type,
 268        .get_trip_temp = pch_get_trip_temp,
 269};
 270
 271enum board_ids {
 272        board_hsw,
 273        board_wpt,
 274        board_skl,
 275        board_cnl,
 276        board_cml,
 277};
 278
 279static const struct board_info {
 280        const char *name;
 281        const struct pch_dev_ops *ops;
 282} board_info[] = {
 283        [board_hsw] = {
 284                .name = "pch_haswell",
 285                .ops = &pch_dev_ops_wpt,
 286        },
 287        [board_wpt] = {
 288                .name = "pch_wildcat_point",
 289                .ops = &pch_dev_ops_wpt,
 290        },
 291        [board_skl] = {
 292                .name = "pch_skylake",
 293                .ops = &pch_dev_ops_wpt,
 294        },
 295        [board_cnl] = {
 296                .name = "pch_cannonlake",
 297                .ops = &pch_dev_ops_wpt,
 298        },
 299        [board_cml] = {
 300                .name = "pch_cometlake",
 301                .ops = &pch_dev_ops_wpt,
 302        }
 303};
 304
 305static int intel_pch_thermal_probe(struct pci_dev *pdev,
 306                                   const struct pci_device_id *id)
 307{
 308        enum board_ids board_id = id->driver_data;
 309        const struct board_info *bi = &board_info[board_id];
 310        struct pch_thermal_device *ptd;
 311        int err;
 312        int nr_trips;
 313
 314        ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
 315        if (!ptd)
 316                return -ENOMEM;
 317
 318        ptd->ops = bi->ops;
 319
 320        pci_set_drvdata(pdev, ptd);
 321        ptd->pdev = pdev;
 322
 323        err = pci_enable_device(pdev);
 324        if (err) {
 325                dev_err(&pdev->dev, "failed to enable pci device\n");
 326                return err;
 327        }
 328
 329        err = pci_request_regions(pdev, driver_name);
 330        if (err) {
 331                dev_err(&pdev->dev, "failed to request pci region\n");
 332                goto error_disable;
 333        }
 334
 335        ptd->hw_base = pci_ioremap_bar(pdev, 0);
 336        if (!ptd->hw_base) {
 337                err = -ENOMEM;
 338                dev_err(&pdev->dev, "failed to map mem base\n");
 339                goto error_release;
 340        }
 341
 342        err = ptd->ops->hw_init(ptd, &nr_trips);
 343        if (err)
 344                goto error_cleanup;
 345
 346        ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd,
 347                                                &tzd_ops, NULL, 0, 0);
 348        if (IS_ERR(ptd->tzd)) {
 349                dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
 350                        bi->name);
 351                err = PTR_ERR(ptd->tzd);
 352                goto error_cleanup;
 353        }
 354        err = thermal_zone_device_enable(ptd->tzd);
 355        if (err)
 356                goto err_unregister;
 357
 358        return 0;
 359
 360err_unregister:
 361        thermal_zone_device_unregister(ptd->tzd);
 362error_cleanup:
 363        iounmap(ptd->hw_base);
 364error_release:
 365        pci_release_regions(pdev);
 366error_disable:
 367        pci_disable_device(pdev);
 368        dev_err(&pdev->dev, "pci device failed to probe\n");
 369        return err;
 370}
 371
 372static void intel_pch_thermal_remove(struct pci_dev *pdev)
 373{
 374        struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
 375
 376        thermal_zone_device_unregister(ptd->tzd);
 377        iounmap(ptd->hw_base);
 378        pci_set_drvdata(pdev, NULL);
 379        pci_release_regions(pdev);
 380        pci_disable_device(pdev);
 381}
 382
 383static int intel_pch_thermal_suspend(struct device *device)
 384{
 385        struct pch_thermal_device *ptd = dev_get_drvdata(device);
 386
 387        return ptd->ops->suspend(ptd);
 388}
 389
 390static int intel_pch_thermal_resume(struct device *device)
 391{
 392        struct pch_thermal_device *ptd = dev_get_drvdata(device);
 393
 394        return ptd->ops->resume(ptd);
 395}
 396
 397static const struct pci_device_id intel_pch_thermal_id[] = {
 398        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1),
 399                .driver_data = board_hsw, },
 400        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2),
 401                .driver_data = board_hsw, },
 402        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT),
 403                .driver_data = board_wpt, },
 404        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL),
 405                .driver_data = board_skl, },
 406        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H),
 407                .driver_data = board_skl, },
 408        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL),
 409                .driver_data = board_cnl, },
 410        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H),
 411                .driver_data = board_cnl, },
 412        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H),
 413                .driver_data = board_cml, },
 414        { 0, },
 415};
 416MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
 417
 418static const struct dev_pm_ops intel_pch_pm_ops = {
 419        .suspend = intel_pch_thermal_suspend,
 420        .resume = intel_pch_thermal_resume,
 421};
 422
 423static struct pci_driver intel_pch_thermal_driver = {
 424        .name           = "intel_pch_thermal",
 425        .id_table       = intel_pch_thermal_id,
 426        .probe          = intel_pch_thermal_probe,
 427        .remove         = intel_pch_thermal_remove,
 428        .driver.pm      = &intel_pch_pm_ops,
 429};
 430
 431module_pci_driver(intel_pch_thermal_driver);
 432
 433MODULE_LICENSE("GPL v2");
 434MODULE_DESCRIPTION("Intel PCH Thermal driver");
 435