| /* | 
 |  * 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); |