Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * OMAP thermal driver interface
0004  *
0005  * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
0006  * Contact:
0007  *   Eduardo Valentin <eduardo.valentin@ti.com>
0008  */
0009 
0010 #include <linux/device.h>
0011 #include <linux/err.h>
0012 #include <linux/mutex.h>
0013 #include <linux/gfp.h>
0014 #include <linux/kernel.h>
0015 #include <linux/workqueue.h>
0016 #include <linux/thermal.h>
0017 #include <linux/cpufreq.h>
0018 #include <linux/cpumask.h>
0019 #include <linux/cpu_cooling.h>
0020 #include <linux/of.h>
0021 
0022 #include "ti-thermal.h"
0023 #include "ti-bandgap.h"
0024 #include "../thermal_hwmon.h"
0025 
0026 /* common data structures */
0027 struct ti_thermal_data {
0028     struct cpufreq_policy *policy;
0029     struct thermal_zone_device *ti_thermal;
0030     struct thermal_zone_device *pcb_tz;
0031     struct thermal_cooling_device *cool_dev;
0032     struct ti_bandgap *bgp;
0033     enum thermal_device_mode mode;
0034     struct work_struct thermal_wq;
0035     int sensor_id;
0036     bool our_zone;
0037 };
0038 
0039 static void ti_thermal_work(struct work_struct *work)
0040 {
0041     struct ti_thermal_data *data = container_of(work,
0042                     struct ti_thermal_data, thermal_wq);
0043 
0044     thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
0045 
0046     dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
0047         data->ti_thermal->type);
0048 }
0049 
0050 /**
0051  * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
0052  * @t:  omap sensor temperature
0053  * @s:  omap sensor slope value
0054  * @c:  omap sensor const value
0055  */
0056 static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
0057 {
0058     int delta = t * s / 1000 + c;
0059 
0060     if (delta < 0)
0061         delta = 0;
0062 
0063     return t + delta;
0064 }
0065 
0066 /* thermal zone ops */
0067 /* Get temperature callback function for thermal zone */
0068 static inline int __ti_thermal_get_temp(void *devdata, int *temp)
0069 {
0070     struct thermal_zone_device *pcb_tz = NULL;
0071     struct ti_thermal_data *data = devdata;
0072     struct ti_bandgap *bgp;
0073     const struct ti_temp_sensor *s;
0074     int ret, tmp, slope, constant;
0075     int pcb_temp;
0076 
0077     if (!data)
0078         return 0;
0079 
0080     bgp = data->bgp;
0081     s = &bgp->conf->sensors[data->sensor_id];
0082 
0083     ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);
0084     if (ret)
0085         return ret;
0086 
0087     /* Default constants */
0088     slope = thermal_zone_get_slope(data->ti_thermal);
0089     constant = thermal_zone_get_offset(data->ti_thermal);
0090 
0091     pcb_tz = data->pcb_tz;
0092     /* In case pcb zone is available, use the extrapolation rule with it */
0093     if (!IS_ERR(pcb_tz)) {
0094         ret = thermal_zone_get_temp(pcb_tz, &pcb_temp);
0095         if (!ret) {
0096             tmp -= pcb_temp; /* got a valid PCB temp */
0097             slope = s->slope_pcb;
0098             constant = s->constant_pcb;
0099         } else {
0100             dev_err(bgp->dev,
0101                 "Failed to read PCB state. Using defaults\n");
0102             ret = 0;
0103         }
0104     }
0105     *temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
0106 
0107     return ret;
0108 }
0109 
0110 static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend)
0111 {
0112     struct ti_thermal_data *data = p;
0113     struct ti_bandgap *bgp;
0114     int id, tr, ret = 0;
0115 
0116     bgp = data->bgp;
0117     id = data->sensor_id;
0118 
0119     ret = ti_bandgap_get_trend(bgp, id, &tr);
0120     if (ret)
0121         return ret;
0122 
0123     if (tr > 0)
0124         *trend = THERMAL_TREND_RAISING;
0125     else if (tr < 0)
0126         *trend = THERMAL_TREND_DROPPING;
0127     else
0128         *trend = THERMAL_TREND_STABLE;
0129 
0130     return 0;
0131 }
0132 
0133 static const struct thermal_zone_of_device_ops ti_of_thermal_ops = {
0134     .get_temp = __ti_thermal_get_temp,
0135     .get_trend = __ti_thermal_get_trend,
0136 };
0137 
0138 static struct ti_thermal_data
0139 *ti_thermal_build_data(struct ti_bandgap *bgp, int id)
0140 {
0141     struct ti_thermal_data *data;
0142 
0143     data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
0144     if (!data) {
0145         dev_err(bgp->dev, "kzalloc fail\n");
0146         return NULL;
0147     }
0148     data->sensor_id = id;
0149     data->bgp = bgp;
0150     data->mode = THERMAL_DEVICE_ENABLED;
0151     /* pcb_tz will be either valid or PTR_ERR() */
0152     data->pcb_tz = thermal_zone_get_zone_by_name("pcb");
0153     INIT_WORK(&data->thermal_wq, ti_thermal_work);
0154 
0155     return data;
0156 }
0157 
0158 int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
0159                  char *domain)
0160 {
0161     struct ti_thermal_data *data;
0162     int interval;
0163 
0164     data = ti_bandgap_get_sensor_data(bgp, id);
0165 
0166     if (IS_ERR_OR_NULL(data))
0167         data = ti_thermal_build_data(bgp, id);
0168 
0169     if (!data)
0170         return -EINVAL;
0171 
0172     /* in case this is specified by DT */
0173     data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id,
0174                     data, &ti_of_thermal_ops);
0175     if (IS_ERR(data->ti_thermal)) {
0176         dev_err(bgp->dev, "thermal zone device is NULL\n");
0177         return PTR_ERR(data->ti_thermal);
0178     }
0179 
0180     interval = jiffies_to_msecs(data->ti_thermal->polling_delay_jiffies);
0181 
0182     ti_bandgap_set_sensor_data(bgp, id, data);
0183     ti_bandgap_write_update_interval(bgp, data->sensor_id, interval);
0184 
0185     if (devm_thermal_add_hwmon_sysfs(data->ti_thermal))
0186         dev_warn(bgp->dev, "failed to add hwmon sysfs attributes\n");
0187 
0188     return 0;
0189 }
0190 
0191 int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
0192 {
0193     struct ti_thermal_data *data;
0194 
0195     data = ti_bandgap_get_sensor_data(bgp, id);
0196 
0197     if (!IS_ERR_OR_NULL(data) && data->ti_thermal) {
0198         if (data->our_zone)
0199             thermal_zone_device_unregister(data->ti_thermal);
0200     }
0201 
0202     return 0;
0203 }
0204 
0205 int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
0206 {
0207     struct ti_thermal_data *data;
0208 
0209     data = ti_bandgap_get_sensor_data(bgp, id);
0210 
0211     schedule_work(&data->thermal_wq);
0212 
0213     return 0;
0214 }
0215 
0216 int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
0217 {
0218     struct ti_thermal_data *data;
0219     struct device_node *np = bgp->dev->of_node;
0220 
0221     /*
0222      * We are assuming here that if one deploys the zone
0223      * using DT, then it must be aware that the cooling device
0224      * loading has to happen via cpufreq driver.
0225      */
0226     if (of_find_property(np, "#thermal-sensor-cells", NULL))
0227         return 0;
0228 
0229     data = ti_bandgap_get_sensor_data(bgp, id);
0230     if (!data || IS_ERR(data))
0231         data = ti_thermal_build_data(bgp, id);
0232 
0233     if (!data)
0234         return -EINVAL;
0235 
0236     data->policy = cpufreq_cpu_get(0);
0237     if (!data->policy) {
0238         pr_debug("%s: CPUFreq policy not found\n", __func__);
0239         return -EPROBE_DEFER;
0240     }
0241 
0242     /* Register cooling device */
0243     data->cool_dev = cpufreq_cooling_register(data->policy);
0244     if (IS_ERR(data->cool_dev)) {
0245         int ret = PTR_ERR(data->cool_dev);
0246         dev_err(bgp->dev, "Failed to register cpu cooling device %d\n",
0247             ret);
0248         cpufreq_cpu_put(data->policy);
0249 
0250         return ret;
0251     }
0252     ti_bandgap_set_sensor_data(bgp, id, data);
0253 
0254     return 0;
0255 }
0256 
0257 int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
0258 {
0259     struct ti_thermal_data *data;
0260 
0261     data = ti_bandgap_get_sensor_data(bgp, id);
0262 
0263     if (!IS_ERR_OR_NULL(data)) {
0264         cpufreq_cooling_unregister(data->cool_dev);
0265         if (data->policy)
0266             cpufreq_cpu_put(data->policy);
0267     }
0268 
0269     return 0;
0270 }