root/drivers/thermal/intel/intel_bxt_pmic_thermal.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pmic_thermal_irq_handler
  2. pmic_thermal_probe

   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 
  29 struct 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 
  39 struct thermal_irq_map {
  40         char handle[20];
  41         int num_trips;
  42         const struct trip_config_map *trip_config;
  43 };
  44 
  45 struct pmic_thermal_data {
  46         const struct thermal_irq_map *maps;
  47         int num_maps;
  48 };
  49 
  50 static 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 
  71 static 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 
  92 static 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 
 113 static 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 
 125 static 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 
 148 static 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 
 153 static 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 
 208 static 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 
 269 static 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 
 277 static 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 
 285 MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
 286 module_platform_driver(pmic_thermal_driver);
 287 
 288 MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
 289 MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
 290 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */