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"); 210