19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26a92c366SVincenzo Frascino /*
36a92c366SVincenzo Frascino  * SPEAr thermal driver.
46a92c366SVincenzo Frascino  *
56a92c366SVincenzo Frascino  * Copyright (C) 2011-2012 ST Microelectronics
66a92c366SVincenzo Frascino  * Author: Vincenzo Frascino <vincenzo.frascino@st.com>
76a92c366SVincenzo Frascino  */
86a92c366SVincenzo Frascino 
96a92c366SVincenzo Frascino #include <linux/clk.h>
106a92c366SVincenzo Frascino #include <linux/device.h>
116a92c366SVincenzo Frascino #include <linux/err.h>
126a92c366SVincenzo Frascino #include <linux/io.h>
136a92c366SVincenzo Frascino #include <linux/kernel.h>
14b9c7aff4SViresh Kumar #include <linux/of.h>
156a92c366SVincenzo Frascino #include <linux/module.h>
166a92c366SVincenzo Frascino #include <linux/platform_device.h>
176a92c366SVincenzo Frascino #include <linux/thermal.h>
186a92c366SVincenzo Frascino 
196a92c366SVincenzo Frascino #define MD_FACTOR	1000
206a92c366SVincenzo Frascino 
216a92c366SVincenzo Frascino /* SPEAr Thermal Sensor Dev Structure */
226a92c366SVincenzo Frascino struct spear_thermal_dev {
236a92c366SVincenzo Frascino 	/* pointer to base address of the thermal sensor */
246a92c366SVincenzo Frascino 	void __iomem *thermal_base;
256a92c366SVincenzo Frascino 	/* clk structure */
266a92c366SVincenzo Frascino 	struct clk *clk;
276a92c366SVincenzo Frascino 	/* pointer to thermal flags */
286a92c366SVincenzo Frascino 	unsigned int flags;
296a92c366SVincenzo Frascino };
306a92c366SVincenzo Frascino 
thermal_get_temp(struct thermal_zone_device * thermal,int * temp)316a92c366SVincenzo Frascino static inline int thermal_get_temp(struct thermal_zone_device *thermal,
3217e8351aSSascha Hauer 				int *temp)
336a92c366SVincenzo Frascino {
345f68d078SDaniel Lezcano 	struct spear_thermal_dev *stdev = thermal_zone_device_priv(thermal);
356a92c366SVincenzo Frascino 
366a92c366SVincenzo Frascino 	/*
376a92c366SVincenzo Frascino 	 * Data are ready to be read after 628 usec from POWERDOWN signal
386a92c366SVincenzo Frascino 	 * (PDN) = 1
396a92c366SVincenzo Frascino 	 */
40de716e32SViresh Kumar 	*temp = (readl_relaxed(stdev->thermal_base) & 0x7F) * MD_FACTOR;
416a92c366SVincenzo Frascino 	return 0;
426a92c366SVincenzo Frascino }
436a92c366SVincenzo Frascino 
446a92c366SVincenzo Frascino static struct thermal_zone_device_ops ops = {
456a92c366SVincenzo Frascino 	.get_temp = thermal_get_temp,
466a92c366SVincenzo Frascino };
476a92c366SVincenzo Frascino 
spear_thermal_suspend(struct device * dev)48d612c64dSArnd Bergmann static int __maybe_unused spear_thermal_suspend(struct device *dev)
496a92c366SVincenzo Frascino {
503fc62efeSWolfram Sang 	struct thermal_zone_device *spear_thermal = dev_get_drvdata(dev);
515f68d078SDaniel Lezcano 	struct spear_thermal_dev *stdev = thermal_zone_device_priv(spear_thermal);
526a92c366SVincenzo Frascino 	unsigned int actual_mask = 0;
536a92c366SVincenzo Frascino 
546a92c366SVincenzo Frascino 	/* Disable SPEAr Thermal Sensor */
55de716e32SViresh Kumar 	actual_mask = readl_relaxed(stdev->thermal_base);
56de716e32SViresh Kumar 	writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base);
576a92c366SVincenzo Frascino 
586a92c366SVincenzo Frascino 	clk_disable(stdev->clk);
596a92c366SVincenzo Frascino 	dev_info(dev, "Suspended.\n");
606a92c366SVincenzo Frascino 
616a92c366SVincenzo Frascino 	return 0;
626a92c366SVincenzo Frascino }
636a92c366SVincenzo Frascino 
spear_thermal_resume(struct device * dev)64d612c64dSArnd Bergmann static int __maybe_unused spear_thermal_resume(struct device *dev)
656a92c366SVincenzo Frascino {
663fc62efeSWolfram Sang 	struct thermal_zone_device *spear_thermal = dev_get_drvdata(dev);
675f68d078SDaniel Lezcano 	struct spear_thermal_dev *stdev = thermal_zone_device_priv(spear_thermal);
686a92c366SVincenzo Frascino 	unsigned int actual_mask = 0;
696a92c366SVincenzo Frascino 	int ret = 0;
706a92c366SVincenzo Frascino 
716a92c366SVincenzo Frascino 	ret = clk_enable(stdev->clk);
726a92c366SVincenzo Frascino 	if (ret) {
733fc62efeSWolfram Sang 		dev_err(dev, "Can't enable clock\n");
746a92c366SVincenzo Frascino 		return ret;
756a92c366SVincenzo Frascino 	}
766a92c366SVincenzo Frascino 
776a92c366SVincenzo Frascino 	/* Enable SPEAr Thermal Sensor */
78de716e32SViresh Kumar 	actual_mask = readl_relaxed(stdev->thermal_base);
79de716e32SViresh Kumar 	writel_relaxed(actual_mask | stdev->flags, stdev->thermal_base);
806a92c366SVincenzo Frascino 
816a92c366SVincenzo Frascino 	dev_info(dev, "Resumed.\n");
826a92c366SVincenzo Frascino 
836a92c366SVincenzo Frascino 	return 0;
846a92c366SVincenzo Frascino }
856a92c366SVincenzo Frascino 
866a92c366SVincenzo Frascino static SIMPLE_DEV_PM_OPS(spear_thermal_pm_ops, spear_thermal_suspend,
876a92c366SVincenzo Frascino 		spear_thermal_resume);
886a92c366SVincenzo Frascino 
spear_thermal_probe(struct platform_device * pdev)896a92c366SVincenzo Frascino static int spear_thermal_probe(struct platform_device *pdev)
906a92c366SVincenzo Frascino {
916a92c366SVincenzo Frascino 	struct thermal_zone_device *spear_thermal = NULL;
926a92c366SVincenzo Frascino 	struct spear_thermal_dev *stdev;
93b9c7aff4SViresh Kumar 	struct device_node *np = pdev->dev.of_node;
94b9c7aff4SViresh Kumar 	int ret = 0, val;
95b9c7aff4SViresh Kumar 
96b9c7aff4SViresh Kumar 	if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
97b9c7aff4SViresh Kumar 		dev_err(&pdev->dev, "Failed: DT Pdata not passed\n");
98b9c7aff4SViresh Kumar 		return -EINVAL;
99b9c7aff4SViresh Kumar 	}
1006a92c366SVincenzo Frascino 
1016a92c366SVincenzo Frascino 	stdev = devm_kzalloc(&pdev->dev, sizeof(*stdev), GFP_KERNEL);
102fa018d3eSJingoo Han 	if (!stdev)
1036a92c366SVincenzo Frascino 		return -ENOMEM;
1046a92c366SVincenzo Frascino 
1056a92c366SVincenzo Frascino 	/* Enable thermal sensor */
106f8887fdcSye xingchen 	stdev->thermal_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
107c21bec86SZhang Rui 	if (IS_ERR(stdev->thermal_base))
108253e3ae1SZhang Rui 		return PTR_ERR(stdev->thermal_base);
1096a92c366SVincenzo Frascino 
11003b79bdaSJulia Lawall 	stdev->clk = devm_clk_get(&pdev->dev, NULL);
1116a92c366SVincenzo Frascino 	if (IS_ERR(stdev->clk)) {
1126a92c366SVincenzo Frascino 		dev_err(&pdev->dev, "Can't get clock\n");
1136a92c366SVincenzo Frascino 		return PTR_ERR(stdev->clk);
1146a92c366SVincenzo Frascino 	}
1156a92c366SVincenzo Frascino 
1166a92c366SVincenzo Frascino 	ret = clk_enable(stdev->clk);
1176a92c366SVincenzo Frascino 	if (ret) {
1186a92c366SVincenzo Frascino 		dev_err(&pdev->dev, "Can't enable clock\n");
11903b79bdaSJulia Lawall 		return ret;
1206a92c366SVincenzo Frascino 	}
1216a92c366SVincenzo Frascino 
122b9c7aff4SViresh Kumar 	stdev->flags = val;
123de716e32SViresh Kumar 	writel_relaxed(stdev->flags, stdev->thermal_base);
1246a92c366SVincenzo Frascino 
125*cbcd51e8SRafael J. Wysocki 	spear_thermal = thermal_tripless_zone_device_register("spear_thermal",
126*cbcd51e8SRafael J. Wysocki 							      stdev, &ops, NULL);
12703ee62f0SDan Carpenter 	if (IS_ERR(spear_thermal)) {
1286a92c366SVincenzo Frascino 		dev_err(&pdev->dev, "thermal zone device is NULL\n");
12903ee62f0SDan Carpenter 		ret = PTR_ERR(spear_thermal);
1306a92c366SVincenzo Frascino 		goto disable_clk;
1316a92c366SVincenzo Frascino 	}
132bbcf90c0SAndrzej Pietrasiewicz 	ret = thermal_zone_device_enable(spear_thermal);
133bbcf90c0SAndrzej Pietrasiewicz 	if (ret) {
134bbcf90c0SAndrzej Pietrasiewicz 		dev_err(&pdev->dev, "Cannot enable thermal zone\n");
135bbcf90c0SAndrzej Pietrasiewicz 		goto unregister_tzd;
136bbcf90c0SAndrzej Pietrasiewicz 	}
1376a92c366SVincenzo Frascino 
1386a92c366SVincenzo Frascino 	platform_set_drvdata(pdev, spear_thermal);
1396a92c366SVincenzo Frascino 
14025e43976SDaniel Lezcano 	dev_info(&pdev->dev, "Thermal Sensor Loaded at: 0x%p.\n",
1416a92c366SVincenzo Frascino 			stdev->thermal_base);
1426a92c366SVincenzo Frascino 
1436a92c366SVincenzo Frascino 	return 0;
1446a92c366SVincenzo Frascino 
145bbcf90c0SAndrzej Pietrasiewicz unregister_tzd:
146bbcf90c0SAndrzej Pietrasiewicz 	thermal_zone_device_unregister(spear_thermal);
1476a92c366SVincenzo Frascino disable_clk:
1486a92c366SVincenzo Frascino 	clk_disable(stdev->clk);
1496a92c366SVincenzo Frascino 
1506a92c366SVincenzo Frascino 	return ret;
1516a92c366SVincenzo Frascino }
1526a92c366SVincenzo Frascino 
spear_thermal_exit(struct platform_device * pdev)1536a92c366SVincenzo Frascino static int spear_thermal_exit(struct platform_device *pdev)
1546a92c366SVincenzo Frascino {
1556a92c366SVincenzo Frascino 	unsigned int actual_mask = 0;
1566a92c366SVincenzo Frascino 	struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
1575f68d078SDaniel Lezcano 	struct spear_thermal_dev *stdev = thermal_zone_device_priv(spear_thermal);
1586a92c366SVincenzo Frascino 
1596a92c366SVincenzo Frascino 	thermal_zone_device_unregister(spear_thermal);
1606a92c366SVincenzo Frascino 
1616a92c366SVincenzo Frascino 	/* Disable SPEAr Thermal Sensor */
162de716e32SViresh Kumar 	actual_mask = readl_relaxed(stdev->thermal_base);
163de716e32SViresh Kumar 	writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base);
1646a92c366SVincenzo Frascino 
1656a92c366SVincenzo Frascino 	clk_disable(stdev->clk);
1666a92c366SVincenzo Frascino 
1676a92c366SVincenzo Frascino 	return 0;
1686a92c366SVincenzo Frascino }
1696a92c366SVincenzo Frascino 
170b9c7aff4SViresh Kumar static const struct of_device_id spear_thermal_id_table[] = {
171b9c7aff4SViresh Kumar 	{ .compatible = "st,thermal-spear1340" },
172b9c7aff4SViresh Kumar 	{}
173b9c7aff4SViresh Kumar };
174b9c7aff4SViresh Kumar MODULE_DEVICE_TABLE(of, spear_thermal_id_table);
175b9c7aff4SViresh Kumar 
1766a92c366SVincenzo Frascino static struct platform_driver spear_thermal_driver = {
1776a92c366SVincenzo Frascino 	.probe = spear_thermal_probe,
1786a92c366SVincenzo Frascino 	.remove = spear_thermal_exit,
1796a92c366SVincenzo Frascino 	.driver = {
1806a92c366SVincenzo Frascino 		.name = "spear_thermal",
1816a92c366SVincenzo Frascino 		.pm = &spear_thermal_pm_ops,
1825454f211SSachin Kamat 		.of_match_table = spear_thermal_id_table,
1836a92c366SVincenzo Frascino 	},
1846a92c366SVincenzo Frascino };
1856a92c366SVincenzo Frascino 
1866a92c366SVincenzo Frascino module_platform_driver(spear_thermal_driver);
1876a92c366SVincenzo Frascino 
1886a92c366SVincenzo Frascino MODULE_AUTHOR("Vincenzo Frascino <vincenzo.frascino@st.com>");
1896a92c366SVincenzo Frascino MODULE_DESCRIPTION("SPEAr thermal driver");
1906a92c366SVincenzo Frascino MODULE_LICENSE("GPL");
191