linux/drivers/thermal/intel/intel_bxt_pmic_thermal.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Intel Broxton PMIC thermal driver
   4 *
   5 * Copyright (C) 2016 Intel Corporation. All rights reserved.
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/kernel.h>
  10#include <linux/slab.h>
  11#include <linux/delay.h>
  12#include <linux/interrupt.h>
  13#include <linux/device.h>
  14#include <linux/thermal.h>
  15#include <linux/platform_device.h>
  16#include <linux/sched.h>
  17#include <linux/mfd/intel_soc_pmic.h>
  18
  19#define BXTWC_THRM0IRQ          0x4E04
  20#define BXTWC_THRM1IRQ          0x4E05
  21#define BXTWC_THRM2IRQ          0x4E06
  22#define BXTWC_MTHRM0IRQ         0x4E12
  23#define BXTWC_MTHRM1IRQ         0x4E13
  24#define BXTWC_MTHRM2IRQ         0x4E14
  25#define BXTWC_STHRM0IRQ         0x4F19
  26#define BXTWC_STHRM1IRQ         0x4F1A
  27#define BXTWC_STHRM2IRQ         0x4F1B
  28
  29struct trip_config_map {
  30        u16 irq_reg;
  31        u16 irq_en;
  32        u16 evt_stat;
  33        u8 irq_mask;
  34        u8 irq_en_mask;
  35        u8 evt_mask;
  36        u8 trip_num;
  37};
  38
  39struct thermal_irq_map {
  40        char handle[20];
  41        int num_trips;
  42        const struct trip_config_map *trip_config;
  43};
  44
  45struct pmic_thermal_data {
  46        const struct thermal_irq_map *maps;
  47        int num_maps;
  48};
  49
  50static const struct trip_config_map bxtwc_str0_trip_config[] = {
  51        {
  52                .irq_reg = BXTWC_THRM0IRQ,
  53                .irq_mask = 0x01,
  54                .irq_en = BXTWC_MTHRM0IRQ,
  55                .irq_en_mask = 0x01,
  56                .evt_stat = BXTWC_STHRM0IRQ,
  57                .evt_mask = 0x01,
  58                .trip_num = 0
  59        },
  60        {
  61                .irq_reg = BXTWC_THRM0IRQ,
  62                .irq_mask = 0x10,
  63                .irq_en = BXTWC_MTHRM0IRQ,
  64                .irq_en_mask = 0x10,
  65                .evt_stat = BXTWC_STHRM0IRQ,
  66                .evt_mask = 0x10,
  67                .trip_num = 1
  68        }
  69};
  70
  71static const struct trip_config_map bxtwc_str1_trip_config[] = {
  72        {
  73                .irq_reg = BXTWC_THRM0IRQ,
  74                .irq_mask = 0x02,
  75                .irq_en = BXTWC_MTHRM0IRQ,
  76                .irq_en_mask = 0x02,
  77                .evt_stat = BXTWC_STHRM0IRQ,
  78                .evt_mask = 0x02,
  79                .trip_num = 0
  80        },
  81        {
  82                .irq_reg = BXTWC_THRM0IRQ,
  83                .irq_mask = 0x20,
  84                .irq_en = BXTWC_MTHRM0IRQ,
  85                .irq_en_mask = 0x20,
  86                .evt_stat = BXTWC_STHRM0IRQ,
  87                .evt_mask = 0x20,
  88                .trip_num = 1
  89        },
  90};
  91
  92static const struct trip_config_map bxtwc_str2_trip_config[] = {
  93        {
  94                .irq_reg = BXTWC_THRM0IRQ,
  95                .irq_mask = 0x04,
  96                .irq_en = BXTWC_MTHRM0IRQ,
  97                .irq_en_mask = 0x04,
  98                .evt_stat = BXTWC_STHRM0IRQ,
  99                .evt_mask = 0x04,
 100                .trip_num = 0
 101        },
 102        {
 103                .irq_reg = BXTWC_THRM0IRQ,
 104                .irq_mask = 0x40,
 105                .irq_en = BXTWC_MTHRM0IRQ,
 106                .irq_en_mask = 0x40,
 107                .evt_stat = BXTWC_STHRM0IRQ,
 108                .evt_mask = 0x40,
 109                .trip_num = 1
 110        },
 111};
 112
 113static const struct trip_config_map bxtwc_str3_trip_config[] = {
 114        {
 115                .irq_reg = BXTWC_THRM2IRQ,
 116                .irq_mask = 0x10,
 117                .irq_en = BXTWC_MTHRM2IRQ,
 118                .irq_en_mask = 0x10,
 119                .evt_stat = BXTWC_STHRM2IRQ,
 120                .evt_mask = 0x10,
 121                .trip_num = 0
 122        },
 123};
 124
 125static const struct thermal_irq_map bxtwc_thermal_irq_map[] = {
 126        {
 127                .handle = "STR0",
 128                .trip_config = bxtwc_str0_trip_config,
 129                .num_trips = ARRAY_SIZE(bxtwc_str0_trip_config),
 130        },
 131        {
 132                .handle = "STR1",
 133                .trip_config = bxtwc_str1_trip_config,
 134                .num_trips = ARRAY_SIZE(bxtwc_str1_trip_config),
 135        },
 136        {
 137                .handle = "STR2",
 138                .trip_config = bxtwc_str2_trip_config,
 139                .num_trips = ARRAY_SIZE(bxtwc_str2_trip_config),
 140        },
 141        {
 142                .handle = "STR3",
 143                .trip_config = bxtwc_str3_trip_config,
 144                .num_trips = ARRAY_SIZE(bxtwc_str3_trip_config),
 145        },
 146};
 147
 148static const struct pmic_thermal_data bxtwc_thermal_data = {
 149        .maps = bxtwc_thermal_irq_map,
 150        .num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map),
 151};
 152
 153static irqreturn_t pmic_thermal_irq_handler(int irq, void *data)
 154{
 155        struct platform_device *pdev = data;
 156        struct thermal_zone_device *tzd;
 157        struct pmic_thermal_data *td;
 158        struct intel_soc_pmic *pmic;
 159        struct regmap *regmap;
 160        u8 reg_val, mask, irq_stat;
 161        u16 reg, evt_stat_reg;
 162        int i, j, ret;
 163
 164        pmic = dev_get_drvdata(pdev->dev.parent);
 165        regmap = pmic->regmap;
 166        td = (struct pmic_thermal_data *)
 167                platform_get_device_id(pdev)->driver_data;
 168
 169        /* Resolve thermal irqs */
 170        for (i = 0; i < td->num_maps; i++) {
 171                for (j = 0; j < td->maps[i].num_trips; j++) {
 172                        reg = td->maps[i].trip_config[j].irq_reg;
 173                        mask = td->maps[i].trip_config[j].irq_mask;
 174                        /*
 175                         * Read the irq register to resolve whether the
 176                         * interrupt was triggered for this sensor
 177                         */
 178                        if (regmap_read(regmap, reg, &ret))
 179                                return IRQ_HANDLED;
 180
 181                        reg_val = (u8)ret;
 182                        irq_stat = ((u8)ret & mask);
 183
 184                        if (!irq_stat)
 185                                continue;
 186
 187                        /*
 188                         * Read the status register to find out what
 189                         * event occurred i.e a high or a low
 190                         */
 191                        evt_stat_reg = td->maps[i].trip_config[j].evt_stat;
 192                        if (regmap_read(regmap, evt_stat_reg, &ret))
 193                                return IRQ_HANDLED;
 194
 195                        tzd = thermal_zone_get_zone_by_name(td->maps[i].handle);
 196                        if (!IS_ERR(tzd))
 197                                thermal_zone_device_update(tzd,
 198                                                THERMAL_EVENT_UNSPECIFIED);
 199
 200                        /* Clear the appropriate irq */
 201                        regmap_write(regmap, reg, reg_val & mask);
 202                }
 203        }
 204
 205        return IRQ_HANDLED;
 206}
 207
 208static int pmic_thermal_probe(struct platform_device *pdev)
 209{
 210        struct regmap_irq_chip_data *regmap_irq_chip;
 211        struct pmic_thermal_data *thermal_data;
 212        int ret, irq, virq, i, j, pmic_irq_count;
 213        struct intel_soc_pmic *pmic;
 214        struct regmap *regmap;
 215        struct device *dev;
 216        u16 reg;
 217        u8 mask;
 218
 219        dev = &pdev->dev;
 220        pmic = dev_get_drvdata(pdev->dev.parent);
 221        if (!pmic) {
 222                dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n");
 223                return -ENODEV;
 224        }
 225
 226        thermal_data = (struct pmic_thermal_data *)
 227                                platform_get_device_id(pdev)->driver_data;
 228        if (!thermal_data) {
 229                dev_err(dev, "No thermal data initialized!!\n");
 230                return -ENODEV;
 231        }
 232
 233        regmap = pmic->regmap;
 234        regmap_irq_chip = pmic->irq_chip_data;
 235
 236        pmic_irq_count = 0;
 237        while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) {
 238                virq = regmap_irq_get_virq(regmap_irq_chip, irq);
 239                if (virq < 0) {
 240                        dev_err(dev, "failed to get virq by irq %d\n", irq);
 241                        return virq;
 242                }
 243
 244                ret = devm_request_threaded_irq(&pdev->dev, virq,
 245                                NULL, pmic_thermal_irq_handler,
 246                                IRQF_ONESHOT, "pmic_thermal", pdev);
 247
 248                if (ret) {
 249                        dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
 250                        return ret;
 251                }
 252                pmic_irq_count++;
 253        }
 254
 255        /* Enable thermal interrupts */
 256        for (i = 0; i < thermal_data->num_maps; i++) {
 257                for (j = 0; j < thermal_data->maps[i].num_trips; j++) {
 258                        reg = thermal_data->maps[i].trip_config[j].irq_en;
 259                        mask = thermal_data->maps[i].trip_config[j].irq_en_mask;
 260                        ret = regmap_update_bits(regmap, reg, mask, 0x00);
 261                        if (ret)
 262                                return ret;
 263                }
 264        }
 265
 266        return 0;
 267}
 268
 269static const struct platform_device_id pmic_thermal_id_table[] = {
 270        {
 271                .name = "bxt_wcove_thermal",
 272                .driver_data = (kernel_ulong_t)&bxtwc_thermal_data,
 273        },
 274        {},
 275};
 276
 277static struct platform_driver pmic_thermal_driver = {
 278        .probe = pmic_thermal_probe,
 279        .driver = {
 280                .name = "pmic_thermal",
 281        },
 282        .id_table = pmic_thermal_id_table,
 283};
 284
 285MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
 286module_platform_driver(pmic_thermal_driver);
 287
 288MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
 289MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
 290MODULE_LICENSE("GPL v2");
 291