1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org>
4 */
5
6#define LOG_CATEGORY UCLASS_IOMMU
7
8#include <common.h>
9#include <dm.h>
10#include <iommu.h>
11#include <malloc.h>
12#include <phys2bus.h>
13#include <asm/io.h>
14
15#if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA))
16
17#if CONFIG_IS_ENABLED(PCI)
18static int dev_pci_iommu_enable(struct udevice *dev)
19{
20	struct udevice *parent = dev->parent;
21	struct udevice *dev_iommu;
22	u32 *iommu_map;
23	u32 iommu_map_mask, length, phandle, rid, rid_base;
24	int i, count, len, ret;
25
26	while (parent) {
27		len = dev_read_size(parent, "iommu-map");
28		if (len > 0)
29			break;
30		parent = parent->parent;
31	}
32
33	if (len <= 0)
34		return 0;
35
36	iommu_map = malloc(len);
37	if (!iommu_map)
38		return -ENOMEM;
39
40	count = len / sizeof(u32);
41	ret = dev_read_u32_array(parent, "iommu-map", iommu_map, count);
42	if (ret < 0) {
43		free(iommu_map);
44		return 0;
45	}
46
47	iommu_map_mask = dev_read_u32_default(parent, "iommu-map-mask", ~0);
48	rid = (dm_pci_get_bdf(dev) >> 8) & iommu_map_mask;
49
50	/* Loop over entries until mapping is found. */
51	for (i = 0; i < count; i += 4) {
52		rid_base = iommu_map[i];
53		phandle = iommu_map[i + 1];
54		length = iommu_map[i + 3];
55
56		if (rid < rid_base || rid >= rid_base + length)
57			continue;
58
59		ret = uclass_get_device_by_phandle_id(UCLASS_IOMMU, phandle,
60						      &dev_iommu);
61		if (ret) {
62			debug("%s: uclass_get_device_by_ofnode failed: %d\n",
63			      __func__, ret);
64			free(iommu_map);
65			return ret;
66		}
67		dev->iommu = dev_iommu;
68		break;
69	}
70
71	free(iommu_map);
72	return 0;
73}
74#endif
75
76int dev_iommu_enable(struct udevice *dev)
77{
78	struct ofnode_phandle_args args;
79	struct udevice *dev_iommu;
80	const struct iommu_ops *ops;
81	int i, count, ret = 0;
82
83	count = dev_count_phandle_with_args(dev, "iommus",
84					    "#iommu-cells", 0);
85	for (i = 0; i < count; i++) {
86		ret = dev_read_phandle_with_args(dev, "iommus",
87						 "#iommu-cells", 0, i, &args);
88		if (ret) {
89			log_err("%s: Failed to parse 'iommus' property for '%s': %d\n",
90				__func__, dev->name, ret);
91			return ret;
92		}
93
94		ret = uclass_get_device_by_ofnode(UCLASS_IOMMU, args.node,
95						  &dev_iommu);
96		if (ret) {
97			log_err("%s: Failed to find IOMMU device for '%s': %d\n",
98				__func__, dev->name, ret);
99			return ret;
100		}
101		dev->iommu = dev_iommu;
102
103		if (dev->parent && dev->parent->iommu == dev_iommu)
104			continue;
105
106		ops = device_get_ops(dev->iommu);
107		if (ops && ops->connect) {
108			ret = ops->connect(dev);
109			if (ret) {
110				log_err("%s: Failed to connect '%s' to IOMMU '%s': %d\n",
111					__func__, dev->name, dev->iommu->name, ret);
112				return ret;
113			}
114		}
115	}
116
117#if CONFIG_IS_ENABLED(PCI)
118	if (count < 0 && device_is_on_pci_bus(dev))
119		return dev_pci_iommu_enable(dev);
120#endif
121
122	return 0;
123}
124#endif
125
126dma_addr_t dev_iommu_dma_map(struct udevice *dev, void *addr, size_t size)
127{
128	const struct iommu_ops *ops;
129
130	if (dev->iommu) {
131		ops = device_get_ops(dev->iommu);
132		if (ops && ops->map)
133			return ops->map(dev->iommu, addr, size);
134	}
135
136	return dev_phys_to_bus(dev, virt_to_phys(addr));
137}
138
139void dev_iommu_dma_unmap(struct udevice *dev, dma_addr_t addr, size_t size)
140{
141	const struct iommu_ops *ops;
142
143	if (dev->iommu) {
144		ops = device_get_ops(dev->iommu);
145		if (ops && ops->unmap)
146			ops->unmap(dev->iommu, addr, size);
147	}
148}
149
150UCLASS_DRIVER(iommu) = {
151	.id		= UCLASS_IOMMU,
152	.name		= "iommu",
153};
154