summaryrefslogtreecommitdiffstats
path: root/configs/platform-v7a/patches/barebox-2019.12.0/0005-of-Read-dma_offset-from-device-tree.patch
blob: d46d44b75a3b2429fbac19a79c2a0f962956691b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
From: Sascha Hauer <s.hauer@pengutronix.de>
Date: Fri, 20 Dec 2019 12:32:23 +0100
Subject: [PATCH] of: Read dma_offset from device tree

This reads the dma-ranges property from the device tree and sets
dma_offset in the devices accordingly. The code is mostly taken
from the Kernel as of v5.5-rc1. of_dma_configure() is trimmed down
to the cases we want to support currently.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/of/address.c  | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/of/platform.c |  18 +++++++++
 include/of_address.h  |   9 +++++
 3 files changed, 136 insertions(+)

diff --git a/drivers/of/address.c b/drivers/of/address.c
index 4e12522a0a75..2020f5b7b1ed 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -458,6 +458,33 @@ bool of_can_translate_address(struct device_node *dev)
 }
 EXPORT_SYMBOL(of_can_translate_address);
 
+static struct device_node *__of_get_dma_parent(struct device_node *np)
+{
+	struct of_phandle_args args;
+	int ret, index;
+
+	index = of_property_match_string(np, "interconnect-names", "dma-mem");
+	if (index < 0)
+		return of_get_parent(np);
+
+	ret = of_parse_phandle_with_args(np, "interconnects",
+					 "#interconnect-cells",
+					 index, &args);
+	if (ret < 0)
+		return of_get_parent(np);
+
+	return args.np;
+}
+
+static struct device_node *of_get_next_dma_parent(struct device_node *np)
+{
+	struct device_node *parent;
+
+	parent = __of_get_dma_parent(np);
+
+	return parent;
+}
+
 const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
 		    unsigned int *flags)
 {
@@ -586,3 +613,85 @@ void __iomem *of_iomap(struct device_node *np, int index)
 	return IOMEM(res.start);
 }
 EXPORT_SYMBOL(of_iomap);
+
+/**
+ * of_dma_get_range - Get DMA range info
+ * @np:		device node to get DMA range info
+ * @dma_addr:	pointer to store initial DMA address of DMA range
+ * @paddr:	pointer to store initial CPU address of DMA range
+ * @size:	pointer to store size of DMA range
+ *
+ * Look in bottom up direction for the first "dma-ranges" property
+ * and parse it.
+ *  dma-ranges format:
+ *	DMA addr (dma_addr)	: naddr cells
+ *	CPU addr (phys_addr_t)	: pna cells
+ *	size			: nsize cells
+ *
+ * It returns -ENODEV if "dma-ranges" property was not found
+ * for this device in DT.
+ */
+int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size)
+{
+	struct device_node *node = np;
+	const __be32 *ranges = NULL;
+	int len, naddr, nsize, pna;
+	int ret = 0;
+	bool found_dma_ranges = false;
+	u64 dmaaddr;
+
+	while (node) {
+		ranges = of_get_property(node, "dma-ranges", &len);
+
+		/* Ignore empty ranges, they imply no translation required */
+		if (ranges && len > 0)
+			break;
+
+		/* Once we find 'dma-ranges', then a missing one is an error */
+		if (found_dma_ranges && !ranges) {
+			ret = -ENODEV;
+			goto out;
+		}
+		found_dma_ranges = true;
+
+		node = of_get_next_dma_parent(node);
+	}
+
+	if (!node || !ranges) {
+		pr_debug("no dma-ranges found for node(%pOF)\n", np);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	naddr = of_bus_n_addr_cells(node);
+	nsize = of_bus_n_size_cells(node);
+	pna = of_n_addr_cells(node);
+	if ((len / sizeof(__be32)) % (pna + naddr + nsize)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* dma-ranges format:
+	 * DMA addr	: naddr cells
+	 * CPU addr	: pna cells
+	 * size		: nsize cells
+	 */
+	dmaaddr = of_read_number(ranges, naddr);
+	*paddr = of_translate_dma_address(node, ranges + naddr);
+	if (*paddr == OF_BAD_ADDR) {
+		pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n",
+		       dmaaddr, np);
+		ret = -EINVAL;
+		goto out;
+	}
+	*dma_addr = dmaaddr;
+
+	*size = of_read_number(ranges + naddr + pna, nsize);
+
+	pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
+		 *dma_addr, *paddr, *size);
+
+out:
+
+	return ret;
+}
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index d3795d799a13..b1a7eb673064 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -74,6 +74,22 @@ static void of_device_make_bus_id(struct device_d *dev)
 	}
 }
 
+static void of_dma_configure(struct device_d *dev, struct device_node *np)
+{
+	u64 dma_addr, paddr, size = 0;
+	unsigned long offset;
+	int ret;
+
+	ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
+	if (ret < 0) {
+		dma_addr = offset = 0;
+	} else {
+		offset = paddr - dma_addr;
+	}
+
+	dev->dma_offset = offset;
+}
+
 /**
  * of_platform_device_create - Alloc, initialize and register an of_device
  * @np: pointer to node to create device for
@@ -148,6 +164,8 @@ struct device_d *of_platform_device_create(struct device_node *np,
 	dev->num_resources = num_reg;
 	of_device_make_bus_id(dev);
 
+	of_dma_configure(dev, np);
+
 	resinval = (-1);
 
 	debug("%s: register device %s, io=%pa\n",
diff --git a/include/of_address.h b/include/of_address.h
index ebf3ec2a2423..350ecaec827a 100644
--- a/include/of_address.h
+++ b/include/of_address.h
@@ -56,6 +56,9 @@ extern struct device_node *of_find_matching_node_by_address(
 	u64 base_address);
 extern void __iomem *of_iomap(struct device_node *np, int index);
 
+extern int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr,
+			    u64 *size);
+
 #else /* CONFIG_OFTREE */
 
 static inline u64 of_translate_address(struct device_node *dev,
@@ -99,6 +102,12 @@ static inline void __iomem *of_iomap(struct device_node *np, int index)
 	return NULL;
 }
 
+static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr,
+				   u64 *paddr, u64 *size)
+{
+	return -ENOSYS;
+}
+
 #endif /* CONFIG_OFTREE */
 
 #ifdef CONFIG_OF_PCI