18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Intel Broxton PMIC thermal driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Intel Corporation. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/thermal.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/sched.h>
178c2ecf20Sopenharmony_ci#include <linux/mfd/intel_soc_pmic.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define BXTWC_THRM0IRQ		0x4E04
208c2ecf20Sopenharmony_ci#define BXTWC_THRM1IRQ		0x4E05
218c2ecf20Sopenharmony_ci#define BXTWC_THRM2IRQ		0x4E06
228c2ecf20Sopenharmony_ci#define BXTWC_MTHRM0IRQ		0x4E12
238c2ecf20Sopenharmony_ci#define BXTWC_MTHRM1IRQ		0x4E13
248c2ecf20Sopenharmony_ci#define BXTWC_MTHRM2IRQ		0x4E14
258c2ecf20Sopenharmony_ci#define BXTWC_STHRM0IRQ		0x4F19
268c2ecf20Sopenharmony_ci#define BXTWC_STHRM1IRQ		0x4F1A
278c2ecf20Sopenharmony_ci#define BXTWC_STHRM2IRQ		0x4F1B
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct trip_config_map {
308c2ecf20Sopenharmony_ci	u16 irq_reg;
318c2ecf20Sopenharmony_ci	u16 irq_en;
328c2ecf20Sopenharmony_ci	u16 evt_stat;
338c2ecf20Sopenharmony_ci	u8 irq_mask;
348c2ecf20Sopenharmony_ci	u8 irq_en_mask;
358c2ecf20Sopenharmony_ci	u8 evt_mask;
368c2ecf20Sopenharmony_ci	u8 trip_num;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct thermal_irq_map {
408c2ecf20Sopenharmony_ci	char handle[20];
418c2ecf20Sopenharmony_ci	int num_trips;
428c2ecf20Sopenharmony_ci	const struct trip_config_map *trip_config;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct pmic_thermal_data {
468c2ecf20Sopenharmony_ci	const struct thermal_irq_map *maps;
478c2ecf20Sopenharmony_ci	int num_maps;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic const struct trip_config_map bxtwc_str0_trip_config[] = {
518c2ecf20Sopenharmony_ci	{
528c2ecf20Sopenharmony_ci		.irq_reg = BXTWC_THRM0IRQ,
538c2ecf20Sopenharmony_ci		.irq_mask = 0x01,
548c2ecf20Sopenharmony_ci		.irq_en = BXTWC_MTHRM0IRQ,
558c2ecf20Sopenharmony_ci		.irq_en_mask = 0x01,
568c2ecf20Sopenharmony_ci		.evt_stat = BXTWC_STHRM0IRQ,
578c2ecf20Sopenharmony_ci		.evt_mask = 0x01,
588c2ecf20Sopenharmony_ci		.trip_num = 0
598c2ecf20Sopenharmony_ci	},
608c2ecf20Sopenharmony_ci	{
618c2ecf20Sopenharmony_ci		.irq_reg = BXTWC_THRM0IRQ,
628c2ecf20Sopenharmony_ci		.irq_mask = 0x10,
638c2ecf20Sopenharmony_ci		.irq_en = BXTWC_MTHRM0IRQ,
648c2ecf20Sopenharmony_ci		.irq_en_mask = 0x10,
658c2ecf20Sopenharmony_ci		.evt_stat = BXTWC_STHRM0IRQ,
668c2ecf20Sopenharmony_ci		.evt_mask = 0x10,
678c2ecf20Sopenharmony_ci		.trip_num = 1
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic const struct trip_config_map bxtwc_str1_trip_config[] = {
728c2ecf20Sopenharmony_ci	{
738c2ecf20Sopenharmony_ci		.irq_reg = BXTWC_THRM0IRQ,
748c2ecf20Sopenharmony_ci		.irq_mask = 0x02,
758c2ecf20Sopenharmony_ci		.irq_en = BXTWC_MTHRM0IRQ,
768c2ecf20Sopenharmony_ci		.irq_en_mask = 0x02,
778c2ecf20Sopenharmony_ci		.evt_stat = BXTWC_STHRM0IRQ,
788c2ecf20Sopenharmony_ci		.evt_mask = 0x02,
798c2ecf20Sopenharmony_ci		.trip_num = 0
808c2ecf20Sopenharmony_ci	},
818c2ecf20Sopenharmony_ci	{
828c2ecf20Sopenharmony_ci		.irq_reg = BXTWC_THRM0IRQ,
838c2ecf20Sopenharmony_ci		.irq_mask = 0x20,
848c2ecf20Sopenharmony_ci		.irq_en = BXTWC_MTHRM0IRQ,
858c2ecf20Sopenharmony_ci		.irq_en_mask = 0x20,
868c2ecf20Sopenharmony_ci		.evt_stat = BXTWC_STHRM0IRQ,
878c2ecf20Sopenharmony_ci		.evt_mask = 0x20,
888c2ecf20Sopenharmony_ci		.trip_num = 1
898c2ecf20Sopenharmony_ci	},
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic const struct trip_config_map bxtwc_str2_trip_config[] = {
938c2ecf20Sopenharmony_ci	{
948c2ecf20Sopenharmony_ci		.irq_reg = BXTWC_THRM0IRQ,
958c2ecf20Sopenharmony_ci		.irq_mask = 0x04,
968c2ecf20Sopenharmony_ci		.irq_en = BXTWC_MTHRM0IRQ,
978c2ecf20Sopenharmony_ci		.irq_en_mask = 0x04,
988c2ecf20Sopenharmony_ci		.evt_stat = BXTWC_STHRM0IRQ,
998c2ecf20Sopenharmony_ci		.evt_mask = 0x04,
1008c2ecf20Sopenharmony_ci		.trip_num = 0
1018c2ecf20Sopenharmony_ci	},
1028c2ecf20Sopenharmony_ci	{
1038c2ecf20Sopenharmony_ci		.irq_reg = BXTWC_THRM0IRQ,
1048c2ecf20Sopenharmony_ci		.irq_mask = 0x40,
1058c2ecf20Sopenharmony_ci		.irq_en = BXTWC_MTHRM0IRQ,
1068c2ecf20Sopenharmony_ci		.irq_en_mask = 0x40,
1078c2ecf20Sopenharmony_ci		.evt_stat = BXTWC_STHRM0IRQ,
1088c2ecf20Sopenharmony_ci		.evt_mask = 0x40,
1098c2ecf20Sopenharmony_ci		.trip_num = 1
1108c2ecf20Sopenharmony_ci	},
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic const struct trip_config_map bxtwc_str3_trip_config[] = {
1148c2ecf20Sopenharmony_ci	{
1158c2ecf20Sopenharmony_ci		.irq_reg = BXTWC_THRM2IRQ,
1168c2ecf20Sopenharmony_ci		.irq_mask = 0x10,
1178c2ecf20Sopenharmony_ci		.irq_en = BXTWC_MTHRM2IRQ,
1188c2ecf20Sopenharmony_ci		.irq_en_mask = 0x10,
1198c2ecf20Sopenharmony_ci		.evt_stat = BXTWC_STHRM2IRQ,
1208c2ecf20Sopenharmony_ci		.evt_mask = 0x10,
1218c2ecf20Sopenharmony_ci		.trip_num = 0
1228c2ecf20Sopenharmony_ci	},
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic const struct thermal_irq_map bxtwc_thermal_irq_map[] = {
1268c2ecf20Sopenharmony_ci	{
1278c2ecf20Sopenharmony_ci		.handle = "STR0",
1288c2ecf20Sopenharmony_ci		.trip_config = bxtwc_str0_trip_config,
1298c2ecf20Sopenharmony_ci		.num_trips = ARRAY_SIZE(bxtwc_str0_trip_config),
1308c2ecf20Sopenharmony_ci	},
1318c2ecf20Sopenharmony_ci	{
1328c2ecf20Sopenharmony_ci		.handle = "STR1",
1338c2ecf20Sopenharmony_ci		.trip_config = bxtwc_str1_trip_config,
1348c2ecf20Sopenharmony_ci		.num_trips = ARRAY_SIZE(bxtwc_str1_trip_config),
1358c2ecf20Sopenharmony_ci	},
1368c2ecf20Sopenharmony_ci	{
1378c2ecf20Sopenharmony_ci		.handle = "STR2",
1388c2ecf20Sopenharmony_ci		.trip_config = bxtwc_str2_trip_config,
1398c2ecf20Sopenharmony_ci		.num_trips = ARRAY_SIZE(bxtwc_str2_trip_config),
1408c2ecf20Sopenharmony_ci	},
1418c2ecf20Sopenharmony_ci	{
1428c2ecf20Sopenharmony_ci		.handle = "STR3",
1438c2ecf20Sopenharmony_ci		.trip_config = bxtwc_str3_trip_config,
1448c2ecf20Sopenharmony_ci		.num_trips = ARRAY_SIZE(bxtwc_str3_trip_config),
1458c2ecf20Sopenharmony_ci	},
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic const struct pmic_thermal_data bxtwc_thermal_data = {
1498c2ecf20Sopenharmony_ci	.maps = bxtwc_thermal_irq_map,
1508c2ecf20Sopenharmony_ci	.num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map),
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic irqreturn_t pmic_thermal_irq_handler(int irq, void *data)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct platform_device *pdev = data;
1568c2ecf20Sopenharmony_ci	struct thermal_zone_device *tzd;
1578c2ecf20Sopenharmony_ci	struct pmic_thermal_data *td;
1588c2ecf20Sopenharmony_ci	struct intel_soc_pmic *pmic;
1598c2ecf20Sopenharmony_ci	struct regmap *regmap;
1608c2ecf20Sopenharmony_ci	u8 reg_val, mask, irq_stat;
1618c2ecf20Sopenharmony_ci	u16 reg, evt_stat_reg;
1628c2ecf20Sopenharmony_ci	int i, j, ret;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	pmic = dev_get_drvdata(pdev->dev.parent);
1658c2ecf20Sopenharmony_ci	regmap = pmic->regmap;
1668c2ecf20Sopenharmony_ci	td = (struct pmic_thermal_data *)
1678c2ecf20Sopenharmony_ci		platform_get_device_id(pdev)->driver_data;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* Resolve thermal irqs */
1708c2ecf20Sopenharmony_ci	for (i = 0; i < td->num_maps; i++) {
1718c2ecf20Sopenharmony_ci		for (j = 0; j < td->maps[i].num_trips; j++) {
1728c2ecf20Sopenharmony_ci			reg = td->maps[i].trip_config[j].irq_reg;
1738c2ecf20Sopenharmony_ci			mask = td->maps[i].trip_config[j].irq_mask;
1748c2ecf20Sopenharmony_ci			/*
1758c2ecf20Sopenharmony_ci			 * Read the irq register to resolve whether the
1768c2ecf20Sopenharmony_ci			 * interrupt was triggered for this sensor
1778c2ecf20Sopenharmony_ci			 */
1788c2ecf20Sopenharmony_ci			if (regmap_read(regmap, reg, &ret))
1798c2ecf20Sopenharmony_ci				return IRQ_HANDLED;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci			reg_val = (u8)ret;
1828c2ecf20Sopenharmony_ci			irq_stat = ((u8)ret & mask);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci			if (!irq_stat)
1858c2ecf20Sopenharmony_ci				continue;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci			/*
1888c2ecf20Sopenharmony_ci			 * Read the status register to find out what
1898c2ecf20Sopenharmony_ci			 * event occurred i.e a high or a low
1908c2ecf20Sopenharmony_ci			 */
1918c2ecf20Sopenharmony_ci			evt_stat_reg = td->maps[i].trip_config[j].evt_stat;
1928c2ecf20Sopenharmony_ci			if (regmap_read(regmap, evt_stat_reg, &ret))
1938c2ecf20Sopenharmony_ci				return IRQ_HANDLED;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci			tzd = thermal_zone_get_zone_by_name(td->maps[i].handle);
1968c2ecf20Sopenharmony_ci			if (!IS_ERR(tzd))
1978c2ecf20Sopenharmony_ci				thermal_zone_device_update(tzd,
1988c2ecf20Sopenharmony_ci						THERMAL_EVENT_UNSPECIFIED);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci			/* Clear the appropriate irq */
2018c2ecf20Sopenharmony_ci			regmap_write(regmap, reg, reg_val & mask);
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int pmic_thermal_probe(struct platform_device *pdev)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct regmap_irq_chip_data *regmap_irq_chip;
2118c2ecf20Sopenharmony_ci	struct pmic_thermal_data *thermal_data;
2128c2ecf20Sopenharmony_ci	int ret, irq, virq, i, j, pmic_irq_count;
2138c2ecf20Sopenharmony_ci	struct intel_soc_pmic *pmic;
2148c2ecf20Sopenharmony_ci	struct regmap *regmap;
2158c2ecf20Sopenharmony_ci	struct device *dev;
2168c2ecf20Sopenharmony_ci	u16 reg;
2178c2ecf20Sopenharmony_ci	u8 mask;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	dev = &pdev->dev;
2208c2ecf20Sopenharmony_ci	pmic = dev_get_drvdata(pdev->dev.parent);
2218c2ecf20Sopenharmony_ci	if (!pmic) {
2228c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n");
2238c2ecf20Sopenharmony_ci		return -ENODEV;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	thermal_data = (struct pmic_thermal_data *)
2278c2ecf20Sopenharmony_ci				platform_get_device_id(pdev)->driver_data;
2288c2ecf20Sopenharmony_ci	if (!thermal_data) {
2298c2ecf20Sopenharmony_ci		dev_err(dev, "No thermal data initialized!!\n");
2308c2ecf20Sopenharmony_ci		return -ENODEV;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	regmap = pmic->regmap;
2348c2ecf20Sopenharmony_ci	regmap_irq_chip = pmic->irq_chip_data;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	pmic_irq_count = 0;
2378c2ecf20Sopenharmony_ci	while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) {
2388c2ecf20Sopenharmony_ci		virq = regmap_irq_get_virq(regmap_irq_chip, irq);
2398c2ecf20Sopenharmony_ci		if (virq < 0) {
2408c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get virq by irq %d\n", irq);
2418c2ecf20Sopenharmony_ci			return virq;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(&pdev->dev, virq,
2458c2ecf20Sopenharmony_ci				NULL, pmic_thermal_irq_handler,
2468c2ecf20Sopenharmony_ci				IRQF_ONESHOT, "pmic_thermal", pdev);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		if (ret) {
2498c2ecf20Sopenharmony_ci			dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
2508c2ecf20Sopenharmony_ci			return ret;
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci		pmic_irq_count++;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* Enable thermal interrupts */
2568c2ecf20Sopenharmony_ci	for (i = 0; i < thermal_data->num_maps; i++) {
2578c2ecf20Sopenharmony_ci		for (j = 0; j < thermal_data->maps[i].num_trips; j++) {
2588c2ecf20Sopenharmony_ci			reg = thermal_data->maps[i].trip_config[j].irq_en;
2598c2ecf20Sopenharmony_ci			mask = thermal_data->maps[i].trip_config[j].irq_en_mask;
2608c2ecf20Sopenharmony_ci			ret = regmap_update_bits(regmap, reg, mask, 0x00);
2618c2ecf20Sopenharmony_ci			if (ret)
2628c2ecf20Sopenharmony_ci				return ret;
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic const struct platform_device_id pmic_thermal_id_table[] = {
2708c2ecf20Sopenharmony_ci	{
2718c2ecf20Sopenharmony_ci		.name = "bxt_wcove_thermal",
2728c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&bxtwc_thermal_data,
2738c2ecf20Sopenharmony_ci	},
2748c2ecf20Sopenharmony_ci	{},
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic struct platform_driver pmic_thermal_driver = {
2788c2ecf20Sopenharmony_ci	.probe = pmic_thermal_probe,
2798c2ecf20Sopenharmony_ci	.driver = {
2808c2ecf20Sopenharmony_ci		.name = "pmic_thermal",
2818c2ecf20Sopenharmony_ci	},
2828c2ecf20Sopenharmony_ci	.id_table = pmic_thermal_id_table,
2838c2ecf20Sopenharmony_ci};
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
2868c2ecf20Sopenharmony_cimodule_platform_driver(pmic_thermal_driver);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
2898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
2908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
291