aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/ibmebus.c
diff options
context:
space:
mode:
authorHeiko J Schick <schihei@de.ibm.com>2005-11-16 02:56:43 -0500
committerPaul Mackerras <paulus@samba.org>2006-01-08 22:49:06 -0500
commitd7a301033f1990188f65abf4fe8e5b90ef0e3888 (patch)
tree5df64f9e06049e6006e6a124dc6fa4953d6e42b9 /arch/powerpc/kernel/ibmebus.c
parent401d1f029bebb7153ca704997772113dc36d9527 (diff)
[PATCH] powerpc: IBMEBUS bus support
This patch adds the necessary core bus support used by device drivers that sit on the IBM GX bus on modern pSeries machines like the Galaxy infiniband for example. It provide transparent DMA ops (the low level driver works with virtual addresses directly) along with a simple bus layer using the Open Firmware matching routines. Signed-off-by: Heiko J Schick <schickhj@de.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/ibmebus.c')
-rw-r--r--arch/powerpc/kernel/ibmebus.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c
new file mode 100644
index 000000000000..e47d40ac6f39
--- /dev/null
+++ b/arch/powerpc/kernel/ibmebus.c
@@ -0,0 +1,396 @@
1/*
2 * IBM PowerPC IBM eBus Infrastructure Support.
3 *
4 * Copyright (c) 2005 IBM Corporation
5 * Heiko J Schick <schickhj@de.ibm.com>
6 *
7 * All rights reserved.
8 *
9 * This source code is distributed under a dual license of GPL v2.0 and OpenIB
10 * BSD.
11 *
12 * OpenIB BSD License
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are met:
16 *
17 * Redistributions of source code must retain the above copyright notice, this
18 * list of conditions and the following disclaimer.
19 *
20 * Redistributions in binary form must reproduce the above copyright notice,
21 * this list of conditions and the following disclaimer in the documentation
22 * and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
32 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
33 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <linux/init.h>
39#include <linux/console.h>
40#include <linux/kobject.h>
41#include <linux/dma-mapping.h>
42#include <linux/interrupt.h>
43#include <asm/ibmebus.h>
44#include <asm/abs_addr.h>
45
46static struct ibmebus_dev ibmebus_bus_device = { /* fake "parent" device */
47 .name = ibmebus_bus_device.ofdev.dev.bus_id,
48 .ofdev.dev.bus_id = "ibmebus",
49 .ofdev.dev.bus = &ibmebus_bus_type,
50};
51
52static void *ibmebus_alloc_coherent(struct device *dev,
53 size_t size,
54 dma_addr_t *dma_handle,
55 gfp_t flag)
56{
57 void *mem;
58
59 mem = kmalloc(size, flag);
60 *dma_handle = (dma_addr_t)mem;
61
62 return mem;
63}
64
65static void ibmebus_free_coherent(struct device *dev,
66 size_t size, void *vaddr,
67 dma_addr_t dma_handle)
68{
69 kfree(vaddr);
70}
71
72static dma_addr_t ibmebus_map_single(struct device *dev,
73 void *ptr,
74 size_t size,
75 enum dma_data_direction direction)
76{
77 return (dma_addr_t)(ptr);
78}
79
80static void ibmebus_unmap_single(struct device *dev,
81 dma_addr_t dma_addr,
82 size_t size,
83 enum dma_data_direction direction)
84{
85 return;
86}
87
88static int ibmebus_map_sg(struct device *dev,
89 struct scatterlist *sg,
90 int nents, enum dma_data_direction direction)
91{
92 int i;
93
94 for (i = 0; i < nents; i++) {
95 sg[i].dma_address = (dma_addr_t)page_address(sg[i].page)
96 + sg[i].offset;
97 sg[i].dma_length = sg[i].length;
98 }
99
100 return nents;
101}
102
103static void ibmebus_unmap_sg(struct device *dev,
104 struct scatterlist *sg,
105 int nents, enum dma_data_direction direction)
106{
107 return;
108}
109
110static int ibmebus_dma_supported(struct device *dev, u64 mask)
111{
112 return 1;
113}
114
115struct dma_mapping_ops ibmebus_dma_ops = {
116 .alloc_coherent = ibmebus_alloc_coherent,
117 .free_coherent = ibmebus_free_coherent,
118 .map_single = ibmebus_map_single,
119 .unmap_single = ibmebus_unmap_single,
120 .map_sg = ibmebus_map_sg,
121 .unmap_sg = ibmebus_unmap_sg,
122 .dma_supported = ibmebus_dma_supported,
123};
124
125static int ibmebus_bus_probe(struct device *dev)
126{
127 struct ibmebus_dev *ibmebusdev = to_ibmebus_dev(dev);
128 struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver);
129 const struct of_device_id *id;
130 int error = -ENODEV;
131
132 if (!ibmebusdrv->probe)
133 return error;
134
135 id = of_match_device(ibmebusdrv->id_table, &ibmebusdev->ofdev);
136 if (id) {
137 error = ibmebusdrv->probe(ibmebusdev, id);
138 }
139
140 return error;
141}
142
143static int ibmebus_bus_remove(struct device *dev)
144{
145 struct ibmebus_dev *ibmebusdev = to_ibmebus_dev(dev);
146 struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver);
147
148 if (ibmebusdrv->remove) {
149 return ibmebusdrv->remove(ibmebusdev);
150 }
151
152 return 0;
153}
154
155static void __devinit ibmebus_dev_release(struct device *dev)
156{
157 of_node_put(to_ibmebus_dev(dev)->ofdev.node);
158 kfree(to_ibmebus_dev(dev));
159}
160
161static ssize_t ibmebusdev_show_name(struct device *dev,
162 struct device_attribute *attr, char *buf)
163{
164 return sprintf(buf, "%s\n", to_ibmebus_dev(dev)->name);
165}
166static DEVICE_ATTR(name, S_IRUSR | S_IRGRP | S_IROTH, ibmebusdev_show_name,
167 NULL);
168
169static struct ibmebus_dev* __devinit ibmebus_register_device_common(
170 struct ibmebus_dev *dev, char *name)
171{
172 int err = 0;
173
174 dev->name = name;
175 dev->ofdev.dev.parent = &ibmebus_bus_device.ofdev.dev;
176 dev->ofdev.dev.bus = &ibmebus_bus_type;
177 dev->ofdev.dev.release = ibmebus_dev_release;
178
179 /* An ibmebusdev is based on a of_device. We have to change the
180 * bus type to use our own DMA mapping operations.
181 */
182 if ((err = of_device_register(&dev->ofdev)) != 0) {
183 printk(KERN_ERR "%s: failed to register device (%d).\n",
184 __FUNCTION__, err);
185 return NULL;
186 }
187
188 device_create_file(&dev->ofdev.dev, &dev_attr_name);
189
190 return dev;
191}
192
193static struct ibmebus_dev* __devinit ibmebus_register_device_node(
194 struct device_node *dn)
195{
196 struct ibmebus_dev *dev;
197 char *loc_code;
198 int length;
199
200 loc_code = (char *)get_property(dn, "ibm,loc-code", NULL);
201 if (!loc_code) {
202 printk(KERN_WARNING "%s: node %s missing 'ibm,loc-code'\n",
203 __FUNCTION__, dn->name ? dn->name : "<unknown>");
204 return NULL;
205 }
206
207 if (strlen(loc_code) == 0) {
208 printk(KERN_WARNING "%s: 'ibm,loc-code' is invalid\n",
209 __FUNCTION__);
210 return NULL;
211 }
212
213 dev = kmalloc(sizeof(struct ibmebus_dev), GFP_KERNEL);
214 if (!dev) {
215 return NULL;
216 }
217 memset(dev, 0, sizeof(struct ibmebus_dev));
218
219 dev->ofdev.node = of_node_get(dn);
220
221 length = strlen(loc_code);
222 memcpy(dev->ofdev.dev.bus_id, loc_code
223 + (length - min(length, BUS_ID_SIZE - 1)),
224 min(length, BUS_ID_SIZE - 1));
225
226 /* Register with generic device framework. */
227 if (ibmebus_register_device_common(dev, dn->name) == NULL) {
228 kfree(dev);
229 return NULL;
230 }
231
232 return dev;
233}
234
235static void ibmebus_probe_of_nodes(char* name)
236{
237 struct device_node *dn = NULL;
238
239 while ((dn = of_find_node_by_name(dn, name))) {
240 if (ibmebus_register_device_node(dn) == NULL) {
241 of_node_put(dn);
242
243 return;
244 }
245 }
246
247 of_node_put(dn);
248
249 return;
250}
251
252static void ibmebus_add_devices_by_id(struct of_device_id *idt)
253{
254 while (strlen(idt->name) > 0) {
255 ibmebus_probe_of_nodes(idt->name);
256 idt++;
257 }
258
259 return;
260}
261
262static int ibmebus_match_helper(struct device *dev, void *data)
263{
264 if (strcmp((char*)data, to_ibmebus_dev(dev)->name) == 0)
265 return 1;
266
267 return 0;
268}
269
270static int ibmebus_unregister_device(struct device *dev)
271{
272 device_remove_file(dev, &dev_attr_name);
273 of_device_unregister(to_of_device(dev));
274
275 return 0;
276}
277
278static void ibmebus_remove_devices_by_id(struct of_device_id *idt)
279{
280 struct device *dev;
281
282 while (strlen(idt->name) > 0) {
283 while ((dev = bus_find_device(&ibmebus_bus_type, NULL,
284 (void*)idt->name,
285 ibmebus_match_helper))) {
286 ibmebus_unregister_device(dev);
287 }
288 idt++;
289
290 }
291
292 return;
293}
294
295int ibmebus_register_driver(struct ibmebus_driver *drv)
296{
297 int err = 0;
298
299 drv->driver.name = drv->name;
300 drv->driver.bus = &ibmebus_bus_type;
301 drv->driver.probe = ibmebus_bus_probe;
302 drv->driver.remove = ibmebus_bus_remove;
303
304 if ((err = driver_register(&drv->driver) != 0))
305 return err;
306
307 ibmebus_add_devices_by_id(drv->id_table);
308
309 return 0;
310}
311EXPORT_SYMBOL(ibmebus_register_driver);
312
313void ibmebus_unregister_driver(struct ibmebus_driver *drv)
314{
315 driver_unregister(&drv->driver);
316 ibmebus_remove_devices_by_id(drv->id_table);
317}
318EXPORT_SYMBOL(ibmebus_unregister_driver);
319
320int ibmebus_request_irq(struct ibmebus_dev *dev,
321 u32 ist,
322 irqreturn_t (*handler)(int, void*, struct pt_regs *),
323 unsigned long irq_flags, const char * devname,
324 void *dev_id)
325{
326 unsigned int irq = virt_irq_create_mapping(ist);
327
328 if (irq == NO_IRQ)
329 return -EINVAL;
330
331 irq = irq_offset_up(irq);
332
333 return request_irq(irq, handler,
334 irq_flags, devname, dev_id);
335}
336EXPORT_SYMBOL(ibmebus_request_irq);
337
338void ibmebus_free_irq(struct ibmebus_dev *dev, u32 ist, void *dev_id)
339{
340 unsigned int irq = virt_irq_create_mapping(ist);
341
342 irq = irq_offset_up(irq);
343 free_irq(irq, dev_id);
344
345 return;
346}
347EXPORT_SYMBOL(ibmebus_free_irq);
348
349static int ibmebus_bus_match(struct device *dev, struct device_driver *drv)
350{
351 const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev);
352 struct ibmebus_driver *ebus_drv = to_ibmebus_driver(drv);
353 const struct of_device_id *ids = ebus_drv->id_table;
354 const struct of_device_id *found_id;
355
356 if (!ids)
357 return 0;
358
359 found_id = of_match_device(ids, &ebus_dev->ofdev);
360 if (found_id)
361 return 1;
362
363 return 0;
364}
365
366struct bus_type ibmebus_bus_type = {
367 .name = "ibmebus",
368 .match = ibmebus_bus_match,
369};
370EXPORT_SYMBOL(ibmebus_bus_type);
371
372static int __init ibmebus_bus_init(void)
373{
374 int err;
375
376 printk(KERN_INFO "IBM eBus Device Driver\n");
377
378 err = bus_register(&ibmebus_bus_type);
379 if (err) {
380 printk(KERN_ERR ":%s: failed to register IBM eBus.\n",
381 __FUNCTION__);
382 return err;
383 }
384
385 err = device_register(&ibmebus_bus_device.ofdev.dev);
386 if (err) {
387 printk(KERN_WARNING "%s: device_register returned %i\n",
388 __FUNCTION__, err);
389 bus_unregister(&ibmebus_bus_type);
390
391 return err;
392 }
393
394 return 0;
395}
396__initcall(ibmebus_bus_init);