aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
authorGrant Likely <grant.likely@linaro.org>2013-09-15 17:32:39 -0400
committerGrant Likely <grant.likely@linaro.org>2013-10-24 06:43:04 -0400
commit2361613206e66ce59cc0e08efa8d98ec15b84ed1 (patch)
tree5e4f5fc33f715202f71770890dcc09ac09a2d851 /drivers/of
parente6d30ab1e7d1281784672c0fc2ffa385cfb7279e (diff)
of/irq: Refactor interrupt-map parsing
All the users of of_irq_parse_raw pass in a raw interrupt specifier from the device tree and expect it to be returned (possibly modified) in an of_phandle_args structure. However, the primary function of of_irq_parse_raw() is to check for translations due to the presence of one or more interrupt-map properties. The actual placing of the data into an of_phandle_args structure is trivial. If it is refactored to accept an of_phandle_args structure directly, then it becomes possible to consume of_phandle_args from other sources. This is important for an upcoming patch that allows a device to be connected to more than one interrupt parent. It also simplifies the code a bit. The biggest complication with this patch is that the old version works on the interrupt specifiers in __be32 form, but the of_phandle_args structure is intended to carry it in the cpu-native version. A bit of churn was required to make this work. In the end it results in tighter code, so the churn is worth it. Signed-off-by: Grant Likely <grant.likely@linaro.org> Acked-by: Tony Lindgren <tony@atomide.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/irq.c108
-rw-r--r--drivers/of/of_pci_irq.c7
2 files changed, 62 insertions, 53 deletions
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 84184c44e8db..0ed5ed4a5f9b 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -80,31 +80,32 @@ struct device_node *of_irq_find_parent(struct device_node *child)
80/** 80/**
81 * of_irq_parse_raw - Low level interrupt tree parsing 81 * of_irq_parse_raw - Low level interrupt tree parsing
82 * @parent: the device interrupt parent 82 * @parent: the device interrupt parent
83 * @intspec: interrupt specifier ("interrupts" property of the device) 83 * @addr: address specifier (start of "reg" property of the device) in be32 format
84 * @ointsize: size of the passed in interrupt specifier 84 * @out_irq: structure of_irq updated by this function
85 * @addr: address specifier (start of "reg" property of the device)
86 * @out_irq: structure of_irq filled by this function
87 * 85 *
88 * Returns 0 on success and a negative number on error 86 * Returns 0 on success and a negative number on error
89 * 87 *
90 * This function is a low-level interrupt tree walking function. It 88 * This function is a low-level interrupt tree walking function. It
91 * can be used to do a partial walk with synthetized reg and interrupts 89 * can be used to do a partial walk with synthetized reg and interrupts
92 * properties, for example when resolving PCI interrupts when no device 90 * properties, for example when resolving PCI interrupts when no device
93 * node exist for the parent. 91 * node exist for the parent. It takes an interrupt specifier structure as
92 * input, walks the tree looking for any interrupt-map properties, translates
93 * the specifier for each map, and then returns the translated map.
94 */ 94 */
95int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, 95int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
96 u32 ointsize, const __be32 *addr, struct of_phandle_args *out_irq)
97{ 96{
98 struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; 97 struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
99 const __be32 *tmp, *imap, *imask; 98 __be32 initial_match_array[8];
99 const __be32 *match_array = initial_match_array;
100 const __be32 *tmp, *imap, *imask, dummy_imask[] = { ~0, ~0, ~0, ~0, ~0 };
100 u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; 101 u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
101 int imaplen, match, i; 102 int imaplen, match, i;
102 103
103 pr_debug("of_irq_parse_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", 104 pr_debug("of_irq_parse_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n",
104 of_node_full_name(parent), be32_to_cpup(intspec), 105 of_node_full_name(out_irq->np), out_irq->args[0], out_irq->args[1],
105 be32_to_cpup(intspec + 1), ointsize); 106 out_irq->args_count);
106 107
107 ipar = of_node_get(parent); 108 ipar = of_node_get(out_irq->np);
108 109
109 /* First get the #interrupt-cells property of the current cursor 110 /* First get the #interrupt-cells property of the current cursor
110 * that tells us how to interpret the passed-in intspec. If there 111 * that tells us how to interpret the passed-in intspec. If there
@@ -127,7 +128,7 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
127 128
128 pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize); 129 pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize);
129 130
130 if (ointsize != intsize) 131 if (out_irq->args_count != intsize)
131 return -EINVAL; 132 return -EINVAL;
132 133
133 /* Look for this #address-cells. We have to implement the old linux 134 /* Look for this #address-cells. We have to implement the old linux
@@ -146,6 +147,21 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
146 147
147 pr_debug(" -> addrsize=%d\n", addrsize); 148 pr_debug(" -> addrsize=%d\n", addrsize);
148 149
150 /* If we were passed no "reg" property and we attempt to parse
151 * an interrupt-map, then #address-cells must be 0.
152 * Fail if it's not.
153 */
154 if (addr == NULL && addrsize != 0) {
155 pr_debug(" -> no reg passed in when needed !\n");
156 return -EINVAL;
157 }
158
159 /* Precalculate the match array - this simplifies match loop */
160 for (i = 0; i < addrsize; i++)
161 initial_match_array[i] = addr[i];
162 for (i = 0; i < intsize; i++)
163 initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]);
164
149 /* Now start the actual "proper" walk of the interrupt tree */ 165 /* Now start the actual "proper" walk of the interrupt tree */
150 while (ipar != NULL) { 166 while (ipar != NULL) {
151 /* Now check if cursor is an interrupt-controller and if it is 167 /* Now check if cursor is an interrupt-controller and if it is
@@ -154,11 +170,6 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
154 if (of_get_property(ipar, "interrupt-controller", NULL) != 170 if (of_get_property(ipar, "interrupt-controller", NULL) !=
155 NULL) { 171 NULL) {
156 pr_debug(" -> got it !\n"); 172 pr_debug(" -> got it !\n");
157 for (i = 0; i < intsize; i++)
158 out_irq->args[i] =
159 of_read_number(intspec +i, 1);
160 out_irq->args_count = intsize;
161 out_irq->np = ipar;
162 of_node_put(old); 173 of_node_put(old);
163 return 0; 174 return 0;
164 } 175 }
@@ -175,34 +186,16 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
175 186
176 /* Look for a mask */ 187 /* Look for a mask */
177 imask = of_get_property(ipar, "interrupt-map-mask", NULL); 188 imask = of_get_property(ipar, "interrupt-map-mask", NULL);
178 189 if (!imask)
179 /* If we were passed no "reg" property and we attempt to parse 190 imask = dummy_imask;
180 * an interrupt-map, then #address-cells must be 0.
181 * Fail if it's not.
182 */
183 if (addr == NULL && addrsize != 0) {
184 pr_debug(" -> no reg passed in when needed !\n");
185 goto fail;
186 }
187 191
188 /* Parse interrupt-map */ 192 /* Parse interrupt-map */
189 match = 0; 193 match = 0;
190 while (imaplen > (addrsize + intsize + 1) && !match) { 194 while (imaplen > (addrsize + intsize + 1) && !match) {
191 /* Compare specifiers */ 195 /* Compare specifiers */
192 match = 1; 196 match = 1;
193 for (i = 0; i < addrsize && match; ++i) { 197 for (i = 0; i < (addrsize + intsize); i++, imaplen--)
194 __be32 mask = imask ? imask[i] 198 match = !((match_array[i] ^ *imap++) & imask[i]);
195 : cpu_to_be32(0xffffffffu);
196 match = ((addr[i] ^ imap[i]) & mask) == 0;
197 }
198 for (; i < (addrsize + intsize) && match; ++i) {
199 __be32 mask = imask ? imask[i]
200 : cpu_to_be32(0xffffffffu);
201 match =
202 ((intspec[i-addrsize] ^ imap[i]) & mask) == 0;
203 }
204 imap += addrsize + intsize;
205 imaplen -= addrsize + intsize;
206 199
207 pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); 200 pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
208 201
@@ -247,12 +240,18 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
247 if (!match) 240 if (!match)
248 goto fail; 241 goto fail;
249 242
250 of_node_put(old); 243 /*
251 old = of_node_get(newpar); 244 * Successfully parsed an interrrupt-map translation; copy new
245 * interrupt specifier into the out_irq structure
246 */
247 of_node_put(out_irq->np);
248 out_irq->np = of_node_get(newpar);
249
250 match_array = imap - newaddrsize - newintsize;
251 for (i = 0; i < newintsize; i++)
252 out_irq->args[i] = be32_to_cpup(imap - newintsize + i);
253 out_irq->args_count = intsize = newintsize;
252 addrsize = newaddrsize; 254 addrsize = newaddrsize;
253 intsize = newintsize;
254 intspec = imap - intsize;
255 addr = intspec - addrsize;
256 255
257 skiplevel: 256 skiplevel:
258 /* Iterate again with new parent */ 257 /* Iterate again with new parent */
@@ -263,7 +262,7 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
263 } 262 }
264 fail: 263 fail:
265 of_node_put(ipar); 264 of_node_put(ipar);
266 of_node_put(old); 265 of_node_put(out_irq->np);
267 of_node_put(newpar); 266 of_node_put(newpar);
268 267
269 return -EINVAL; 268 return -EINVAL;
@@ -276,15 +275,16 @@ EXPORT_SYMBOL_GPL(of_irq_parse_raw);
276 * @index: index of the interrupt to resolve 275 * @index: index of the interrupt to resolve
277 * @out_irq: structure of_irq filled by this function 276 * @out_irq: structure of_irq filled by this function
278 * 277 *
279 * This function resolves an interrupt, walking the tree, for a given 278 * This function resolves an interrupt for a node by walking the interrupt tree,
280 * device-tree node. It's the high level pendant to of_irq_parse_raw(). 279 * finding which interrupt controller node it is attached to, and returning the
280 * interrupt specifier that can be used to retrieve a Linux IRQ number.
281 */ 281 */
282int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq) 282int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)
283{ 283{
284 struct device_node *p; 284 struct device_node *p;
285 const __be32 *intspec, *tmp, *addr; 285 const __be32 *intspec, *tmp, *addr;
286 u32 intsize, intlen; 286 u32 intsize, intlen;
287 int res = -EINVAL; 287 int i, res = -EINVAL;
288 288
289 pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index); 289 pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);
290 290
@@ -320,9 +320,15 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar
320 if ((index + 1) * intsize > intlen) 320 if ((index + 1) * intsize > intlen)
321 goto out; 321 goto out;
322 322
323 /* Get new specifier and map it */ 323 /* Copy intspec into irq structure */
324 res = of_irq_parse_raw(p, intspec + index * intsize, intsize, 324 intspec += index * intsize;
325 addr, out_irq); 325 out_irq->np = p;
326 out_irq->args_count = intsize;
327 for (i = 0; i < intsize; i++)
328 out_irq->args[i] = be32_to_cpup(intspec++);
329
330 /* Check if there are any interrupt-map translations to process */
331 res = of_irq_parse_raw(addr, out_irq);
326 out: 332 out:
327 of_node_put(p); 333 of_node_put(p);
328 return res; 334 return res;
diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c
index ee3293d4b66b..303afebc247a 100644
--- a/drivers/of/of_pci_irq.c
+++ b/drivers/of/of_pci_irq.c
@@ -85,9 +85,12 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
85 pdev = ppdev; 85 pdev = ppdev;
86 } 86 }
87 87
88 out_irq->np = ppnode;
89 out_irq->args_count = 1;
90 out_irq->args[0] = lspec;
88 lspec_be = cpu_to_be32(lspec); 91 lspec_be = cpu_to_be32(lspec);
89 laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); 92 laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
90 laddr[1] = laddr[2] = cpu_to_be32(0); 93 laddr[1] = laddr[2] = cpu_to_be32(0);
91 return of_irq_parse_raw(ppnode, &lspec_be, 1, laddr, out_irq); 94 return of_irq_parse_raw(laddr, out_irq);
92} 95}
93EXPORT_SYMBOL_GPL(of_irq_parse_pci); 96EXPORT_SYMBOL_GPL(of_irq_parse_pci);