Commit | Line | Data |
---|---|---|
13439479 LA |
1 | /* |
2 | * Based on work from: | |
3 | * Andrew Andrianov <andrew@ncrmnt.org> | |
4 | ||
5 | * The Linux Foundation | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | ||
12 | #include <linux/init.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/of.h> | |
16 | #include <linux/of_platform.h> | |
17 | #include <linux/of_address.h> | |
18 | #include <linux/clk.h> | |
19 | #include <linux/dma-mapping.h> | |
20 | #include <linux/cma.h> | |
21 | #include <linux/dma-contiguous.h> | |
22 | #include <linux/io.h> | |
23 | #include <linux/of_reserved_mem.h> | |
24 | #include "ion.h" | |
25 | #include "ion_priv.h" | |
26 | #include "ion_of.h" | |
27 | ||
28 | int ion_parse_dt_heap_common(struct device_node *heap_node, | |
29 | struct ion_platform_heap *heap, | |
30 | struct ion_of_heap *compatible) | |
31 | { | |
32 | int i; | |
33 | ||
34 | for (i = 0; compatible[i].name != NULL; i++) { | |
35 | if (of_device_is_compatible(heap_node, compatible[i].compat)) | |
36 | break; | |
37 | } | |
38 | ||
39 | if (compatible[i].name == NULL) | |
40 | return -ENODEV; | |
41 | ||
42 | heap->id = compatible[i].heap_id; | |
43 | heap->type = compatible[i].type; | |
44 | heap->name = compatible[i].name; | |
45 | heap->align = compatible[i].align; | |
46 | ||
47 | /* Some kind of callback function pointer? */ | |
48 | ||
49 | pr_info("%s: id %d type %d name %s align %lx\n", __func__, | |
50 | heap->id, heap->type, heap->name, heap->align); | |
51 | return 0; | |
52 | } | |
53 | ||
54 | int ion_setup_heap_common(struct platform_device *parent, | |
55 | struct device_node *heap_node, | |
56 | struct ion_platform_heap *heap) | |
57 | { | |
58 | int ret = 0; | |
59 | ||
60 | switch (heap->type) { | |
61 | case ION_HEAP_TYPE_CARVEOUT: | |
62 | case ION_HEAP_TYPE_CHUNK: | |
63 | if (heap->base && heap->size) | |
64 | return 0; | |
65 | ||
66 | ret = of_reserved_mem_device_init(heap->priv); | |
67 | break; | |
68 | default: | |
69 | break; | |
70 | } | |
71 | ||
72 | return ret; | |
73 | } | |
74 | ||
75 | struct ion_platform_data *ion_parse_dt(struct platform_device *pdev, | |
76 | struct ion_of_heap *compatible) | |
77 | { | |
78 | int num_heaps, ret; | |
79 | const struct device_node *dt_node = pdev->dev.of_node; | |
80 | struct device_node *node; | |
81 | struct ion_platform_heap *heaps; | |
82 | struct ion_platform_data *data; | |
83 | int i = 0; | |
84 | ||
85 | num_heaps = of_get_available_child_count(dt_node); | |
86 | ||
87 | if (!num_heaps) | |
88 | return ERR_PTR(-EINVAL); | |
89 | ||
90 | heaps = devm_kzalloc(&pdev->dev, | |
91 | sizeof(struct ion_platform_heap)*num_heaps, | |
92 | GFP_KERNEL); | |
93 | if (!heaps) | |
94 | return ERR_PTR(-ENOMEM); | |
95 | ||
96 | data = devm_kzalloc(&pdev->dev, sizeof(struct ion_platform_data), | |
97 | GFP_KERNEL); | |
98 | if (!data) | |
99 | return ERR_PTR(-ENOMEM); | |
100 | ||
101 | for_each_available_child_of_node(dt_node, node) { | |
102 | struct platform_device *heap_pdev; | |
103 | ||
104 | ret = ion_parse_dt_heap_common(node, &heaps[i], compatible); | |
105 | if (ret) | |
106 | return ERR_PTR(ret); | |
107 | ||
108 | heap_pdev = of_platform_device_create(node, heaps[i].name, | |
109 | &pdev->dev); | |
110 | if (!pdev) | |
111 | return ERR_PTR(-ENOMEM); | |
112 | heap_pdev->dev.platform_data = &heaps[i]; | |
113 | ||
114 | heaps[i].priv = &heap_pdev->dev; | |
115 | ||
116 | ret = ion_setup_heap_common(pdev, node, &heaps[i]); | |
117 | if (ret) | |
118 | goto out_err; | |
119 | i++; | |
120 | } | |
121 | ||
122 | ||
123 | data->heaps = heaps; | |
124 | data->nr = num_heaps; | |
125 | return data; | |
126 | ||
127 | out_err: | |
128 | for ( ; i >= 0; i--) | |
129 | if (heaps[i].priv) | |
130 | of_device_unregister(to_platform_device(heaps[i].priv)); | |
131 | ||
132 | return ERR_PTR(ret); | |
133 | } | |
134 | ||
135 | void ion_destroy_platform_data(struct ion_platform_data *data) | |
136 | { | |
137 | int i; | |
138 | ||
139 | for (i = 0; i < data->nr; i++) | |
140 | if (data->heaps[i].priv) | |
141 | of_device_unregister(to_platform_device( | |
142 | data->heaps[i].priv)); | |
143 | } | |
144 | ||
145 | #ifdef CONFIG_OF_RESERVED_MEM | |
146 | #include <linux/of.h> | |
147 | #include <linux/of_fdt.h> | |
148 | #include <linux/of_reserved_mem.h> | |
149 | ||
150 | static int rmem_ion_device_init(struct reserved_mem *rmem, struct device *dev) | |
151 | { | |
152 | struct platform_device *pdev = to_platform_device(dev); | |
153 | struct ion_platform_heap *heap = pdev->dev.platform_data; | |
154 | ||
155 | heap->base = rmem->base; | |
156 | heap->base = rmem->size; | |
157 | pr_debug("%s: heap %s base %pa size %pa dev %p\n", __func__, | |
158 | heap->name, &rmem->base, &rmem->size, dev); | |
159 | return 0; | |
160 | } | |
161 | ||
162 | static void rmem_ion_device_release(struct reserved_mem *rmem, | |
163 | struct device *dev) | |
164 | { | |
165 | return; | |
166 | } | |
167 | ||
168 | static const struct reserved_mem_ops rmem_dma_ops = { | |
169 | .device_init = rmem_ion_device_init, | |
170 | .device_release = rmem_ion_device_release, | |
171 | }; | |
172 | ||
173 | static int __init rmem_ion_setup(struct reserved_mem *rmem) | |
174 | { | |
175 | phys_addr_t size = rmem->size; | |
176 | ||
177 | size = size / 1024; | |
178 | ||
179 | pr_info("Ion memory setup at %pa size %pa MiB\n", | |
180 | &rmem->base, &size); | |
181 | rmem->ops = &rmem_dma_ops; | |
182 | return 0; | |
183 | } | |
184 | RESERVEDMEM_OF_DECLARE(ion, "ion-region", rmem_ion_setup); | |
185 | #endif |