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