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