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