• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/powerpc/platforms/iseries/
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
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);
557