[SCSI] libsas: Provide a transport-level facility to request SAS addrs
[linux-2.6] / 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/gfp.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 = kmalloc(strlen(path) + 1, GFP_KERNEL);
91         if (!np->full_name) {
92                 kfree(np);
93                 return NULL;
94         }
95         strcpy(np->full_name, path);
96         of_node_set_flag(np, OF_DYNAMIC);
97         kref_init(&np->kref);
98         np->parent = of_node_get(parent);
99         return np;
100 }
101
102 static void free_node(struct device_node *np)
103 {
104         struct property *next;
105         struct property *prop;
106
107         next = np->properties;
108         while (next) {
109                 prop = next;
110                 next = prop->next;
111                 free_property(prop);
112         }
113         of_node_put(np->parent);
114         kfree(np->full_name);
115         kfree(np);
116 }
117
118 static int add_string_property(struct device_node *np, const char *name,
119                 const char *value)
120 {
121         struct property *nprop = new_property(name, strlen(value) + 1, value);
122
123         if (!nprop)
124                 return 0;
125         prom_add_property(np, nprop);
126         return 1;
127 }
128
129 static int add_raw_property(struct device_node *np, const char *name,
130                 int length, const void *value)
131 {
132         struct property *nprop = new_property(name, length, value);
133
134         if (!nprop)
135                 return 0;
136         prom_add_property(np, nprop);
137         return 1;
138 }
139
140 static struct device_node *do_device_node(struct device_node *parent,
141                 const char *name, u32 reg, u32 unit, const char *type,
142                 const char *compat, struct vio_resource *res)
143 {
144         struct device_node *np;
145         char path[32];
146
147         snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg);
148         np = new_node(path, parent);
149         if (!np)
150                 return NULL;
151         if (!add_string_property(np, "name", name) ||
152                 !add_string_property(np, "device_type", type) ||
153                 !add_string_property(np, "compatible", compat) ||
154                 !add_raw_property(np, "reg", sizeof(reg), &reg) ||
155                 !add_raw_property(np, "linux,unit_address",
156                         sizeof(unit), &unit)) {
157                 goto node_free;
158         }
159         if (res) {
160                 if (!add_raw_property(np, "linux,vio_rsrcname",
161                                 sizeof(res->rsrcname), res->rsrcname) ||
162                         !add_raw_property(np, "linux,vio_type",
163                                 sizeof(res->type), res->type) ||
164                         !add_raw_property(np, "linux,vio_model",
165                                 sizeof(res->model), res->model))
166                         goto node_free;
167         }
168         np->name = of_get_property(np, "name", NULL);
169         np->type = of_get_property(np, "device_type", NULL);
170         of_attach_node(np);
171 #ifdef CONFIG_PROC_DEVICETREE
172         if (parent->pde) {
173                 struct proc_dir_entry *ent;
174
175                 ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde);
176                 if (ent)
177                         proc_device_tree_add_node(np, ent);
178         }
179 #endif
180         return np;
181
182  node_free:
183         free_node(np);
184         return NULL;
185 }
186
187 /*
188  * This is here so that we can dynamically add viodasd
189  * devices without exposing all the above infrastructure.
190  */
191 struct vio_dev *vio_create_viodasd(u32 unit)
192 {
193         struct device_node *vio_root;
194         struct device_node *np;
195         struct vio_dev *vdev = NULL;
196
197         vio_root = of_find_node_by_path("/vdevice");
198         if (!vio_root)
199                 return NULL;
200         np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
201                         "block", "IBM,iSeries-viodasd", NULL);
202         of_node_put(vio_root);
203         if (np) {
204                 vdev = vio_register_device_node(np);
205                 if (!vdev)
206                         free_node(np);
207         }
208         return vdev;
209 }
210 EXPORT_SYMBOL_GPL(vio_create_viodasd);
211
212 static void __init handle_block_event(struct HvLpEvent *event)
213 {
214         struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
215         struct vio_waitevent *pwe;
216
217         if (event == NULL)
218                 /* Notification that a partition went away! */
219                 return;
220         /* First, we should NEVER get an int here...only acks */
221         if (hvlpevent_is_int(event)) {
222                 printk(KERN_WARNING "handle_viod_request: "
223                        "Yikes! got an int in viodasd event handler!\n");
224                 if (hvlpevent_need_ack(event)) {
225                         event->xRc = HvLpEvent_Rc_InvalidSubtype;
226                         HvCallEvent_ackLpEvent(event);
227                 }
228                 return;
229         }
230
231         switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
232         case vioblockopen:
233                 /*
234                  * Handle a response to an open request.  We get all the
235                  * disk information in the response, so update it.  The
236                  * correlation token contains a pointer to a waitevent
237                  * structure that has a completion in it.  update the
238                  * return code in the waitevent structure and post the
239                  * completion to wake up the guy who sent the request
240                  */
241                 pwe = (struct vio_waitevent *)event->xCorrelationToken;
242                 pwe->rc = event->xRc;
243                 pwe->sub_result = bevent->sub_result;
244                 complete(&pwe->com);
245                 break;
246         case vioblockclose:
247                 break;
248         default:
249                 printk(KERN_WARNING "handle_viod_request: unexpected subtype!");
250                 if (hvlpevent_need_ack(event)) {
251                         event->xRc = HvLpEvent_Rc_InvalidSubtype;
252                         HvCallEvent_ackLpEvent(event);
253                 }
254         }
255 }
256
257 static void __init probe_disk(struct device_node *vio_root, u32 unit)
258 {
259         HvLpEvent_Rc hvrc;
260         struct vio_waitevent we;
261         u16 flags = 0;
262
263 retry:
264         init_completion(&we.com);
265
266         /* Send the open event to OS/400 */
267         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
268                         HvLpEvent_Type_VirtualIo,
269                         viomajorsubtype_blockio | vioblockopen,
270                         HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
271                         viopath_sourceinst(viopath_hostLp),
272                         viopath_targetinst(viopath_hostLp),
273                         (u64)(unsigned long)&we, VIOVERSION << 16,
274                         ((u64)unit << 48) | ((u64)flags<< 32),
275                         0, 0, 0);
276         if (hvrc != 0) {
277                 printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n",
278                         (int)hvrc);
279                 return;
280         }
281
282         wait_for_completion(&we.com);
283
284         if (we.rc != 0) {
285                 if (flags != 0)
286                         return;
287                 /* try again with read only flag set */
288                 flags = vioblockflags_ro;
289                 goto retry;
290         }
291
292         /* Send the close event to OS/400.  We DON'T expect a response */
293         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
294                         HvLpEvent_Type_VirtualIo,
295                         viomajorsubtype_blockio | vioblockclose,
296                         HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
297                         viopath_sourceinst(viopath_hostLp),
298                         viopath_targetinst(viopath_hostLp),
299                         0, VIOVERSION << 16,
300                         ((u64)unit << 48) | ((u64)flags << 32),
301                         0, 0, 0);
302         if (hvrc != 0) {
303                 printk(KERN_WARNING "probe_disk: "
304                        "bad rc sending event to OS/400 %d\n", (int)hvrc);
305                 return;
306         }
307
308         do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
309                         "block", "IBM,iSeries-viodasd", NULL);
310 }
311
312 static void __init get_viodasd_info(struct device_node *vio_root)
313 {
314         int rc;
315         u32 unit;
316
317         rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2);
318         if (rc) {
319                 printk(KERN_WARNING "get_viodasd_info: "
320                        "error opening path to host partition %d\n",
321                        viopath_hostLp);
322                 return;
323         }
324
325         /* Initialize our request handler */
326         vio_setHandler(viomajorsubtype_blockio, handle_block_event);
327
328         for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++)
329                 probe_disk(vio_root, unit);
330
331         vio_clearHandler(viomajorsubtype_blockio);
332         viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2);
333 }
334
335 static void __init handle_cd_event(struct HvLpEvent *event)
336 {
337         struct viocdlpevent *bevent;
338         struct vio_waitevent *pwe;
339
340         if (!event)
341                 /* Notification that a partition went away! */
342                 return;
343
344         /* First, we should NEVER get an int here...only acks */
345         if (hvlpevent_is_int(event)) {
346                 printk(KERN_WARNING "handle_cd_event: got an unexpected int\n");
347                 if (hvlpevent_need_ack(event)) {
348                         event->xRc = HvLpEvent_Rc_InvalidSubtype;
349                         HvCallEvent_ackLpEvent(event);
350                 }
351                 return;
352         }
353
354         bevent = (struct viocdlpevent *)event;
355
356         switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
357         case viocdgetinfo:
358                 pwe = (struct vio_waitevent *)event->xCorrelationToken;
359                 pwe->rc = event->xRc;
360                 pwe->sub_result = bevent->sub_result;
361                 complete(&pwe->com);
362                 break;
363
364         default:
365                 printk(KERN_WARNING "handle_cd_event: "
366                         "message with unexpected subtype %0x04X!\n",
367                         event->xSubtype & VIOMINOR_SUBTYPE_MASK);
368                 if (hvlpevent_need_ack(event)) {
369                         event->xRc = HvLpEvent_Rc_InvalidSubtype;
370                         HvCallEvent_ackLpEvent(event);
371                 }
372         }
373 }
374
375 static void __init get_viocd_info(struct device_node *vio_root)
376 {
377         HvLpEvent_Rc hvrc;
378         u32 unit;
379         struct vio_waitevent we;
380         struct vio_resource *unitinfo;
381         dma_addr_t unitinfo_dmaaddr;
382         int ret;
383
384         ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2);
385         if (ret) {
386                 printk(KERN_WARNING
387                         "get_viocd_info: error opening path to host partition %d\n",
388                         viopath_hostLp);
389                 return;
390         }
391
392         /* Initialize our request handler */
393         vio_setHandler(viomajorsubtype_cdio, handle_cd_event);
394
395         unitinfo = iseries_hv_alloc(
396                         sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
397                         &unitinfo_dmaaddr, GFP_ATOMIC);
398         if (!unitinfo) {
399                 printk(KERN_WARNING
400                         "get_viocd_info: error allocating unitinfo\n");
401                 goto clear_handler;
402         }
403
404         memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS);
405
406         init_completion(&we.com);
407
408         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
409                         HvLpEvent_Type_VirtualIo,
410                         viomajorsubtype_cdio | viocdgetinfo,
411                         HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
412                         viopath_sourceinst(viopath_hostLp),
413                         viopath_targetinst(viopath_hostLp),
414                         (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0,
415                         sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0);
416         if (hvrc != HvLpEvent_Rc_Good) {
417                 printk(KERN_WARNING
418                         "get_viocd_info: cdrom error sending event. rc %d\n",
419                         (int)hvrc);
420                 goto hv_free;
421         }
422
423         wait_for_completion(&we.com);
424
425         if (we.rc) {
426                 printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n",
427                         we.rc, we.sub_result);
428                 goto hv_free;
429         }
430
431         for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) &&
432                         unitinfo[unit].rsrcname[0]; unit++) {
433                 if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit,
434                                 "block", "IBM,iSeries-viocd", &unitinfo[unit]))
435                         break;
436         }
437
438  hv_free:
439         iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
440                         unitinfo, unitinfo_dmaaddr);
441  clear_handler:
442         vio_clearHandler(viomajorsubtype_cdio);
443         viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2);
444 }
445
446 /* Handle interrupt events for tape */
447 static void __init handle_tape_event(struct HvLpEvent *event)
448 {
449         struct vio_waitevent *we;
450         struct viotapelpevent *tevent = (struct viotapelpevent *)event;
451
452         if (event == NULL)
453                 /* Notification that a partition went away! */
454                 return;
455
456         we = (struct vio_waitevent *)event->xCorrelationToken;
457         switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
458         case viotapegetinfo:
459                 we->rc = tevent->sub_type_result;
460                 complete(&we->com);
461                 break;
462         default:
463                 printk(KERN_WARNING "handle_tape_event: weird ack\n");
464         }
465 }
466
467 static void __init get_viotape_info(struct device_node *vio_root)
468 {
469         HvLpEvent_Rc hvrc;
470         u32 unit;
471         struct vio_resource *unitinfo;
472         dma_addr_t unitinfo_dmaaddr;
473         size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES;
474         struct vio_waitevent we;
475         int ret;
476
477         ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
478         if (ret) {
479                 printk(KERN_WARNING "get_viotape_info: "
480                         "error on viopath_open to hostlp %d\n", ret);
481                 return;
482         }
483
484         vio_setHandler(viomajorsubtype_tape, handle_tape_event);
485
486         unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
487         if (!unitinfo)
488                 goto clear_handler;
489
490         memset(unitinfo, 0, len);
491
492         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
493                         HvLpEvent_Type_VirtualIo,
494                         viomajorsubtype_tape | viotapegetinfo,
495                         HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
496                         viopath_sourceinst(viopath_hostLp),
497                         viopath_targetinst(viopath_hostLp),
498                         (u64)(unsigned long)&we, VIOVERSION << 16,
499                         unitinfo_dmaaddr, len, 0, 0);
500         if (hvrc != HvLpEvent_Rc_Good) {
501                 printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
502                                 (int)hvrc);
503                 goto hv_free;
504         }
505
506         wait_for_completion(&we.com);
507
508         for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
509                         unitinfo[unit].rsrcname[0]; unit++) {
510                 if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit,
511                                 unit, "byte", "IBM,iSeries-viotape",
512                                 &unitinfo[unit]))
513                         break;
514         }
515
516  hv_free:
517         iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
518  clear_handler:
519         vio_clearHandler(viomajorsubtype_tape);
520         viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
521 }
522
523 static int __init iseries_vio_init(void)
524 {
525         struct device_node *vio_root;
526         int ret = -ENODEV;
527
528         if (!firmware_has_feature(FW_FEATURE_ISERIES))
529                 goto out;
530
531         iommu_vio_init();
532
533         vio_root = of_find_node_by_path("/vdevice");
534         if (!vio_root)
535                 goto out;
536
537         if (viopath_hostLp == HvLpIndexInvalid) {
538                 vio_set_hostlp();
539                 /* If we don't have a host, bail out */
540                 if (viopath_hostLp == HvLpIndexInvalid)
541                         goto put_node;
542         }
543
544         get_viodasd_info(vio_root);
545         get_viocd_info(vio_root);
546         get_viotape_info(vio_root);
547
548         ret = 0;
549
550  put_node:
551         of_node_put(vio_root);
552  out:
553         return ret;
554 }
555 arch_initcall(iseries_vio_init);