// SPDX-License-Identifier: GPL-2.0
/*
* IOMMU API for Renesas VMSA-compatible IPMMU
* Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
* Copyright (C) 2014-2020 Renesas Electronics Corporation
*/
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/io-pgtable.h>
#include <linux/iommu.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA)
#include <asm/dma-iommu.h>
#else
#define arm_iommu_create_mapping(...) NULL
#define arm_iommu_attach_device(...) -ENODEV
#define arm_iommu_release_mapping(...) do {} while (0)
#endif
#define IPMMU_CTX_MAX 16U
#define IPMMU_CTX_INVALID -1
#define IPMMU_UTLB_MAX 64U
struct ipmmu_features {
bool use_ns_alias_offset;
bool has_cache_leaf_nodes;
unsigned int number_of_contexts;
unsigned int num_utlbs;
bool setup_imbuscr;
bool twobit_imttbcr_sl0;
bool reserved_context;
bool cache_snoop;
unsigned int ctx_offset_base;
unsigned int ctx_offset_stride;
unsigned int utlb_offset_base;
};
struct ipmmu_vmsa_device {
struct device *dev;
void __iomem *base;
struct iommu_device iommu;
struct ipmmu_vmsa_device *root;
const struct ipmmu_features *features;
unsigned int num_ctx;
spinlock_t lock; /* Protects ctx and domains[] */
DECLARE_BITMAP(ctx, IPMMU_CTX_MAX);
struct ipmmu_vmsa_domain *domains[IPMMU_CTX_MAX];
s8 utlb_ctx[IPMMU_UTLB_MAX];
struct dma_iommu_mapping *mapping;
};
struct ipmmu_vmsa_domain {
struct ipmmu_vmsa_device *mmu;
struct iommu_domain io_domain;
struct io_pgtable_cfg cfg;
struct io_pgtable_ops *iop;
unsigned int context_id;
struct mutex mutex; /* Protects mappings */
};
static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom)
{
return container_of(dom, struct ipmmu_vmsa_domain, io_domain);
}
static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev)
{
return dev_iommu_priv_get(dev);
}
#define TLB_LOOP_TIMEOUT 100 /* 100us */
/* -----------------------------------------------------------------------------
* Registers Definition
*/
#define IM_NS_ALIAS_OFFSET 0x800
/* MMU "context" registers */
#define IMCTR 0x0000 /* R-Car Gen2/3 */
#define IMCTR_INTEN (1 << 2) /* R-Car Gen2/3 */
#define IMCTR_FLUSH (1 << 1) /* R-Car Gen2/3 */
#define IMCTR_MMUEN (1 << 0) /* R-Car Gen2/3 */
#define IMTTBCR 0x0008 /* R-Car Gen2/3 */
#define IMTTBCR_EAE (1 << 31) /* R-Car Gen2/3 */
#define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12) /* R-Car Gen2 only */
#define IMTTBCR_ORGN0_WB_WA (1 << 10) /* R-Car Gen2 only */
#define IMTTBCR_IRGN0_WB_WA (1 << 8) /* R-Car Gen2 only */
#define IMTTBCR_SL0_TWOBIT_LVL_1 (2 << 6) /* R-Car Gen3 only */
#define IMTTBCR_SL0_LVL_1 (1 << 4) /* R-Car Gen2 only */
#define IMBUSCR 0x000c /* R-Car Gen2 only */
#define IMBUSCR_DVM (1 << 2) /* R-Car Gen2 only */
#define IMBUSCR_BUSSEL_MASK (3 << 0) /* R-Car Gen2 only */
#define IMTTLBR0 0x0010 /* R-Car Gen2/3 */
#define IMTTUBR0 0x0014 /* R-Car Gen2/3 */
#define IMSTR 0x0020 /* R-Car Gen2/3 */
#define IMSTR_MHIT (1 << 4) /* R-Car Gen2/3 */
#define IMSTR_ABORT (1 << 2) /* R-Car Gen2/3 */
#define IMSTR_PF (1 << 1) /* R-Car Gen2/3 */
#define IMSTR_TF (1 << 0) /* R-Car Gen2/3 */
#define IMMAIR0 0x0028 /* R-Car Gen2/3 */
#define IMELAR 0x0030 /* R-Car Gen2/3, IMEAR on R-Car Gen2 */
#define IMEUAR 0x0034 /* R-Car Gen3 only */
/* uTLB registers */
#define IMUCTR(n) ((n) < 32 ? IMUCTR0(n) : IMUCTR32(n))
#define IMUCTR0(n) (0x0300 + ((n) * 16)) /* R-Car Gen2/3 */
#define IMUCTR32(n) (0x0600 + (((n) - 32) * 16)) /* R-Car Gen3 only */
#define IMUCTR_TTSEL_MMU(n) ((n) << 4) /* R-Car Gen2/3 */
#define IMUCTR_FLUSH (1 << 1) /* R-Car Gen2/3 */
#define IMUCTR_MMUEN (1 << 0) /* R-Car Gen2/3 */
#define IMUASID(n) ((n) < 32 ? IMUASID0(n) : IMUASID32(n))
#define IMUASID0(n) (0x0308 + ((n) * 16)) /* R-Car Gen2/3 */
#define IMUASID32(n) (0x0608 + (((n) - 32) * 16)) /* R-Car Gen3 only */
/* -----------------------------------------------------------------------------
* Root device handling
*/
static struct platform_driver ipmmu_driver;
static bool ipmmu_is_root(struct ipmmu_vmsa_device *mmu)
{
return mmu->root == mmu;
}
static int __ipmmu_check_device(struct device *dev, void *data)
{
struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev);
struct ipmmu_vmsa_device **rootp = data;
if (ipmmu_is_root(mmu))
*rootp = mmu;
return 0;
}
static struct ipmmu_vmsa_device *ipmmu_find_root(void)
{
struct ipmmu_vmsa_device *root = NULL;
return driver_for_each_device(&ipmmu_driver.driver, NULL, &root,
__ipmmu_check_device) == 0 ? root : NULL;
}
/* -----------------------------------------------------------------------------
* Read/Write Access
*/
static u32 ipmmu_read(struct ipmmu_vmsa_device *mmu, unsigned int offset)
{
return ioread32(mmu->base + offset);
}
static void ipmmu_write(struct ipmmu_vmsa_device *mmu, unsigned int offset,
u32