thermal: Add palmas thermal support
[linux-2.6.git] / drivers / thermal / generic_adc_thermal.c
1 /*
2  * drivers/staging/iio/generic_adc_thermal.c
3  *
4  * Generic ADC thermal driver
5  *
6  * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
7  *
8  * Author: Jinyoung Park <jinyoungp@nvidia.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation version 2.
13  *
14  * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
15  * whether express or implied; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22  * 02111-1307, USA
23  */
24 #include <linux/kernel.h>
25 #include <linux/platform_device.h>
26 #include <linux/slab.h>
27 #include <linux/uaccess.h>
28 #include <linux/module.h>
29 #include <linux/debugfs.h>
30 #include <linux/seq_file.h>
31 #include <linux/err.h>
32 #include <linux/thermal.h>
33 #include <linux/iio/consumer.h>
34 #include <linux/iio/types.h>
35 #include <linux/generic_adc_thermal.h>
36
37 struct gadc_thermal_driver_data {
38         struct device *dev;
39         struct thermal_zone_device *tz;
40         struct iio_channel *channel;
41         struct gadc_thermal_platform_data *pdata;
42         struct dentry *dentry;
43 };
44
45 static int gadc_thermal_get_temp(struct thermal_zone_device *tz,
46                                  unsigned long *temp)
47 {
48         struct gadc_thermal_driver_data *drvdata = tz->devdata;
49         struct gadc_thermal_platform_data *pdata = drvdata->pdata;
50         int val, val2 = 0;
51         int ret;
52
53         ret = iio_st_read_channel_raw(drvdata->channel, &val, &val2);
54         if (ret < 0) {
55                 dev_err(drvdata->dev, "%s: Failed to read channel, %d\n",
56                         __func__, ret);
57                 return ret;
58         }
59
60         if (pdata->adc_to_temp)
61                 *temp = pdata->adc_to_temp(pdata, &val, &val2);
62         else
63                 *temp = val;
64
65         *temp += pdata->temp_offset;
66         return 0;
67 }
68
69 static struct thermal_zone_device_ops gadc_thermal_ops = {
70         .get_temp = gadc_thermal_get_temp,
71 };
72
73 #ifdef CONFIG_DEBUG_FS
74 static int adc_temp_show(struct seq_file *s, void *p)
75 {
76         struct gadc_thermal_driver_data *drvdata = s->private;
77         struct gadc_thermal_platform_data *pdata = drvdata->pdata;
78         int val, val2 = 0, temp;
79         int ret;
80
81         ret = iio_st_read_channel_raw(drvdata->channel, &val, &val2);
82         if (ret < 0) {
83                 dev_err(drvdata->dev, "%s: Failed to read channel, %d\n",
84                         __func__, ret);
85                 return ret;
86         }
87
88         if (pdata->adc_to_temp)
89                 temp = pdata->adc_to_temp(pdata, &val, &val2);
90         else
91                 temp = val;
92
93         temp += pdata->temp_offset;
94         seq_printf(s, "%d %d %d\n", val, val2, temp);
95         return 0;
96 }
97
98 static int adc_temp_open(struct inode *inode, struct file *file)
99 {
100         return single_open(file, adc_temp_show, inode->i_private);
101 }
102
103 static const struct file_operations adc_temp_fops = {
104         .open           = adc_temp_open,
105         .read           = seq_read,
106         .llseek         = seq_lseek,
107         .release        = single_release,
108 };
109
110 static int temp_offset_write(struct file *file, const char __user *user_buf,
111                              size_t count, loff_t *ppos)
112 {
113         struct gadc_thermal_driver_data *drvdata =
114                         ((struct seq_file *)(file->private_data))->private;
115         char buf[32];
116         ssize_t buf_size;
117         char *start = buf;
118         long val;
119
120         buf_size = min(count, (sizeof(buf)-1));
121         if (copy_from_user(buf, user_buf, buf_size)) {
122                 dev_err(drvdata->dev, "%s: Failed to copy from user\n",
123                         __func__);
124                 return -EFAULT;
125         }
126         buf[buf_size] = 0;
127
128         while (*start == ' ')
129                 start++;
130
131         if (kstrtol(start, 10, &val))
132                 return -EINVAL;
133
134         drvdata->pdata->temp_offset = val;
135         return buf_size;
136 }
137
138 static int temp_offset_show(struct seq_file *s, void *p)
139 {
140         struct gadc_thermal_driver_data *drvdata = s->private;
141
142         seq_printf(s, "%d\n", drvdata->pdata->temp_offset);
143         return 0;
144 }
145
146 static int temp_offset_open(struct inode *inode, struct file *file)
147 {
148         return single_open(file, temp_offset_show, inode->i_private);
149 }
150
151 static const struct file_operations temp_offset_fops = {
152         .open           = temp_offset_open,
153         .write          = temp_offset_write,
154         .read           = seq_read,
155         .llseek         = seq_lseek,
156         .release        = single_release,
157 };
158
159 static int gadc_thermal_debugfs_init(struct gadc_thermal_driver_data *drvdata)
160 {
161         struct dentry *d_file;
162
163         drvdata->dentry = debugfs_create_dir(dev_name(drvdata->dev), NULL);
164         if (!drvdata->dentry)
165                 return -ENOMEM;
166
167         d_file = debugfs_create_file("adc_temp", 0444, drvdata->dentry,
168                                      drvdata, &adc_temp_fops);
169         if (!d_file)
170                 goto error;
171
172         d_file = debugfs_create_file("temp_offset", 0644, drvdata->dentry,
173                                      drvdata, &temp_offset_fops);
174         if (!d_file)
175                 goto error;
176
177         return 0;
178
179 error:
180         debugfs_remove_recursive(drvdata->dentry);
181         return -ENOMEM;
182 }
183 #endif /*  CONFIG_DEBUG_FS */
184
185 static int __devinit gadc_thermal_probe(struct platform_device *pdev)
186 {
187         struct gadc_thermal_platform_data *pdata = dev_get_platdata(&pdev->dev);
188         struct gadc_thermal_driver_data *drvdata;
189         int ret;
190
191         if (!pdata) {
192                 dev_err(&pdev->dev, "%s: No platform data\n", __func__);
193                 return -ENODEV;
194         }
195
196         drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
197         if (!drvdata) {
198                 dev_err(&pdev->dev,
199                         "%s: Failed to alloc memory for driver data\n",
200                         __func__);
201                 return -ENOMEM;
202         }
203
204         platform_set_drvdata(pdev, drvdata);
205         drvdata->dev = &pdev->dev;
206         drvdata->pdata = pdata;
207
208         drvdata->channel = iio_st_channel_get(dev_name(&pdev->dev),
209                                         pdata->iio_channel_name);
210         if (IS_ERR(drvdata->channel)) {
211                 dev_err(&pdev->dev, "%s: Failed to get channel %s, %ld\n",
212                         __func__, pdata->iio_channel_name,
213                         PTR_ERR(drvdata->channel));
214                 return PTR_ERR(drvdata->channel);
215         }
216
217         drvdata->tz = thermal_zone_device_register(pdata->tz_name, 0, 0,
218                                         drvdata, &gadc_thermal_ops, NULL, 0, 0);
219         if (IS_ERR(drvdata->tz)) {
220                 dev_err(&pdev->dev,
221                         "%s: Failed to register thermal zone %s, %ld\n",
222                         __func__, pdata->tz_name, PTR_ERR(drvdata->tz));
223                 ret = PTR_ERR(drvdata->tz);
224                 goto error_release_channel;
225         }
226
227         gadc_thermal_debugfs_init(drvdata);
228
229         return 0;
230
231 error_release_channel:
232         iio_st_channel_release(drvdata->channel);
233         return ret;
234 }
235
236 static int __devexit gadc_thermal_remove(struct platform_device *pdev)
237 {
238         struct gadc_thermal_driver_data *drvdata = platform_get_drvdata(pdev);
239
240         if (drvdata->dentry)
241                 debugfs_remove_recursive(drvdata->dentry);
242         thermal_zone_device_unregister(drvdata->tz);
243         iio_st_channel_release(drvdata->channel);
244         return 0;
245 }
246
247 static struct platform_driver gadc_thermal_driver = {
248         .driver = {
249                 .name = "generic-adc-thermal",
250                 .owner = THIS_MODULE,
251         },
252         .probe = gadc_thermal_probe,
253         .remove = __devexit_p(gadc_thermal_remove),
254 };
255
256 static int gadc_thermal_init(void)
257 {
258         return platform_driver_register(&gadc_thermal_driver);
259 }
260 late_initcall(gadc_thermal_init);
261
262 static void gadc_thermal_exit(void)
263 {
264         platform_driver_unregister(&gadc_thermal_driver);
265 }
266 module_exit(gadc_thermal_exit);
267
268 MODULE_AUTHOR("Jinyoung Park <jinyoungp@nvidia.com>");
269 MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework");
270 MODULE_LICENSE("GPL v2");