xref: /titanic_50/usr/src/uts/sun4u/starcat/io/fcgp2.c (revision 29e83d4b25fd82feb8e0e0fbe89f7e2a8438533d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * fcgp2.c: Framework gp2 (Safari) fcode ops
31  */
32 #include <sys/types.h>
33 #include <sys/kmem.h>
34 #include <sys/systm.h>
35 #include <sys/pci.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/sunndi.h>
39 #include <sys/ddidmareq.h>
40 #include <sys/modctl.h>
41 #include <sys/ndi_impldefs.h>
42 #include <sys/fcode.h>
43 #include <sys/promif.h>
44 #include <sys/promimpl.h>
45 
46 static int gfc_map_in(dev_info_t *, fco_handle_t, fc_ci_t *);
47 static int gfc_map_out(dev_info_t *, fco_handle_t, fc_ci_t *);
48 static int gfc_register_fetch(dev_info_t *, fco_handle_t, fc_ci_t *);
49 static int gfc_register_store(dev_info_t *, fco_handle_t, fc_ci_t *);
50 static int gfc_claim_address(dev_info_t *, fco_handle_t, fc_ci_t *);
51 static int gfc_claim_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
52 static int gfc_release_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
53 static int gfc_vtop(dev_info_t *, fco_handle_t, fc_ci_t *);
54 static int gfc_master_intr(dev_info_t *, fco_handle_t, fc_ci_t *);
55 
56 static int gfc_config_child(dev_info_t *, fco_handle_t, fc_ci_t *);
57 
58 static int gfc_get_fcode_size(dev_info_t *, fco_handle_t, fc_ci_t *);
59 static int gfc_get_fcode(dev_info_t *, fco_handle_t, fc_ci_t *);
60 
61 int prom_get_fcode_size(char *);
62 int prom_get_fcode(char *, char *);
63 
64 int fcpci_unloadable;
65 int no_advisory_dma;
66 
67 #define	HIADDR(n) ((uint32_t)(((uint64_t)(n) & 0xFFFFFFFF00000000)>> 32))
68 #define	LOADDR(n)((uint32_t)((uint64_t)(n) & 0x00000000FFFFFFFF))
69 #define	LADDR(lo, hi)    (((uint64_t)(hi) << 32) | (uint32_t)(lo))
70 #define	PCI_4GIG_LIMIT 0xFFFFFFFFUL
71 
72 
73 /*
74  * Module linkage information for the kernel.
75  */
76 static struct modlmisc modlmisc = {
77 	&mod_miscops, "FCode gp2 (safari) bus functions %I%"
78 };
79 
80 static struct modlinkage modlinkage = {
81 	MODREV_1, (void *)&modlmisc, NULL
82 };
83 
84 int
85 _init(void)
86 {
87 	return (mod_install(&modlinkage));
88 }
89 
90 int
91 _fini(void)
92 {
93 	if (fcpci_unloadable)
94 		return (mod_remove(&modlinkage));
95 	return (EBUSY);
96 }
97 
98 int
99 _info(struct modinfo *modinfop)
100 {
101 	return (mod_info(&modlinkage, modinfop));
102 }
103 
104 
105 struct gfc_ops_v {
106 	char *svc_name;
107 	fc_ops_t *f;
108 };
109 
110 struct gfc_ops_v gp2_pov[] = {
111 	{	"map-in",		gfc_map_in},
112 	{	"map-out",		gfc_map_out},
113 	{	"rx@",			gfc_register_fetch},
114 	{	"rl@",			gfc_register_fetch},
115 	{	"rw@",			gfc_register_fetch},
116 	{	"rb@",			gfc_register_fetch},
117 	{	"rx!",			gfc_register_store},
118 	{	"rl!",			gfc_register_store},
119 	{	"rw!",			gfc_register_store},
120 	{	"rb!",			gfc_register_store},
121 	{	"claim-address",	gfc_claim_address},
122 	{	"master-interrupt",	gfc_master_intr},
123 	{	"claim-memory",		gfc_claim_memory},
124 	{	"release-memory",	gfc_release_memory},
125 	{	"vtop",			gfc_vtop},
126 	{	FC_CONFIG_CHILD,	gfc_config_child},
127 	{	FC_GET_FCODE_SIZE,	gfc_get_fcode_size},
128 	{	FC_GET_FCODE,		gfc_get_fcode},
129 	{	NULL,			NULL}
130 };
131 
132 struct gfc_ops_v gp2_shared_pov[] = {
133 	{	NULL,			NULL}
134 };
135 
136 static int gp2_map_phys(dev_info_t *, struct regspec *,  caddr_t *,
137     ddi_device_acc_attr_t *, ddi_acc_handle_t *);
138 static void gp2_unmap_phys(ddi_acc_handle_t *);
139 
140 fco_handle_t
141 gp2_fc_ops_alloc_handle(dev_info_t *ap, dev_info_t *child,
142     void *fcode, size_t fcode_size, char *unit_address,
143     char *my_args)
144 {
145 	fco_handle_t rp;
146 	phandle_t h;
147 
148 	rp = kmem_zalloc(sizeof (struct fc_resource_list), KM_SLEEP);
149 	rp->next_handle = fc_ops_alloc_handle(ap, child, fcode, fcode_size,
150 	    unit_address, NULL);
151 	rp->ap = ap;
152 	rp->child = child;
153 	rp->fcode = fcode;
154 	rp->fcode_size = fcode_size;
155 	rp->my_args = my_args;
156 
157 	if (unit_address) {
158 		char *buf;
159 
160 		buf = kmem_zalloc(strlen(unit_address) + 1, KM_SLEEP);
161 		(void) strcpy(buf, unit_address);
162 		rp->unit_address = buf;
163 	}
164 
165 	/*
166 	 * Add the child's nodeid to our table...
167 	 */
168 	h = ddi_get_nodeid(rp->child);
169 	fc_add_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child, h);
170 
171 	return (rp);
172 }
173 
174 void
175 gp2_fc_ops_free_handle(fco_handle_t rp)
176 {
177 	struct fc_resource *ip, *np;
178 
179 	ASSERT(rp);
180 
181 	if (rp->next_handle)
182 		fc_ops_free_handle(rp->next_handle);
183 	if (rp->unit_address)
184 		kmem_free(rp->unit_address, strlen(rp->unit_address) + 1);
185 	if (rp->my_args != NULL)
186 		kmem_free(rp->my_args, strlen(rp->my_args) + 1);
187 
188 	/*
189 	 * Release all the resources from the resource list
190 	 */
191 	for (ip = rp->head; ip != NULL; ip = np) {
192 		np = ip->next;
193 		switch (ip->type) {
194 		case RT_MAP:
195 			FC_DEBUG1(1, CE_CONT, "gp2_fc_ops_free: "
196 			    " map handle - %p\n", ip->fc_map_handle);
197 			break;
198 		case RT_DMA:
199 			/* DMA has to be freed up at exit time */
200 			cmn_err(CE_CONT, "gfc_fc_ops_free: DMA seen!\n");
201 			break;
202 		case RT_CONTIGIOUS:
203 			FC_DEBUG2(1, CE_CONT, "gp2_fc_ops_free: "
204 			    "Free claim-memory resource 0x%lx size 0x%x\n",
205 			    ip->fc_contig_virt, ip->fc_contig_len);
206 
207 			(void) ndi_ra_free(ddi_root_node(),
208 			    (uint64_t)ip->fc_contig_virt,
209 			    ip->fc_contig_len, "gptwo-contigousmem",
210 			    NDI_RA_PASS);
211 
212 			break;
213 		default:
214 			cmn_err(CE_CONT, "gp2_fc_ops_free: "
215 			    "unknown resource type %d\n", ip->type);
216 			break;
217 		}
218 		fc_rem_resource(rp, ip);
219 		kmem_free(ip, sizeof (struct fc_resource));
220 	}
221 	kmem_free(rp, sizeof (struct fc_resource_list));
222 }
223 
224 int
225 gp2_fc_ops(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
226 {
227 	struct gfc_ops_v *pv;
228 	char *name = fc_cell2ptr(cp->svc_name);
229 
230 	ASSERT(rp);
231 
232 	/*
233 	 * First try the generic fc_ops. If the ops is a shared op,
234 	 * also call our local function.
235 	 */
236 	if (fc_ops(ap, rp->next_handle, cp) == 0) {
237 		for (pv = gp2_shared_pov; pv->svc_name != NULL; ++pv)
238 			if (strcmp(pv->svc_name, name) == 0)
239 				return (pv->f(ap, rp, cp));
240 		return (0);
241 	}
242 
243 	for (pv = gp2_pov; pv->svc_name != NULL; ++pv)
244 		if (strcmp(pv->svc_name, name) == 0)
245 			return (pv->f(ap, rp, cp));
246 
247 	FC_DEBUG1(9, CE_CONT, "gp2_fc_ops: <%s> not serviced\n", name);
248 
249 	return (-1);
250 }
251 
252 /*
253  * map-in  (phys.lo phys.hi size -- virt )
254  */
255 static int
256 gfc_map_in(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
257 {
258 	size_t len;
259 	int error;
260 	caddr_t virt;
261 	struct fc_resource *ip;
262 	struct regspec r;
263 	ddi_device_acc_attr_t acc;
264 	ddi_acc_handle_t h;
265 
266 	if (fc_cell2int(cp->nargs) != 3)
267 		return (fc_syntax_error(cp, "nargs must be 3"));
268 
269 	if (fc_cell2int(cp->nresults) < 1)
270 		return (fc_syntax_error(cp, "nresults must be >= 1"));
271 
272 	r.regspec_size = len = fc_cell2size(fc_arg(cp, 0));
273 	r.regspec_bustype = fc_cell2uint(fc_arg(cp, 1));
274 	r.regspec_addr = fc_cell2uint(fc_arg(cp, 2));
275 
276 	acc.devacc_attr_version = DDI_DEVICE_ATTR_V0;
277 	acc.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
278 	acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
279 
280 	FC_DEBUG3(1, CE_CONT, "gfc_map_in: attempting map in "
281 	    "address 0x%08x.%08x length %x\n", r.regspec_bustype,
282 	    r.regspec_addr, r.regspec_size);
283 
284 	error = gp2_map_phys(rp->child, &r, &virt, &acc, &h);
285 
286 	if (error)  {
287 		FC_DEBUG3(1, CE_CONT, "gfc_map_in: map in failed - "
288 		    "address 0x%08x.%08x length %x\n", r.regspec_bustype,
289 		    r.regspec_addr, r.regspec_size);
290 
291 		return (fc_priv_error(cp, "gp2 map-in failed"));
292 	}
293 
294 	FC_DEBUG1(3, CE_CONT, "gp2_map_in: returning virt %p\n", virt);
295 
296 	cp->nresults = fc_int2cell(1);
297 	fc_result(cp, 0) = fc_ptr2cell(virt);
298 
299 	/*
300 	 * Log this resource ...
301 	 */
302 	ip = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
303 	ip->type = RT_MAP;
304 	ip->fc_map_virt = virt;
305 	ip->fc_map_len = len;
306 	ip->fc_map_handle = h;
307 	fc_add_resource(rp, ip);
308 
309 	return (fc_success_op(ap, rp, cp));
310 }
311 
312 /*
313  * map-out ( virt size -- )
314  */
315 static int
316 gfc_map_out(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
317 {
318 	caddr_t virt;
319 	size_t len;
320 	struct fc_resource *ip;
321 
322 	if (fc_cell2int(cp->nargs) != 2)
323 		return (fc_syntax_error(cp, "nargs must be 2"));
324 
325 	virt = fc_cell2ptr(fc_arg(cp, 1));
326 
327 	len = fc_cell2size(fc_arg(cp, 0));
328 
329 	FC_DEBUG2(1, CE_CONT, "gp2_map_out: attempting map out %p %x\n",
330 	    virt, len);
331 
332 	/*
333 	 * Find if this request matches a mapping resource we set up.
334 	 */
335 	fc_lock_resource_list(rp);
336 	for (ip = rp->head; ip != NULL; ip = ip->next) {
337 		if (ip->type != RT_MAP)
338 			continue;
339 		if (ip->fc_map_virt != virt)
340 			continue;
341 		if (ip->fc_map_len == len)
342 			break;
343 	}
344 	fc_unlock_resource_list(rp);
345 
346 	if (ip == NULL)
347 		return (fc_priv_error(cp, "request doesn't match a "
348 		    "known mapping"));
349 
350 	gp2_unmap_phys(&ip->fc_map_handle);
351 
352 	/*
353 	 * remove the resource from the list and release it.
354 	 */
355 	fc_rem_resource(rp, ip);
356 	kmem_free(ip, sizeof (struct fc_resource));
357 
358 	cp->nresults = fc_int2cell(0);
359 	return (fc_success_op(ap, rp, cp));
360 }
361 
362 static int
363 gfc_register_fetch(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
364 {
365 	size_t len;
366 	caddr_t virt;
367 	int error = 0;
368 	uint64_t x;
369 	uint32_t l;
370 	uint16_t w;
371 	uint8_t b;
372 	char *name = fc_cell2ptr(cp->svc_name);
373 	struct fc_resource *ip;
374 
375 	if (fc_cell2int(cp->nargs) != 1)
376 		return (fc_syntax_error(cp, "nargs must be 1"));
377 
378 	if (fc_cell2int(cp->nresults) < 1)
379 		return (fc_syntax_error(cp, "nresults must be >= 1"));
380 
381 	virt = fc_cell2ptr(fc_arg(cp, 0));
382 
383 	/*
384 	 * Determine the access width .. we can switch on the 2nd
385 	 * character of the name which is "rx@", "rl@", "rb@" or "rw@"
386 	 */
387 	switch (*(name + 1)) {
388 	case 'x':	len = sizeof (x); break;
389 	case 'l':	len = sizeof (l); break;
390 	case 'w':	len = sizeof (w); break;
391 	case 'b':	len = sizeof (b); break;
392 	}
393 
394 	/*
395 	 * Check the alignment ...
396 	 */
397 	if (((intptr_t)virt & (len - 1)) != 0)
398 		return (fc_priv_error(cp, "unaligned access"));
399 
400 	/*
401 	 * Find if this virt is 'within' a request we know about
402 	 */
403 	fc_lock_resource_list(rp);
404 	for (ip = rp->head; ip != NULL; ip = ip->next) {
405 		if (ip->type == RT_MAP) {
406 		    if ((virt >= (caddr_t)ip->fc_map_virt) && ((virt + len) <=
407 			((caddr_t)ip->fc_map_virt + ip->fc_map_len)))
408 				break;
409 		} else if (ip->type == RT_CONTIGIOUS) {
410 		    if ((virt >= (caddr_t)ip->fc_contig_virt) && ((virt + len)
411 			<= ((caddr_t)ip->fc_contig_virt + ip->fc_contig_len)))
412 				break;
413 		}
414 	}
415 	fc_unlock_resource_list(rp);
416 
417 	if (ip == NULL) {
418 		return (fc_priv_error(cp, "request not within a "
419 		    "known mapping or contigious adddress"));
420 	}
421 
422 	switch (len) {
423 	case sizeof (x):
424 		if (ip->type == RT_MAP)
425 		    error = ddi_peek64(rp->child,
426 			(int64_t *)virt, (int64_t *)&x);
427 		else /* RT_CONTIGIOUS */
428 		    x = *(int64_t *)virt;
429 		break;
430 	case sizeof (l):
431 		if (ip->type == RT_MAP)
432 		    error = ddi_peek32(rp->child,
433 			(int32_t *)virt, (int32_t *)&l);
434 		else /* RT_CONTIGIOUS */
435 		    l = *(int32_t *)virt;
436 		break;
437 	case sizeof (w):
438 		if (ip->type == RT_MAP)
439 		    error = ddi_peek16(rp->child,
440 			(int16_t *)virt, (int16_t *)&w);
441 		else /* RT_CONTIGIOUS */
442 		    w = *(int16_t *)virt;
443 		break;
444 	case sizeof (b):
445 		if (ip->type == RT_MAP)
446 		    error = ddi_peek8(rp->child,
447 			(int8_t *)virt, (int8_t *)&b);
448 		else /* RT_CONTIGIOUS */
449 		    b = *(int8_t *)virt;
450 		break;
451 	}
452 
453 	if (error) {
454 		FC_DEBUG2(1, CE_CONT, "gfc_register_fetch: access error "
455 		    "accessing virt %p len %d\n", virt, len);
456 		return (fc_priv_error(cp, "access error"));
457 	}
458 
459 	cp->nresults = fc_int2cell(1);
460 	switch (len) {
461 	case sizeof (x): fc_result(cp, 0) = x; break;
462 	case sizeof (l): fc_result(cp, 0) = fc_uint32_t2cell(l); break;
463 	case sizeof (w): fc_result(cp, 0) = fc_uint16_t2cell(w); break;
464 	case sizeof (b): fc_result(cp, 0) = fc_uint8_t2cell(b); break;
465 	}
466 	return (fc_success_op(ap, rp, cp));
467 }
468 
469 static int
470 gfc_register_store(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
471 {
472 	size_t len;
473 	caddr_t virt;
474 	uint64_t x;
475 	uint32_t l;
476 	uint16_t w;
477 	uint8_t b;
478 	char *name = fc_cell2ptr(cp->svc_name);
479 	struct fc_resource *ip;
480 	int error = 0;
481 
482 	if (fc_cell2int(cp->nargs) != 2)
483 		return (fc_syntax_error(cp, "nargs must be 2"));
484 
485 	virt = fc_cell2ptr(fc_arg(cp, 0));
486 
487 	/*
488 	 * Determine the access width .. we can switch on the 2nd
489 	 * character of the name which is "rx!", "rl!", "rb!" or "rw!"
490 	 */
491 	switch (*(name + 1)) {
492 	case 'x': len = sizeof (x); x = fc_arg(cp, 1); break;
493 	case 'l': len = sizeof (l); l = fc_cell2uint32_t(fc_arg(cp, 1)); break;
494 	case 'w': len = sizeof (w); w = fc_cell2uint16_t(fc_arg(cp, 1)); break;
495 	case 'b': len = sizeof (b); b = fc_cell2uint8_t(fc_arg(cp, 1)); break;
496 	}
497 
498 	/*
499 	 * Check the alignment ...
500 	 */
501 	if (((intptr_t)virt & (len - 1)) != 0)
502 		return (fc_priv_error(cp, "unaligned access"));
503 
504 	/*
505 	 * Find if this virt is 'within' a request we know about
506 	 */
507 	fc_lock_resource_list(rp);
508 	for (ip = rp->head; ip != NULL; ip = ip->next) {
509 		if (ip->type == RT_MAP) {
510 		    if ((virt >= (caddr_t)ip->fc_map_virt) && ((virt + len) <=
511 			((caddr_t)ip->fc_map_virt + ip->fc_map_len)))
512 				break;
513 		} else if (ip->type == RT_CONTIGIOUS) {
514 		    if ((virt >= (caddr_t)ip->fc_contig_virt) && ((virt + len)
515 			<= ((caddr_t)ip->fc_contig_virt + ip->fc_contig_len)))
516 				break;
517 		}
518 	}
519 	fc_unlock_resource_list(rp);
520 
521 	if (ip == NULL)
522 		return (fc_priv_error(cp, "request not within a "
523 		    "known mapping or contigious address"));
524 
525 	switch (len) {
526 	case sizeof (x):
527 		if (ip->type == RT_MAP)
528 			error = ddi_poke64(rp->child, (int64_t *)virt, x);
529 		else if (ip->type == RT_CONTIGIOUS)
530 			*(uint64_t *)virt = x;
531 		break;
532 	case sizeof (l):
533 		if (ip->type == RT_MAP)
534 			error = ddi_poke32(rp->child, (int32_t *)virt, l);
535 		else if (ip->type == RT_CONTIGIOUS)
536 			*(uint32_t *)virt = l;
537 		break;
538 	case sizeof (w):
539 		if (ip->type == RT_MAP)
540 			error = ddi_poke16(rp->child, (int16_t *)virt, w);
541 		else if (ip->type == RT_CONTIGIOUS)
542 			*(uint16_t *)virt = w;
543 		break;
544 	case sizeof (b):
545 		if (ip->type == RT_MAP)
546 			error = ddi_poke8(rp->child, (int8_t *)virt, b);
547 		else if (ip->type == RT_CONTIGIOUS)
548 			*(uint8_t *)virt = b;
549 		break;
550 	}
551 
552 	if (error == DDI_FAILURE) {
553 		FC_DEBUG2(1, CE_CONT, "gfc_register_store: access error "
554 		    "accessing virt %p len %d\n", virt, len);
555 		return (fc_priv_error(cp, "access error"));
556 	}
557 
558 	cp->nresults = fc_int2cell(0);
559 	return (fc_success_op(ap, rp, cp));
560 }
561 
562 static int
563 gfc_master_intr(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
564 {
565 	int xt, portid;
566 
567 	if (fc_cell2int(cp->nargs) != 2)
568 		return (fc_syntax_error(cp, "nargs must be 4"));
569 
570 	if (fc_cell2int(cp->nresults) < 1)
571 		return (fc_syntax_error(cp, "nresults must be >= 1"));
572 
573 	xt = fc_cell2int(fc_arg(cp, 1));
574 	portid = fc_cell2int(fc_arg(cp, 0));
575 
576 	FC_DEBUG2(1, CE_CONT, "gfc_master_intr: reset-int-xt=%x portid=%x",
577 	    xt, portid);
578 
579 	cp->nresults = fc_int2cell(1);
580 	fc_result(cp, 0) = 0;
581 
582 	return (fc_success_op(ap, rp, cp));
583 }
584 
585 /*
586  * gfc_claim_address
587  *
588  * claim-address (size.lo size.hi type align bar portid -- base.lo base.hi )
589  */
590 static int
591 gfc_claim_address(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
592 {
593 	int bar, portid;
594 	uint64_t exp, slot, port, slice;
595 	uint64_t paddr;
596 
597 	if (fc_cell2int(cp->nargs) != 6)
598 		return (fc_syntax_error(cp, "nargs must be 6"));
599 
600 	if (fc_cell2int(cp->nresults) < 2)
601 		return (fc_syntax_error(cp, "nresults must be 2"));
602 
603 	bar = fc_cell2int(fc_arg(cp, 1));
604 	portid = fc_cell2int(fc_arg(cp, 0));
605 
606 	exp = portid >> 5;
607 	slot = (0x8 & portid) >> 3;
608 	port = portid & 0x1;
609 
610 	switch (bar) {
611 	case 0: /* PCI IO Bus A */
612 		paddr = (exp << 28) | (port << 26) | (slot << 27) |
613 		    ((uint64_t)0x402 << 32);
614 
615 		break;
616 	case 1: /* PCI Memory Bus A */
617 		slice = (exp * 2) + slot + 1;
618 
619 		paddr = ((uint64_t)1 << 42) | ((uint64_t)slice << 34) |
620 		    ((uint64_t)port << 33);
621 
622 		break;
623 	case 2: /* PCI IO Bus B */
624 		paddr = (exp << 28) | (port << 26) | (slot << 27) |
625 		    ((uint64_t)0x402 << 32)  | (1 << 25);
626 
627 		break;
628 	case 3: /* PCI Memory Bus B */
629 		slice = (exp * 2) + slot + 1;
630 
631 		paddr = ((uint64_t)1 << 42) | ((uint64_t)slice << 34) |
632 		    ((uint64_t)port << 33);
633 
634 		paddr |= ((uint64_t)1 << 32);
635 
636 		break;
637 	default:
638 		cmn_err(CE_WARN,
639 		    "gfc_claim_address - invalid BAR=0x%x\n", bar);
640 
641 		return (fc_syntax_error(cp, "invalid argument"));
642 	}
643 
644 	FC_DEBUG1(1, CE_CONT, "gfc_claim_address: returning 0x%lx\n", paddr);
645 
646 	cp->nresults = fc_int2cell(2);
647 	fc_result(cp, 0) = LOADDR(paddr);
648 	fc_result(cp, 1) = HIADDR(paddr);
649 
650 	return (fc_success_op(ap, rp, cp));
651 }
652 
653 /*
654  * gfc_claim_memory
655  *
656  * claim-memory ( align size vhint -- vaddr)
657  */
658 static int
659 gfc_claim_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
660 {
661 	int align, size, vhint;
662 	ndi_ra_request_t request;
663 	uint64_t answer, alen;
664 	struct fc_resource *ip;
665 
666 	if (fc_cell2int(cp->nargs) != 3)
667 		return (fc_syntax_error(cp, "nargs must be 3"));
668 
669 	if (fc_cell2int(cp->nresults) < 1)
670 		return (fc_syntax_error(cp, "nresults must be >= 1"));
671 
672 	vhint = fc_cell2int(fc_arg(cp, 2));
673 	size = fc_cell2int(fc_arg(cp, 1));
674 	align = fc_cell2int(fc_arg(cp, 0));
675 
676 	FC_DEBUG3(1, CE_CONT, "gfc_claim_memory: align=0x%x size=0x%x "
677 	    "vhint=0x%x\n", align, size, vhint);
678 
679 	if (size == 0) {
680 		cmn_err(CE_WARN, " gfc_claim_memory - unable to allocate "
681 		    "contigiuos memory of size zero\n");
682 		return (fc_priv_error(cp, "allocation error"));
683 	}
684 
685 	if (vhint) {
686 		cmn_err(CE_WARN, "gfc_claim_memory - vhint is not zero "
687 		    "vhint=0x%x - Ignoring Argument\n", vhint);
688 	}
689 
690 	bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
691 	request.ra_flags  = NDI_RA_ALLOC_BOUNDED;
692 	request.ra_boundbase = 0;
693 	request.ra_boundlen = 0xffffffff;
694 	request.ra_len = size;
695 	request.ra_align_mask = align - 1;
696 
697 	if (ndi_ra_alloc(ddi_root_node(), &request, &answer, &alen,
698 	    "gptwo-contigousmem", NDI_RA_PASS) != NDI_SUCCESS) {
699 		cmn_err(CE_WARN, " gfc_claim_memory - unable to allocate "
700 		    "contigiuos memory\n");
701 		return (fc_priv_error(cp, "allocation error"));
702 
703 	}
704 
705 	FC_DEBUG2(1, CE_CONT, "gfc_claim_memory: address allocated=0x%lx "
706 	    "size=0x%x\n", answer, alen);
707 
708 	cp->nresults = fc_int2cell(1);
709 	fc_result(cp, 0) = answer;
710 
711 	/*
712 	 * Log this resource ...
713 	 */
714 	ip = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
715 	ip->type = RT_CONTIGIOUS;
716 	ip->fc_contig_virt = (void *)answer;
717 	ip->fc_contig_len = size;
718 	fc_add_resource(rp, ip);
719 
720 	return (fc_success_op(ap, rp, cp));
721 }
722 
723 /*
724  * gfc_release_memory
725  *
726  * release-memory ( size vaddr -- )
727  */
728 static int
729 gfc_release_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
730 {
731 	int32_t vaddr, size;
732 	struct fc_resource *ip;
733 
734 	if (fc_cell2int(cp->nargs) != 2)
735 		return (fc_syntax_error(cp, "nargs must be 2"));
736 
737 	if (fc_cell2int(cp->nresults) != 0)
738 		return (fc_syntax_error(cp, "nresults must be 0"));
739 
740 	vaddr = fc_cell2int(fc_arg(cp, 1));
741 	size = fc_cell2int(fc_arg(cp, 0));
742 
743 	FC_DEBUG2(1, CE_CONT, "gfc_release_memory: vaddr=0x%x size=0x%x\n",
744 	    vaddr, size);
745 	/*
746 	 * Find if this request matches a mapping resource we set up.
747 	 */
748 	fc_lock_resource_list(rp);
749 	for (ip = rp->head; ip != NULL; ip = ip->next) {
750 		if (ip->type != RT_CONTIGIOUS)
751 			continue;
752 		if (ip->fc_contig_virt != (void *)(uintptr_t)vaddr)
753 			continue;
754 		if (ip->fc_contig_len == size)
755 			break;
756 	}
757 	fc_unlock_resource_list(rp);
758 
759 	if (ip == NULL)
760 		return (fc_priv_error(cp, "request doesn't match a "
761 		    "known mapping"));
762 
763 	(void) ndi_ra_free(ddi_root_node(), vaddr, size,
764 	    "gptwo-contigousmem", NDI_RA_PASS);
765 
766 	/*
767 	 * remove the resource from the list and release it.
768 	 */
769 	fc_rem_resource(rp, ip);
770 	kmem_free(ip, sizeof (struct fc_resource));
771 
772 	cp->nresults = fc_int2cell(0);
773 
774 	return (fc_success_op(ap, rp, cp));
775 }
776 
777 /*
778  * gfc_vtop
779  *
780  * vtop ( vaddr -- paddr.lo paddr.hi)
781  */
782 static int
783 gfc_vtop(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
784 {
785 	int vaddr;
786 	uint64_t paddr;
787 	struct fc_resource *ip;
788 
789 	if (fc_cell2int(cp->nargs) != 1)
790 		return (fc_syntax_error(cp, "nargs must be 1"));
791 
792 	if (fc_cell2int(cp->nresults) >= 3)
793 		return (fc_syntax_error(cp, "nresults must be less than 2"));
794 
795 	vaddr = fc_cell2int(fc_arg(cp, 0));
796 
797 	/*
798 	 * Find if this request matches a mapping resource we set up.
799 	 */
800 	fc_lock_resource_list(rp);
801 	for (ip = rp->head; ip != NULL; ip = ip->next) {
802 		if (ip->type != RT_CONTIGIOUS)
803 			continue;
804 		if (ip->fc_contig_virt == (void *)(uintptr_t)vaddr)
805 				break;
806 	}
807 	fc_unlock_resource_list(rp);
808 
809 	if (ip == NULL)
810 		return (fc_priv_error(cp, "request doesn't match a "
811 		    "known mapping"));
812 
813 
814 	paddr = va_to_pa((void *)(uintptr_t)vaddr);
815 
816 	FC_DEBUG2(1, CE_CONT, "gfc_vtop: vaddr=0x%x paddr=0x%x\n",
817 	    vaddr, paddr);
818 
819 	cp->nresults = fc_int2cell(2);
820 
821 	fc_result(cp, 0) = paddr;
822 	fc_result(cp, 1) = 0;
823 
824 	return (fc_success_op(ap, rp, cp));
825 }
826 
827 static int
828 gfc_config_child(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
829 {
830 	fc_phandle_t h;
831 
832 	if (fc_cell2int(cp->nargs) != 0)
833 		return (fc_syntax_error(cp, "nargs must be 0"));
834 
835 	if (fc_cell2int(cp->nresults) < 1)
836 		return (fc_syntax_error(cp, "nresults must be >= 1"));
837 
838 	h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child);
839 
840 	cp->nresults = fc_int2cell(1);
841 	fc_result(cp, 0) = fc_phandle2cell(h);
842 
843 	return (fc_success_op(ap, rp, cp));
844 }
845 
846 static int
847 gfc_get_fcode(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
848 {
849 	caddr_t name_virt, fcode_virt;
850 	char *name, *fcode;
851 	int fcode_len, status;
852 
853 	if (fc_cell2int(cp->nargs) != 3)
854 		return (fc_syntax_error(cp, "nargs must be 3"));
855 
856 	if (fc_cell2int(cp->nresults) < 1)
857 		return (fc_syntax_error(cp, "nresults must be >= 1"));
858 
859 	name_virt = fc_cell2ptr(fc_arg(cp, 0));
860 
861 	fcode_virt = fc_cell2ptr(fc_arg(cp, 1));
862 
863 	fcode_len = fc_cell2int(fc_arg(cp, 2));
864 
865 	name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
866 
867 	if (copyinstr(fc_cell2ptr(name_virt), name,
868 	    FC_SVC_NAME_LEN - 1, NULL))  {
869 		FC_DEBUG1(1, CE_CONT, "gfc_get_fcode: "
870 		    "fault copying in drop in name %p\n", name_virt);
871 		status = 0;
872 	} else {
873 
874 		fcode = kmem_zalloc(fcode_len, KM_SLEEP);
875 
876 		if ((status = prom_get_fcode(name, fcode)) != 0) {
877 
878 			if (copyout((void *)fcode, (void *)fcode_virt,
879 			    fcode_len)) {
880 				cmn_err(CE_WARN, " gfc_get_fcode: Unable "
881 				    "to copy out fcode image\n");
882 				status = 0;
883 			}
884 		}
885 
886 		kmem_free(fcode, fcode_len);
887 	}
888 
889 	kmem_free(name, FC_SVC_NAME_LEN);
890 
891 	cp->nresults = fc_int2cell(1);
892 	fc_result(cp, 0) = status;
893 
894 	return (fc_success_op(ap, rp, cp));
895 }
896 
897 static int
898 gfc_get_fcode_size(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
899 {
900 	caddr_t virt;
901 	char *name;
902 	int len;
903 
904 	if (fc_cell2int(cp->nargs) != 1)
905 		return (fc_syntax_error(cp, "nargs must be 1"));
906 
907 	if (fc_cell2int(cp->nresults) < 1)
908 		return (fc_syntax_error(cp, "nresults must be >= 1"));
909 
910 	virt = fc_cell2ptr(fc_arg(cp, 0));
911 
912 	name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
913 
914 	if (copyinstr(fc_cell2ptr(virt), name,
915 	    FC_SVC_NAME_LEN - 1, NULL))  {
916 		FC_DEBUG1(1, CE_CONT, "gfc_get_fcode_size: "
917 		    "fault copying in drop in name %p\n", virt);
918 		len = 0;
919 	} else {
920 
921 		len = prom_get_fcode_size(name);
922 	}
923 
924 	kmem_free(name, FC_SVC_NAME_LEN);
925 
926 	cp->nresults = fc_int2cell(1);
927 	fc_result(cp, 0) = len;
928 
929 	return (fc_success_op(ap, rp, cp));
930 }
931 
932 static int
933 gp2_map_phys(dev_info_t *dip, struct regspec *phys_spec,
934 	caddr_t *addrp, ddi_device_acc_attr_t *accattrp,
935 	ddi_acc_handle_t *handlep)
936 {
937 	ddi_map_req_t mr;
938 	ddi_acc_hdl_t *hp;
939 	int result;
940 	struct regspec *ph;
941 
942 	*handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL);
943 	hp = impl_acc_hdl_get(*handlep);
944 	hp->ah_vers = VERS_ACCHDL;
945 	hp->ah_dip = dip;
946 	hp->ah_rnumber = 0;
947 	hp->ah_offset = 0;
948 	hp->ah_len = 0;
949 	hp->ah_acc = *accattrp;
950 	ph = kmem_zalloc(sizeof (struct regspec), KM_SLEEP);
951 	*ph = *phys_spec;
952 	hp->ah_bus_private = ph;	/* cache a copy of the reg spec */
953 
954 	mr.map_op = DDI_MO_MAP_LOCKED;
955 	mr.map_type = DDI_MT_REGSPEC;
956 	mr.map_obj.rp = (struct regspec *)phys_spec;
957 	mr.map_prot = PROT_READ | PROT_WRITE;
958 	mr.map_flags = DDI_MF_KERNEL_MAPPING;
959 	mr.map_handlep = hp;
960 	mr.map_vers = DDI_MAP_VERSION;
961 
962 	result = ddi_map(dip, &mr, 0, 0, addrp);
963 
964 	if (result != DDI_SUCCESS) {
965 		impl_acc_hdl_free(*handlep);
966 		*handlep = (ddi_acc_handle_t)NULL;
967 	} else {
968 		hp->ah_addr = *addrp;
969 	}
970 
971 	return (result);
972 }
973 
974 static void
975 gp2_unmap_phys(ddi_acc_handle_t *handlep)
976 {
977 	ddi_map_req_t mr;
978 	ddi_acc_hdl_t *hp;
979 	struct regspec_t *ph;
980 
981 	hp = impl_acc_hdl_get(*handlep);
982 	ASSERT(hp);
983 	ph = hp->ah_bus_private;
984 
985 	mr.map_op = DDI_MO_UNMAP;
986 	mr.map_type = DDI_MT_REGSPEC;
987 	mr.map_obj.rp = (struct regspec *)ph;
988 	mr.map_prot = PROT_READ | PROT_WRITE;
989 	mr.map_flags = DDI_MF_KERNEL_MAPPING;
990 	mr.map_handlep = hp;
991 	mr.map_vers = DDI_MAP_VERSION;
992 
993 	(void) ddi_map(hp->ah_dip, &mr, hp->ah_offset,
994 		hp->ah_len, &hp->ah_addr);
995 
996 	impl_acc_hdl_free(*handlep);
997 	kmem_free(ph, sizeof (struct regspec));	/* Free the cached copy */
998 	*handlep = (ddi_acc_handle_t)NULL;
999 }
1000