ARM: tegra: powermon: Fix copyrights from GPLv3 to GPLv2
[linux-3.10.git] / arch / arm / mach-tegra / tegra_bbc_thermal.c
1 /*
2  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 #define pr_fmt(fmt) "%s: " fmt, __func__
18
19 #include <linux/slab.h>
20 #include <linux/err.h>
21 #include <linux/printk.h>
22 #include <linux/thermal.h>
23 #include <linux/nvshm_stats.h>
24
25 struct bbc_thermal_private {
26         u32 disabled_safe;
27         const u32 *enabled_ptr;
28         struct thermal_zone_device **tzds;
29         int tz_no;
30         struct notifier_block nb;
31 };
32
33 static struct bbc_thermal_private private;
34
35 static int bbc_get_temp(struct thermal_zone_device *tzd, unsigned long *t)
36 {
37         const u32 *temp = (const u32 *) tzd->devdata;
38
39         /* Check that we thermal is enabled and temperature has been updated */
40         if (!*private.enabled_ptr || (*temp > 300))
41                 return -ENODATA;
42
43         /* °C to m°C */
44         *t = *temp * 1000;
45         return 0;
46 }
47
48 static const struct thermal_zone_device_ops bbc_thermal_ops = {
49         .get_temp = bbc_get_temp,
50 };
51
52 static void bbc_thermal_remove(void)
53 {
54         int i;
55
56         private.enabled_ptr = &private.disabled_safe;
57         if (!private.tzds)
58                 return;
59
60         for (i = 0; i < private.tz_no; i++)
61                 thermal_zone_device_unregister(private.tzds[i]);
62
63         kfree(private.tzds);
64         private.tzds = NULL;
65 }
66
67 static int bbc_thermal_install(void)
68 {
69         struct nvshm_stats_iter it;
70         unsigned int index;
71         const u32 *enabled_ptr;
72         int rc = 0;
73
74         if (private.tzds) {
75                 pr_warn("BBC thermal already registered, unregistering\n");
76                 bbc_thermal_remove();
77         }
78
79         /* Get iterator for top structure */
80         enabled_ptr = nvshm_stats_top("DrvTemperatureSysStats", &it);
81         if (IS_ERR(enabled_ptr)) {
82                 pr_err("BBC thermal zones missing");
83                 return PTR_ERR(enabled_ptr);
84         }
85
86         private.enabled_ptr = enabled_ptr;
87         /* Look for array of sensor data structures */
88         while (nvshm_stats_type(&it) != NVSHM_STATS_END) {
89                 if (!strcmp(nvshm_stats_name(&it), "sensorStats"))
90                         break;
91
92                 nvshm_stats_next(&it);
93         }
94
95         if (nvshm_stats_type(&it) != NVSHM_STATS_SUB) {
96                 pr_err("sensorStats not found or incorrect type: %d",
97                        nvshm_stats_type(&it));
98                 return -EINVAL;
99         }
100
101         /* Parse sensors */
102         private.tz_no = nvshm_stats_elems(&it);
103         pr_info("BBC can report temperatures from %d thermal zones",
104                 private.tz_no);
105         private.tzds = kmalloc(private.tz_no * sizeof(*private.tzds),
106                                GFP_KERNEL);
107         if (!private.tzds) {
108                 pr_err("failed to allocate array of sensors\n");
109                 return -ENOMEM;
110         }
111
112         for (index = 0; index < private.tz_no; index++) {
113                 struct nvshm_stats_iter sub_it;
114                 char name[16];
115
116                 /* Get iterator to sensor data structure */
117                 nvshm_stats_sub(&it, index, &sub_it);
118                 /* We only care about temperature */
119                 while (nvshm_stats_type(&sub_it) != NVSHM_STATS_END) {
120                         if (!strcmp(nvshm_stats_name(&sub_it), "tempCelcius"))
121                                 break;
122
123                         nvshm_stats_next(&sub_it);
124                 }
125
126                 /* This will either fail at first time or not at all */
127                 if (nvshm_stats_type(&sub_it) != NVSHM_STATS_UINT32) {
128                         pr_err("tempCelcius not found or incorrect type: %d",
129                                nvshm_stats_type(&sub_it));
130                         kfree(private.tzds);
131                         private.tzds = NULL;
132                         return -EINVAL;
133                 }
134
135                 /* Ok we got it, let's register a new thermal zone */
136                 sprintf(name, "BBC-therm%d", index);
137                 private.tzds[index] = thermal_zone_device_register(name, 0, 0,
138                         (void *) nvshm_stats_valueptr_uint32(&sub_it, 0),
139                         &bbc_thermal_ops, NULL, 0, 0);
140                 if (IS_ERR(private.tzds)) {
141                         pr_err("failed to register thermal zone #%d, abort\n",
142                                index);
143                         rc = PTR_ERR(private.tzds);
144                         break;
145                 }
146         }
147
148         if (rc)
149                 bbc_thermal_remove();
150
151         return rc;
152 }
153
154 static int bbc_thermal_notify(struct notifier_block *self,
155                                unsigned long action,
156                                void *user)
157 {
158         switch (action) {
159         case NVSHM_STATS_MODEM_UP:
160                 bbc_thermal_install();
161                 break;
162         case NVSHM_STATS_MODEM_DOWN:
163                 bbc_thermal_remove();
164                 break;
165         }
166         return NOTIFY_OK;
167 }
168
169 void tegra_bbc_thermal_init(void)
170 {
171         private.enabled_ptr = &private.disabled_safe;
172         private.nb.notifier_call = bbc_thermal_notify;
173         nvshm_stats_register(&private.nb);
174 }