Skip to content

Commit

Permalink
iommu/rockchip: Add missing set_platform_dma_ops callback
Browse files Browse the repository at this point in the history
Similar to exynos, we need a set_platform_dma_ops() callback for proper
operation on ARM 32 bit after recent changes in the IOMMU framework
(detach ops removal). But also the use of a NULL domain is confusing.

Rework the code to add support for IOMMU_DOMAIN_IDENTITY and a singleton
rk_identity_domain which is assigned to domain when using an identity
mapping rather than "detaching". This makes the code easier to reason about.

Signed-off-by: Steven Price <steven.price@arm.com>
Acked-by: Heiko Stuebner <heiko@sntech.de>
Tested-by: Heiko Stuebner <heiko@sntech.de>
Link: https://lore.kernel.org/r/20230331095154.2671129-1-steven.price@arm.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
  • Loading branch information
Steven Price authored and Joerg Roedel committed Apr 13, 2023
1 parent e8d018d commit 25c2325
Showing 1 changed file with 47 additions and 14 deletions.
61 changes: 47 additions & 14 deletions drivers/iommu/rockchip-iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ struct rk_iommudata {

static struct device *dma_dev;
static const struct rk_iommu_ops *rk_ops;
static struct iommu_domain rk_identity_domain;

static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
unsigned int count)
Expand Down Expand Up @@ -646,7 +647,7 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
* Ignore the return code, though, since we always zap cache
* and clear the page fault anyway.
*/
if (iommu->domain)
if (iommu->domain != &rk_identity_domain)
report_iommu_fault(iommu->domain, iommu->dev, iova,
flags);
else
Expand Down Expand Up @@ -980,26 +981,27 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
return ret;
}

static void rk_iommu_detach_device(struct iommu_domain *domain,
struct device *dev)
static int rk_iommu_identity_attach(struct iommu_domain *identity_domain,
struct device *dev)
{
struct rk_iommu *iommu;
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
struct rk_iommu_domain *rk_domain;
unsigned long flags;
int ret;

/* Allow 'virtual devices' (eg drm) to detach from domain */
iommu = rk_iommu_from_dev(dev);
if (!iommu)
return;
return -ENODEV;

rk_domain = to_rk_domain(iommu->domain);

dev_dbg(dev, "Detaching from iommu domain\n");

/* iommu already detached */
if (iommu->domain != domain)
return;
if (iommu->domain == identity_domain)
return 0;

iommu->domain = NULL;
iommu->domain = identity_domain;

spin_lock_irqsave(&rk_domain->iommus_lock, flags);
list_del_init(&iommu->node);
Expand All @@ -1011,8 +1013,31 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
rk_iommu_disable(iommu);
pm_runtime_put(iommu->dev);
}

return 0;
}

static void rk_iommu_identity_free(struct iommu_domain *domain)
{
}

static struct iommu_domain_ops rk_identity_ops = {
.attach_dev = rk_iommu_identity_attach,
.free = rk_iommu_identity_free,
};

static struct iommu_domain rk_identity_domain = {
.type = IOMMU_DOMAIN_IDENTITY,
.ops = &rk_identity_ops,
};

#ifdef CONFIG_ARM
static void rk_iommu_set_platform_dma(struct device *dev)
{
WARN_ON(rk_iommu_identity_attach(&rk_identity_domain, dev));
}
#endif

static int rk_iommu_attach_device(struct iommu_domain *domain,
struct device *dev)
{
Expand All @@ -1035,8 +1060,9 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
if (iommu->domain == domain)
return 0;

if (iommu->domain)
rk_iommu_detach_device(iommu->domain, dev);
ret = rk_iommu_identity_attach(&rk_identity_domain, dev);
if (ret)
return ret;

iommu->domain = domain;

Expand All @@ -1050,7 +1076,7 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,

ret = rk_iommu_enable(iommu);
if (ret)
rk_iommu_detach_device(iommu->domain, dev);
WARN_ON(rk_iommu_identity_attach(&rk_identity_domain, dev));

pm_runtime_put(iommu->dev);

Expand All @@ -1061,6 +1087,9 @@ static struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
{
struct rk_iommu_domain *rk_domain;

if (type == IOMMU_DOMAIN_IDENTITY)
return &rk_identity_domain;

if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
return NULL;

Expand Down Expand Up @@ -1176,6 +1205,7 @@ static int rk_iommu_of_xlate(struct device *dev,
iommu_dev = of_find_device_by_node(args->np);

data->iommu = platform_get_drvdata(iommu_dev);
data->iommu->domain = &rk_identity_domain;
dev_iommu_priv_set(dev, data);

platform_device_put(iommu_dev);
Expand All @@ -1188,6 +1218,9 @@ static const struct iommu_ops rk_iommu_ops = {
.probe_device = rk_iommu_probe_device,
.release_device = rk_iommu_release_device,
.device_group = rk_iommu_device_group,
#ifdef CONFIG_ARM
.set_platform_dma_ops = rk_iommu_set_platform_dma,
#endif
.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
.of_xlate = rk_iommu_of_xlate,
.default_domain_ops = &(const struct iommu_domain_ops) {
Expand Down Expand Up @@ -1343,7 +1376,7 @@ static int __maybe_unused rk_iommu_suspend(struct device *dev)
{
struct rk_iommu *iommu = dev_get_drvdata(dev);

if (!iommu->domain)
if (iommu->domain == &rk_identity_domain)
return 0;

rk_iommu_disable(iommu);
Expand All @@ -1354,7 +1387,7 @@ static int __maybe_unused rk_iommu_resume(struct device *dev)
{
struct rk_iommu *iommu = dev_get_drvdata(dev);

if (!iommu->domain)
if (iommu->domain == &rk_identity_domain)
return 0;

return rk_iommu_enable(iommu);
Expand Down

0 comments on commit 25c2325

Please sign in to comment.