18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TI Bandgap temperature sensor driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/ 68c2ecf20Sopenharmony_ci * Author: J Keerthy <j-keerthy@ti.com> 78c2ecf20Sopenharmony_ci * Author: Moiz Sonasath <m-sonasath@ti.com> 88c2ecf20Sopenharmony_ci * Couple of fixes, DT and MFD adaptation: 98c2ecf20Sopenharmony_ci * Eduardo Valentin <eduardo.valentin@ti.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/export.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci#include <linux/sys_soc.h> 248c2ecf20Sopenharmony_ci#include <linux/reboot.h> 258c2ecf20Sopenharmony_ci#include <linux/of_device.h> 268c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 278c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h> 308c2ecf20Sopenharmony_ci#include <linux/device.h> 318c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 328c2ecf20Sopenharmony_ci#include <linux/pm.h> 338c2ecf20Sopenharmony_ci#include <linux/of.h> 348c2ecf20Sopenharmony_ci#include <linux/of_device.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "ti-bandgap.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id); 398c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 408c2ecf20Sopenharmony_cistatic int bandgap_omap_cpu_notifier(struct notifier_block *nb, 418c2ecf20Sopenharmony_ci unsigned long cmd, void *v); 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/*** Helper functions to access registers and their bitfields ***/ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/** 478c2ecf20Sopenharmony_ci * ti_bandgap_readl() - simple read helper function 488c2ecf20Sopenharmony_ci * @bgp: pointer to ti_bandgap structure 498c2ecf20Sopenharmony_ci * @reg: desired register (offset) to be read 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * Helper function to read bandgap registers. It uses the io remapped area. 528c2ecf20Sopenharmony_ci * Return: the register value. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic u32 ti_bandgap_readl(struct ti_bandgap *bgp, u32 reg) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return readl(bgp->base + reg); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/** 608c2ecf20Sopenharmony_ci * ti_bandgap_writel() - simple write helper function 618c2ecf20Sopenharmony_ci * @bgp: pointer to ti_bandgap structure 628c2ecf20Sopenharmony_ci * @val: desired register value to be written 638c2ecf20Sopenharmony_ci * @reg: desired register (offset) to be written 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * Helper function to write bandgap registers. It uses the io remapped area. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic void ti_bandgap_writel(struct ti_bandgap *bgp, u32 val, u32 reg) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci writel(val, bgp->base + reg); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/** 738c2ecf20Sopenharmony_ci * DOC: macro to update bits. 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * RMW_BITS() - used to read, modify and update bandgap bitfields. 768c2ecf20Sopenharmony_ci * The value passed will be shifted. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci#define RMW_BITS(bgp, id, reg, mask, val) \ 798c2ecf20Sopenharmony_cido { \ 808c2ecf20Sopenharmony_ci struct temp_sensor_registers *t; \ 818c2ecf20Sopenharmony_ci u32 r; \ 828c2ecf20Sopenharmony_ci \ 838c2ecf20Sopenharmony_ci t = bgp->conf->sensors[(id)].registers; \ 848c2ecf20Sopenharmony_ci r = ti_bandgap_readl(bgp, t->reg); \ 858c2ecf20Sopenharmony_ci r &= ~t->mask; \ 868c2ecf20Sopenharmony_ci r |= (val) << __ffs(t->mask); \ 878c2ecf20Sopenharmony_ci ti_bandgap_writel(bgp, r, t->reg); \ 888c2ecf20Sopenharmony_ci} while (0) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/*** Basic helper functions ***/ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/** 938c2ecf20Sopenharmony_ci * ti_bandgap_power() - controls the power state of a bandgap device 948c2ecf20Sopenharmony_ci * @bgp: pointer to ti_bandgap structure 958c2ecf20Sopenharmony_ci * @on: desired power state (1 - on, 0 - off) 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * Used to power on/off a bandgap device instance. Only used on those 988c2ecf20Sopenharmony_ci * that features tempsoff bit. 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * Return: 0 on success, -ENOTSUPP if tempsoff is not supported. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic int ti_bandgap_power(struct ti_bandgap *bgp, bool on) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci int i; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!TI_BANDGAP_HAS(bgp, POWER_SWITCH)) 1078c2ecf20Sopenharmony_ci return -ENOTSUPP; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) 1108c2ecf20Sopenharmony_ci /* active on 0 */ 1118c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, temp_sensor_ctrl, bgap_tempsoff_mask, !on); 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/** 1168c2ecf20Sopenharmony_ci * ti_errata814_bandgap_read_temp() - helper function to read dra7 sensor temperature 1178c2ecf20Sopenharmony_ci * @bgp: pointer to ti_bandgap structure 1188c2ecf20Sopenharmony_ci * @reg: desired register (offset) to be read 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * Function to read dra7 bandgap sensor temperature. This is done separately 1218c2ecf20Sopenharmony_ci * so as to workaround the errata "Bandgap Temperature read Dtemp can be 1228c2ecf20Sopenharmony_ci * corrupted" - Errata ID: i814". 1238c2ecf20Sopenharmony_ci * Read accesses to registers listed below can be corrupted due to incorrect 1248c2ecf20Sopenharmony_ci * resynchronization between clock domains. 1258c2ecf20Sopenharmony_ci * Read access to registers below can be corrupted : 1268c2ecf20Sopenharmony_ci * CTRL_CORE_DTEMP_MPU/GPU/CORE/DSPEVE/IVA_n (n = 0 to 4) 1278c2ecf20Sopenharmony_ci * CTRL_CORE_TEMP_SENSOR_MPU/GPU/CORE/DSPEVE/IVA_n 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Return: the register value. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_cistatic u32 ti_errata814_bandgap_read_temp(struct ti_bandgap *bgp, u32 reg) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci u32 val1, val2; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci val1 = ti_bandgap_readl(bgp, reg); 1368c2ecf20Sopenharmony_ci val2 = ti_bandgap_readl(bgp, reg); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* If both times we read the same value then that is right */ 1398c2ecf20Sopenharmony_ci if (val1 == val2) 1408c2ecf20Sopenharmony_ci return val1; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* if val1 and val2 are different read it third time */ 1438c2ecf20Sopenharmony_ci return ti_bandgap_readl(bgp, reg); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/** 1478c2ecf20Sopenharmony_ci * ti_bandgap_read_temp() - helper function to read sensor temperature 1488c2ecf20Sopenharmony_ci * @bgp: pointer to ti_bandgap structure 1498c2ecf20Sopenharmony_ci * @id: bandgap sensor id 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci * Function to concentrate the steps to read sensor temperature register. 1528c2ecf20Sopenharmony_ci * This function is desired because, depending on bandgap device version, 1538c2ecf20Sopenharmony_ci * it might be needed to freeze the bandgap state machine, before fetching 1548c2ecf20Sopenharmony_ci * the register value. 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * Return: temperature in ADC values. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic u32 ti_bandgap_read_temp(struct ti_bandgap *bgp, int id) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 1618c2ecf20Sopenharmony_ci u32 temp, reg; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[id].registers; 1648c2ecf20Sopenharmony_ci reg = tsr->temp_sensor_ctrl; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, FREEZE_BIT)) { 1678c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 1); 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * In case we cannot read from cur_dtemp / dtemp_0, 1708c2ecf20Sopenharmony_ci * then we read from the last valid temp read 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci reg = tsr->ctrl_dtemp_1; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* read temperature */ 1768c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, ERRATA_814)) 1778c2ecf20Sopenharmony_ci temp = ti_errata814_bandgap_read_temp(bgp, reg); 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci temp = ti_bandgap_readl(bgp, reg); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci temp &= tsr->bgap_dtemp_mask; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, FREEZE_BIT)) 1848c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 0); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return temp; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/*** IRQ handlers ***/ 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/** 1928c2ecf20Sopenharmony_ci * ti_bandgap_talert_irq_handler() - handles Temperature alert IRQs 1938c2ecf20Sopenharmony_ci * @irq: IRQ number 1948c2ecf20Sopenharmony_ci * @data: private data (struct ti_bandgap *) 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * This is the Talert handler. Use it only if bandgap device features 1978c2ecf20Sopenharmony_ci * HAS(TALERT). This handler goes over all sensors and checks their 1988c2ecf20Sopenharmony_ci * conditions and acts accordingly. In case there are events pending, 1998c2ecf20Sopenharmony_ci * it will reset the event mask to wait for the opposite event (next event). 2008c2ecf20Sopenharmony_ci * Every time there is a new event, it will be reported to thermal layer. 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * Return: IRQ_HANDLED 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistatic irqreturn_t ti_bandgap_talert_irq_handler(int irq, void *data) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct ti_bandgap *bgp = data; 2078c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 2088c2ecf20Sopenharmony_ci u32 t_hot = 0, t_cold = 0, ctrl; 2098c2ecf20Sopenharmony_ci int i; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci spin_lock(&bgp->lock); 2128c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) { 2138c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[i].registers; 2148c2ecf20Sopenharmony_ci ctrl = ti_bandgap_readl(bgp, tsr->bgap_status); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Read the status of t_hot */ 2178c2ecf20Sopenharmony_ci t_hot = ctrl & tsr->status_hot_mask; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Read the status of t_cold */ 2208c2ecf20Sopenharmony_ci t_cold = ctrl & tsr->status_cold_mask; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (!t_cold && !t_hot) 2238c2ecf20Sopenharmony_ci continue; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ctrl = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl); 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * One TALERT interrupt: Two sources 2288c2ecf20Sopenharmony_ci * If the interrupt is due to t_hot then mask t_hot and 2298c2ecf20Sopenharmony_ci * and unmask t_cold else mask t_cold and unmask t_hot 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci if (t_hot) { 2328c2ecf20Sopenharmony_ci ctrl &= ~tsr->mask_hot_mask; 2338c2ecf20Sopenharmony_ci ctrl |= tsr->mask_cold_mask; 2348c2ecf20Sopenharmony_ci } else if (t_cold) { 2358c2ecf20Sopenharmony_ci ctrl &= ~tsr->mask_cold_mask; 2368c2ecf20Sopenharmony_ci ctrl |= tsr->mask_hot_mask; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ti_bandgap_writel(bgp, ctrl, tsr->bgap_mask_ctrl); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci dev_dbg(bgp->dev, 2428c2ecf20Sopenharmony_ci "%s: IRQ from %s sensor: hotevent %d coldevent %d\n", 2438c2ecf20Sopenharmony_ci __func__, bgp->conf->sensors[i].domain, 2448c2ecf20Sopenharmony_ci t_hot, t_cold); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* report temperature to whom may concern */ 2478c2ecf20Sopenharmony_ci if (bgp->conf->report_temperature) 2488c2ecf20Sopenharmony_ci bgp->conf->report_temperature(bgp, i); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci spin_unlock(&bgp->lock); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/** 2568c2ecf20Sopenharmony_ci * ti_bandgap_tshut_irq_handler() - handles Temperature shutdown signal 2578c2ecf20Sopenharmony_ci * @irq: IRQ number 2588c2ecf20Sopenharmony_ci * @data: private data (unused) 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * This is the Tshut handler. Use it only if bandgap device features 2618c2ecf20Sopenharmony_ci * HAS(TSHUT). If any sensor fires the Tshut signal, we simply shutdown 2628c2ecf20Sopenharmony_ci * the system. 2638c2ecf20Sopenharmony_ci * 2648c2ecf20Sopenharmony_ci * Return: IRQ_HANDLED 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cistatic irqreturn_t ti_bandgap_tshut_irq_handler(int irq, void *data) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci pr_emerg("%s: TSHUT temperature reached. Needs shut down...\n", 2698c2ecf20Sopenharmony_ci __func__); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci orderly_poweroff(true); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/*** Helper functions which manipulate conversion ADC <-> mi Celsius ***/ 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/** 2798c2ecf20Sopenharmony_ci * ti_bandgap_adc_to_mcelsius() - converts an ADC value to mCelsius scale 2808c2ecf20Sopenharmony_ci * @bgp: struct ti_bandgap pointer 2818c2ecf20Sopenharmony_ci * @adc_val: value in ADC representation 2828c2ecf20Sopenharmony_ci * @t: address where to write the resulting temperature in mCelsius 2838c2ecf20Sopenharmony_ci * 2848c2ecf20Sopenharmony_ci * Simple conversion from ADC representation to mCelsius. In case the ADC value 2858c2ecf20Sopenharmony_ci * is out of the ADC conv table range, it returns -ERANGE, 0 on success. 2868c2ecf20Sopenharmony_ci * The conversion table is indexed by the ADC values. 2878c2ecf20Sopenharmony_ci * 2888c2ecf20Sopenharmony_ci * Return: 0 if conversion was successful, else -ERANGE in case the @adc_val 2898c2ecf20Sopenharmony_ci * argument is out of the ADC conv table range. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_cistatic 2928c2ecf20Sopenharmony_ciint ti_bandgap_adc_to_mcelsius(struct ti_bandgap *bgp, int adc_val, int *t) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci const struct ti_bandgap_data *conf = bgp->conf; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* look up for temperature in the table and return the temperature */ 2978c2ecf20Sopenharmony_ci if (adc_val < conf->adc_start_val || adc_val > conf->adc_end_val) 2988c2ecf20Sopenharmony_ci return -ERANGE; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci *t = bgp->conf->conv_table[adc_val - conf->adc_start_val]; 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/** 3058c2ecf20Sopenharmony_ci * ti_bandgap_validate() - helper to check the sanity of a struct ti_bandgap 3068c2ecf20Sopenharmony_ci * @bgp: struct ti_bandgap pointer 3078c2ecf20Sopenharmony_ci * @id: bandgap sensor id 3088c2ecf20Sopenharmony_ci * 3098c2ecf20Sopenharmony_ci * Checks if the bandgap pointer is valid and if the sensor id is also 3108c2ecf20Sopenharmony_ci * applicable. 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * Return: 0 if no errors, -EINVAL for invalid @bgp pointer or -ERANGE if 3138c2ecf20Sopenharmony_ci * @id cannot index @bgp sensors. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_cistatic inline int ti_bandgap_validate(struct ti_bandgap *bgp, int id) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci if (!bgp || IS_ERR(bgp)) { 3188c2ecf20Sopenharmony_ci pr_err("%s: invalid bandgap pointer\n", __func__); 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if ((id < 0) || (id >= bgp->conf->sensor_count)) { 3238c2ecf20Sopenharmony_ci dev_err(bgp->dev, "%s: sensor id out of range (%d)\n", 3248c2ecf20Sopenharmony_ci __func__, id); 3258c2ecf20Sopenharmony_ci return -ERANGE; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/** 3328c2ecf20Sopenharmony_ci * ti_bandgap_read_counter() - read the sensor counter 3338c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 3348c2ecf20Sopenharmony_ci * @id: sensor id 3358c2ecf20Sopenharmony_ci * @interval: resulting update interval in miliseconds 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_cistatic void ti_bandgap_read_counter(struct ti_bandgap *bgp, int id, 3388c2ecf20Sopenharmony_ci int *interval) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 3418c2ecf20Sopenharmony_ci int time; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[id].registers; 3448c2ecf20Sopenharmony_ci time = ti_bandgap_readl(bgp, tsr->bgap_counter); 3458c2ecf20Sopenharmony_ci time = (time & tsr->counter_mask) >> 3468c2ecf20Sopenharmony_ci __ffs(tsr->counter_mask); 3478c2ecf20Sopenharmony_ci time = time * 1000 / bgp->clk_rate; 3488c2ecf20Sopenharmony_ci *interval = time; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/** 3528c2ecf20Sopenharmony_ci * ti_bandgap_read_counter_delay() - read the sensor counter delay 3538c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 3548c2ecf20Sopenharmony_ci * @id: sensor id 3558c2ecf20Sopenharmony_ci * @interval: resulting update interval in miliseconds 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_cistatic void ti_bandgap_read_counter_delay(struct ti_bandgap *bgp, int id, 3588c2ecf20Sopenharmony_ci int *interval) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 3618c2ecf20Sopenharmony_ci int reg_val; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[id].registers; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci reg_val = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl); 3668c2ecf20Sopenharmony_ci reg_val = (reg_val & tsr->mask_counter_delay_mask) >> 3678c2ecf20Sopenharmony_ci __ffs(tsr->mask_counter_delay_mask); 3688c2ecf20Sopenharmony_ci switch (reg_val) { 3698c2ecf20Sopenharmony_ci case 0: 3708c2ecf20Sopenharmony_ci *interval = 0; 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case 1: 3738c2ecf20Sopenharmony_ci *interval = 1; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci case 2: 3768c2ecf20Sopenharmony_ci *interval = 10; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case 3: 3798c2ecf20Sopenharmony_ci *interval = 100; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case 4: 3828c2ecf20Sopenharmony_ci *interval = 250; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci case 5: 3858c2ecf20Sopenharmony_ci *interval = 500; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci default: 3888c2ecf20Sopenharmony_ci dev_warn(bgp->dev, "Wrong counter delay value read from register %X", 3898c2ecf20Sopenharmony_ci reg_val); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/** 3948c2ecf20Sopenharmony_ci * ti_bandgap_read_update_interval() - read the sensor update interval 3958c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 3968c2ecf20Sopenharmony_ci * @id: sensor id 3978c2ecf20Sopenharmony_ci * @interval: resulting update interval in miliseconds 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Return: 0 on success or the proper error code 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ciint ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id, 4028c2ecf20Sopenharmony_ci int *interval) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci int ret = 0; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = ti_bandgap_validate(bgp, id); 4078c2ecf20Sopenharmony_ci if (ret) 4088c2ecf20Sopenharmony_ci goto exit; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (!TI_BANDGAP_HAS(bgp, COUNTER) && 4118c2ecf20Sopenharmony_ci !TI_BANDGAP_HAS(bgp, COUNTER_DELAY)) { 4128c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 4138c2ecf20Sopenharmony_ci goto exit; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, COUNTER)) { 4178c2ecf20Sopenharmony_ci ti_bandgap_read_counter(bgp, id, interval); 4188c2ecf20Sopenharmony_ci goto exit; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ti_bandgap_read_counter_delay(bgp, id, interval); 4228c2ecf20Sopenharmony_ciexit: 4238c2ecf20Sopenharmony_ci return ret; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/** 4278c2ecf20Sopenharmony_ci * ti_bandgap_write_counter_delay() - set the counter_delay 4288c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 4298c2ecf20Sopenharmony_ci * @id: sensor id 4308c2ecf20Sopenharmony_ci * @interval: desired update interval in miliseconds 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * Return: 0 on success or the proper error code 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_cistatic int ti_bandgap_write_counter_delay(struct ti_bandgap *bgp, int id, 4358c2ecf20Sopenharmony_ci u32 interval) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci int rval; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci switch (interval) { 4408c2ecf20Sopenharmony_ci case 0: /* Immediate conversion */ 4418c2ecf20Sopenharmony_ci rval = 0x0; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci case 1: /* Conversion after ever 1ms */ 4448c2ecf20Sopenharmony_ci rval = 0x1; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case 10: /* Conversion after ever 10ms */ 4478c2ecf20Sopenharmony_ci rval = 0x2; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci case 100: /* Conversion after ever 100ms */ 4508c2ecf20Sopenharmony_ci rval = 0x3; 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci case 250: /* Conversion after ever 250ms */ 4538c2ecf20Sopenharmony_ci rval = 0x4; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci case 500: /* Conversion after ever 500ms */ 4568c2ecf20Sopenharmony_ci rval = 0x5; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci default: 4598c2ecf20Sopenharmony_ci dev_warn(bgp->dev, "Delay %d ms is not supported\n", interval); 4608c2ecf20Sopenharmony_ci return -EINVAL; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci spin_lock(&bgp->lock); 4648c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, bgap_mask_ctrl, mask_counter_delay_mask, rval); 4658c2ecf20Sopenharmony_ci spin_unlock(&bgp->lock); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/** 4718c2ecf20Sopenharmony_ci * ti_bandgap_write_counter() - set the bandgap sensor counter 4728c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 4738c2ecf20Sopenharmony_ci * @id: sensor id 4748c2ecf20Sopenharmony_ci * @interval: desired update interval in miliseconds 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_cistatic void ti_bandgap_write_counter(struct ti_bandgap *bgp, int id, 4778c2ecf20Sopenharmony_ci u32 interval) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci interval = interval * bgp->clk_rate / 1000; 4808c2ecf20Sopenharmony_ci spin_lock(&bgp->lock); 4818c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, bgap_counter, counter_mask, interval); 4828c2ecf20Sopenharmony_ci spin_unlock(&bgp->lock); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci/** 4868c2ecf20Sopenharmony_ci * ti_bandgap_write_update_interval() - set the update interval 4878c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 4888c2ecf20Sopenharmony_ci * @id: sensor id 4898c2ecf20Sopenharmony_ci * @interval: desired update interval in miliseconds 4908c2ecf20Sopenharmony_ci * 4918c2ecf20Sopenharmony_ci * Return: 0 on success or the proper error code 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ciint ti_bandgap_write_update_interval(struct ti_bandgap *bgp, 4948c2ecf20Sopenharmony_ci int id, u32 interval) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci int ret = ti_bandgap_validate(bgp, id); 4978c2ecf20Sopenharmony_ci if (ret) 4988c2ecf20Sopenharmony_ci goto exit; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (!TI_BANDGAP_HAS(bgp, COUNTER) && 5018c2ecf20Sopenharmony_ci !TI_BANDGAP_HAS(bgp, COUNTER_DELAY)) { 5028c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 5038c2ecf20Sopenharmony_ci goto exit; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, COUNTER)) { 5078c2ecf20Sopenharmony_ci ti_bandgap_write_counter(bgp, id, interval); 5088c2ecf20Sopenharmony_ci goto exit; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ret = ti_bandgap_write_counter_delay(bgp, id, interval); 5128c2ecf20Sopenharmony_ciexit: 5138c2ecf20Sopenharmony_ci return ret; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/** 5178c2ecf20Sopenharmony_ci * ti_bandgap_read_temperature() - report current temperature 5188c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 5198c2ecf20Sopenharmony_ci * @id: sensor id 5208c2ecf20Sopenharmony_ci * @temperature: resulting temperature 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * Return: 0 on success or the proper error code 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ciint ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id, 5258c2ecf20Sopenharmony_ci int *temperature) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci u32 temp; 5288c2ecf20Sopenharmony_ci int ret; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci ret = ti_bandgap_validate(bgp, id); 5318c2ecf20Sopenharmony_ci if (ret) 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (!TI_BANDGAP_HAS(bgp, MODE_CONFIG)) { 5358c2ecf20Sopenharmony_ci ret = ti_bandgap_force_single_read(bgp, id); 5368c2ecf20Sopenharmony_ci if (ret) 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci spin_lock(&bgp->lock); 5418c2ecf20Sopenharmony_ci temp = ti_bandgap_read_temp(bgp, id); 5428c2ecf20Sopenharmony_ci spin_unlock(&bgp->lock); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ret = ti_bandgap_adc_to_mcelsius(bgp, temp, &temp); 5458c2ecf20Sopenharmony_ci if (ret) 5468c2ecf20Sopenharmony_ci return -EIO; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci *temperature = temp; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/** 5548c2ecf20Sopenharmony_ci * ti_bandgap_set_sensor_data() - helper function to store thermal 5558c2ecf20Sopenharmony_ci * framework related data. 5568c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 5578c2ecf20Sopenharmony_ci * @id: sensor id 5588c2ecf20Sopenharmony_ci * @data: thermal framework related data to be stored 5598c2ecf20Sopenharmony_ci * 5608c2ecf20Sopenharmony_ci * Return: 0 on success or the proper error code 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ciint ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci int ret = ti_bandgap_validate(bgp, id); 5658c2ecf20Sopenharmony_ci if (ret) 5668c2ecf20Sopenharmony_ci return ret; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci bgp->regval[id].data = data; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/** 5748c2ecf20Sopenharmony_ci * ti_bandgap_get_sensor_data() - helper function to get thermal 5758c2ecf20Sopenharmony_ci * framework related data. 5768c2ecf20Sopenharmony_ci * @bgp: pointer to bandgap instance 5778c2ecf20Sopenharmony_ci * @id: sensor id 5788c2ecf20Sopenharmony_ci * 5798c2ecf20Sopenharmony_ci * Return: data stored by set function with sensor id on success or NULL 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_civoid *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci int ret = ti_bandgap_validate(bgp, id); 5848c2ecf20Sopenharmony_ci if (ret) 5858c2ecf20Sopenharmony_ci return ERR_PTR(ret); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci return bgp->regval[id].data; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/*** Helper functions used during device initialization ***/ 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci/** 5938c2ecf20Sopenharmony_ci * ti_bandgap_force_single_read() - executes 1 single ADC conversion 5948c2ecf20Sopenharmony_ci * @bgp: pointer to struct ti_bandgap 5958c2ecf20Sopenharmony_ci * @id: sensor id which it is desired to read 1 temperature 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * Used to initialize the conversion state machine and set it to a valid 5988c2ecf20Sopenharmony_ci * state. Called during device initialization and context restore events. 5998c2ecf20Sopenharmony_ci * 6008c2ecf20Sopenharmony_ci * Return: 0 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_cistatic int 6038c2ecf20Sopenharmony_citi_bandgap_force_single_read(struct ti_bandgap *bgp, int id) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci u32 counter = 1000; 6068c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* Select single conversion mode */ 6098c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) 6108c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 0); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Start of Conversion = 1 */ 6138c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* Wait for EOCZ going up */ 6168c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[id].registers; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci while (--counter) { 6198c2ecf20Sopenharmony_ci if (ti_bandgap_readl(bgp, tsr->temp_sensor_ctrl) & 6208c2ecf20Sopenharmony_ci tsr->bgap_eocz_mask) 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* Start of Conversion = 0 */ 6258c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 0); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Wait for EOCZ going down */ 6288c2ecf20Sopenharmony_ci counter = 1000; 6298c2ecf20Sopenharmony_ci while (--counter) { 6308c2ecf20Sopenharmony_ci if (!(ti_bandgap_readl(bgp, tsr->temp_sensor_ctrl) & 6318c2ecf20Sopenharmony_ci tsr->bgap_eocz_mask)) 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci return 0; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci/** 6398c2ecf20Sopenharmony_ci * ti_bandgap_set_continuous_mode() - One time enabling of continuous mode 6408c2ecf20Sopenharmony_ci * @bgp: pointer to struct ti_bandgap 6418c2ecf20Sopenharmony_ci * 6428c2ecf20Sopenharmony_ci * Call this function only if HAS(MODE_CONFIG) is set. As this driver may 6438c2ecf20Sopenharmony_ci * be used for junction temperature monitoring, it is desirable that the 6448c2ecf20Sopenharmony_ci * sensors are operational all the time, so that alerts are generated 6458c2ecf20Sopenharmony_ci * properly. 6468c2ecf20Sopenharmony_ci * 6478c2ecf20Sopenharmony_ci * Return: 0 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_cistatic int ti_bandgap_set_continuous_mode(struct ti_bandgap *bgp) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci int i; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) { 6548c2ecf20Sopenharmony_ci /* Perform a single read just before enabling continuous */ 6558c2ecf20Sopenharmony_ci ti_bandgap_force_single_read(bgp, i); 6568c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, bgap_mode_ctrl, mode_ctrl_mask, 1); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/** 6638c2ecf20Sopenharmony_ci * ti_bandgap_get_trend() - To fetch the temperature trend of a sensor 6648c2ecf20Sopenharmony_ci * @bgp: pointer to struct ti_bandgap 6658c2ecf20Sopenharmony_ci * @id: id of the individual sensor 6668c2ecf20Sopenharmony_ci * @trend: Pointer to trend. 6678c2ecf20Sopenharmony_ci * 6688c2ecf20Sopenharmony_ci * This function needs to be called to fetch the temperature trend of a 6698c2ecf20Sopenharmony_ci * Particular sensor. The function computes the difference in temperature 6708c2ecf20Sopenharmony_ci * w.r.t time. For the bandgaps with built in history buffer the temperatures 6718c2ecf20Sopenharmony_ci * are read from the buffer and for those without the Buffer -ENOTSUPP is 6728c2ecf20Sopenharmony_ci * returned. 6738c2ecf20Sopenharmony_ci * 6748c2ecf20Sopenharmony_ci * Return: 0 if no error, else return corresponding error. If no 6758c2ecf20Sopenharmony_ci * error then the trend value is passed on to trend parameter 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ciint ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 6808c2ecf20Sopenharmony_ci u32 temp1, temp2, reg1, reg2; 6818c2ecf20Sopenharmony_ci int t1, t2, interval, ret = 0; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci ret = ti_bandgap_validate(bgp, id); 6848c2ecf20Sopenharmony_ci if (ret) 6858c2ecf20Sopenharmony_ci goto exit; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (!TI_BANDGAP_HAS(bgp, HISTORY_BUFFER) || 6888c2ecf20Sopenharmony_ci !TI_BANDGAP_HAS(bgp, FREEZE_BIT)) { 6898c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 6908c2ecf20Sopenharmony_ci goto exit; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci spin_lock(&bgp->lock); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[id].registers; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* Freeze and read the last 2 valid readings */ 6988c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 1); 6998c2ecf20Sopenharmony_ci reg1 = tsr->ctrl_dtemp_1; 7008c2ecf20Sopenharmony_ci reg2 = tsr->ctrl_dtemp_2; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* read temperature from history buffer */ 7038c2ecf20Sopenharmony_ci temp1 = ti_bandgap_readl(bgp, reg1); 7048c2ecf20Sopenharmony_ci temp1 &= tsr->bgap_dtemp_mask; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci temp2 = ti_bandgap_readl(bgp, reg2); 7078c2ecf20Sopenharmony_ci temp2 &= tsr->bgap_dtemp_mask; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* Convert from adc values to mCelsius temperature */ 7108c2ecf20Sopenharmony_ci ret = ti_bandgap_adc_to_mcelsius(bgp, temp1, &t1); 7118c2ecf20Sopenharmony_ci if (ret) 7128c2ecf20Sopenharmony_ci goto unfreeze; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci ret = ti_bandgap_adc_to_mcelsius(bgp, temp2, &t2); 7158c2ecf20Sopenharmony_ci if (ret) 7168c2ecf20Sopenharmony_ci goto unfreeze; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Fetch the update interval */ 7198c2ecf20Sopenharmony_ci ret = ti_bandgap_read_update_interval(bgp, id, &interval); 7208c2ecf20Sopenharmony_ci if (ret) 7218c2ecf20Sopenharmony_ci goto unfreeze; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* Set the interval to 1 ms if bandgap counter delay is not set */ 7248c2ecf20Sopenharmony_ci if (interval == 0) 7258c2ecf20Sopenharmony_ci interval = 1; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci *trend = (t1 - t2) / interval; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci dev_dbg(bgp->dev, "The temperatures are t1 = %d and t2 = %d and trend =%d\n", 7308c2ecf20Sopenharmony_ci t1, t2, *trend); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ciunfreeze: 7338c2ecf20Sopenharmony_ci RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 0); 7348c2ecf20Sopenharmony_ci spin_unlock(&bgp->lock); 7358c2ecf20Sopenharmony_ciexit: 7368c2ecf20Sopenharmony_ci return ret; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/** 7408c2ecf20Sopenharmony_ci * ti_bandgap_tshut_init() - setup and initialize tshut handling 7418c2ecf20Sopenharmony_ci * @bgp: pointer to struct ti_bandgap 7428c2ecf20Sopenharmony_ci * @pdev: pointer to device struct platform_device 7438c2ecf20Sopenharmony_ci * 7448c2ecf20Sopenharmony_ci * Call this function only in case the bandgap features HAS(TSHUT). 7458c2ecf20Sopenharmony_ci * In this case, the driver needs to handle the TSHUT signal as an IRQ. 7468c2ecf20Sopenharmony_ci * The IRQ is wired as a GPIO, and for this purpose, it is required 7478c2ecf20Sopenharmony_ci * to specify which GPIO line is used. TSHUT IRQ is fired anytime 7488c2ecf20Sopenharmony_ci * one of the bandgap sensors violates the TSHUT high/hot threshold. 7498c2ecf20Sopenharmony_ci * And in that case, the system must go off. 7508c2ecf20Sopenharmony_ci * 7518c2ecf20Sopenharmony_ci * Return: 0 if no error, else error status 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_cistatic int ti_bandgap_tshut_init(struct ti_bandgap *bgp, 7548c2ecf20Sopenharmony_ci struct platform_device *pdev) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci int status; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci status = request_irq(gpiod_to_irq(bgp->tshut_gpiod), 7598c2ecf20Sopenharmony_ci ti_bandgap_tshut_irq_handler, 7608c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING, "tshut", NULL); 7618c2ecf20Sopenharmony_ci if (status) 7628c2ecf20Sopenharmony_ci dev_err(bgp->dev, "request irq failed for TSHUT"); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/** 7688c2ecf20Sopenharmony_ci * ti_bandgap_alert_init() - setup and initialize talert handling 7698c2ecf20Sopenharmony_ci * @bgp: pointer to struct ti_bandgap 7708c2ecf20Sopenharmony_ci * @pdev: pointer to device struct platform_device 7718c2ecf20Sopenharmony_ci * 7728c2ecf20Sopenharmony_ci * Call this function only in case the bandgap features HAS(TALERT). 7738c2ecf20Sopenharmony_ci * In this case, the driver needs to handle the TALERT signals as an IRQs. 7748c2ecf20Sopenharmony_ci * TALERT is a normal IRQ and it is fired any time thresholds (hot or cold) 7758c2ecf20Sopenharmony_ci * are violated. In these situation, the driver must reprogram the thresholds, 7768c2ecf20Sopenharmony_ci * accordingly to specified policy. 7778c2ecf20Sopenharmony_ci * 7788c2ecf20Sopenharmony_ci * Return: 0 if no error, else return corresponding error. 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_cistatic int ti_bandgap_talert_init(struct ti_bandgap *bgp, 7818c2ecf20Sopenharmony_ci struct platform_device *pdev) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci int ret; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci bgp->irq = platform_get_irq(pdev, 0); 7868c2ecf20Sopenharmony_ci if (bgp->irq < 0) 7878c2ecf20Sopenharmony_ci return bgp->irq; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci ret = request_threaded_irq(bgp->irq, NULL, 7908c2ecf20Sopenharmony_ci ti_bandgap_talert_irq_handler, 7918c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 7928c2ecf20Sopenharmony_ci "talert", bgp); 7938c2ecf20Sopenharmony_ci if (ret) { 7948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Request threaded irq failed.\n"); 7958c2ecf20Sopenharmony_ci return ret; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return 0; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic const struct of_device_id of_ti_bandgap_match[]; 8028c2ecf20Sopenharmony_ci/** 8038c2ecf20Sopenharmony_ci * ti_bandgap_build() - parse DT and setup a struct ti_bandgap 8048c2ecf20Sopenharmony_ci * @pdev: pointer to device struct platform_device 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * Used to read the device tree properties accordingly to the bandgap 8078c2ecf20Sopenharmony_ci * matching version. Based on bandgap version and its capabilities it 8088c2ecf20Sopenharmony_ci * will build a struct ti_bandgap out of the required DT entries. 8098c2ecf20Sopenharmony_ci * 8108c2ecf20Sopenharmony_ci * Return: valid bandgap structure if successful, else returns ERR_PTR 8118c2ecf20Sopenharmony_ci * return value must be verified with IS_ERR. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_cistatic struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 8168c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 8178c2ecf20Sopenharmony_ci struct ti_bandgap *bgp; 8188c2ecf20Sopenharmony_ci struct resource *res; 8198c2ecf20Sopenharmony_ci int i; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* just for the sake */ 8228c2ecf20Sopenharmony_ci if (!node) { 8238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform information available\n"); 8248c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci bgp = devm_kzalloc(&pdev->dev, sizeof(*bgp), GFP_KERNEL); 8288c2ecf20Sopenharmony_ci if (!bgp) 8298c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci of_id = of_match_device(of_ti_bandgap_match, &pdev->dev); 8328c2ecf20Sopenharmony_ci if (of_id) 8338c2ecf20Sopenharmony_ci bgp->conf = of_id->data; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* register shadow for context save and restore */ 8368c2ecf20Sopenharmony_ci bgp->regval = devm_kcalloc(&pdev->dev, bgp->conf->sensor_count, 8378c2ecf20Sopenharmony_ci sizeof(*bgp->regval), GFP_KERNEL); 8388c2ecf20Sopenharmony_ci if (!bgp->regval) 8398c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci i = 0; 8428c2ecf20Sopenharmony_ci do { 8438c2ecf20Sopenharmony_ci void __iomem *chunk; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, i); 8468c2ecf20Sopenharmony_ci if (!res) 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci chunk = devm_ioremap_resource(&pdev->dev, res); 8498c2ecf20Sopenharmony_ci if (i == 0) 8508c2ecf20Sopenharmony_ci bgp->base = chunk; 8518c2ecf20Sopenharmony_ci if (IS_ERR(chunk)) 8528c2ecf20Sopenharmony_ci return ERR_CAST(chunk); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci i++; 8558c2ecf20Sopenharmony_ci } while (res); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TSHUT)) { 8588c2ecf20Sopenharmony_ci bgp->tshut_gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN); 8598c2ecf20Sopenharmony_ci if (IS_ERR(bgp->tshut_gpiod)) { 8608c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid gpio for tshut\n"); 8618c2ecf20Sopenharmony_ci return ERR_CAST(bgp->tshut_gpiod); 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return bgp; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci/* 8698c2ecf20Sopenharmony_ci * List of SoCs on which the CPU PM notifier can cause erros on the DTEMP 8708c2ecf20Sopenharmony_ci * readout. 8718c2ecf20Sopenharmony_ci * Enabled notifier on these machines results in erroneous, random values which 8728c2ecf20Sopenharmony_ci * could trigger unexpected thermal shutdown. 8738c2ecf20Sopenharmony_ci */ 8748c2ecf20Sopenharmony_cistatic const struct soc_device_attribute soc_no_cpu_notifier[] = { 8758c2ecf20Sopenharmony_ci { .machine = "OMAP4430" }, 8768c2ecf20Sopenharmony_ci { /* sentinel */ }, 8778c2ecf20Sopenharmony_ci}; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci/*** Device driver call backs ***/ 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic 8828c2ecf20Sopenharmony_ciint ti_bandgap_probe(struct platform_device *pdev) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci struct ti_bandgap *bgp; 8858c2ecf20Sopenharmony_ci int clk_rate, ret, i; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci bgp = ti_bandgap_build(pdev); 8888c2ecf20Sopenharmony_ci if (IS_ERR(bgp)) { 8898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to fetch platform data\n"); 8908c2ecf20Sopenharmony_ci return PTR_ERR(bgp); 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci bgp->dev = &pdev->dev; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, UNRELIABLE)) 8958c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 8968c2ecf20Sopenharmony_ci "This OMAP thermal sensor is unreliable. You've been warned\n"); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TSHUT)) { 8998c2ecf20Sopenharmony_ci ret = ti_bandgap_tshut_init(bgp, pdev); 9008c2ecf20Sopenharmony_ci if (ret) { 9018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 9028c2ecf20Sopenharmony_ci "failed to initialize system tshut IRQ\n"); 9038c2ecf20Sopenharmony_ci return ret; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci bgp->fclock = clk_get(NULL, bgp->conf->fclock_name); 9088c2ecf20Sopenharmony_ci if (IS_ERR(bgp->fclock)) { 9098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to request fclock reference\n"); 9108c2ecf20Sopenharmony_ci ret = PTR_ERR(bgp->fclock); 9118c2ecf20Sopenharmony_ci goto free_irqs; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci bgp->div_clk = clk_get(NULL, bgp->conf->div_ck_name); 9158c2ecf20Sopenharmony_ci if (IS_ERR(bgp->div_clk)) { 9168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to request div_ts_ck clock ref\n"); 9178c2ecf20Sopenharmony_ci ret = PTR_ERR(bgp->div_clk); 9188c2ecf20Sopenharmony_ci goto put_fclock; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) { 9228c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 9238c2ecf20Sopenharmony_ci u32 val; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[i].registers; 9268c2ecf20Sopenharmony_ci /* 9278c2ecf20Sopenharmony_ci * check if the efuse has a non-zero value if not 9288c2ecf20Sopenharmony_ci * it is an untrimmed sample and the temperatures 9298c2ecf20Sopenharmony_ci * may not be accurate 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci val = ti_bandgap_readl(bgp, tsr->bgap_efuse); 9328c2ecf20Sopenharmony_ci if (!val) 9338c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 9348c2ecf20Sopenharmony_ci "Non-trimmed BGAP, Temp not accurate\n"); 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci clk_rate = clk_round_rate(bgp->div_clk, 9388c2ecf20Sopenharmony_ci bgp->conf->sensors[0].ts_data->max_freq); 9398c2ecf20Sopenharmony_ci if (clk_rate < bgp->conf->sensors[0].ts_data->min_freq || 9408c2ecf20Sopenharmony_ci clk_rate <= 0) { 9418c2ecf20Sopenharmony_ci ret = -ENODEV; 9428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "wrong clock rate (%d)\n", clk_rate); 9438c2ecf20Sopenharmony_ci goto put_clks; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ret = clk_set_rate(bgp->div_clk, clk_rate); 9478c2ecf20Sopenharmony_ci if (ret) 9488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n"); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci bgp->clk_rate = clk_rate; 9518c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) 9528c2ecf20Sopenharmony_ci clk_prepare_enable(bgp->fclock); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci spin_lock_init(&bgp->lock); 9568c2ecf20Sopenharmony_ci bgp->dev = &pdev->dev; 9578c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, bgp); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci ti_bandgap_power(bgp, true); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* Set default counter to 1 for now */ 9628c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, COUNTER)) 9638c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) 9648c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, bgap_counter, counter_mask, 1); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* Set default thresholds for alert and shutdown */ 9678c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) { 9688c2ecf20Sopenharmony_ci struct temp_sensor_data *ts_data; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci ts_data = bgp->conf->sensors[i].ts_data; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TALERT)) { 9738c2ecf20Sopenharmony_ci /* Set initial Talert thresholds */ 9748c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, bgap_threshold, 9758c2ecf20Sopenharmony_ci threshold_tcold_mask, ts_data->t_cold); 9768c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, bgap_threshold, 9778c2ecf20Sopenharmony_ci threshold_thot_mask, ts_data->t_hot); 9788c2ecf20Sopenharmony_ci /* Enable the alert events */ 9798c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, bgap_mask_ctrl, mask_hot_mask, 1); 9808c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, bgap_mask_ctrl, mask_cold_mask, 1); 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) { 9848c2ecf20Sopenharmony_ci /* Set initial Tshut thresholds */ 9858c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, tshut_threshold, 9868c2ecf20Sopenharmony_ci tshut_hot_mask, ts_data->tshut_hot); 9878c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, tshut_threshold, 9888c2ecf20Sopenharmony_ci tshut_cold_mask, ts_data->tshut_cold); 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) 9938c2ecf20Sopenharmony_ci ti_bandgap_set_continuous_mode(bgp); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* Set .250 seconds time as default counter */ 9968c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, COUNTER)) 9978c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) 9988c2ecf20Sopenharmony_ci RMW_BITS(bgp, i, bgap_counter, counter_mask, 9998c2ecf20Sopenharmony_ci bgp->clk_rate / 4); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* Every thing is good? Then expose the sensors */ 10028c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) { 10038c2ecf20Sopenharmony_ci char *domain; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (bgp->conf->sensors[i].register_cooling) { 10068c2ecf20Sopenharmony_ci ret = bgp->conf->sensors[i].register_cooling(bgp, i); 10078c2ecf20Sopenharmony_ci if (ret) 10088c2ecf20Sopenharmony_ci goto remove_sensors; 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (bgp->conf->expose_sensor) { 10128c2ecf20Sopenharmony_ci domain = bgp->conf->sensors[i].domain; 10138c2ecf20Sopenharmony_ci ret = bgp->conf->expose_sensor(bgp, i, domain); 10148c2ecf20Sopenharmony_ci if (ret) 10158c2ecf20Sopenharmony_ci goto remove_last_cooling; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* 10208c2ecf20Sopenharmony_ci * Enable the Interrupts once everything is set. Otherwise irq handler 10218c2ecf20Sopenharmony_ci * might be called as soon as it is enabled where as rest of framework 10228c2ecf20Sopenharmony_ci * is still getting initialised. 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TALERT)) { 10258c2ecf20Sopenharmony_ci ret = ti_bandgap_talert_init(bgp, pdev); 10268c2ecf20Sopenharmony_ci if (ret) { 10278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize Talert IRQ\n"); 10288c2ecf20Sopenharmony_ci i = bgp->conf->sensor_count; 10298c2ecf20Sopenharmony_ci goto disable_clk; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 10348c2ecf20Sopenharmony_ci bgp->nb.notifier_call = bandgap_omap_cpu_notifier; 10358c2ecf20Sopenharmony_ci if (!soc_device_match(soc_no_cpu_notifier)) 10368c2ecf20Sopenharmony_ci cpu_pm_register_notifier(&bgp->nb); 10378c2ecf20Sopenharmony_ci#endif 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci return 0; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ciremove_last_cooling: 10428c2ecf20Sopenharmony_ci if (bgp->conf->sensors[i].unregister_cooling) 10438c2ecf20Sopenharmony_ci bgp->conf->sensors[i].unregister_cooling(bgp, i); 10448c2ecf20Sopenharmony_ciremove_sensors: 10458c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) { 10468c2ecf20Sopenharmony_ci if (bgp->conf->sensors[i].unregister_cooling) 10478c2ecf20Sopenharmony_ci bgp->conf->sensors[i].unregister_cooling(bgp, i); 10488c2ecf20Sopenharmony_ci if (bgp->conf->remove_sensor) 10498c2ecf20Sopenharmony_ci bgp->conf->remove_sensor(bgp, i); 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci ti_bandgap_power(bgp, false); 10528c2ecf20Sopenharmony_cidisable_clk: 10538c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) 10548c2ecf20Sopenharmony_ci clk_disable_unprepare(bgp->fclock); 10558c2ecf20Sopenharmony_ciput_clks: 10568c2ecf20Sopenharmony_ci clk_put(bgp->div_clk); 10578c2ecf20Sopenharmony_ciput_fclock: 10588c2ecf20Sopenharmony_ci clk_put(bgp->fclock); 10598c2ecf20Sopenharmony_cifree_irqs: 10608c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TSHUT)) 10618c2ecf20Sopenharmony_ci free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci return ret; 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_cistatic 10678c2ecf20Sopenharmony_ciint ti_bandgap_remove(struct platform_device *pdev) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct ti_bandgap *bgp = platform_get_drvdata(pdev); 10708c2ecf20Sopenharmony_ci int i; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (!soc_device_match(soc_no_cpu_notifier)) 10738c2ecf20Sopenharmony_ci cpu_pm_unregister_notifier(&bgp->nb); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* Remove sensor interfaces */ 10768c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) { 10778c2ecf20Sopenharmony_ci if (bgp->conf->sensors[i].unregister_cooling) 10788c2ecf20Sopenharmony_ci bgp->conf->sensors[i].unregister_cooling(bgp, i); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci if (bgp->conf->remove_sensor) 10818c2ecf20Sopenharmony_ci bgp->conf->remove_sensor(bgp, i); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci ti_bandgap_power(bgp, false); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) 10878c2ecf20Sopenharmony_ci clk_disable_unprepare(bgp->fclock); 10888c2ecf20Sopenharmony_ci clk_put(bgp->fclock); 10898c2ecf20Sopenharmony_ci clk_put(bgp->div_clk); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TALERT)) 10928c2ecf20Sopenharmony_ci free_irq(bgp->irq, bgp); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TSHUT)) 10958c2ecf20Sopenharmony_ci free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return 0; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11018c2ecf20Sopenharmony_cistatic int ti_bandgap_save_ctxt(struct ti_bandgap *bgp) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci int i; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) { 11068c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 11078c2ecf20Sopenharmony_ci struct temp_sensor_regval *rval; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci rval = &bgp->regval[i]; 11108c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[i].registers; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) 11138c2ecf20Sopenharmony_ci rval->bg_mode_ctrl = ti_bandgap_readl(bgp, 11148c2ecf20Sopenharmony_ci tsr->bgap_mode_ctrl); 11158c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, COUNTER)) 11168c2ecf20Sopenharmony_ci rval->bg_counter = ti_bandgap_readl(bgp, 11178c2ecf20Sopenharmony_ci tsr->bgap_counter); 11188c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TALERT)) { 11198c2ecf20Sopenharmony_ci rval->bg_threshold = ti_bandgap_readl(bgp, 11208c2ecf20Sopenharmony_ci tsr->bgap_threshold); 11218c2ecf20Sopenharmony_ci rval->bg_ctrl = ti_bandgap_readl(bgp, 11228c2ecf20Sopenharmony_ci tsr->bgap_mask_ctrl); 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) 11268c2ecf20Sopenharmony_ci rval->tshut_threshold = ti_bandgap_readl(bgp, 11278c2ecf20Sopenharmony_ci tsr->tshut_threshold); 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci return 0; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic int ti_bandgap_restore_ctxt(struct ti_bandgap *bgp) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci int i; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci for (i = 0; i < bgp->conf->sensor_count; i++) { 11388c2ecf20Sopenharmony_ci struct temp_sensor_registers *tsr; 11398c2ecf20Sopenharmony_ci struct temp_sensor_regval *rval; 11408c2ecf20Sopenharmony_ci u32 val = 0; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci rval = &bgp->regval[i]; 11438c2ecf20Sopenharmony_ci tsr = bgp->conf->sensors[i].registers; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, COUNTER)) 11468c2ecf20Sopenharmony_ci val = ti_bandgap_readl(bgp, tsr->bgap_counter); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) 11498c2ecf20Sopenharmony_ci ti_bandgap_writel(bgp, rval->tshut_threshold, 11508c2ecf20Sopenharmony_ci tsr->tshut_threshold); 11518c2ecf20Sopenharmony_ci /* Force immediate temperature measurement and update 11528c2ecf20Sopenharmony_ci * of the DTEMP field 11538c2ecf20Sopenharmony_ci */ 11548c2ecf20Sopenharmony_ci ti_bandgap_force_single_read(bgp, i); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, COUNTER)) 11578c2ecf20Sopenharmony_ci ti_bandgap_writel(bgp, rval->bg_counter, 11588c2ecf20Sopenharmony_ci tsr->bgap_counter); 11598c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) 11608c2ecf20Sopenharmony_ci ti_bandgap_writel(bgp, rval->bg_mode_ctrl, 11618c2ecf20Sopenharmony_ci tsr->bgap_mode_ctrl); 11628c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, TALERT)) { 11638c2ecf20Sopenharmony_ci ti_bandgap_writel(bgp, rval->bg_threshold, 11648c2ecf20Sopenharmony_ci tsr->bgap_threshold); 11658c2ecf20Sopenharmony_ci ti_bandgap_writel(bgp, rval->bg_ctrl, 11668c2ecf20Sopenharmony_ci tsr->bgap_mask_ctrl); 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci return 0; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic int ti_bandgap_suspend(struct device *dev) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci struct ti_bandgap *bgp = dev_get_drvdata(dev); 11768c2ecf20Sopenharmony_ci int err; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci err = ti_bandgap_save_ctxt(bgp); 11798c2ecf20Sopenharmony_ci ti_bandgap_power(bgp, false); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) 11828c2ecf20Sopenharmony_ci clk_disable_unprepare(bgp->fclock); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci bgp->is_suspended = true; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return err; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic int bandgap_omap_cpu_notifier(struct notifier_block *nb, 11908c2ecf20Sopenharmony_ci unsigned long cmd, void *v) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci struct ti_bandgap *bgp; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci bgp = container_of(nb, struct ti_bandgap, nb); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci spin_lock(&bgp->lock); 11978c2ecf20Sopenharmony_ci switch (cmd) { 11988c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 11998c2ecf20Sopenharmony_ci if (bgp->is_suspended) 12008c2ecf20Sopenharmony_ci break; 12018c2ecf20Sopenharmony_ci ti_bandgap_save_ctxt(bgp); 12028c2ecf20Sopenharmony_ci ti_bandgap_power(bgp, false); 12038c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) 12048c2ecf20Sopenharmony_ci clk_disable(bgp->fclock); 12058c2ecf20Sopenharmony_ci break; 12068c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER_FAILED: 12078c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 12088c2ecf20Sopenharmony_ci if (bgp->is_suspended) 12098c2ecf20Sopenharmony_ci break; 12108c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) 12118c2ecf20Sopenharmony_ci clk_enable(bgp->fclock); 12128c2ecf20Sopenharmony_ci ti_bandgap_power(bgp, true); 12138c2ecf20Sopenharmony_ci ti_bandgap_restore_ctxt(bgp); 12148c2ecf20Sopenharmony_ci break; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci spin_unlock(&bgp->lock); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci return NOTIFY_OK; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic int ti_bandgap_resume(struct device *dev) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci struct ti_bandgap *bgp = dev_get_drvdata(dev); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) 12268c2ecf20Sopenharmony_ci clk_prepare_enable(bgp->fclock); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci ti_bandgap_power(bgp, true); 12298c2ecf20Sopenharmony_ci bgp->is_suspended = false; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return ti_bandgap_restore_ctxt(bgp); 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ti_bandgap_dev_pm_ops, ti_bandgap_suspend, 12348c2ecf20Sopenharmony_ci ti_bandgap_resume); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci#define DEV_PM_OPS (&ti_bandgap_dev_pm_ops) 12378c2ecf20Sopenharmony_ci#else 12388c2ecf20Sopenharmony_ci#define DEV_PM_OPS NULL 12398c2ecf20Sopenharmony_ci#endif 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic const struct of_device_id of_ti_bandgap_match[] = { 12428c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP3_THERMAL 12438c2ecf20Sopenharmony_ci { 12448c2ecf20Sopenharmony_ci .compatible = "ti,omap34xx-bandgap", 12458c2ecf20Sopenharmony_ci .data = (void *)&omap34xx_data, 12468c2ecf20Sopenharmony_ci }, 12478c2ecf20Sopenharmony_ci { 12488c2ecf20Sopenharmony_ci .compatible = "ti,omap36xx-bandgap", 12498c2ecf20Sopenharmony_ci .data = (void *)&omap36xx_data, 12508c2ecf20Sopenharmony_ci }, 12518c2ecf20Sopenharmony_ci#endif 12528c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP4_THERMAL 12538c2ecf20Sopenharmony_ci { 12548c2ecf20Sopenharmony_ci .compatible = "ti,omap4430-bandgap", 12558c2ecf20Sopenharmony_ci .data = (void *)&omap4430_data, 12568c2ecf20Sopenharmony_ci }, 12578c2ecf20Sopenharmony_ci { 12588c2ecf20Sopenharmony_ci .compatible = "ti,omap4460-bandgap", 12598c2ecf20Sopenharmony_ci .data = (void *)&omap4460_data, 12608c2ecf20Sopenharmony_ci }, 12618c2ecf20Sopenharmony_ci { 12628c2ecf20Sopenharmony_ci .compatible = "ti,omap4470-bandgap", 12638c2ecf20Sopenharmony_ci .data = (void *)&omap4470_data, 12648c2ecf20Sopenharmony_ci }, 12658c2ecf20Sopenharmony_ci#endif 12668c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP5_THERMAL 12678c2ecf20Sopenharmony_ci { 12688c2ecf20Sopenharmony_ci .compatible = "ti,omap5430-bandgap", 12698c2ecf20Sopenharmony_ci .data = (void *)&omap5430_data, 12708c2ecf20Sopenharmony_ci }, 12718c2ecf20Sopenharmony_ci#endif 12728c2ecf20Sopenharmony_ci#ifdef CONFIG_DRA752_THERMAL 12738c2ecf20Sopenharmony_ci { 12748c2ecf20Sopenharmony_ci .compatible = "ti,dra752-bandgap", 12758c2ecf20Sopenharmony_ci .data = (void *)&dra752_data, 12768c2ecf20Sopenharmony_ci }, 12778c2ecf20Sopenharmony_ci#endif 12788c2ecf20Sopenharmony_ci /* Sentinel */ 12798c2ecf20Sopenharmony_ci { }, 12808c2ecf20Sopenharmony_ci}; 12818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_ti_bandgap_match); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic struct platform_driver ti_bandgap_sensor_driver = { 12848c2ecf20Sopenharmony_ci .probe = ti_bandgap_probe, 12858c2ecf20Sopenharmony_ci .remove = ti_bandgap_remove, 12868c2ecf20Sopenharmony_ci .driver = { 12878c2ecf20Sopenharmony_ci .name = "ti-soc-thermal", 12888c2ecf20Sopenharmony_ci .pm = DEV_PM_OPS, 12898c2ecf20Sopenharmony_ci .of_match_table = of_ti_bandgap_match, 12908c2ecf20Sopenharmony_ci }, 12918c2ecf20Sopenharmony_ci}; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cimodule_platform_driver(ti_bandgap_sensor_driver); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OMAP4+ bandgap temperature sensor driver"); 12968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 12978c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ti-soc-thermal"); 12988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instrument Inc."); 1299