diff options
Diffstat (limited to 'arch/arm/plat-omap/iommu.c')
| -rw-r--r-- | arch/arm/plat-omap/iommu.c | 101 |
1 files changed, 66 insertions, 35 deletions
diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c index 0e137663349c..bc094dbacee6 100644 --- a/arch/arm/plat-omap/iommu.c +++ b/arch/arm/plat-omap/iommu.c | |||
| @@ -25,6 +25,11 @@ | |||
| 25 | 25 | ||
| 26 | #include "iopgtable.h" | 26 | #include "iopgtable.h" |
| 27 | 27 | ||
| 28 | #define for_each_iotlb_cr(obj, n, __i, cr) \ | ||
| 29 | for (__i = 0; \ | ||
| 30 | (__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \ | ||
| 31 | __i++) | ||
| 32 | |||
| 28 | /* accommodate the difference between omap1 and omap2/3 */ | 33 | /* accommodate the difference between omap1 and omap2/3 */ |
| 29 | static const struct iommu_functions *arch_iommu; | 34 | static const struct iommu_functions *arch_iommu; |
| 30 | 35 | ||
| @@ -172,15 +177,12 @@ static void iotlb_lock_get(struct iommu *obj, struct iotlb_lock *l) | |||
| 172 | l->base = MMU_LOCK_BASE(val); | 177 | l->base = MMU_LOCK_BASE(val); |
| 173 | l->vict = MMU_LOCK_VICT(val); | 178 | l->vict = MMU_LOCK_VICT(val); |
| 174 | 179 | ||
| 175 | BUG_ON(l->base != 0); /* Currently no preservation is used */ | ||
| 176 | } | 180 | } |
| 177 | 181 | ||
| 178 | static void iotlb_lock_set(struct iommu *obj, struct iotlb_lock *l) | 182 | static void iotlb_lock_set(struct iommu *obj, struct iotlb_lock *l) |
| 179 | { | 183 | { |
| 180 | u32 val; | 184 | u32 val; |
| 181 | 185 | ||
| 182 | BUG_ON(l->base != 0); /* Currently no preservation is used */ | ||
| 183 | |||
| 184 | val = (l->base << MMU_LOCK_BASE_SHIFT); | 186 | val = (l->base << MMU_LOCK_BASE_SHIFT); |
| 185 | val |= (l->vict << MMU_LOCK_VICT_SHIFT); | 187 | val |= (l->vict << MMU_LOCK_VICT_SHIFT); |
| 186 | 188 | ||
| @@ -214,6 +216,20 @@ static inline ssize_t iotlb_dump_cr(struct iommu *obj, struct cr_regs *cr, | |||
| 214 | return arch_iommu->dump_cr(obj, cr, buf); | 216 | return arch_iommu->dump_cr(obj, cr, buf); |
| 215 | } | 217 | } |
| 216 | 218 | ||
| 219 | /* only used in iotlb iteration for-loop */ | ||
| 220 | static struct cr_regs __iotlb_read_cr(struct iommu *obj, int n) | ||
| 221 | { | ||
| 222 | struct cr_regs cr; | ||
| 223 | struct iotlb_lock l; | ||
| 224 | |||
| 225 | iotlb_lock_get(obj, &l); | ||
| 226 | l.vict = n; | ||
| 227 | iotlb_lock_set(obj, &l); | ||
| 228 | iotlb_read_cr(obj, &cr); | ||
| 229 | |||
| 230 | return cr; | ||
| 231 | } | ||
| 232 | |||
| 217 | /** | 233 | /** |
| 218 | * load_iotlb_entry - Set an iommu tlb entry | 234 | * load_iotlb_entry - Set an iommu tlb entry |
| 219 | * @obj: target iommu | 235 | * @obj: target iommu |
| @@ -221,7 +237,6 @@ static inline ssize_t iotlb_dump_cr(struct iommu *obj, struct cr_regs *cr, | |||
| 221 | **/ | 237 | **/ |
| 222 | int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e) | 238 | int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e) |
| 223 | { | 239 | { |
| 224 | int i; | ||
| 225 | int err = 0; | 240 | int err = 0; |
| 226 | struct iotlb_lock l; | 241 | struct iotlb_lock l; |
| 227 | struct cr_regs *cr; | 242 | struct cr_regs *cr; |
| @@ -231,21 +246,30 @@ int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e) | |||
| 231 | 246 | ||
| 232 | clk_enable(obj->clk); | 247 | clk_enable(obj->clk); |
| 233 | 248 | ||
| 234 | for (i = 0; i < obj->nr_tlb_entries; i++) { | 249 | iotlb_lock_get(obj, &l); |
| 250 | if (l.base == obj->nr_tlb_entries) { | ||
| 251 | dev_warn(obj->dev, "%s: preserve entries full\n", __func__); | ||
| 252 | err = -EBUSY; | ||
| 253 | goto out; | ||
| 254 | } | ||
| 255 | if (!e->prsvd) { | ||
| 256 | int i; | ||
| 235 | struct cr_regs tmp; | 257 | struct cr_regs tmp; |
| 236 | 258 | ||
| 259 | for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, tmp) | ||
| 260 | if (!iotlb_cr_valid(&tmp)) | ||
| 261 | break; | ||
| 262 | |||
| 263 | if (i == obj->nr_tlb_entries) { | ||
| 264 | dev_dbg(obj->dev, "%s: full: no entry\n", __func__); | ||
| 265 | err = -EBUSY; | ||
| 266 | goto out; | ||
| 267 | } | ||
| 268 | |||
| 237 | iotlb_lock_get(obj, &l); | 269 | iotlb_lock_get(obj, &l); |
| 238 | l.vict = i; | 270 | } else { |
| 271 | l.vict = l.base; | ||
| 239 | iotlb_lock_set(obj, &l); | 272 | iotlb_lock_set(obj, &l); |
| 240 | iotlb_read_cr(obj, &tmp); | ||
| 241 | if (!iotlb_cr_valid(&tmp)) | ||
| 242 | break; | ||
| 243 | } | ||
| 244 | |||
| 245 | if (i == obj->nr_tlb_entries) { | ||
| 246 | dev_dbg(obj->dev, "%s: full: no entry\n", __func__); | ||
| 247 | err = -EBUSY; | ||
| 248 | goto out; | ||
| 249 | } | 273 | } |
| 250 | 274 | ||
| 251 | cr = iotlb_alloc_cr(obj, e); | 275 | cr = iotlb_alloc_cr(obj, e); |
| @@ -257,9 +281,11 @@ int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e) | |||
| 257 | iotlb_load_cr(obj, cr); | 281 | iotlb_load_cr(obj, cr); |
| 258 | kfree(cr); | 282 | kfree(cr); |
| 259 | 283 | ||
| 284 | if (e->prsvd) | ||
| 285 | l.base++; | ||
| 260 | /* increment victim for next tlb load */ | 286 | /* increment victim for next tlb load */ |
| 261 | if (++l.vict == obj->nr_tlb_entries) | 287 | if (++l.vict == obj->nr_tlb_entries) |
| 262 | l.vict = 0; | 288 | l.vict = l.base; |
| 263 | iotlb_lock_set(obj, &l); | 289 | iotlb_lock_set(obj, &l); |
| 264 | out: | 290 | out: |
| 265 | clk_disable(obj->clk); | 291 | clk_disable(obj->clk); |
| @@ -276,20 +302,15 @@ EXPORT_SYMBOL_GPL(load_iotlb_entry); | |||
| 276 | **/ | 302 | **/ |
| 277 | void flush_iotlb_page(struct iommu *obj, u32 da) | 303 | void flush_iotlb_page(struct iommu *obj, u32 da) |
| 278 | { | 304 | { |
| 279 | struct iotlb_lock l; | ||
| 280 | int i; | 305 | int i; |
| 306 | struct cr_regs cr; | ||
| 281 | 307 | ||
| 282 | clk_enable(obj->clk); | 308 | clk_enable(obj->clk); |
| 283 | 309 | ||
| 284 | for (i = 0; i < obj->nr_tlb_entries; i++) { | 310 | for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) { |
| 285 | struct cr_regs cr; | ||
| 286 | u32 start; | 311 | u32 start; |
| 287 | size_t bytes; | 312 | size_t bytes; |
| 288 | 313 | ||
| 289 | iotlb_lock_get(obj, &l); | ||
| 290 | l.vict = i; | ||
| 291 | iotlb_lock_set(obj, &l); | ||
| 292 | iotlb_read_cr(obj, &cr); | ||
| 293 | if (!iotlb_cr_valid(&cr)) | 314 | if (!iotlb_cr_valid(&cr)) |
| 294 | continue; | 315 | continue; |
| 295 | 316 | ||
| @@ -299,7 +320,6 @@ void flush_iotlb_page(struct iommu *obj, u32 da) | |||
| 299 | if ((start <= da) && (da < start + bytes)) { | 320 | if ((start <= da) && (da < start + bytes)) { |
| 300 | dev_dbg(obj->dev, "%s: %08x<=%08x(%x)\n", | 321 | dev_dbg(obj->dev, "%s: %08x<=%08x(%x)\n", |
| 301 | __func__, start, da, bytes); | 322 | __func__, start, da, bytes); |
| 302 | iotlb_load_cr(obj, &cr); | ||
| 303 | iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY); | 323 | iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY); |
| 304 | } | 324 | } |
| 305 | } | 325 | } |
| @@ -370,26 +390,19 @@ EXPORT_SYMBOL_GPL(iommu_dump_ctx); | |||
| 370 | static int __dump_tlb_entries(struct iommu *obj, struct cr_regs *crs, int num) | 390 | static int __dump_tlb_entries(struct iommu *obj, struct cr_regs *crs, int num) |
| 371 | { | 391 | { |
| 372 | int i; | 392 | int i; |
| 373 | struct iotlb_lock saved, l; | 393 | struct iotlb_lock saved; |
| 394 | struct cr_regs tmp; | ||
| 374 | struct cr_regs *p = crs; | 395 | struct cr_regs *p = crs; |
| 375 | 396 | ||
| 376 | clk_enable(obj->clk); | 397 | clk_enable(obj->clk); |
| 377 | |||
| 378 | iotlb_lock_get(obj, &saved); | 398 | iotlb_lock_get(obj, &saved); |
| 379 | memcpy(&l, &saved, sizeof(saved)); | ||
| 380 | 399 | ||
| 381 | for (i = 0; i < num; i++) { | 400 | for_each_iotlb_cr(obj, num, i, tmp) { |
| 382 | struct cr_regs tmp; | ||
| 383 | |||
| 384 | iotlb_lock_get(obj, &l); | ||
| 385 | l.vict = i; | ||
| 386 | iotlb_lock_set(obj, &l); | ||
| 387 | iotlb_read_cr(obj, &tmp); | ||
| 388 | if (!iotlb_cr_valid(&tmp)) | 401 | if (!iotlb_cr_valid(&tmp)) |
| 389 | continue; | 402 | continue; |
| 390 | |||
| 391 | *p++ = tmp; | 403 | *p++ = tmp; |
| 392 | } | 404 | } |
| 405 | |||
| 393 | iotlb_lock_set(obj, &saved); | 406 | iotlb_lock_set(obj, &saved); |
| 394 | clk_disable(obj->clk); | 407 | clk_disable(obj->clk); |
| 395 | 408 | ||
| @@ -503,6 +516,12 @@ static int iopgd_alloc_section(struct iommu *obj, u32 da, u32 pa, u32 prot) | |||
| 503 | { | 516 | { |
| 504 | u32 *iopgd = iopgd_offset(obj, da); | 517 | u32 *iopgd = iopgd_offset(obj, da); |
| 505 | 518 | ||
| 519 | if ((da | pa) & ~IOSECTION_MASK) { | ||
| 520 | dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n", | ||
| 521 | __func__, da, pa, IOSECTION_SIZE); | ||
| 522 | return -EINVAL; | ||
| 523 | } | ||
| 524 | |||
| 506 | *iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION; | 525 | *iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION; |
| 507 | flush_iopgd_range(iopgd, iopgd); | 526 | flush_iopgd_range(iopgd, iopgd); |
| 508 | return 0; | 527 | return 0; |
| @@ -513,6 +532,12 @@ static int iopgd_alloc_super(struct iommu *obj, u32 da, u32 pa, u32 prot) | |||
| 513 | u32 *iopgd = iopgd_offset(obj, da); | 532 | u32 *iopgd = iopgd_offset(obj, da); |
| 514 | int i; | 533 | int i; |
| 515 | 534 | ||
| 535 | if ((da | pa) & ~IOSUPER_MASK) { | ||
| 536 | dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n", | ||
| 537 | __func__, da, pa, IOSUPER_SIZE); | ||
| 538 | return -EINVAL; | ||
| 539 | } | ||
| 540 | |||
| 516 | for (i = 0; i < 16; i++) | 541 | for (i = 0; i < 16; i++) |
| 517 | *(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER; | 542 | *(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER; |
| 518 | flush_iopgd_range(iopgd, iopgd + 15); | 543 | flush_iopgd_range(iopgd, iopgd + 15); |
| @@ -542,6 +567,12 @@ static int iopte_alloc_large(struct iommu *obj, u32 da, u32 pa, u32 prot) | |||
| 542 | u32 *iopte = iopte_alloc(obj, iopgd, da); | 567 | u32 *iopte = iopte_alloc(obj, iopgd, da); |
| 543 | int i; | 568 | int i; |
| 544 | 569 | ||
| 570 | if ((da | pa) & ~IOLARGE_MASK) { | ||
| 571 | dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n", | ||
| 572 | __func__, da, pa, IOLARGE_SIZE); | ||
| 573 | return -EINVAL; | ||
| 574 | } | ||
| 575 | |||
| 545 | if (IS_ERR(iopte)) | 576 | if (IS_ERR(iopte)) |
| 546 | return PTR_ERR(iopte); | 577 | return PTR_ERR(iopte); |
| 547 | 578 | ||
