@@ -4,6 +4,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/clk.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -87,10 +88,17 @@ struct rk_iommu_domain {
struct iommu_domain domain;
};
+/* list of clocks required by IOMMU */
+static const char * const rk_iommu_clocks[] = {
+ "aclk", "iface",
+};
+
struct rk_iommu {
struct device *dev;
void __iomem **bases;
int num_mmu;
+ struct clk_bulk_data *clocks;
+ int num_clocks;
bool reset_disabled;
struct iommu_device iommu;
struct list_head node; /* entry in rk_iommu_domain.iommus */
@@ -506,6 +514,8 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
irqreturn_t ret = IRQ_NONE;
int i;
+ WARN_ON(clk_bulk_enable(iommu->num_clocks, iommu->clocks));
+
for (i = 0; i < iommu->num_mmu; i++) {
int_status = rk_iommu_read(iommu->bases[i], RK_MMU_INT_STATUS);
if (int_status == 0)
@@ -552,6 +562,8 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
rk_iommu_write(iommu->bases[i], RK_MMU_INT_CLEAR, int_status);
}
+ clk_bulk_disable(iommu->num_clocks, iommu->clocks);
+
return ret;
}
@@ -594,7 +606,9 @@ static void rk_iommu_zap_iova(struct rk_iommu_domain *rk_domain,
list_for_each(pos, &rk_domain->iommus) {
struct rk_iommu *iommu;
iommu = list_entry(pos, struct rk_iommu, node);
+ WARN_ON(clk_bulk_enable(iommu->num_clocks, iommu->clocks));
rk_iommu_zap_lines(iommu, iova, size);
+ clk_bulk_disable(iommu->num_clocks, iommu->clocks);
}
spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
}
@@ -823,10 +837,14 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
if (!iommu)
return 0;
- ret = rk_iommu_enable_stall(iommu);
+ ret = clk_bulk_enable(iommu->num_clocks, iommu->clocks);
if (ret)
return ret;
+ ret = rk_iommu_enable_stall(iommu);
+ if (ret)
+ goto out_disable_clocks;
+
ret = rk_iommu_force_reset(iommu);
if (ret)
goto out_disable_stall;
@@ -852,6 +870,8 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
out_disable_stall:
rk_iommu_disable_stall(iommu);
+out_disable_clocks:
+ clk_bulk_disable(iommu->num_clocks, iommu->clocks);
return ret;
}
@@ -873,6 +893,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
/* Ignore error while disabling, just keep going */
+ WARN_ON(clk_bulk_enable(iommu->num_clocks, iommu->clocks));
rk_iommu_enable_stall(iommu);
rk_iommu_disable_paging(iommu);
for (i = 0; i < iommu->num_mmu; i++) {
@@ -880,6 +901,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 0);
}
rk_iommu_disable_stall(iommu);
+ clk_bulk_disable(iommu->num_clocks, iommu->clocks);
iommu->domain = NULL;
@@ -1172,15 +1194,37 @@ static int rk_iommu_probe(struct platform_device *pdev)
iommu->reset_disabled = device_property_read_bool(dev,
"rockchip,disable-mmu-reset");
- err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev));
+ iommu->num_clocks = ARRAY_SIZE(rk_iommu_clocks);
+ iommu->clocks = devm_kcalloc(iommu->dev, iommu->num_clocks,
+ sizeof(*iommu->clocks), GFP_KERNEL);
+ if (!iommu->clocks)
+ return -ENOMEM;
+
+ for (i = 0; i < iommu->num_clocks; ++i)
+ iommu->clocks[i].id = rk_iommu_clocks[i];
+
+ err = devm_clk_bulk_get(iommu->dev, iommu->num_clocks, iommu->clocks);
+ if (err)
+ return err;
+
+ err = clk_bulk_prepare(iommu->num_clocks, iommu->clocks);
if (err)
return err;
+ err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev));
+ if (err)
+ goto err_unprepare_clocks;
+
iommu_device_set_ops(&iommu->iommu, &rk_iommu_ops);
err = iommu_device_register(&iommu->iommu);
if (err)
- iommu_device_sysfs_remove(&iommu->iommu);
+ goto err_remove_sysfs;
+ return 0;
+err_remove_sysfs:
+ iommu_device_sysfs_remove(&iommu->iommu);
+err_unprepare_clocks:
+ clk_bulk_unprepare(iommu->num_clocks, iommu->clocks);
return err;
}