0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/module.h>
0009 #include <linux/kernel.h>
0010 #include <linux/slab.h>
0011 #include <linux/delay.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/device.h>
0014 #include <linux/thermal.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/sched.h>
0017 #include <linux/mfd/intel_soc_pmic.h>
0018
0019 #define BXTWC_THRM0IRQ 0x4E04
0020 #define BXTWC_THRM1IRQ 0x4E05
0021 #define BXTWC_THRM2IRQ 0x4E06
0022 #define BXTWC_MTHRM0IRQ 0x4E12
0023 #define BXTWC_MTHRM1IRQ 0x4E13
0024 #define BXTWC_MTHRM2IRQ 0x4E14
0025 #define BXTWC_STHRM0IRQ 0x4F19
0026 #define BXTWC_STHRM1IRQ 0x4F1A
0027 #define BXTWC_STHRM2IRQ 0x4F1B
0028
0029 struct trip_config_map {
0030 u16 irq_reg;
0031 u16 irq_en;
0032 u16 evt_stat;
0033 u8 irq_mask;
0034 u8 irq_en_mask;
0035 u8 evt_mask;
0036 u8 trip_num;
0037 };
0038
0039 struct thermal_irq_map {
0040 char handle[20];
0041 int num_trips;
0042 const struct trip_config_map *trip_config;
0043 };
0044
0045 struct pmic_thermal_data {
0046 const struct thermal_irq_map *maps;
0047 int num_maps;
0048 };
0049
0050 static const struct trip_config_map bxtwc_str0_trip_config[] = {
0051 {
0052 .irq_reg = BXTWC_THRM0IRQ,
0053 .irq_mask = 0x01,
0054 .irq_en = BXTWC_MTHRM0IRQ,
0055 .irq_en_mask = 0x01,
0056 .evt_stat = BXTWC_STHRM0IRQ,
0057 .evt_mask = 0x01,
0058 .trip_num = 0
0059 },
0060 {
0061 .irq_reg = BXTWC_THRM0IRQ,
0062 .irq_mask = 0x10,
0063 .irq_en = BXTWC_MTHRM0IRQ,
0064 .irq_en_mask = 0x10,
0065 .evt_stat = BXTWC_STHRM0IRQ,
0066 .evt_mask = 0x10,
0067 .trip_num = 1
0068 }
0069 };
0070
0071 static const struct trip_config_map bxtwc_str1_trip_config[] = {
0072 {
0073 .irq_reg = BXTWC_THRM0IRQ,
0074 .irq_mask = 0x02,
0075 .irq_en = BXTWC_MTHRM0IRQ,
0076 .irq_en_mask = 0x02,
0077 .evt_stat = BXTWC_STHRM0IRQ,
0078 .evt_mask = 0x02,
0079 .trip_num = 0
0080 },
0081 {
0082 .irq_reg = BXTWC_THRM0IRQ,
0083 .irq_mask = 0x20,
0084 .irq_en = BXTWC_MTHRM0IRQ,
0085 .irq_en_mask = 0x20,
0086 .evt_stat = BXTWC_STHRM0IRQ,
0087 .evt_mask = 0x20,
0088 .trip_num = 1
0089 },
0090 };
0091
0092 static const struct trip_config_map bxtwc_str2_trip_config[] = {
0093 {
0094 .irq_reg = BXTWC_THRM0IRQ,
0095 .irq_mask = 0x04,
0096 .irq_en = BXTWC_MTHRM0IRQ,
0097 .irq_en_mask = 0x04,
0098 .evt_stat = BXTWC_STHRM0IRQ,
0099 .evt_mask = 0x04,
0100 .trip_num = 0
0101 },
0102 {
0103 .irq_reg = BXTWC_THRM0IRQ,
0104 .irq_mask = 0x40,
0105 .irq_en = BXTWC_MTHRM0IRQ,
0106 .irq_en_mask = 0x40,
0107 .evt_stat = BXTWC_STHRM0IRQ,
0108 .evt_mask = 0x40,
0109 .trip_num = 1
0110 },
0111 };
0112
0113 static const struct trip_config_map bxtwc_str3_trip_config[] = {
0114 {
0115 .irq_reg = BXTWC_THRM2IRQ,
0116 .irq_mask = 0x10,
0117 .irq_en = BXTWC_MTHRM2IRQ,
0118 .irq_en_mask = 0x10,
0119 .evt_stat = BXTWC_STHRM2IRQ,
0120 .evt_mask = 0x10,
0121 .trip_num = 0
0122 },
0123 };
0124
0125 static const struct thermal_irq_map bxtwc_thermal_irq_map[] = {
0126 {
0127 .handle = "STR0",
0128 .trip_config = bxtwc_str0_trip_config,
0129 .num_trips = ARRAY_SIZE(bxtwc_str0_trip_config),
0130 },
0131 {
0132 .handle = "STR1",
0133 .trip_config = bxtwc_str1_trip_config,
0134 .num_trips = ARRAY_SIZE(bxtwc_str1_trip_config),
0135 },
0136 {
0137 .handle = "STR2",
0138 .trip_config = bxtwc_str2_trip_config,
0139 .num_trips = ARRAY_SIZE(bxtwc_str2_trip_config),
0140 },
0141 {
0142 .handle = "STR3",
0143 .trip_config = bxtwc_str3_trip_config,
0144 .num_trips = ARRAY_SIZE(bxtwc_str3_trip_config),
0145 },
0146 };
0147
0148 static const struct pmic_thermal_data bxtwc_thermal_data = {
0149 .maps = bxtwc_thermal_irq_map,
0150 .num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map),
0151 };
0152
0153 static irqreturn_t pmic_thermal_irq_handler(int irq, void *data)
0154 {
0155 struct platform_device *pdev = data;
0156 struct thermal_zone_device *tzd;
0157 struct pmic_thermal_data *td;
0158 struct intel_soc_pmic *pmic;
0159 struct regmap *regmap;
0160 u8 reg_val, mask, irq_stat;
0161 u16 reg, evt_stat_reg;
0162 int i, j, ret;
0163
0164 pmic = dev_get_drvdata(pdev->dev.parent);
0165 regmap = pmic->regmap;
0166 td = (struct pmic_thermal_data *)
0167 platform_get_device_id(pdev)->driver_data;
0168
0169
0170 for (i = 0; i < td->num_maps; i++) {
0171 for (j = 0; j < td->maps[i].num_trips; j++) {
0172 reg = td->maps[i].trip_config[j].irq_reg;
0173 mask = td->maps[i].trip_config[j].irq_mask;
0174
0175
0176
0177
0178 if (regmap_read(regmap, reg, &ret))
0179 return IRQ_HANDLED;
0180
0181 reg_val = (u8)ret;
0182 irq_stat = ((u8)ret & mask);
0183
0184 if (!irq_stat)
0185 continue;
0186
0187
0188
0189
0190
0191 evt_stat_reg = td->maps[i].trip_config[j].evt_stat;
0192 if (regmap_read(regmap, evt_stat_reg, &ret))
0193 return IRQ_HANDLED;
0194
0195 tzd = thermal_zone_get_zone_by_name(td->maps[i].handle);
0196 if (!IS_ERR(tzd))
0197 thermal_zone_device_update(tzd,
0198 THERMAL_EVENT_UNSPECIFIED);
0199
0200
0201 regmap_write(regmap, reg, reg_val & mask);
0202 }
0203 }
0204
0205 return IRQ_HANDLED;
0206 }
0207
0208 static int pmic_thermal_probe(struct platform_device *pdev)
0209 {
0210 struct regmap_irq_chip_data *regmap_irq_chip;
0211 struct pmic_thermal_data *thermal_data;
0212 int ret, irq, virq, i, j, pmic_irq_count;
0213 struct intel_soc_pmic *pmic;
0214 struct regmap *regmap;
0215 struct device *dev;
0216 u16 reg;
0217 u8 mask;
0218
0219 dev = &pdev->dev;
0220 pmic = dev_get_drvdata(pdev->dev.parent);
0221 if (!pmic) {
0222 dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n");
0223 return -ENODEV;
0224 }
0225
0226 thermal_data = (struct pmic_thermal_data *)
0227 platform_get_device_id(pdev)->driver_data;
0228 if (!thermal_data) {
0229 dev_err(dev, "No thermal data initialized!!\n");
0230 return -ENODEV;
0231 }
0232
0233 regmap = pmic->regmap;
0234 regmap_irq_chip = pmic->irq_chip_data;
0235
0236 pmic_irq_count = 0;
0237 while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) {
0238 virq = regmap_irq_get_virq(regmap_irq_chip, irq);
0239 if (virq < 0) {
0240 dev_err(dev, "failed to get virq by irq %d\n", irq);
0241 return virq;
0242 }
0243
0244 ret = devm_request_threaded_irq(&pdev->dev, virq,
0245 NULL, pmic_thermal_irq_handler,
0246 IRQF_ONESHOT, "pmic_thermal", pdev);
0247
0248 if (ret) {
0249 dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
0250 return ret;
0251 }
0252 pmic_irq_count++;
0253 }
0254
0255
0256 for (i = 0; i < thermal_data->num_maps; i++) {
0257 for (j = 0; j < thermal_data->maps[i].num_trips; j++) {
0258 reg = thermal_data->maps[i].trip_config[j].irq_en;
0259 mask = thermal_data->maps[i].trip_config[j].irq_en_mask;
0260 ret = regmap_update_bits(regmap, reg, mask, 0x00);
0261 if (ret)
0262 return ret;
0263 }
0264 }
0265
0266 return 0;
0267 }
0268
0269 static const struct platform_device_id pmic_thermal_id_table[] = {
0270 {
0271 .name = "bxt_wcove_thermal",
0272 .driver_data = (kernel_ulong_t)&bxtwc_thermal_data,
0273 },
0274 {},
0275 };
0276
0277 static struct platform_driver pmic_thermal_driver = {
0278 .probe = pmic_thermal_probe,
0279 .driver = {
0280 .name = "pmic_thermal",
0281 },
0282 .id_table = pmic_thermal_id_table,
0283 };
0284
0285 MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
0286 module_platform_driver(pmic_thermal_driver);
0287
0288 MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
0289 MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
0290 MODULE_LICENSE("GPL v2");