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, &reg);
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, &reg_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