diff options
Diffstat (limited to 'arch/arm/mach-msm/iommu_dev.c')
-rw-r--r-- | arch/arm/mach-msm/iommu_dev.c | 230 |
1 files changed, 137 insertions, 93 deletions
diff --git a/arch/arm/mach-msm/iommu_dev.c b/arch/arm/mach-msm/iommu_dev.c index b83c73b41fd1..8e8fb079852d 100644 --- a/arch/arm/mach-msm/iommu_dev.c +++ b/arch/arm/mach-msm/iommu_dev.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* Copyright (c) 2010, Code Aurora Forum. All rights reserved. | 1 | /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. |
2 | * | 2 | * |
3 | * This program is free software; you can redistribute it and/or modify | 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 | 4 | * it under the terms of the GNU General Public License version 2 and |
@@ -29,6 +29,7 @@ | |||
29 | 29 | ||
30 | #include <mach/iommu_hw-8xxx.h> | 30 | #include <mach/iommu_hw-8xxx.h> |
31 | #include <mach/iommu.h> | 31 | #include <mach/iommu.h> |
32 | #include <mach/clk.h> | ||
32 | 33 | ||
33 | struct iommu_ctx_iter_data { | 34 | struct iommu_ctx_iter_data { |
34 | /* input */ | 35 | /* input */ |
@@ -84,9 +85,9 @@ fail: | |||
84 | } | 85 | } |
85 | EXPORT_SYMBOL(msm_iommu_get_ctx); | 86 | EXPORT_SYMBOL(msm_iommu_get_ctx); |
86 | 87 | ||
87 | static void msm_iommu_reset(void __iomem *base) | 88 | static void msm_iommu_reset(void __iomem *base, int ncb) |
88 | { | 89 | { |
89 | int ctx, ncb; | 90 | int ctx; |
90 | 91 | ||
91 | SET_RPUE(base, 0); | 92 | SET_RPUE(base, 0); |
92 | SET_RPUEIE(base, 0); | 93 | SET_RPUEIE(base, 0); |
@@ -99,7 +100,6 @@ static void msm_iommu_reset(void __iomem *base) | |||
99 | SET_GLOBAL_TLBIALL(base, 0); | 100 | SET_GLOBAL_TLBIALL(base, 0); |
100 | SET_RPU_ACR(base, 0); | 101 | SET_RPU_ACR(base, 0); |
101 | SET_TLBLKCRWE(base, 1); | 102 | SET_TLBLKCRWE(base, 1); |
102 | ncb = GET_NCB(base)+1; | ||
103 | 103 | ||
104 | for (ctx = 0; ctx < ncb; ctx++) { | 104 | for (ctx = 0; ctx < ncb; ctx++) { |
105 | SET_BPRCOSH(base, ctx, 0); | 105 | SET_BPRCOSH(base, ctx, 0); |
@@ -130,117 +130,140 @@ static int msm_iommu_probe(struct platform_device *pdev) | |||
130 | { | 130 | { |
131 | struct resource *r, *r2; | 131 | struct resource *r, *r2; |
132 | struct clk *iommu_clk; | 132 | struct clk *iommu_clk; |
133 | struct clk *iommu_pclk; | ||
133 | struct msm_iommu_drvdata *drvdata; | 134 | struct msm_iommu_drvdata *drvdata; |
134 | struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data; | 135 | struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data; |
135 | void __iomem *regs_base; | 136 | void __iomem *regs_base; |
136 | resource_size_t len; | 137 | resource_size_t len; |
137 | int ret = 0, ncb, nm2v, irq; | 138 | int ret, irq, par; |
138 | 139 | ||
139 | if (pdev->id != -1) { | 140 | if (pdev->id == -1) { |
140 | drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); | 141 | msm_iommu_root_dev = pdev; |
142 | return 0; | ||
143 | } | ||
141 | 144 | ||
142 | if (!drvdata) { | 145 | drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); |
143 | ret = -ENOMEM; | ||
144 | goto fail; | ||
145 | } | ||
146 | 146 | ||
147 | if (!iommu_dev) { | 147 | if (!drvdata) { |
148 | ret = -ENODEV; | 148 | ret = -ENOMEM; |
149 | goto fail; | 149 | goto fail; |
150 | } | 150 | } |
151 | 151 | ||
152 | if (iommu_dev->clk_rate != 0) { | 152 | if (!iommu_dev) { |
153 | iommu_clk = clk_get(&pdev->dev, "iommu_clk"); | 153 | ret = -ENODEV; |
154 | 154 | goto fail; | |
155 | if (IS_ERR(iommu_clk)) { | 155 | } |
156 | ret = -ENODEV; | 156 | |
157 | goto fail; | 157 | iommu_pclk = clk_get(NULL, "smmu_pclk"); |
158 | } | 158 | if (IS_ERR(iommu_pclk)) { |
159 | 159 | ret = -ENODEV; | |
160 | if (iommu_dev->clk_rate > 0) { | 160 | goto fail; |
161 | ret = clk_set_rate(iommu_clk, | 161 | } |
162 | iommu_dev->clk_rate); | 162 | |
163 | if (ret) { | 163 | ret = clk_enable(iommu_pclk); |
164 | clk_put(iommu_clk); | 164 | if (ret) |
165 | goto fail; | 165 | goto fail_enable; |
166 | } | 166 | |
167 | } | 167 | iommu_clk = clk_get(&pdev->dev, "iommu_clk"); |
168 | 168 | ||
169 | ret = clk_enable(iommu_clk); | 169 | if (!IS_ERR(iommu_clk)) { |
170 | if (ret) { | 170 | if (clk_get_rate(iommu_clk) == 0) |
171 | clk_put(iommu_clk); | 171 | clk_set_min_rate(iommu_clk, 1); |
172 | goto fail; | 172 | |
173 | } | 173 | ret = clk_enable(iommu_clk); |
174 | if (ret) { | ||
174 | clk_put(iommu_clk); | 175 | clk_put(iommu_clk); |
176 | goto fail_pclk; | ||
175 | } | 177 | } |
178 | } else | ||
179 | iommu_clk = NULL; | ||
176 | 180 | ||
177 | r = platform_get_resource_byname(pdev, IORESOURCE_MEM, | 181 | r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "physbase"); |
178 | "physbase"); | ||
179 | if (!r) { | ||
180 | ret = -ENODEV; | ||
181 | goto fail; | ||
182 | } | ||
183 | 182 | ||
184 | len = r->end - r->start + 1; | 183 | if (!r) { |
184 | ret = -ENODEV; | ||
185 | goto fail_clk; | ||
186 | } | ||
185 | 187 | ||
186 | r2 = request_mem_region(r->start, len, r->name); | 188 | len = resource_size(r); |
187 | if (!r2) { | ||
188 | pr_err("Could not request memory region: " | ||
189 | "start=%p, len=%d\n", (void *) r->start, len); | ||
190 | ret = -EBUSY; | ||
191 | goto fail; | ||
192 | } | ||
193 | 189 | ||
194 | regs_base = ioremap(r2->start, len); | 190 | r2 = request_mem_region(r->start, len, r->name); |
191 | if (!r2) { | ||
192 | pr_err("Could not request memory region: start=%p, len=%d\n", | ||
193 | (void *) r->start, len); | ||
194 | ret = -EBUSY; | ||
195 | goto fail_clk; | ||
196 | } | ||
195 | 197 | ||
196 | if (!regs_base) { | 198 | regs_base = ioremap(r2->start, len); |
197 | pr_err("Could not ioremap: start=%p, len=%d\n", | ||
198 | (void *) r2->start, len); | ||
199 | ret = -EBUSY; | ||
200 | goto fail_mem; | ||
201 | } | ||
202 | 199 | ||
203 | irq = platform_get_irq_byname(pdev, "secure_irq"); | 200 | if (!regs_base) { |
204 | if (irq < 0) { | 201 | pr_err("Could not ioremap: start=%p, len=%d\n", |
205 | ret = -ENODEV; | 202 | (void *) r2->start, len); |
206 | goto fail_io; | 203 | ret = -EBUSY; |
207 | } | 204 | goto fail_mem; |
205 | } | ||
208 | 206 | ||
209 | mb(); | 207 | irq = platform_get_irq_byname(pdev, "secure_irq"); |
208 | if (irq < 0) { | ||
209 | ret = -ENODEV; | ||
210 | goto fail_io; | ||
211 | } | ||
210 | 212 | ||
211 | if (GET_IDR(regs_base) == 0) { | 213 | msm_iommu_reset(regs_base, iommu_dev->ncb); |
212 | pr_err("Invalid IDR value detected\n"); | ||
213 | ret = -ENODEV; | ||
214 | goto fail_io; | ||
215 | } | ||
216 | 214 | ||
217 | ret = request_irq(irq, msm_iommu_fault_handler, 0, | 215 | SET_M(regs_base, 0, 1); |
218 | "msm_iommu_secure_irpt_handler", drvdata); | 216 | SET_PAR(regs_base, 0, 0); |
219 | if (ret) { | 217 | SET_V2PCFG(regs_base, 0, 1); |
220 | pr_err("Request IRQ %d failed with ret=%d\n", irq, ret); | 218 | SET_V2PPR(regs_base, 0, 0); |
221 | goto fail_io; | 219 | par = GET_PAR(regs_base, 0); |
222 | } | 220 | SET_V2PCFG(regs_base, 0, 0); |
221 | SET_M(regs_base, 0, 0); | ||
223 | 222 | ||
224 | msm_iommu_reset(regs_base); | 223 | if (!par) { |
225 | drvdata->base = regs_base; | 224 | pr_err("%s: Invalid PAR value detected\n", iommu_dev->name); |
226 | drvdata->irq = irq; | 225 | ret = -ENODEV; |
226 | goto fail_io; | ||
227 | } | ||
227 | 228 | ||
228 | nm2v = GET_NM2VCBMT((unsigned long) regs_base); | 229 | ret = request_irq(irq, msm_iommu_fault_handler, 0, |
229 | ncb = GET_NCB((unsigned long) regs_base); | 230 | "msm_iommu_secure_irpt_handler", drvdata); |
231 | if (ret) { | ||
232 | pr_err("Request IRQ %d failed with ret=%d\n", irq, ret); | ||
233 | goto fail_io; | ||
234 | } | ||
230 | 235 | ||
231 | pr_info("device %s mapped at %p, irq %d with %d ctx banks\n", | ||
232 | iommu_dev->name, regs_base, irq, ncb+1); | ||
233 | 236 | ||
234 | platform_set_drvdata(pdev, drvdata); | 237 | drvdata->pclk = iommu_pclk; |
235 | } else | 238 | drvdata->clk = iommu_clk; |
236 | msm_iommu_root_dev = pdev; | 239 | drvdata->base = regs_base; |
240 | drvdata->irq = irq; | ||
241 | drvdata->ncb = iommu_dev->ncb; | ||
237 | 242 | ||
238 | return 0; | 243 | pr_info("device %s mapped at %p, irq %d with %d ctx banks\n", |
244 | iommu_dev->name, regs_base, irq, iommu_dev->ncb); | ||
245 | |||
246 | platform_set_drvdata(pdev, drvdata); | ||
247 | |||
248 | if (iommu_clk) | ||
249 | clk_disable(iommu_clk); | ||
250 | |||
251 | clk_disable(iommu_pclk); | ||
239 | 252 | ||
253 | return 0; | ||
240 | fail_io: | 254 | fail_io: |
241 | iounmap(regs_base); | 255 | iounmap(regs_base); |
242 | fail_mem: | 256 | fail_mem: |
243 | release_mem_region(r->start, len); | 257 | release_mem_region(r->start, len); |
258 | fail_clk: | ||
259 | if (iommu_clk) { | ||
260 | clk_disable(iommu_clk); | ||
261 | clk_put(iommu_clk); | ||
262 | } | ||
263 | fail_pclk: | ||
264 | clk_disable(iommu_pclk); | ||
265 | fail_enable: | ||
266 | clk_put(iommu_pclk); | ||
244 | fail: | 267 | fail: |
245 | kfree(drvdata); | 268 | kfree(drvdata); |
246 | return ret; | 269 | return ret; |
@@ -252,7 +275,10 @@ static int msm_iommu_remove(struct platform_device *pdev) | |||
252 | 275 | ||
253 | drv = platform_get_drvdata(pdev); | 276 | drv = platform_get_drvdata(pdev); |
254 | if (drv) { | 277 | if (drv) { |
255 | memset(drv, 0, sizeof(struct msm_iommu_drvdata)); | 278 | if (drv->clk) |
279 | clk_put(drv->clk); | ||
280 | clk_put(drv->pclk); | ||
281 | memset(drv, 0, sizeof(*drv)); | ||
256 | kfree(drv); | 282 | kfree(drv); |
257 | platform_set_drvdata(pdev, NULL); | 283 | platform_set_drvdata(pdev, NULL); |
258 | } | 284 | } |
@@ -264,7 +290,7 @@ static int msm_iommu_ctx_probe(struct platform_device *pdev) | |||
264 | struct msm_iommu_ctx_dev *c = pdev->dev.platform_data; | 290 | struct msm_iommu_ctx_dev *c = pdev->dev.platform_data; |
265 | struct msm_iommu_drvdata *drvdata; | 291 | struct msm_iommu_drvdata *drvdata; |
266 | struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL; | 292 | struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL; |
267 | int i, ret = 0; | 293 | int i, ret; |
268 | if (!c || !pdev->dev.parent) { | 294 | if (!c || !pdev->dev.parent) { |
269 | ret = -EINVAL; | 295 | ret = -EINVAL; |
270 | goto fail; | 296 | goto fail; |
@@ -288,6 +314,18 @@ static int msm_iommu_ctx_probe(struct platform_device *pdev) | |||
288 | INIT_LIST_HEAD(&ctx_drvdata->attached_elm); | 314 | INIT_LIST_HEAD(&ctx_drvdata->attached_elm); |
289 | platform_set_drvdata(pdev, ctx_drvdata); | 315 | platform_set_drvdata(pdev, ctx_drvdata); |
290 | 316 | ||
317 | ret = clk_enable(drvdata->pclk); | ||
318 | if (ret) | ||
319 | goto fail; | ||
320 | |||
321 | if (drvdata->clk) { | ||
322 | ret = clk_enable(drvdata->clk); | ||
323 | if (ret) { | ||
324 | clk_disable(drvdata->pclk); | ||
325 | goto fail; | ||
326 | } | ||
327 | } | ||
328 | |||
291 | /* Program the M2V tables for this context */ | 329 | /* Program the M2V tables for this context */ |
292 | for (i = 0; i < MAX_NUM_MIDS; i++) { | 330 | for (i = 0; i < MAX_NUM_MIDS; i++) { |
293 | int mid = c->mids[i]; | 331 | int mid = c->mids[i]; |
@@ -297,21 +335,27 @@ static int msm_iommu_ctx_probe(struct platform_device *pdev) | |||
297 | SET_M2VCBR_N(drvdata->base, mid, 0); | 335 | SET_M2VCBR_N(drvdata->base, mid, 0); |
298 | SET_CBACR_N(drvdata->base, c->num, 0); | 336 | SET_CBACR_N(drvdata->base, c->num, 0); |
299 | 337 | ||
300 | /* Set VMID = MID */ | 338 | /* Set VMID = 0 */ |
301 | SET_VMID(drvdata->base, mid, mid); | 339 | SET_VMID(drvdata->base, mid, 0); |
302 | 340 | ||
303 | /* Set the context number for that MID to this context */ | 341 | /* Set the context number for that MID to this context */ |
304 | SET_CBNDX(drvdata->base, mid, c->num); | 342 | SET_CBNDX(drvdata->base, mid, c->num); |
305 | 343 | ||
306 | /* Set MID associated with this context bank */ | 344 | /* Set MID associated with this context bank to 0*/ |
307 | SET_CBVMID(drvdata->base, c->num, mid); | 345 | SET_CBVMID(drvdata->base, c->num, 0); |
346 | |||
347 | /* Set the ASID for TLB tagging for this context */ | ||
348 | SET_CONTEXTIDR_ASID(drvdata->base, c->num, c->num); | ||
308 | 349 | ||
309 | /* Set security bit override to be Non-secure */ | 350 | /* Set security bit override to be Non-secure */ |
310 | SET_NSCFG(drvdata->base, mid, 3); | 351 | SET_NSCFG(drvdata->base, mid, 3); |
311 | } | 352 | } |
312 | 353 | ||
313 | pr_info("context device %s with bank index %d\n", c->name, c->num); | 354 | if (drvdata->clk) |
355 | clk_disable(drvdata->clk); | ||
356 | clk_disable(drvdata->pclk); | ||
314 | 357 | ||
358 | dev_info(&pdev->dev, "context %s using bank %d\n", c->name, c->num); | ||
315 | return 0; | 359 | return 0; |
316 | fail: | 360 | fail: |
317 | kfree(ctx_drvdata); | 361 | kfree(ctx_drvdata); |