17060aa36SNobuhiro Iwamatsu /* 27060aa36SNobuhiro Iwamatsu * Kirkwood thermal sensor driver 37060aa36SNobuhiro Iwamatsu * 47060aa36SNobuhiro Iwamatsu * Copyright (C) 2012 Nobuhiro Iwamatsu <iwamatsu@nigauri.org> 57060aa36SNobuhiro Iwamatsu * 67060aa36SNobuhiro Iwamatsu * This software is licensed under the terms of the GNU General Public 77060aa36SNobuhiro Iwamatsu * License version 2, as published by the Free Software Foundation, and 87060aa36SNobuhiro Iwamatsu * may be copied, distributed, and modified under those terms. 97060aa36SNobuhiro Iwamatsu * 107060aa36SNobuhiro Iwamatsu * This program is distributed in the hope that it will be useful, 117060aa36SNobuhiro Iwamatsu * but WITHOUT ANY WARRANTY; without even the implied warranty of 127060aa36SNobuhiro Iwamatsu * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 137060aa36SNobuhiro Iwamatsu * GNU General Public License for more details. 147060aa36SNobuhiro Iwamatsu * 157060aa36SNobuhiro Iwamatsu */ 167060aa36SNobuhiro Iwamatsu #include <linux/device.h> 177060aa36SNobuhiro Iwamatsu #include <linux/err.h> 187060aa36SNobuhiro Iwamatsu #include <linux/io.h> 197060aa36SNobuhiro Iwamatsu #include <linux/kernel.h> 207060aa36SNobuhiro Iwamatsu #include <linux/of.h> 217060aa36SNobuhiro Iwamatsu #include <linux/module.h> 227060aa36SNobuhiro Iwamatsu #include <linux/platform_device.h> 237060aa36SNobuhiro Iwamatsu #include <linux/thermal.h> 247060aa36SNobuhiro Iwamatsu 257060aa36SNobuhiro Iwamatsu #define KIRKWOOD_THERMAL_VALID_OFFSET 9 267060aa36SNobuhiro Iwamatsu #define KIRKWOOD_THERMAL_VALID_MASK 0x1 277060aa36SNobuhiro Iwamatsu #define KIRKWOOD_THERMAL_TEMP_OFFSET 10 287060aa36SNobuhiro Iwamatsu #define KIRKWOOD_THERMAL_TEMP_MASK 0x1FF 297060aa36SNobuhiro Iwamatsu 307060aa36SNobuhiro Iwamatsu /* Kirkwood Thermal Sensor Dev Structure */ 317060aa36SNobuhiro Iwamatsu struct kirkwood_thermal_priv { 327060aa36SNobuhiro Iwamatsu void __iomem *sensor; 337060aa36SNobuhiro Iwamatsu }; 347060aa36SNobuhiro Iwamatsu 357060aa36SNobuhiro Iwamatsu static int kirkwood_get_temp(struct thermal_zone_device *thermal, 367060aa36SNobuhiro Iwamatsu unsigned long *temp) 377060aa36SNobuhiro Iwamatsu { 387060aa36SNobuhiro Iwamatsu unsigned long reg; 397060aa36SNobuhiro Iwamatsu struct kirkwood_thermal_priv *priv = thermal->devdata; 407060aa36SNobuhiro Iwamatsu 417060aa36SNobuhiro Iwamatsu reg = readl_relaxed(priv->sensor); 427060aa36SNobuhiro Iwamatsu 437060aa36SNobuhiro Iwamatsu /* Valid check */ 4402519d33SEzequiel Garcia if (!((reg >> KIRKWOOD_THERMAL_VALID_OFFSET) & 4502519d33SEzequiel Garcia KIRKWOOD_THERMAL_VALID_MASK)) { 467060aa36SNobuhiro Iwamatsu dev_err(&thermal->device, 477060aa36SNobuhiro Iwamatsu "Temperature sensor reading not valid\n"); 487060aa36SNobuhiro Iwamatsu return -EIO; 497060aa36SNobuhiro Iwamatsu } 507060aa36SNobuhiro Iwamatsu 517060aa36SNobuhiro Iwamatsu /* 52696b6075SEzequiel Garcia * Calculate temperature. According to Marvell internal 53696b6075SEzequiel Garcia * documentation the formula for this is: 54696b6075SEzequiel Garcia * Celsius = (322-reg)/1.3625 557060aa36SNobuhiro Iwamatsu */ 567060aa36SNobuhiro Iwamatsu reg = (reg >> KIRKWOOD_THERMAL_TEMP_OFFSET) & 577060aa36SNobuhiro Iwamatsu KIRKWOOD_THERMAL_TEMP_MASK; 58696b6075SEzequiel Garcia *temp = ((3220000000UL - (10000000UL * reg)) / 13625); 597060aa36SNobuhiro Iwamatsu 607060aa36SNobuhiro Iwamatsu return 0; 617060aa36SNobuhiro Iwamatsu } 627060aa36SNobuhiro Iwamatsu 637060aa36SNobuhiro Iwamatsu static struct thermal_zone_device_ops ops = { 647060aa36SNobuhiro Iwamatsu .get_temp = kirkwood_get_temp, 657060aa36SNobuhiro Iwamatsu }; 667060aa36SNobuhiro Iwamatsu 677060aa36SNobuhiro Iwamatsu static const struct of_device_id kirkwood_thermal_id_table[] = { 687060aa36SNobuhiro Iwamatsu { .compatible = "marvell,kirkwood-thermal" }, 697060aa36SNobuhiro Iwamatsu {} 707060aa36SNobuhiro Iwamatsu }; 717060aa36SNobuhiro Iwamatsu 727060aa36SNobuhiro Iwamatsu static int kirkwood_thermal_probe(struct platform_device *pdev) 737060aa36SNobuhiro Iwamatsu { 747060aa36SNobuhiro Iwamatsu struct thermal_zone_device *thermal = NULL; 757060aa36SNobuhiro Iwamatsu struct kirkwood_thermal_priv *priv; 767060aa36SNobuhiro Iwamatsu struct resource *res; 777060aa36SNobuhiro Iwamatsu 787060aa36SNobuhiro Iwamatsu priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 797060aa36SNobuhiro Iwamatsu if (!priv) 807060aa36SNobuhiro Iwamatsu return -ENOMEM; 817060aa36SNobuhiro Iwamatsu 82c28f692cSWolfram Sang res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 83aa3b5d22SSachin Kamat priv->sensor = devm_ioremap_resource(&pdev->dev, res); 84aa3b5d22SSachin Kamat if (IS_ERR(priv->sensor)) 85aa3b5d22SSachin Kamat return PTR_ERR(priv->sensor); 867060aa36SNobuhiro Iwamatsu 877060aa36SNobuhiro Iwamatsu thermal = thermal_zone_device_register("kirkwood_thermal", 0, 0, 887060aa36SNobuhiro Iwamatsu priv, &ops, NULL, 0, 0); 897060aa36SNobuhiro Iwamatsu if (IS_ERR(thermal)) { 907060aa36SNobuhiro Iwamatsu dev_err(&pdev->dev, 917060aa36SNobuhiro Iwamatsu "Failed to register thermal zone device\n"); 927060aa36SNobuhiro Iwamatsu return PTR_ERR(thermal); 937060aa36SNobuhiro Iwamatsu } 947060aa36SNobuhiro Iwamatsu 957060aa36SNobuhiro Iwamatsu platform_set_drvdata(pdev, thermal); 967060aa36SNobuhiro Iwamatsu 977060aa36SNobuhiro Iwamatsu return 0; 987060aa36SNobuhiro Iwamatsu } 997060aa36SNobuhiro Iwamatsu 1007060aa36SNobuhiro Iwamatsu static int kirkwood_thermal_exit(struct platform_device *pdev) 1017060aa36SNobuhiro Iwamatsu { 1027060aa36SNobuhiro Iwamatsu struct thermal_zone_device *kirkwood_thermal = 1037060aa36SNobuhiro Iwamatsu platform_get_drvdata(pdev); 1047060aa36SNobuhiro Iwamatsu 1057060aa36SNobuhiro Iwamatsu thermal_zone_device_unregister(kirkwood_thermal); 1067060aa36SNobuhiro Iwamatsu 1077060aa36SNobuhiro Iwamatsu return 0; 1087060aa36SNobuhiro Iwamatsu } 1097060aa36SNobuhiro Iwamatsu 1107060aa36SNobuhiro Iwamatsu MODULE_DEVICE_TABLE(of, kirkwood_thermal_id_table); 1117060aa36SNobuhiro Iwamatsu 1127060aa36SNobuhiro Iwamatsu static struct platform_driver kirkwood_thermal_driver = { 1137060aa36SNobuhiro Iwamatsu .probe = kirkwood_thermal_probe, 1147060aa36SNobuhiro Iwamatsu .remove = kirkwood_thermal_exit, 1157060aa36SNobuhiro Iwamatsu .driver = { 1167060aa36SNobuhiro Iwamatsu .name = "kirkwood_thermal", 117*90c3194eSSachin Kamat .of_match_table = kirkwood_thermal_id_table, 1187060aa36SNobuhiro Iwamatsu }, 1197060aa36SNobuhiro Iwamatsu }; 1207060aa36SNobuhiro Iwamatsu 1217060aa36SNobuhiro Iwamatsu module_platform_driver(kirkwood_thermal_driver); 1227060aa36SNobuhiro Iwamatsu 1237060aa36SNobuhiro Iwamatsu MODULE_AUTHOR("Nobuhiro Iwamatsu <iwamatsu@nigauri.org>"); 1247060aa36SNobuhiro Iwamatsu MODULE_DESCRIPTION("kirkwood thermal driver"); 1257060aa36SNobuhiro Iwamatsu MODULE_LICENSE("GPL"); 126