Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * db8500_thermal.c - DB8500 Thermal Management Implementation
0004  *
0005  * Copyright (C) 2012 ST-Ericsson
0006  * Copyright (C) 2012-2019 Linaro Ltd.
0007  *
0008  * Authors: Hongbo Zhang, Linus Walleij
0009  */
0010 
0011 #include <linux/cpu_cooling.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/mfd/dbx500-prcmu.h>
0014 #include <linux/module.h>
0015 #include <linux/of.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/slab.h>
0018 #include <linux/thermal.h>
0019 
0020 #define PRCMU_DEFAULT_MEASURE_TIME  0xFFF
0021 #define PRCMU_DEFAULT_LOW_TEMP      0
0022 
0023 /**
0024  * db8500_thermal_points - the interpolation points that trigger
0025  * interrupts
0026  */
0027 static const unsigned long db8500_thermal_points[] = {
0028     15000,
0029     20000,
0030     25000,
0031     30000,
0032     35000,
0033     40000,
0034     45000,
0035     50000,
0036     55000,
0037     60000,
0038     65000,
0039     70000,
0040     75000,
0041     80000,
0042     /*
0043      * This is where things start to get really bad for the
0044      * SoC and the thermal zones should be set up to trigger
0045      * critical temperature at 85000 mC so we don't get above
0046      * this point.
0047      */
0048     85000,
0049     90000,
0050     95000,
0051     100000,
0052 };
0053 
0054 struct db8500_thermal_zone {
0055     struct thermal_zone_device *tz;
0056     unsigned long interpolated_temp;
0057     unsigned int cur_index;
0058 };
0059 
0060 /* Callback to get current temperature */
0061 static int db8500_thermal_get_temp(void *data, int *temp)
0062 {
0063     struct db8500_thermal_zone *th = data;
0064 
0065     /*
0066      * TODO: There is no PRCMU interface to get temperature data currently,
0067      * so a pseudo temperature is returned , it works for thermal framework
0068      * and this will be fixed when the PRCMU interface is available.
0069      */
0070     *temp = th->interpolated_temp;
0071 
0072     return 0;
0073 }
0074 
0075 static struct thermal_zone_of_device_ops thdev_ops = {
0076     .get_temp = db8500_thermal_get_temp,
0077 };
0078 
0079 static void db8500_thermal_update_config(struct db8500_thermal_zone *th,
0080                      unsigned int idx,
0081                      unsigned long next_low,
0082                      unsigned long next_high)
0083 {
0084     prcmu_stop_temp_sense();
0085 
0086     th->cur_index = idx;
0087     th->interpolated_temp = (next_low + next_high)/2;
0088 
0089     /*
0090      * The PRCMU accept absolute temperatures in celsius so divide
0091      * down the millicelsius with 1000
0092      */
0093     prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));
0094     prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
0095 }
0096 
0097 static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data)
0098 {
0099     struct db8500_thermal_zone *th = irq_data;
0100     unsigned int idx = th->cur_index;
0101     unsigned long next_low, next_high;
0102 
0103     if (idx == 0)
0104         /* Meaningless for thermal management, ignoring it */
0105         return IRQ_HANDLED;
0106 
0107     if (idx == 1) {
0108         next_high = db8500_thermal_points[0];
0109         next_low = PRCMU_DEFAULT_LOW_TEMP;
0110     } else {
0111         next_high = db8500_thermal_points[idx - 1];
0112         next_low = db8500_thermal_points[idx - 2];
0113     }
0114     idx -= 1;
0115 
0116     db8500_thermal_update_config(th, idx, next_low, next_high);
0117     dev_dbg(&th->tz->device,
0118         "PRCMU set max %ld, min %ld\n", next_high, next_low);
0119 
0120     thermal_zone_device_update(th->tz, THERMAL_EVENT_UNSPECIFIED);
0121 
0122     return IRQ_HANDLED;
0123 }
0124 
0125 static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data)
0126 {
0127     struct db8500_thermal_zone *th = irq_data;
0128     unsigned int idx = th->cur_index;
0129     unsigned long next_low, next_high;
0130     int num_points = ARRAY_SIZE(db8500_thermal_points);
0131 
0132     if (idx < num_points - 1) {
0133         next_high = db8500_thermal_points[idx+1];
0134         next_low = db8500_thermal_points[idx];
0135         idx += 1;
0136 
0137         db8500_thermal_update_config(th, idx, next_low, next_high);
0138 
0139         dev_dbg(&th->tz->device,
0140             "PRCMU set max %ld, min %ld\n", next_high, next_low);
0141     } else if (idx == num_points - 1)
0142         /* So we roof out 1 degree over the max point */
0143         th->interpolated_temp = db8500_thermal_points[idx] + 1;
0144 
0145     thermal_zone_device_update(th->tz, THERMAL_EVENT_UNSPECIFIED);
0146 
0147     return IRQ_HANDLED;
0148 }
0149 
0150 static int db8500_thermal_probe(struct platform_device *pdev)
0151 {
0152     struct db8500_thermal_zone *th = NULL;
0153     struct device *dev = &pdev->dev;
0154     int low_irq, high_irq, ret = 0;
0155 
0156     th = devm_kzalloc(dev, sizeof(*th), GFP_KERNEL);
0157     if (!th)
0158         return -ENOMEM;
0159 
0160     low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
0161     if (low_irq < 0)
0162         return low_irq;
0163 
0164     ret = devm_request_threaded_irq(dev, low_irq, NULL,
0165         prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
0166         "dbx500_temp_low", th);
0167     if (ret < 0) {
0168         dev_err(dev, "failed to allocate temp low irq\n");
0169         return ret;
0170     }
0171 
0172     high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
0173     if (high_irq < 0)
0174         return high_irq;
0175 
0176     ret = devm_request_threaded_irq(dev, high_irq, NULL,
0177         prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
0178         "dbx500_temp_high", th);
0179     if (ret < 0) {
0180         dev_err(dev, "failed to allocate temp high irq\n");
0181         return ret;
0182     }
0183 
0184     /* register of thermal sensor and get info from DT */
0185     th->tz = devm_thermal_zone_of_sensor_register(dev, 0, th, &thdev_ops);
0186     if (IS_ERR(th->tz)) {
0187         dev_err(dev, "register thermal zone sensor failed\n");
0188         return PTR_ERR(th->tz);
0189     }
0190     dev_info(dev, "thermal zone sensor registered\n");
0191 
0192     /* Start measuring at the lowest point */
0193     db8500_thermal_update_config(th, 0, PRCMU_DEFAULT_LOW_TEMP,
0194                      db8500_thermal_points[0]);
0195 
0196     platform_set_drvdata(pdev, th);
0197 
0198     return 0;
0199 }
0200 
0201 static int db8500_thermal_suspend(struct platform_device *pdev,
0202         pm_message_t state)
0203 {
0204     prcmu_stop_temp_sense();
0205 
0206     return 0;
0207 }
0208 
0209 static int db8500_thermal_resume(struct platform_device *pdev)
0210 {
0211     struct db8500_thermal_zone *th = platform_get_drvdata(pdev);
0212 
0213     /* Resume and start measuring at the lowest point */
0214     db8500_thermal_update_config(th, 0, PRCMU_DEFAULT_LOW_TEMP,
0215                      db8500_thermal_points[0]);
0216 
0217     return 0;
0218 }
0219 
0220 static const struct of_device_id db8500_thermal_match[] = {
0221     { .compatible = "stericsson,db8500-thermal" },
0222     {},
0223 };
0224 MODULE_DEVICE_TABLE(of, db8500_thermal_match);
0225 
0226 static struct platform_driver db8500_thermal_driver = {
0227     .driver = {
0228         .name = "db8500-thermal",
0229         .of_match_table = of_match_ptr(db8500_thermal_match),
0230     },
0231     .probe = db8500_thermal_probe,
0232     .suspend = db8500_thermal_suspend,
0233     .resume = db8500_thermal_resume,
0234 };
0235 
0236 module_platform_driver(db8500_thermal_driver);
0237 
0238 MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
0239 MODULE_DESCRIPTION("DB8500 thermal driver");
0240 MODULE_LICENSE("GPL");