1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org>
4 */
5
6#include <dm.h>
7#include <iommu.h>
8#include <lmb.h>
9#include <asm/io.h>
10#include <linux/sizes.h>
11
12#define IOMMU_PAGE_SIZE		SZ_4K
13
14struct sandbox_iommu_priv {
15	struct lmb lmb;
16};
17
18static dma_addr_t sandbox_iommu_map(struct udevice *dev, void *addr,
19				    size_t size)
20{
21	struct sandbox_iommu_priv *priv = dev_get_priv(dev);
22	phys_addr_t paddr, dva;
23	phys_size_t psize, off;
24
25	paddr = ALIGN_DOWN(virt_to_phys(addr), IOMMU_PAGE_SIZE);
26	off = virt_to_phys(addr) - paddr;
27	psize = ALIGN(size + off, IOMMU_PAGE_SIZE);
28
29	dva = lmb_alloc(&priv->lmb, psize, IOMMU_PAGE_SIZE);
30
31	return dva + off;
32}
33
34static void sandbox_iommu_unmap(struct udevice *dev, dma_addr_t addr,
35				size_t size)
36{
37	struct sandbox_iommu_priv *priv = dev_get_priv(dev);
38	phys_addr_t dva;
39	phys_size_t psize;
40
41	dva = ALIGN_DOWN(addr, IOMMU_PAGE_SIZE);
42	psize = size + (addr - dva);
43	psize = ALIGN(psize, IOMMU_PAGE_SIZE);
44
45	lmb_free(&priv->lmb, dva, psize);
46}
47
48static struct iommu_ops sandbox_iommu_ops = {
49	.map = sandbox_iommu_map,
50	.unmap = sandbox_iommu_unmap,
51};
52
53static int sandbox_iommu_probe(struct udevice *dev)
54{
55	struct sandbox_iommu_priv *priv = dev_get_priv(dev);
56
57	lmb_init(&priv->lmb);
58	lmb_add(&priv->lmb, 0x89abc000, SZ_16K);
59
60	return 0;
61}
62
63static const struct udevice_id sandbox_iommu_ids[] = {
64	{ .compatible = "sandbox,iommu" },
65	{ /* sentinel */ }
66};
67
68U_BOOT_DRIVER(sandbox_iommu) = {
69	.name = "sandbox_iommu",
70	.id = UCLASS_IOMMU,
71	.of_match = sandbox_iommu_ids,
72	.priv_auto = sizeof(struct sandbox_iommu_priv),
73	.ops = &sandbox_iommu_ops,
74	.probe = sandbox_iommu_probe,
75};
76