|  | /* | 
|  | * IPMMU/IPMMUI | 
|  | * Copyright (C) 2012  Hideki EIRAKU | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License as published by | 
|  | * the Free Software Foundation; version 2 of the License. | 
|  | */ | 
|  |  | 
|  | #include <linux/err.h> | 
|  | #include <linux/export.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/platform_data/sh_ipmmu.h> | 
|  | #include "shmobile-ipmmu.h" | 
|  |  | 
|  | #define IMCTR1 0x000 | 
|  | #define IMCTR2 0x004 | 
|  | #define IMASID 0x010 | 
|  | #define IMTTBR 0x014 | 
|  | #define IMTTBCR 0x018 | 
|  |  | 
|  | #define IMCTR1_TLBEN (1 << 0) | 
|  | #define IMCTR1_FLUSH (1 << 1) | 
|  |  | 
|  | static void ipmmu_reg_write(struct shmobile_ipmmu *ipmmu, unsigned long reg_off, | 
|  | unsigned long data) | 
|  | { | 
|  | iowrite32(data, ipmmu->ipmmu_base + reg_off); | 
|  | } | 
|  |  | 
|  | void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu) | 
|  | { | 
|  | if (!ipmmu) | 
|  | return; | 
|  |  | 
|  | mutex_lock(&ipmmu->flush_lock); | 
|  | if (ipmmu->tlb_enabled) | 
|  | ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN); | 
|  | else | 
|  | ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH); | 
|  | mutex_unlock(&ipmmu->flush_lock); | 
|  | } | 
|  |  | 
|  | void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size, | 
|  | int asid) | 
|  | { | 
|  | if (!ipmmu) | 
|  | return; | 
|  |  | 
|  | mutex_lock(&ipmmu->flush_lock); | 
|  | switch (size) { | 
|  | default: | 
|  | ipmmu->tlb_enabled = 0; | 
|  | break; | 
|  | case 0x2000: | 
|  | ipmmu_reg_write(ipmmu, IMTTBCR, 1); | 
|  | ipmmu->tlb_enabled = 1; | 
|  | break; | 
|  | case 0x1000: | 
|  | ipmmu_reg_write(ipmmu, IMTTBCR, 2); | 
|  | ipmmu->tlb_enabled = 1; | 
|  | break; | 
|  | case 0x800: | 
|  | ipmmu_reg_write(ipmmu, IMTTBCR, 3); | 
|  | ipmmu->tlb_enabled = 1; | 
|  | break; | 
|  | case 0x400: | 
|  | ipmmu_reg_write(ipmmu, IMTTBCR, 4); | 
|  | ipmmu->tlb_enabled = 1; | 
|  | break; | 
|  | case 0x200: | 
|  | ipmmu_reg_write(ipmmu, IMTTBCR, 5); | 
|  | ipmmu->tlb_enabled = 1; | 
|  | break; | 
|  | case 0x100: | 
|  | ipmmu_reg_write(ipmmu, IMTTBCR, 6); | 
|  | ipmmu->tlb_enabled = 1; | 
|  | break; | 
|  | case 0x80: | 
|  | ipmmu_reg_write(ipmmu, IMTTBCR, 7); | 
|  | ipmmu->tlb_enabled = 1; | 
|  | break; | 
|  | } | 
|  | ipmmu_reg_write(ipmmu, IMTTBR, phys); | 
|  | ipmmu_reg_write(ipmmu, IMASID, asid); | 
|  | mutex_unlock(&ipmmu->flush_lock); | 
|  | } | 
|  |  | 
|  | static int ipmmu_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct shmobile_ipmmu *ipmmu; | 
|  | struct resource *res; | 
|  | struct shmobile_ipmmu_platform_data *pdata = pdev->dev.platform_data; | 
|  |  | 
|  | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | if (!res) { | 
|  | dev_err(&pdev->dev, "cannot get platform resources\n"); | 
|  | return -ENOENT; | 
|  | } | 
|  | ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL); | 
|  | if (!ipmmu) { | 
|  | dev_err(&pdev->dev, "cannot allocate device data\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  | mutex_init(&ipmmu->flush_lock); | 
|  | ipmmu->dev = &pdev->dev; | 
|  | ipmmu->ipmmu_base = devm_ioremap_nocache(&pdev->dev, res->start, | 
|  | resource_size(res)); | 
|  | if (!ipmmu->ipmmu_base) { | 
|  | dev_err(&pdev->dev, "ioremap_nocache failed\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  | ipmmu->dev_names = pdata->dev_names; | 
|  | ipmmu->num_dev_names = pdata->num_dev_names; | 
|  | platform_set_drvdata(pdev, ipmmu); | 
|  | ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */ | 
|  | ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */ | 
|  | ipmmu_iommu_init(ipmmu); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct platform_driver ipmmu_driver = { | 
|  | .probe = ipmmu_probe, | 
|  | .driver = { | 
|  | .owner = THIS_MODULE, | 
|  | .name = "ipmmu", | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static int __init ipmmu_init(void) | 
|  | { | 
|  | return platform_driver_register(&ipmmu_driver); | 
|  | } | 
|  | subsys_initcall(ipmmu_init); |