linux/drivers/iommu/msm_iommu_dev.c
<<
>>
Prefs
   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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.