diff options
Diffstat (limited to 'drivers/misc/sram.c')
-rw-r--r-- | drivers/misc/sram.c | 127 |
1 files changed, 118 insertions, 9 deletions
diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index afe66571ce0b..21181fa243df 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c | |||
@@ -24,6 +24,9 @@ | |||
24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/of.h> | 26 | #include <linux/of.h> |
27 | #include <linux/of_address.h> | ||
28 | #include <linux/list.h> | ||
29 | #include <linux/list_sort.h> | ||
27 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
28 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
29 | #include <linux/spinlock.h> | 32 | #include <linux/spinlock.h> |
@@ -36,14 +39,35 @@ struct sram_dev { | |||
36 | struct clk *clk; | 39 | struct clk *clk; |
37 | }; | 40 | }; |
38 | 41 | ||
42 | struct sram_reserve { | ||
43 | struct list_head list; | ||
44 | u32 start; | ||
45 | u32 size; | ||
46 | }; | ||
47 | |||
48 | static int sram_reserve_cmp(void *priv, struct list_head *a, | ||
49 | struct list_head *b) | ||
50 | { | ||
51 | struct sram_reserve *ra = list_entry(a, struct sram_reserve, list); | ||
52 | struct sram_reserve *rb = list_entry(b, struct sram_reserve, list); | ||
53 | |||
54 | return ra->start - rb->start; | ||
55 | } | ||
56 | |||
39 | static int sram_probe(struct platform_device *pdev) | 57 | static int sram_probe(struct platform_device *pdev) |
40 | { | 58 | { |
41 | void __iomem *virt_base; | 59 | void __iomem *virt_base; |
42 | struct sram_dev *sram; | 60 | struct sram_dev *sram; |
43 | struct resource *res; | 61 | struct resource *res; |
44 | unsigned long size; | 62 | struct device_node *np = pdev->dev.of_node, *child; |
63 | unsigned long size, cur_start, cur_size; | ||
64 | struct sram_reserve *rblocks, *block; | ||
65 | struct list_head reserve_list; | ||
66 | unsigned int nblocks; | ||
45 | int ret; | 67 | int ret; |
46 | 68 | ||
69 | INIT_LIST_HEAD(&reserve_list); | ||
70 | |||
47 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 71 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
48 | virt_base = devm_ioremap_resource(&pdev->dev, res); | 72 | virt_base = devm_ioremap_resource(&pdev->dev, res); |
49 | if (IS_ERR(virt_base)) | 73 | if (IS_ERR(virt_base)) |
@@ -65,19 +89,106 @@ static int sram_probe(struct platform_device *pdev) | |||
65 | if (!sram->pool) | 89 | if (!sram->pool) |
66 | return -ENOMEM; | 90 | return -ENOMEM; |
67 | 91 | ||
68 | ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base, | 92 | /* |
69 | res->start, size, -1); | 93 | * We need an additional block to mark the end of the memory region |
70 | if (ret < 0) { | 94 | * after the reserved blocks from the dt are processed. |
71 | if (sram->clk) | 95 | */ |
72 | clk_disable_unprepare(sram->clk); | 96 | nblocks = (np) ? of_get_available_child_count(np) + 1 : 1; |
73 | return ret; | 97 | rblocks = kmalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL); |
98 | if (!rblocks) { | ||
99 | ret = -ENOMEM; | ||
100 | goto err_alloc; | ||
101 | } | ||
102 | |||
103 | block = &rblocks[0]; | ||
104 | for_each_available_child_of_node(np, child) { | ||
105 | struct resource child_res; | ||
106 | |||
107 | ret = of_address_to_resource(child, 0, &child_res); | ||
108 | if (ret < 0) { | ||
109 | dev_err(&pdev->dev, | ||
110 | "could not get address for node %s\n", | ||
111 | child->full_name); | ||
112 | goto err_chunks; | ||
113 | } | ||
114 | |||
115 | if (child_res.start < res->start || child_res.end > res->end) { | ||
116 | dev_err(&pdev->dev, | ||
117 | "reserved block %s outside the sram area\n", | ||
118 | child->full_name); | ||
119 | ret = -EINVAL; | ||
120 | goto err_chunks; | ||
121 | } | ||
122 | |||
123 | block->start = child_res.start - res->start; | ||
124 | block->size = resource_size(&child_res); | ||
125 | list_add_tail(&block->list, &reserve_list); | ||
126 | |||
127 | dev_dbg(&pdev->dev, "found reserved block 0x%x-0x%x\n", | ||
128 | block->start, | ||
129 | block->start + block->size); | ||
130 | |||
131 | block++; | ||
132 | } | ||
133 | |||
134 | /* the last chunk marks the end of the region */ | ||
135 | rblocks[nblocks - 1].start = size; | ||
136 | rblocks[nblocks - 1].size = 0; | ||
137 | list_add_tail(&rblocks[nblocks - 1].list, &reserve_list); | ||
138 | |||
139 | list_sort(NULL, &reserve_list, sram_reserve_cmp); | ||
140 | |||
141 | cur_start = 0; | ||
142 | |||
143 | list_for_each_entry(block, &reserve_list, list) { | ||
144 | /* can only happen if sections overlap */ | ||
145 | if (block->start < cur_start) { | ||
146 | dev_err(&pdev->dev, | ||
147 | "block at 0x%x starts after current offset 0x%lx\n", | ||
148 | block->start, cur_start); | ||
149 | ret = -EINVAL; | ||
150 | goto err_chunks; | ||
151 | } | ||
152 | |||
153 | /* current start is in a reserved block, so continue after it */ | ||
154 | if (block->start == cur_start) { | ||
155 | cur_start = block->start + block->size; | ||
156 | continue; | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * allocate the space between the current starting | ||
161 | * address and the following reserved block, or the | ||
162 | * end of the region. | ||
163 | */ | ||
164 | cur_size = block->start - cur_start; | ||
165 | |||
166 | dev_dbg(&pdev->dev, "adding chunk 0x%lx-0x%lx\n", | ||
167 | cur_start, cur_start + cur_size); | ||
168 | ret = gen_pool_add_virt(sram->pool, | ||
169 | (unsigned long)virt_base + cur_start, | ||
170 | res->start + cur_start, cur_size, -1); | ||
171 | if (ret < 0) | ||
172 | goto err_chunks; | ||
173 | |||
174 | /* next allocation after this reserved block */ | ||
175 | cur_start = block->start + block->size; | ||
74 | } | 176 | } |
75 | 177 | ||
178 | kfree(rblocks); | ||
179 | |||
76 | platform_set_drvdata(pdev, sram); | 180 | platform_set_drvdata(pdev, sram); |
77 | 181 | ||
78 | dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base); | 182 | dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base); |
79 | 183 | ||
80 | return 0; | 184 | return 0; |
185 | |||
186 | err_chunks: | ||
187 | kfree(rblocks); | ||
188 | err_alloc: | ||
189 | if (sram->clk) | ||
190 | clk_disable_unprepare(sram->clk); | ||
191 | return ret; | ||
81 | } | 192 | } |
82 | 193 | ||
83 | static int sram_remove(struct platform_device *pdev) | 194 | static int sram_remove(struct platform_device *pdev) |
@@ -87,8 +198,6 @@ static int sram_remove(struct platform_device *pdev) | |||
87 | if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool)) | 198 | if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool)) |
88 | dev_dbg(&pdev->dev, "removed while SRAM allocated\n"); | 199 | dev_dbg(&pdev->dev, "removed while SRAM allocated\n"); |
89 | 200 | ||
90 | gen_pool_destroy(sram->pool); | ||
91 | |||
92 | if (sram->clk) | 201 | if (sram->clk) |
93 | clk_disable_unprepare(sram->clk); | 202 | clk_disable_unprepare(sram->clk); |
94 | 203 | ||