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