2 * OMAP3/DM3730 band gap thermal driver.
4 * Copyright (C) 2014 Grazvydas Ignotas
5 * based on SPEAr Thermal Sensor driver (spear_thermal.c)
6 * Copyright (C) 2011-2012 ST Microelectronics
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/device.h>
23 #include <linux/platform_device.h>
24 #include <linux/delay.h>
25 #include <linux/thermal.h>
26 #include <linux/pm_runtime.h>
29 #define ADC_CODE_MASK 0x7f
31 struct omap3_thermal_dev {
33 void __iomem *thermal_base;
34 const int *adc_to_temp;
39 static const int omap3630_adc_to_temp[128] = {
40 -40000, -40000, -40000, -40000, -40000, -40000, -40000, -40000, // 7
41 -40000, -40000, -40000, -40000, -40000, -39000, -36500, -34500, // 15
42 -33000, -31000, -29000, -27000, -25000, -23000, -21000, -19250, // 23
43 -17750, -16000, -14250, -12750, -11000, -9000, -7250, -5750, // 31
44 -4250, -2500, -750, 1000, 2750, 4250, 5750, 7500, // 39
45 9250, 11000, 12750, 14250, 16000, 18000, 20000, 22000, // 47
46 24000, 26000, 27750, 29250, 31000, 32750, 34250, 36000, // 55
47 37750, 39250, 41000, 42750, 44250, 46000, 47750, 49250, // 63
48 51000, 52750, 54250, 560, 57750, 59250, 61000, 63000, // 71
49 65000, 67000, 69000, 70750, 72500, 74250, 76000, 77750, // 79
50 79250, 81000, 82750, 84250, 86000, 87750, 89250, 91000, // 87
51 92750, 94250, 96000, 97750, 99250, 101000, 102750, 104250, // 95
52 106000, 108000, 110000, 112000, 114000, 116000, 117750, 119250, // 103
53 121000, 122750, 124025, 125000, 125000, 125000, 125000, 125000, // 111
54 125000, 125000, 125000, 125000, 125000, 125000, 125000, 125000, // 119
55 125000, 125000, 125000, 125000, 125000, 125000, 125000, 125000 // 127
58 static const int omap3530_adc_to_temp[128] = {
59 -40000, -40000, -40000, -40000, -40000, -39500, -38200, -36800, // 7
60 -34700, -32500, -31100, -29700, -28200, -26800, -25400, -24000, // 15
61 -22600, -21200, -19800, -18400, -17000, -15600, -14100, -12700, // 23
62 -11300, -9900, -8500, -7100, -5700, -4250, -2800, -1400, // 31
63 50, 1550, 3000, 4400, 5850, 7300, 8700, 10100, // 39
64 11550, 13000, 14400, 15800, 17200, 18850, 20100, 21500, // 47
65 22900, 24350, 25800, 27200, 28600, 30000, 31400, 32800, // 55
66 34200, 35650, 37100, 38500, 39900, 41300, 42700, 44150, // 63
67 45600, 47000, 48400, 49800, 51300, 52600, 53950, 55300, // 71
68 56700, 58100, 59500, 60900, 62300, 63700, 70050, 66400, // 79
69 67800, 69200, 70600, 72000, 73400, 74800, 76200, 77600, // 87
70 79000, 80400, 81700, 83050, 84500, 85850, 87200, 88600, // 95
71 89950, 91300, 92700, 94050, 95400, 96800, 98200, 99550, // 103
72 100900, 102300, 103650, 105000, 106400, 107800, 109150, 110500, // 111
73 111900, 113300, 114650, 116000, 117400, 118750, 120100, 121500, // 119
74 122850, 124200, 124950, 125000, 125000, 125000, 125000, 125000 // 127
77 static int omap3_thermal_get_temp(struct thermal_zone_device *thermal,
80 struct omap3_thermal_dev *tdev = thermal->devdata;
85 ret = pm_runtime_get_sync(tdev->dev);
87 dev_err(tdev->dev, "pm_runtime_get_sync failed: %d\n", ret);
91 val = readl(tdev->thermal_base);
92 val |= tdev->bgap_soc_mask; /* start of conversion */
94 writel(val, tdev->thermal_base);
95 usleep_range(428, 1000); /* at least 14 32k cycles */
97 val &= ~tdev->bgap_soc_mask;
98 writel(val, tdev->thermal_base);
100 usleep_range(1221, 2000); /* at least 36+4 32k cycles */
101 for (timeout = 1000; timeout > 0; timeout--) {
102 val = readl(tdev->thermal_base);
103 if (!(val & tdev->bgap_eocz_mask))
108 pm_runtime_mark_last_busy(tdev->dev);
109 ret = pm_runtime_put_autosuspend(tdev->dev);
112 dev_err(tdev->dev, "timeout waiting for eocz\n");
114 *temp = tdev->adc_to_temp[val & ADC_CODE_MASK];
118 static const struct thermal_zone_device_ops omap3_thermal_ops = {
119 .get_temp = omap3_thermal_get_temp,
122 static int omap3_thermal_probe(struct platform_device *pdev)
124 struct thermal_zone_device *omap3_thermal = NULL;
125 struct omap3_thermal_dev *tdev;
127 struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
130 dev_err(&pdev->dev, "memory resource missing\n");
134 tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
138 tdev->dev = &pdev->dev;
140 if (cpu_is_omap3630()) {
141 tdev->bgap_soc_mask = BIT(9);
142 tdev->bgap_eocz_mask = BIT(8);
143 tdev->adc_to_temp = omap3630_adc_to_temp;
144 } else if (cpu_is_omap34xx()) {
145 tdev->bgap_soc_mask = BIT(8);
146 tdev->bgap_eocz_mask = BIT(7);
147 tdev->adc_to_temp = omap3530_adc_to_temp;
149 dev_err(&pdev->dev, "not OMAP3 family\n");
153 tdev->thermal_base = devm_ioremap(&pdev->dev, stres->start,
154 resource_size(stres));
155 if (!tdev->thermal_base) {
156 dev_err(&pdev->dev, "ioremap failed\n");
160 pm_runtime_enable(&pdev->dev);
161 pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
162 pm_runtime_use_autosuspend(&pdev->dev);
164 omap3_thermal = thermal_zone_device_register("omap3-thermal", 0,
165 tdev, &omap3_thermal_ops, 0, 0, 0, 0);
166 if (!omap3_thermal) {
167 dev_err(&pdev->dev, "thermal zone device is NULL\n");
172 platform_set_drvdata(pdev, omap3_thermal);
177 pm_runtime_disable(&pdev->dev);
181 static int omap3_thermal_exit(struct platform_device *pdev)
183 struct thermal_zone_device *omap3_thermal = platform_get_drvdata(pdev);
185 thermal_zone_device_unregister(omap3_thermal);
186 platform_set_drvdata(pdev, NULL);
187 pm_runtime_disable(&pdev->dev);
192 static struct platform_driver omap3_thermal_driver = {
193 .probe = omap3_thermal_probe,
194 .remove = omap3_thermal_exit,
196 .name = "omap3-thermal",
197 .owner = THIS_MODULE,
201 module_platform_driver(omap3_thermal_driver);
203 MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
204 MODULE_DESCRIPTION("OMAP3/DM3730 thermal driver");
205 MODULE_LICENSE("GPL");