blob: 00b6730bc48f07392f0604eab8b472badb121242 [file] [log] [blame]
Stephen Rothwellb833b482007-10-11 14:57:26 +10001/*
2 * Legacy iSeries specific vio initialisation
3 * that needs to be built in (not a module).
4 *
Stephen Rothwellbfce5c32007-10-12 15:37:32 +10005 * © Copyright 2007 IBM Corporation
Stephen Rothwellb833b482007-10-11 14:57:26 +10006 * 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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090025#include <linux/slab.h>
Stephen Rothwellb833b482007-10-11 14:57:26 +100026#include <linux/completion.h>
27#include <linux/proc_fs.h>
Stephen Rothwell8251b4c2007-10-11 14:59:54 +100028#include <linux/module.h>
Stephen Rothwellb833b482007-10-11 14:57:26 +100029
30#include <asm/firmware.h>
Stephen Rothwell8251b4c2007-10-11 14:59:54 +100031#include <asm/vio.h>
Stephen Rothwellb833b482007-10-11 14:57:26 +100032#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
Stephen Rothwell7465ce02007-10-11 14:58:31 +100050struct 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
Stephen Rothwell8251b4c2007-10-11 14:59:54 +100062static struct property *new_property(const char *name, int length,
Stephen Rothwellb833b482007-10-11 14:57:26 +100063 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
Adrian Bunk16e543f2008-02-14 08:30:55 +110078static void free_property(struct property *np)
Stephen Rothwellb833b482007-10-11 14:57:26 +100079{
80 kfree(np);
81}
82
Stephen Rothwell8251b4c2007-10-11 14:59:54 +100083static struct device_node *new_node(const char *path,
Stephen Rothwellb833b482007-10-11 14:57:26 +100084 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
Stephen Rothwell8251b4c2007-10-11 14:59:54 +1000102static void free_node(struct device_node *np)
Stephen Rothwellb833b482007-10-11 14:57:26 +1000103{
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
Stephen Rothwell8251b4c2007-10-11 14:59:54 +1000118static int add_string_property(struct device_node *np, const char *name,
Stephen Rothwellb833b482007-10-11 14:57:26 +1000119 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
Stephen Rothwell8251b4c2007-10-11 14:59:54 +1000129static int add_raw_property(struct device_node *np, const char *name,
Stephen Rothwellb833b482007-10-11 14:57:26 +1000130 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
Stephen Rothwell8251b4c2007-10-11 14:59:54 +1000140static 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 */
191struct 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}
210EXPORT_SYMBOL_GPL(vio_create_viodasd);
211
212static 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
257static 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
263retry:
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
312static 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
Stephen Rothwellb833b482007-10-11 14:57:26 +1000335static void __init handle_cd_event(struct HvLpEvent *event)
336{
337 struct viocdlpevent *bevent;
Stephen Rothwell7465ce02007-10-11 14:58:31 +1000338 struct vio_waitevent *pwe;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000339
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:
Stephen Rothwell7465ce02007-10-11 14:58:31 +1000358 pwe = (struct vio_waitevent *)event->xCorrelationToken;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000359 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
375static void __init get_viocd_info(struct device_node *vio_root)
376{
377 HvLpEvent_Rc hvrc;
378 u32 unit;
Stephen Rothwell7465ce02007-10-11 14:58:31 +1000379 struct vio_waitevent we;
380 struct vio_resource *unitinfo;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000381 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++) {
Stephen Rothwell8251b4c2007-10-11 14:59:54 +1000433 if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit,
434 "block", "IBM,iSeries-viocd", &unitinfo[unit]))
435 break;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000436 }
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
Stephen Rothwell7465ce02007-10-11 14:58:31 +1000446/* Handle interrupt events for tape */
447static 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
467static 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
Michael Ellermanb0ff1532010-01-12 21:10:56 +0000477 init_completion(&we.com);
478
Stephen Rothwell7465ce02007-10-11 14:58:31 +1000479 ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
480 if (ret) {
481 printk(KERN_WARNING "get_viotape_info: "
482 "error on viopath_open to hostlp %d\n", ret);
483 return;
484 }
485
486 vio_setHandler(viomajorsubtype_tape, handle_tape_event);
487
488 unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
489 if (!unitinfo)
490 goto clear_handler;
491
492 memset(unitinfo, 0, len);
493
494 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
495 HvLpEvent_Type_VirtualIo,
496 viomajorsubtype_tape | viotapegetinfo,
497 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
498 viopath_sourceinst(viopath_hostLp),
499 viopath_targetinst(viopath_hostLp),
500 (u64)(unsigned long)&we, VIOVERSION << 16,
501 unitinfo_dmaaddr, len, 0, 0);
502 if (hvrc != HvLpEvent_Rc_Good) {
503 printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
504 (int)hvrc);
505 goto hv_free;
506 }
507
508 wait_for_completion(&we.com);
509
510 for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
511 unitinfo[unit].rsrcname[0]; unit++) {
Stephen Rothwell8251b4c2007-10-11 14:59:54 +1000512 if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit,
513 unit, "byte", "IBM,iSeries-viotape",
514 &unitinfo[unit]))
515 break;
Stephen Rothwell7465ce02007-10-11 14:58:31 +1000516 }
517
518 hv_free:
519 iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
520 clear_handler:
521 vio_clearHandler(viomajorsubtype_tape);
522 viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
523}
524
Stephen Rothwellb833b482007-10-11 14:57:26 +1000525static int __init iseries_vio_init(void)
526{
527 struct device_node *vio_root;
Stephen Rothwell79923442007-11-06 17:26:42 +1100528 int ret = -ENODEV;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000529
530 if (!firmware_has_feature(FW_FEATURE_ISERIES))
Stephen Rothwell79923442007-11-06 17:26:42 +1100531 goto out;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000532
533 iommu_vio_init();
534
535 vio_root = of_find_node_by_path("/vdevice");
536 if (!vio_root)
Stephen Rothwell79923442007-11-06 17:26:42 +1100537 goto out;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000538
539 if (viopath_hostLp == HvLpIndexInvalid) {
540 vio_set_hostlp();
541 /* If we don't have a host, bail out */
542 if (viopath_hostLp == HvLpIndexInvalid)
543 goto put_node;
544 }
545
Stephen Rothwell8251b4c2007-10-11 14:59:54 +1000546 get_viodasd_info(vio_root);
Stephen Rothwellb833b482007-10-11 14:57:26 +1000547 get_viocd_info(vio_root);
Stephen Rothwell7465ce02007-10-11 14:58:31 +1000548 get_viotape_info(vio_root);
Stephen Rothwellb833b482007-10-11 14:57:26 +1000549
Stephen Rothwell79923442007-11-06 17:26:42 +1100550 ret = 0;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000551
552 put_node:
553 of_node_put(vio_root);
Stephen Rothwell79923442007-11-06 17:26:42 +1100554 out:
555 return ret;
Stephen Rothwellb833b482007-10-11 14:57:26 +1000556}
557arch_initcall(iseries_vio_init);