1 /* 2 * Marvell Armada 370/XP thermal sensor driver 3 * 4 * Copyright (C) 2013 Marvell 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/delay.h> 23 #include <linux/platform_device.h> 24 #include <linux/of_device.h> 25 #include <linux/thermal.h> 26 27 #define THERMAL_VALID_OFFSET 9 28 #define THERMAL_VALID_MASK 0x1 29 #define THERMAL_TEMP_OFFSET 10 30 #define THERMAL_TEMP_MASK 0x1ff 31 32 /* Thermal Manager Control and Status Register */ 33 #define PMU_TDC0_SW_RST_MASK (0x1 << 1) 34 #define PMU_TM_DISABLE_OFFS 0 35 #define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_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_OTF_CAL_MASK (0x1 << 30) 39 #define PMU_TDC0_START_CAL_MASK (0x1 << 25) 40 41 struct armada_thermal_ops; 42 43 /* Marvell EBU Thermal Sensor Dev Structure */ 44 struct armada_thermal_priv { 45 void __iomem *sensor; 46 void __iomem *control; 47 struct armada_thermal_ops *ops; 48 }; 49 50 struct armada_thermal_ops { 51 /* Initialize the sensor */ 52 void (*init_sensor)(struct armada_thermal_priv *); 53 54 /* Test for a valid sensor value (optional) */ 55 bool (*is_valid)(struct armada_thermal_priv *); 56 }; 57 58 static void armadaxp_init_sensor(struct armada_thermal_priv *priv) 59 { 60 unsigned long reg; 61 62 reg = readl_relaxed(priv->control); 63 reg |= PMU_TDC0_OTF_CAL_MASK; 64 writel(reg, priv->control); 65 66 /* Reference calibration value */ 67 reg &= ~PMU_TDC0_REF_CAL_CNT_MASK; 68 reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS); 69 writel(reg, priv->control); 70 71 /* Reset the sensor */ 72 reg = readl_relaxed(priv->control); 73 writel((reg | PMU_TDC0_SW_RST_MASK), priv->control); 74 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 83 static void armada370_init_sensor(struct armada_thermal_priv *priv) 84 { 85 unsigned long reg; 86 87 reg = readl_relaxed(priv->control); 88 reg |= PMU_TDC0_OTF_CAL_MASK; 89 writel(reg, priv->control); 90 91 /* Reference calibration value */ 92 reg &= ~PMU_TDC0_REF_CAL_CNT_MASK; 93 reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS); 94 writel(reg, priv->control); 95 96 reg &= ~PMU_TDC0_START_CAL_MASK; 97 writel(reg, priv->control); 98 99 mdelay(10); 100 } 101 102 static bool armada_is_valid(struct armada_thermal_priv *priv) 103 { 104 unsigned long reg = readl_relaxed(priv->sensor); 105 106 return (reg >> THERMAL_VALID_OFFSET) & THERMAL_VALID_MASK; 107 } 108 109 static int armada_get_temp(struct thermal_zone_device *thermal, 110 unsigned long *temp) 111 { 112 struct armada_thermal_priv *priv = thermal->devdata; 113 unsigned long reg; 114 115 /* Valid check */ 116 if (priv->ops->is_valid && !priv->ops->is_valid(priv)) { 117 dev_err(&thermal->device, 118 "Temperature sensor reading not valid\n"); 119 return -EIO; 120 } 121 122 reg = readl_relaxed(priv->sensor); 123 reg = (reg >> THERMAL_TEMP_OFFSET) & THERMAL_TEMP_MASK; 124 *temp = (3153000000UL - (10000000UL*reg)) / 13825; 125 return 0; 126 } 127 128 static struct thermal_zone_device_ops ops = { 129 .get_temp = armada_get_temp, 130 }; 131 132 static const struct armada_thermal_ops armadaxp_ops = { 133 .init_sensor = armadaxp_init_sensor, 134 }; 135 136 static const struct armada_thermal_ops armada370_ops = { 137 .is_valid = armada_is_valid, 138 .init_sensor = armada370_init_sensor, 139 }; 140 141 static const struct of_device_id armada_thermal_id_table[] = { 142 { 143 .compatible = "marvell,armadaxp-thermal", 144 .data = &armadaxp_ops, 145 }, 146 { 147 .compatible = "marvell,armada370-thermal", 148 .data = &armada370_ops, 149 }, 150 { 151 /* sentinel */ 152 }, 153 }; 154 MODULE_DEVICE_TABLE(of, armada_thermal_id_table); 155 156 static int armada_thermal_probe(struct platform_device *pdev) 157 { 158 struct thermal_zone_device *thermal; 159 const struct of_device_id *match; 160 struct armada_thermal_priv *priv; 161 struct resource *res; 162 163 match = of_match_device(armada_thermal_id_table, &pdev->dev); 164 if (!match) 165 return -ENODEV; 166 167 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 168 if (!priv) 169 return -ENOMEM; 170 171 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 172 priv->sensor = devm_ioremap_resource(&pdev->dev, res); 173 if (IS_ERR(priv->sensor)) 174 return PTR_ERR(priv->sensor); 175 176 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 177 priv->control = devm_ioremap_resource(&pdev->dev, res); 178 if (IS_ERR(priv->control)) 179 return PTR_ERR(priv->control); 180 181 priv->ops = (struct armada_thermal_ops *)match->data; 182 priv->ops->init_sensor(priv); 183 184 thermal = thermal_zone_device_register("armada_thermal", 0, 0, 185 priv, &ops, NULL, 0, 0); 186 if (IS_ERR(thermal)) { 187 dev_err(&pdev->dev, 188 "Failed to register thermal zone device\n"); 189 return PTR_ERR(thermal); 190 } 191 192 platform_set_drvdata(pdev, thermal); 193 194 return 0; 195 } 196 197 static int armada_thermal_exit(struct platform_device *pdev) 198 { 199 struct thermal_zone_device *armada_thermal = 200 platform_get_drvdata(pdev); 201 202 thermal_zone_device_unregister(armada_thermal); 203 204 return 0; 205 } 206 207 static struct platform_driver armada_thermal_driver = { 208 .probe = armada_thermal_probe, 209 .remove = armada_thermal_exit, 210 .driver = { 211 .name = "armada_thermal", 212 .owner = THIS_MODULE, 213 .of_match_table = armada_thermal_id_table, 214 }, 215 }; 216 217 module_platform_driver(armada_thermal_driver); 218 219 MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>"); 220 MODULE_DESCRIPTION("Armada 370/XP thermal driver"); 221 MODULE_LICENSE("GPL v2"); 222