static struct kmem_cache *lv2table_kmem_cache;
+static struct sysmmu_drvdata *sysmmu_drvdata_list;
+
struct exynos_client {
struct list_head list;
struct device_node *master_np;
return;
}
+static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
+
+static int get_hw_version(struct device *dev, void __iomem *sfrbase)
+{
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to runtime pm get(%d)\n", ret);
+ return ret;
+ }
+ ret = MMU_RAW_VER(__raw_readl(sfrbase + REG_MMU_VERSION));
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static struct iommu_ops exynos_iommu_ops;
static int __init exynos_sysmmu_probe(struct platform_device *pdev)
{
- /* Dummy */
+ int irq, ret;
+ struct device *dev = &pdev->dev;
+ struct sysmmu_drvdata *data;
+ struct resource *res;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "Failed to get resource info\n");
+ return -ENOENT;
+ }
+
+ data->sfrbase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(data->sfrbase))
+ return PTR_ERR(data->sfrbase);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(dev, "Unable to find IRQ resource\n");
+ return irq;
+ }
+
+ ret = devm_request_irq(dev, irq, exynos_sysmmu_irq, 0,
+ dev_name(dev), data);
+ if (ret) {
+ dev_err(dev, "Unabled to register handler of irq %d\n", irq);
+ return ret;
+ }
+
+ data->clk = devm_clk_get(dev, "aclk");
+ if (IS_ERR(data->clk)) {
+ dev_err(dev, "Failed to get clock!\n");
+ return PTR_ERR(data->clk);
+ } else {
+ ret = clk_prepare(data->clk);
+ if (ret) {
+ dev_err(dev, "Failed to prepare clk\n");
+ return ret;
+ }
+ }
+
+ data->sysmmu = dev;
+ spin_lock_init(&data->lock);
+ if (!sysmmu_drvdata_list) {
+ sysmmu_drvdata_list = data;
+ } else {
+ data->next = sysmmu_drvdata_list->next;
+ sysmmu_drvdata_list->next = data;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ pm_runtime_enable(dev);
+
+ data->version = get_hw_version(dev, data->sfrbase);
+
+ /* TODO: Parsing Device Tree for properties */
+
+ iommu_device_set_ops(&data->iommu, &exynos_iommu_ops);
+ iommu_device_set_fwnode(&data->iommu, &dev->of_node->fwnode);
+
+ ret = iommu_device_register(&data->iommu);
+ if (ret) {
+ dev_err(dev, "Failed to register device\n");
+ return ret;
+ }
+
+ dev_info(data->sysmmu, "is probed. Version %d.%d.%d\n",
+ MMU_MAJ_VER(data->version),
+ MMU_MIN_VER(data->version),
+ MMU_REV_VER(data->version));
return 0;
}
#define NUM_LV2ENTRIES (SECT_SIZE / SPAGE_SIZE)
#define LV2TABLE_SIZE (NUM_LV2ENTRIES * sizeof(sysmmu_pte_t))
+#define REG_MMU_CTRL 0x000
+#define REG_MMU_CFG 0x004
+#define REG_MMU_STATUS 0x008
+#define REG_MMU_VERSION 0x034
+
+#define MMU_MAJ_VER(val) ((val) >> 11)
+#define MMU_MIN_VER(val) ((val >> 4) & 0x7F)
+#define MMU_REV_VER(val) ((val) & 0xF)
+#define MMU_RAW_VER(reg) (((reg) >> 17) & 0x7FFF) /* upper 15 bits */
+
+#define MAKE_MMU_VER(maj, min) ((((maj) & 0xF) << 11) | \
+ (((min) & 0x7F) << 4))
/*
* This structure exynos specific generalization of struct iommu_domain.
* It contains list of all master devices represented by owner, which has
struct device *sysmmu; /* SYSMMU controller device */
void __iomem *sfrbase; /* our registers */
struct clk *clk; /* SYSMMU's clock */
+ struct iommu_device iommu; /* IOMMU core handle */
int activations; /* number of calls to sysmmu_enable */
int runtime_active; /* Runtime PM activated count from master */
spinlock_t lock; /* lock for modyfying state */
phys_addr_t pgtable; /* assigned page table structure */
- unsigned int version; /* our version */
+ int version; /* our version */
};
struct exynos_vm_region {