aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/iseries/vio.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/iseries/vio.c')
-rw-r--r--arch/powerpc/platforms/iseries/vio.c556
1 files changed, 0 insertions, 556 deletions
diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c
deleted file mode 100644
index 04be62d368a6..000000000000
--- a/arch/powerpc/platforms/iseries/vio.c
+++ /dev/null
@@ -1,556 +0,0 @@
1/*
2 * Legacy iSeries specific vio initialisation
3 * that needs to be built in (not a module).
4 *
5 * © Copyright 2007 IBM Corporation
6 * Author: Stephen Rothwell
7 * Some parts collected from various other files
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23#include <linux/of.h>
24#include <linux/init.h>
25#include <linux/slab.h>
26#include <linux/completion.h>
27#include <linux/proc_fs.h>
28#include <linux/export.h>
29
30#include <asm/firmware.h>
31#include <asm/vio.h>
32#include <asm/iseries/vio.h>
33#include <asm/iseries/iommu.h>
34#include <asm/iseries/hv_types.h>
35#include <asm/iseries/hv_lp_event.h>
36
37#define FIRST_VTY 0
38#define NUM_VTYS 1
39#define FIRST_VSCSI (FIRST_VTY + NUM_VTYS)
40#define NUM_VSCSIS 1
41#define FIRST_VLAN (FIRST_VSCSI + NUM_VSCSIS)
42#define NUM_VLANS HVMAXARCHITECTEDVIRTUALLANS
43#define FIRST_VIODASD (FIRST_VLAN + NUM_VLANS)
44#define NUM_VIODASDS HVMAXARCHITECTEDVIRTUALDISKS
45#define FIRST_VIOCD (FIRST_VIODASD + NUM_VIODASDS)
46#define NUM_VIOCDS HVMAXARCHITECTEDVIRTUALCDROMS
47#define FIRST_VIOTAPE (FIRST_VIOCD + NUM_VIOCDS)
48#define NUM_VIOTAPES HVMAXARCHITECTEDVIRTUALTAPES
49
50struct vio_waitevent {
51 struct completion com;
52 int rc;
53 u16 sub_result;
54};
55
56struct vio_resource {
57 char rsrcname[10];
58 char type[4];
59 char model[3];
60};
61
62static struct property *new_property(const char *name, int length,
63 const void *value)
64{
65 struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length,
66 GFP_KERNEL);
67
68 if (!np)
69 return NULL;
70 np->name = (char *)(np + 1);
71 np->value = np->name + strlen(name) + 1;
72 strcpy(np->name, name);
73 memcpy(np->value, value, length);
74 np->length = length;
75 return np;
76}
77
78static void free_property(struct property *np)
79{
80 kfree(np);
81}
82
83static struct device_node *new_node(const char *path,
84 struct device_node *parent)
85{
86 struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL);
87
88 if (!np)
89 return NULL;
90 np->full_name = kstrdup(path, GFP_KERNEL);
91 if (!np->full_name) {
92 kfree(np);
93 return NULL;
94 }
95 of_node_set_flag(np, OF_DYNAMIC);
96 kref_init(&np->kref);
97 np->parent = of_node_get(parent);
98 return np;
99}
100
101static void free_node(struct device_node *np)
102{
103 struct property *next;
104 struct property *prop;
105
106 next = np->properties;
107 while (next) {
108 prop = next;
109 next = prop->next;
110 free_property(prop);
111 }
112 of_node_put(np->parent);
113 kfree(np->full_name);
114 kfree(np);
115}
116
117static int add_string_property(struct device_node *np, const char *name,
118 const char *value)
119{
120 struct property *nprop = new_property(name, strlen(value) + 1, value);
121
122 if (!nprop)
123 return 0;
124 prom_add_property(np, nprop);
125 return 1;
126}
127
128static int add_raw_property(struct device_node *np, const char *name,
129 int length, const void *value)
130{
131 struct property *nprop = new_property(name, length, value);
132
133 if (!nprop)
134 return 0;
135 prom_add_property(np, nprop);
136 return 1;
137}
138
139static struct device_node *do_device_node(struct device_node *parent,
140 const char *name, u32 reg, u32 unit, const char *type,
141 const char *compat, struct vio_resource *res)
142{
143 struct device_node *np;
144 char path[32];
145
146 snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg);
147 np = new_node(path, parent);
148 if (!np)
149 return NULL;
150 if (!add_string_property(np, "name", name) ||
151 !add_string_property(np, "device_type", type) ||
152 !add_string_property(np, "compatible", compat) ||
153 !add_raw_property(np, "reg", sizeof(reg), &reg) ||
154 !add_raw_property(np, "linux,unit_address",
155 sizeof(unit), &unit)) {
156 goto node_free;
157 }
158 if (res) {
159 if (!add_raw_property(np, "linux,vio_rsrcname",
160 sizeof(res->rsrcname), res->rsrcname) ||
161 !add_raw_property(np, "linux,vio_type",
162 sizeof(res->type), res->type) ||
163 !add_raw_property(np, "linux,vio_model",
164 sizeof(res->model), res->model))
165 goto node_free;
166 }
167 np->name = of_get_property(np, "name", NULL);
168 np->type = of_get_property(np, "device_type", NULL);
169 of_attach_node(np);
170#ifdef CONFIG_PROC_DEVICETREE
171 if (parent->pde) {
172 struct proc_dir_entry *ent;
173
174 ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde);
175 if (ent)
176 proc_device_tree_add_node(np, ent);
177 }
178#endif
179 return np;
180
181 node_free:
182 free_node(np);
183 return NULL;
184}
185
186/*
187 * This is here so that we can dynamically add viodasd
188 * devices without exposing all the above infrastructure.
189 */
190struct vio_dev *vio_create_viodasd(u32 unit)
191{
192 struct device_node *vio_root;
193 struct device_node *np;
194 struct vio_dev *vdev = NULL;
195
196 vio_root = of_find_node_by_path("/vdevice");
197 if (!vio_root)
198 return NULL;
199 np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
200 "block", "IBM,iSeries-viodasd", NULL);
201 of_node_put(vio_root);
202 if (np) {
203 vdev = vio_register_device_node(np);
204 if (!vdev)
205 free_node(np);
206 }
207 return vdev;
208}
209EXPORT_SYMBOL_GPL(vio_create_viodasd);
210
211static void __init handle_block_event(struct HvLpEvent *event)
212{
213 struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
214 struct vio_waitevent *pwe;
215
216 if (event == NULL)
217 /* Notification that a partition went away! */
218 return;
219 /* First, we should NEVER get an int here...only acks */
220 if (hvlpevent_is_int(event)) {
221 printk(KERN_WARNING "handle_viod_request: "
222 "Yikes! got an int in viodasd event handler!\n");
223 if (hvlpevent_need_ack(event)) {
224 event->xRc = HvLpEvent_Rc_InvalidSubtype;
225 HvCallEvent_ackLpEvent(event);
226 }
227 return;
228 }
229
230 switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
231 case vioblockopen:
232 /*
233 * Handle a response to an open request. We get all the
234 * disk information in the response, so update it. The
235 * correlation token contains a pointer to a waitevent
236 * structure that has a completion in it. update the
237 * return code in the waitevent structure and post the
238 * completion to wake up the guy who sent the request
239 */
240 pwe = (struct vio_waitevent *)event->xCorrelationToken;
241 pwe->rc = event->xRc;
242 pwe->sub_result = bevent->sub_result;
243 complete(&pwe->com);
244 break;
245 case vioblockclose:
246 break;
247 default:
248 printk(KERN_WARNING "handle_viod_request: unexpected subtype!");
249 if (hvlpevent_need_ack(event)) {
250 event->xRc = HvLpEvent_Rc_InvalidSubtype;
251 HvCallEvent_ackLpEvent(event);
252 }
253 }
254}
255
256static void __init probe_disk(struct device_node *vio_root, u32 unit)
257{
258 HvLpEvent_Rc hvrc;
259 struct vio_waitevent we;
260 u16 flags = 0;
261
262retry:
263 init_completion(&we.com);
264
265 /* Send the open event to OS/400 */
266 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
267 HvLpEvent_Type_VirtualIo,
268 viomajorsubtype_blockio | vioblockopen,
269 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
270 viopath_sourceinst(viopath_hostLp),
271 viopath_targetinst(viopath_hostLp),
272 (u64)(unsigned long)&we, VIOVERSION << 16,
273 ((u64)unit << 48) | ((u64)flags<< 32),
274 0, 0, 0);
275 if (hvrc != 0) {
276 printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n",
277 (int)hvrc);
278 return;
279 }
280
281 wait_for_completion(&we.com);
282
283 if (we.rc != 0) {
284 if (flags != 0)
285 return;
286 /* try again with read only flag set */
287 flags = vioblockflags_ro;
288 goto retry;
289 }
290
291 /* Send the close event to OS/400. We DON'T expect a response */
292 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
293 HvLpEvent_Type_VirtualIo,
294 viomajorsubtype_blockio | vioblockclose,
295 HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
296 viopath_sourceinst(viopath_hostLp),
297 viopath_targetinst(viopath_hostLp),
298 0, VIOVERSION << 16,
299 ((u64)unit << 48) | ((u64)flags << 32),
300 0, 0, 0);
301 if (hvrc != 0) {
302 printk(KERN_WARNING "probe_disk: "
303 "bad rc sending event to OS/400 %d\n", (int)hvrc);
304 return;
305 }
306
307 do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
308 "block", "IBM,iSeries-viodasd", NULL);
309}
310
311static void __init get_viodasd_info(struct device_node *vio_root)
312{
313 int rc;
314 u32 unit;
315
316 rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2);
317 if (rc) {
318 printk(KERN_WARNING "get_viodasd_info: "
319 "error opening path to host partition %d\n",
320 viopath_hostLp);
321 return;
322 }
323
324 /* Initialize our request handler */
325 vio_setHandler(viomajorsubtype_blockio, handle_block_event);
326
327 for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++)
328 probe_disk(vio_root, unit);
329
330 vio_clearHandler(viomajorsubtype_blockio);
331 viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2);
332}
333
334static void __init handle_cd_event(struct HvLpEvent *event)
335{
336 struct viocdlpevent *bevent;
337 struct vio_waitevent *pwe;
338
339 if (!event)
340 /* Notification that a partition went away! */
341 return;
342
343 /* First, we should NEVER get an int here...only acks */
344 if (hvlpevent_is_int(event)) {
345 printk(KERN_WARNING "handle_cd_event: got an unexpected int\n");
346 if (hvlpevent_need_ack(event)) {
347 event->xRc = HvLpEvent_Rc_InvalidSubtype;
348 HvCallEvent_ackLpEvent(event);
349 }
350 return;
351 }
352
353 bevent = (struct viocdlpevent *)event;
354
355 switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
356 case viocdgetinfo:
357 pwe = (struct vio_waitevent *)event->xCorrelationToken;
358 pwe->rc = event->xRc;
359 pwe->sub_result = bevent->sub_result;
360 complete(&pwe->com);
361 break;
362
363 default:
364 printk(KERN_WARNING "handle_cd_event: "
365 "message with unexpected subtype %0x04X!\n",
366 event->xSubtype & VIOMINOR_SUBTYPE_MASK);
367 if (hvlpevent_need_ack(event)) {
368 event->xRc = HvLpEvent_Rc_InvalidSubtype;
369 HvCallEvent_ackLpEvent(event);
370 }
371 }
372}
373
374static void __init get_viocd_info(struct device_node *vio_root)
375{
376 HvLpEvent_Rc hvrc;
377 u32 unit;
378 struct vio_waitevent we;
379 struct vio_resource *unitinfo;
380 dma_addr_t unitinfo_dmaaddr;
381 int ret;
382
383 ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2);
384 if (ret) {
385 printk(KERN_WARNING
386 "get_viocd_info: error opening path to host partition %d\n",
387 viopath_hostLp);
388 return;
389 }
390
391 /* Initialize our request handler */
392 vio_setHandler(viomajorsubtype_cdio, handle_cd_event);
393
394 unitinfo = iseries_hv_alloc(
395 sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
396 &unitinfo_dmaaddr, GFP_ATOMIC);
397 if (!unitinfo) {
398 printk(KERN_WARNING
399 "get_viocd_info: error allocating unitinfo\n");
400 goto clear_handler;
401 }
402
403 memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS);
404
405 init_completion(&we.com);
406
407 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
408 HvLpEvent_Type_VirtualIo,
409 viomajorsubtype_cdio | viocdgetinfo,
410 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
411 viopath_sourceinst(viopath_hostLp),
412 viopath_targetinst(viopath_hostLp),
413 (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0,
414 sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0);
415 if (hvrc != HvLpEvent_Rc_Good) {
416 printk(KERN_WARNING
417 "get_viocd_info: cdrom error sending event. rc %d\n",
418 (int)hvrc);
419 goto hv_free;
420 }
421
422 wait_for_completion(&we.com);
423
424 if (we.rc) {
425 printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n",
426 we.rc, we.sub_result);
427 goto hv_free;
428 }
429
430 for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) &&
431 unitinfo[unit].rsrcname[0]; unit++) {
432 if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit,
433 "block", "IBM,iSeries-viocd", &unitinfo[unit]))
434 break;
435 }
436
437 hv_free:
438 iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
439 unitinfo, unitinfo_dmaaddr);
440 clear_handler:
441 vio_clearHandler(viomajorsubtype_cdio);
442 viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2);
443}
444
445/* Handle interrupt events for tape */
446static void __init handle_tape_event(struct HvLpEvent *event)
447{
448 struct vio_waitevent *we;
449 struct viotapelpevent *tevent = (struct viotapelpevent *)event;
450
451 if (event == NULL)
452 /* Notification that a partition went away! */
453 return;
454
455 we = (struct vio_waitevent *)event->xCorrelationToken;
456 switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
457 case viotapegetinfo:
458 we->rc = tevent->sub_type_result;
459 complete(&we->com);
460 break;
461 default:
462 printk(KERN_WARNING "handle_tape_event: weird ack\n");
463 }
464}
465
466static void __init get_viotape_info(struct device_node *vio_root)
467{
468 HvLpEvent_Rc hvrc;
469 u32 unit;
470 struct vio_resource *unitinfo;
471 dma_addr_t unitinfo_dmaaddr;
472 size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES;
473 struct vio_waitevent we;
474 int ret;
475
476 init_completion(&we.com);
477
478 ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
479 if (ret) {
480 printk(KERN_WARNING "get_viotape_info: "
481 "error on viopath_open to hostlp %d\n", ret);
482 return;
483 }
484
485 vio_setHandler(viomajorsubtype_tape, handle_tape_event);
486
487 unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
488 if (!unitinfo)
489 goto clear_handler;
490
491 memset(unitinfo, 0, len);
492
493 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
494 HvLpEvent_Type_VirtualIo,
495 viomajorsubtype_tape | viotapegetinfo,
496 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
497 viopath_sourceinst(viopath_hostLp),
498 viopath_targetinst(viopath_hostLp),
499 (u64)(unsigned long)&we, VIOVERSION << 16,
500 unitinfo_dmaaddr, len, 0, 0);
501 if (hvrc != HvLpEvent_Rc_Good) {
502 printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
503 (int)hvrc);
504 goto hv_free;
505 }
506
507 wait_for_completion(&we.com);
508
509 for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
510 unitinfo[unit].rsrcname[0]; unit++) {
511 if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit,
512 unit, "byte", "IBM,iSeries-viotape",
513 &unitinfo[unit]))
514 break;
515 }
516
517 hv_free:
518 iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
519 clear_handler:
520 vio_clearHandler(viomajorsubtype_tape);
521 viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
522}
523
524static int __init iseries_vio_init(void)
525{
526 struct device_node *vio_root;
527 int ret = -ENODEV;
528
529 if (!firmware_has_feature(FW_FEATURE_ISERIES))
530 goto out;
531
532 iommu_vio_init();
533
534 vio_root = of_find_node_by_path("/vdevice");
535 if (!vio_root)
536 goto out;
537
538 if (viopath_hostLp == HvLpIndexInvalid) {
539 vio_set_hostlp();
540 /* If we don't have a host, bail out */
541 if (viopath_hostLp == HvLpIndexInvalid)
542 goto put_node;
543 }
544
545 get_viodasd_info(vio_root);
546 get_viocd_info(vio_root);
547 get_viotape_info(vio_root);
548
549 ret = 0;
550
551 put_node:
552 of_node_put(vio_root);
553 out:
554 return ret;
555}
556arch_initcall(iseries_vio_init);