brintos

brintos / linux-shallow public Read only

0
0
Text · 6.6 KiB · 6312c6b Raw
291 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Intel Broxton PMIC thermal driver4 *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		0x4E0420#define BXTWC_THRM1IRQ		0x4E0521#define BXTWC_THRM2IRQ		0x4E0622#define BXTWC_MTHRM0IRQ		0x4E1223#define BXTWC_MTHRM1IRQ		0x4E1324#define BXTWC_MTHRM2IRQ		0x4E1425#define BXTWC_STHRM0IRQ		0x4F1926#define BXTWC_STHRM1IRQ		0x4F1A27#define BXTWC_STHRM2IRQ		0x4F1B28 29struct 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 39struct thermal_irq_map {40	char handle[20];41	int num_trips;42	const struct trip_config_map *trip_config;43};44 45struct pmic_thermal_data {46	const struct thermal_irq_map *maps;47	int num_maps;48};49 50static 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 = 059	},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 = 168	}69};70 71static 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 = 080	},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 = 189	},90};91 92static 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 = 0101	},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 = 1110	},111};112 113static 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 = 0122	},123};124 125static 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 148static 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 153static 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 the176			 * interrupt was triggered for this sensor177			 */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 what189			 * event occurred i.e a high or a low190			 */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 208static 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 269static 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 277static 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 285MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);286module_platform_driver(pmic_thermal_driver);287 288MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");289MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");290MODULE_LICENSE("GPL v2");291