Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.13 418 lines 9.1 kB view raw
1/* Copyright (c) 2010-2011, 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 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 15 * 02110-1301, USA. 16 */ 17 18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 19 20#include <linux/kernel.h> 21#include <linux/module.h> 22#include <linux/platform_device.h> 23#include <linux/io.h> 24#include <linux/clk.h> 25#include <linux/iommu.h> 26#include <linux/interrupt.h> 27#include <linux/err.h> 28#include <linux/slab.h> 29 30#include "msm_iommu_hw-8xxx.h" 31#include "msm_iommu.h" 32 33struct iommu_ctx_iter_data { 34 /* input */ 35 const char *name; 36 37 /* output */ 38 struct device *dev; 39}; 40 41static struct platform_device *msm_iommu_root_dev; 42 43static int each_iommu_ctx(struct device *dev, void *data) 44{ 45 struct iommu_ctx_iter_data *res = data; 46 struct msm_iommu_ctx_dev *c = dev->platform_data; 47 48 if (!res || !c || !c->name || !res->name) 49 return -EINVAL; 50 51 if (!strcmp(res->name, c->name)) { 52 res->dev = dev; 53 return 1; 54 } 55 return 0; 56} 57 58static int each_iommu(struct device *dev, void *data) 59{ 60 return device_for_each_child(dev, data, each_iommu_ctx); 61} 62 63struct device *msm_iommu_get_ctx(const char *ctx_name) 64{ 65 struct iommu_ctx_iter_data r; 66 int found; 67 68 if (!msm_iommu_root_dev) { 69 pr_err("No root IOMMU device.\n"); 70 goto fail; 71 } 72 73 r.name = ctx_name; 74 found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu); 75 76 if (!found) { 77 pr_err("Could not find context <%s>\n", ctx_name); 78 goto fail; 79 } 80 81 return r.dev; 82fail: 83 return NULL; 84} 85EXPORT_SYMBOL(msm_iommu_get_ctx); 86 87static void msm_iommu_reset(void __iomem *base, int ncb) 88{ 89 int ctx; 90 91 SET_RPUE(base, 0); 92 SET_RPUEIE(base, 0); 93 SET_ESRRESTORE(base, 0); 94 SET_TBE(base, 0); 95 SET_CR(base, 0); 96 SET_SPDMBE(base, 0); 97 SET_TESTBUSCR(base, 0); 98 SET_TLBRSW(base, 0); 99 SET_GLOBAL_TLBIALL(base, 0); 100 SET_RPU_ACR(base, 0); 101 SET_TLBLKCRWE(base, 1); 102 103 for (ctx = 0; ctx < ncb; ctx++) { 104 SET_BPRCOSH(base, ctx, 0); 105 SET_BPRCISH(base, ctx, 0); 106 SET_BPRCNSH(base, ctx, 0); 107 SET_BPSHCFG(base, ctx, 0); 108 SET_BPMTCFG(base, ctx, 0); 109 SET_ACTLR(base, ctx, 0); 110 SET_SCTLR(base, ctx, 0); 111 SET_FSRRESTORE(base, ctx, 0); 112 SET_TTBR0(base, ctx, 0); 113 SET_TTBR1(base, ctx, 0); 114 SET_TTBCR(base, ctx, 0); 115 SET_BFBCR(base, ctx, 0); 116 SET_PAR(base, ctx, 0); 117 SET_FAR(base, ctx, 0); 118 SET_CTX_TLBIALL(base, ctx, 0); 119 SET_TLBFLPTER(base, ctx, 0); 120 SET_TLBSLPTER(base, ctx, 0); 121 SET_TLBLKCR(base, ctx, 0); 122 SET_PRRR(base, ctx, 0); 123 SET_NMRR(base, ctx, 0); 124 SET_CONTEXTIDR(base, ctx, 0); 125 } 126} 127 128static int msm_iommu_probe(struct platform_device *pdev) 129{ 130 struct resource *r, *r2; 131 struct clk *iommu_clk; 132 struct clk *iommu_pclk; 133 struct msm_iommu_drvdata *drvdata; 134 struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data; 135 void __iomem *regs_base; 136 resource_size_t len; 137 int ret, irq, par; 138 139 if (pdev->id == -1) { 140 msm_iommu_root_dev = pdev; 141 return 0; 142 } 143 144 drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); 145 146 if (!drvdata) { 147 ret = -ENOMEM; 148 goto fail; 149 } 150 151 if (!iommu_dev) { 152 ret = -ENODEV; 153 goto fail; 154 } 155 156 iommu_pclk = clk_get(NULL, "smmu_pclk"); 157 if (IS_ERR(iommu_pclk)) { 158 ret = -ENODEV; 159 goto fail; 160 } 161 162 ret = clk_prepare_enable(iommu_pclk); 163 if (ret) 164 goto fail_enable; 165 166 iommu_clk = clk_get(&pdev->dev, "iommu_clk"); 167 168 if (!IS_ERR(iommu_clk)) { 169 if (clk_get_rate(iommu_clk) == 0) 170 clk_set_rate(iommu_clk, 1); 171 172 ret = clk_prepare_enable(iommu_clk); 173 if (ret) { 174 clk_put(iommu_clk); 175 goto fail_pclk; 176 } 177 } else 178 iommu_clk = NULL; 179 180 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "physbase"); 181 182 if (!r) { 183 ret = -ENODEV; 184 goto fail_clk; 185 } 186 187 len = resource_size(r); 188 189 r2 = request_mem_region(r->start, len, r->name); 190 if (!r2) { 191 pr_err("Could not request memory region: start=%p, len=%d\n", 192 (void *) r->start, len); 193 ret = -EBUSY; 194 goto fail_clk; 195 } 196 197 regs_base = ioremap(r2->start, len); 198 199 if (!regs_base) { 200 pr_err("Could not ioremap: start=%p, len=%d\n", 201 (void *) r2->start, len); 202 ret = -EBUSY; 203 goto fail_mem; 204 } 205 206 irq = platform_get_irq_byname(pdev, "secure_irq"); 207 if (irq < 0) { 208 ret = -ENODEV; 209 goto fail_io; 210 } 211 212 msm_iommu_reset(regs_base, iommu_dev->ncb); 213 214 SET_M(regs_base, 0, 1); 215 SET_PAR(regs_base, 0, 0); 216 SET_V2PCFG(regs_base, 0, 1); 217 SET_V2PPR(regs_base, 0, 0); 218 par = GET_PAR(regs_base, 0); 219 SET_V2PCFG(regs_base, 0, 0); 220 SET_M(regs_base, 0, 0); 221 222 if (!par) { 223 pr_err("%s: Invalid PAR value detected\n", iommu_dev->name); 224 ret = -ENODEV; 225 goto fail_io; 226 } 227 228 ret = request_irq(irq, msm_iommu_fault_handler, 0, 229 "msm_iommu_secure_irpt_handler", drvdata); 230 if (ret) { 231 pr_err("Request IRQ %d failed with ret=%d\n", irq, ret); 232 goto fail_io; 233 } 234 235 236 drvdata->pclk = iommu_pclk; 237 drvdata->clk = iommu_clk; 238 drvdata->base = regs_base; 239 drvdata->irq = irq; 240 drvdata->ncb = iommu_dev->ncb; 241 242 pr_info("device %s mapped at %p, irq %d with %d ctx banks\n", 243 iommu_dev->name, regs_base, irq, iommu_dev->ncb); 244 245 platform_set_drvdata(pdev, drvdata); 246 247 if (iommu_clk) 248 clk_disable(iommu_clk); 249 250 clk_disable(iommu_pclk); 251 252 return 0; 253fail_io: 254 iounmap(regs_base); 255fail_mem: 256 release_mem_region(r->start, len); 257fail_clk: 258 if (iommu_clk) { 259 clk_disable(iommu_clk); 260 clk_put(iommu_clk); 261 } 262fail_pclk: 263 clk_disable_unprepare(iommu_pclk); 264fail_enable: 265 clk_put(iommu_pclk); 266fail: 267 kfree(drvdata); 268 return ret; 269} 270 271static int msm_iommu_remove(struct platform_device *pdev) 272{ 273 struct msm_iommu_drvdata *drv = NULL; 274 275 drv = platform_get_drvdata(pdev); 276 if (drv) { 277 if (drv->clk) { 278 clk_unprepare(drv->clk); 279 clk_put(drv->clk); 280 } 281 clk_unprepare(drv->pclk); 282 clk_put(drv->pclk); 283 memset(drv, 0, sizeof(*drv)); 284 kfree(drv); 285 } 286 return 0; 287} 288 289static int msm_iommu_ctx_probe(struct platform_device *pdev) 290{ 291 struct msm_iommu_ctx_dev *c = pdev->dev.platform_data; 292 struct msm_iommu_drvdata *drvdata; 293 struct msm_iommu_ctx_drvdata *ctx_drvdata; 294 int i, ret; 295 296 if (!c || !pdev->dev.parent) 297 return -EINVAL; 298 299 drvdata = dev_get_drvdata(pdev->dev.parent); 300 if (!drvdata) 301 return -ENODEV; 302 303 ctx_drvdata = kzalloc(sizeof(*ctx_drvdata), GFP_KERNEL); 304 if (!ctx_drvdata) 305 return -ENOMEM; 306 307 ctx_drvdata->num = c->num; 308 ctx_drvdata->pdev = pdev; 309 310 INIT_LIST_HEAD(&ctx_drvdata->attached_elm); 311 platform_set_drvdata(pdev, ctx_drvdata); 312 313 ret = clk_prepare_enable(drvdata->pclk); 314 if (ret) 315 goto fail; 316 317 if (drvdata->clk) { 318 ret = clk_prepare_enable(drvdata->clk); 319 if (ret) { 320 clk_disable_unprepare(drvdata->pclk); 321 goto fail; 322 } 323 } 324 325 /* Program the M2V tables for this context */ 326 for (i = 0; i < MAX_NUM_MIDS; i++) { 327 int mid = c->mids[i]; 328 if (mid == -1) 329 break; 330 331 SET_M2VCBR_N(drvdata->base, mid, 0); 332 SET_CBACR_N(drvdata->base, c->num, 0); 333 334 /* Set VMID = 0 */ 335 SET_VMID(drvdata->base, mid, 0); 336 337 /* Set the context number for that MID to this context */ 338 SET_CBNDX(drvdata->base, mid, c->num); 339 340 /* Set MID associated with this context bank to 0*/ 341 SET_CBVMID(drvdata->base, c->num, 0); 342 343 /* Set the ASID for TLB tagging for this context */ 344 SET_CONTEXTIDR_ASID(drvdata->base, c->num, c->num); 345 346 /* Set security bit override to be Non-secure */ 347 SET_NSCFG(drvdata->base, mid, 3); 348 } 349 350 if (drvdata->clk) 351 clk_disable(drvdata->clk); 352 clk_disable(drvdata->pclk); 353 354 dev_info(&pdev->dev, "context %s using bank %d\n", c->name, c->num); 355 return 0; 356fail: 357 kfree(ctx_drvdata); 358 return ret; 359} 360 361static int msm_iommu_ctx_remove(struct platform_device *pdev) 362{ 363 struct msm_iommu_ctx_drvdata *drv = NULL; 364 drv = platform_get_drvdata(pdev); 365 if (drv) { 366 memset(drv, 0, sizeof(struct msm_iommu_ctx_drvdata)); 367 kfree(drv); 368 } 369 return 0; 370} 371 372static struct platform_driver msm_iommu_driver = { 373 .driver = { 374 .name = "msm_iommu", 375 }, 376 .probe = msm_iommu_probe, 377 .remove = msm_iommu_remove, 378}; 379 380static struct platform_driver msm_iommu_ctx_driver = { 381 .driver = { 382 .name = "msm_iommu_ctx", 383 }, 384 .probe = msm_iommu_ctx_probe, 385 .remove = msm_iommu_ctx_remove, 386}; 387 388static int __init msm_iommu_driver_init(void) 389{ 390 int ret; 391 ret = platform_driver_register(&msm_iommu_driver); 392 if (ret != 0) { 393 pr_err("Failed to register IOMMU driver\n"); 394 goto error; 395 } 396 397 ret = platform_driver_register(&msm_iommu_ctx_driver); 398 if (ret != 0) { 399 platform_driver_unregister(&msm_iommu_driver); 400 pr_err("Failed to register IOMMU context driver\n"); 401 goto error; 402 } 403 404error: 405 return ret; 406} 407 408static void __exit msm_iommu_driver_exit(void) 409{ 410 platform_driver_unregister(&msm_iommu_ctx_driver); 411 platform_driver_unregister(&msm_iommu_driver); 412} 413 414subsys_initcall(msm_iommu_driver_init); 415module_exit(msm_iommu_driver_exit); 416 417MODULE_LICENSE("GPL v2"); 418MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");