Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Thermal: Dove: Add Themal sensor support for Dove.

The Marvell Dove SoC has a thermal sensor. Add a driver using the
thermal framework.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>

authored by

Andrew Lunn and committed by
Zhang Rui
74ffa64c 7060aa36

+236
+18
Documentation/devicetree/bindings/thermal/dove-thermal.txt
··· 1 + * Dove Thermal 2 + 3 + This driver is for Dove SoCs which contain a thermal sensor. 4 + 5 + Required properties: 6 + - compatible : "marvell,dove-thermal" 7 + - reg : Address range of the thermal registers 8 + 9 + The reg properties should contain two ranges. The first is for the 10 + three Thermal Manager registers, while the second range contains the 11 + Thermal Diode Control Registers. 12 + 13 + Example: 14 + 15 + thermal@10078 { 16 + compatible = "marvell,dove-thermal"; 17 + reg = <0xd001c 0x0c>, <0xd005c 0x08>; 18 + };
+8
drivers/thermal/Kconfig
··· 126 126 device directory to support emulation mode. With emulation mode sysfs 127 127 node, you can manually input temperature to TMU for simulation purpose. 128 128 129 + config DOVE_THERMAL 130 + tristate "Temperature sensor on Marvell Dove SoCs" 131 + depends on ARCH_DOVE 132 + depends on OF 133 + help 134 + Support for the Dove thermal sensor driver in the Linux thermal 135 + framework. 136 + 129 137 config DB8500_THERMAL 130 138 bool "DB8500 thermal management" 131 139 depends on ARCH_U8500
+1
drivers/thermal/Makefile
··· 17 17 obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o 18 18 obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o 19 19 obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o 20 + obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o 20 21 obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o 21 22 obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o 22 23 obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
+209
drivers/thermal/dove_thermal.c
··· 1 + /* 2 + * Dove thermal sensor driver 3 + * 4 + * Copyright (C) 2013 Andrew Lunn <andrew@lunn.ch> 5 + * 6 + * This software is licensed under the terms of the GNU General Public 7 + * License version 2, as published by the Free Software Foundation, and 8 + * may be copied, distributed, and modified under those terms. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + */ 16 + #include <linux/device.h> 17 + #include <linux/err.h> 18 + #include <linux/io.h> 19 + #include <linux/kernel.h> 20 + #include <linux/of.h> 21 + #include <linux/module.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/thermal.h> 24 + 25 + #define DOVE_THERMAL_TEMP_OFFSET 1 26 + #define DOVE_THERMAL_TEMP_MASK 0x1FF 27 + 28 + /* Dove Thermal Manager Control and Status Register */ 29 + #define PMU_TM_DISABLE_OFFS 0 30 + #define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_OFFS) 31 + 32 + /* Dove Theraml Diode Control 0 Register */ 33 + #define PMU_TDC0_SW_RST_MASK (0x1 << 1) 34 + #define PMU_TDC0_SEL_VCAL_OFFS 5 35 + #define PMU_TDC0_SEL_VCAL_MASK (0x3 << PMU_TDC0_SEL_VCAL_OFFS) 36 + #define PMU_TDC0_REF_CAL_CNT_OFFS 11 37 + #define PMU_TDC0_REF_CAL_CNT_MASK (0x1FF << PMU_TDC0_REF_CAL_CNT_OFFS) 38 + #define PMU_TDC0_AVG_NUM_OFFS 25 39 + #define PMU_TDC0_AVG_NUM_MASK (0x7 << PMU_TDC0_AVG_NUM_OFFS) 40 + 41 + /* Dove Thermal Diode Control 1 Register */ 42 + #define PMU_TEMP_DIOD_CTRL1_REG 0x04 43 + #define PMU_TDC1_TEMP_VALID_MASK (0x1 << 10) 44 + 45 + /* Dove Thermal Sensor Dev Structure */ 46 + struct dove_thermal_priv { 47 + void __iomem *sensor; 48 + void __iomem *control; 49 + }; 50 + 51 + static int dove_init_sensor(const struct dove_thermal_priv *priv) 52 + { 53 + u32 reg; 54 + u32 i; 55 + 56 + /* Configure the Diode Control Register #0 */ 57 + reg = readl_relaxed(priv->control); 58 + 59 + /* Use average of 2 */ 60 + reg &= ~PMU_TDC0_AVG_NUM_MASK; 61 + reg |= (0x1 << PMU_TDC0_AVG_NUM_OFFS); 62 + 63 + /* Reference calibration value */ 64 + reg &= ~PMU_TDC0_REF_CAL_CNT_MASK; 65 + reg |= (0x0F1 << PMU_TDC0_REF_CAL_CNT_OFFS); 66 + 67 + /* Set the high level reference for calibration */ 68 + reg &= ~PMU_TDC0_SEL_VCAL_MASK; 69 + reg |= (0x2 << PMU_TDC0_SEL_VCAL_OFFS); 70 + writel(reg, priv->control); 71 + 72 + /* Reset the sensor */ 73 + reg = readl_relaxed(priv->control); 74 + writel((reg | PMU_TDC0_SW_RST_MASK), priv->control); 75 + writel(reg, priv->control); 76 + 77 + /* Enable the sensor */ 78 + reg = readl_relaxed(priv->sensor); 79 + reg &= ~PMU_TM_DISABLE_MASK; 80 + writel(reg, priv->sensor); 81 + 82 + /* Poll the sensor for the first reading */ 83 + for (i = 0; i < 1000000; i++) { 84 + reg = readl_relaxed(priv->sensor); 85 + if (reg & DOVE_THERMAL_TEMP_MASK) 86 + break; 87 + } 88 + 89 + if (i == 1000000) 90 + return -EIO; 91 + 92 + return 0; 93 + } 94 + 95 + static int dove_get_temp(struct thermal_zone_device *thermal, 96 + unsigned long *temp) 97 + { 98 + unsigned long reg; 99 + struct dove_thermal_priv *priv = thermal->devdata; 100 + 101 + /* Valid check */ 102 + reg = readl_relaxed(priv->control + PMU_TEMP_DIOD_CTRL1_REG); 103 + if ((reg & PMU_TDC1_TEMP_VALID_MASK) == 0x0) { 104 + dev_err(&thermal->device, 105 + "Temperature sensor reading not valid\n"); 106 + return -EIO; 107 + } 108 + 109 + /* 110 + * Calculate temperature. See Section 8.10.1 of 88AP510, 111 + * Documentation/arm/Marvell/README 112 + */ 113 + reg = readl_relaxed(priv->sensor); 114 + reg = (reg >> DOVE_THERMAL_TEMP_OFFSET) & DOVE_THERMAL_TEMP_MASK; 115 + *temp = ((2281638UL - (7298*reg)) / 10); 116 + 117 + return 0; 118 + } 119 + 120 + static struct thermal_zone_device_ops ops = { 121 + .get_temp = dove_get_temp, 122 + }; 123 + 124 + static const struct of_device_id dove_thermal_id_table[] = { 125 + { .compatible = "marvell,dove-thermal" }, 126 + {} 127 + }; 128 + 129 + static int dove_thermal_probe(struct platform_device *pdev) 130 + { 131 + struct thermal_zone_device *thermal = NULL; 132 + struct dove_thermal_priv *priv; 133 + struct resource *res; 134 + int ret; 135 + 136 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 137 + if (!res) { 138 + dev_err(&pdev->dev, "Failed to get platform resource\n"); 139 + return -ENODEV; 140 + } 141 + 142 + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 143 + if (!priv) 144 + return -ENOMEM; 145 + 146 + priv->sensor = devm_request_and_ioremap(&pdev->dev, res); 147 + if (!priv->sensor) { 148 + dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); 149 + return -EADDRNOTAVAIL; 150 + } 151 + 152 + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 153 + if (!res) { 154 + dev_err(&pdev->dev, "Failed to get platform resource\n"); 155 + return -ENODEV; 156 + } 157 + priv->control = devm_request_and_ioremap(&pdev->dev, res); 158 + if (!priv->control) { 159 + dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); 160 + return -EADDRNOTAVAIL; 161 + } 162 + 163 + ret = dove_init_sensor(priv); 164 + if (ret) { 165 + dev_err(&pdev->dev, "Failed to initialize sensor\n"); 166 + return ret; 167 + } 168 + 169 + thermal = thermal_zone_device_register("dove_thermal", 0, 0, 170 + priv, &ops, NULL, 0, 0); 171 + if (IS_ERR(thermal)) { 172 + dev_err(&pdev->dev, 173 + "Failed to register thermal zone device\n"); 174 + return PTR_ERR(thermal); 175 + } 176 + 177 + platform_set_drvdata(pdev, thermal); 178 + 179 + return 0; 180 + } 181 + 182 + static int dove_thermal_exit(struct platform_device *pdev) 183 + { 184 + struct thermal_zone_device *dove_thermal = 185 + platform_get_drvdata(pdev); 186 + 187 + thermal_zone_device_unregister(dove_thermal); 188 + platform_set_drvdata(pdev, NULL); 189 + 190 + return 0; 191 + } 192 + 193 + MODULE_DEVICE_TABLE(of, dove_thermal_id_table); 194 + 195 + static struct platform_driver dove_thermal_driver = { 196 + .probe = dove_thermal_probe, 197 + .remove = dove_thermal_exit, 198 + .driver = { 199 + .name = "dove_thermal", 200 + .owner = THIS_MODULE, 201 + .of_match_table = of_match_ptr(dove_thermal_id_table), 202 + }, 203 + }; 204 + 205 + module_platform_driver(dove_thermal_driver); 206 + 207 + MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); 208 + MODULE_DESCRIPTION("Dove thermal driver"); 209 + MODULE_LICENSE("GPL");