Merge master.kernel.org:/pub/scm/linux/kernel/git/lethal/genesis-2.6 into devel-stable
[sfrench/cifs-2.6.git] / arch / powerpc / platforms / iseries / vio.c
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/module.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
50 struct vio_waitevent {
51         struct completion       com;
52         int                     rc;
53         u16                     sub_result;
54 };
55
56 struct vio_resource {
57         char    rsrcname[10];
58         char    type[4];
59         char    model[3];
60 };
61
62 static 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
78 static void free_property(struct property *np)
79 {
80         kfree(np);
81 }
82
83 static 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
101 static 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
117 static 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
128 static 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
139 static 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  */
190 struct 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 }
209 EXPORT_SYMBOL_GPL(vio_create_viodasd);
210
211 static 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
256 static 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
262 retry:
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
311 static 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
334 static 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
374 static 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 */
446 static 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
466 static 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
524 static 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 }
556 arch_initcall(iseries_vio_init);