18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ST Thermal Sensor Driver for memory mapped sensors.
48c2ecf20Sopenharmony_ci * Author: Ajit Pal Singh <ajitpal.singh@st.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/of.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "st_thermal.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define STIH416_MPE_CONF			0x0
158c2ecf20Sopenharmony_ci#define STIH416_MPE_STATUS			0x4
168c2ecf20Sopenharmony_ci#define STIH416_MPE_INT_THRESH			0x8
178c2ecf20Sopenharmony_ci#define STIH416_MPE_INT_EN			0xC
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Power control bits for the memory mapped thermal sensor */
208c2ecf20Sopenharmony_ci#define THERMAL_PDN				BIT(4)
218c2ecf20Sopenharmony_ci#define THERMAL_SRSTN				BIT(10)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic const struct reg_field st_mmap_thermal_regfields[MAX_REGFIELDS] = {
248c2ecf20Sopenharmony_ci	/*
258c2ecf20Sopenharmony_ci	 * According to the STIH416 MPE temp sensor data sheet -
268c2ecf20Sopenharmony_ci	 * the PDN (Power Down Bit) and SRSTN (Soft Reset Bit) need to be
278c2ecf20Sopenharmony_ci	 * written simultaneously for powering on and off the temperature
288c2ecf20Sopenharmony_ci	 * sensor. regmap_update_bits() will be used to update the register.
298c2ecf20Sopenharmony_ci	 */
308c2ecf20Sopenharmony_ci	[INT_THRESH_HI]	= REG_FIELD(STIH416_MPE_INT_THRESH, 	0,  7),
318c2ecf20Sopenharmony_ci	[DCORRECT]	= REG_FIELD(STIH416_MPE_CONF,		5,  9),
328c2ecf20Sopenharmony_ci	[OVERFLOW]	= REG_FIELD(STIH416_MPE_STATUS,		9,  9),
338c2ecf20Sopenharmony_ci	[DATA]		= REG_FIELD(STIH416_MPE_STATUS,		11, 18),
348c2ecf20Sopenharmony_ci	[INT_ENABLE]	= REG_FIELD(STIH416_MPE_INT_EN,		0,  0),
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic irqreturn_t st_mmap_thermal_trip_handler(int irq, void *sdata)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct st_thermal_sensor *sensor = sdata;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	thermal_zone_device_update(sensor->thermal_dev,
428c2ecf20Sopenharmony_ci				   THERMAL_EVENT_UNSPECIFIED);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Private ops for the Memory Mapped based thermal sensors */
488c2ecf20Sopenharmony_cistatic int st_mmap_power_ctrl(struct st_thermal_sensor *sensor,
498c2ecf20Sopenharmony_ci			      enum st_thermal_power_state power_state)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	const unsigned int mask = (THERMAL_PDN | THERMAL_SRSTN);
528c2ecf20Sopenharmony_ci	const unsigned int val = power_state ? mask : 0;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return regmap_update_bits(sensor->regmap, STIH416_MPE_CONF, mask, val);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int st_mmap_alloc_regfields(struct st_thermal_sensor *sensor)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct device *dev = sensor->dev;
608c2ecf20Sopenharmony_ci	struct regmap *regmap = sensor->regmap;
618c2ecf20Sopenharmony_ci	const struct reg_field *reg_fields = sensor->cdata->reg_fields;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	sensor->int_thresh_hi = devm_regmap_field_alloc(dev, regmap,
648c2ecf20Sopenharmony_ci						reg_fields[INT_THRESH_HI]);
658c2ecf20Sopenharmony_ci	sensor->int_enable = devm_regmap_field_alloc(dev, regmap,
668c2ecf20Sopenharmony_ci						reg_fields[INT_ENABLE]);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (IS_ERR(sensor->int_thresh_hi) || IS_ERR(sensor->int_enable)) {
698c2ecf20Sopenharmony_ci		dev_err(dev, "failed to alloc mmap regfields\n");
708c2ecf20Sopenharmony_ci		return -EINVAL;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int st_mmap_enable_irq(struct st_thermal_sensor *sensor)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	int ret;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* Set upper critical threshold */
818c2ecf20Sopenharmony_ci	ret = regmap_field_write(sensor->int_thresh_hi,
828c2ecf20Sopenharmony_ci				 sensor->cdata->crit_temp -
838c2ecf20Sopenharmony_ci				 sensor->cdata->temp_adjust_val);
848c2ecf20Sopenharmony_ci	if (ret)
858c2ecf20Sopenharmony_ci		return ret;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return regmap_field_write(sensor->int_enable, 1);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int st_mmap_register_enable_irq(struct st_thermal_sensor *sensor)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct device *dev = sensor->dev;
938c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
948c2ecf20Sopenharmony_ci	int ret;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	sensor->irq = platform_get_irq(pdev, 0);
978c2ecf20Sopenharmony_ci	if (sensor->irq < 0)
988c2ecf20Sopenharmony_ci		return sensor->irq;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(dev, sensor->irq,
1018c2ecf20Sopenharmony_ci					NULL, st_mmap_thermal_trip_handler,
1028c2ecf20Sopenharmony_ci					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
1038c2ecf20Sopenharmony_ci					dev->driver->name, sensor);
1048c2ecf20Sopenharmony_ci	if (ret) {
1058c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register IRQ %d\n", sensor->irq);
1068c2ecf20Sopenharmony_ci		return ret;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return st_mmap_enable_irq(sensor);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic const struct regmap_config st_416mpe_regmap_config = {
1138c2ecf20Sopenharmony_ci	.reg_bits = 32,
1148c2ecf20Sopenharmony_ci	.val_bits = 32,
1158c2ecf20Sopenharmony_ci	.reg_stride = 4,
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int st_mmap_regmap_init(struct st_thermal_sensor *sensor)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct device *dev = sensor->dev;
1218c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
1228c2ecf20Sopenharmony_ci	struct resource *res;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1258c2ecf20Sopenharmony_ci	if (!res) {
1268c2ecf20Sopenharmony_ci		dev_err(dev, "no memory resources defined\n");
1278c2ecf20Sopenharmony_ci		return -ENODEV;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	sensor->mmio_base = devm_ioremap_resource(dev, res);
1318c2ecf20Sopenharmony_ci	if (IS_ERR(sensor->mmio_base)) {
1328c2ecf20Sopenharmony_ci		dev_err(dev, "failed to remap IO\n");
1338c2ecf20Sopenharmony_ci		return PTR_ERR(sensor->mmio_base);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	sensor->regmap = devm_regmap_init_mmio(dev, sensor->mmio_base,
1378c2ecf20Sopenharmony_ci				&st_416mpe_regmap_config);
1388c2ecf20Sopenharmony_ci	if (IS_ERR(sensor->regmap)) {
1398c2ecf20Sopenharmony_ci		dev_err(dev, "failed to initialise regmap\n");
1408c2ecf20Sopenharmony_ci		return PTR_ERR(sensor->regmap);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic const struct st_thermal_sensor_ops st_mmap_sensor_ops = {
1478c2ecf20Sopenharmony_ci	.power_ctrl		= st_mmap_power_ctrl,
1488c2ecf20Sopenharmony_ci	.alloc_regfields	= st_mmap_alloc_regfields,
1498c2ecf20Sopenharmony_ci	.regmap_init		= st_mmap_regmap_init,
1508c2ecf20Sopenharmony_ci	.register_enable_irq	= st_mmap_register_enable_irq,
1518c2ecf20Sopenharmony_ci	.enable_irq		= st_mmap_enable_irq,
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/* Compatible device data stih416 mpe thermal sensor */
1558c2ecf20Sopenharmony_cistatic const struct st_thermal_compat_data st_416mpe_cdata = {
1568c2ecf20Sopenharmony_ci	.reg_fields		= st_mmap_thermal_regfields,
1578c2ecf20Sopenharmony_ci	.ops			= &st_mmap_sensor_ops,
1588c2ecf20Sopenharmony_ci	.calibration_val	= 14,
1598c2ecf20Sopenharmony_ci	.temp_adjust_val	= -95,
1608c2ecf20Sopenharmony_ci	.crit_temp		= 120,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/* Compatible device data stih407 thermal sensor */
1648c2ecf20Sopenharmony_cistatic const struct st_thermal_compat_data st_407_cdata = {
1658c2ecf20Sopenharmony_ci	.reg_fields		= st_mmap_thermal_regfields,
1668c2ecf20Sopenharmony_ci	.ops			= &st_mmap_sensor_ops,
1678c2ecf20Sopenharmony_ci	.calibration_val	= 16,
1688c2ecf20Sopenharmony_ci	.temp_adjust_val	= -95,
1698c2ecf20Sopenharmony_ci	.crit_temp		= 120,
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic const struct of_device_id st_mmap_thermal_of_match[] = {
1738c2ecf20Sopenharmony_ci	{ .compatible = "st,stih416-mpe-thermal", .data = &st_416mpe_cdata },
1748c2ecf20Sopenharmony_ci	{ .compatible = "st,stih407-thermal",     .data = &st_407_cdata },
1758c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, st_mmap_thermal_of_match);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int st_mmap_probe(struct platform_device *pdev)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	return st_thermal_register(pdev,  st_mmap_thermal_of_match);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic int st_mmap_remove(struct platform_device *pdev)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	return st_thermal_unregister(pdev);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic struct platform_driver st_mmap_thermal_driver = {
1908c2ecf20Sopenharmony_ci	.driver = {
1918c2ecf20Sopenharmony_ci		.name	= "st_thermal_mmap",
1928c2ecf20Sopenharmony_ci		.pm     = &st_thermal_pm_ops,
1938c2ecf20Sopenharmony_ci		.of_match_table = st_mmap_thermal_of_match,
1948c2ecf20Sopenharmony_ci	},
1958c2ecf20Sopenharmony_ci	.probe		= st_mmap_probe,
1968c2ecf20Sopenharmony_ci	.remove		= st_mmap_remove,
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cimodule_platform_driver(st_mmap_thermal_driver);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ciMODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");
2028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");
2038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
204