Skip to content

Commit

Permalink
thermal: add support for thermal sensor present on SPEAr13xx machines
Browse files Browse the repository at this point in the history
ST's SPEAr13xx machines are based on CortexA9 ARM processors.  These
machines contain a thermal sensor for junction temperature monitoring.

This patch adds support for this thermal sensor in existing thermal
framework.

[akpm@linux-foundation.org: little code cleanup]
[akpm@linux-foundation.org: print the pointer correctly]
[viresh.kumar@st.com: thermal/spear_thermal: add compilation dependency on PLAT_SPEAR]
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@st.com>
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Vincenzo Frascino authored and Len Brown committed Mar 22, 2012
1 parent c5a01dd commit 6a92c36
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 0 deletions.
8 changes: 8 additions & 0 deletions drivers/thermal/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ config THERMAL_HWMON
depends on THERMAL
depends on HWMON=y || HWMON=THERMAL
default y

config SPEAR_THERMAL
bool "SPEAr thermal sensor driver"
depends on THERMAL
depends on PLAT_SPEAR
help
Enable this to plug the SPEAr thermal sensor driver into the Linux
thermal framework
1 change: 1 addition & 0 deletions drivers/thermal/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
#

obj-$(CONFIG_THERMAL) += thermal_sys.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
206 changes: 206 additions & 0 deletions drivers/thermal/spear_thermal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* SPEAr thermal driver.
*
* Copyright (C) 2011-2012 ST Microelectronics
* Author: Vincenzo Frascino <vincenzo.frascino@st.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/

#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/spear_thermal.h>
#include <linux/thermal.h>

#define MD_FACTOR 1000

/* SPEAr Thermal Sensor Dev Structure */
struct spear_thermal_dev {
/* pointer to base address of the thermal sensor */
void __iomem *thermal_base;
/* clk structure */
struct clk *clk;
/* pointer to thermal flags */
unsigned int flags;
};

static inline int thermal_get_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
{
struct spear_thermal_dev *stdev = thermal->devdata;

/*
* Data are ready to be read after 628 usec from POWERDOWN signal
* (PDN) = 1
*/
*temp = (readl(stdev->thermal_base) & 0x7F) * MD_FACTOR;
return 0;
}

static struct thermal_zone_device_ops ops = {
.get_temp = thermal_get_temp,
};

#ifdef CONFIG_PM
static int spear_thermal_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
struct spear_thermal_dev *stdev = spear_thermal->devdata;
unsigned int actual_mask = 0;

/* Disable SPEAr Thermal Sensor */
actual_mask = readl(stdev->thermal_base);
writel(actual_mask & ~stdev->flags, stdev->thermal_base);

clk_disable(stdev->clk);
dev_info(dev, "Suspended.\n");

return 0;
}

static int spear_thermal_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
struct spear_thermal_dev *stdev = spear_thermal->devdata;
unsigned int actual_mask = 0;
int ret = 0;

ret = clk_enable(stdev->clk);
if (ret) {
dev_err(&pdev->dev, "Can't enable clock\n");
return ret;
}

/* Enable SPEAr Thermal Sensor */
actual_mask = readl(stdev->thermal_base);
writel(actual_mask | stdev->flags, stdev->thermal_base);

dev_info(dev, "Resumed.\n");

return 0;
}
#endif

static SIMPLE_DEV_PM_OPS(spear_thermal_pm_ops, spear_thermal_suspend,
spear_thermal_resume);

static int spear_thermal_probe(struct platform_device *pdev)
{
struct thermal_zone_device *spear_thermal = NULL;
struct spear_thermal_dev *stdev;
struct spear_thermal_pdata *pdata;
int ret = 0;
struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (!stres) {
dev_err(&pdev->dev, "memory resource missing\n");
return -ENODEV;
}

pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "platform data is NULL\n");
return -EINVAL;
}

stdev = devm_kzalloc(&pdev->dev, sizeof(*stdev), GFP_KERNEL);
if (!stdev) {
dev_err(&pdev->dev, "kzalloc fail\n");
return -ENOMEM;
}

/* Enable thermal sensor */
stdev->thermal_base = devm_ioremap(&pdev->dev, stres->start,
resource_size(stres));
if (!stdev->thermal_base) {
dev_err(&pdev->dev, "ioremap failed\n");
return -ENOMEM;
}

stdev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(stdev->clk)) {
dev_err(&pdev->dev, "Can't get clock\n");
return PTR_ERR(stdev->clk);
}

ret = clk_enable(stdev->clk);
if (ret) {
dev_err(&pdev->dev, "Can't enable clock\n");
goto put_clk;
}

stdev->flags = pdata->thermal_flags;
writel(stdev->flags, stdev->thermal_base);

spear_thermal = thermal_zone_device_register("spear_thermal", 0,
stdev, &ops, 0, 0, 0, 0);
if (!spear_thermal) {
dev_err(&pdev->dev, "thermal zone device is NULL\n");
ret = -EINVAL;
goto disable_clk;
}

platform_set_drvdata(pdev, spear_thermal);

dev_info(&spear_thermal->device, "Thermal Sensor Loaded at: 0x%p.\n",
stdev->thermal_base);

return 0;

disable_clk:
clk_disable(stdev->clk);
put_clk:
clk_put(stdev->clk);

return ret;
}

static int spear_thermal_exit(struct platform_device *pdev)
{
unsigned int actual_mask = 0;
struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
struct spear_thermal_dev *stdev = spear_thermal->devdata;

thermal_zone_device_unregister(spear_thermal);
platform_set_drvdata(pdev, NULL);

/* Disable SPEAr Thermal Sensor */
actual_mask = readl(stdev->thermal_base);
writel(actual_mask & ~stdev->flags, stdev->thermal_base);

clk_disable(stdev->clk);
clk_put(stdev->clk);

return 0;
}

static struct platform_driver spear_thermal_driver = {
.probe = spear_thermal_probe,
.remove = spear_thermal_exit,
.driver = {
.name = "spear_thermal",
.owner = THIS_MODULE,
.pm = &spear_thermal_pm_ops,
},
};

module_platform_driver(spear_thermal_driver);

MODULE_AUTHOR("Vincenzo Frascino <vincenzo.frascino@st.com>");
MODULE_DESCRIPTION("SPEAr thermal driver");
MODULE_LICENSE("GPL");
26 changes: 26 additions & 0 deletions include/linux/platform_data/spear_thermal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* SPEAr thermal driver platform data.
*
* Copyright (C) 2011-2012 ST Microelectronics
* Author: Vincenzo Frascino <vincenzo.frascino@st.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef SPEAR_THERMAL_H
#define SPEAR_THERMAL_H

/* SPEAr Thermal Sensor Platform Data */
struct spear_thermal_pdata {
/* flags used to enable thermal sensor */
unsigned int thermal_flags;
};

#endif /* SPEAR_THERMAL_H */

0 comments on commit 6a92c36

Please sign in to comment.