12d71d8deSAmit Kucheria // SPDX-License-Identifier: GPL-2.0 29066073cSRajendra Nayak /* 39066073cSRajendra Nayak * Copyright (c) 2015, The Linux Foundation. All rights reserved. 4a7ff8297SAmit Kucheria * Copyright (c) 2019, 2020, Linaro Ltd. 59066073cSRajendra Nayak */ 69066073cSRajendra Nayak 77c938f48SAmit Kucheria #include <linux/debugfs.h> 89066073cSRajendra Nayak #include <linux/err.h> 9a7ff8297SAmit Kucheria #include <linux/io.h> 109066073cSRajendra Nayak #include <linux/module.h> 11a7ff8297SAmit Kucheria #include <linux/nvmem-consumer.h> 129066073cSRajendra Nayak #include <linux/of.h> 13a7ff8297SAmit Kucheria #include <linux/of_address.h> 14634e11d5SAmit Kucheria #include <linux/of_platform.h> 1553e2a20eSAnsuel Smith #include <linux/mfd/syscon.h> 169066073cSRajendra Nayak #include <linux/platform_device.h> 179066073cSRajendra Nayak #include <linux/pm.h> 18a7ff8297SAmit Kucheria #include <linux/regmap.h> 199066073cSRajendra Nayak #include <linux/slab.h> 209066073cSRajendra Nayak #include <linux/thermal.h> 219066073cSRajendra Nayak #include "tsens.h" 229066073cSRajendra Nayak 23a7ff8297SAmit Kucheria /** 24a7ff8297SAmit Kucheria * struct tsens_irq_data - IRQ status and temperature violations 25a7ff8297SAmit Kucheria * @up_viol: upper threshold violated 26a7ff8297SAmit Kucheria * @up_thresh: upper threshold temperature value 27a7ff8297SAmit Kucheria * @up_irq_mask: mask register for upper threshold irqs 28a7ff8297SAmit Kucheria * @up_irq_clear: clear register for uppper threshold irqs 29a7ff8297SAmit Kucheria * @low_viol: lower threshold violated 30a7ff8297SAmit Kucheria * @low_thresh: lower threshold temperature value 31a7ff8297SAmit Kucheria * @low_irq_mask: mask register for lower threshold irqs 32a7ff8297SAmit Kucheria * @low_irq_clear: clear register for lower threshold irqs 33a7ff8297SAmit Kucheria * @crit_viol: critical threshold violated 34a7ff8297SAmit Kucheria * @crit_thresh: critical threshold temperature value 35a7ff8297SAmit Kucheria * @crit_irq_mask: mask register for critical threshold irqs 36a7ff8297SAmit Kucheria * @crit_irq_clear: clear register for critical threshold irqs 37a7ff8297SAmit Kucheria * 38a7ff8297SAmit Kucheria * Structure containing data about temperature threshold settings and 39a7ff8297SAmit Kucheria * irq status if they were violated. 40a7ff8297SAmit Kucheria */ 41a7ff8297SAmit Kucheria struct tsens_irq_data { 42a7ff8297SAmit Kucheria u32 up_viol; 43a7ff8297SAmit Kucheria int up_thresh; 44a7ff8297SAmit Kucheria u32 up_irq_mask; 45a7ff8297SAmit Kucheria u32 up_irq_clear; 46a7ff8297SAmit Kucheria u32 low_viol; 47a7ff8297SAmit Kucheria int low_thresh; 48a7ff8297SAmit Kucheria u32 low_irq_mask; 49a7ff8297SAmit Kucheria u32 low_irq_clear; 50a7ff8297SAmit Kucheria u32 crit_viol; 51a7ff8297SAmit Kucheria u32 crit_thresh; 52a7ff8297SAmit Kucheria u32 crit_irq_mask; 53a7ff8297SAmit Kucheria u32 crit_irq_clear; 54a7ff8297SAmit Kucheria }; 55a7ff8297SAmit Kucheria 56a7ff8297SAmit Kucheria char *qfprom_read(struct device *dev, const char *cname) 57a7ff8297SAmit Kucheria { 58a7ff8297SAmit Kucheria struct nvmem_cell *cell; 59a7ff8297SAmit Kucheria ssize_t data; 60a7ff8297SAmit Kucheria char *ret; 61a7ff8297SAmit Kucheria 62a7ff8297SAmit Kucheria cell = nvmem_cell_get(dev, cname); 63a7ff8297SAmit Kucheria if (IS_ERR(cell)) 64a7ff8297SAmit Kucheria return ERR_CAST(cell); 65a7ff8297SAmit Kucheria 66a7ff8297SAmit Kucheria ret = nvmem_cell_read(cell, &data); 67a7ff8297SAmit Kucheria nvmem_cell_put(cell); 68a7ff8297SAmit Kucheria 69a7ff8297SAmit Kucheria return ret; 70a7ff8297SAmit Kucheria } 71a7ff8297SAmit Kucheria 72a7ff8297SAmit Kucheria /* 73a7ff8297SAmit Kucheria * Use this function on devices where slope and offset calculations 74a7ff8297SAmit Kucheria * depend on calibration data read from qfprom. On others the slope 75a7ff8297SAmit Kucheria * and offset values are derived from tz->tzp->slope and tz->tzp->offset 76a7ff8297SAmit Kucheria * resp. 77a7ff8297SAmit Kucheria */ 78a7ff8297SAmit Kucheria void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, 79a7ff8297SAmit Kucheria u32 *p2, u32 mode) 80a7ff8297SAmit Kucheria { 81a7ff8297SAmit Kucheria int i; 82a7ff8297SAmit Kucheria int num, den; 83a7ff8297SAmit Kucheria 84a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 85a7ff8297SAmit Kucheria dev_dbg(priv->dev, 86a7ff8297SAmit Kucheria "%s: sensor%d - data_point1:%#x data_point2:%#x\n", 87a7ff8297SAmit Kucheria __func__, i, p1[i], p2[i]); 88a7ff8297SAmit Kucheria 899d51769bSAnsuel Smith if (!priv->sensor[i].slope) 90a7ff8297SAmit Kucheria priv->sensor[i].slope = SLOPE_DEFAULT; 91a7ff8297SAmit Kucheria if (mode == TWO_PT_CALIB) { 92a7ff8297SAmit Kucheria /* 93a7ff8297SAmit Kucheria * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ 94a7ff8297SAmit Kucheria * temp_120_degc - temp_30_degc (x2 - x1) 95a7ff8297SAmit Kucheria */ 96a7ff8297SAmit Kucheria num = p2[i] - p1[i]; 97a7ff8297SAmit Kucheria num *= SLOPE_FACTOR; 98a7ff8297SAmit Kucheria den = CAL_DEGC_PT2 - CAL_DEGC_PT1; 99a7ff8297SAmit Kucheria priv->sensor[i].slope = num / den; 100a7ff8297SAmit Kucheria } 101a7ff8297SAmit Kucheria 102a7ff8297SAmit Kucheria priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - 103a7ff8297SAmit Kucheria (CAL_DEGC_PT1 * 104a7ff8297SAmit Kucheria priv->sensor[i].slope); 105a7ff8297SAmit Kucheria dev_dbg(priv->dev, "%s: offset:%d\n", __func__, 106a7ff8297SAmit Kucheria priv->sensor[i].offset); 107a7ff8297SAmit Kucheria } 108a7ff8297SAmit Kucheria } 109a7ff8297SAmit Kucheria 110a7ff8297SAmit Kucheria static inline u32 degc_to_code(int degc, const struct tsens_sensor *s) 111a7ff8297SAmit Kucheria { 112a7ff8297SAmit Kucheria u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR); 113a7ff8297SAmit Kucheria 114a7ff8297SAmit Kucheria pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc); 115a7ff8297SAmit Kucheria return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE); 116a7ff8297SAmit Kucheria } 117a7ff8297SAmit Kucheria 118a7ff8297SAmit Kucheria static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) 119a7ff8297SAmit Kucheria { 120a7ff8297SAmit Kucheria int degc, num, den; 121a7ff8297SAmit Kucheria 122a7ff8297SAmit Kucheria num = (adc_code * SLOPE_FACTOR) - s->offset; 123a7ff8297SAmit Kucheria den = s->slope; 124a7ff8297SAmit Kucheria 125a7ff8297SAmit Kucheria if (num > 0) 126a7ff8297SAmit Kucheria degc = num + (den / 2); 127a7ff8297SAmit Kucheria else if (num < 0) 128a7ff8297SAmit Kucheria degc = num - (den / 2); 129a7ff8297SAmit Kucheria else 130a7ff8297SAmit Kucheria degc = num; 131a7ff8297SAmit Kucheria 132a7ff8297SAmit Kucheria degc /= den; 133a7ff8297SAmit Kucheria 134a7ff8297SAmit Kucheria return degc; 135a7ff8297SAmit Kucheria } 136a7ff8297SAmit Kucheria 137a7ff8297SAmit Kucheria /** 138a7ff8297SAmit Kucheria * tsens_hw_to_mC - Return sign-extended temperature in mCelsius. 139a7ff8297SAmit Kucheria * @s: Pointer to sensor struct 140a7ff8297SAmit Kucheria * @field: Index into regmap_field array pointing to temperature data 141a7ff8297SAmit Kucheria * 142a7ff8297SAmit Kucheria * This function handles temperature returned in ADC code or deciCelsius 143a7ff8297SAmit Kucheria * depending on IP version. 144a7ff8297SAmit Kucheria * 145a7ff8297SAmit Kucheria * Return: Temperature in milliCelsius on success, a negative errno will 146a7ff8297SAmit Kucheria * be returned in error cases 147a7ff8297SAmit Kucheria */ 148a7ff8297SAmit Kucheria static int tsens_hw_to_mC(const struct tsens_sensor *s, int field) 149a7ff8297SAmit Kucheria { 150a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 151a7ff8297SAmit Kucheria u32 resolution; 152a7ff8297SAmit Kucheria u32 temp = 0; 153a7ff8297SAmit Kucheria int ret; 154a7ff8297SAmit Kucheria 155a7ff8297SAmit Kucheria resolution = priv->fields[LAST_TEMP_0].msb - 156a7ff8297SAmit Kucheria priv->fields[LAST_TEMP_0].lsb; 157a7ff8297SAmit Kucheria 158a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[field], &temp); 159a7ff8297SAmit Kucheria if (ret) 160a7ff8297SAmit Kucheria return ret; 161a7ff8297SAmit Kucheria 162a7ff8297SAmit Kucheria /* Convert temperature from ADC code to milliCelsius */ 163a7ff8297SAmit Kucheria if (priv->feat->adc) 164a7ff8297SAmit Kucheria return code_to_degc(temp, s) * 1000; 165a7ff8297SAmit Kucheria 166a7ff8297SAmit Kucheria /* deciCelsius -> milliCelsius along with sign extension */ 167a7ff8297SAmit Kucheria return sign_extend32(temp, resolution) * 100; 168a7ff8297SAmit Kucheria } 169a7ff8297SAmit Kucheria 170a7ff8297SAmit Kucheria /** 171a7ff8297SAmit Kucheria * tsens_mC_to_hw - Convert temperature to hardware register value 172a7ff8297SAmit Kucheria * @s: Pointer to sensor struct 173a7ff8297SAmit Kucheria * @temp: temperature in milliCelsius to be programmed to hardware 174a7ff8297SAmit Kucheria * 175a7ff8297SAmit Kucheria * This function outputs the value to be written to hardware in ADC code 176a7ff8297SAmit Kucheria * or deciCelsius depending on IP version. 177a7ff8297SAmit Kucheria * 178a7ff8297SAmit Kucheria * Return: ADC code or temperature in deciCelsius. 179a7ff8297SAmit Kucheria */ 180a7ff8297SAmit Kucheria static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp) 181a7ff8297SAmit Kucheria { 182a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 183a7ff8297SAmit Kucheria 184a7ff8297SAmit Kucheria /* milliC to adc code */ 185a7ff8297SAmit Kucheria if (priv->feat->adc) 186a7ff8297SAmit Kucheria return degc_to_code(temp / 1000, s); 187a7ff8297SAmit Kucheria 188a7ff8297SAmit Kucheria /* milliC to deciC */ 189a7ff8297SAmit Kucheria return temp / 100; 190a7ff8297SAmit Kucheria } 191a7ff8297SAmit Kucheria 192a7ff8297SAmit Kucheria static inline enum tsens_ver tsens_version(struct tsens_priv *priv) 193a7ff8297SAmit Kucheria { 194a7ff8297SAmit Kucheria return priv->feat->ver_major; 195a7ff8297SAmit Kucheria } 196a7ff8297SAmit Kucheria 197a7ff8297SAmit Kucheria static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id, 198a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable) 199a7ff8297SAmit Kucheria { 200a7ff8297SAmit Kucheria u32 index = 0; 201a7ff8297SAmit Kucheria 202a7ff8297SAmit Kucheria switch (irq_type) { 203a7ff8297SAmit Kucheria case UPPER: 204a7ff8297SAmit Kucheria index = UP_INT_CLEAR_0 + hw_id; 205a7ff8297SAmit Kucheria break; 206a7ff8297SAmit Kucheria case LOWER: 207a7ff8297SAmit Kucheria index = LOW_INT_CLEAR_0 + hw_id; 208a7ff8297SAmit Kucheria break; 209a7ff8297SAmit Kucheria case CRITICAL: 210a7ff8297SAmit Kucheria /* No critical interrupts before v2 */ 211a7ff8297SAmit Kucheria return; 212a7ff8297SAmit Kucheria } 213a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index], enable ? 0 : 1); 214a7ff8297SAmit Kucheria } 215a7ff8297SAmit Kucheria 216a7ff8297SAmit Kucheria static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id, 217a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable) 218a7ff8297SAmit Kucheria { 219a7ff8297SAmit Kucheria u32 index_mask = 0, index_clear = 0; 220a7ff8297SAmit Kucheria 221a7ff8297SAmit Kucheria /* 222a7ff8297SAmit Kucheria * To enable the interrupt flag for a sensor: 223a7ff8297SAmit Kucheria * - clear the mask bit 224a7ff8297SAmit Kucheria * To disable the interrupt flag for a sensor: 225a7ff8297SAmit Kucheria * - Mask further interrupts for this sensor 226a7ff8297SAmit Kucheria * - Write 1 followed by 0 to clear the interrupt 227a7ff8297SAmit Kucheria */ 228a7ff8297SAmit Kucheria switch (irq_type) { 229a7ff8297SAmit Kucheria case UPPER: 230a7ff8297SAmit Kucheria index_mask = UP_INT_MASK_0 + hw_id; 231a7ff8297SAmit Kucheria index_clear = UP_INT_CLEAR_0 + hw_id; 232a7ff8297SAmit Kucheria break; 233a7ff8297SAmit Kucheria case LOWER: 234a7ff8297SAmit Kucheria index_mask = LOW_INT_MASK_0 + hw_id; 235a7ff8297SAmit Kucheria index_clear = LOW_INT_CLEAR_0 + hw_id; 236a7ff8297SAmit Kucheria break; 237a7ff8297SAmit Kucheria case CRITICAL: 238a7ff8297SAmit Kucheria index_mask = CRIT_INT_MASK_0 + hw_id; 239a7ff8297SAmit Kucheria index_clear = CRIT_INT_CLEAR_0 + hw_id; 240a7ff8297SAmit Kucheria break; 241a7ff8297SAmit Kucheria } 242a7ff8297SAmit Kucheria 243a7ff8297SAmit Kucheria if (enable) { 244a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_mask], 0); 245a7ff8297SAmit Kucheria } else { 246a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_mask], 1); 247a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_clear], 1); 248a7ff8297SAmit Kucheria regmap_field_write(priv->rf[index_clear], 0); 249a7ff8297SAmit Kucheria } 250a7ff8297SAmit Kucheria } 251a7ff8297SAmit Kucheria 252a7ff8297SAmit Kucheria /** 253a7ff8297SAmit Kucheria * tsens_set_interrupt - Set state of an interrupt 254a7ff8297SAmit Kucheria * @priv: Pointer to tsens controller private data 255a7ff8297SAmit Kucheria * @hw_id: Hardware ID aka. sensor number 256a7ff8297SAmit Kucheria * @irq_type: irq_type from enum tsens_irq_type 257a7ff8297SAmit Kucheria * @enable: false = disable, true = enable 258a7ff8297SAmit Kucheria * 259a7ff8297SAmit Kucheria * Call IP-specific function to set state of an interrupt 260a7ff8297SAmit Kucheria * 261a7ff8297SAmit Kucheria * Return: void 262a7ff8297SAmit Kucheria */ 263a7ff8297SAmit Kucheria static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id, 264a7ff8297SAmit Kucheria enum tsens_irq_type irq_type, bool enable) 265a7ff8297SAmit Kucheria { 266a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__, 267a7ff8297SAmit Kucheria irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW", 268a7ff8297SAmit Kucheria enable ? "en" : "dis"); 269a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X) 270a7ff8297SAmit Kucheria tsens_set_interrupt_v2(priv, hw_id, irq_type, enable); 271a7ff8297SAmit Kucheria else 272a7ff8297SAmit Kucheria tsens_set_interrupt_v1(priv, hw_id, irq_type, enable); 273a7ff8297SAmit Kucheria } 274a7ff8297SAmit Kucheria 275a7ff8297SAmit Kucheria /** 276a7ff8297SAmit Kucheria * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold 277a7ff8297SAmit Kucheria * @priv: Pointer to tsens controller private data 278a7ff8297SAmit Kucheria * @hw_id: Hardware ID aka. sensor number 279a7ff8297SAmit Kucheria * @d: Pointer to irq state data 280a7ff8297SAmit Kucheria * 281a7ff8297SAmit Kucheria * Return: 0 if threshold was not violated, 1 if it was violated and negative 282a7ff8297SAmit Kucheria * errno in case of errors 283a7ff8297SAmit Kucheria */ 284a7ff8297SAmit Kucheria static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id, 285a7ff8297SAmit Kucheria struct tsens_irq_data *d) 286a7ff8297SAmit Kucheria { 287a7ff8297SAmit Kucheria int ret; 288a7ff8297SAmit Kucheria 289a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol); 290a7ff8297SAmit Kucheria if (ret) 291a7ff8297SAmit Kucheria return ret; 292a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol); 293a7ff8297SAmit Kucheria if (ret) 294a7ff8297SAmit Kucheria return ret; 295a7ff8297SAmit Kucheria 296a7ff8297SAmit Kucheria if (priv->feat->crit_int) { 297a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id], 298a7ff8297SAmit Kucheria &d->crit_viol); 299a7ff8297SAmit Kucheria if (ret) 300a7ff8297SAmit Kucheria return ret; 301a7ff8297SAmit Kucheria } 302a7ff8297SAmit Kucheria 303a7ff8297SAmit Kucheria if (d->up_viol || d->low_viol || d->crit_viol) 304a7ff8297SAmit Kucheria return 1; 305a7ff8297SAmit Kucheria 306a7ff8297SAmit Kucheria return 0; 307a7ff8297SAmit Kucheria } 308a7ff8297SAmit Kucheria 309a7ff8297SAmit Kucheria static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, 310a7ff8297SAmit Kucheria const struct tsens_sensor *s, 311a7ff8297SAmit Kucheria struct tsens_irq_data *d) 312a7ff8297SAmit Kucheria { 313a7ff8297SAmit Kucheria int ret; 314a7ff8297SAmit Kucheria 315a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear); 316a7ff8297SAmit Kucheria if (ret) 317a7ff8297SAmit Kucheria return ret; 318a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear); 319a7ff8297SAmit Kucheria if (ret) 320a7ff8297SAmit Kucheria return ret; 321a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X) { 322a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask); 323a7ff8297SAmit Kucheria if (ret) 324a7ff8297SAmit Kucheria return ret; 325a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask); 326a7ff8297SAmit Kucheria if (ret) 327a7ff8297SAmit Kucheria return ret; 328a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id], 329a7ff8297SAmit Kucheria &d->crit_irq_clear); 330a7ff8297SAmit Kucheria if (ret) 331a7ff8297SAmit Kucheria return ret; 332a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id], 333a7ff8297SAmit Kucheria &d->crit_irq_mask); 334a7ff8297SAmit Kucheria if (ret) 335a7ff8297SAmit Kucheria return ret; 336a7ff8297SAmit Kucheria 337a7ff8297SAmit Kucheria d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id); 338a7ff8297SAmit Kucheria } else { 339a7ff8297SAmit Kucheria /* No mask register on older TSENS */ 340a7ff8297SAmit Kucheria d->up_irq_mask = 0; 341a7ff8297SAmit Kucheria d->low_irq_mask = 0; 342a7ff8297SAmit Kucheria d->crit_irq_clear = 0; 343a7ff8297SAmit Kucheria d->crit_irq_mask = 0; 344a7ff8297SAmit Kucheria d->crit_thresh = 0; 345a7ff8297SAmit Kucheria } 346a7ff8297SAmit Kucheria 347a7ff8297SAmit Kucheria d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id); 348a7ff8297SAmit Kucheria d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id); 349a7ff8297SAmit Kucheria 350a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n", 351a7ff8297SAmit Kucheria hw_id, __func__, 352a7ff8297SAmit Kucheria (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", 353a7ff8297SAmit Kucheria d->low_viol, d->up_viol, d->crit_viol, 354a7ff8297SAmit Kucheria d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear, 355a7ff8297SAmit Kucheria d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask); 356a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__, 357a7ff8297SAmit Kucheria (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "", 358a7ff8297SAmit Kucheria d->low_thresh, d->up_thresh, d->crit_thresh); 359a7ff8297SAmit Kucheria 360a7ff8297SAmit Kucheria return 0; 361a7ff8297SAmit Kucheria } 362a7ff8297SAmit Kucheria 363a7ff8297SAmit Kucheria static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver) 364a7ff8297SAmit Kucheria { 365a7ff8297SAmit Kucheria if (ver > VER_1_X) 366a7ff8297SAmit Kucheria return mask & (1 << hw_id); 367a7ff8297SAmit Kucheria 368a7ff8297SAmit Kucheria /* v1, v0.1 don't have a irq mask register */ 369a7ff8297SAmit Kucheria return 0; 370a7ff8297SAmit Kucheria } 371a7ff8297SAmit Kucheria 372a7ff8297SAmit Kucheria /** 373a7ff8297SAmit Kucheria * tsens_critical_irq_thread() - Threaded handler for critical interrupts 374a7ff8297SAmit Kucheria * @irq: irq number 375a7ff8297SAmit Kucheria * @data: tsens controller private data 376a7ff8297SAmit Kucheria * 377a7ff8297SAmit Kucheria * Check FSM watchdog bark status and clear if needed. 378a7ff8297SAmit Kucheria * Check all sensors to find ones that violated their critical threshold limits. 379a7ff8297SAmit Kucheria * Clear and then re-enable the interrupt. 380a7ff8297SAmit Kucheria * 381a7ff8297SAmit Kucheria * The level-triggered interrupt might deassert if the temperature returned to 382a7ff8297SAmit Kucheria * within the threshold limits by the time the handler got scheduled. We 383a7ff8297SAmit Kucheria * consider the irq to have been handled in that case. 384a7ff8297SAmit Kucheria * 385a7ff8297SAmit Kucheria * Return: IRQ_HANDLED 386a7ff8297SAmit Kucheria */ 3873ecc8292SAmit Kucheria static irqreturn_t tsens_critical_irq_thread(int irq, void *data) 388a7ff8297SAmit Kucheria { 389a7ff8297SAmit Kucheria struct tsens_priv *priv = data; 390a7ff8297SAmit Kucheria struct tsens_irq_data d; 391a7ff8297SAmit Kucheria int temp, ret, i; 392a7ff8297SAmit Kucheria u32 wdog_status, wdog_count; 393a7ff8297SAmit Kucheria 394a7ff8297SAmit Kucheria if (priv->feat->has_watchdog) { 395a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS], 396a7ff8297SAmit Kucheria &wdog_status); 397a7ff8297SAmit Kucheria if (ret) 398a7ff8297SAmit Kucheria return ret; 399a7ff8297SAmit Kucheria 400a7ff8297SAmit Kucheria if (wdog_status) { 401a7ff8297SAmit Kucheria /* Clear WDOG interrupt */ 402a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1); 403a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0); 404a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT], 405a7ff8297SAmit Kucheria &wdog_count); 406a7ff8297SAmit Kucheria if (ret) 407a7ff8297SAmit Kucheria return ret; 408a7ff8297SAmit Kucheria if (wdog_count) 409a7ff8297SAmit Kucheria dev_dbg(priv->dev, "%s: watchdog count: %d\n", 410a7ff8297SAmit Kucheria __func__, wdog_count); 411a7ff8297SAmit Kucheria 412a7ff8297SAmit Kucheria /* Fall through to handle critical interrupts if any */ 413a7ff8297SAmit Kucheria } 414a7ff8297SAmit Kucheria } 415a7ff8297SAmit Kucheria 416a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 417a7ff8297SAmit Kucheria const struct tsens_sensor *s = &priv->sensor[i]; 418a7ff8297SAmit Kucheria u32 hw_id = s->hw_id; 419a7ff8297SAmit Kucheria 420*cf969218SAnsuel Smith if (!s->tzd) 421a7ff8297SAmit Kucheria continue; 422a7ff8297SAmit Kucheria if (!tsens_threshold_violated(priv, hw_id, &d)) 423a7ff8297SAmit Kucheria continue; 424a7ff8297SAmit Kucheria ret = get_temp_tsens_valid(s, &temp); 425a7ff8297SAmit Kucheria if (ret) { 426a7ff8297SAmit Kucheria dev_err(priv->dev, "[%u] %s: error reading sensor\n", 427a7ff8297SAmit Kucheria hw_id, __func__); 428a7ff8297SAmit Kucheria continue; 429a7ff8297SAmit Kucheria } 430a7ff8297SAmit Kucheria 431a7ff8297SAmit Kucheria tsens_read_irq_state(priv, hw_id, s, &d); 432a7ff8297SAmit Kucheria if (d.crit_viol && 433a7ff8297SAmit Kucheria !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) { 434a7ff8297SAmit Kucheria /* Mask critical interrupts, unused on Linux */ 435a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, CRITICAL, false); 436a7ff8297SAmit Kucheria } 437a7ff8297SAmit Kucheria } 438a7ff8297SAmit Kucheria 439a7ff8297SAmit Kucheria return IRQ_HANDLED; 440a7ff8297SAmit Kucheria } 441a7ff8297SAmit Kucheria 442a7ff8297SAmit Kucheria /** 443a7ff8297SAmit Kucheria * tsens_irq_thread - Threaded interrupt handler for uplow interrupts 444a7ff8297SAmit Kucheria * @irq: irq number 445a7ff8297SAmit Kucheria * @data: tsens controller private data 446a7ff8297SAmit Kucheria * 447a7ff8297SAmit Kucheria * Check all sensors to find ones that violated their threshold limits. If the 448a7ff8297SAmit Kucheria * temperature is still outside the limits, call thermal_zone_device_update() to 449a7ff8297SAmit Kucheria * update the thresholds, else re-enable the interrupts. 450a7ff8297SAmit Kucheria * 451a7ff8297SAmit Kucheria * The level-triggered interrupt might deassert if the temperature returned to 452a7ff8297SAmit Kucheria * within the threshold limits by the time the handler got scheduled. We 453a7ff8297SAmit Kucheria * consider the irq to have been handled in that case. 454a7ff8297SAmit Kucheria * 455a7ff8297SAmit Kucheria * Return: IRQ_HANDLED 456a7ff8297SAmit Kucheria */ 4573ecc8292SAmit Kucheria static irqreturn_t tsens_irq_thread(int irq, void *data) 458a7ff8297SAmit Kucheria { 459a7ff8297SAmit Kucheria struct tsens_priv *priv = data; 460a7ff8297SAmit Kucheria struct tsens_irq_data d; 461a7ff8297SAmit Kucheria bool enable = true, disable = false; 462a7ff8297SAmit Kucheria unsigned long flags; 463a7ff8297SAmit Kucheria int temp, ret, i; 464a7ff8297SAmit Kucheria 465a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 466a7ff8297SAmit Kucheria bool trigger = false; 467a7ff8297SAmit Kucheria const struct tsens_sensor *s = &priv->sensor[i]; 468a7ff8297SAmit Kucheria u32 hw_id = s->hw_id; 469a7ff8297SAmit Kucheria 470*cf969218SAnsuel Smith if (!s->tzd) 471a7ff8297SAmit Kucheria continue; 472a7ff8297SAmit Kucheria if (!tsens_threshold_violated(priv, hw_id, &d)) 473a7ff8297SAmit Kucheria continue; 474a7ff8297SAmit Kucheria ret = get_temp_tsens_valid(s, &temp); 475a7ff8297SAmit Kucheria if (ret) { 476a7ff8297SAmit Kucheria dev_err(priv->dev, "[%u] %s: error reading sensor\n", 477a7ff8297SAmit Kucheria hw_id, __func__); 478a7ff8297SAmit Kucheria continue; 479a7ff8297SAmit Kucheria } 480a7ff8297SAmit Kucheria 481a7ff8297SAmit Kucheria spin_lock_irqsave(&priv->ul_lock, flags); 482a7ff8297SAmit Kucheria 483a7ff8297SAmit Kucheria tsens_read_irq_state(priv, hw_id, s, &d); 484a7ff8297SAmit Kucheria 485a7ff8297SAmit Kucheria if (d.up_viol && 486a7ff8297SAmit Kucheria !masked_irq(hw_id, d.up_irq_mask, tsens_version(priv))) { 487a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, UPPER, disable); 488a7ff8297SAmit Kucheria if (d.up_thresh > temp) { 489a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s: re-arm upper\n", 490a7ff8297SAmit Kucheria hw_id, __func__); 491a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, UPPER, enable); 492a7ff8297SAmit Kucheria } else { 493a7ff8297SAmit Kucheria trigger = true; 494a7ff8297SAmit Kucheria /* Keep irq masked */ 495a7ff8297SAmit Kucheria } 496a7ff8297SAmit Kucheria } else if (d.low_viol && 497a7ff8297SAmit Kucheria !masked_irq(hw_id, d.low_irq_mask, tsens_version(priv))) { 498a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, LOWER, disable); 499a7ff8297SAmit Kucheria if (d.low_thresh < temp) { 500a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s: re-arm low\n", 501a7ff8297SAmit Kucheria hw_id, __func__); 502a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, LOWER, enable); 503a7ff8297SAmit Kucheria } else { 504a7ff8297SAmit Kucheria trigger = true; 505a7ff8297SAmit Kucheria /* Keep irq masked */ 506a7ff8297SAmit Kucheria } 507a7ff8297SAmit Kucheria } 508a7ff8297SAmit Kucheria 509a7ff8297SAmit Kucheria spin_unlock_irqrestore(&priv->ul_lock, flags); 510a7ff8297SAmit Kucheria 511a7ff8297SAmit Kucheria if (trigger) { 512a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n", 513a7ff8297SAmit Kucheria hw_id, __func__, temp); 514a7ff8297SAmit Kucheria thermal_zone_device_update(s->tzd, 515a7ff8297SAmit Kucheria THERMAL_EVENT_UNSPECIFIED); 516a7ff8297SAmit Kucheria } else { 517a7ff8297SAmit Kucheria dev_dbg(priv->dev, "[%u] %s: no violation: %d\n", 518a7ff8297SAmit Kucheria hw_id, __func__, temp); 519a7ff8297SAmit Kucheria } 52053e2a20eSAnsuel Smith 52153e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) { 52253e2a20eSAnsuel Smith /* Constraint: There is only 1 interrupt control register for all 52353e2a20eSAnsuel Smith * 11 temperature sensor. So monitoring more than 1 sensor based 52453e2a20eSAnsuel Smith * on interrupts will yield inconsistent result. To overcome this 52553e2a20eSAnsuel Smith * issue we will monitor only sensor 0 which is the master sensor. 52653e2a20eSAnsuel Smith */ 52753e2a20eSAnsuel Smith break; 52853e2a20eSAnsuel Smith } 529a7ff8297SAmit Kucheria } 530a7ff8297SAmit Kucheria 531a7ff8297SAmit Kucheria return IRQ_HANDLED; 532a7ff8297SAmit Kucheria } 533a7ff8297SAmit Kucheria 5343ecc8292SAmit Kucheria static int tsens_set_trips(void *_sensor, int low, int high) 535a7ff8297SAmit Kucheria { 536a7ff8297SAmit Kucheria struct tsens_sensor *s = _sensor; 537a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 538a7ff8297SAmit Kucheria struct device *dev = priv->dev; 539a7ff8297SAmit Kucheria struct tsens_irq_data d; 540a7ff8297SAmit Kucheria unsigned long flags; 541a7ff8297SAmit Kucheria int high_val, low_val, cl_high, cl_low; 542a7ff8297SAmit Kucheria u32 hw_id = s->hw_id; 543a7ff8297SAmit Kucheria 54453e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) { 54553e2a20eSAnsuel Smith /* Pre v0.1 IP had a single register for each type of interrupt 54653e2a20eSAnsuel Smith * and thresholds 54753e2a20eSAnsuel Smith */ 54853e2a20eSAnsuel Smith hw_id = 0; 54953e2a20eSAnsuel Smith } 55053e2a20eSAnsuel Smith 551a7ff8297SAmit Kucheria dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", 552a7ff8297SAmit Kucheria hw_id, __func__, low, high); 553a7ff8297SAmit Kucheria 554a7ff8297SAmit Kucheria cl_high = clamp_val(high, -40000, 120000); 555a7ff8297SAmit Kucheria cl_low = clamp_val(low, -40000, 120000); 556a7ff8297SAmit Kucheria 557a7ff8297SAmit Kucheria high_val = tsens_mC_to_hw(s, cl_high); 558a7ff8297SAmit Kucheria low_val = tsens_mC_to_hw(s, cl_low); 559a7ff8297SAmit Kucheria 560a7ff8297SAmit Kucheria spin_lock_irqsave(&priv->ul_lock, flags); 561a7ff8297SAmit Kucheria 562a7ff8297SAmit Kucheria tsens_read_irq_state(priv, hw_id, s, &d); 563a7ff8297SAmit Kucheria 564a7ff8297SAmit Kucheria /* Write the new thresholds and clear the status */ 565a7ff8297SAmit Kucheria regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val); 566a7ff8297SAmit Kucheria regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val); 567a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, LOWER, true); 568a7ff8297SAmit Kucheria tsens_set_interrupt(priv, hw_id, UPPER, true); 569a7ff8297SAmit Kucheria 570a7ff8297SAmit Kucheria spin_unlock_irqrestore(&priv->ul_lock, flags); 571a7ff8297SAmit Kucheria 572a7ff8297SAmit Kucheria dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n", 573a7ff8297SAmit Kucheria hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high); 574a7ff8297SAmit Kucheria 575a7ff8297SAmit Kucheria return 0; 576a7ff8297SAmit Kucheria } 577a7ff8297SAmit Kucheria 5783ecc8292SAmit Kucheria static int tsens_enable_irq(struct tsens_priv *priv) 579a7ff8297SAmit Kucheria { 580a7ff8297SAmit Kucheria int ret; 581a7ff8297SAmit Kucheria int val = tsens_version(priv) > VER_1_X ? 7 : 1; 582a7ff8297SAmit Kucheria 583a7ff8297SAmit Kucheria ret = regmap_field_write(priv->rf[INT_EN], val); 584a7ff8297SAmit Kucheria if (ret < 0) 585a7ff8297SAmit Kucheria dev_err(priv->dev, "%s: failed to enable interrupts\n", 586a7ff8297SAmit Kucheria __func__); 587a7ff8297SAmit Kucheria 588a7ff8297SAmit Kucheria return ret; 589a7ff8297SAmit Kucheria } 590a7ff8297SAmit Kucheria 5913ecc8292SAmit Kucheria static void tsens_disable_irq(struct tsens_priv *priv) 592a7ff8297SAmit Kucheria { 593a7ff8297SAmit Kucheria regmap_field_write(priv->rf[INT_EN], 0); 594a7ff8297SAmit Kucheria } 595a7ff8297SAmit Kucheria 596a7ff8297SAmit Kucheria int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) 597a7ff8297SAmit Kucheria { 598a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 599a7ff8297SAmit Kucheria int hw_id = s->hw_id; 600a7ff8297SAmit Kucheria u32 temp_idx = LAST_TEMP_0 + hw_id; 601a7ff8297SAmit Kucheria u32 valid_idx = VALID_0 + hw_id; 602a7ff8297SAmit Kucheria u32 valid; 603a7ff8297SAmit Kucheria int ret; 604a7ff8297SAmit Kucheria 60553e2a20eSAnsuel Smith /* VER_0 doesn't have VALID bit */ 60653e2a20eSAnsuel Smith if (tsens_version(priv) >= VER_0_1) { 607a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[valid_idx], &valid); 608a7ff8297SAmit Kucheria if (ret) 609a7ff8297SAmit Kucheria return ret; 610a7ff8297SAmit Kucheria while (!valid) { 611a7ff8297SAmit Kucheria /* Valid bit is 0 for 6 AHB clock cycles. 612a7ff8297SAmit Kucheria * At 19.2MHz, 1 AHB clock is ~60ns. 613a7ff8297SAmit Kucheria * We should enter this loop very, very rarely. 614a7ff8297SAmit Kucheria */ 615a7ff8297SAmit Kucheria ndelay(400); 616a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[valid_idx], &valid); 617a7ff8297SAmit Kucheria if (ret) 618a7ff8297SAmit Kucheria return ret; 619a7ff8297SAmit Kucheria } 62053e2a20eSAnsuel Smith } 621a7ff8297SAmit Kucheria 622a7ff8297SAmit Kucheria /* Valid bit is set, OK to read the temperature */ 623a7ff8297SAmit Kucheria *temp = tsens_hw_to_mC(s, temp_idx); 624a7ff8297SAmit Kucheria 625a7ff8297SAmit Kucheria return 0; 626a7ff8297SAmit Kucheria } 627a7ff8297SAmit Kucheria 628a7ff8297SAmit Kucheria int get_temp_common(const struct tsens_sensor *s, int *temp) 629a7ff8297SAmit Kucheria { 630a7ff8297SAmit Kucheria struct tsens_priv *priv = s->priv; 631a7ff8297SAmit Kucheria int hw_id = s->hw_id; 63253e2a20eSAnsuel Smith int last_temp = 0, ret, trdy; 63353e2a20eSAnsuel Smith unsigned long timeout; 63453e2a20eSAnsuel Smith 63553e2a20eSAnsuel Smith timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); 63653e2a20eSAnsuel Smith do { 63753e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0) { 63853e2a20eSAnsuel Smith ret = regmap_field_read(priv->rf[TRDY], &trdy); 63953e2a20eSAnsuel Smith if (ret) 64053e2a20eSAnsuel Smith return ret; 64153e2a20eSAnsuel Smith if (!trdy) 64253e2a20eSAnsuel Smith continue; 64353e2a20eSAnsuel Smith } 644a7ff8297SAmit Kucheria 645a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); 646a7ff8297SAmit Kucheria if (ret) 647a7ff8297SAmit Kucheria return ret; 648a7ff8297SAmit Kucheria 649a7ff8297SAmit Kucheria *temp = code_to_degc(last_temp, s) * 1000; 650a7ff8297SAmit Kucheria 651a7ff8297SAmit Kucheria return 0; 65253e2a20eSAnsuel Smith } while (time_before(jiffies, timeout)); 65353e2a20eSAnsuel Smith 65453e2a20eSAnsuel Smith return -ETIMEDOUT; 655a7ff8297SAmit Kucheria } 656a7ff8297SAmit Kucheria 657a7ff8297SAmit Kucheria #ifdef CONFIG_DEBUG_FS 658a7ff8297SAmit Kucheria static int dbg_sensors_show(struct seq_file *s, void *data) 659a7ff8297SAmit Kucheria { 660a7ff8297SAmit Kucheria struct platform_device *pdev = s->private; 661a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev); 662a7ff8297SAmit Kucheria int i; 663a7ff8297SAmit Kucheria 664a7ff8297SAmit Kucheria seq_printf(s, "max: %2d\nnum: %2d\n\n", 665a7ff8297SAmit Kucheria priv->feat->max_sensors, priv->num_sensors); 666a7ff8297SAmit Kucheria 667a7ff8297SAmit Kucheria seq_puts(s, " id slope offset\n--------------------------\n"); 668a7ff8297SAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 669a7ff8297SAmit Kucheria seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id, 670a7ff8297SAmit Kucheria priv->sensor[i].slope, priv->sensor[i].offset); 671a7ff8297SAmit Kucheria } 672a7ff8297SAmit Kucheria 673a7ff8297SAmit Kucheria return 0; 674a7ff8297SAmit Kucheria } 675a7ff8297SAmit Kucheria 676a7ff8297SAmit Kucheria static int dbg_version_show(struct seq_file *s, void *data) 677a7ff8297SAmit Kucheria { 678a7ff8297SAmit Kucheria struct platform_device *pdev = s->private; 679a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev); 680a7ff8297SAmit Kucheria u32 maj_ver, min_ver, step_ver; 681a7ff8297SAmit Kucheria int ret; 682a7ff8297SAmit Kucheria 683a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_0_1) { 684a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver); 685a7ff8297SAmit Kucheria if (ret) 686a7ff8297SAmit Kucheria return ret; 687a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver); 688a7ff8297SAmit Kucheria if (ret) 689a7ff8297SAmit Kucheria return ret; 690a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_STEP], &step_ver); 691a7ff8297SAmit Kucheria if (ret) 692a7ff8297SAmit Kucheria return ret; 693a7ff8297SAmit Kucheria seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver); 694a7ff8297SAmit Kucheria } else { 695a7ff8297SAmit Kucheria seq_puts(s, "0.1.0\n"); 696a7ff8297SAmit Kucheria } 697a7ff8297SAmit Kucheria 698a7ff8297SAmit Kucheria return 0; 699a7ff8297SAmit Kucheria } 700a7ff8297SAmit Kucheria 701a7ff8297SAmit Kucheria DEFINE_SHOW_ATTRIBUTE(dbg_version); 702a7ff8297SAmit Kucheria DEFINE_SHOW_ATTRIBUTE(dbg_sensors); 703a7ff8297SAmit Kucheria 704a7ff8297SAmit Kucheria static void tsens_debug_init(struct platform_device *pdev) 705a7ff8297SAmit Kucheria { 706a7ff8297SAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev); 707a7ff8297SAmit Kucheria struct dentry *root, *file; 708a7ff8297SAmit Kucheria 709a7ff8297SAmit Kucheria root = debugfs_lookup("tsens", NULL); 710a7ff8297SAmit Kucheria if (!root) 711a7ff8297SAmit Kucheria priv->debug_root = debugfs_create_dir("tsens", NULL); 712a7ff8297SAmit Kucheria else 713a7ff8297SAmit Kucheria priv->debug_root = root; 714a7ff8297SAmit Kucheria 715a7ff8297SAmit Kucheria file = debugfs_lookup("version", priv->debug_root); 716a7ff8297SAmit Kucheria if (!file) 717a7ff8297SAmit Kucheria debugfs_create_file("version", 0444, priv->debug_root, 718a7ff8297SAmit Kucheria pdev, &dbg_version_fops); 719a7ff8297SAmit Kucheria 720a7ff8297SAmit Kucheria /* A directory for each instance of the TSENS IP */ 721a7ff8297SAmit Kucheria priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root); 722a7ff8297SAmit Kucheria debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops); 723a7ff8297SAmit Kucheria } 724a7ff8297SAmit Kucheria #else 725a7ff8297SAmit Kucheria static inline void tsens_debug_init(struct platform_device *pdev) {} 726a7ff8297SAmit Kucheria #endif 727a7ff8297SAmit Kucheria 728a7ff8297SAmit Kucheria static const struct regmap_config tsens_config = { 729a7ff8297SAmit Kucheria .name = "tm", 730a7ff8297SAmit Kucheria .reg_bits = 32, 731a7ff8297SAmit Kucheria .val_bits = 32, 732a7ff8297SAmit Kucheria .reg_stride = 4, 733a7ff8297SAmit Kucheria }; 734a7ff8297SAmit Kucheria 735a7ff8297SAmit Kucheria static const struct regmap_config tsens_srot_config = { 736a7ff8297SAmit Kucheria .name = "srot", 737a7ff8297SAmit Kucheria .reg_bits = 32, 738a7ff8297SAmit Kucheria .val_bits = 32, 739a7ff8297SAmit Kucheria .reg_stride = 4, 740a7ff8297SAmit Kucheria }; 741a7ff8297SAmit Kucheria 742a7ff8297SAmit Kucheria int __init init_common(struct tsens_priv *priv) 743a7ff8297SAmit Kucheria { 744a7ff8297SAmit Kucheria void __iomem *tm_base, *srot_base; 745a7ff8297SAmit Kucheria struct device *dev = priv->dev; 746a7ff8297SAmit Kucheria u32 ver_minor; 747a7ff8297SAmit Kucheria struct resource *res; 748a7ff8297SAmit Kucheria u32 enabled; 749a7ff8297SAmit Kucheria int ret, i, j; 750a7ff8297SAmit Kucheria struct platform_device *op = of_find_device_by_node(priv->dev->of_node); 751a7ff8297SAmit Kucheria 752a7ff8297SAmit Kucheria if (!op) 753a7ff8297SAmit Kucheria return -EINVAL; 754a7ff8297SAmit Kucheria 755a7ff8297SAmit Kucheria if (op->num_resources > 1) { 756a7ff8297SAmit Kucheria /* DT with separate SROT and TM address space */ 757a7ff8297SAmit Kucheria priv->tm_offset = 0; 758a7ff8297SAmit Kucheria res = platform_get_resource(op, IORESOURCE_MEM, 1); 759a7ff8297SAmit Kucheria srot_base = devm_ioremap_resource(dev, res); 760a7ff8297SAmit Kucheria if (IS_ERR(srot_base)) { 761a7ff8297SAmit Kucheria ret = PTR_ERR(srot_base); 762a7ff8297SAmit Kucheria goto err_put_device; 763a7ff8297SAmit Kucheria } 764a7ff8297SAmit Kucheria 765a7ff8297SAmit Kucheria priv->srot_map = devm_regmap_init_mmio(dev, srot_base, 766a7ff8297SAmit Kucheria &tsens_srot_config); 767a7ff8297SAmit Kucheria if (IS_ERR(priv->srot_map)) { 768a7ff8297SAmit Kucheria ret = PTR_ERR(priv->srot_map); 769a7ff8297SAmit Kucheria goto err_put_device; 770a7ff8297SAmit Kucheria } 771a7ff8297SAmit Kucheria } else { 772a7ff8297SAmit Kucheria /* old DTs where SROT and TM were in a contiguous 2K block */ 773a7ff8297SAmit Kucheria priv->tm_offset = 0x1000; 774a7ff8297SAmit Kucheria } 775a7ff8297SAmit Kucheria 77653e2a20eSAnsuel Smith if (tsens_version(priv) >= VER_0_1) { 777a7ff8297SAmit Kucheria res = platform_get_resource(op, IORESOURCE_MEM, 0); 778a7ff8297SAmit Kucheria tm_base = devm_ioremap_resource(dev, res); 779a7ff8297SAmit Kucheria if (IS_ERR(tm_base)) { 780a7ff8297SAmit Kucheria ret = PTR_ERR(tm_base); 781a7ff8297SAmit Kucheria goto err_put_device; 782a7ff8297SAmit Kucheria } 783a7ff8297SAmit Kucheria 784a7ff8297SAmit Kucheria priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); 78553e2a20eSAnsuel Smith } else { /* VER_0 share the same gcc regs using a syscon */ 78653e2a20eSAnsuel Smith struct device *parent = priv->dev->parent; 78753e2a20eSAnsuel Smith 78853e2a20eSAnsuel Smith if (parent) 78953e2a20eSAnsuel Smith priv->tm_map = syscon_node_to_regmap(parent->of_node); 79053e2a20eSAnsuel Smith } 79153e2a20eSAnsuel Smith 79253e2a20eSAnsuel Smith if (IS_ERR_OR_NULL(priv->tm_map)) { 79353e2a20eSAnsuel Smith if (!priv->tm_map) 79453e2a20eSAnsuel Smith ret = -ENODEV; 79553e2a20eSAnsuel Smith else 796a7ff8297SAmit Kucheria ret = PTR_ERR(priv->tm_map); 797a7ff8297SAmit Kucheria goto err_put_device; 798a7ff8297SAmit Kucheria } 799a7ff8297SAmit Kucheria 80053e2a20eSAnsuel Smith /* VER_0 have only tm_map */ 80153e2a20eSAnsuel Smith if (!priv->srot_map) 80253e2a20eSAnsuel Smith priv->srot_map = priv->tm_map; 80353e2a20eSAnsuel Smith 804a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_0_1) { 805a7ff8297SAmit Kucheria for (i = VER_MAJOR; i <= VER_STEP; i++) { 806a7ff8297SAmit Kucheria priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map, 807a7ff8297SAmit Kucheria priv->fields[i]); 808f4136863SGuangqing Zhu if (IS_ERR(priv->rf[i])) { 809f4136863SGuangqing Zhu ret = PTR_ERR(priv->rf[i]); 810f4136863SGuangqing Zhu goto err_put_device; 811f4136863SGuangqing Zhu } 812a7ff8297SAmit Kucheria } 813a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor); 814a7ff8297SAmit Kucheria if (ret) 815a7ff8297SAmit Kucheria goto err_put_device; 816a7ff8297SAmit Kucheria } 817a7ff8297SAmit Kucheria 818a7ff8297SAmit Kucheria priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map, 819a7ff8297SAmit Kucheria priv->fields[TSENS_EN]); 820a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[TSENS_EN])) { 821a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[TSENS_EN]); 822a7ff8297SAmit Kucheria goto err_put_device; 823a7ff8297SAmit Kucheria } 82453e2a20eSAnsuel Smith /* in VER_0 TSENS need to be explicitly enabled */ 82553e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0) 82653e2a20eSAnsuel Smith regmap_field_write(priv->rf[TSENS_EN], 1); 82753e2a20eSAnsuel Smith 828a7ff8297SAmit Kucheria ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); 829a7ff8297SAmit Kucheria if (ret) 830a7ff8297SAmit Kucheria goto err_put_device; 831a7ff8297SAmit Kucheria if (!enabled) { 832a7ff8297SAmit Kucheria dev_err(dev, "%s: device not enabled\n", __func__); 833a7ff8297SAmit Kucheria ret = -ENODEV; 834a7ff8297SAmit Kucheria goto err_put_device; 835a7ff8297SAmit Kucheria } 836a7ff8297SAmit Kucheria 837a7ff8297SAmit Kucheria priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map, 838a7ff8297SAmit Kucheria priv->fields[SENSOR_EN]); 839a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[SENSOR_EN])) { 840a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[SENSOR_EN]); 841a7ff8297SAmit Kucheria goto err_put_device; 842a7ff8297SAmit Kucheria } 843a7ff8297SAmit Kucheria priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map, 844a7ff8297SAmit Kucheria priv->fields[INT_EN]); 845a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[INT_EN])) { 846a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[INT_EN]); 847a7ff8297SAmit Kucheria goto err_put_device; 848a7ff8297SAmit Kucheria } 849a7ff8297SAmit Kucheria 85053e2a20eSAnsuel Smith priv->rf[TSENS_SW_RST] = 85153e2a20eSAnsuel Smith devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]); 85253e2a20eSAnsuel Smith if (IS_ERR(priv->rf[TSENS_SW_RST])) { 85353e2a20eSAnsuel Smith ret = PTR_ERR(priv->rf[TSENS_SW_RST]); 85453e2a20eSAnsuel Smith goto err_put_device; 85553e2a20eSAnsuel Smith } 85653e2a20eSAnsuel Smith 85753e2a20eSAnsuel Smith priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]); 85853e2a20eSAnsuel Smith if (IS_ERR(priv->rf[TRDY])) { 85953e2a20eSAnsuel Smith ret = PTR_ERR(priv->rf[TRDY]); 86053e2a20eSAnsuel Smith goto err_put_device; 86153e2a20eSAnsuel Smith } 86253e2a20eSAnsuel Smith 863a7ff8297SAmit Kucheria /* This loop might need changes if enum regfield_ids is reordered */ 864a7ff8297SAmit Kucheria for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) { 865a7ff8297SAmit Kucheria for (i = 0; i < priv->feat->max_sensors; i++) { 866a7ff8297SAmit Kucheria int idx = j + i; 867a7ff8297SAmit Kucheria 868a7ff8297SAmit Kucheria priv->rf[idx] = devm_regmap_field_alloc(dev, 869a7ff8297SAmit Kucheria priv->tm_map, 870a7ff8297SAmit Kucheria priv->fields[idx]); 871a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[idx])) { 872a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[idx]); 873a7ff8297SAmit Kucheria goto err_put_device; 874a7ff8297SAmit Kucheria } 875a7ff8297SAmit Kucheria } 876a7ff8297SAmit Kucheria } 877a7ff8297SAmit Kucheria 87853e2a20eSAnsuel Smith if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) { 879a7ff8297SAmit Kucheria /* Loop might need changes if enum regfield_ids is reordered */ 880a7ff8297SAmit Kucheria for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) { 881a7ff8297SAmit Kucheria for (i = 0; i < priv->feat->max_sensors; i++) { 882a7ff8297SAmit Kucheria int idx = j + i; 883a7ff8297SAmit Kucheria 884a7ff8297SAmit Kucheria priv->rf[idx] = 885a7ff8297SAmit Kucheria devm_regmap_field_alloc(dev, 886a7ff8297SAmit Kucheria priv->tm_map, 887a7ff8297SAmit Kucheria priv->fields[idx]); 888a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[idx])) { 889a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[idx]); 890a7ff8297SAmit Kucheria goto err_put_device; 891a7ff8297SAmit Kucheria } 892a7ff8297SAmit Kucheria } 893a7ff8297SAmit Kucheria } 894a7ff8297SAmit Kucheria } 895a7ff8297SAmit Kucheria 896a7ff8297SAmit Kucheria if (tsens_version(priv) > VER_1_X && ver_minor > 2) { 897a7ff8297SAmit Kucheria /* Watchdog is present only on v2.3+ */ 898a7ff8297SAmit Kucheria priv->feat->has_watchdog = 1; 899a7ff8297SAmit Kucheria for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) { 900a7ff8297SAmit Kucheria priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map, 901a7ff8297SAmit Kucheria priv->fields[i]); 902a7ff8297SAmit Kucheria if (IS_ERR(priv->rf[i])) { 903a7ff8297SAmit Kucheria ret = PTR_ERR(priv->rf[i]); 904a7ff8297SAmit Kucheria goto err_put_device; 905a7ff8297SAmit Kucheria } 906a7ff8297SAmit Kucheria } 907a7ff8297SAmit Kucheria /* 908a7ff8297SAmit Kucheria * Watchdog is already enabled, unmask the bark. 909a7ff8297SAmit Kucheria * Disable cycle completion monitoring 910a7ff8297SAmit Kucheria */ 911a7ff8297SAmit Kucheria regmap_field_write(priv->rf[WDOG_BARK_MASK], 0); 912a7ff8297SAmit Kucheria regmap_field_write(priv->rf[CC_MON_MASK], 1); 913a7ff8297SAmit Kucheria } 914a7ff8297SAmit Kucheria 915a7ff8297SAmit Kucheria spin_lock_init(&priv->ul_lock); 91653e2a20eSAnsuel Smith 91753e2a20eSAnsuel Smith /* VER_0 interrupt doesn't need to be enabled */ 91853e2a20eSAnsuel Smith if (tsens_version(priv) >= VER_0_1) 919a7ff8297SAmit Kucheria tsens_enable_irq(priv); 92053e2a20eSAnsuel Smith 921a7ff8297SAmit Kucheria tsens_debug_init(op); 922a7ff8297SAmit Kucheria 923a7ff8297SAmit Kucheria err_put_device: 924a7ff8297SAmit Kucheria put_device(&op->dev); 925a7ff8297SAmit Kucheria return ret; 926a7ff8297SAmit Kucheria } 927a7ff8297SAmit Kucheria 9289066073cSRajendra Nayak static int tsens_get_temp(void *data, int *temp) 9299066073cSRajendra Nayak { 9308b71bce4SAmit Kucheria struct tsens_sensor *s = data; 93169b628acSAmit Kucheria struct tsens_priv *priv = s->priv; 9329066073cSRajendra Nayak 9338b71bce4SAmit Kucheria return priv->ops->get_temp(s, temp); 9349066073cSRajendra Nayak } 9359066073cSRajendra Nayak 9362cbcd2eaSAmit Kucheria static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend) 9379066073cSRajendra Nayak { 9388b71bce4SAmit Kucheria struct tsens_sensor *s = data; 93969b628acSAmit Kucheria struct tsens_priv *priv = s->priv; 9409066073cSRajendra Nayak 94169b628acSAmit Kucheria if (priv->ops->get_trend) 9428b71bce4SAmit Kucheria return priv->ops->get_trend(s, trend); 9439066073cSRajendra Nayak 9449066073cSRajendra Nayak return -ENOTSUPP; 9459066073cSRajendra Nayak } 9469066073cSRajendra Nayak 9475b97469aSArnd Bergmann static int __maybe_unused tsens_suspend(struct device *dev) 9489066073cSRajendra Nayak { 94969b628acSAmit Kucheria struct tsens_priv *priv = dev_get_drvdata(dev); 9509066073cSRajendra Nayak 95169b628acSAmit Kucheria if (priv->ops && priv->ops->suspend) 95269b628acSAmit Kucheria return priv->ops->suspend(priv); 9539066073cSRajendra Nayak 9549066073cSRajendra Nayak return 0; 9559066073cSRajendra Nayak } 9569066073cSRajendra Nayak 9575b97469aSArnd Bergmann static int __maybe_unused tsens_resume(struct device *dev) 9589066073cSRajendra Nayak { 95969b628acSAmit Kucheria struct tsens_priv *priv = dev_get_drvdata(dev); 9609066073cSRajendra Nayak 96169b628acSAmit Kucheria if (priv->ops && priv->ops->resume) 96269b628acSAmit Kucheria return priv->ops->resume(priv); 9639066073cSRajendra Nayak 9649066073cSRajendra Nayak return 0; 9659066073cSRajendra Nayak } 9669066073cSRajendra Nayak 9679066073cSRajendra Nayak static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); 9689066073cSRajendra Nayak 9699066073cSRajendra Nayak static const struct of_device_id tsens_table[] = { 9709066073cSRajendra Nayak { 9716b3aeafbSAnsuel Smith .compatible = "qcom,ipq8064-tsens", 9726b3aeafbSAnsuel Smith .data = &data_8960, 9736b3aeafbSAnsuel Smith }, { 974a2149ab8SKonrad Dybcio .compatible = "qcom,mdm9607-tsens", 975a2149ab8SKonrad Dybcio .data = &data_9607, 976a2149ab8SKonrad Dybcio }, { 9779066073cSRajendra Nayak .compatible = "qcom,msm8916-tsens", 978840a5bd3SRajendra Nayak .data = &data_8916, 9799066073cSRajendra Nayak }, { 980332bc8ebSShawn Guo .compatible = "qcom,msm8939-tsens", 981332bc8ebSShawn Guo .data = &data_8939, 982332bc8ebSShawn Guo }, { 9839066073cSRajendra Nayak .compatible = "qcom,msm8974-tsens", 9845e6703bdSRajendra Nayak .data = &data_8974, 985d059c739SRajendra Nayak }, { 9860e580290SAngeloGioacchino Del Regno .compatible = "qcom,msm8976-tsens", 9870e580290SAngeloGioacchino Del Regno .data = &data_8976, 9880e580290SAngeloGioacchino Del Regno }, { 989d059c739SRajendra Nayak .compatible = "qcom,msm8996-tsens", 990d059c739SRajendra Nayak .data = &data_8996, 991191dc74bSAmit Kucheria }, { 992e8c24c6fSAmit Kucheria .compatible = "qcom,tsens-v1", 993e8c24c6fSAmit Kucheria .data = &data_tsens_v1, 994e8c24c6fSAmit Kucheria }, { 995191dc74bSAmit Kucheria .compatible = "qcom,tsens-v2", 996191dc74bSAmit Kucheria .data = &data_tsens_v2, 9979066073cSRajendra Nayak }, 9989066073cSRajendra Nayak {} 9999066073cSRajendra Nayak }; 10009066073cSRajendra Nayak MODULE_DEVICE_TABLE(of, tsens_table); 10019066073cSRajendra Nayak 10029066073cSRajendra Nayak static const struct thermal_zone_of_device_ops tsens_of_ops = { 10039066073cSRajendra Nayak .get_temp = tsens_get_temp, 10049066073cSRajendra Nayak .get_trend = tsens_get_trend, 1005634e11d5SAmit Kucheria .set_trips = tsens_set_trips, 10069066073cSRajendra Nayak }; 10079066073cSRajendra Nayak 100879125e03SAmit Kucheria static int tsens_register_irq(struct tsens_priv *priv, char *irqname, 100979125e03SAmit Kucheria irq_handler_t thread_fn) 101079125e03SAmit Kucheria { 101179125e03SAmit Kucheria struct platform_device *pdev; 101279125e03SAmit Kucheria int ret, irq; 101379125e03SAmit Kucheria 101479125e03SAmit Kucheria pdev = of_find_device_by_node(priv->dev->of_node); 101579125e03SAmit Kucheria if (!pdev) 101679125e03SAmit Kucheria return -ENODEV; 101779125e03SAmit Kucheria 101879125e03SAmit Kucheria irq = platform_get_irq_byname(pdev, irqname); 101979125e03SAmit Kucheria if (irq < 0) { 102079125e03SAmit Kucheria ret = irq; 102179125e03SAmit Kucheria /* For old DTs with no IRQ defined */ 102279125e03SAmit Kucheria if (irq == -ENXIO) 102379125e03SAmit Kucheria ret = 0; 102479125e03SAmit Kucheria } else { 102553e2a20eSAnsuel Smith /* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */ 102653e2a20eSAnsuel Smith if (tsens_version(priv) == VER_0) 102779125e03SAmit Kucheria ret = devm_request_threaded_irq(&pdev->dev, irq, 102853e2a20eSAnsuel Smith thread_fn, NULL, 102953e2a20eSAnsuel Smith IRQF_TRIGGER_RISING, 103053e2a20eSAnsuel Smith dev_name(&pdev->dev), 103153e2a20eSAnsuel Smith priv); 103253e2a20eSAnsuel Smith else 103353e2a20eSAnsuel Smith ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 103453e2a20eSAnsuel Smith thread_fn, IRQF_ONESHOT, 103553e2a20eSAnsuel Smith dev_name(&pdev->dev), 103653e2a20eSAnsuel Smith priv); 103753e2a20eSAnsuel Smith 103879125e03SAmit Kucheria if (ret) 103979125e03SAmit Kucheria dev_err(&pdev->dev, "%s: failed to get irq\n", 104079125e03SAmit Kucheria __func__); 104179125e03SAmit Kucheria else 104279125e03SAmit Kucheria enable_irq_wake(irq); 104379125e03SAmit Kucheria } 104479125e03SAmit Kucheria 104579125e03SAmit Kucheria put_device(&pdev->dev); 104679125e03SAmit Kucheria return ret; 104779125e03SAmit Kucheria } 104879125e03SAmit Kucheria 104969b628acSAmit Kucheria static int tsens_register(struct tsens_priv *priv) 10509066073cSRajendra Nayak { 105179125e03SAmit Kucheria int i, ret; 10529066073cSRajendra Nayak struct thermal_zone_device *tzd; 10539066073cSRajendra Nayak 105469b628acSAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 105569b628acSAmit Kucheria priv->sensor[i].priv = priv; 10568b71bce4SAmit Kucheria tzd = devm_thermal_zone_of_sensor_register(priv->dev, priv->sensor[i].hw_id, 105769b628acSAmit Kucheria &priv->sensor[i], 10589066073cSRajendra Nayak &tsens_of_ops); 10599066073cSRajendra Nayak if (IS_ERR(tzd)) 10609066073cSRajendra Nayak continue; 106169b628acSAmit Kucheria priv->sensor[i].tzd = tzd; 106269b628acSAmit Kucheria if (priv->ops->enable) 106369b628acSAmit Kucheria priv->ops->enable(priv, i); 10649066073cSRajendra Nayak } 1065634e11d5SAmit Kucheria 106653e2a20eSAnsuel Smith /* VER_0 require to set MIN and MAX THRESH 106753e2a20eSAnsuel Smith * These 2 regs are set using the: 106853e2a20eSAnsuel Smith * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C 106953e2a20eSAnsuel Smith * - CRIT_THRESH_1 for MIN THRESH hardcoded to 0°C 107053e2a20eSAnsuel Smith */ 107153e2a20eSAnsuel Smith if (tsens_version(priv) < VER_0_1) { 107253e2a20eSAnsuel Smith regmap_field_write(priv->rf[CRIT_THRESH_0], 107353e2a20eSAnsuel Smith tsens_mC_to_hw(priv->sensor, 120000)); 107453e2a20eSAnsuel Smith 107553e2a20eSAnsuel Smith regmap_field_write(priv->rf[CRIT_THRESH_1], 107653e2a20eSAnsuel Smith tsens_mC_to_hw(priv->sensor, 0)); 107753e2a20eSAnsuel Smith } 107853e2a20eSAnsuel Smith 107979125e03SAmit Kucheria ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); 108079125e03SAmit Kucheria if (ret < 0) 108179125e03SAmit Kucheria return ret; 1082634e11d5SAmit Kucheria 108379125e03SAmit Kucheria if (priv->feat->crit_int) 108479125e03SAmit Kucheria ret = tsens_register_irq(priv, "critical", 108579125e03SAmit Kucheria tsens_critical_irq_thread); 1086634e11d5SAmit Kucheria 1087634e11d5SAmit Kucheria return ret; 10889066073cSRajendra Nayak } 10899066073cSRajendra Nayak 10909066073cSRajendra Nayak static int tsens_probe(struct platform_device *pdev) 10919066073cSRajendra Nayak { 10929066073cSRajendra Nayak int ret, i; 10939066073cSRajendra Nayak struct device *dev; 10949066073cSRajendra Nayak struct device_node *np; 109569b628acSAmit Kucheria struct tsens_priv *priv; 10963c040ce0SAmit Kucheria const struct tsens_plat_data *data; 10979066073cSRajendra Nayak const struct of_device_id *id; 10986d7c70d1SBjorn Andersson u32 num_sensors; 10999066073cSRajendra Nayak 11009066073cSRajendra Nayak if (pdev->dev.of_node) 11019066073cSRajendra Nayak dev = &pdev->dev; 11029066073cSRajendra Nayak else 11039066073cSRajendra Nayak dev = pdev->dev.parent; 11049066073cSRajendra Nayak 11059066073cSRajendra Nayak np = dev->of_node; 11069066073cSRajendra Nayak 11079066073cSRajendra Nayak id = of_match_node(tsens_table, np); 110820d4fd84SRajendra Nayak if (id) 11099066073cSRajendra Nayak data = id->data; 111020d4fd84SRajendra Nayak else 111120d4fd84SRajendra Nayak data = &data_8960; 11129066073cSRajendra Nayak 11136d7c70d1SBjorn Andersson num_sensors = data->num_sensors; 11146d7c70d1SBjorn Andersson 11156d7c70d1SBjorn Andersson if (np) 11166d7c70d1SBjorn Andersson of_property_read_u32(np, "#qcom,sensors", &num_sensors); 11176d7c70d1SBjorn Andersson 11186d7c70d1SBjorn Andersson if (num_sensors <= 0) { 11193795ad5eSAmit Kucheria dev_err(dev, "%s: invalid number of sensors\n", __func__); 11209066073cSRajendra Nayak return -EINVAL; 11219066073cSRajendra Nayak } 11229066073cSRajendra Nayak 112369b628acSAmit Kucheria priv = devm_kzalloc(dev, 112469b628acSAmit Kucheria struct_size(priv, sensor, num_sensors), 11250ed2dd03SKees Cook GFP_KERNEL); 112669b628acSAmit Kucheria if (!priv) 11279066073cSRajendra Nayak return -ENOMEM; 11289066073cSRajendra Nayak 112969b628acSAmit Kucheria priv->dev = dev; 113069b628acSAmit Kucheria priv->num_sensors = num_sensors; 113169b628acSAmit Kucheria priv->ops = data->ops; 113269b628acSAmit Kucheria for (i = 0; i < priv->num_sensors; i++) { 11339066073cSRajendra Nayak if (data->hw_ids) 113469b628acSAmit Kucheria priv->sensor[i].hw_id = data->hw_ids[i]; 11359066073cSRajendra Nayak else 113669b628acSAmit Kucheria priv->sensor[i].hw_id = i; 11379066073cSRajendra Nayak } 1138c1997054SAmit Kucheria priv->feat = data->feat; 1139c1997054SAmit Kucheria priv->fields = data->fields; 11409066073cSRajendra Nayak 11410e9c0bc7SAmit Kucheria platform_set_drvdata(pdev, priv); 11420e9c0bc7SAmit Kucheria 114369b628acSAmit Kucheria if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) 11449066073cSRajendra Nayak return -EINVAL; 11459066073cSRajendra Nayak 114669b628acSAmit Kucheria ret = priv->ops->init(priv); 11479066073cSRajendra Nayak if (ret < 0) { 11483795ad5eSAmit Kucheria dev_err(dev, "%s: init failed\n", __func__); 11499066073cSRajendra Nayak return ret; 11509066073cSRajendra Nayak } 11519066073cSRajendra Nayak 115269b628acSAmit Kucheria if (priv->ops->calibrate) { 115369b628acSAmit Kucheria ret = priv->ops->calibrate(priv); 11549066073cSRajendra Nayak if (ret < 0) { 1155fc7d18cfSAmit Kucheria if (ret != -EPROBE_DEFER) 11563795ad5eSAmit Kucheria dev_err(dev, "%s: calibration failed\n", __func__); 11579066073cSRajendra Nayak return ret; 11589066073cSRajendra Nayak } 11599066073cSRajendra Nayak } 11609066073cSRajendra Nayak 11610e9c0bc7SAmit Kucheria return tsens_register(priv); 11629066073cSRajendra Nayak } 11639066073cSRajendra Nayak 11649066073cSRajendra Nayak static int tsens_remove(struct platform_device *pdev) 11659066073cSRajendra Nayak { 116669b628acSAmit Kucheria struct tsens_priv *priv = platform_get_drvdata(pdev); 11679066073cSRajendra Nayak 11687c938f48SAmit Kucheria debugfs_remove_recursive(priv->debug_root); 1169634e11d5SAmit Kucheria tsens_disable_irq(priv); 117069b628acSAmit Kucheria if (priv->ops->disable) 117169b628acSAmit Kucheria priv->ops->disable(priv); 11729066073cSRajendra Nayak 11739066073cSRajendra Nayak return 0; 11749066073cSRajendra Nayak } 11759066073cSRajendra Nayak 11769066073cSRajendra Nayak static struct platform_driver tsens_driver = { 11779066073cSRajendra Nayak .probe = tsens_probe, 11789066073cSRajendra Nayak .remove = tsens_remove, 11799066073cSRajendra Nayak .driver = { 11809066073cSRajendra Nayak .name = "qcom-tsens", 11819066073cSRajendra Nayak .pm = &tsens_pm_ops, 11829066073cSRajendra Nayak .of_match_table = tsens_table, 11839066073cSRajendra Nayak }, 11849066073cSRajendra Nayak }; 11859066073cSRajendra Nayak module_platform_driver(tsens_driver); 11869066073cSRajendra Nayak 11879066073cSRajendra Nayak MODULE_LICENSE("GPL v2"); 11889066073cSRajendra Nayak MODULE_DESCRIPTION("QCOM Temperature Sensor driver"); 11899066073cSRajendra Nayak MODULE_ALIAS("platform:qcom-tsens"); 1190