Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Kirkwood thermal sensor driver
0004  *
0005  * Copyright (C) 2012 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
0006  */
0007 #include <linux/device.h>
0008 #include <linux/err.h>
0009 #include <linux/io.h>
0010 #include <linux/kernel.h>
0011 #include <linux/of.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/thermal.h>
0015 
0016 #define KIRKWOOD_THERMAL_VALID_OFFSET   9
0017 #define KIRKWOOD_THERMAL_VALID_MASK 0x1
0018 #define KIRKWOOD_THERMAL_TEMP_OFFSET    10
0019 #define KIRKWOOD_THERMAL_TEMP_MASK  0x1FF
0020 
0021 /* Kirkwood Thermal Sensor Dev Structure */
0022 struct kirkwood_thermal_priv {
0023     void __iomem *sensor;
0024 };
0025 
0026 static int kirkwood_get_temp(struct thermal_zone_device *thermal,
0027               int *temp)
0028 {
0029     unsigned long reg;
0030     struct kirkwood_thermal_priv *priv = thermal->devdata;
0031 
0032     reg = readl_relaxed(priv->sensor);
0033 
0034     /* Valid check */
0035     if (!((reg >> KIRKWOOD_THERMAL_VALID_OFFSET) &
0036         KIRKWOOD_THERMAL_VALID_MASK)) {
0037         dev_err(&thermal->device,
0038             "Temperature sensor reading not valid\n");
0039         return -EIO;
0040     }
0041 
0042     /*
0043      * Calculate temperature. According to Marvell internal
0044      * documentation the formula for this is:
0045      * Celsius = (322-reg)/1.3625
0046      */
0047     reg = (reg >> KIRKWOOD_THERMAL_TEMP_OFFSET) &
0048         KIRKWOOD_THERMAL_TEMP_MASK;
0049     *temp = ((3220000000UL - (10000000UL * reg)) / 13625);
0050 
0051     return 0;
0052 }
0053 
0054 static struct thermal_zone_device_ops ops = {
0055     .get_temp = kirkwood_get_temp,
0056 };
0057 
0058 static const struct of_device_id kirkwood_thermal_id_table[] = {
0059     { .compatible = "marvell,kirkwood-thermal" },
0060     {}
0061 };
0062 
0063 static int kirkwood_thermal_probe(struct platform_device *pdev)
0064 {
0065     struct thermal_zone_device *thermal = NULL;
0066     struct kirkwood_thermal_priv *priv;
0067     struct resource *res;
0068     int ret;
0069 
0070     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0071     if (!priv)
0072         return -ENOMEM;
0073 
0074     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0075     priv->sensor = devm_ioremap_resource(&pdev->dev, res);
0076     if (IS_ERR(priv->sensor))
0077         return PTR_ERR(priv->sensor);
0078 
0079     thermal = thermal_zone_device_register("kirkwood_thermal", 0, 0,
0080                            priv, &ops, NULL, 0, 0);
0081     if (IS_ERR(thermal)) {
0082         dev_err(&pdev->dev,
0083             "Failed to register thermal zone device\n");
0084         return PTR_ERR(thermal);
0085     }
0086     ret = thermal_zone_device_enable(thermal);
0087     if (ret) {
0088         thermal_zone_device_unregister(thermal);
0089         dev_err(&pdev->dev, "Failed to enable thermal zone device\n");
0090         return ret;
0091     }
0092 
0093     platform_set_drvdata(pdev, thermal);
0094 
0095     return 0;
0096 }
0097 
0098 static int kirkwood_thermal_exit(struct platform_device *pdev)
0099 {
0100     struct thermal_zone_device *kirkwood_thermal =
0101         platform_get_drvdata(pdev);
0102 
0103     thermal_zone_device_unregister(kirkwood_thermal);
0104 
0105     return 0;
0106 }
0107 
0108 MODULE_DEVICE_TABLE(of, kirkwood_thermal_id_table);
0109 
0110 static struct platform_driver kirkwood_thermal_driver = {
0111     .probe = kirkwood_thermal_probe,
0112     .remove = kirkwood_thermal_exit,
0113     .driver = {
0114         .name = "kirkwood_thermal",
0115         .of_match_table = kirkwood_thermal_id_table,
0116     },
0117 };
0118 
0119 module_platform_driver(kirkwood_thermal_driver);
0120 
0121 MODULE_AUTHOR("Nobuhiro Iwamatsu <iwamatsu@nigauri.org>");
0122 MODULE_DESCRIPTION("kirkwood thermal driver");
0123 MODULE_LICENSE("GPL");