18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru> 78c2ecf20Sopenharmony_ci * Serge Semin <Sergey.Semin@baikalelectronics.ru> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Baikal-T1 Process, Voltage, Temperature sensor driver 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 138c2ecf20Sopenharmony_ci#include <linux/bitops.h> 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/completion.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 198c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/ktime.h> 248c2ecf20Sopenharmony_ci#include <linux/limits.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/mutex.h> 278c2ecf20Sopenharmony_ci#include <linux/of.h> 288c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 298c2ecf20Sopenharmony_ci#include <linux/seqlock.h> 308c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 318c2ecf20Sopenharmony_ci#include <linux/types.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "bt1-pvt.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * For the sake of the code simplification we created the sensors info table 378c2ecf20Sopenharmony_ci * with the sensor names, activation modes, threshold registers base address 388c2ecf20Sopenharmony_ci * and the thresholds bit fields. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic const struct pvt_sensor_info pvt_info[] = { 418c2ecf20Sopenharmony_ci PVT_SENSOR_INFO(0, "CPU Core Temperature", hwmon_temp, TEMP, TTHRES), 428c2ecf20Sopenharmony_ci PVT_SENSOR_INFO(0, "CPU Core Voltage", hwmon_in, VOLT, VTHRES), 438c2ecf20Sopenharmony_ci PVT_SENSOR_INFO(1, "CPU Core Low-Vt", hwmon_in, LVT, LTHRES), 448c2ecf20Sopenharmony_ci PVT_SENSOR_INFO(2, "CPU Core High-Vt", hwmon_in, HVT, HTHRES), 458c2ecf20Sopenharmony_ci PVT_SENSOR_INFO(3, "CPU Core Standard-Vt", hwmon_in, SVT, STHRES), 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * The original translation formulae of the temperature (in degrees of Celsius) 508c2ecf20Sopenharmony_ci * to PVT data and vice-versa are following: 518c2ecf20Sopenharmony_ci * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) + 528c2ecf20Sopenharmony_ci * 1.7204e2, 538c2ecf20Sopenharmony_ci * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) + 548c2ecf20Sopenharmony_ci * 3.1020e-1*(N^1) - 4.838e1, 558c2ecf20Sopenharmony_ci * where T = [-48.380, 147.438]C and N = [0, 1023]. 568c2ecf20Sopenharmony_ci * They must be accordingly altered to be suitable for the integer arithmetics. 578c2ecf20Sopenharmony_ci * The technique is called 'factor redistribution', which just makes sure the 588c2ecf20Sopenharmony_ci * multiplications and divisions are made so to have a result of the operations 598c2ecf20Sopenharmony_ci * within the integer numbers limit. In addition we need to translate the 608c2ecf20Sopenharmony_ci * formulae to accept millidegrees of Celsius. Here what they look like after 618c2ecf20Sopenharmony_ci * the alterations: 628c2ecf20Sopenharmony_ci * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T + 638c2ecf20Sopenharmony_ci * 17204e2) / 1e4, 648c2ecf20Sopenharmony_ci * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D - 658c2ecf20Sopenharmony_ci * 48380, 668c2ecf20Sopenharmony_ci * where T = [-48380, 147438] mC and N = [0, 1023]. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic const struct pvt_poly __maybe_unused poly_temp_to_N = { 698c2ecf20Sopenharmony_ci .total_divider = 10000, 708c2ecf20Sopenharmony_ci .terms = { 718c2ecf20Sopenharmony_ci {4, 18322, 10000, 10000}, 728c2ecf20Sopenharmony_ci {3, 2343, 10000, 10}, 738c2ecf20Sopenharmony_ci {2, 87018, 10000, 10}, 748c2ecf20Sopenharmony_ci {1, 39269, 1000, 1}, 758c2ecf20Sopenharmony_ci {0, 1720400, 1, 1} 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const struct pvt_poly poly_N_to_temp = { 808c2ecf20Sopenharmony_ci .total_divider = 1, 818c2ecf20Sopenharmony_ci .terms = { 828c2ecf20Sopenharmony_ci {4, -16743, 1000, 1}, 838c2ecf20Sopenharmony_ci {3, 81542, 1000, 1}, 848c2ecf20Sopenharmony_ci {2, -182010, 1000, 1}, 858c2ecf20Sopenharmony_ci {1, 310200, 1000, 1}, 868c2ecf20Sopenharmony_ci {0, -48380, 1, 1} 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * Similar alterations are performed for the voltage conversion equations. 928c2ecf20Sopenharmony_ci * The original formulae are: 938c2ecf20Sopenharmony_ci * N = 1.8658e3*V - 1.1572e3, 948c2ecf20Sopenharmony_ci * V = (N + 1.1572e3) / 1.8658e3, 958c2ecf20Sopenharmony_ci * where V = [0.620, 1.168] V and N = [0, 1023]. 968c2ecf20Sopenharmony_ci * After the optimization they looks as follows: 978c2ecf20Sopenharmony_ci * N = (18658e-3*V - 11572) / 10, 988c2ecf20Sopenharmony_ci * V = N * 10^5 / 18658 + 11572 * 10^4 / 18658. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistatic const struct pvt_poly __maybe_unused poly_volt_to_N = { 1018c2ecf20Sopenharmony_ci .total_divider = 10, 1028c2ecf20Sopenharmony_ci .terms = { 1038c2ecf20Sopenharmony_ci {1, 18658, 1000, 1}, 1048c2ecf20Sopenharmony_ci {0, -11572, 1, 1} 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const struct pvt_poly poly_N_to_volt = { 1098c2ecf20Sopenharmony_ci .total_divider = 10, 1108c2ecf20Sopenharmony_ci .terms = { 1118c2ecf20Sopenharmony_ci {1, 100000, 18658, 1}, 1128c2ecf20Sopenharmony_ci {0, 115720000, 1, 18658} 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Here is the polynomial calculation function, which performs the 1188c2ecf20Sopenharmony_ci * redistributed terms calculations. It's pretty straightforward. We walk 1198c2ecf20Sopenharmony_ci * over each degree term up to the free one, and perform the redistributed 1208c2ecf20Sopenharmony_ci * multiplication of the term coefficient, its divider (as for the rationale 1218c2ecf20Sopenharmony_ci * fraction representation), data power and the rational fraction divider 1228c2ecf20Sopenharmony_ci * leftover. Then all of this is collected in a total sum variable, which 1238c2ecf20Sopenharmony_ci * value is normalized by the total divider before being returned. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic long pvt_calc_poly(const struct pvt_poly *poly, long data) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci const struct pvt_poly_term *term = poly->terms; 1288c2ecf20Sopenharmony_ci long tmp, ret = 0; 1298c2ecf20Sopenharmony_ci int deg; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci do { 1328c2ecf20Sopenharmony_ci tmp = term->coef; 1338c2ecf20Sopenharmony_ci for (deg = 0; deg < term->deg; ++deg) 1348c2ecf20Sopenharmony_ci tmp = mult_frac(tmp, data, term->divider); 1358c2ecf20Sopenharmony_ci ret += tmp / term->divider_leftover; 1368c2ecf20Sopenharmony_ci } while ((term++)->deg); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return ret / poly->total_divider; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci u32 old; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci old = readl_relaxed(reg); 1468c2ecf20Sopenharmony_ci writel((old & ~mask) | (data & mask), reg); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return old & mask; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Baikal-T1 PVT mode can be updated only when the controller is disabled. 1538c2ecf20Sopenharmony_ci * So first we disable it, then set the new mode together with the controller 1548c2ecf20Sopenharmony_ci * getting back enabled. The same concerns the temperature trim and 1558c2ecf20Sopenharmony_ci * measurements timeout. If it is necessary the interface mutex is supposed 1568c2ecf20Sopenharmony_ci * to be locked at the time the operations are performed. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci u32 old; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci mode = FIELD_PREP(PVT_CTRL_MODE_MASK, mode); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); 1658c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN, 1668c2ecf20Sopenharmony_ci mode | old); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic inline u32 pvt_calc_trim(long temp) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci temp = clamp_val(temp, 0, PVT_TRIM_TEMP); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return DIV_ROUND_UP(temp, PVT_TRIM_STEP); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci u32 old; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci trim = FIELD_PREP(PVT_CTRL_TRIM_MASK, trim); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); 1838c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN, 1848c2ecf20Sopenharmony_ci trim | old); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci u32 old; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); 1928c2ecf20Sopenharmony_ci writel(tout, pvt->regs + PVT_TTIMEOUT); 1938c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * This driver can optionally provide the hwmon alarms for each sensor the PVT 1988c2ecf20Sopenharmony_ci * controller supports. The alarms functionality is made compile-time 1998c2ecf20Sopenharmony_ci * configurable due to the hardware interface implementation peculiarity 2008c2ecf20Sopenharmony_ci * described further in this comment. So in case if alarms are unnecessary in 2018c2ecf20Sopenharmony_ci * your system design it's recommended to have them disabled to prevent the PVT 2028c2ecf20Sopenharmony_ci * IRQs being periodically raised to get the data cache/alarms status up to 2038c2ecf20Sopenharmony_ci * date. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * Baikal-T1 PVT embedded controller is based on the Analog Bits PVT sensor, 2068c2ecf20Sopenharmony_ci * but is equipped with a dedicated control wrapper. It exposes the PVT 2078c2ecf20Sopenharmony_ci * sub-block registers space via the APB3 bus. In addition the wrapper provides 2088c2ecf20Sopenharmony_ci * a common interrupt vector of the sensors conversion completion events and 2098c2ecf20Sopenharmony_ci * threshold value alarms. Alas the wrapper interface hasn't been fully thought 2108c2ecf20Sopenharmony_ci * through. There is only one sensor can be activated at a time, for which the 2118c2ecf20Sopenharmony_ci * thresholds comparator is enabled right after the data conversion is 2128c2ecf20Sopenharmony_ci * completed. Due to this if alarms need to be implemented for all available 2138c2ecf20Sopenharmony_ci * sensors we can't just set the thresholds and enable the interrupts. We need 2148c2ecf20Sopenharmony_ci * to enable the sensors one after another and let the controller to detect 2158c2ecf20Sopenharmony_ci * the alarms by itself at each conversion. This also makes pointless to handle 2168c2ecf20Sopenharmony_ci * the alarms interrupts, since in occasion they happen synchronously with 2178c2ecf20Sopenharmony_ci * data conversion completion. The best driver design would be to have the 2188c2ecf20Sopenharmony_ci * completion interrupts enabled only and keep the converted value in the 2198c2ecf20Sopenharmony_ci * driver data cache. This solution is implemented if hwmon alarms are enabled 2208c2ecf20Sopenharmony_ci * in this driver. In case if the alarms are disabled, the conversion is 2218c2ecf20Sopenharmony_ci * performed on demand at the time a sensors input file is read. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci#define pvt_hard_isr NULL 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic irqreturn_t pvt_soft_isr(int irq, void *data) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci const struct pvt_sensor_info *info; 2318c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt = data; 2328c2ecf20Sopenharmony_ci struct pvt_cache *cache; 2338c2ecf20Sopenharmony_ci u32 val, thres_sts, old; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * DVALID bit will be cleared by reading the data. We need to save the 2378c2ecf20Sopenharmony_ci * status before the next conversion happens. Threshold events will be 2388c2ecf20Sopenharmony_ci * handled a bit later. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* 2438c2ecf20Sopenharmony_ci * Then lets recharge the PVT interface with the next sampling mode. 2448c2ecf20Sopenharmony_ci * Lock the interface mutex to serialize trim, timeouts and alarm 2458c2ecf20Sopenharmony_ci * thresholds settings. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci cache = &pvt->cache[pvt->sensor]; 2488c2ecf20Sopenharmony_ci info = &pvt_info[pvt->sensor]; 2498c2ecf20Sopenharmony_ci pvt->sensor = (pvt->sensor == PVT_SENSOR_LAST) ? 2508c2ecf20Sopenharmony_ci PVT_SENSOR_FIRST : (pvt->sensor + 1); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * For some reason we have to mask the interrupt before changing the 2548c2ecf20Sopenharmony_ci * mode, otherwise sometimes the temperature mode doesn't get 2558c2ecf20Sopenharmony_ci * activated even though the actual mode in the ctrl register 2568c2ecf20Sopenharmony_ci * corresponds to one. Then we read the data. By doing so we also 2578c2ecf20Sopenharmony_ci * recharge the data conversion. After this the mode corresponding 2588c2ecf20Sopenharmony_ci * to the next sensor in the row is set. Finally we enable the 2598c2ecf20Sopenharmony_ci * interrupts back. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci mutex_lock(&pvt->iface_mtx); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 2648c2ecf20Sopenharmony_ci PVT_INTR_DVALID); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci val = readl(pvt->regs + PVT_DATA); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci pvt_set_mode(pvt, pvt_info[pvt->sensor].mode); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci mutex_unlock(&pvt->iface_mtx); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * We can now update the data cache with data just retrieved from the 2768c2ecf20Sopenharmony_ci * sensor. Lock write-seqlock to make sure the reader has a coherent 2778c2ecf20Sopenharmony_ci * data. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci write_seqlock(&cache->data_seqlock); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci cache->data = FIELD_GET(PVT_DATA_DATA_MASK, val); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci write_sequnlock(&cache->data_seqlock); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * While PVT core is doing the next mode data conversion, we'll check 2878c2ecf20Sopenharmony_ci * whether the alarms were triggered for the current sensor. Note that 2888c2ecf20Sopenharmony_ci * according to the documentation only one threshold IRQ status can be 2898c2ecf20Sopenharmony_ci * set at a time, that's why if-else statement is utilized. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci if ((thres_sts & info->thres_sts_lo) ^ cache->thres_sts_lo) { 2928c2ecf20Sopenharmony_ci WRITE_ONCE(cache->thres_sts_lo, thres_sts & info->thres_sts_lo); 2938c2ecf20Sopenharmony_ci hwmon_notify_event(pvt->hwmon, info->type, info->attr_min_alarm, 2948c2ecf20Sopenharmony_ci info->channel); 2958c2ecf20Sopenharmony_ci } else if ((thres_sts & info->thres_sts_hi) ^ cache->thres_sts_hi) { 2968c2ecf20Sopenharmony_ci WRITE_ONCE(cache->thres_sts_hi, thres_sts & info->thres_sts_hi); 2978c2ecf20Sopenharmony_ci hwmon_notify_event(pvt->hwmon, info->type, info->attr_max_alarm, 2988c2ecf20Sopenharmony_ci info->channel); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci return 0644; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci return 0444; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type, 3158c2ecf20Sopenharmony_ci long *val) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct pvt_cache *cache = &pvt->cache[type]; 3188c2ecf20Sopenharmony_ci unsigned int seq; 3198c2ecf20Sopenharmony_ci u32 data; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci do { 3228c2ecf20Sopenharmony_ci seq = read_seqbegin(&cache->data_seqlock); 3238c2ecf20Sopenharmony_ci data = cache->data; 3248c2ecf20Sopenharmony_ci } while (read_seqretry(&cache->data_seqlock, seq)); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (type == PVT_TEMP) 3278c2ecf20Sopenharmony_ci *val = pvt_calc_poly(&poly_N_to_temp, data); 3288c2ecf20Sopenharmony_ci else 3298c2ecf20Sopenharmony_ci *val = pvt_calc_poly(&poly_N_to_volt, data); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, 3358c2ecf20Sopenharmony_ci bool is_low, long *val) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci u32 data; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* No need in serialization, since it is just read from MMIO. */ 3408c2ecf20Sopenharmony_ci data = readl(pvt->regs + pvt_info[type].thres_base); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (is_low) 3438c2ecf20Sopenharmony_ci data = FIELD_GET(PVT_THRES_LO_MASK, data); 3448c2ecf20Sopenharmony_ci else 3458c2ecf20Sopenharmony_ci data = FIELD_GET(PVT_THRES_HI_MASK, data); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (type == PVT_TEMP) 3488c2ecf20Sopenharmony_ci *val = pvt_calc_poly(&poly_N_to_temp, data); 3498c2ecf20Sopenharmony_ci else 3508c2ecf20Sopenharmony_ci *val = pvt_calc_poly(&poly_N_to_volt, data); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, 3568c2ecf20Sopenharmony_ci bool is_low, long val) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci u32 data, limit, mask; 3598c2ecf20Sopenharmony_ci int ret; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (type == PVT_TEMP) { 3628c2ecf20Sopenharmony_ci val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX); 3638c2ecf20Sopenharmony_ci data = pvt_calc_poly(&poly_temp_to_N, val); 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX); 3668c2ecf20Sopenharmony_ci data = pvt_calc_poly(&poly_volt_to_N, val); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Serialize limit update, since a part of the register is changed. */ 3708c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&pvt->iface_mtx); 3718c2ecf20Sopenharmony_ci if (ret) 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* Make sure the upper and lower ranges don't intersect. */ 3758c2ecf20Sopenharmony_ci limit = readl(pvt->regs + pvt_info[type].thres_base); 3768c2ecf20Sopenharmony_ci if (is_low) { 3778c2ecf20Sopenharmony_ci limit = FIELD_GET(PVT_THRES_HI_MASK, limit); 3788c2ecf20Sopenharmony_ci data = clamp_val(data, PVT_DATA_MIN, limit); 3798c2ecf20Sopenharmony_ci data = FIELD_PREP(PVT_THRES_LO_MASK, data); 3808c2ecf20Sopenharmony_ci mask = PVT_THRES_LO_MASK; 3818c2ecf20Sopenharmony_ci } else { 3828c2ecf20Sopenharmony_ci limit = FIELD_GET(PVT_THRES_LO_MASK, limit); 3838c2ecf20Sopenharmony_ci data = clamp_val(data, limit, PVT_DATA_MAX); 3848c2ecf20Sopenharmony_ci data = FIELD_PREP(PVT_THRES_HI_MASK, data); 3858c2ecf20Sopenharmony_ci mask = PVT_THRES_HI_MASK; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci mutex_unlock(&pvt->iface_mtx); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type, 3968c2ecf20Sopenharmony_ci bool is_low, long *val) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci if (is_low) 3998c2ecf20Sopenharmony_ci *val = !!READ_ONCE(pvt->cache[type].thres_sts_lo); 4008c2ecf20Sopenharmony_ci else 4018c2ecf20Sopenharmony_ci *val = !!READ_ONCE(pvt->cache[type].thres_sts_hi); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *pvt_channel_info[] = { 4078c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(chip, 4088c2ecf20Sopenharmony_ci HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), 4098c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(temp, 4108c2ecf20Sopenharmony_ci HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL | 4118c2ecf20Sopenharmony_ci HWMON_T_MIN | HWMON_T_MIN_ALARM | 4128c2ecf20Sopenharmony_ci HWMON_T_MAX | HWMON_T_MAX_ALARM | 4138c2ecf20Sopenharmony_ci HWMON_T_OFFSET), 4148c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(in, 4158c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL | 4168c2ecf20Sopenharmony_ci HWMON_I_MIN | HWMON_I_MIN_ALARM | 4178c2ecf20Sopenharmony_ci HWMON_I_MAX | HWMON_I_MAX_ALARM, 4188c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL | 4198c2ecf20Sopenharmony_ci HWMON_I_MIN | HWMON_I_MIN_ALARM | 4208c2ecf20Sopenharmony_ci HWMON_I_MAX | HWMON_I_MAX_ALARM, 4218c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL | 4228c2ecf20Sopenharmony_ci HWMON_I_MIN | HWMON_I_MIN_ALARM | 4238c2ecf20Sopenharmony_ci HWMON_I_MAX | HWMON_I_MAX_ALARM, 4248c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL | 4258c2ecf20Sopenharmony_ci HWMON_I_MIN | HWMON_I_MIN_ALARM | 4268c2ecf20Sopenharmony_ci HWMON_I_MAX | HWMON_I_MAX_ALARM), 4278c2ecf20Sopenharmony_ci NULL 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */ 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic irqreturn_t pvt_hard_isr(int irq, void *data) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt = data; 4358c2ecf20Sopenharmony_ci struct pvt_cache *cache; 4368c2ecf20Sopenharmony_ci u32 val; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* 4398c2ecf20Sopenharmony_ci * Mask the DVALID interrupt so after exiting from the handler a 4408c2ecf20Sopenharmony_ci * repeated conversion wouldn't happen. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 4438c2ecf20Sopenharmony_ci PVT_INTR_DVALID); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* 4468c2ecf20Sopenharmony_ci * Nothing special for alarm-less driver. Just read the data, update 4478c2ecf20Sopenharmony_ci * the cache and notify a waiter of this event. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci val = readl(pvt->regs + PVT_DATA); 4508c2ecf20Sopenharmony_ci if (!(val & PVT_DATA_VALID)) { 4518c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Got IRQ when data isn't valid\n"); 4528c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci cache = &pvt->cache[pvt->sensor]; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci WRITE_ONCE(cache->data, FIELD_GET(PVT_DATA_DATA_MASK, val)); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci complete(&cache->conversion); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci#define pvt_soft_isr NULL 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type, 4778c2ecf20Sopenharmony_ci long *val) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct pvt_cache *cache = &pvt->cache[type]; 4808c2ecf20Sopenharmony_ci unsigned long timeout; 4818c2ecf20Sopenharmony_ci u32 data; 4828c2ecf20Sopenharmony_ci int ret; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* 4858c2ecf20Sopenharmony_ci * Lock PVT conversion interface until data cache is updated. The 4868c2ecf20Sopenharmony_ci * data read procedure is following: set the requested PVT sensor 4878c2ecf20Sopenharmony_ci * mode, enable IRQ and conversion, wait until conversion is finished, 4888c2ecf20Sopenharmony_ci * then disable conversion and IRQ, and read the cached data. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&pvt->iface_mtx); 4918c2ecf20Sopenharmony_ci if (ret) 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci pvt->sensor = type; 4958c2ecf20Sopenharmony_ci pvt_set_mode(pvt, pvt_info[type].mode); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * Unmask the DVALID interrupt and enable the sensors conversions. 4998c2ecf20Sopenharmony_ci * Do the reverse procedure when conversion is done. 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0); 5028c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * Wait with timeout since in case if the sensor is suddenly powered 5068c2ecf20Sopenharmony_ci * down the request won't be completed and the caller will hang up on 5078c2ecf20Sopenharmony_ci * this procedure until the power is back up again. Multiply the 5088c2ecf20Sopenharmony_ci * timeout by the factor of two to prevent a false timeout. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout)); 5118c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&cache->conversion, timeout); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); 5148c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 5158c2ecf20Sopenharmony_ci PVT_INTR_DVALID); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci data = READ_ONCE(cache->data); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci mutex_unlock(&pvt->iface_mtx); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (!ret) 5228c2ecf20Sopenharmony_ci return -ETIMEDOUT; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (type == PVT_TEMP) 5258c2ecf20Sopenharmony_ci *val = pvt_calc_poly(&poly_N_to_temp, data); 5268c2ecf20Sopenharmony_ci else 5278c2ecf20Sopenharmony_ci *val = pvt_calc_poly(&poly_N_to_volt, data); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, 5338c2ecf20Sopenharmony_ci bool is_low, long *val) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, 5398c2ecf20Sopenharmony_ci bool is_low, long val) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type, 5458c2ecf20Sopenharmony_ci bool is_low, long *val) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *pvt_channel_info[] = { 5518c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(chip, 5528c2ecf20Sopenharmony_ci HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), 5538c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(temp, 5548c2ecf20Sopenharmony_ci HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL | 5558c2ecf20Sopenharmony_ci HWMON_T_OFFSET), 5568c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(in, 5578c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 5588c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 5598c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL, 5608c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL), 5618c2ecf20Sopenharmony_ci NULL 5628c2ecf20Sopenharmony_ci}; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */ 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic inline bool pvt_hwmon_channel_is_valid(enum hwmon_sensor_types type, 5678c2ecf20Sopenharmony_ci int ch) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci switch (type) { 5708c2ecf20Sopenharmony_ci case hwmon_temp: 5718c2ecf20Sopenharmony_ci if (ch < 0 || ch >= PVT_TEMP_CHS) 5728c2ecf20Sopenharmony_ci return false; 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci case hwmon_in: 5758c2ecf20Sopenharmony_ci if (ch < 0 || ch >= PVT_VOLT_CHS) 5768c2ecf20Sopenharmony_ci return false; 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci default: 5798c2ecf20Sopenharmony_ci break; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* The rest of the types are independent from the channel number. */ 5838c2ecf20Sopenharmony_ci return true; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic umode_t pvt_hwmon_is_visible(const void *data, 5878c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 5888c2ecf20Sopenharmony_ci u32 attr, int ch) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci if (!pvt_hwmon_channel_is_valid(type, ch)) 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci switch (type) { 5948c2ecf20Sopenharmony_ci case hwmon_chip: 5958c2ecf20Sopenharmony_ci switch (attr) { 5968c2ecf20Sopenharmony_ci case hwmon_chip_update_interval: 5978c2ecf20Sopenharmony_ci return 0644; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci break; 6008c2ecf20Sopenharmony_ci case hwmon_temp: 6018c2ecf20Sopenharmony_ci switch (attr) { 6028c2ecf20Sopenharmony_ci case hwmon_temp_input: 6038c2ecf20Sopenharmony_ci case hwmon_temp_type: 6048c2ecf20Sopenharmony_ci case hwmon_temp_label: 6058c2ecf20Sopenharmony_ci return 0444; 6068c2ecf20Sopenharmony_ci case hwmon_temp_min: 6078c2ecf20Sopenharmony_ci case hwmon_temp_max: 6088c2ecf20Sopenharmony_ci return pvt_limit_is_visible(ch); 6098c2ecf20Sopenharmony_ci case hwmon_temp_min_alarm: 6108c2ecf20Sopenharmony_ci case hwmon_temp_max_alarm: 6118c2ecf20Sopenharmony_ci return pvt_alarm_is_visible(ch); 6128c2ecf20Sopenharmony_ci case hwmon_temp_offset: 6138c2ecf20Sopenharmony_ci return 0644; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci break; 6168c2ecf20Sopenharmony_ci case hwmon_in: 6178c2ecf20Sopenharmony_ci switch (attr) { 6188c2ecf20Sopenharmony_ci case hwmon_in_input: 6198c2ecf20Sopenharmony_ci case hwmon_in_label: 6208c2ecf20Sopenharmony_ci return 0444; 6218c2ecf20Sopenharmony_ci case hwmon_in_min: 6228c2ecf20Sopenharmony_ci case hwmon_in_max: 6238c2ecf20Sopenharmony_ci return pvt_limit_is_visible(PVT_VOLT + ch); 6248c2ecf20Sopenharmony_ci case hwmon_in_min_alarm: 6258c2ecf20Sopenharmony_ci case hwmon_in_max_alarm: 6268c2ecf20Sopenharmony_ci return pvt_alarm_is_visible(PVT_VOLT + ch); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci default: 6308c2ecf20Sopenharmony_ci break; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return 0; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int pvt_read_trim(struct pvt_hwmon *pvt, long *val) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci u32 data; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci data = readl(pvt->regs + PVT_CTRL); 6418c2ecf20Sopenharmony_ci *val = FIELD_GET(PVT_CTRL_TRIM_MASK, data) * PVT_TRIM_STEP; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic int pvt_write_trim(struct pvt_hwmon *pvt, long val) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci u32 trim; 6498c2ecf20Sopenharmony_ci int ret; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* 6528c2ecf20Sopenharmony_ci * Serialize trim update, since a part of the register is changed and 6538c2ecf20Sopenharmony_ci * the controller is supposed to be disabled during this operation. 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&pvt->iface_mtx); 6568c2ecf20Sopenharmony_ci if (ret) 6578c2ecf20Sopenharmony_ci return ret; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci trim = pvt_calc_trim(val); 6608c2ecf20Sopenharmony_ci pvt_set_trim(pvt, trim); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci mutex_unlock(&pvt->iface_mtx); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return 0; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic int pvt_read_timeout(struct pvt_hwmon *pvt, long *val) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci int ret; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&pvt->iface_mtx); 6728c2ecf20Sopenharmony_ci if (ret) 6738c2ecf20Sopenharmony_ci return ret; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* Return the result in msec as hwmon sysfs interface requires. */ 6768c2ecf20Sopenharmony_ci *val = ktime_to_ms(pvt->timeout); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci mutex_unlock(&pvt->iface_mtx); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci return 0; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic int pvt_write_timeout(struct pvt_hwmon *pvt, long val) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci unsigned long rate; 6868c2ecf20Sopenharmony_ci ktime_t kt, cache; 6878c2ecf20Sopenharmony_ci u32 data; 6888c2ecf20Sopenharmony_ci int ret; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk); 6918c2ecf20Sopenharmony_ci if (!rate) 6928c2ecf20Sopenharmony_ci return -ENODEV; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* 6958c2ecf20Sopenharmony_ci * If alarms are enabled, the requested timeout must be divided 6968c2ecf20Sopenharmony_ci * between all available sensors to have the requested delay 6978c2ecf20Sopenharmony_ci * applicable to each individual sensor. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci cache = kt = ms_to_ktime(val); 7008c2ecf20Sopenharmony_ci#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) 7018c2ecf20Sopenharmony_ci kt = ktime_divns(kt, PVT_SENSORS_NUM); 7028c2ecf20Sopenharmony_ci#endif 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* 7058c2ecf20Sopenharmony_ci * Subtract a constant lag, which always persists due to the limited 7068c2ecf20Sopenharmony_ci * PVT sampling rate. Make sure the timeout is not negative. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_ci kt = ktime_sub_ns(kt, PVT_TOUT_MIN); 7098c2ecf20Sopenharmony_ci if (ktime_to_ns(kt) < 0) 7108c2ecf20Sopenharmony_ci kt = ktime_set(0, 0); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* 7138c2ecf20Sopenharmony_ci * Finally recalculate the timeout in terms of the reference clock 7148c2ecf20Sopenharmony_ci * period. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_ci data = ktime_divns(kt * rate, NSEC_PER_SEC); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * Update the measurements delay, but lock the interface first, since 7208c2ecf20Sopenharmony_ci * we have to disable PVT in order to have the new delay actually 7218c2ecf20Sopenharmony_ci * updated. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&pvt->iface_mtx); 7248c2ecf20Sopenharmony_ci if (ret) 7258c2ecf20Sopenharmony_ci return ret; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci pvt_set_tout(pvt, data); 7288c2ecf20Sopenharmony_ci pvt->timeout = cache; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci mutex_unlock(&pvt->iface_mtx); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic int pvt_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 7368c2ecf20Sopenharmony_ci u32 attr, int ch, long *val) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt = dev_get_drvdata(dev); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!pvt_hwmon_channel_is_valid(type, ch)) 7418c2ecf20Sopenharmony_ci return -EINVAL; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci switch (type) { 7448c2ecf20Sopenharmony_ci case hwmon_chip: 7458c2ecf20Sopenharmony_ci switch (attr) { 7468c2ecf20Sopenharmony_ci case hwmon_chip_update_interval: 7478c2ecf20Sopenharmony_ci return pvt_read_timeout(pvt, val); 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci case hwmon_temp: 7518c2ecf20Sopenharmony_ci switch (attr) { 7528c2ecf20Sopenharmony_ci case hwmon_temp_input: 7538c2ecf20Sopenharmony_ci return pvt_read_data(pvt, ch, val); 7548c2ecf20Sopenharmony_ci case hwmon_temp_type: 7558c2ecf20Sopenharmony_ci *val = 1; 7568c2ecf20Sopenharmony_ci return 0; 7578c2ecf20Sopenharmony_ci case hwmon_temp_min: 7588c2ecf20Sopenharmony_ci return pvt_read_limit(pvt, ch, true, val); 7598c2ecf20Sopenharmony_ci case hwmon_temp_max: 7608c2ecf20Sopenharmony_ci return pvt_read_limit(pvt, ch, false, val); 7618c2ecf20Sopenharmony_ci case hwmon_temp_min_alarm: 7628c2ecf20Sopenharmony_ci return pvt_read_alarm(pvt, ch, true, val); 7638c2ecf20Sopenharmony_ci case hwmon_temp_max_alarm: 7648c2ecf20Sopenharmony_ci return pvt_read_alarm(pvt, ch, false, val); 7658c2ecf20Sopenharmony_ci case hwmon_temp_offset: 7668c2ecf20Sopenharmony_ci return pvt_read_trim(pvt, val); 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci break; 7698c2ecf20Sopenharmony_ci case hwmon_in: 7708c2ecf20Sopenharmony_ci switch (attr) { 7718c2ecf20Sopenharmony_ci case hwmon_in_input: 7728c2ecf20Sopenharmony_ci return pvt_read_data(pvt, PVT_VOLT + ch, val); 7738c2ecf20Sopenharmony_ci case hwmon_in_min: 7748c2ecf20Sopenharmony_ci return pvt_read_limit(pvt, PVT_VOLT + ch, true, val); 7758c2ecf20Sopenharmony_ci case hwmon_in_max: 7768c2ecf20Sopenharmony_ci return pvt_read_limit(pvt, PVT_VOLT + ch, false, val); 7778c2ecf20Sopenharmony_ci case hwmon_in_min_alarm: 7788c2ecf20Sopenharmony_ci return pvt_read_alarm(pvt, PVT_VOLT + ch, true, val); 7798c2ecf20Sopenharmony_ci case hwmon_in_max_alarm: 7808c2ecf20Sopenharmony_ci return pvt_read_alarm(pvt, PVT_VOLT + ch, false, val); 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci break; 7838c2ecf20Sopenharmony_ci default: 7848c2ecf20Sopenharmony_ci break; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic int pvt_hwmon_read_string(struct device *dev, 7918c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 7928c2ecf20Sopenharmony_ci u32 attr, int ch, const char **str) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci if (!pvt_hwmon_channel_is_valid(type, ch)) 7958c2ecf20Sopenharmony_ci return -EINVAL; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci switch (type) { 7988c2ecf20Sopenharmony_ci case hwmon_temp: 7998c2ecf20Sopenharmony_ci switch (attr) { 8008c2ecf20Sopenharmony_ci case hwmon_temp_label: 8018c2ecf20Sopenharmony_ci *str = pvt_info[ch].label; 8028c2ecf20Sopenharmony_ci return 0; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci break; 8058c2ecf20Sopenharmony_ci case hwmon_in: 8068c2ecf20Sopenharmony_ci switch (attr) { 8078c2ecf20Sopenharmony_ci case hwmon_in_label: 8088c2ecf20Sopenharmony_ci *str = pvt_info[PVT_VOLT + ch].label; 8098c2ecf20Sopenharmony_ci return 0; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci break; 8128c2ecf20Sopenharmony_ci default: 8138c2ecf20Sopenharmony_ci break; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int pvt_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 8208c2ecf20Sopenharmony_ci u32 attr, int ch, long val) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt = dev_get_drvdata(dev); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (!pvt_hwmon_channel_is_valid(type, ch)) 8258c2ecf20Sopenharmony_ci return -EINVAL; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci switch (type) { 8288c2ecf20Sopenharmony_ci case hwmon_chip: 8298c2ecf20Sopenharmony_ci switch (attr) { 8308c2ecf20Sopenharmony_ci case hwmon_chip_update_interval: 8318c2ecf20Sopenharmony_ci return pvt_write_timeout(pvt, val); 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci break; 8348c2ecf20Sopenharmony_ci case hwmon_temp: 8358c2ecf20Sopenharmony_ci switch (attr) { 8368c2ecf20Sopenharmony_ci case hwmon_temp_min: 8378c2ecf20Sopenharmony_ci return pvt_write_limit(pvt, ch, true, val); 8388c2ecf20Sopenharmony_ci case hwmon_temp_max: 8398c2ecf20Sopenharmony_ci return pvt_write_limit(pvt, ch, false, val); 8408c2ecf20Sopenharmony_ci case hwmon_temp_offset: 8418c2ecf20Sopenharmony_ci return pvt_write_trim(pvt, val); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci case hwmon_in: 8458c2ecf20Sopenharmony_ci switch (attr) { 8468c2ecf20Sopenharmony_ci case hwmon_in_min: 8478c2ecf20Sopenharmony_ci return pvt_write_limit(pvt, PVT_VOLT + ch, true, val); 8488c2ecf20Sopenharmony_ci case hwmon_in_max: 8498c2ecf20Sopenharmony_ci return pvt_write_limit(pvt, PVT_VOLT + ch, false, val); 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci break; 8528c2ecf20Sopenharmony_ci default: 8538c2ecf20Sopenharmony_ci break; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic const struct hwmon_ops pvt_hwmon_ops = { 8608c2ecf20Sopenharmony_ci .is_visible = pvt_hwmon_is_visible, 8618c2ecf20Sopenharmony_ci .read = pvt_hwmon_read, 8628c2ecf20Sopenharmony_ci .read_string = pvt_hwmon_read_string, 8638c2ecf20Sopenharmony_ci .write = pvt_hwmon_write 8648c2ecf20Sopenharmony_ci}; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info pvt_hwmon_info = { 8678c2ecf20Sopenharmony_ci .ops = &pvt_hwmon_ops, 8688c2ecf20Sopenharmony_ci .info = pvt_channel_info 8698c2ecf20Sopenharmony_ci}; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic void pvt_clear_data(void *data) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt = data; 8748c2ecf20Sopenharmony_ci#if !defined(CONFIG_SENSORS_BT1_PVT_ALARMS) 8758c2ecf20Sopenharmony_ci int idx; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci for (idx = 0; idx < PVT_SENSORS_NUM; ++idx) 8788c2ecf20Sopenharmony_ci complete_all(&pvt->cache[idx].conversion); 8798c2ecf20Sopenharmony_ci#endif 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci mutex_destroy(&pvt->iface_mtx); 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic struct pvt_hwmon *pvt_create_data(struct platform_device *pdev) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 8878c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt; 8888c2ecf20Sopenharmony_ci int ret, idx; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); 8918c2ecf20Sopenharmony_ci if (!pvt) 8928c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci ret = devm_add_action(dev, pvt_clear_data, pvt); 8958c2ecf20Sopenharmony_ci if (ret) { 8968c2ecf20Sopenharmony_ci dev_err(dev, "Can't add PVT data clear action\n"); 8978c2ecf20Sopenharmony_ci return ERR_PTR(ret); 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci pvt->dev = dev; 9018c2ecf20Sopenharmony_ci pvt->sensor = PVT_SENSOR_FIRST; 9028c2ecf20Sopenharmony_ci mutex_init(&pvt->iface_mtx); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) 9058c2ecf20Sopenharmony_ci for (idx = 0; idx < PVT_SENSORS_NUM; ++idx) 9068c2ecf20Sopenharmony_ci seqlock_init(&pvt->cache[idx].data_seqlock); 9078c2ecf20Sopenharmony_ci#else 9088c2ecf20Sopenharmony_ci for (idx = 0; idx < PVT_SENSORS_NUM; ++idx) 9098c2ecf20Sopenharmony_ci init_completion(&pvt->cache[idx].conversion); 9108c2ecf20Sopenharmony_ci#endif 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return pvt; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic int pvt_request_regs(struct pvt_hwmon *pvt) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(pvt->dev); 9188c2ecf20Sopenharmony_ci struct resource *res; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 9218c2ecf20Sopenharmony_ci if (!res) { 9228c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Couldn't find PVT memresource\n"); 9238c2ecf20Sopenharmony_ci return -EINVAL; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci pvt->regs = devm_ioremap_resource(pvt->dev, res); 9278c2ecf20Sopenharmony_ci if (IS_ERR(pvt->regs)) { 9288c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Couldn't map PVT registers\n"); 9298c2ecf20Sopenharmony_ci return PTR_ERR(pvt->regs); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return 0; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic void pvt_disable_clks(void *data) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt = data; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci clk_bulk_disable_unprepare(PVT_CLOCK_NUM, pvt->clks); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cistatic int pvt_request_clks(struct pvt_hwmon *pvt) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci int ret; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci pvt->clks[PVT_CLOCK_APB].id = "pclk"; 9478c2ecf20Sopenharmony_ci pvt->clks[PVT_CLOCK_REF].id = "ref"; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks); 9508c2ecf20Sopenharmony_ci if (ret) { 9518c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n"); 9528c2ecf20Sopenharmony_ci return ret; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks); 9568c2ecf20Sopenharmony_ci if (ret) { 9578c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Couldn't enable the PVT clocks\n"); 9588c2ecf20Sopenharmony_ci return ret; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt); 9628c2ecf20Sopenharmony_ci if (ret) { 9638c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Can't add PVT clocks disable action\n"); 9648c2ecf20Sopenharmony_ci return ret; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return 0; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic int pvt_check_pwr(struct pvt_hwmon *pvt) 9718c2ecf20Sopenharmony_ci{ 9728c2ecf20Sopenharmony_ci unsigned long tout; 9738c2ecf20Sopenharmony_ci int ret = 0; 9748c2ecf20Sopenharmony_ci u32 data; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* 9778c2ecf20Sopenharmony_ci * Test out the sensor conversion functionality. If it is not done on 9788c2ecf20Sopenharmony_ci * time then the domain must have been unpowered and we won't be able 9798c2ecf20Sopenharmony_ci * to use the device later in this driver. 9808c2ecf20Sopenharmony_ci * Note If the power source is lost during the normal driver work the 9818c2ecf20Sopenharmony_ci * data read procedure will either return -ETIMEDOUT (for the 9828c2ecf20Sopenharmony_ci * alarm-less driver configuration) or just stop the repeated 9838c2ecf20Sopenharmony_ci * conversion. In the later case alas we won't be able to detect the 9848c2ecf20Sopenharmony_ci * problem. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL); 9878c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); 9888c2ecf20Sopenharmony_ci pvt_set_tout(pvt, 0); 9898c2ecf20Sopenharmony_ci readl(pvt->regs + PVT_DATA); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci tout = PVT_TOUT_MIN / NSEC_PER_USEC; 9928c2ecf20Sopenharmony_ci usleep_range(tout, 2 * tout); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci data = readl(pvt->regs + PVT_DATA); 9958c2ecf20Sopenharmony_ci if (!(data & PVT_DATA_VALID)) { 9968c2ecf20Sopenharmony_ci ret = -ENODEV; 9978c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Sensor is powered down\n"); 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return ret; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic int pvt_init_iface(struct pvt_hwmon *pvt) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci unsigned long rate; 10088c2ecf20Sopenharmony_ci u32 trim, temp; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk); 10118c2ecf20Sopenharmony_ci if (!rate) { 10128c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Invalid reference clock rate\n"); 10138c2ecf20Sopenharmony_ci return -ENODEV; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* 10178c2ecf20Sopenharmony_ci * Make sure all interrupts and controller are disabled so not to 10188c2ecf20Sopenharmony_ci * accidentally have ISR executed before the driver data is fully 10198c2ecf20Sopenharmony_ci * initialized. Clear the IRQ status as well. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL); 10228c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); 10238c2ecf20Sopenharmony_ci readl(pvt->regs + PVT_CLR_INTR); 10248c2ecf20Sopenharmony_ci readl(pvt->regs + PVT_DATA); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* Setup default sensor mode, timeout and temperature trim. */ 10278c2ecf20Sopenharmony_ci pvt_set_mode(pvt, pvt_info[pvt->sensor].mode); 10288c2ecf20Sopenharmony_ci pvt_set_tout(pvt, PVT_TOUT_DEF); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* 10318c2ecf20Sopenharmony_ci * Preserve the current ref-clock based delay (Ttotal) between the 10328c2ecf20Sopenharmony_ci * sensors data samples in the driver data so not to recalculate it 10338c2ecf20Sopenharmony_ci * each time on the data requests and timeout reads. It consists of the 10348c2ecf20Sopenharmony_ci * delay introduced by the internal ref-clock timer (N / Fclk) and the 10358c2ecf20Sopenharmony_ci * constant timeout caused by each conversion latency (Tmin): 10368c2ecf20Sopenharmony_ci * Ttotal = N / Fclk + Tmin 10378c2ecf20Sopenharmony_ci * If alarms are enabled the sensors are polled one after another and 10388c2ecf20Sopenharmony_ci * in order to get the next measurement of a particular sensor the 10398c2ecf20Sopenharmony_ci * caller will have to wait for at most until all the others are 10408c2ecf20Sopenharmony_ci * polled. In that case the formulae will look a bit different: 10418c2ecf20Sopenharmony_ci * Ttotal = 5 * (N / Fclk + Tmin) 10428c2ecf20Sopenharmony_ci */ 10438c2ecf20Sopenharmony_ci#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) 10448c2ecf20Sopenharmony_ci pvt->timeout = ktime_set(PVT_SENSORS_NUM * PVT_TOUT_DEF, 0); 10458c2ecf20Sopenharmony_ci pvt->timeout = ktime_divns(pvt->timeout, rate); 10468c2ecf20Sopenharmony_ci pvt->timeout = ktime_add_ns(pvt->timeout, PVT_SENSORS_NUM * PVT_TOUT_MIN); 10478c2ecf20Sopenharmony_ci#else 10488c2ecf20Sopenharmony_ci pvt->timeout = ktime_set(PVT_TOUT_DEF, 0); 10498c2ecf20Sopenharmony_ci pvt->timeout = ktime_divns(pvt->timeout, rate); 10508c2ecf20Sopenharmony_ci pvt->timeout = ktime_add_ns(pvt->timeout, PVT_TOUT_MIN); 10518c2ecf20Sopenharmony_ci#endif 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci trim = PVT_TRIM_DEF; 10548c2ecf20Sopenharmony_ci if (!of_property_read_u32(pvt->dev->of_node, 10558c2ecf20Sopenharmony_ci "baikal,pvt-temp-offset-millicelsius", &temp)) 10568c2ecf20Sopenharmony_ci trim = pvt_calc_trim(temp); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci pvt_set_trim(pvt, trim); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic int pvt_request_irq(struct pvt_hwmon *pvt) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(pvt->dev); 10668c2ecf20Sopenharmony_ci int ret; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci pvt->irq = platform_get_irq(pdev, 0); 10698c2ecf20Sopenharmony_ci if (pvt->irq < 0) 10708c2ecf20Sopenharmony_ci return pvt->irq; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(pvt->dev, pvt->irq, 10738c2ecf20Sopenharmony_ci pvt_hard_isr, pvt_soft_isr, 10748c2ecf20Sopenharmony_ci#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) 10758c2ecf20Sopenharmony_ci IRQF_SHARED | IRQF_TRIGGER_HIGH | 10768c2ecf20Sopenharmony_ci IRQF_ONESHOT, 10778c2ecf20Sopenharmony_ci#else 10788c2ecf20Sopenharmony_ci IRQF_SHARED | IRQF_TRIGGER_HIGH, 10798c2ecf20Sopenharmony_ci#endif 10808c2ecf20Sopenharmony_ci "pvt", pvt); 10818c2ecf20Sopenharmony_ci if (ret) { 10828c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Couldn't request PVT IRQ\n"); 10838c2ecf20Sopenharmony_ci return ret; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci return 0; 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic int pvt_create_hwmon(struct pvt_hwmon *pvt) 10908c2ecf20Sopenharmony_ci{ 10918c2ecf20Sopenharmony_ci pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", pvt, 10928c2ecf20Sopenharmony_ci &pvt_hwmon_info, NULL); 10938c2ecf20Sopenharmony_ci if (IS_ERR(pvt->hwmon)) { 10948c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Couldn't create hwmon device\n"); 10958c2ecf20Sopenharmony_ci return PTR_ERR(pvt->hwmon); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic void pvt_disable_iface(void *data) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt = data; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci mutex_lock(&pvt->iface_mtx); 11088c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); 11098c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 11108c2ecf20Sopenharmony_ci PVT_INTR_DVALID); 11118c2ecf20Sopenharmony_ci mutex_unlock(&pvt->iface_mtx); 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic int pvt_enable_iface(struct pvt_hwmon *pvt) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci int ret; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci ret = devm_add_action(pvt->dev, pvt_disable_iface, pvt); 11198c2ecf20Sopenharmony_ci if (ret) { 11208c2ecf20Sopenharmony_ci dev_err(pvt->dev, "Can't add PVT disable interface action\n"); 11218c2ecf20Sopenharmony_ci return ret; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* 11258c2ecf20Sopenharmony_ci * Enable sensors data conversion and IRQ. We need to lock the 11268c2ecf20Sopenharmony_ci * interface mutex since hwmon has just been created and the 11278c2ecf20Sopenharmony_ci * corresponding sysfs files are accessible from user-space, 11288c2ecf20Sopenharmony_ci * which theoretically may cause races. 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_ci mutex_lock(&pvt->iface_mtx); 11318c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0); 11328c2ecf20Sopenharmony_ci pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); 11338c2ecf20Sopenharmony_ci mutex_unlock(&pvt->iface_mtx); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci return 0; 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */ 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic int pvt_enable_iface(struct pvt_hwmon *pvt) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci return 0; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */ 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic int pvt_probe(struct platform_device *pdev) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct pvt_hwmon *pvt; 11508c2ecf20Sopenharmony_ci int ret; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci pvt = pvt_create_data(pdev); 11538c2ecf20Sopenharmony_ci if (IS_ERR(pvt)) 11548c2ecf20Sopenharmony_ci return PTR_ERR(pvt); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci ret = pvt_request_regs(pvt); 11578c2ecf20Sopenharmony_ci if (ret) 11588c2ecf20Sopenharmony_ci return ret; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci ret = pvt_request_clks(pvt); 11618c2ecf20Sopenharmony_ci if (ret) 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci ret = pvt_check_pwr(pvt); 11658c2ecf20Sopenharmony_ci if (ret) 11668c2ecf20Sopenharmony_ci return ret; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci ret = pvt_init_iface(pvt); 11698c2ecf20Sopenharmony_ci if (ret) 11708c2ecf20Sopenharmony_ci return ret; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci ret = pvt_request_irq(pvt); 11738c2ecf20Sopenharmony_ci if (ret) 11748c2ecf20Sopenharmony_ci return ret; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci ret = pvt_create_hwmon(pvt); 11778c2ecf20Sopenharmony_ci if (ret) 11788c2ecf20Sopenharmony_ci return ret; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci ret = pvt_enable_iface(pvt); 11818c2ecf20Sopenharmony_ci if (ret) 11828c2ecf20Sopenharmony_ci return ret; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cistatic const struct of_device_id pvt_of_match[] = { 11888c2ecf20Sopenharmony_ci { .compatible = "baikal,bt1-pvt" }, 11898c2ecf20Sopenharmony_ci { } 11908c2ecf20Sopenharmony_ci}; 11918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pvt_of_match); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic struct platform_driver pvt_driver = { 11948c2ecf20Sopenharmony_ci .probe = pvt_probe, 11958c2ecf20Sopenharmony_ci .driver = { 11968c2ecf20Sopenharmony_ci .name = "bt1-pvt", 11978c2ecf20Sopenharmony_ci .of_match_table = pvt_of_match 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci}; 12008c2ecf20Sopenharmony_cimodule_platform_driver(pvt_driver); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>"); 12038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Baikal-T1 PVT driver"); 12048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1205