18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Broadcom STB AVS TMON thermal sensor driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015-2017 Broadcom
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define DRV_NAME	"brcmstb_thermal"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	DRV_NAME ": " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/bitops.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/irqreturn.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/of_device.h>
228c2ecf20Sopenharmony_ci#include <linux/thermal.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define AVS_TMON_STATUS			0x00
258c2ecf20Sopenharmony_ci #define AVS_TMON_STATUS_valid_msk	BIT(11)
268c2ecf20Sopenharmony_ci #define AVS_TMON_STATUS_data_msk	GENMASK(10, 1)
278c2ecf20Sopenharmony_ci #define AVS_TMON_STATUS_data_shift	1
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define AVS_TMON_EN_OVERTEMP_RESET	0x04
308c2ecf20Sopenharmony_ci #define AVS_TMON_EN_OVERTEMP_RESET_msk	BIT(0)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define AVS_TMON_RESET_THRESH		0x08
338c2ecf20Sopenharmony_ci #define AVS_TMON_RESET_THRESH_msk	GENMASK(10, 1)
348c2ecf20Sopenharmony_ci #define AVS_TMON_RESET_THRESH_shift	1
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define AVS_TMON_INT_IDLE_TIME		0x10
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define AVS_TMON_EN_TEMP_INT_SRCS	0x14
398c2ecf20Sopenharmony_ci #define AVS_TMON_EN_TEMP_INT_SRCS_high	BIT(1)
408c2ecf20Sopenharmony_ci #define AVS_TMON_EN_TEMP_INT_SRCS_low	BIT(0)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define AVS_TMON_INT_THRESH		0x18
438c2ecf20Sopenharmony_ci #define AVS_TMON_INT_THRESH_high_msk	GENMASK(26, 17)
448c2ecf20Sopenharmony_ci #define AVS_TMON_INT_THRESH_high_shift	17
458c2ecf20Sopenharmony_ci #define AVS_TMON_INT_THRESH_low_msk	GENMASK(10, 1)
468c2ecf20Sopenharmony_ci #define AVS_TMON_INT_THRESH_low_shift	1
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define AVS_TMON_TEMP_INT_CODE		0x1c
498c2ecf20Sopenharmony_ci#define AVS_TMON_TP_TEST_ENABLE		0x20
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Default coefficients */
528c2ecf20Sopenharmony_ci#define AVS_TMON_TEMP_SLOPE		487
538c2ecf20Sopenharmony_ci#define AVS_TMON_TEMP_OFFSET		410040
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* HW related temperature constants */
568c2ecf20Sopenharmony_ci#define AVS_TMON_TEMP_MAX		0x3ff
578c2ecf20Sopenharmony_ci#define AVS_TMON_TEMP_MIN		-88161
588c2ecf20Sopenharmony_ci#define AVS_TMON_TEMP_MASK		AVS_TMON_TEMP_MAX
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cienum avs_tmon_trip_type {
618c2ecf20Sopenharmony_ci	TMON_TRIP_TYPE_LOW = 0,
628c2ecf20Sopenharmony_ci	TMON_TRIP_TYPE_HIGH,
638c2ecf20Sopenharmony_ci	TMON_TRIP_TYPE_RESET,
648c2ecf20Sopenharmony_ci	TMON_TRIP_TYPE_MAX,
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct avs_tmon_trip {
688c2ecf20Sopenharmony_ci	/* HW bit to enable the trip */
698c2ecf20Sopenharmony_ci	u32 enable_offs;
708c2ecf20Sopenharmony_ci	u32 enable_mask;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* HW field to read the trip temperature */
738c2ecf20Sopenharmony_ci	u32 reg_offs;
748c2ecf20Sopenharmony_ci	u32 reg_msk;
758c2ecf20Sopenharmony_ci	int reg_shift;
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic struct avs_tmon_trip avs_tmon_trips[] = {
798c2ecf20Sopenharmony_ci	/* Trips when temperature is below threshold */
808c2ecf20Sopenharmony_ci	[TMON_TRIP_TYPE_LOW] = {
818c2ecf20Sopenharmony_ci		.enable_offs	= AVS_TMON_EN_TEMP_INT_SRCS,
828c2ecf20Sopenharmony_ci		.enable_mask	= AVS_TMON_EN_TEMP_INT_SRCS_low,
838c2ecf20Sopenharmony_ci		.reg_offs	= AVS_TMON_INT_THRESH,
848c2ecf20Sopenharmony_ci		.reg_msk	= AVS_TMON_INT_THRESH_low_msk,
858c2ecf20Sopenharmony_ci		.reg_shift	= AVS_TMON_INT_THRESH_low_shift,
868c2ecf20Sopenharmony_ci	},
878c2ecf20Sopenharmony_ci	/* Trips when temperature is above threshold */
888c2ecf20Sopenharmony_ci	[TMON_TRIP_TYPE_HIGH] = {
898c2ecf20Sopenharmony_ci		.enable_offs	= AVS_TMON_EN_TEMP_INT_SRCS,
908c2ecf20Sopenharmony_ci		.enable_mask	= AVS_TMON_EN_TEMP_INT_SRCS_high,
918c2ecf20Sopenharmony_ci		.reg_offs	= AVS_TMON_INT_THRESH,
928c2ecf20Sopenharmony_ci		.reg_msk	= AVS_TMON_INT_THRESH_high_msk,
938c2ecf20Sopenharmony_ci		.reg_shift	= AVS_TMON_INT_THRESH_high_shift,
948c2ecf20Sopenharmony_ci	},
958c2ecf20Sopenharmony_ci	/* Automatically resets chip when above threshold */
968c2ecf20Sopenharmony_ci	[TMON_TRIP_TYPE_RESET] = {
978c2ecf20Sopenharmony_ci		.enable_offs	= AVS_TMON_EN_OVERTEMP_RESET,
988c2ecf20Sopenharmony_ci		.enable_mask	= AVS_TMON_EN_OVERTEMP_RESET_msk,
998c2ecf20Sopenharmony_ci		.reg_offs	= AVS_TMON_RESET_THRESH,
1008c2ecf20Sopenharmony_ci		.reg_msk	= AVS_TMON_RESET_THRESH_msk,
1018c2ecf20Sopenharmony_ci		.reg_shift	= AVS_TMON_RESET_THRESH_shift,
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistruct brcmstb_thermal_params {
1068c2ecf20Sopenharmony_ci	unsigned int offset;
1078c2ecf20Sopenharmony_ci	unsigned int mult;
1088c2ecf20Sopenharmony_ci	const struct thermal_zone_of_device_ops *of_ops;
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistruct brcmstb_thermal_priv {
1128c2ecf20Sopenharmony_ci	void __iomem *tmon_base;
1138c2ecf20Sopenharmony_ci	struct device *dev;
1148c2ecf20Sopenharmony_ci	struct thermal_zone_device *thermal;
1158c2ecf20Sopenharmony_ci	/* Process specific thermal parameters used for calculations */
1168c2ecf20Sopenharmony_ci	const struct brcmstb_thermal_params *temp_params;
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* Convert a HW code to a temperature reading (millidegree celsius) */
1208c2ecf20Sopenharmony_cistatic inline int avs_tmon_code_to_temp(struct brcmstb_thermal_priv *priv,
1218c2ecf20Sopenharmony_ci					u32 code)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int offset = priv->temp_params->offset;
1248c2ecf20Sopenharmony_ci	int mult = priv->temp_params->mult;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return (offset - (int)((code & AVS_TMON_TEMP_MASK) * mult));
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * Convert a temperature value (millidegree celsius) to a HW code
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci * @temp: temperature to convert
1338c2ecf20Sopenharmony_ci * @low: if true, round toward the low side
1348c2ecf20Sopenharmony_ci */
1358c2ecf20Sopenharmony_cistatic inline u32 avs_tmon_temp_to_code(struct brcmstb_thermal_priv *priv,
1368c2ecf20Sopenharmony_ci					int temp, bool low)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	int offset = priv->temp_params->offset;
1398c2ecf20Sopenharmony_ci	int mult = priv->temp_params->mult;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (temp < AVS_TMON_TEMP_MIN)
1428c2ecf20Sopenharmony_ci		return AVS_TMON_TEMP_MAX;	/* Maximum code value */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (temp >= offset)
1458c2ecf20Sopenharmony_ci		return 0;	/* Minimum code value */
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (low)
1488c2ecf20Sopenharmony_ci		return (u32)(DIV_ROUND_UP(offset - temp, mult));
1498c2ecf20Sopenharmony_ci	else
1508c2ecf20Sopenharmony_ci		return (u32)((offset - temp) / mult);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int brcmstb_get_temp(void *data, int *temp)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct brcmstb_thermal_priv *priv = data;
1568c2ecf20Sopenharmony_ci	u32 val;
1578c2ecf20Sopenharmony_ci	long t;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	val = __raw_readl(priv->tmon_base + AVS_TMON_STATUS);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!(val & AVS_TMON_STATUS_valid_msk)) {
1628c2ecf20Sopenharmony_ci		dev_err(priv->dev, "reading not valid\n");
1638c2ecf20Sopenharmony_ci		return -EIO;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	t = avs_tmon_code_to_temp(priv, val);
1698c2ecf20Sopenharmony_ci	if (t < 0)
1708c2ecf20Sopenharmony_ci		*temp = 0;
1718c2ecf20Sopenharmony_ci	else
1728c2ecf20Sopenharmony_ci		*temp = t;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void avs_tmon_trip_enable(struct brcmstb_thermal_priv *priv,
1788c2ecf20Sopenharmony_ci				 enum avs_tmon_trip_type type, int en)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct avs_tmon_trip *trip = &avs_tmon_trips[type];
1818c2ecf20Sopenharmony_ci	u32 val = __raw_readl(priv->tmon_base + trip->enable_offs);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "%sable trip, type %d\n", en ? "en" : "dis", type);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (en)
1868c2ecf20Sopenharmony_ci		val |= trip->enable_mask;
1878c2ecf20Sopenharmony_ci	else
1888c2ecf20Sopenharmony_ci		val &= ~trip->enable_mask;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	__raw_writel(val, priv->tmon_base + trip->enable_offs);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int avs_tmon_get_trip_temp(struct brcmstb_thermal_priv *priv,
1948c2ecf20Sopenharmony_ci				  enum avs_tmon_trip_type type)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct avs_tmon_trip *trip = &avs_tmon_trips[type];
1978c2ecf20Sopenharmony_ci	u32 val = __raw_readl(priv->tmon_base + trip->reg_offs);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	val &= trip->reg_msk;
2008c2ecf20Sopenharmony_ci	val >>= trip->reg_shift;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return avs_tmon_code_to_temp(priv, val);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv,
2068c2ecf20Sopenharmony_ci				   enum avs_tmon_trip_type type,
2078c2ecf20Sopenharmony_ci				   int temp)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct avs_tmon_trip *trip = &avs_tmon_trips[type];
2108c2ecf20Sopenharmony_ci	u32 val, orig;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "set temp %d to %d\n", type, temp);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* round toward low temp for the low interrupt */
2158c2ecf20Sopenharmony_ci	val = avs_tmon_temp_to_code(priv, temp,
2168c2ecf20Sopenharmony_ci				    type == TMON_TRIP_TYPE_LOW);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	val <<= trip->reg_shift;
2198c2ecf20Sopenharmony_ci	val &= trip->reg_msk;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	orig = __raw_readl(priv->tmon_base + trip->reg_offs);
2228c2ecf20Sopenharmony_ci	orig &= ~trip->reg_msk;
2238c2ecf20Sopenharmony_ci	orig |= val;
2248c2ecf20Sopenharmony_ci	__raw_writel(orig, priv->tmon_base + trip->reg_offs);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int avs_tmon_get_intr_temp(struct brcmstb_thermal_priv *priv)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	u32 val;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	val = __raw_readl(priv->tmon_base + AVS_TMON_TEMP_INT_CODE);
2328c2ecf20Sopenharmony_ci	return avs_tmon_code_to_temp(priv, val);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic irqreturn_t brcmstb_tmon_irq_thread(int irq, void *data)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct brcmstb_thermal_priv *priv = data;
2388c2ecf20Sopenharmony_ci	int low, high, intr;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	low = avs_tmon_get_trip_temp(priv, TMON_TRIP_TYPE_LOW);
2418c2ecf20Sopenharmony_ci	high = avs_tmon_get_trip_temp(priv, TMON_TRIP_TYPE_HIGH);
2428c2ecf20Sopenharmony_ci	intr = avs_tmon_get_intr_temp(priv);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "low/intr/high: %d/%d/%d\n",
2458c2ecf20Sopenharmony_ci			low, intr, high);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* Disable high-temp until next threshold shift */
2488c2ecf20Sopenharmony_ci	if (intr >= high)
2498c2ecf20Sopenharmony_ci		avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0);
2508c2ecf20Sopenharmony_ci	/* Disable low-temp until next threshold shift */
2518c2ecf20Sopenharmony_ci	if (intr <= low)
2528c2ecf20Sopenharmony_ci		avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/*
2558c2ecf20Sopenharmony_ci	 * Notify using the interrupt temperature, in case the temperature
2568c2ecf20Sopenharmony_ci	 * changes before it can next be read out
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	thermal_zone_device_update(priv->thermal, intr);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int brcmstb_set_trips(void *data, int low, int high)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct brcmstb_thermal_priv *priv = data;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "set trips %d <--> %d\n", low, high);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/*
2708c2ecf20Sopenharmony_ci	 * Disable low-temp if "low" is too small. As per thermal framework
2718c2ecf20Sopenharmony_ci	 * API, we use -INT_MAX rather than INT_MIN.
2728c2ecf20Sopenharmony_ci	 */
2738c2ecf20Sopenharmony_ci	if (low <= -INT_MAX) {
2748c2ecf20Sopenharmony_ci		avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0);
2758c2ecf20Sopenharmony_ci	} else {
2768c2ecf20Sopenharmony_ci		avs_tmon_set_trip_temp(priv, TMON_TRIP_TYPE_LOW, low);
2778c2ecf20Sopenharmony_ci		avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 1);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* Disable high-temp if "high" is too big. */
2818c2ecf20Sopenharmony_ci	if (high == INT_MAX) {
2828c2ecf20Sopenharmony_ci		avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0);
2838c2ecf20Sopenharmony_ci	} else {
2848c2ecf20Sopenharmony_ci		avs_tmon_set_trip_temp(priv, TMON_TRIP_TYPE_HIGH, high);
2858c2ecf20Sopenharmony_ci		avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 1);
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic const struct thermal_zone_of_device_ops brcmstb_16nm_of_ops = {
2928c2ecf20Sopenharmony_ci	.get_temp	= brcmstb_get_temp,
2938c2ecf20Sopenharmony_ci};
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic const struct brcmstb_thermal_params brcmstb_16nm_params = {
2968c2ecf20Sopenharmony_ci	.offset	= 457829,
2978c2ecf20Sopenharmony_ci	.mult	= 557,
2988c2ecf20Sopenharmony_ci	.of_ops	= &brcmstb_16nm_of_ops,
2998c2ecf20Sopenharmony_ci};
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic const struct thermal_zone_of_device_ops brcmstb_28nm_of_ops = {
3028c2ecf20Sopenharmony_ci	.get_temp	= brcmstb_get_temp,
3038c2ecf20Sopenharmony_ci	.set_trips	= brcmstb_set_trips,
3048c2ecf20Sopenharmony_ci};
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic const struct brcmstb_thermal_params brcmstb_28nm_params = {
3078c2ecf20Sopenharmony_ci	.offset	= 410040,
3088c2ecf20Sopenharmony_ci	.mult	= 487,
3098c2ecf20Sopenharmony_ci	.of_ops	= &brcmstb_28nm_of_ops,
3108c2ecf20Sopenharmony_ci};
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic const struct of_device_id brcmstb_thermal_id_table[] = {
3138c2ecf20Sopenharmony_ci	{ .compatible = "brcm,avs-tmon-bcm7216", .data = &brcmstb_16nm_params },
3148c2ecf20Sopenharmony_ci	{ .compatible = "brcm,avs-tmon", .data = &brcmstb_28nm_params },
3158c2ecf20Sopenharmony_ci	{},
3168c2ecf20Sopenharmony_ci};
3178c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic int brcmstb_thermal_probe(struct platform_device *pdev)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	const struct thermal_zone_of_device_ops *of_ops;
3228c2ecf20Sopenharmony_ci	struct thermal_zone_device *thermal;
3238c2ecf20Sopenharmony_ci	struct brcmstb_thermal_priv *priv;
3248c2ecf20Sopenharmony_ci	struct resource *res;
3258c2ecf20Sopenharmony_ci	int irq, ret;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
3288c2ecf20Sopenharmony_ci	if (!priv)
3298c2ecf20Sopenharmony_ci		return -ENOMEM;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	priv->temp_params = of_device_get_match_data(&pdev->dev);
3328c2ecf20Sopenharmony_ci	if (!priv->temp_params)
3338c2ecf20Sopenharmony_ci		return -EINVAL;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3368c2ecf20Sopenharmony_ci	priv->tmon_base = devm_ioremap_resource(&pdev->dev, res);
3378c2ecf20Sopenharmony_ci	if (IS_ERR(priv->tmon_base))
3388c2ecf20Sopenharmony_ci		return PTR_ERR(priv->tmon_base);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	priv->dev = &pdev->dev;
3418c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
3428c2ecf20Sopenharmony_ci	of_ops = priv->temp_params->of_ops;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	thermal = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv,
3458c2ecf20Sopenharmony_ci						       of_ops);
3468c2ecf20Sopenharmony_ci	if (IS_ERR(thermal)) {
3478c2ecf20Sopenharmony_ci		ret = PTR_ERR(thermal);
3488c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not register sensor: %d\n", ret);
3498c2ecf20Sopenharmony_ci		return ret;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	priv->thermal = thermal;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
3558c2ecf20Sopenharmony_ci	if (irq >= 0) {
3568c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
3578c2ecf20Sopenharmony_ci						brcmstb_tmon_irq_thread,
3588c2ecf20Sopenharmony_ci						IRQF_ONESHOT,
3598c2ecf20Sopenharmony_ci						DRV_NAME, priv);
3608c2ecf20Sopenharmony_ci		if (ret < 0) {
3618c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "could not request IRQ: %d\n", ret);
3628c2ecf20Sopenharmony_ci			return ret;
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n");
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic struct platform_driver brcmstb_thermal_driver = {
3728c2ecf20Sopenharmony_ci	.probe = brcmstb_thermal_probe,
3738c2ecf20Sopenharmony_ci	.driver = {
3748c2ecf20Sopenharmony_ci		.name = DRV_NAME,
3758c2ecf20Sopenharmony_ci		.of_match_table = brcmstb_thermal_id_table,
3768c2ecf20Sopenharmony_ci	},
3778c2ecf20Sopenharmony_ci};
3788c2ecf20Sopenharmony_cimodule_platform_driver(brcmstb_thermal_driver);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Brian Norris");
3828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom STB AVS TMON thermal driver");
383