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 "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>");
 419
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.