0001
0002
0003
0004
0005
0006
0007
0008
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
0025
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
0044
0045
0046
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
0061 static int db8500_thermal_get_temp(void *data, int *temp)
0062 {
0063 struct db8500_thermal_zone *th = data;
0064
0065
0066
0067
0068
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
0091
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
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
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
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
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
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");