13e8c4d31SAmit Kucheria /* 23e8c4d31SAmit Kucheria * Intel Broxton PMIC thermal driver 33e8c4d31SAmit Kucheria * 43e8c4d31SAmit Kucheria * Copyright (C) 2016 Intel Corporation. All rights reserved. 53e8c4d31SAmit Kucheria * 63e8c4d31SAmit Kucheria * This program is free software; you can redistribute it and/or 73e8c4d31SAmit Kucheria * modify it under the terms of the GNU General Public License version 83e8c4d31SAmit Kucheria * 2 as published by the Free Software Foundation. 93e8c4d31SAmit Kucheria * 103e8c4d31SAmit Kucheria * This program is distributed in the hope that it will be useful, 113e8c4d31SAmit Kucheria * but WITHOUT ANY WARRANTY; without even the implied warranty of 123e8c4d31SAmit Kucheria * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 133e8c4d31SAmit Kucheria * GNU General Public License for more details. 143e8c4d31SAmit Kucheria * 153e8c4d31SAmit Kucheria */ 163e8c4d31SAmit Kucheria 173e8c4d31SAmit Kucheria #include <linux/module.h> 183e8c4d31SAmit Kucheria #include <linux/kernel.h> 193e8c4d31SAmit Kucheria #include <linux/slab.h> 203e8c4d31SAmit Kucheria #include <linux/delay.h> 213e8c4d31SAmit Kucheria #include <linux/interrupt.h> 223e8c4d31SAmit Kucheria #include <linux/device.h> 233e8c4d31SAmit Kucheria #include <linux/thermal.h> 243e8c4d31SAmit Kucheria #include <linux/platform_device.h> 253e8c4d31SAmit Kucheria #include <linux/sched.h> 263e8c4d31SAmit Kucheria #include <linux/mfd/intel_soc_pmic.h> 273e8c4d31SAmit Kucheria 283e8c4d31SAmit Kucheria #define BXTWC_THRM0IRQ 0x4E04 293e8c4d31SAmit Kucheria #define BXTWC_THRM1IRQ 0x4E05 303e8c4d31SAmit Kucheria #define BXTWC_THRM2IRQ 0x4E06 313e8c4d31SAmit Kucheria #define BXTWC_MTHRM0IRQ 0x4E12 323e8c4d31SAmit Kucheria #define BXTWC_MTHRM1IRQ 0x4E13 333e8c4d31SAmit Kucheria #define BXTWC_MTHRM2IRQ 0x4E14 343e8c4d31SAmit Kucheria #define BXTWC_STHRM0IRQ 0x4F19 353e8c4d31SAmit Kucheria #define BXTWC_STHRM1IRQ 0x4F1A 363e8c4d31SAmit Kucheria #define BXTWC_STHRM2IRQ 0x4F1B 373e8c4d31SAmit Kucheria 383e8c4d31SAmit Kucheria struct trip_config_map { 393e8c4d31SAmit Kucheria u16 irq_reg; 403e8c4d31SAmit Kucheria u16 irq_en; 413e8c4d31SAmit Kucheria u16 evt_stat; 423e8c4d31SAmit Kucheria u8 irq_mask; 433e8c4d31SAmit Kucheria u8 irq_en_mask; 443e8c4d31SAmit Kucheria u8 evt_mask; 453e8c4d31SAmit Kucheria u8 trip_num; 463e8c4d31SAmit Kucheria }; 473e8c4d31SAmit Kucheria 483e8c4d31SAmit Kucheria struct thermal_irq_map { 493e8c4d31SAmit Kucheria char handle[20]; 503e8c4d31SAmit Kucheria int num_trips; 513e8c4d31SAmit Kucheria const struct trip_config_map *trip_config; 523e8c4d31SAmit Kucheria }; 533e8c4d31SAmit Kucheria 543e8c4d31SAmit Kucheria struct pmic_thermal_data { 553e8c4d31SAmit Kucheria const struct thermal_irq_map *maps; 563e8c4d31SAmit Kucheria int num_maps; 573e8c4d31SAmit Kucheria }; 583e8c4d31SAmit Kucheria 593e8c4d31SAmit Kucheria static const struct trip_config_map bxtwc_str0_trip_config[] = { 603e8c4d31SAmit Kucheria { 613e8c4d31SAmit Kucheria .irq_reg = BXTWC_THRM0IRQ, 623e8c4d31SAmit Kucheria .irq_mask = 0x01, 633e8c4d31SAmit Kucheria .irq_en = BXTWC_MTHRM0IRQ, 643e8c4d31SAmit Kucheria .irq_en_mask = 0x01, 653e8c4d31SAmit Kucheria .evt_stat = BXTWC_STHRM0IRQ, 663e8c4d31SAmit Kucheria .evt_mask = 0x01, 673e8c4d31SAmit Kucheria .trip_num = 0 683e8c4d31SAmit Kucheria }, 693e8c4d31SAmit Kucheria { 703e8c4d31SAmit Kucheria .irq_reg = BXTWC_THRM0IRQ, 713e8c4d31SAmit Kucheria .irq_mask = 0x10, 723e8c4d31SAmit Kucheria .irq_en = BXTWC_MTHRM0IRQ, 733e8c4d31SAmit Kucheria .irq_en_mask = 0x10, 743e8c4d31SAmit Kucheria .evt_stat = BXTWC_STHRM0IRQ, 753e8c4d31SAmit Kucheria .evt_mask = 0x10, 763e8c4d31SAmit Kucheria .trip_num = 1 773e8c4d31SAmit Kucheria } 783e8c4d31SAmit Kucheria }; 793e8c4d31SAmit Kucheria 803e8c4d31SAmit Kucheria static const struct trip_config_map bxtwc_str1_trip_config[] = { 813e8c4d31SAmit Kucheria { 823e8c4d31SAmit Kucheria .irq_reg = BXTWC_THRM0IRQ, 833e8c4d31SAmit Kucheria .irq_mask = 0x02, 843e8c4d31SAmit Kucheria .irq_en = BXTWC_MTHRM0IRQ, 853e8c4d31SAmit Kucheria .irq_en_mask = 0x02, 863e8c4d31SAmit Kucheria .evt_stat = BXTWC_STHRM0IRQ, 873e8c4d31SAmit Kucheria .evt_mask = 0x02, 883e8c4d31SAmit Kucheria .trip_num = 0 893e8c4d31SAmit Kucheria }, 903e8c4d31SAmit Kucheria { 913e8c4d31SAmit Kucheria .irq_reg = BXTWC_THRM0IRQ, 923e8c4d31SAmit Kucheria .irq_mask = 0x20, 933e8c4d31SAmit Kucheria .irq_en = BXTWC_MTHRM0IRQ, 943e8c4d31SAmit Kucheria .irq_en_mask = 0x20, 953e8c4d31SAmit Kucheria .evt_stat = BXTWC_STHRM0IRQ, 963e8c4d31SAmit Kucheria .evt_mask = 0x20, 973e8c4d31SAmit Kucheria .trip_num = 1 983e8c4d31SAmit Kucheria }, 993e8c4d31SAmit Kucheria }; 1003e8c4d31SAmit Kucheria 1013e8c4d31SAmit Kucheria static const struct trip_config_map bxtwc_str2_trip_config[] = { 1023e8c4d31SAmit Kucheria { 1033e8c4d31SAmit Kucheria .irq_reg = BXTWC_THRM0IRQ, 1043e8c4d31SAmit Kucheria .irq_mask = 0x04, 1053e8c4d31SAmit Kucheria .irq_en = BXTWC_MTHRM0IRQ, 1063e8c4d31SAmit Kucheria .irq_en_mask = 0x04, 1073e8c4d31SAmit Kucheria .evt_stat = BXTWC_STHRM0IRQ, 1083e8c4d31SAmit Kucheria .evt_mask = 0x04, 1093e8c4d31SAmit Kucheria .trip_num = 0 1103e8c4d31SAmit Kucheria }, 1113e8c4d31SAmit Kucheria { 1123e8c4d31SAmit Kucheria .irq_reg = BXTWC_THRM0IRQ, 1133e8c4d31SAmit Kucheria .irq_mask = 0x40, 1143e8c4d31SAmit Kucheria .irq_en = BXTWC_MTHRM0IRQ, 1153e8c4d31SAmit Kucheria .irq_en_mask = 0x40, 1163e8c4d31SAmit Kucheria .evt_stat = BXTWC_STHRM0IRQ, 1173e8c4d31SAmit Kucheria .evt_mask = 0x40, 1183e8c4d31SAmit Kucheria .trip_num = 1 1193e8c4d31SAmit Kucheria }, 1203e8c4d31SAmit Kucheria }; 1213e8c4d31SAmit Kucheria 1223e8c4d31SAmit Kucheria static const struct trip_config_map bxtwc_str3_trip_config[] = { 1233e8c4d31SAmit Kucheria { 1243e8c4d31SAmit Kucheria .irq_reg = BXTWC_THRM2IRQ, 1253e8c4d31SAmit Kucheria .irq_mask = 0x10, 1263e8c4d31SAmit Kucheria .irq_en = BXTWC_MTHRM2IRQ, 1273e8c4d31SAmit Kucheria .irq_en_mask = 0x10, 1283e8c4d31SAmit Kucheria .evt_stat = BXTWC_STHRM2IRQ, 1293e8c4d31SAmit Kucheria .evt_mask = 0x10, 1303e8c4d31SAmit Kucheria .trip_num = 0 1313e8c4d31SAmit Kucheria }, 1323e8c4d31SAmit Kucheria }; 1333e8c4d31SAmit Kucheria 1343e8c4d31SAmit Kucheria static const struct thermal_irq_map bxtwc_thermal_irq_map[] = { 1353e8c4d31SAmit Kucheria { 1363e8c4d31SAmit Kucheria .handle = "STR0", 1373e8c4d31SAmit Kucheria .trip_config = bxtwc_str0_trip_config, 1383e8c4d31SAmit Kucheria .num_trips = ARRAY_SIZE(bxtwc_str0_trip_config), 1393e8c4d31SAmit Kucheria }, 1403e8c4d31SAmit Kucheria { 1413e8c4d31SAmit Kucheria .handle = "STR1", 1423e8c4d31SAmit Kucheria .trip_config = bxtwc_str1_trip_config, 1433e8c4d31SAmit Kucheria .num_trips = ARRAY_SIZE(bxtwc_str1_trip_config), 1443e8c4d31SAmit Kucheria }, 1453e8c4d31SAmit Kucheria { 1463e8c4d31SAmit Kucheria .handle = "STR2", 1473e8c4d31SAmit Kucheria .trip_config = bxtwc_str2_trip_config, 1483e8c4d31SAmit Kucheria .num_trips = ARRAY_SIZE(bxtwc_str2_trip_config), 1493e8c4d31SAmit Kucheria }, 1503e8c4d31SAmit Kucheria { 1513e8c4d31SAmit Kucheria .handle = "STR3", 1523e8c4d31SAmit Kucheria .trip_config = bxtwc_str3_trip_config, 1533e8c4d31SAmit Kucheria .num_trips = ARRAY_SIZE(bxtwc_str3_trip_config), 1543e8c4d31SAmit Kucheria }, 1553e8c4d31SAmit Kucheria }; 1563e8c4d31SAmit Kucheria 1573e8c4d31SAmit Kucheria static const struct pmic_thermal_data bxtwc_thermal_data = { 1583e8c4d31SAmit Kucheria .maps = bxtwc_thermal_irq_map, 1593e8c4d31SAmit Kucheria .num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map), 1603e8c4d31SAmit Kucheria }; 1613e8c4d31SAmit Kucheria 1623e8c4d31SAmit Kucheria static irqreturn_t pmic_thermal_irq_handler(int irq, void *data) 1633e8c4d31SAmit Kucheria { 1643e8c4d31SAmit Kucheria struct platform_device *pdev = data; 1653e8c4d31SAmit Kucheria struct thermal_zone_device *tzd; 1663e8c4d31SAmit Kucheria struct pmic_thermal_data *td; 1673e8c4d31SAmit Kucheria struct intel_soc_pmic *pmic; 1683e8c4d31SAmit Kucheria struct regmap *regmap; 1693e8c4d31SAmit Kucheria u8 reg_val, mask, irq_stat; 1703e8c4d31SAmit Kucheria u16 reg, evt_stat_reg; 1713e8c4d31SAmit Kucheria int i, j, ret; 1723e8c4d31SAmit Kucheria 1733e8c4d31SAmit Kucheria pmic = dev_get_drvdata(pdev->dev.parent); 1743e8c4d31SAmit Kucheria regmap = pmic->regmap; 1753e8c4d31SAmit Kucheria td = (struct pmic_thermal_data *) 1763e8c4d31SAmit Kucheria platform_get_device_id(pdev)->driver_data; 1773e8c4d31SAmit Kucheria 1783e8c4d31SAmit Kucheria /* Resolve thermal irqs */ 1793e8c4d31SAmit Kucheria for (i = 0; i < td->num_maps; i++) { 1803e8c4d31SAmit Kucheria for (j = 0; j < td->maps[i].num_trips; j++) { 1813e8c4d31SAmit Kucheria reg = td->maps[i].trip_config[j].irq_reg; 1823e8c4d31SAmit Kucheria mask = td->maps[i].trip_config[j].irq_mask; 1833e8c4d31SAmit Kucheria /* 1843e8c4d31SAmit Kucheria * Read the irq register to resolve whether the 1853e8c4d31SAmit Kucheria * interrupt was triggered for this sensor 1863e8c4d31SAmit Kucheria */ 1873e8c4d31SAmit Kucheria if (regmap_read(regmap, reg, &ret)) 1883e8c4d31SAmit Kucheria return IRQ_HANDLED; 1893e8c4d31SAmit Kucheria 1903e8c4d31SAmit Kucheria reg_val = (u8)ret; 1913e8c4d31SAmit Kucheria irq_stat = ((u8)ret & mask); 1923e8c4d31SAmit Kucheria 1933e8c4d31SAmit Kucheria if (!irq_stat) 1943e8c4d31SAmit Kucheria continue; 1953e8c4d31SAmit Kucheria 1963e8c4d31SAmit Kucheria /* 1973e8c4d31SAmit Kucheria * Read the status register to find out what 1983e8c4d31SAmit Kucheria * event occurred i.e a high or a low 1993e8c4d31SAmit Kucheria */ 2003e8c4d31SAmit Kucheria evt_stat_reg = td->maps[i].trip_config[j].evt_stat; 2013e8c4d31SAmit Kucheria if (regmap_read(regmap, evt_stat_reg, &ret)) 2023e8c4d31SAmit Kucheria return IRQ_HANDLED; 2033e8c4d31SAmit Kucheria 2043e8c4d31SAmit Kucheria tzd = thermal_zone_get_zone_by_name(td->maps[i].handle); 2053e8c4d31SAmit Kucheria if (!IS_ERR(tzd)) 2063e8c4d31SAmit Kucheria thermal_zone_device_update(tzd, 2073e8c4d31SAmit Kucheria THERMAL_EVENT_UNSPECIFIED); 2083e8c4d31SAmit Kucheria 2093e8c4d31SAmit Kucheria /* Clear the appropriate irq */ 2103e8c4d31SAmit Kucheria regmap_write(regmap, reg, reg_val & mask); 2113e8c4d31SAmit Kucheria } 2123e8c4d31SAmit Kucheria } 2133e8c4d31SAmit Kucheria 2143e8c4d31SAmit Kucheria return IRQ_HANDLED; 2153e8c4d31SAmit Kucheria } 2163e8c4d31SAmit Kucheria 2173e8c4d31SAmit Kucheria static int pmic_thermal_probe(struct platform_device *pdev) 2183e8c4d31SAmit Kucheria { 2193e8c4d31SAmit Kucheria struct regmap_irq_chip_data *regmap_irq_chip; 2203e8c4d31SAmit Kucheria struct pmic_thermal_data *thermal_data; 2213e8c4d31SAmit Kucheria int ret, irq, virq, i, j, pmic_irq_count; 2223e8c4d31SAmit Kucheria struct intel_soc_pmic *pmic; 2233e8c4d31SAmit Kucheria struct regmap *regmap; 2243e8c4d31SAmit Kucheria struct device *dev; 2253e8c4d31SAmit Kucheria u16 reg; 2263e8c4d31SAmit Kucheria u8 mask; 2273e8c4d31SAmit Kucheria 2283e8c4d31SAmit Kucheria dev = &pdev->dev; 2293e8c4d31SAmit Kucheria pmic = dev_get_drvdata(pdev->dev.parent); 2303e8c4d31SAmit Kucheria if (!pmic) { 2313e8c4d31SAmit Kucheria dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n"); 2323e8c4d31SAmit Kucheria return -ENODEV; 2333e8c4d31SAmit Kucheria } 2343e8c4d31SAmit Kucheria 2353e8c4d31SAmit Kucheria thermal_data = (struct pmic_thermal_data *) 2363e8c4d31SAmit Kucheria platform_get_device_id(pdev)->driver_data; 2373e8c4d31SAmit Kucheria if (!thermal_data) { 2383e8c4d31SAmit Kucheria dev_err(dev, "No thermal data initialized!!\n"); 2393e8c4d31SAmit Kucheria return -ENODEV; 2403e8c4d31SAmit Kucheria } 2413e8c4d31SAmit Kucheria 2423e8c4d31SAmit Kucheria regmap = pmic->regmap; 2433e8c4d31SAmit Kucheria regmap_irq_chip = pmic->irq_chip_data; 2443e8c4d31SAmit Kucheria 2453e8c4d31SAmit Kucheria pmic_irq_count = 0; 2463e8c4d31SAmit Kucheria while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) { 2473e8c4d31SAmit Kucheria virq = regmap_irq_get_virq(regmap_irq_chip, irq); 2483e8c4d31SAmit Kucheria if (virq < 0) { 2493e8c4d31SAmit Kucheria dev_err(dev, "failed to get virq by irq %d\n", irq); 2503e8c4d31SAmit Kucheria return virq; 2513e8c4d31SAmit Kucheria } 2523e8c4d31SAmit Kucheria 2533e8c4d31SAmit Kucheria ret = devm_request_threaded_irq(&pdev->dev, virq, 2543e8c4d31SAmit Kucheria NULL, pmic_thermal_irq_handler, 2553e8c4d31SAmit Kucheria IRQF_ONESHOT, "pmic_thermal", pdev); 2563e8c4d31SAmit Kucheria 2573e8c4d31SAmit Kucheria if (ret) { 2583e8c4d31SAmit Kucheria dev_err(dev, "request irq(%d) failed: %d\n", virq, ret); 2593e8c4d31SAmit Kucheria return ret; 2603e8c4d31SAmit Kucheria } 2613e8c4d31SAmit Kucheria pmic_irq_count++; 2623e8c4d31SAmit Kucheria } 2633e8c4d31SAmit Kucheria 2643e8c4d31SAmit Kucheria /* Enable thermal interrupts */ 2653e8c4d31SAmit Kucheria for (i = 0; i < thermal_data->num_maps; i++) { 2663e8c4d31SAmit Kucheria for (j = 0; j < thermal_data->maps[i].num_trips; j++) { 2673e8c4d31SAmit Kucheria reg = thermal_data->maps[i].trip_config[j].irq_en; 2683e8c4d31SAmit Kucheria mask = thermal_data->maps[i].trip_config[j].irq_en_mask; 2693e8c4d31SAmit Kucheria ret = regmap_update_bits(regmap, reg, mask, 0x00); 2703e8c4d31SAmit Kucheria if (ret) 2713e8c4d31SAmit Kucheria return ret; 2723e8c4d31SAmit Kucheria } 2733e8c4d31SAmit Kucheria } 2743e8c4d31SAmit Kucheria 2753e8c4d31SAmit Kucheria return 0; 2763e8c4d31SAmit Kucheria } 2773e8c4d31SAmit Kucheria 2783e8c4d31SAmit Kucheria static const struct platform_device_id pmic_thermal_id_table[] = { 2793e8c4d31SAmit Kucheria { 2803e8c4d31SAmit Kucheria .name = "bxt_wcove_thermal", 2813e8c4d31SAmit Kucheria .driver_data = (kernel_ulong_t)&bxtwc_thermal_data, 2823e8c4d31SAmit Kucheria }, 2833e8c4d31SAmit Kucheria {}, 2843e8c4d31SAmit Kucheria }; 2853e8c4d31SAmit Kucheria 2863e8c4d31SAmit Kucheria static struct platform_driver pmic_thermal_driver = { 2873e8c4d31SAmit Kucheria .probe = pmic_thermal_probe, 2883e8c4d31SAmit Kucheria .driver = { 2893e8c4d31SAmit Kucheria .name = "pmic_thermal", 2903e8c4d31SAmit Kucheria }, 2913e8c4d31SAmit Kucheria .id_table = pmic_thermal_id_table, 2923e8c4d31SAmit Kucheria }; 2933e8c4d31SAmit Kucheria 2943e8c4d31SAmit Kucheria MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table); 2953e8c4d31SAmit Kucheria module_platform_driver(pmic_thermal_driver); 2963e8c4d31SAmit Kucheria 2973e8c4d31SAmit Kucheria MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>"); 2983e8c4d31SAmit Kucheria MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver"); 2993e8c4d31SAmit Kucheria MODULE_LICENSE("GPL v2"); 300