pandora: defconfig: update
[pandora-kernel.git] / drivers / thermal / omap3_thermal.c
1 /*
2  * OMAP3/DM3730 band gap thermal driver.
3  *
4  * Copyright (C) 2014 Grazvydas Ignotas
5  * based on SPEAr Thermal Sensor driver (spear_thermal.c)
6  * Copyright (C) 2011-2012 ST Microelectronics
7  *
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.
11  *
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.
16  *
17  */
18
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/device.h>
22 #include <linux/io.h>
23 #include <linux/platform_device.h>
24 #include <linux/delay.h>
25 #include <linux/thermal.h>
26 #include <linux/pm_runtime.h>
27 #include <plat/cpu.h>
28
29 #define ADC_CODE_MASK 0x7f
30
31 struct omap3_thermal_dev {
32         struct device *dev;
33         void __iomem *thermal_base;
34         const int *adc_to_temp;
35         u32 bgap_soc_mask;
36         u32 bgap_eocz_mask;
37 };
38
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
56 };
57
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
75 };
76
77 static int omap3_thermal_get_temp(struct thermal_zone_device *thermal,
78                                   unsigned long *temp)
79 {
80         struct omap3_thermal_dev *tdev = thermal->devdata;
81         int timeout;
82         u32 val;
83         int ret;
84
85         ret = pm_runtime_get_sync(tdev->dev);
86         if (ret < 0) {
87                 dev_err(tdev->dev, "pm_runtime_get_sync failed: %d\n", ret);
88                 return ret;
89         }
90
91         val = readl(tdev->thermal_base);
92         val |= tdev->bgap_soc_mask; /* start of conversion */
93
94         writel(val, tdev->thermal_base);
95         usleep_range(428, 1000); /* at least 14 32k cycles */
96
97         val &= ~tdev->bgap_soc_mask;
98         writel(val, tdev->thermal_base);
99
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))
104                         break;
105                 cpu_relax();
106         }
107
108         pm_runtime_mark_last_busy(tdev->dev);
109         ret = pm_runtime_put_autosuspend(tdev->dev);
110
111         if (timeout == 0)
112                 dev_err(tdev->dev, "timeout waiting for eocz\n");
113
114         *temp = tdev->adc_to_temp[val & ADC_CODE_MASK];
115         return 0;
116 }
117
118 static const struct thermal_zone_device_ops omap3_thermal_ops = {
119         .get_temp = omap3_thermal_get_temp,
120 };
121
122 static int omap3_thermal_probe(struct platform_device *pdev)
123 {
124         struct thermal_zone_device *omap3_thermal = NULL;
125         struct omap3_thermal_dev *tdev;
126         int ret = 0;
127         struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
128
129         if (!stres) {
130                 dev_err(&pdev->dev, "memory resource missing\n");
131                 return -ENODEV;
132         }
133
134         tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
135         if (!tdev)
136                 return -ENOMEM;
137
138         tdev->dev = &pdev->dev;
139
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;
148         } else {
149                 dev_err(&pdev->dev, "not OMAP3 family\n");
150                 return -ENODEV;
151         }
152
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");
157                 return -ENOMEM;
158         }
159
160         pm_runtime_enable(&pdev->dev);
161         pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
162         pm_runtime_use_autosuspend(&pdev->dev);
163
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");
168                 ret = -EINVAL;
169                 goto put_pm;
170         }
171
172         platform_set_drvdata(pdev, omap3_thermal);
173
174         return 0;
175
176 put_pm:
177         pm_runtime_disable(&pdev->dev);
178         return ret;
179 }
180
181 static int omap3_thermal_exit(struct platform_device *pdev)
182 {
183         struct thermal_zone_device *omap3_thermal = platform_get_drvdata(pdev);
184
185         thermal_zone_device_unregister(omap3_thermal);
186         platform_set_drvdata(pdev, NULL);
187         pm_runtime_disable(&pdev->dev);
188
189         return 0;
190 }
191
192 static struct platform_driver omap3_thermal_driver = {
193         .probe = omap3_thermal_probe,
194         .remove = omap3_thermal_exit,
195         .driver = {
196                 .name = "omap3-thermal",
197                 .owner = THIS_MODULE,
198         },
199 };
200
201 module_platform_driver(omap3_thermal_driver);
202
203 MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
204 MODULE_DESCRIPTION("OMAP3/DM3730 thermal driver");
205 MODULE_LICENSE("GPL");