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