blob: 26ac0483531db78d4996835aaf62eacaabebc88f [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/err.h>
17#include <linux/thermal.h>
18#include <linux/slab.h>
19#include <linux/io.h>
20#include <mach/msm_memtypes.h>
21
22#define POP_MEM_LPDDR1_REFRESH_MASK 0x00000700
23#define POP_MEM_LPDDR1_REFRESH_SHIFT 0x8
24
25#define POP_MEM_LPDDR2_REFRESH_MASK 0x00000007
26#define POP_MEM_LPDDR2_REFRESH_SHIFT 0x0
27
28#define POP_MEM_REFRESH_REG 0x3C
29
30#define POP_MEM_LOW_TEMPERATURE 25000
31#define POP_MEM_NORMAL_TEMPERATURE 50000
32#define POP_MEM_HIGH_TEMPERATURE 85000
33
34#define POP_MEM_TRIP_OUT_OF_SPEC 0
35#define POP_MEM_TRIP_NUM 1
36
37struct pop_mem_tm_device {
38 unsigned long baseaddr;
39 struct thermal_zone_device *tz_dev;
40 unsigned long refresh_mask;
41 unsigned int refresh_shift;
42};
43
44
45static int pop_mem_tm_read_refresh(struct pop_mem_tm_device *tm,
46 unsigned int *ref_rate){
47 unsigned int ref;
48
49 ref = __raw_readl(tm->baseaddr + POP_MEM_REFRESH_REG);
50 *ref_rate = (ref & tm->refresh_mask) >> tm->refresh_shift;
51
52 return 0;
53}
54
55
56static int pop_mem_tm_get_temperature(struct thermal_zone_device *thermal,
57 unsigned long *temperature)
58{
59 struct pop_mem_tm_device *tm = thermal->devdata;
60 unsigned int ref_rate;
61 int rc;
62
63 if (!tm || !temperature)
64 return -EINVAL;
65
66 rc = pop_mem_tm_read_refresh(tm, &ref_rate);
67 if (rc < 0)
68 return rc;
69
70 switch (ref_rate) {
71 case 0:
72 case 1:
73 case 2:
74 *temperature = POP_MEM_LOW_TEMPERATURE;
75 break;
76 case 3:
77 case 4:
78 *temperature = POP_MEM_NORMAL_TEMPERATURE;
79 break;
80 case 5:
81 case 6:
82 case 7:
83 *temperature = POP_MEM_HIGH_TEMPERATURE;
84 break;
85 default:
86 return -EINVAL;
87 }
88
89 return 0;
90}
91
92static int pop_mem_tm_get_trip_type(struct thermal_zone_device *thermal,
93 int trip, enum thermal_trip_type *type)
94{
95 struct pop_mem_tm_device *tm = thermal->devdata;
96
97 if (!tm || trip < 0 || !type)
98 return -EINVAL;
99
100 if (trip == POP_MEM_TRIP_OUT_OF_SPEC)
101 *type = THERMAL_TRIP_CRITICAL;
102 else
103 return -EINVAL;
104
105 return 0;
106}
107
108static int pop_mem_tm_get_trip_temperature(struct thermal_zone_device *thermal,
109 int trip, unsigned long *temperature)
110{
111 struct pop_mem_tm_device *tm = thermal->devdata;
112
113 if (!tm || trip < 0 || !temperature)
114 return -EINVAL;
115
116 if (trip == POP_MEM_TRIP_OUT_OF_SPEC)
117 *temperature = POP_MEM_HIGH_TEMPERATURE;
118 else
119 return -EINVAL;
120
121 return 0;
122}
123
124
125static int pop_mem_tm_get_crit_temperature(struct thermal_zone_device *thermal,
126 unsigned long *temperature)
127{
128 struct pop_mem_tm_device *tm = thermal->devdata;
129
130 if (!tm || !temperature)
131 return -EINVAL;
132
133 *temperature = POP_MEM_HIGH_TEMPERATURE;
134
135 return 0;
136}
137
138
139static struct thermal_zone_device_ops pop_mem_thermal_zone_ops = {
140 .get_temp = pop_mem_tm_get_temperature,
141 .get_trip_type = pop_mem_tm_get_trip_type,
142 .get_trip_temp = pop_mem_tm_get_trip_temperature,
143 .get_crit_temp = pop_mem_tm_get_crit_temperature,
144};
145
146
147static int __devinit pop_mem_tm_probe(struct platform_device *pdev)
148{
149 int rc, len, numcontrollers;
150 struct resource *controller_mem = NULL;
151 struct resource *res_mem = NULL;
152 struct pop_mem_tm_device *tmdev = NULL;
153 void __iomem *base = NULL;
154
155 rc = len = 0;
156 numcontrollers = get_num_populated_chipselects();
157
158 if (pdev->id >= numcontrollers) {
159 pr_err("%s: memory controller %d does not exist", __func__,
160 pdev->id);
161 rc = -ENODEV;
162 goto fail;
163 }
164
165 controller_mem = platform_get_resource_byname(pdev,
166 IORESOURCE_MEM, "physbase");
167 if (!controller_mem) {
168 pr_err("%s: could not get resources for controller %d",
169 __func__, pdev->id);
170 rc = -EFAULT;
171 goto fail;
172 }
173
174 len = controller_mem->end - controller_mem->start + 1;
175
176 res_mem = request_mem_region(controller_mem->start, len,
177 controller_mem->name);
178 if (!res_mem) {
179 pr_err("%s: Could not request memory region: "
180 "start=%p, len=%d\n", __func__,
181 (void *) controller_mem->start, len);
182 rc = -EBUSY;
183 goto fail;
184
185 }
186
187 base = ioremap(res_mem->start, len);
188 if (!base) {
189 pr_err("%s: Could not ioremap: start=%p, len=%d\n",
190 __func__, (void *) controller_mem->start, len);
191 rc = -EBUSY;
192 goto fail;
193
194 }
195
196 tmdev = kzalloc(sizeof(*tmdev), GFP_KERNEL);
197 if (tmdev == NULL) {
198 pr_err("%s: kzalloc() failed.\n", __func__);
199 rc = -ENOMEM;
200 goto fail;
201 }
202
203 if (numcontrollers == 1) {
204 tmdev->refresh_mask = POP_MEM_LPDDR1_REFRESH_MASK;
205 tmdev->refresh_shift = POP_MEM_LPDDR1_REFRESH_SHIFT;
206 } else {
207 tmdev->refresh_mask = POP_MEM_LPDDR2_REFRESH_MASK;
208 tmdev->refresh_shift = POP_MEM_LPDDR2_REFRESH_SHIFT;
209 }
210 tmdev->baseaddr = (unsigned long) base;
211 tmdev->tz_dev = thermal_zone_device_register("msm_popmem_tz",
212 POP_MEM_TRIP_NUM, tmdev,
213 &pop_mem_thermal_zone_ops,
214 0, 0, 0, 0);
215
216 if (tmdev->tz_dev == NULL) {
217 pr_err("%s: thermal_zone_device_register() failed.\n",
218 __func__);
219 goto fail;
220 }
221
222 platform_set_drvdata(pdev, tmdev);
223
224 pr_notice("%s: device %d probed successfully\n", __func__, pdev->id);
225
226 return rc;
227
228fail:
229 if (base)
230 iounmap(base);
231 if (res_mem)
232 release_mem_region(controller_mem->start, len);
233 kfree(tmdev);
234
235 return rc;
236}
237
238static int __devexit pop_mem_tm_remove(struct platform_device *pdev)
239{
240
241 int len;
242 struct pop_mem_tm_device *tmdev = platform_get_drvdata(pdev);
243 struct resource *controller_mem;
244
245 iounmap((void __iomem *)tmdev->baseaddr);
246
247 controller_mem = platform_get_resource_byname(pdev,
248 IORESOURCE_MEM, "physbase");
249 len = controller_mem->end - controller_mem->start + 1;
250 release_mem_region(controller_mem->start, len);
251
252 thermal_zone_device_unregister(tmdev->tz_dev);
253 platform_set_drvdata(pdev, NULL);
254 kfree(tmdev);
255
256 return 0;
257}
258
259static struct platform_driver pop_mem_tm_driver = {
260 .probe = pop_mem_tm_probe,
261 .remove = pop_mem_tm_remove,
262 .driver = {
263 .name = "msm_popmem-tm",
264 .owner = THIS_MODULE
265 },
266};
267
268static int __init pop_mem_tm_init(void)
269{
270 return platform_driver_register(&pop_mem_tm_driver);
271}
272
273static void __exit pop_mem_tm_exit(void)
274{
275 platform_driver_unregister(&pop_mem_tm_driver);
276}
277
278module_init(pop_mem_tm_init);
279module_exit(pop_mem_tm_exit);
280
281MODULE_LICENSE("GPL v2");
282MODULE_DESCRIPTION("Pop memory thermal manager driver");
283MODULE_VERSION("1.0");
284MODULE_ALIAS("platform:popmem-tm");