[RFC PATCH 3/5] thermal: Add support for the thermal sensor on A23/33
Icenowy Zheng
icenowy at aosc.xyz
Tue Jun 28 03:13:23 PDT 2016
This patch adds support for the sun8iw3 thermal sensor on
Allwinner A23/33 SoCs.
Signed-off-by: Icenowy Zheng <icenowy at aosc.xyz>
---
drivers/thermal/Kconfig | 7 ++
drivers/thermal/Makefile | 1 +
drivers/thermal/sun8iw3_thermal.c | 191 ++++++++++++++++++++++++++++++++++++++
3 files changed, 199 insertions(+)
create mode 100644 drivers/thermal/sun8iw3_thermal.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 2d702ca..2f29ad7 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -351,6 +351,13 @@ config MTK_THERMAL
Enable this option if you want to have support for thermal management
controller present in Mediatek SoCs
+config SUN8IW3_THERMAL
+ tristate "Temperature sensor driver for Allwinner A23/33 SoCs"
+ depends on MACH_SUN8I
+ depends on OF
+ help
+ Enable this to support thermal reporting on Allwinner A23/33 SoCs.
+
menu "Texas Instruments thermal drivers"
depends on ARCH_HAS_BANDGAP || COMPILE_TEST
depends on HAS_IOMEM
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 10b07c1..a4b0072 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -50,4 +50,5 @@ obj-$(CONFIG_ST_THERMAL) += st/
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
+obj-$(CONFIG_SUN8IW3_THERMAL) += sun8iw3_thermal.o
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
diff --git a/drivers/thermal/sun8iw3_thermal.c b/drivers/thermal/sun8iw3_thermal.c
new file mode 100644
index 0000000..332359f
--- /dev/null
+++ b/drivers/thermal/sun8iw3_thermal.c
@@ -0,0 +1,191 @@
+/*
+ * sun8iw3 THS driver
+ * Based on the work of Li Ming <liming at allwinnertech.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/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include <linux/printk.h>
+
+#define THERMAL_DATA_DELAY (100)
+
+#define THERMAL_BASSADDRESS (0xf1c25000)
+
+#define THS_CTRL_REG0 (0x00)
+#define THS_CTRL_REG1 (0x04)
+#define THS_PRO_CTRL_REG (0x18)
+
+#define THS_CTRL_REG0_VALUE_SUN8IW3 (0x00a300ff)
+#define THS_CTRL_REG0_VALUE_SUN8IW5 (0x002000ff)
+#define THS_CTRL_REG1_VALUE (0x120)
+#define THS_PRO_CTRL_REG_VALUE (0x1005f)
+
+#define THS_DATA_REG (0x20)
+
+struct sun8iw3_ths_data {
+ void __iomem *regs;
+ struct platform_device *pdev;
+ struct clk *clk;
+ struct thermal_zone_device *tzd;
+ struct device_node *np;
+ struct delayed_work work;
+ u32 temp;
+ int divisor, minus;
+ u32 ctrl0_value;
+};
+
+static const struct of_device_id sun8iw3_ths_dt_ids[] = {
+ { .compatible = "allwinner,sun8i-a23-ths", },
+ { .compatible = "allwinner,sun8i-a33-ths", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sun8iw3_ths_dt_ids);
+
+static int sun8iw3_ths_get_temp(void *_data, int *out)
+{
+ struct sun8iw3_ths_data *data = _data;
+
+ if (data->temp == 0)
+ return -EINVAL;
+
+ /* According to Allwinner's 3.4 kernel */
+ *out = (data->temp * 100) / data->divisor - data->minus;
+
+ /* Convert from Celsius to millidegree Celsius */
+ *out *= 1000;
+
+ return 0;
+}
+
+static struct thermal_zone_of_device_ops sun8iw3_of_thermal_ops = {
+ .get_temp = sun8iw3_ths_get_temp,
+};
+
+static void sun8iw3_ths_work(struct work_struct *work)
+{
+ struct sun8iw3_ths_data *data;
+
+ data = container_of(work, struct sun8iw3_ths_data, work.work);
+
+ data->temp = readl(data->regs + THS_DATA_REG);
+ if (data->temp)
+ thermal_zone_device_update(data->tzd);
+
+ schedule_delayed_work(&data->work, msecs_to_jiffies(1000));
+}
+
+static int sun8iw3_ths_init(struct platform_device *pdev,
+ struct sun8iw3_ths_data *data)
+{
+ struct resource *r;
+ int ret;
+
+ data->np = pdev->dev.of_node;
+
+ /* divisor and minus data are from Allwinner's 3.4 kernel */
+ if (of_device_is_compatible(data->np, "allwinner,sun8i-a23-ths")) {
+ data->ctrl0_value = THS_CTRL_REG0_VALUE_SUN8IW3;
+ data->divisor = 625;
+ data->minus = 265;
+ } else if (of_device_is_compatible(data->np, "allwinner,sun8i-a33-ths")) {
+ data->ctrl0_value = THS_CTRL_REG0_VALUE_SUN8IW5;
+ data->divisor = 618;
+ data->minus = 269;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(data->regs))
+ return PTR_ERR(data->regs);
+
+ data->clk = devm_clk_get(&pdev->dev, "pll2");
+ if (IS_ERR(data->clk)) {
+ ret = PTR_ERR(data->clk);
+ dev_err(&pdev->dev, "failed to get pll2 clk: %d\n", ret);
+ return ret;
+ }
+
+ data->tzd = devm_thermal_zone_of_sensor_register(
+ &pdev->dev, 0, data, &sun8iw3_of_thermal_ops);
+ if (IS_ERR(data->tzd)) {
+ ret = PTR_ERR(data->tzd);
+ dev_err(&pdev->dev, "can't register thermal zone: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(data->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable pll2 clk: %d\n", ret);
+ return ret;
+ }
+
+ writel(data->ctrl0_value, data->regs + THS_CTRL_REG0);
+ writel(THS_CTRL_REG1_VALUE, data->regs + THS_CTRL_REG1);
+ writel(THS_PRO_CTRL_REG_VALUE, data->regs + THS_PRO_CTRL_REG);
+
+ INIT_DELAYED_WORK(&data->work, sun8iw3_ths_work);
+ schedule_delayed_work(&data->work, msecs_to_jiffies(1000));
+
+ return 0;
+}
+
+static int sun8iw3_ths_exit(struct platform_device *pdev,
+ struct sun8iw3_ths_data *data)
+{
+ thermal_zone_device_unregister(data->tzd);
+ cancel_delayed_work(&data->work);
+ clk_disable_unprepare(data->clk);
+ writel(0, data->regs + THS_CTRL_REG0);
+ writel(0, data->regs + THS_CTRL_REG1);
+ writel(0, data->regs + THS_PRO_CTRL_REG);
+ return 0;
+}
+
+static int sun8iw3_ths_probe(struct platform_device *pdev)
+{
+ struct sun8iw3_ths_data *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+
+ return sun8iw3_ths_init(pdev, data);
+}
+
+static int sun8iw3_ths_remove(struct platform_device *pdev)
+{
+ struct sun8iw3_ths_data *data = platform_get_drvdata(pdev);
+
+ return sun8iw3_ths_exit(pdev, data);
+}
+
+static struct platform_driver sun8iw3_ths_driver = {
+ .driver = {
+ .name = "sun8iw3-ths",
+ .of_match_table = sun8iw3_ths_dt_ids,
+ },
+ .probe = sun8iw3_ths_probe,
+ .remove = sun8iw3_ths_remove,
+};
+
+module_platform_driver(sun8iw3_ths_driver);
+MODULE_AUTHOR("Icenowy Zheng <icenowy at aosc.xyz>");
+MODULE_DESCRIPTION("Allwinner A23/33 SoC thermal driver");
+MODULE_LICENSE("GPL v2");
--
2.9.0
More information about the linux-arm-kernel
mailing list