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
  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                platform_set_drvdata(pdev, NULL);
 286        }
 287        return 0;
 288}
 289
 290static int msm_iommu_ctx_probe(struct platform_device *pdev)
 291{
 292        struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
 293        struct msm_iommu_drvdata *drvdata;
 294        struct msm_iommu_ctx_drvdata *ctx_drvdata;
 295        int i, ret;
 296
 297        if (!c || !pdev->dev.parent)
 298                return -EINVAL;
 299
 300        drvdata = dev_get_drvdata(pdev->dev.parent);
 301        if (!drvdata)
 302                return -ENODEV;
 303
 304        ctx_drvdata = kzalloc(sizeof(*ctx_drvdata), GFP_KERNEL);
 305        if (!ctx_drvdata)
 306                return -ENOMEM;
 307
 308        ctx_drvdata->num = c->num;
 309        ctx_drvdata->pdev = pdev;
 310
 311        INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
 312        platform_set_drvdata(pdev, ctx_drvdata);
 313
 314        ret = clk_prepare_enable(drvdata->pclk);
 315        if (ret)
 316                goto fail;
 317
 318        if (drvdata->clk) {
 319                ret = clk_prepare_enable(drvdata->clk);
 320                if (ret) {
 321                        clk_disable_unprepare(drvdata->pclk);
 322                        goto fail;
 323                }
 324        }
 325
 326        /* Program the M2V tables for this context */
 327        for (i = 0; i < MAX_NUM_MIDS; i++) {
 328                int mid = c->mids[i];
 329                if (mid == -1)
 330                        break;
 331
 332                SET_M2VCBR_N(drvdata->base, mid, 0);
 333                SET_CBACR_N(drvdata->base, c->num, 0);
 334
 335                /* Set VMID = 0 */
 336                SET_VMID(drvdata->base, mid, 0);
 337
 338                /* Set the context number for that MID to this context */
 339                SET_CBNDX(drvdata->base, mid, c->num);
 340
 341                /* Set MID associated with this context bank to 0*/
 342                SET_CBVMID(drvdata->base, c->num, 0);
 343
 344                /* Set the ASID for TLB tagging for this context */
 345                SET_CONTEXTIDR_ASID(drvdata->base, c->num, c->num);
 346
 347                /* Set security bit override to be Non-secure */
 348                SET_NSCFG(drvdata->base, mid, 3);
 349        }
 350
 351        if (drvdata->clk)
 352                clk_disable(drvdata->clk);
 353        clk_disable(drvdata->pclk);
 354
 355        dev_info(&pdev->dev, "context %s using bank %d\n", c->name, c->num);
 356        return 0;
 357fail:
 358        kfree(ctx_drvdata);
 359        return ret;
 360}
 361
 362static int msm_iommu_ctx_remove(struct platform_device *pdev)
 363{
 364        struct msm_iommu_ctx_drvdata *drv = NULL;
 365        drv = platform_get_drvdata(pdev);
 366        if (drv) {
 367                memset(drv, 0, sizeof(struct msm_iommu_ctx_drvdata));
 368                kfree(drv);
 369                platform_set_drvdata(pdev, NULL);
 370        }
 371        return 0;
 372}
 373
 374static struct platform_driver msm_iommu_driver = {
 375        .driver = {
 376                .name   = "msm_iommu",
 377        },
 378        .probe          = msm_iommu_probe,
 379        .remove         = msm_iommu_remove,
 380};
 381
 382static struct platform_driver msm_iommu_ctx_driver = {
 383        .driver = {
 384                .name   = "msm_iommu_ctx",
 385        },
 386        .probe          = msm_iommu_ctx_probe,
 387        .remove         = msm_iommu_ctx_remove,
 388};
 389
 390static int __init msm_iommu_driver_init(void)
 391{
 392        int ret;
 393        ret = platform_driver_register(&msm_iommu_driver);
 394        if (ret != 0) {
 395                pr_err("Failed to register IOMMU driver\n");
 396                goto error;
 397        }
 398
 399        ret = platform_driver_register(&msm_iommu_ctx_driver);
 400        if (ret != 0) {
 401                platform_driver_unregister(&msm_iommu_driver);
 402                pr_err("Failed to register IOMMU context driver\n");
 403                goto error;
 404        }
 405
 406error:
 407        return ret;
 408}
 409
 410static void __exit msm_iommu_driver_exit(void)
 411{
 412        platform_driver_unregister(&msm_iommu_ctx_driver);
 413        platform_driver_unregister(&msm_iommu_driver);
 414}
 415
 416subsys_initcall(msm_iommu_driver_init);
 417module_exit(msm_iommu_driver_exit);
 418
 419MODULE_LICENSE("GPL v2");
 420MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");
 421
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.