18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci * Copyright (c) 2019, 2020, Linaro Ltd. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/pm.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/thermal.h> 208c2ecf20Sopenharmony_ci#include "tsens.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/** 238c2ecf20Sopenharmony_ci * struct tsens_irq_data - IRQ status and temperature violations 248c2ecf20Sopenharmony_ci * @up_viol: upper threshold violated 258c2ecf20Sopenharmony_ci * @up_thresh: upper threshold temperature value 268c2ecf20Sopenharmony_ci * @up_irq_mask: mask register for upper threshold irqs 278c2ecf20Sopenharmony_ci * @up_irq_clear: clear register for uppper threshold irqs 288c2ecf20Sopenharmony_ci * @low_viol: lower threshold violated 298c2ecf20Sopenharmony_ci * @low_thresh: lower threshold temperature value 308c2ecf20Sopenharmony_ci * @low_irq_mask: mask register for lower threshold irqs 318c2ecf20Sopenharmony_ci * @low_irq_clear: clear register for lower threshold irqs 328c2ecf20Sopenharmony_ci * @crit_viol: critical threshold violated 338c2ecf20Sopenharmony_ci * @crit_thresh: critical threshold temperature value 348c2ecf20Sopenharmony_ci * @crit_irq_mask: mask register for critical threshold irqs 358c2ecf20Sopenharmony_ci * @crit_irq_clear: clear register for critical threshold irqs 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Structure containing data about temperature threshold settings and 388c2ecf20Sopenharmony_ci * irq status if they were violated. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistruct tsens_irq_data { 418c2ecf20Sopenharmony_ci u32 up_viol; 428c2ecf20Sopenharmony_ci int up_thresh; 438c2ecf20Sopenharmony_ci u32 up_irq_mask; 448c2ecf20Sopenharmony_ci u32 up_irq_clear; 458c2ecf20Sopenharmony_ci u32 low_viol; 468c2ecf20Sopenharmony_ci int low_thresh; 478c2ecf20Sopenharmony_ci u32 low_irq_mask; 488c2ecf20Sopenharmony_ci u32 low_irq_clear; 498c2ecf20Sopenharmony_ci u32 crit_viol; 508c2ecf20Sopenharmony_ci u32 crit_thresh; 518c2ecf20Sopenharmony_ci u32 crit_irq_mask; 528c2ecf20Sopenharmony_ci u32 crit_irq_clear; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cichar *qfprom_read(struct device *dev, const char *cname) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct nvmem_cell *cell; 588c2ecf20Sopenharmony_ci ssize_t data; 598c2ecf20Sopenharmony_ci char *ret; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci cell = nvmem_cell_get(dev, cname); 628c2ecf20Sopenharmony_ci if (IS_ERR(cell)) 638c2ecf20Sopenharmony_ci return ERR_CAST(cell); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret = nvmem_cell_read(cell, &data); 668c2ecf20Sopenharmony_ci nvmem_cell_put(cell); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return ret; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * Use this function on devices where slope and offset calculations 738c2ecf20Sopenharmony_ci * depend on calibration data read from qfprom. On others the slope 748c2ecf20Sopenharmony_ci * and offset values are derived from tz->tzp->slope and tz->tzp->offset 758c2ecf20Sopenharmony_ci * resp. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_civoid compute_intercept_slope(struct tsens_priv *priv, u32 *p1, 788c2ecf20Sopenharmony_ci u32 *p2, u32 mode) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int i; 818c2ecf20Sopenharmony_ci int num, den; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_sensors; i++) { 848c2ecf20Sopenharmony_ci dev_dbg(priv->dev, 858c2ecf20Sopenharmony_ci "%s: sensor%d - data_point1:%#x data_point2:%#x\n", 868c2ecf20Sopenharmony_ci __func__, i, p1[i], p2[i]); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci priv->sensor[i].slope = SLOPE_DEFAULT; 898c2ecf20Sopenharmony_ci if (mode == TWO_PT_CALIB) { 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ 928c2ecf20Sopenharmony_ci * temp_120_degc - temp_30_degc (x2 - x1) 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci num = p2[i] - p1[i]; 958c2ecf20Sopenharmony_ci num *= SLOPE_FACTOR; 968c2ecf20Sopenharmony_ci den = CAL_DEGC_PT2 - CAL_DEGC_PT1; 978c2ecf20Sopenharmony_ci priv->sensor[i].slope = num / den; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - 1018c2ecf20Sopenharmony_ci (CAL_DEGC_PT1 * 1028c2ecf20Sopenharmony_ci priv->sensor[i].slope); 1038c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: offset:%d\n", __func__, 1048c2ecf20Sopenharmony_ci priv->sensor[i].offset); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline u32 degc_to_code(int degc, const struct tsens_sensor *s) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc); 1138c2ecf20Sopenharmony_ci return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int degc, num, den; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci num = (adc_code * SLOPE_FACTOR) - s->offset; 1218c2ecf20Sopenharmony_ci den = s->slope; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (num > 0) 1248c2ecf20Sopenharmony_ci degc = num + (den / 2); 1258c2ecf20Sopenharmony_ci else if (num < 0) 1268c2ecf20Sopenharmony_ci degc = num - (den / 2); 1278c2ecf20Sopenharmony_ci else 1288c2ecf20Sopenharmony_ci degc = num; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci degc /= den; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return degc; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/** 1368c2ecf20Sopenharmony_ci * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. 1378c2ecf20Sopenharmony_ci * @s: Pointer to sensor struct 1388c2ecf20Sopenharmony_ci * @field: Index into regmap_field array pointing to temperature data 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * This function handles temperature returned in ADC code or deciCelsius 1418c2ecf20Sopenharmony_ci * depending on IP version. 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * Return: Temperature in milliCelsius on success, a negative errno will 1448c2ecf20Sopenharmony_ci * be returned in error cases 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic int tsens_hw_to_mC(const struct tsens_sensor *s, int field) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct tsens_priv *priv = s->priv; 1498c2ecf20Sopenharmony_ci u32 resolution; 1508c2ecf20Sopenharmony_ci u32 temp = 0; 1518c2ecf20Sopenharmony_ci int ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci resolution = priv->fields[LAST_TEMP_0].msb - 1548c2ecf20Sopenharmony_ci priv->fields[LAST_TEMP_0].lsb; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[field], &temp); 1578c2ecf20Sopenharmony_ci if (ret) 1588c2ecf20Sopenharmony_ci return ret; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Convert temperature from ADC code to milliCelsius */ 1618c2ecf20Sopenharmony_ci if (priv->feat->adc) 1628c2ecf20Sopenharmony_ci return code_to_degc(temp, s) * 1000; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* deciCelsius -> milliCelsius along with sign extension */ 1658c2ecf20Sopenharmony_ci return sign_extend32(temp, resolution) * 100; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/** 1698c2ecf20Sopenharmony_ci * tsens_mC_to_hw - Convert temperature to hardware register value 1708c2ecf20Sopenharmony_ci * @s: Pointer to sensor struct 1718c2ecf20Sopenharmony_ci * @temp: temperature in milliCelsius to be programmed to hardware 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * This function outputs the value to be written to hardware in ADC code 1748c2ecf20Sopenharmony_ci * or deciCelsius depending on IP version. 1758c2ecf20Sopenharmony_ci * 1768c2ecf20Sopenharmony_ci * Return: ADC code or temperature in deciCelsius. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistatic int tsens_mC_to_hw(const struct tsens_sensor *s, int temp) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct tsens_priv *priv = s->priv; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* milliC to adc code */ 1838c2ecf20Sopenharmony_ci if (priv->feat->adc) 1848c2ecf20Sopenharmony_ci return degc_to_code(temp / 1000, s); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* milliC to deciC */ 1878c2ecf20Sopenharmony_ci return temp / 100; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic inline enum tsens_ver tsens_version(struct tsens_priv *priv) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return priv->feat->ver_major; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id, 1968c2ecf20Sopenharmony_ci enum tsens_irq_type irq_type, bool enable) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci u32 index = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci switch (irq_type) { 2018c2ecf20Sopenharmony_ci case UPPER: 2028c2ecf20Sopenharmony_ci index = UP_INT_CLEAR_0 + hw_id; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case LOWER: 2058c2ecf20Sopenharmony_ci index = LOW_INT_CLEAR_0 + hw_id; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci case CRITICAL: 2088c2ecf20Sopenharmony_ci /* No critical interrupts before v2 */ 2098c2ecf20Sopenharmony_ci return; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[index], enable ? 0 : 1); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id, 2158c2ecf20Sopenharmony_ci enum tsens_irq_type irq_type, bool enable) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci u32 index_mask = 0, index_clear = 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* 2208c2ecf20Sopenharmony_ci * To enable the interrupt flag for a sensor: 2218c2ecf20Sopenharmony_ci * - clear the mask bit 2228c2ecf20Sopenharmony_ci * To disable the interrupt flag for a sensor: 2238c2ecf20Sopenharmony_ci * - Mask further interrupts for this sensor 2248c2ecf20Sopenharmony_ci * - Write 1 followed by 0 to clear the interrupt 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci switch (irq_type) { 2278c2ecf20Sopenharmony_ci case UPPER: 2288c2ecf20Sopenharmony_ci index_mask = UP_INT_MASK_0 + hw_id; 2298c2ecf20Sopenharmony_ci index_clear = UP_INT_CLEAR_0 + hw_id; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci case LOWER: 2328c2ecf20Sopenharmony_ci index_mask = LOW_INT_MASK_0 + hw_id; 2338c2ecf20Sopenharmony_ci index_clear = LOW_INT_CLEAR_0 + hw_id; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci case CRITICAL: 2368c2ecf20Sopenharmony_ci index_mask = CRIT_INT_MASK_0 + hw_id; 2378c2ecf20Sopenharmony_ci index_clear = CRIT_INT_CLEAR_0 + hw_id; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (enable) { 2428c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[index_mask], 0); 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[index_mask], 1); 2458c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[index_clear], 1); 2468c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[index_clear], 0); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/** 2518c2ecf20Sopenharmony_ci * tsens_set_interrupt - Set state of an interrupt 2528c2ecf20Sopenharmony_ci * @priv: Pointer to tsens controller private data 2538c2ecf20Sopenharmony_ci * @hw_id: Hardware ID aka. sensor number 2548c2ecf20Sopenharmony_ci * @irq_type: irq_type from enum tsens_irq_type 2558c2ecf20Sopenharmony_ci * @enable: false = disable, true = enable 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * Call IP-specific function to set state of an interrupt 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * Return: void 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_cistatic void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id, 2628c2ecf20Sopenharmony_ci enum tsens_irq_type irq_type, bool enable) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__, 2658c2ecf20Sopenharmony_ci irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW", 2668c2ecf20Sopenharmony_ci enable ? "en" : "dis"); 2678c2ecf20Sopenharmony_ci if (tsens_version(priv) > VER_1_X) 2688c2ecf20Sopenharmony_ci tsens_set_interrupt_v2(priv, hw_id, irq_type, enable); 2698c2ecf20Sopenharmony_ci else 2708c2ecf20Sopenharmony_ci tsens_set_interrupt_v1(priv, hw_id, irq_type, enable); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/** 2748c2ecf20Sopenharmony_ci * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold 2758c2ecf20Sopenharmony_ci * @priv: Pointer to tsens controller private data 2768c2ecf20Sopenharmony_ci * @hw_id: Hardware ID aka. sensor number 2778c2ecf20Sopenharmony_ci * @d: Pointer to irq state data 2788c2ecf20Sopenharmony_ci * 2798c2ecf20Sopenharmony_ci * Return: 0 if threshold was not violated, 1 if it was violated and negative 2808c2ecf20Sopenharmony_ci * errno in case of errors 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_cistatic int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id, 2838c2ecf20Sopenharmony_ci struct tsens_irq_data *d) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int ret; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol); 2888c2ecf20Sopenharmony_ci if (ret) 2898c2ecf20Sopenharmony_ci return ret; 2908c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol); 2918c2ecf20Sopenharmony_ci if (ret) 2928c2ecf20Sopenharmony_ci return ret; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (priv->feat->crit_int) { 2958c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id], 2968c2ecf20Sopenharmony_ci &d->crit_viol); 2978c2ecf20Sopenharmony_ci if (ret) 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (d->up_viol || d->low_viol || d->crit_viol) 3028c2ecf20Sopenharmony_ci return 1; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, 3088c2ecf20Sopenharmony_ci const struct tsens_sensor *s, 3098c2ecf20Sopenharmony_ci struct tsens_irq_data *d) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int ret; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear); 3148c2ecf20Sopenharmony_ci if (ret) 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear); 3178c2ecf20Sopenharmony_ci if (ret) 3188c2ecf20Sopenharmony_ci return ret; 3198c2ecf20Sopenharmony_ci if (tsens_version(priv) > VER_1_X) { 3208c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask); 3218c2ecf20Sopenharmony_ci if (ret) 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask); 3248c2ecf20Sopenharmony_ci if (ret) 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id], 3278c2ecf20Sopenharmony_ci &d->crit_irq_clear); 3288c2ecf20Sopenharmony_ci if (ret) 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id], 3318c2ecf20Sopenharmony_ci &d->crit_irq_mask); 3328c2ecf20Sopenharmony_ci if (ret) 3338c2ecf20Sopenharmony_ci return ret; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id); 3368c2ecf20Sopenharmony_ci } else { 3378c2ecf20Sopenharmony_ci /* No mask register on older TSENS */ 3388c2ecf20Sopenharmony_ci d->up_irq_mask = 0; 3398c2ecf20Sopenharmony_ci d->low_irq_mask = 0; 3408c2ecf20Sopenharmony_ci d->crit_irq_clear = 0; 3418c2ecf20Sopenharmony_ci d->crit_irq_mask = 0; 3428c2ecf20Sopenharmony_ci d->crit_thresh = 0; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id); 3468c2ecf20Sopenharmony_ci d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n", 3498c2ecf20Sopenharmony_ci hw_id, __func__, 3508c2ecf20Sopenharmony_ci (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", 3518c2ecf20Sopenharmony_ci d->low_viol, d->up_viol, d->crit_viol, 3528c2ecf20Sopenharmony_ci d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear, 3538c2ecf20Sopenharmony_ci d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask); 3548c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__, 3558c2ecf20Sopenharmony_ci (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", 3568c2ecf20Sopenharmony_ci d->low_thresh, d->up_thresh, d->crit_thresh); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci if (ver > VER_1_X) 3648c2ecf20Sopenharmony_ci return mask & (1 << hw_id); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* v1, v0.1 don't have a irq mask register */ 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/** 3718c2ecf20Sopenharmony_ci * tsens_critical_irq_thread() - Threaded handler for critical interrupts 3728c2ecf20Sopenharmony_ci * @irq: irq number 3738c2ecf20Sopenharmony_ci * @data: tsens controller private data 3748c2ecf20Sopenharmony_ci * 3758c2ecf20Sopenharmony_ci * Check FSM watchdog bark status and clear if needed. 3768c2ecf20Sopenharmony_ci * Check all sensors to find ones that violated their critical threshold limits. 3778c2ecf20Sopenharmony_ci * Clear and then re-enable the interrupt. 3788c2ecf20Sopenharmony_ci * 3798c2ecf20Sopenharmony_ci * The level-triggered interrupt might deassert if the temperature returned to 3808c2ecf20Sopenharmony_ci * within the threshold limits by the time the handler got scheduled. We 3818c2ecf20Sopenharmony_ci * consider the irq to have been handled in that case. 3828c2ecf20Sopenharmony_ci * 3838c2ecf20Sopenharmony_ci * Return: IRQ_HANDLED 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_cistatic irqreturn_t tsens_critical_irq_thread(int irq, void *data) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct tsens_priv *priv = data; 3888c2ecf20Sopenharmony_ci struct tsens_irq_data d; 3898c2ecf20Sopenharmony_ci int temp, ret, i; 3908c2ecf20Sopenharmony_ci u32 wdog_status, wdog_count; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (priv->feat->has_watchdog) { 3938c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS], 3948c2ecf20Sopenharmony_ci &wdog_status); 3958c2ecf20Sopenharmony_ci if (ret) 3968c2ecf20Sopenharmony_ci return ret; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (wdog_status) { 3998c2ecf20Sopenharmony_ci /* Clear WDOG interrupt */ 4008c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1); 4018c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0); 4028c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT], 4038c2ecf20Sopenharmony_ci &wdog_count); 4048c2ecf20Sopenharmony_ci if (ret) 4058c2ecf20Sopenharmony_ci return ret; 4068c2ecf20Sopenharmony_ci if (wdog_count) 4078c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: watchdog count: %d\n", 4088c2ecf20Sopenharmony_ci __func__, wdog_count); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Fall through to handle critical interrupts if any */ 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_sensors; i++) { 4158c2ecf20Sopenharmony_ci const struct tsens_sensor *s = &priv->sensor[i]; 4168c2ecf20Sopenharmony_ci u32 hw_id = s->hw_id; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (!s->tzd) 4198c2ecf20Sopenharmony_ci continue; 4208c2ecf20Sopenharmony_ci if (!tsens_threshold_violated(priv, hw_id, &d)) 4218c2ecf20Sopenharmony_ci continue; 4228c2ecf20Sopenharmony_ci ret = get_temp_tsens_valid(s, &temp); 4238c2ecf20Sopenharmony_ci if (ret) { 4248c2ecf20Sopenharmony_ci dev_err(priv->dev, "[%u] %s: error reading sensor\n", 4258c2ecf20Sopenharmony_ci hw_id, __func__); 4268c2ecf20Sopenharmony_ci continue; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci tsens_read_irq_state(priv, hw_id, s, &d); 4308c2ecf20Sopenharmony_ci if (d.crit_viol && 4318c2ecf20Sopenharmony_ci !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) { 4328c2ecf20Sopenharmony_ci /* Mask critical interrupts, unused on Linux */ 4338c2ecf20Sopenharmony_ci tsens_set_interrupt(priv, hw_id, CRITICAL, false); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci/** 4418c2ecf20Sopenharmony_ci * tsens_irq_thread - Threaded interrupt handler for uplow interrupts 4428c2ecf20Sopenharmony_ci * @irq: irq number 4438c2ecf20Sopenharmony_ci * @data: tsens controller private data 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * Check all sensors to find ones that violated their threshold limits. If the 4468c2ecf20Sopenharmony_ci * temperature is still outside the limits, call thermal_zone_device_update() to 4478c2ecf20Sopenharmony_ci * update the thresholds, else re-enable the interrupts. 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * The level-triggered interrupt might deassert if the temperature returned to 4508c2ecf20Sopenharmony_ci * within the threshold limits by the time the handler got scheduled. We 4518c2ecf20Sopenharmony_ci * consider the irq to have been handled in that case. 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * Return: IRQ_HANDLED 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_cistatic irqreturn_t tsens_irq_thread(int irq, void *data) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct tsens_priv *priv = data; 4588c2ecf20Sopenharmony_ci struct tsens_irq_data d; 4598c2ecf20Sopenharmony_ci bool enable = true, disable = false; 4608c2ecf20Sopenharmony_ci unsigned long flags; 4618c2ecf20Sopenharmony_ci int temp, ret, i; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_sensors; i++) { 4648c2ecf20Sopenharmony_ci bool trigger = false; 4658c2ecf20Sopenharmony_ci const struct tsens_sensor *s = &priv->sensor[i]; 4668c2ecf20Sopenharmony_ci u32 hw_id = s->hw_id; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (!s->tzd) 4698c2ecf20Sopenharmony_ci continue; 4708c2ecf20Sopenharmony_ci if (!tsens_threshold_violated(priv, hw_id, &d)) 4718c2ecf20Sopenharmony_ci continue; 4728c2ecf20Sopenharmony_ci ret = get_temp_tsens_valid(s, &temp); 4738c2ecf20Sopenharmony_ci if (ret) { 4748c2ecf20Sopenharmony_ci dev_err(priv->dev, "[%u] %s: error reading sensor\n", 4758c2ecf20Sopenharmony_ci hw_id, __func__); 4768c2ecf20Sopenharmony_ci continue; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->ul_lock, flags); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci tsens_read_irq_state(priv, hw_id, s, &d); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (d.up_viol && 4848c2ecf20Sopenharmony_ci !masked_irq(hw_id, d.up_irq_mask, tsens_version(priv))) { 4858c2ecf20Sopenharmony_ci tsens_set_interrupt(priv, hw_id, UPPER, disable); 4868c2ecf20Sopenharmony_ci if (d.up_thresh > temp) { 4878c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "[%u] %s: re-arm upper\n", 4888c2ecf20Sopenharmony_ci hw_id, __func__); 4898c2ecf20Sopenharmony_ci tsens_set_interrupt(priv, hw_id, UPPER, enable); 4908c2ecf20Sopenharmony_ci } else { 4918c2ecf20Sopenharmony_ci trigger = true; 4928c2ecf20Sopenharmony_ci /* Keep irq masked */ 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci } else if (d.low_viol && 4958c2ecf20Sopenharmony_ci !masked_irq(hw_id, d.low_irq_mask, tsens_version(priv))) { 4968c2ecf20Sopenharmony_ci tsens_set_interrupt(priv, hw_id, LOWER, disable); 4978c2ecf20Sopenharmony_ci if (d.low_thresh < temp) { 4988c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "[%u] %s: re-arm low\n", 4998c2ecf20Sopenharmony_ci hw_id, __func__); 5008c2ecf20Sopenharmony_ci tsens_set_interrupt(priv, hw_id, LOWER, enable); 5018c2ecf20Sopenharmony_ci } else { 5028c2ecf20Sopenharmony_ci trigger = true; 5038c2ecf20Sopenharmony_ci /* Keep irq masked */ 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->ul_lock, flags); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (trigger) { 5108c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n", 5118c2ecf20Sopenharmony_ci hw_id, __func__, temp); 5128c2ecf20Sopenharmony_ci thermal_zone_device_update(s->tzd, 5138c2ecf20Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 5148c2ecf20Sopenharmony_ci } else { 5158c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "[%u] %s: no violation: %d\n", 5168c2ecf20Sopenharmony_ci hw_id, __func__, temp); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic int tsens_set_trips(void *_sensor, int low, int high) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct tsens_sensor *s = _sensor; 5268c2ecf20Sopenharmony_ci struct tsens_priv *priv = s->priv; 5278c2ecf20Sopenharmony_ci struct device *dev = priv->dev; 5288c2ecf20Sopenharmony_ci struct tsens_irq_data d; 5298c2ecf20Sopenharmony_ci unsigned long flags; 5308c2ecf20Sopenharmony_ci int high_val, low_val, cl_high, cl_low; 5318c2ecf20Sopenharmony_ci u32 hw_id = s->hw_id; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", 5348c2ecf20Sopenharmony_ci hw_id, __func__, low, high); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci cl_high = clamp_val(high, -40000, 120000); 5378c2ecf20Sopenharmony_ci cl_low = clamp_val(low, -40000, 120000); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci high_val = tsens_mC_to_hw(s, cl_high); 5408c2ecf20Sopenharmony_ci low_val = tsens_mC_to_hw(s, cl_low); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->ul_lock, flags); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci tsens_read_irq_state(priv, hw_id, s, &d); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* Write the new thresholds and clear the status */ 5478c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val); 5488c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val); 5498c2ecf20Sopenharmony_ci tsens_set_interrupt(priv, hw_id, LOWER, true); 5508c2ecf20Sopenharmony_ci tsens_set_interrupt(priv, hw_id, UPPER, true); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->ul_lock, flags); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n", 5558c2ecf20Sopenharmony_ci hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic int tsens_enable_irq(struct tsens_priv *priv) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci int ret; 5638c2ecf20Sopenharmony_ci int val = tsens_version(priv) > VER_1_X ? 7 : 1; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci ret = regmap_field_write(priv->rf[INT_EN], val); 5668c2ecf20Sopenharmony_ci if (ret < 0) 5678c2ecf20Sopenharmony_ci dev_err(priv->dev, "%s: failed to enable interrupts\n", 5688c2ecf20Sopenharmony_ci __func__); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return ret; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic void tsens_disable_irq(struct tsens_priv *priv) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[INT_EN], 0); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ciint get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct tsens_priv *priv = s->priv; 5818c2ecf20Sopenharmony_ci int hw_id = s->hw_id; 5828c2ecf20Sopenharmony_ci u32 temp_idx = LAST_TEMP_0 + hw_id; 5838c2ecf20Sopenharmony_ci u32 valid_idx = VALID_0 + hw_id; 5848c2ecf20Sopenharmony_ci u32 valid; 5858c2ecf20Sopenharmony_ci int ret; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[valid_idx], &valid); 5888c2ecf20Sopenharmony_ci if (ret) 5898c2ecf20Sopenharmony_ci return ret; 5908c2ecf20Sopenharmony_ci while (!valid) { 5918c2ecf20Sopenharmony_ci /* Valid bit is 0 for 6 AHB clock cycles. 5928c2ecf20Sopenharmony_ci * At 19.2MHz, 1 AHB clock is ~60ns. 5938c2ecf20Sopenharmony_ci * We should enter this loop very, very rarely. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_ci ndelay(400); 5968c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[valid_idx], &valid); 5978c2ecf20Sopenharmony_ci if (ret) 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* Valid bit is set, OK to read the temperature */ 6028c2ecf20Sopenharmony_ci *temp = tsens_hw_to_mC(s, temp_idx); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ciint get_temp_common(const struct tsens_sensor *s, int *temp) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct tsens_priv *priv = s->priv; 6108c2ecf20Sopenharmony_ci int hw_id = s->hw_id; 6118c2ecf20Sopenharmony_ci int last_temp = 0, ret; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); 6148c2ecf20Sopenharmony_ci if (ret) 6158c2ecf20Sopenharmony_ci return ret; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci *temp = code_to_degc(last_temp, s) * 1000; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return 0; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 6238c2ecf20Sopenharmony_cistatic int dbg_sensors_show(struct seq_file *s, void *data) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct platform_device *pdev = s->private; 6268c2ecf20Sopenharmony_ci struct tsens_priv *priv = platform_get_drvdata(pdev); 6278c2ecf20Sopenharmony_ci int i; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci seq_printf(s, "max: %2d\nnum: %2d\n\n", 6308c2ecf20Sopenharmony_ci priv->feat->max_sensors, priv->num_sensors); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci seq_puts(s, " id slope offset\n--------------------------\n"); 6338c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_sensors; i++) { 6348c2ecf20Sopenharmony_ci seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id, 6358c2ecf20Sopenharmony_ci priv->sensor[i].slope, priv->sensor[i].offset); 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic int dbg_version_show(struct seq_file *s, void *data) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct platform_device *pdev = s->private; 6448c2ecf20Sopenharmony_ci struct tsens_priv *priv = platform_get_drvdata(pdev); 6458c2ecf20Sopenharmony_ci u32 maj_ver, min_ver, step_ver; 6468c2ecf20Sopenharmony_ci int ret; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (tsens_version(priv) > VER_0_1) { 6498c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver); 6508c2ecf20Sopenharmony_ci if (ret) 6518c2ecf20Sopenharmony_ci return ret; 6528c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver); 6538c2ecf20Sopenharmony_ci if (ret) 6548c2ecf20Sopenharmony_ci return ret; 6558c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[VER_STEP], &step_ver); 6568c2ecf20Sopenharmony_ci if (ret) 6578c2ecf20Sopenharmony_ci return ret; 6588c2ecf20Sopenharmony_ci seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver); 6598c2ecf20Sopenharmony_ci } else { 6608c2ecf20Sopenharmony_ci seq_puts(s, "0.1.0\n"); 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dbg_version); 6678c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dbg_sensors); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic void tsens_debug_init(struct platform_device *pdev) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct tsens_priv *priv = platform_get_drvdata(pdev); 6728c2ecf20Sopenharmony_ci struct dentry *root, *file; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci root = debugfs_lookup("tsens", NULL); 6758c2ecf20Sopenharmony_ci if (!root) 6768c2ecf20Sopenharmony_ci priv->debug_root = debugfs_create_dir("tsens", NULL); 6778c2ecf20Sopenharmony_ci else 6788c2ecf20Sopenharmony_ci priv->debug_root = root; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci file = debugfs_lookup("version", priv->debug_root); 6818c2ecf20Sopenharmony_ci if (!file) 6828c2ecf20Sopenharmony_ci debugfs_create_file("version", 0444, priv->debug_root, 6838c2ecf20Sopenharmony_ci pdev, &dbg_version_fops); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* A directory for each instance of the TSENS IP */ 6868c2ecf20Sopenharmony_ci priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root); 6878c2ecf20Sopenharmony_ci debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops); 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci#else 6908c2ecf20Sopenharmony_cistatic inline void tsens_debug_init(struct platform_device *pdev) {} 6918c2ecf20Sopenharmony_ci#endif 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic const struct regmap_config tsens_config = { 6948c2ecf20Sopenharmony_ci .name = "tm", 6958c2ecf20Sopenharmony_ci .reg_bits = 32, 6968c2ecf20Sopenharmony_ci .val_bits = 32, 6978c2ecf20Sopenharmony_ci .reg_stride = 4, 6988c2ecf20Sopenharmony_ci}; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic const struct regmap_config tsens_srot_config = { 7018c2ecf20Sopenharmony_ci .name = "srot", 7028c2ecf20Sopenharmony_ci .reg_bits = 32, 7038c2ecf20Sopenharmony_ci .val_bits = 32, 7048c2ecf20Sopenharmony_ci .reg_stride = 4, 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ciint __init init_common(struct tsens_priv *priv) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci void __iomem *tm_base, *srot_base; 7108c2ecf20Sopenharmony_ci struct device *dev = priv->dev; 7118c2ecf20Sopenharmony_ci u32 ver_minor; 7128c2ecf20Sopenharmony_ci struct resource *res; 7138c2ecf20Sopenharmony_ci u32 enabled; 7148c2ecf20Sopenharmony_ci int ret, i, j; 7158c2ecf20Sopenharmony_ci struct platform_device *op = of_find_device_by_node(priv->dev->of_node); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (!op) 7188c2ecf20Sopenharmony_ci return -EINVAL; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (op->num_resources > 1) { 7218c2ecf20Sopenharmony_ci /* DT with separate SROT and TM address space */ 7228c2ecf20Sopenharmony_ci priv->tm_offset = 0; 7238c2ecf20Sopenharmony_ci res = platform_get_resource(op, IORESOURCE_MEM, 1); 7248c2ecf20Sopenharmony_ci srot_base = devm_ioremap_resource(dev, res); 7258c2ecf20Sopenharmony_ci if (IS_ERR(srot_base)) { 7268c2ecf20Sopenharmony_ci ret = PTR_ERR(srot_base); 7278c2ecf20Sopenharmony_ci goto err_put_device; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci priv->srot_map = devm_regmap_init_mmio(dev, srot_base, 7318c2ecf20Sopenharmony_ci &tsens_srot_config); 7328c2ecf20Sopenharmony_ci if (IS_ERR(priv->srot_map)) { 7338c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->srot_map); 7348c2ecf20Sopenharmony_ci goto err_put_device; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci } else { 7378c2ecf20Sopenharmony_ci /* old DTs where SROT and TM were in a contiguous 2K block */ 7388c2ecf20Sopenharmony_ci priv->tm_offset = 0x1000; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci res = platform_get_resource(op, IORESOURCE_MEM, 0); 7428c2ecf20Sopenharmony_ci tm_base = devm_ioremap_resource(dev, res); 7438c2ecf20Sopenharmony_ci if (IS_ERR(tm_base)) { 7448c2ecf20Sopenharmony_ci ret = PTR_ERR(tm_base); 7458c2ecf20Sopenharmony_ci goto err_put_device; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); 7498c2ecf20Sopenharmony_ci if (IS_ERR(priv->tm_map)) { 7508c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->tm_map); 7518c2ecf20Sopenharmony_ci goto err_put_device; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (tsens_version(priv) > VER_0_1) { 7558c2ecf20Sopenharmony_ci for (i = VER_MAJOR; i <= VER_STEP; i++) { 7568c2ecf20Sopenharmony_ci priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map, 7578c2ecf20Sopenharmony_ci priv->fields[i]); 7588c2ecf20Sopenharmony_ci if (IS_ERR(priv->rf[i])) { 7598c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->rf[i]); 7608c2ecf20Sopenharmony_ci goto err_put_device; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor); 7648c2ecf20Sopenharmony_ci if (ret) 7658c2ecf20Sopenharmony_ci goto err_put_device; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map, 7698c2ecf20Sopenharmony_ci priv->fields[TSENS_EN]); 7708c2ecf20Sopenharmony_ci if (IS_ERR(priv->rf[TSENS_EN])) { 7718c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->rf[TSENS_EN]); 7728c2ecf20Sopenharmony_ci goto err_put_device; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); 7758c2ecf20Sopenharmony_ci if (ret) 7768c2ecf20Sopenharmony_ci goto err_put_device; 7778c2ecf20Sopenharmony_ci if (!enabled) { 7788c2ecf20Sopenharmony_ci dev_err(dev, "%s: device not enabled\n", __func__); 7798c2ecf20Sopenharmony_ci ret = -ENODEV; 7808c2ecf20Sopenharmony_ci goto err_put_device; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map, 7848c2ecf20Sopenharmony_ci priv->fields[SENSOR_EN]); 7858c2ecf20Sopenharmony_ci if (IS_ERR(priv->rf[SENSOR_EN])) { 7868c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->rf[SENSOR_EN]); 7878c2ecf20Sopenharmony_ci goto err_put_device; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map, 7908c2ecf20Sopenharmony_ci priv->fields[INT_EN]); 7918c2ecf20Sopenharmony_ci if (IS_ERR(priv->rf[INT_EN])) { 7928c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->rf[INT_EN]); 7938c2ecf20Sopenharmony_ci goto err_put_device; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* This loop might need changes if enum regfield_ids is reordered */ 7978c2ecf20Sopenharmony_ci for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) { 7988c2ecf20Sopenharmony_ci for (i = 0; i < priv->feat->max_sensors; i++) { 7998c2ecf20Sopenharmony_ci int idx = j + i; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci priv->rf[idx] = devm_regmap_field_alloc(dev, 8028c2ecf20Sopenharmony_ci priv->tm_map, 8038c2ecf20Sopenharmony_ci priv->fields[idx]); 8048c2ecf20Sopenharmony_ci if (IS_ERR(priv->rf[idx])) { 8058c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->rf[idx]); 8068c2ecf20Sopenharmony_ci goto err_put_device; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (priv->feat->crit_int) { 8128c2ecf20Sopenharmony_ci /* Loop might need changes if enum regfield_ids is reordered */ 8138c2ecf20Sopenharmony_ci for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) { 8148c2ecf20Sopenharmony_ci for (i = 0; i < priv->feat->max_sensors; i++) { 8158c2ecf20Sopenharmony_ci int idx = j + i; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci priv->rf[idx] = 8188c2ecf20Sopenharmony_ci devm_regmap_field_alloc(dev, 8198c2ecf20Sopenharmony_ci priv->tm_map, 8208c2ecf20Sopenharmony_ci priv->fields[idx]); 8218c2ecf20Sopenharmony_ci if (IS_ERR(priv->rf[idx])) { 8228c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->rf[idx]); 8238c2ecf20Sopenharmony_ci goto err_put_device; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (tsens_version(priv) > VER_1_X && ver_minor > 2) { 8308c2ecf20Sopenharmony_ci /* Watchdog is present only on v2.3+ */ 8318c2ecf20Sopenharmony_ci priv->feat->has_watchdog = 1; 8328c2ecf20Sopenharmony_ci for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) { 8338c2ecf20Sopenharmony_ci priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map, 8348c2ecf20Sopenharmony_ci priv->fields[i]); 8358c2ecf20Sopenharmony_ci if (IS_ERR(priv->rf[i])) { 8368c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->rf[i]); 8378c2ecf20Sopenharmony_ci goto err_put_device; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci /* 8418c2ecf20Sopenharmony_ci * Watchdog is already enabled, unmask the bark. 8428c2ecf20Sopenharmony_ci * Disable cycle completion monitoring 8438c2ecf20Sopenharmony_ci */ 8448c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[WDOG_BARK_MASK], 0); 8458c2ecf20Sopenharmony_ci regmap_field_write(priv->rf[CC_MON_MASK], 1); 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci spin_lock_init(&priv->ul_lock); 8498c2ecf20Sopenharmony_ci tsens_enable_irq(priv); 8508c2ecf20Sopenharmony_ci tsens_debug_init(op); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cierr_put_device: 8538c2ecf20Sopenharmony_ci put_device(&op->dev); 8548c2ecf20Sopenharmony_ci return ret; 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic int tsens_get_temp(void *data, int *temp) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct tsens_sensor *s = data; 8608c2ecf20Sopenharmony_ci struct tsens_priv *priv = s->priv; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci return priv->ops->get_temp(s, temp); 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic int tsens_get_trend(void *data, int trip, enum thermal_trend *trend) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct tsens_sensor *s = data; 8688c2ecf20Sopenharmony_ci struct tsens_priv *priv = s->priv; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (priv->ops->get_trend) 8718c2ecf20Sopenharmony_ci return priv->ops->get_trend(s, trend); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci return -ENOTSUPP; 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic int __maybe_unused tsens_suspend(struct device *dev) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci struct tsens_priv *priv = dev_get_drvdata(dev); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (priv->ops && priv->ops->suspend) 8818c2ecf20Sopenharmony_ci return priv->ops->suspend(priv); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic int __maybe_unused tsens_resume(struct device *dev) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct tsens_priv *priv = dev_get_drvdata(dev); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (priv->ops && priv->ops->resume) 8918c2ecf20Sopenharmony_ci return priv->ops->resume(priv); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci return 0; 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic const struct of_device_id tsens_table[] = { 8998c2ecf20Sopenharmony_ci { 9008c2ecf20Sopenharmony_ci .compatible = "qcom,msm8916-tsens", 9018c2ecf20Sopenharmony_ci .data = &data_8916, 9028c2ecf20Sopenharmony_ci }, { 9038c2ecf20Sopenharmony_ci .compatible = "qcom,msm8939-tsens", 9048c2ecf20Sopenharmony_ci .data = &data_8939, 9058c2ecf20Sopenharmony_ci }, { 9068c2ecf20Sopenharmony_ci .compatible = "qcom,msm8956-tsens", 9078c2ecf20Sopenharmony_ci .data = &data_8956, 9088c2ecf20Sopenharmony_ci }, { 9098c2ecf20Sopenharmony_ci .compatible = "qcom,msm8960-tsens", 9108c2ecf20Sopenharmony_ci .data = &data_8960, 9118c2ecf20Sopenharmony_ci }, { 9128c2ecf20Sopenharmony_ci .compatible = "qcom,msm8974-tsens", 9138c2ecf20Sopenharmony_ci .data = &data_8974, 9148c2ecf20Sopenharmony_ci }, { 9158c2ecf20Sopenharmony_ci .compatible = "qcom,msm8976-tsens", 9168c2ecf20Sopenharmony_ci .data = &data_8976, 9178c2ecf20Sopenharmony_ci }, { 9188c2ecf20Sopenharmony_ci .compatible = "qcom,msm8996-tsens", 9198c2ecf20Sopenharmony_ci .data = &data_8996, 9208c2ecf20Sopenharmony_ci }, { 9218c2ecf20Sopenharmony_ci .compatible = "qcom,tsens-v1", 9228c2ecf20Sopenharmony_ci .data = &data_tsens_v1, 9238c2ecf20Sopenharmony_ci }, { 9248c2ecf20Sopenharmony_ci .compatible = "qcom,tsens-v2", 9258c2ecf20Sopenharmony_ci .data = &data_tsens_v2, 9268c2ecf20Sopenharmony_ci }, 9278c2ecf20Sopenharmony_ci {} 9288c2ecf20Sopenharmony_ci}; 9298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tsens_table); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic const struct thermal_zone_of_device_ops tsens_of_ops = { 9328c2ecf20Sopenharmony_ci .get_temp = tsens_get_temp, 9338c2ecf20Sopenharmony_ci .get_trend = tsens_get_trend, 9348c2ecf20Sopenharmony_ci .set_trips = tsens_set_trips, 9358c2ecf20Sopenharmony_ci}; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int tsens_register_irq(struct tsens_priv *priv, char *irqname, 9388c2ecf20Sopenharmony_ci irq_handler_t thread_fn) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct platform_device *pdev; 9418c2ecf20Sopenharmony_ci int ret, irq; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci pdev = of_find_device_by_node(priv->dev->of_node); 9448c2ecf20Sopenharmony_ci if (!pdev) 9458c2ecf20Sopenharmony_ci return -ENODEV; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, irqname); 9488c2ecf20Sopenharmony_ci if (irq < 0) { 9498c2ecf20Sopenharmony_ci ret = irq; 9508c2ecf20Sopenharmony_ci /* For old DTs with no IRQ defined */ 9518c2ecf20Sopenharmony_ci if (irq == -ENXIO) 9528c2ecf20Sopenharmony_ci ret = 0; 9538c2ecf20Sopenharmony_ci } else { 9548c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, 9558c2ecf20Sopenharmony_ci NULL, thread_fn, 9568c2ecf20Sopenharmony_ci IRQF_ONESHOT, 9578c2ecf20Sopenharmony_ci dev_name(&pdev->dev), priv); 9588c2ecf20Sopenharmony_ci if (ret) 9598c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s: failed to get irq\n", 9608c2ecf20Sopenharmony_ci __func__); 9618c2ecf20Sopenharmony_ci else 9628c2ecf20Sopenharmony_ci enable_irq_wake(irq); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci put_device(&pdev->dev); 9668c2ecf20Sopenharmony_ci return ret; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic int tsens_register(struct tsens_priv *priv) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci int i, ret; 9728c2ecf20Sopenharmony_ci struct thermal_zone_device *tzd; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_sensors; i++) { 9758c2ecf20Sopenharmony_ci priv->sensor[i].priv = priv; 9768c2ecf20Sopenharmony_ci tzd = devm_thermal_zone_of_sensor_register(priv->dev, priv->sensor[i].hw_id, 9778c2ecf20Sopenharmony_ci &priv->sensor[i], 9788c2ecf20Sopenharmony_ci &tsens_of_ops); 9798c2ecf20Sopenharmony_ci if (IS_ERR(tzd)) 9808c2ecf20Sopenharmony_ci continue; 9818c2ecf20Sopenharmony_ci priv->sensor[i].tzd = tzd; 9828c2ecf20Sopenharmony_ci if (priv->ops->enable) 9838c2ecf20Sopenharmony_ci priv->ops->enable(priv, i); 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); 9878c2ecf20Sopenharmony_ci if (ret < 0) 9888c2ecf20Sopenharmony_ci return ret; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (priv->feat->crit_int) 9918c2ecf20Sopenharmony_ci ret = tsens_register_irq(priv, "critical", 9928c2ecf20Sopenharmony_ci tsens_critical_irq_thread); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci return ret; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic int tsens_probe(struct platform_device *pdev) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci int ret, i; 10008c2ecf20Sopenharmony_ci struct device *dev; 10018c2ecf20Sopenharmony_ci struct device_node *np; 10028c2ecf20Sopenharmony_ci struct tsens_priv *priv; 10038c2ecf20Sopenharmony_ci const struct tsens_plat_data *data; 10048c2ecf20Sopenharmony_ci const struct of_device_id *id; 10058c2ecf20Sopenharmony_ci u32 num_sensors; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (pdev->dev.of_node) 10088c2ecf20Sopenharmony_ci dev = &pdev->dev; 10098c2ecf20Sopenharmony_ci else 10108c2ecf20Sopenharmony_ci dev = pdev->dev.parent; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci np = dev->of_node; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci id = of_match_node(tsens_table, np); 10158c2ecf20Sopenharmony_ci if (id) 10168c2ecf20Sopenharmony_ci data = id->data; 10178c2ecf20Sopenharmony_ci else 10188c2ecf20Sopenharmony_ci data = &data_8960; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci num_sensors = data->num_sensors; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (np) 10238c2ecf20Sopenharmony_ci of_property_read_u32(np, "#qcom,sensors", &num_sensors); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (num_sensors <= 0) { 10268c2ecf20Sopenharmony_ci dev_err(dev, "%s: invalid number of sensors\n", __func__); 10278c2ecf20Sopenharmony_ci return -EINVAL; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, 10318c2ecf20Sopenharmony_ci struct_size(priv, sensor, num_sensors), 10328c2ecf20Sopenharmony_ci GFP_KERNEL); 10338c2ecf20Sopenharmony_ci if (!priv) 10348c2ecf20Sopenharmony_ci return -ENOMEM; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci priv->dev = dev; 10378c2ecf20Sopenharmony_ci priv->num_sensors = num_sensors; 10388c2ecf20Sopenharmony_ci priv->ops = data->ops; 10398c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_sensors; i++) { 10408c2ecf20Sopenharmony_ci if (data->hw_ids) 10418c2ecf20Sopenharmony_ci priv->sensor[i].hw_id = data->hw_ids[i]; 10428c2ecf20Sopenharmony_ci else 10438c2ecf20Sopenharmony_ci priv->sensor[i].hw_id = i; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci priv->feat = data->feat; 10468c2ecf20Sopenharmony_ci priv->fields = data->fields; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) 10518c2ecf20Sopenharmony_ci return -EINVAL; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci ret = priv->ops->init(priv); 10548c2ecf20Sopenharmony_ci if (ret < 0) { 10558c2ecf20Sopenharmony_ci dev_err(dev, "%s: init failed\n", __func__); 10568c2ecf20Sopenharmony_ci return ret; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (priv->ops->calibrate) { 10608c2ecf20Sopenharmony_ci ret = priv->ops->calibrate(priv); 10618c2ecf20Sopenharmony_ci if (ret < 0) { 10628c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 10638c2ecf20Sopenharmony_ci dev_err(dev, "%s: calibration failed\n", __func__); 10648c2ecf20Sopenharmony_ci return ret; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci return tsens_register(priv); 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cistatic int tsens_remove(struct platform_device *pdev) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci struct tsens_priv *priv = platform_get_drvdata(pdev); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci debugfs_remove_recursive(priv->debug_root); 10768c2ecf20Sopenharmony_ci tsens_disable_irq(priv); 10778c2ecf20Sopenharmony_ci if (priv->ops->disable) 10788c2ecf20Sopenharmony_ci priv->ops->disable(priv); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic struct platform_driver tsens_driver = { 10848c2ecf20Sopenharmony_ci .probe = tsens_probe, 10858c2ecf20Sopenharmony_ci .remove = tsens_remove, 10868c2ecf20Sopenharmony_ci .driver = { 10878c2ecf20Sopenharmony_ci .name = "qcom-tsens", 10888c2ecf20Sopenharmony_ci .pm = &tsens_pm_ops, 10898c2ecf20Sopenharmony_ci .of_match_table = tsens_table, 10908c2ecf20Sopenharmony_ci }, 10918c2ecf20Sopenharmony_ci}; 10928c2ecf20Sopenharmony_cimodule_platform_driver(tsens_driver); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 10958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QCOM Temperature Sensor driver"); 10968c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:qcom-tsens"); 1097