Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/powerpc/platforms/iseries/vio.c
10820 views
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);
557
558