| /* |
| * 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; |
| |
| spin_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); |
| spin_unlock(&ipmmu->flush_lock); |
| } |
| |
| void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size, |
| int asid) |
| { |
| if (!ipmmu) |
| return; |
| |
| spin_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); |
| spin_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; |
| } |
| spin_lock_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 */ |
| return ipmmu_iommu_init(ipmmu); |
| } |
| |
| 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); |