18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/bitops.h> 98c2ecf20Sopenharmony_ci#include <linux/regmap.h> 108c2ecf20Sopenharmony_ci#include <linux/thermal.h> 118c2ecf20Sopenharmony_ci#include "tsens.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define CAL_MDEGC 30000 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define CONFIG_ADDR 0x3640 168c2ecf20Sopenharmony_ci#define CONFIG_ADDR_8660 0x3620 178c2ecf20Sopenharmony_ci/* CONFIG_ADDR bitmasks */ 188c2ecf20Sopenharmony_ci#define CONFIG 0x9b 198c2ecf20Sopenharmony_ci#define CONFIG_MASK 0xf 208c2ecf20Sopenharmony_ci#define CONFIG_8660 1 218c2ecf20Sopenharmony_ci#define CONFIG_SHIFT_8660 28 228c2ecf20Sopenharmony_ci#define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define STATUS_CNTL_ADDR_8064 0x3660 258c2ecf20Sopenharmony_ci#define CNTL_ADDR 0x3620 268c2ecf20Sopenharmony_ci/* CNTL_ADDR bitmasks */ 278c2ecf20Sopenharmony_ci#define EN BIT(0) 288c2ecf20Sopenharmony_ci#define SW_RST BIT(1) 298c2ecf20Sopenharmony_ci#define SENSOR0_EN BIT(3) 308c2ecf20Sopenharmony_ci#define SLP_CLK_ENA BIT(26) 318c2ecf20Sopenharmony_ci#define SLP_CLK_ENA_8660 BIT(24) 328c2ecf20Sopenharmony_ci#define MEASURE_PERIOD 1 338c2ecf20Sopenharmony_ci#define SENSOR0_SHIFT 3 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* INT_STATUS_ADDR bitmasks */ 368c2ecf20Sopenharmony_ci#define MIN_STATUS_MASK BIT(0) 378c2ecf20Sopenharmony_ci#define LOWER_STATUS_CLR BIT(1) 388c2ecf20Sopenharmony_ci#define UPPER_STATUS_CLR BIT(2) 398c2ecf20Sopenharmony_ci#define MAX_STATUS_MASK BIT(3) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define THRESHOLD_ADDR 0x3624 428c2ecf20Sopenharmony_ci/* THRESHOLD_ADDR bitmasks */ 438c2ecf20Sopenharmony_ci#define THRESHOLD_MAX_LIMIT_SHIFT 24 448c2ecf20Sopenharmony_ci#define THRESHOLD_MIN_LIMIT_SHIFT 16 458c2ecf20Sopenharmony_ci#define THRESHOLD_UPPER_LIMIT_SHIFT 8 468c2ecf20Sopenharmony_ci#define THRESHOLD_LOWER_LIMIT_SHIFT 0 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Initial temperature threshold values */ 498c2ecf20Sopenharmony_ci#define LOWER_LIMIT_TH 0x50 508c2ecf20Sopenharmony_ci#define UPPER_LIMIT_TH 0xdf 518c2ecf20Sopenharmony_ci#define MIN_LIMIT_TH 0x0 528c2ecf20Sopenharmony_ci#define MAX_LIMIT_TH 0xff 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define S0_STATUS_ADDR 0x3628 558c2ecf20Sopenharmony_ci#define INT_STATUS_ADDR 0x363c 568c2ecf20Sopenharmony_ci#define TRDY_MASK BIT(7) 578c2ecf20Sopenharmony_ci#define TIMEOUT_US 100 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int suspend_8960(struct tsens_priv *priv) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci int ret; 628c2ecf20Sopenharmony_ci unsigned int mask; 638c2ecf20Sopenharmony_ci struct regmap *map = priv->tm_map; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold); 668c2ecf20Sopenharmony_ci if (ret) 678c2ecf20Sopenharmony_ci return ret; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control); 708c2ecf20Sopenharmony_ci if (ret) 718c2ecf20Sopenharmony_ci return ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (priv->num_sensors > 1) 748c2ecf20Sopenharmony_ci mask = SLP_CLK_ENA | EN; 758c2ecf20Sopenharmony_ci else 768c2ecf20Sopenharmony_ci mask = SLP_CLK_ENA_8660 | EN; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); 798c2ecf20Sopenharmony_ci if (ret) 808c2ecf20Sopenharmony_ci return ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int resume_8960(struct tsens_priv *priv) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci struct regmap *map = priv->tm_map; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); 918c2ecf20Sopenharmony_ci if (ret) 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Separate CONFIG restore is not needed only for 8660 as 968c2ecf20Sopenharmony_ci * config is part of CTRL Addr and its restored as such 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci if (priv->num_sensors > 1) { 998c2ecf20Sopenharmony_ci ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); 1008c2ecf20Sopenharmony_ci if (ret) 1018c2ecf20Sopenharmony_ci return ret; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold); 1058c2ecf20Sopenharmony_ci if (ret) 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = regmap_write(map, CNTL_ADDR, priv->ctx.control); 1098c2ecf20Sopenharmony_ci if (ret) 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int enable_8960(struct tsens_priv *priv, int id) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci int ret; 1188c2ecf20Sopenharmony_ci u32 reg, mask; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = regmap_read(priv->tm_map, CNTL_ADDR, ®); 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mask = BIT(id + SENSOR0_SHIFT); 1258c2ecf20Sopenharmony_ci ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); 1268c2ecf20Sopenharmony_ci if (ret) 1278c2ecf20Sopenharmony_ci return ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (priv->num_sensors > 1) 1308c2ecf20Sopenharmony_ci reg |= mask | SLP_CLK_ENA | EN; 1318c2ecf20Sopenharmony_ci else 1328c2ecf20Sopenharmony_ci reg |= mask | SLP_CLK_ENA_8660 | EN; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = regmap_write(priv->tm_map, CNTL_ADDR, reg); 1358c2ecf20Sopenharmony_ci if (ret) 1368c2ecf20Sopenharmony_ci return ret; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void disable_8960(struct tsens_priv *priv) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int ret; 1448c2ecf20Sopenharmony_ci u32 reg_cntl; 1458c2ecf20Sopenharmony_ci u32 mask; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci mask = GENMASK(priv->num_sensors - 1, 0); 1488c2ecf20Sopenharmony_ci mask <<= SENSOR0_SHIFT; 1498c2ecf20Sopenharmony_ci mask |= EN; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ret = regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl); 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci return; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci reg_cntl &= ~mask; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (priv->num_sensors > 1) 1588c2ecf20Sopenharmony_ci reg_cntl &= ~SLP_CLK_ENA; 1598c2ecf20Sopenharmony_ci else 1608c2ecf20Sopenharmony_ci reg_cntl &= ~SLP_CLK_ENA_8660; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int init_8960(struct tsens_priv *priv) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int ret, i; 1688c2ecf20Sopenharmony_ci u32 reg_cntl; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci priv->tm_map = dev_get_regmap(priv->dev, NULL); 1718c2ecf20Sopenharmony_ci if (!priv->tm_map) 1728c2ecf20Sopenharmony_ci return -ENODEV; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * The status registers for each sensor are discontiguous 1768c2ecf20Sopenharmony_ci * because some SoCs have 5 sensors while others have more 1778c2ecf20Sopenharmony_ci * but the control registers stay in the same place, i.e 1788c2ecf20Sopenharmony_ci * directly after the first 5 status registers. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci for (i = 0; i < priv->num_sensors; i++) { 1818c2ecf20Sopenharmony_ci if (i >= 5) 1828c2ecf20Sopenharmony_ci priv->sensor[i].status = S0_STATUS_ADDR + 40; 1838c2ecf20Sopenharmony_ci priv->sensor[i].status += i * 4; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci reg_cntl = SW_RST; 1878c2ecf20Sopenharmony_ci ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl); 1888c2ecf20Sopenharmony_ci if (ret) 1898c2ecf20Sopenharmony_ci return ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (priv->num_sensors > 1) { 1928c2ecf20Sopenharmony_ci reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); 1938c2ecf20Sopenharmony_ci reg_cntl &= ~SW_RST; 1948c2ecf20Sopenharmony_ci ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR, 1958c2ecf20Sopenharmony_ci CONFIG_MASK, CONFIG); 1968c2ecf20Sopenharmony_ci } else { 1978c2ecf20Sopenharmony_ci reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); 1988c2ecf20Sopenharmony_ci reg_cntl &= ~CONFIG_MASK_8660; 1998c2ecf20Sopenharmony_ci reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT; 2038c2ecf20Sopenharmony_ci ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); 2048c2ecf20Sopenharmony_ci if (ret) 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci reg_cntl |= EN; 2088c2ecf20Sopenharmony_ci ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); 2098c2ecf20Sopenharmony_ci if (ret) 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int calibrate_8960(struct tsens_priv *priv) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci int i; 2188c2ecf20Sopenharmony_ci char *data; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ssize_t num_read = priv->num_sensors; 2218c2ecf20Sopenharmony_ci struct tsens_sensor *s = priv->sensor; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci data = qfprom_read(priv->dev, "calib"); 2248c2ecf20Sopenharmony_ci if (IS_ERR(data)) 2258c2ecf20Sopenharmony_ci data = qfprom_read(priv->dev, "calib_backup"); 2268c2ecf20Sopenharmony_ci if (IS_ERR(data)) 2278c2ecf20Sopenharmony_ci return PTR_ERR(data); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci for (i = 0; i < num_read; i++, s++) 2308c2ecf20Sopenharmony_ci s->offset = data[i]; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci kfree(data); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* Temperature on y axis and ADC-code on x-axis */ 2388c2ecf20Sopenharmony_cistatic inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int slope, offset; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci slope = thermal_zone_get_slope(s->tzd); 2438c2ecf20Sopenharmony_ci offset = CAL_MDEGC - slope * s->offset; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return adc_code * slope + offset; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int get_temp_8960(const struct tsens_sensor *s, int *temp) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int ret; 2518c2ecf20Sopenharmony_ci u32 code, trdy; 2528c2ecf20Sopenharmony_ci struct tsens_priv *priv = s->priv; 2538c2ecf20Sopenharmony_ci unsigned long timeout; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); 2568c2ecf20Sopenharmony_ci do { 2578c2ecf20Sopenharmony_ci ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); 2588c2ecf20Sopenharmony_ci if (ret) 2598c2ecf20Sopenharmony_ci return ret; 2608c2ecf20Sopenharmony_ci if (!(trdy & TRDY_MASK)) 2618c2ecf20Sopenharmony_ci continue; 2628c2ecf20Sopenharmony_ci ret = regmap_read(priv->tm_map, s->status, &code); 2638c2ecf20Sopenharmony_ci if (ret) 2648c2ecf20Sopenharmony_ci return ret; 2658c2ecf20Sopenharmony_ci *temp = code_to_mdegC(code, s); 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci } while (time_before(jiffies, timeout)); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic const struct tsens_ops ops_8960 = { 2738c2ecf20Sopenharmony_ci .init = init_8960, 2748c2ecf20Sopenharmony_ci .calibrate = calibrate_8960, 2758c2ecf20Sopenharmony_ci .get_temp = get_temp_8960, 2768c2ecf20Sopenharmony_ci .enable = enable_8960, 2778c2ecf20Sopenharmony_ci .disable = disable_8960, 2788c2ecf20Sopenharmony_ci .suspend = suspend_8960, 2798c2ecf20Sopenharmony_ci .resume = resume_8960, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistruct tsens_plat_data data_8960 = { 2838c2ecf20Sopenharmony_ci .num_sensors = 11, 2848c2ecf20Sopenharmony_ci .ops = &ops_8960, 2858c2ecf20Sopenharmony_ci}; 286