blob: ae1d6f2761ec77beb657601d7b8c32173c1df5d5 [file] [log] [blame]
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
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#include <mach/ocmem_priv.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/slab.h>
17#include <linux/mm.h>
18#include <linux/rbtree.h>
19#include <linux/genalloc.h>
20#include <linux/of.h>
21#include <linux/io.h>
22#include <linux/platform_device.h>
23#include <linux/debugfs.h>
24#include <linux/seq_file.h>
25
26struct ocmem_partition {
27 const char *name;
28 int id;
29 unsigned long p_start;
30 unsigned long p_size;
31 unsigned long p_min;
Naveen Ramaraj135cd672012-04-23 12:13:28 -070032 unsigned int p_tail;
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -070033};
34
35struct ocmem_plat_data {
36 void __iomem *vbase;
37 unsigned long size;
38 unsigned long base;
39 struct ocmem_partition *parts;
Naveen Ramaraje653c2b2012-05-30 12:59:25 -070040 unsigned nr_parts;
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -070041};
42
43struct ocmem_zone zones[OCMEM_CLIENT_MAX];
44
45struct ocmem_zone *get_zone(unsigned id)
46{
47 if (id < OCMEM_GRAPHICS || id >= OCMEM_CLIENT_MAX)
48 return NULL;
49 else
50 return &zones[id];
51}
52
53static struct ocmem_plat_data *ocmem_pdata;
54
55#define CLIENT_NAME_MAX 10
56/* Must be in sync with enum ocmem_client */
57static const char *client_names[OCMEM_CLIENT_MAX] = {
58 "graphics",
59 "video",
60 "camera",
61 "hp_audio",
62 "voice",
63 "lp_audio",
64 "sensors",
65 "blast",
66};
67
68struct ocmem_quota_table {
69 const char *name;
70 int id;
71 unsigned long start;
72 unsigned long size;
73 unsigned long min;
Naveen Ramaraj135cd672012-04-23 12:13:28 -070074 unsigned int tail;
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -070075};
76
77/* This static table will go away with device tree support */
78static struct ocmem_quota_table qt[OCMEM_CLIENT_MAX] = {
Naveen Ramaraj135cd672012-04-23 12:13:28 -070079 /* name, id, start, size, min, tail */
80 { "graphics", OCMEM_GRAPHICS, 0x0, 0x100000, 0x80000, 0},
81 { "video", OCMEM_VIDEO, 0x100000, 0x80000, 0x55000, 1},
82 { "camera", OCMEM_CAMERA, 0x0, 0x0, 0x0, 0},
83 { "voice", OCMEM_VOICE, 0x0, 0x0, 0x0, 0 },
84 { "hp_audio", OCMEM_HP_AUDIO, 0x0, 0x0, 0x0, 0},
85 { "lp_audio", OCMEM_LP_AUDIO, 0x80000, 0xA0000, 0xA0000, 0},
86 { "blast", OCMEM_BLAST, 0x120000, 0x20000, 0x20000, 0},
87 { "sensors", OCMEM_SENSORS, 0x140000, 0x40000, 0x40000, 0},
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -070088};
89
90static inline int get_id(const char *name)
91{
92 int i = 0;
93 for (i = 0 ; i < OCMEM_CLIENT_MAX; i++) {
94 if (strncmp(name, client_names[i], CLIENT_NAME_MAX) == 0)
95 return i;
96 }
97 return -EINVAL;
98}
99
Naveen Ramarajb9da05782012-05-07 09:07:35 -0700100inline unsigned long phys_to_offset(unsigned long addr)
101{
102 if (!ocmem_pdata)
103 return 0;
104 if (addr < ocmem_pdata->base ||
105 addr > (ocmem_pdata->base + ocmem_pdata->size))
106 return 0;
107 return addr - ocmem_pdata->base;
108}
109
110inline unsigned long offset_to_phys(unsigned long offset)
111{
112 if (!ocmem_pdata)
113 return 0;
114 if (offset > ocmem_pdata->size)
115 return 0;
116 return offset + ocmem_pdata->base;
117}
118
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -0700119static struct ocmem_plat_data *parse_static_config(struct platform_device *pdev)
120{
121 struct ocmem_plat_data *pdata = NULL;
122 struct ocmem_partition *parts = NULL;
123 struct device *dev = &pdev->dev;
Naveen Ramaraje653c2b2012-05-30 12:59:25 -0700124 unsigned nr_parts = 0;
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -0700125 int i;
126 int j;
127
128 pdata = devm_kzalloc(dev, sizeof(struct ocmem_plat_data),
129 GFP_KERNEL);
130
131 if (!pdata) {
132 dev_err(dev, "Unable to allocate memory for"
133 " platform data\n");
134 return NULL;
135 }
136
137 for (i = 0 ; i < ARRAY_SIZE(qt); i++)
138 if (qt[i].size != 0x0)
139 nr_parts++;
140
141 if (nr_parts == 0x0) {
142 dev_err(dev, "No valid ocmem partitions\n");
143 return NULL;
144 } else
145 dev_info(dev, "Total partitions = %d\n", nr_parts);
146
147 parts = devm_kzalloc(dev, sizeof(struct ocmem_partition) * nr_parts,
148 GFP_KERNEL);
149
150 if (!parts) {
151 dev_err(dev, "Unable to allocate memory for"
152 " partition data\n");
153 return NULL;
154 }
155
156 for (i = 0, j = 0; i < ARRAY_SIZE(qt); i++) {
157 if (qt[i].size == 0x0) {
158 dev_dbg(dev, "Skipping creation of pool for %s\n",
159 qt[i].name);
160 continue;
161 }
162 parts[j].id = qt[i].id;
163 parts[j].p_size = qt[i].size;
164 parts[j].p_start = qt[i].start;
165 parts[j].p_min = qt[i].min;
Naveen Ramaraj135cd672012-04-23 12:13:28 -0700166 parts[j].p_tail = qt[i].tail;
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -0700167 j++;
168 }
169 BUG_ON(j != nr_parts);
170 pdata->nr_parts = nr_parts;
171 pdata->parts = parts;
172 pdata->base = OCMEM_PHYS_BASE;
173 pdata->size = OCMEM_PHYS_SIZE;
174 return pdata;
175}
176
177static struct ocmem_plat_data *parse_dt_config(struct platform_device *pdev)
178{
179 return NULL;
180}
181
182static int ocmem_zone_init(struct platform_device *pdev)
183{
184
185 int ret = -1;
186 int i = 0;
187 unsigned active_zones = 0;
188
189 struct ocmem_zone *zone = NULL;
190 struct ocmem_zone_ops *z_ops = NULL;
191 struct device *dev = &pdev->dev;
192 unsigned long start;
193 struct ocmem_plat_data *pdata = NULL;
194
195 pdata = platform_get_drvdata(pdev);
196
197 for (i = 0; i < pdata->nr_parts; i++) {
198 struct ocmem_partition *part = &pdata->parts[i];
199 zone = get_zone(part->id);
200
201 dev_dbg(dev, "Partition %d, start %lx, size %lx for %s\n",
202 i, part->p_start, part->p_size,
203 client_names[part->id]);
204
205 if (part->p_size > pdata->size) {
206 dev_alert(dev, "Quota > ocmem_size for id:%d\n",
207 part->id);
208 continue;
209 }
210
211 zone->z_pool = gen_pool_create(PAGE_SHIFT, -1);
212
213 if (!zone->z_pool) {
214 dev_alert(dev, "Creating pool failed for id:%d\n",
215 part->id);
216 return -EBUSY;
217 }
218
219 start = pdata->base + part->p_start;
220 ret = gen_pool_add(zone->z_pool, start,
221 part->p_size, -1);
222
223 if (ret < 0) {
224 gen_pool_destroy(zone->z_pool);
225 dev_alert(dev, "Unable to back pool %d with "
226 "buffer:%lx\n", part->id, part->p_size);
227 return -EBUSY;
228 }
229
230 /* Initialize zone allocators */
231 z_ops = devm_kzalloc(dev, sizeof(struct ocmem_zone_ops),
232 GFP_KERNEL);
233 if (!z_ops) {
234 pr_alert("ocmem: Unable to allocate memory for"
235 "zone ops:%d\n", i);
236 return -EBUSY;
237 }
238
239 /* Initialize zone parameters */
240 zone->z_start = start;
241 zone->z_head = zone->z_start;
242 zone->z_end = start + part->p_size;
243 zone->z_tail = zone->z_end;
244 zone->z_free = part->p_size;
245 zone->owner = part->id;
246 zone->active_regions = 0;
247 zone->max_regions = 0;
248 INIT_LIST_HEAD(&zone->region_list);
249 zone->z_ops = z_ops;
Naveen Ramaraj135cd672012-04-23 12:13:28 -0700250 if (part->p_tail) {
251 z_ops->allocate = allocate_tail;
252 z_ops->free = free_tail;
253 } else {
254 z_ops->allocate = allocate_head;
255 z_ops->free = free_head;
256 }
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -0700257 active_zones++;
258
259 if (active_zones == 1)
260 pr_info("Physical OCMEM zone layout:\n");
261
262 pr_info(" zone %s\t: 0x%08lx - 0x%08lx (%4ld KB)\n",
263 client_names[part->id], zone->z_start,
264 zone->z_end, part->p_size/SZ_1K);
265 }
266
267 dev_info(dev, "Total active zones = %d\n", active_zones);
268 return 0;
269}
270
271static int __devinit msm_ocmem_probe(struct platform_device *pdev)
272{
273 struct device *dev = &pdev->dev;
274
275 if (!pdev->dev.of_node->child) {
276 dev_info(dev, "Missing Configuration in Device Tree\n");
277 ocmem_pdata = parse_static_config(pdev);
278 } else {
279 ocmem_pdata = parse_dt_config(pdev);
280 }
281
282 /* Check if we have some configuration data to start */
283 if (!ocmem_pdata)
284 return -ENODEV;
285
286 /* Sanity Checks */
287 BUG_ON(!IS_ALIGNED(ocmem_pdata->size, PAGE_SIZE));
288 BUG_ON(!IS_ALIGNED(ocmem_pdata->base, PAGE_SIZE));
289
290 platform_set_drvdata(pdev, ocmem_pdata);
291
292 if (ocmem_zone_init(pdev))
293 return -EBUSY;
294
Naveen Ramarajbdf4dfe2012-04-23 14:09:50 -0700295 if (ocmem_notifier_init())
296 return -EBUSY;
297
Naveen Ramarajb9da05782012-05-07 09:07:35 -0700298 if (ocmem_sched_init())
299 return -EBUSY;
Naveen Ramaraj51f5e8b2012-04-09 15:58:40 -0700300 dev_info(dev, "initialized successfully\n");
301 return 0;
302}
303
304static int __devexit msm_ocmem_remove(struct platform_device *pdev)
305{
306 return 0;
307}
308
309static struct of_device_id msm_ocmem_dt_match[] = {
310 { .compatible = "qcom,msm_ocmem",
311 },
312 {}
313};
314
315static struct platform_driver msm_ocmem_driver = {
316 .probe = msm_ocmem_probe,
317 .remove = __devexit_p(msm_ocmem_remove),
318 .driver = {
319 .name = "msm_ocmem",
320 .owner = THIS_MODULE,
321 .of_match_table = msm_ocmem_dt_match,
322 },
323};
324
325static int __init ocmem_init(void)
326{
327 return platform_driver_register(&msm_ocmem_driver);
328}
329subsys_initcall(ocmem_init);
330
331static void __exit ocmem_exit(void)
332{
333 platform_driver_unregister(&msm_ocmem_driver);
334}
335module_exit(ocmem_exit);
336
337MODULE_LICENSE("GPL v2");
338MODULE_DESCRIPTION("Support for On-Chip Memory on MSM");