Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.16 299 lines 7.2 kB view raw
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");