103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
503831d35Sstevel * Common Development and Distribution License (the "License").
603831d35Sstevel * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel
2203831d35Sstevel /*
23*f500b196SRichard Bean * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2403831d35Sstevel * Use is subject to license terms.
2503831d35Sstevel */
2603831d35Sstevel
2703831d35Sstevel /*
2803831d35Sstevel * fcgp2.c: Framework gp2 (Safari) fcode ops
2903831d35Sstevel */
3003831d35Sstevel #include <sys/types.h>
3103831d35Sstevel #include <sys/kmem.h>
3203831d35Sstevel #include <sys/systm.h>
3303831d35Sstevel #include <sys/pci.h>
3403831d35Sstevel #include <sys/ddi.h>
3503831d35Sstevel #include <sys/sunddi.h>
3603831d35Sstevel #include <sys/sunndi.h>
3703831d35Sstevel #include <sys/ddidmareq.h>
3803831d35Sstevel #include <sys/modctl.h>
3903831d35Sstevel #include <sys/ndi_impldefs.h>
4003831d35Sstevel #include <sys/fcode.h>
4103831d35Sstevel #include <sys/promif.h>
4203831d35Sstevel #include <sys/promimpl.h>
4303831d35Sstevel
4403831d35Sstevel static int gfc_map_in(dev_info_t *, fco_handle_t, fc_ci_t *);
4503831d35Sstevel static int gfc_map_out(dev_info_t *, fco_handle_t, fc_ci_t *);
4603831d35Sstevel static int gfc_register_fetch(dev_info_t *, fco_handle_t, fc_ci_t *);
4703831d35Sstevel static int gfc_register_store(dev_info_t *, fco_handle_t, fc_ci_t *);
4803831d35Sstevel static int gfc_claim_address(dev_info_t *, fco_handle_t, fc_ci_t *);
4903831d35Sstevel static int gfc_claim_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
5003831d35Sstevel static int gfc_release_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
5103831d35Sstevel static int gfc_vtop(dev_info_t *, fco_handle_t, fc_ci_t *);
5203831d35Sstevel static int gfc_master_intr(dev_info_t *, fco_handle_t, fc_ci_t *);
5303831d35Sstevel
5403831d35Sstevel static int gfc_config_child(dev_info_t *, fco_handle_t, fc_ci_t *);
5503831d35Sstevel
5603831d35Sstevel static int gfc_get_fcode_size(dev_info_t *, fco_handle_t, fc_ci_t *);
5703831d35Sstevel static int gfc_get_fcode(dev_info_t *, fco_handle_t, fc_ci_t *);
5803831d35Sstevel
5903831d35Sstevel int prom_get_fcode_size(char *);
6003831d35Sstevel int prom_get_fcode(char *, char *);
6103831d35Sstevel
6203831d35Sstevel int fcpci_unloadable;
6303831d35Sstevel int no_advisory_dma;
6403831d35Sstevel
6503831d35Sstevel #define HIADDR(n) ((uint32_t)(((uint64_t)(n) & 0xFFFFFFFF00000000)>> 32))
6603831d35Sstevel #define LOADDR(n)((uint32_t)((uint64_t)(n) & 0x00000000FFFFFFFF))
6703831d35Sstevel #define LADDR(lo, hi) (((uint64_t)(hi) << 32) | (uint32_t)(lo))
6803831d35Sstevel #define PCI_4GIG_LIMIT 0xFFFFFFFFUL
6903831d35Sstevel
7003831d35Sstevel
7103831d35Sstevel /*
7203831d35Sstevel * Module linkage information for the kernel.
7303831d35Sstevel */
7403831d35Sstevel static struct modlmisc modlmisc = {
75*f500b196SRichard Bean &mod_miscops, "FCode gp2 (safari) bus functions"
7603831d35Sstevel };
7703831d35Sstevel
7803831d35Sstevel static struct modlinkage modlinkage = {
7903831d35Sstevel MODREV_1, (void *)&modlmisc, NULL
8003831d35Sstevel };
8103831d35Sstevel
8203831d35Sstevel int
_init(void)8303831d35Sstevel _init(void)
8403831d35Sstevel {
8503831d35Sstevel return (mod_install(&modlinkage));
8603831d35Sstevel }
8703831d35Sstevel
8803831d35Sstevel int
_fini(void)8903831d35Sstevel _fini(void)
9003831d35Sstevel {
9103831d35Sstevel if (fcpci_unloadable)
9203831d35Sstevel return (mod_remove(&modlinkage));
9303831d35Sstevel return (EBUSY);
9403831d35Sstevel }
9503831d35Sstevel
9603831d35Sstevel int
_info(struct modinfo * modinfop)9703831d35Sstevel _info(struct modinfo *modinfop)
9803831d35Sstevel {
9903831d35Sstevel return (mod_info(&modlinkage, modinfop));
10003831d35Sstevel }
10103831d35Sstevel
10203831d35Sstevel
10303831d35Sstevel struct gfc_ops_v {
10403831d35Sstevel char *svc_name;
10503831d35Sstevel fc_ops_t *f;
10603831d35Sstevel };
10703831d35Sstevel
10803831d35Sstevel struct gfc_ops_v gp2_pov[] = {
10903831d35Sstevel { "map-in", gfc_map_in},
11003831d35Sstevel { "map-out", gfc_map_out},
11103831d35Sstevel { "rx@", gfc_register_fetch},
11203831d35Sstevel { "rl@", gfc_register_fetch},
11303831d35Sstevel { "rw@", gfc_register_fetch},
11403831d35Sstevel { "rb@", gfc_register_fetch},
11503831d35Sstevel { "rx!", gfc_register_store},
11603831d35Sstevel { "rl!", gfc_register_store},
11703831d35Sstevel { "rw!", gfc_register_store},
11803831d35Sstevel { "rb!", gfc_register_store},
11903831d35Sstevel { "claim-address", gfc_claim_address},
12003831d35Sstevel { "master-interrupt", gfc_master_intr},
12125cf1a30Sjl139090 { "claim-memory", gfc_claim_memory},
12225cf1a30Sjl139090 { "release-memory", gfc_release_memory},
12325cf1a30Sjl139090 { "vtop", gfc_vtop},
12403831d35Sstevel { FC_CONFIG_CHILD, gfc_config_child},
12503831d35Sstevel { FC_GET_FCODE_SIZE, gfc_get_fcode_size},
12603831d35Sstevel { FC_GET_FCODE, gfc_get_fcode},
12703831d35Sstevel { NULL, NULL}
12803831d35Sstevel };
12903831d35Sstevel
13003831d35Sstevel struct gfc_ops_v gp2_shared_pov[] = {
13103831d35Sstevel { NULL, NULL}
13203831d35Sstevel };
13303831d35Sstevel
13403831d35Sstevel static int gp2_map_phys(dev_info_t *, struct regspec *, caddr_t *,
13503831d35Sstevel ddi_device_acc_attr_t *, ddi_acc_handle_t *);
13603831d35Sstevel static void gp2_unmap_phys(ddi_acc_handle_t *);
13703831d35Sstevel
13803831d35Sstevel fco_handle_t
gp2_fc_ops_alloc_handle(dev_info_t * ap,dev_info_t * child,void * fcode,size_t fcode_size,char * unit_address,char * my_args)13903831d35Sstevel gp2_fc_ops_alloc_handle(dev_info_t *ap, dev_info_t *child,
14003831d35Sstevel void *fcode, size_t fcode_size, char *unit_address,
14103831d35Sstevel char *my_args)
14203831d35Sstevel {
14303831d35Sstevel fco_handle_t rp;
14403831d35Sstevel phandle_t h;
14503831d35Sstevel
14603831d35Sstevel rp = kmem_zalloc(sizeof (struct fc_resource_list), KM_SLEEP);
14703831d35Sstevel rp->next_handle = fc_ops_alloc_handle(ap, child, fcode, fcode_size,
14803831d35Sstevel unit_address, NULL);
14903831d35Sstevel rp->ap = ap;
15003831d35Sstevel rp->child = child;
15103831d35Sstevel rp->fcode = fcode;
15203831d35Sstevel rp->fcode_size = fcode_size;
15303831d35Sstevel rp->my_args = my_args;
15403831d35Sstevel
15503831d35Sstevel if (unit_address) {
15603831d35Sstevel char *buf;
15703831d35Sstevel
15803831d35Sstevel buf = kmem_zalloc(strlen(unit_address) + 1, KM_SLEEP);
15903831d35Sstevel (void) strcpy(buf, unit_address);
16003831d35Sstevel rp->unit_address = buf;
16103831d35Sstevel }
16203831d35Sstevel
16303831d35Sstevel /*
16403831d35Sstevel * Add the child's nodeid to our table...
16503831d35Sstevel */
16603831d35Sstevel h = ddi_get_nodeid(rp->child);
16703831d35Sstevel fc_add_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child, h);
16803831d35Sstevel
16903831d35Sstevel return (rp);
17003831d35Sstevel }
17103831d35Sstevel
17203831d35Sstevel void
gp2_fc_ops_free_handle(fco_handle_t rp)17303831d35Sstevel gp2_fc_ops_free_handle(fco_handle_t rp)
17403831d35Sstevel {
17503831d35Sstevel struct fc_resource *ip, *np;
17603831d35Sstevel
17703831d35Sstevel ASSERT(rp);
17803831d35Sstevel
17903831d35Sstevel if (rp->next_handle)
18003831d35Sstevel fc_ops_free_handle(rp->next_handle);
18103831d35Sstevel if (rp->unit_address)
18203831d35Sstevel kmem_free(rp->unit_address, strlen(rp->unit_address) + 1);
18303831d35Sstevel if (rp->my_args != NULL)
18403831d35Sstevel kmem_free(rp->my_args, strlen(rp->my_args) + 1);
18503831d35Sstevel
18603831d35Sstevel /*
18703831d35Sstevel * Release all the resources from the resource list
18803831d35Sstevel */
18903831d35Sstevel for (ip = rp->head; ip != NULL; ip = np) {
19003831d35Sstevel np = ip->next;
19103831d35Sstevel switch (ip->type) {
19203831d35Sstevel case RT_MAP:
19303831d35Sstevel FC_DEBUG1(1, CE_CONT, "gp2_fc_ops_free: "
19403831d35Sstevel " map handle - %p\n", ip->fc_map_handle);
19503831d35Sstevel break;
19603831d35Sstevel case RT_DMA:
19703831d35Sstevel /* DMA has to be freed up at exit time */
19803831d35Sstevel cmn_err(CE_CONT, "gfc_fc_ops_free: DMA seen!\n");
19903831d35Sstevel break;
20003831d35Sstevel case RT_CONTIGIOUS:
20103831d35Sstevel FC_DEBUG2(1, CE_CONT, "gp2_fc_ops_free: "
20203831d35Sstevel "Free claim-memory resource 0x%lx size 0x%x\n",
20303831d35Sstevel ip->fc_contig_virt, ip->fc_contig_len);
20403831d35Sstevel
20503831d35Sstevel (void) ndi_ra_free(ddi_root_node(),
20603831d35Sstevel (uint64_t)ip->fc_contig_virt,
20703831d35Sstevel ip->fc_contig_len, "gptwo-contigousmem",
20803831d35Sstevel NDI_RA_PASS);
20903831d35Sstevel
21003831d35Sstevel break;
21103831d35Sstevel default:
21203831d35Sstevel cmn_err(CE_CONT, "gp2_fc_ops_free: "
21303831d35Sstevel "unknown resource type %d\n", ip->type);
21403831d35Sstevel break;
21503831d35Sstevel }
21603831d35Sstevel fc_rem_resource(rp, ip);
21703831d35Sstevel kmem_free(ip, sizeof (struct fc_resource));
21803831d35Sstevel }
21903831d35Sstevel kmem_free(rp, sizeof (struct fc_resource_list));
22003831d35Sstevel }
22103831d35Sstevel
22203831d35Sstevel int
gp2_fc_ops(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)22303831d35Sstevel gp2_fc_ops(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
22403831d35Sstevel {
22503831d35Sstevel struct gfc_ops_v *pv;
22603831d35Sstevel char *name = fc_cell2ptr(cp->svc_name);
22703831d35Sstevel
22803831d35Sstevel ASSERT(rp);
22903831d35Sstevel
23003831d35Sstevel /*
23103831d35Sstevel * First try the generic fc_ops. If the ops is a shared op,
23203831d35Sstevel * also call our local function.
23303831d35Sstevel */
23403831d35Sstevel if (fc_ops(ap, rp->next_handle, cp) == 0) {
23503831d35Sstevel for (pv = gp2_shared_pov; pv->svc_name != NULL; ++pv)
23603831d35Sstevel if (strcmp(pv->svc_name, name) == 0)
23703831d35Sstevel return (pv->f(ap, rp, cp));
23803831d35Sstevel return (0);
23903831d35Sstevel }
24003831d35Sstevel
24103831d35Sstevel for (pv = gp2_pov; pv->svc_name != NULL; ++pv)
24203831d35Sstevel if (strcmp(pv->svc_name, name) == 0)
24303831d35Sstevel return (pv->f(ap, rp, cp));
24403831d35Sstevel
24503831d35Sstevel FC_DEBUG1(9, CE_CONT, "gp2_fc_ops: <%s> not serviced\n", name);
24603831d35Sstevel
24703831d35Sstevel return (-1);
24803831d35Sstevel }
24903831d35Sstevel
25003831d35Sstevel /*
25103831d35Sstevel * map-in (phys.lo phys.hi size -- virt )
25203831d35Sstevel */
25303831d35Sstevel static int
gfc_map_in(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)25403831d35Sstevel gfc_map_in(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
25503831d35Sstevel {
25603831d35Sstevel size_t len;
25703831d35Sstevel int error;
25803831d35Sstevel caddr_t virt;
25903831d35Sstevel struct fc_resource *ip;
26003831d35Sstevel struct regspec r;
26103831d35Sstevel ddi_device_acc_attr_t acc;
26203831d35Sstevel ddi_acc_handle_t h;
26303831d35Sstevel
26403831d35Sstevel if (fc_cell2int(cp->nargs) != 3)
26503831d35Sstevel return (fc_syntax_error(cp, "nargs must be 3"));
26603831d35Sstevel
26703831d35Sstevel if (fc_cell2int(cp->nresults) < 1)
26803831d35Sstevel return (fc_syntax_error(cp, "nresults must be >= 1"));
26903831d35Sstevel
27003831d35Sstevel r.regspec_size = len = fc_cell2size(fc_arg(cp, 0));
27103831d35Sstevel r.regspec_bustype = fc_cell2uint(fc_arg(cp, 1));
27203831d35Sstevel r.regspec_addr = fc_cell2uint(fc_arg(cp, 2));
27303831d35Sstevel
27403831d35Sstevel acc.devacc_attr_version = DDI_DEVICE_ATTR_V0;
27503831d35Sstevel acc.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
27603831d35Sstevel acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
27703831d35Sstevel
27803831d35Sstevel FC_DEBUG3(1, CE_CONT, "gfc_map_in: attempting map in "
27903831d35Sstevel "address 0x%08x.%08x length %x\n", r.regspec_bustype,
28003831d35Sstevel r.regspec_addr, r.regspec_size);
28103831d35Sstevel
28203831d35Sstevel error = gp2_map_phys(rp->child, &r, &virt, &acc, &h);
28303831d35Sstevel
28403831d35Sstevel if (error) {
28503831d35Sstevel FC_DEBUG3(1, CE_CONT, "gfc_map_in: map in failed - "
28603831d35Sstevel "address 0x%08x.%08x length %x\n", r.regspec_bustype,
28703831d35Sstevel r.regspec_addr, r.regspec_size);
28803831d35Sstevel
28903831d35Sstevel return (fc_priv_error(cp, "gp2 map-in failed"));
29003831d35Sstevel }
29103831d35Sstevel
29203831d35Sstevel FC_DEBUG1(3, CE_CONT, "gp2_map_in: returning virt %p\n", virt);
29303831d35Sstevel
29403831d35Sstevel cp->nresults = fc_int2cell(1);
29503831d35Sstevel fc_result(cp, 0) = fc_ptr2cell(virt);
29603831d35Sstevel
29703831d35Sstevel /*
29803831d35Sstevel * Log this resource ...
29903831d35Sstevel */
30003831d35Sstevel ip = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
30103831d35Sstevel ip->type = RT_MAP;
30203831d35Sstevel ip->fc_map_virt = virt;
30303831d35Sstevel ip->fc_map_len = len;
30403831d35Sstevel ip->fc_map_handle = h;
30503831d35Sstevel fc_add_resource(rp, ip);
30603831d35Sstevel
30703831d35Sstevel return (fc_success_op(ap, rp, cp));
30803831d35Sstevel }
30903831d35Sstevel
31003831d35Sstevel /*
31103831d35Sstevel * map-out ( virt size -- )
31203831d35Sstevel */
31303831d35Sstevel static int
gfc_map_out(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)31403831d35Sstevel gfc_map_out(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
31503831d35Sstevel {
31603831d35Sstevel caddr_t virt;
31703831d35Sstevel size_t len;
31803831d35Sstevel struct fc_resource *ip;
31903831d35Sstevel
32003831d35Sstevel if (fc_cell2int(cp->nargs) != 2)
32103831d35Sstevel return (fc_syntax_error(cp, "nargs must be 2"));
32203831d35Sstevel
32303831d35Sstevel virt = fc_cell2ptr(fc_arg(cp, 1));
32403831d35Sstevel
32503831d35Sstevel len = fc_cell2size(fc_arg(cp, 0));
32603831d35Sstevel
32703831d35Sstevel FC_DEBUG2(1, CE_CONT, "gp2_map_out: attempting map out %p %x\n",
32803831d35Sstevel virt, len);
32903831d35Sstevel
33003831d35Sstevel /*
33103831d35Sstevel * Find if this request matches a mapping resource we set up.
33203831d35Sstevel */
33303831d35Sstevel fc_lock_resource_list(rp);
33403831d35Sstevel for (ip = rp->head; ip != NULL; ip = ip->next) {
33503831d35Sstevel if (ip->type != RT_MAP)
33603831d35Sstevel continue;
33703831d35Sstevel if (ip->fc_map_virt != virt)
33803831d35Sstevel continue;
33903831d35Sstevel if (ip->fc_map_len == len)
34003831d35Sstevel break;
34103831d35Sstevel }
34203831d35Sstevel fc_unlock_resource_list(rp);
34303831d35Sstevel
34403831d35Sstevel if (ip == NULL)
34503831d35Sstevel return (fc_priv_error(cp, "request doesn't match a "
34603831d35Sstevel "known mapping"));
34703831d35Sstevel
34803831d35Sstevel gp2_unmap_phys(&ip->fc_map_handle);
34903831d35Sstevel
35003831d35Sstevel /*
35103831d35Sstevel * remove the resource from the list and release it.
35203831d35Sstevel */
35303831d35Sstevel fc_rem_resource(rp, ip);
35403831d35Sstevel kmem_free(ip, sizeof (struct fc_resource));
35503831d35Sstevel
35603831d35Sstevel cp->nresults = fc_int2cell(0);
35703831d35Sstevel return (fc_success_op(ap, rp, cp));
35803831d35Sstevel }
35903831d35Sstevel
36003831d35Sstevel static int
gfc_register_fetch(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)36103831d35Sstevel gfc_register_fetch(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
36203831d35Sstevel {
36303831d35Sstevel size_t len;
36403831d35Sstevel caddr_t virt;
36503831d35Sstevel int error = 0;
36603831d35Sstevel uint64_t x;
36703831d35Sstevel uint32_t l;
36803831d35Sstevel uint16_t w;
36903831d35Sstevel uint8_t b;
37003831d35Sstevel char *name = fc_cell2ptr(cp->svc_name);
37103831d35Sstevel struct fc_resource *ip;
37203831d35Sstevel
37303831d35Sstevel if (fc_cell2int(cp->nargs) != 1)
37403831d35Sstevel return (fc_syntax_error(cp, "nargs must be 1"));
37503831d35Sstevel
37603831d35Sstevel if (fc_cell2int(cp->nresults) < 1)
37703831d35Sstevel return (fc_syntax_error(cp, "nresults must be >= 1"));
37803831d35Sstevel
37903831d35Sstevel virt = fc_cell2ptr(fc_arg(cp, 0));
38003831d35Sstevel
38103831d35Sstevel /*
38203831d35Sstevel * Determine the access width .. we can switch on the 2nd
38303831d35Sstevel * character of the name which is "rx@", "rl@", "rb@" or "rw@"
38403831d35Sstevel */
38503831d35Sstevel switch (*(name + 1)) {
38603831d35Sstevel case 'x': len = sizeof (x); break;
38703831d35Sstevel case 'l': len = sizeof (l); break;
38803831d35Sstevel case 'w': len = sizeof (w); break;
38903831d35Sstevel case 'b': len = sizeof (b); break;
39003831d35Sstevel }
39103831d35Sstevel
39203831d35Sstevel /*
39303831d35Sstevel * Check the alignment ...
39403831d35Sstevel */
39503831d35Sstevel if (((intptr_t)virt & (len - 1)) != 0)
39603831d35Sstevel return (fc_priv_error(cp, "unaligned access"));
39703831d35Sstevel
39803831d35Sstevel /*
39903831d35Sstevel * Find if this virt is 'within' a request we know about
40003831d35Sstevel */
40103831d35Sstevel fc_lock_resource_list(rp);
40203831d35Sstevel for (ip = rp->head; ip != NULL; ip = ip->next) {
40303831d35Sstevel if (ip->type == RT_MAP) {
40403831d35Sstevel if ((virt >= (caddr_t)ip->fc_map_virt) && ((virt + len) <=
40503831d35Sstevel ((caddr_t)ip->fc_map_virt + ip->fc_map_len)))
40603831d35Sstevel break;
40703831d35Sstevel } else if (ip->type == RT_CONTIGIOUS) {
40803831d35Sstevel if ((virt >= (caddr_t)ip->fc_contig_virt) && ((virt + len)
40903831d35Sstevel <= ((caddr_t)ip->fc_contig_virt + ip->fc_contig_len)))
41003831d35Sstevel break;
41103831d35Sstevel }
41203831d35Sstevel }
41303831d35Sstevel fc_unlock_resource_list(rp);
41403831d35Sstevel
41503831d35Sstevel if (ip == NULL) {
41603831d35Sstevel return (fc_priv_error(cp, "request not within a "
41703831d35Sstevel "known mapping or contigious adddress"));
41803831d35Sstevel }
41903831d35Sstevel
42003831d35Sstevel switch (len) {
42103831d35Sstevel case sizeof (x):
42203831d35Sstevel if (ip->type == RT_MAP)
42303831d35Sstevel error = ddi_peek64(rp->child,
42403831d35Sstevel (int64_t *)virt, (int64_t *)&x);
42503831d35Sstevel else /* RT_CONTIGIOUS */
42603831d35Sstevel x = *(int64_t *)virt;
42703831d35Sstevel break;
42803831d35Sstevel case sizeof (l):
42903831d35Sstevel if (ip->type == RT_MAP)
43003831d35Sstevel error = ddi_peek32(rp->child,
43103831d35Sstevel (int32_t *)virt, (int32_t *)&l);
43203831d35Sstevel else /* RT_CONTIGIOUS */
43303831d35Sstevel l = *(int32_t *)virt;
43403831d35Sstevel break;
43503831d35Sstevel case sizeof (w):
43603831d35Sstevel if (ip->type == RT_MAP)
43703831d35Sstevel error = ddi_peek16(rp->child,
43803831d35Sstevel (int16_t *)virt, (int16_t *)&w);
43903831d35Sstevel else /* RT_CONTIGIOUS */
44003831d35Sstevel w = *(int16_t *)virt;
44103831d35Sstevel break;
44203831d35Sstevel case sizeof (b):
44303831d35Sstevel if (ip->type == RT_MAP)
44403831d35Sstevel error = ddi_peek8(rp->child,
44503831d35Sstevel (int8_t *)virt, (int8_t *)&b);
44603831d35Sstevel else /* RT_CONTIGIOUS */
44703831d35Sstevel b = *(int8_t *)virt;
44803831d35Sstevel break;
44903831d35Sstevel }
45003831d35Sstevel
45103831d35Sstevel if (error) {
45203831d35Sstevel FC_DEBUG2(1, CE_CONT, "gfc_register_fetch: access error "
45303831d35Sstevel "accessing virt %p len %d\n", virt, len);
45403831d35Sstevel return (fc_priv_error(cp, "access error"));
45503831d35Sstevel }
45603831d35Sstevel
45703831d35Sstevel cp->nresults = fc_int2cell(1);
45803831d35Sstevel switch (len) {
45903831d35Sstevel case sizeof (x): fc_result(cp, 0) = x; break;
46003831d35Sstevel case sizeof (l): fc_result(cp, 0) = fc_uint32_t2cell(l); break;
46103831d35Sstevel case sizeof (w): fc_result(cp, 0) = fc_uint16_t2cell(w); break;
46203831d35Sstevel case sizeof (b): fc_result(cp, 0) = fc_uint8_t2cell(b); break;
46303831d35Sstevel }
46403831d35Sstevel return (fc_success_op(ap, rp, cp));
46503831d35Sstevel }
46603831d35Sstevel
46703831d35Sstevel static int
gfc_register_store(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)46803831d35Sstevel gfc_register_store(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
46903831d35Sstevel {
47003831d35Sstevel size_t len;
47103831d35Sstevel caddr_t virt;
47203831d35Sstevel uint64_t x;
47303831d35Sstevel uint32_t l;
47403831d35Sstevel uint16_t w;
47503831d35Sstevel uint8_t b;
47603831d35Sstevel char *name = fc_cell2ptr(cp->svc_name);
47703831d35Sstevel struct fc_resource *ip;
47803831d35Sstevel int error = 0;
47903831d35Sstevel
48003831d35Sstevel if (fc_cell2int(cp->nargs) != 2)
48103831d35Sstevel return (fc_syntax_error(cp, "nargs must be 2"));
48203831d35Sstevel
48303831d35Sstevel virt = fc_cell2ptr(fc_arg(cp, 0));
48403831d35Sstevel
48503831d35Sstevel /*
48603831d35Sstevel * Determine the access width .. we can switch on the 2nd
48703831d35Sstevel * character of the name which is "rx!", "rl!", "rb!" or "rw!"
48803831d35Sstevel */
48903831d35Sstevel switch (*(name + 1)) {
49003831d35Sstevel case 'x': len = sizeof (x); x = fc_arg(cp, 1); break;
49103831d35Sstevel case 'l': len = sizeof (l); l = fc_cell2uint32_t(fc_arg(cp, 1)); break;
49203831d35Sstevel case 'w': len = sizeof (w); w = fc_cell2uint16_t(fc_arg(cp, 1)); break;
49303831d35Sstevel case 'b': len = sizeof (b); b = fc_cell2uint8_t(fc_arg(cp, 1)); break;
49403831d35Sstevel }
49503831d35Sstevel
49603831d35Sstevel /*
49703831d35Sstevel * Check the alignment ...
49803831d35Sstevel */
49903831d35Sstevel if (((intptr_t)virt & (len - 1)) != 0)
50003831d35Sstevel return (fc_priv_error(cp, "unaligned access"));
50103831d35Sstevel
50203831d35Sstevel /*
50303831d35Sstevel * Find if this virt is 'within' a request we know about
50403831d35Sstevel */
50503831d35Sstevel fc_lock_resource_list(rp);
50603831d35Sstevel for (ip = rp->head; ip != NULL; ip = ip->next) {
50703831d35Sstevel if (ip->type == RT_MAP) {
50803831d35Sstevel if ((virt >= (caddr_t)ip->fc_map_virt) && ((virt + len) <=
50903831d35Sstevel ((caddr_t)ip->fc_map_virt + ip->fc_map_len)))
51003831d35Sstevel break;
51103831d35Sstevel } else if (ip->type == RT_CONTIGIOUS) {
51203831d35Sstevel if ((virt >= (caddr_t)ip->fc_contig_virt) && ((virt + len)
51303831d35Sstevel <= ((caddr_t)ip->fc_contig_virt + ip->fc_contig_len)))
51403831d35Sstevel break;
51503831d35Sstevel }
51603831d35Sstevel }
51703831d35Sstevel fc_unlock_resource_list(rp);
51803831d35Sstevel
51903831d35Sstevel if (ip == NULL)
52003831d35Sstevel return (fc_priv_error(cp, "request not within a "
52103831d35Sstevel "known mapping or contigious address"));
52203831d35Sstevel
52303831d35Sstevel switch (len) {
52403831d35Sstevel case sizeof (x):
52503831d35Sstevel if (ip->type == RT_MAP)
52603831d35Sstevel error = ddi_poke64(rp->child, (int64_t *)virt, x);
52703831d35Sstevel else if (ip->type == RT_CONTIGIOUS)
52803831d35Sstevel *(uint64_t *)virt = x;
52903831d35Sstevel break;
53003831d35Sstevel case sizeof (l):
53103831d35Sstevel if (ip->type == RT_MAP)
53203831d35Sstevel error = ddi_poke32(rp->child, (int32_t *)virt, l);
53303831d35Sstevel else if (ip->type == RT_CONTIGIOUS)
53403831d35Sstevel *(uint32_t *)virt = l;
53503831d35Sstevel break;
53603831d35Sstevel case sizeof (w):
53703831d35Sstevel if (ip->type == RT_MAP)
53803831d35Sstevel error = ddi_poke16(rp->child, (int16_t *)virt, w);
53903831d35Sstevel else if (ip->type == RT_CONTIGIOUS)
54003831d35Sstevel *(uint16_t *)virt = w;
54103831d35Sstevel break;
54203831d35Sstevel case sizeof (b):
54303831d35Sstevel if (ip->type == RT_MAP)
54403831d35Sstevel error = ddi_poke8(rp->child, (int8_t *)virt, b);
54503831d35Sstevel else if (ip->type == RT_CONTIGIOUS)
54603831d35Sstevel *(uint8_t *)virt = b;
54703831d35Sstevel break;
54803831d35Sstevel }
54903831d35Sstevel
55003831d35Sstevel if (error == DDI_FAILURE) {
55103831d35Sstevel FC_DEBUG2(1, CE_CONT, "gfc_register_store: access error "
55203831d35Sstevel "accessing virt %p len %d\n", virt, len);
55303831d35Sstevel return (fc_priv_error(cp, "access error"));
55403831d35Sstevel }
55503831d35Sstevel
55603831d35Sstevel cp->nresults = fc_int2cell(0);
55703831d35Sstevel return (fc_success_op(ap, rp, cp));
55803831d35Sstevel }
55903831d35Sstevel
56003831d35Sstevel static int
gfc_master_intr(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)56103831d35Sstevel gfc_master_intr(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
56203831d35Sstevel {
56303831d35Sstevel int xt, portid;
56403831d35Sstevel
56503831d35Sstevel if (fc_cell2int(cp->nargs) != 2)
56603831d35Sstevel return (fc_syntax_error(cp, "nargs must be 4"));
56703831d35Sstevel
56803831d35Sstevel if (fc_cell2int(cp->nresults) < 1)
56903831d35Sstevel return (fc_syntax_error(cp, "nresults must be >= 1"));
57003831d35Sstevel
57103831d35Sstevel xt = fc_cell2int(fc_arg(cp, 1));
57203831d35Sstevel portid = fc_cell2int(fc_arg(cp, 0));
57303831d35Sstevel
57403831d35Sstevel FC_DEBUG2(1, CE_CONT, "gfc_master_intr: reset-int-xt=%x portid=%x",
57503831d35Sstevel xt, portid);
57603831d35Sstevel
57703831d35Sstevel cp->nresults = fc_int2cell(1);
57803831d35Sstevel fc_result(cp, 0) = 0;
57903831d35Sstevel
58003831d35Sstevel return (fc_success_op(ap, rp, cp));
58103831d35Sstevel }
58203831d35Sstevel
58303831d35Sstevel /*
58403831d35Sstevel * gfc_claim_address
58503831d35Sstevel *
58603831d35Sstevel * claim-address (size.lo size.hi type align bar portid -- base.lo base.hi )
58703831d35Sstevel */
58803831d35Sstevel static int
gfc_claim_address(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)58903831d35Sstevel gfc_claim_address(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
59003831d35Sstevel {
59103831d35Sstevel int bar, portid;
59203831d35Sstevel uint64_t exp, slot, port, slice;
59303831d35Sstevel uint64_t paddr;
59403831d35Sstevel
59503831d35Sstevel if (fc_cell2int(cp->nargs) != 6)
59603831d35Sstevel return (fc_syntax_error(cp, "nargs must be 6"));
59703831d35Sstevel
59803831d35Sstevel if (fc_cell2int(cp->nresults) < 2)
59903831d35Sstevel return (fc_syntax_error(cp, "nresults must be 2"));
60003831d35Sstevel
60103831d35Sstevel bar = fc_cell2int(fc_arg(cp, 1));
60203831d35Sstevel portid = fc_cell2int(fc_arg(cp, 0));
60303831d35Sstevel
60403831d35Sstevel exp = portid >> 5;
60503831d35Sstevel slot = (0x8 & portid) >> 3;
60603831d35Sstevel port = portid & 0x1;
60703831d35Sstevel
60803831d35Sstevel switch (bar) {
60903831d35Sstevel case 0: /* PCI IO Bus A */
61003831d35Sstevel paddr = (exp << 28) | (port << 26) | (slot << 27) |
61103831d35Sstevel ((uint64_t)0x402 << 32);
61203831d35Sstevel
61303831d35Sstevel break;
61403831d35Sstevel case 1: /* PCI Memory Bus A */
61503831d35Sstevel slice = (exp * 2) + slot + 1;
61603831d35Sstevel
61703831d35Sstevel paddr = ((uint64_t)1 << 42) | ((uint64_t)slice << 34) |
61803831d35Sstevel ((uint64_t)port << 33);
61903831d35Sstevel
62003831d35Sstevel break;
62103831d35Sstevel case 2: /* PCI IO Bus B */
62203831d35Sstevel paddr = (exp << 28) | (port << 26) | (slot << 27) |
62303831d35Sstevel ((uint64_t)0x402 << 32) | (1 << 25);
62403831d35Sstevel
62503831d35Sstevel break;
62603831d35Sstevel case 3: /* PCI Memory Bus B */
62703831d35Sstevel slice = (exp * 2) + slot + 1;
62803831d35Sstevel
62903831d35Sstevel paddr = ((uint64_t)1 << 42) | ((uint64_t)slice << 34) |
63003831d35Sstevel ((uint64_t)port << 33);
63103831d35Sstevel
63203831d35Sstevel paddr |= ((uint64_t)1 << 32);
63303831d35Sstevel
63403831d35Sstevel break;
63503831d35Sstevel default:
63603831d35Sstevel cmn_err(CE_WARN,
63703831d35Sstevel "gfc_claim_address - invalid BAR=0x%x\n", bar);
63803831d35Sstevel
63903831d35Sstevel return (fc_syntax_error(cp, "invalid argument"));
64003831d35Sstevel }
64103831d35Sstevel
64203831d35Sstevel FC_DEBUG1(1, CE_CONT, "gfc_claim_address: returning 0x%lx\n", paddr);
64303831d35Sstevel
64403831d35Sstevel cp->nresults = fc_int2cell(2);
64503831d35Sstevel fc_result(cp, 0) = LOADDR(paddr);
64603831d35Sstevel fc_result(cp, 1) = HIADDR(paddr);
64703831d35Sstevel
64803831d35Sstevel return (fc_success_op(ap, rp, cp));
64903831d35Sstevel }
65003831d35Sstevel
65103831d35Sstevel /*
65203831d35Sstevel * gfc_claim_memory
65303831d35Sstevel *
65425cf1a30Sjl139090 * claim-memory ( align size vhint -- vaddr)
65503831d35Sstevel */
65603831d35Sstevel static int
gfc_claim_memory(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)65703831d35Sstevel gfc_claim_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
65803831d35Sstevel {
65903831d35Sstevel int align, size, vhint;
66003831d35Sstevel ndi_ra_request_t request;
66103831d35Sstevel uint64_t answer, alen;
66203831d35Sstevel struct fc_resource *ip;
66303831d35Sstevel
66403831d35Sstevel if (fc_cell2int(cp->nargs) != 3)
66503831d35Sstevel return (fc_syntax_error(cp, "nargs must be 3"));
66603831d35Sstevel
66703831d35Sstevel if (fc_cell2int(cp->nresults) < 1)
66803831d35Sstevel return (fc_syntax_error(cp, "nresults must be >= 1"));
66903831d35Sstevel
67003831d35Sstevel vhint = fc_cell2int(fc_arg(cp, 2));
67103831d35Sstevel size = fc_cell2int(fc_arg(cp, 1));
67203831d35Sstevel align = fc_cell2int(fc_arg(cp, 0));
67303831d35Sstevel
67403831d35Sstevel FC_DEBUG3(1, CE_CONT, "gfc_claim_memory: align=0x%x size=0x%x "
67503831d35Sstevel "vhint=0x%x\n", align, size, vhint);
67603831d35Sstevel
67703831d35Sstevel if (size == 0) {
67803831d35Sstevel cmn_err(CE_WARN, " gfc_claim_memory - unable to allocate "
67903831d35Sstevel "contigiuos memory of size zero\n");
68003831d35Sstevel return (fc_priv_error(cp, "allocation error"));
68103831d35Sstevel }
68203831d35Sstevel
68303831d35Sstevel if (vhint) {
68403831d35Sstevel cmn_err(CE_WARN, "gfc_claim_memory - vhint is not zero "
68503831d35Sstevel "vhint=0x%x - Ignoring Argument\n", vhint);
68603831d35Sstevel }
68703831d35Sstevel
68803831d35Sstevel bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
68903831d35Sstevel request.ra_flags = NDI_RA_ALLOC_BOUNDED;
69003831d35Sstevel request.ra_boundbase = 0;
69103831d35Sstevel request.ra_boundlen = 0xffffffff;
69203831d35Sstevel request.ra_len = size;
69303831d35Sstevel request.ra_align_mask = align - 1;
69403831d35Sstevel
69503831d35Sstevel if (ndi_ra_alloc(ddi_root_node(), &request, &answer, &alen,
69603831d35Sstevel "gptwo-contigousmem", NDI_RA_PASS) != NDI_SUCCESS) {
69703831d35Sstevel cmn_err(CE_WARN, " gfc_claim_memory - unable to allocate "
69803831d35Sstevel "contigiuos memory\n");
69903831d35Sstevel return (fc_priv_error(cp, "allocation error"));
70003831d35Sstevel
70103831d35Sstevel }
70203831d35Sstevel
70303831d35Sstevel FC_DEBUG2(1, CE_CONT, "gfc_claim_memory: address allocated=0x%lx "
70403831d35Sstevel "size=0x%x\n", answer, alen);
70503831d35Sstevel
70603831d35Sstevel cp->nresults = fc_int2cell(1);
70703831d35Sstevel fc_result(cp, 0) = answer;
70803831d35Sstevel
70903831d35Sstevel /*
71003831d35Sstevel * Log this resource ...
71103831d35Sstevel */
71203831d35Sstevel ip = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
71303831d35Sstevel ip->type = RT_CONTIGIOUS;
71403831d35Sstevel ip->fc_contig_virt = (void *)answer;
71503831d35Sstevel ip->fc_contig_len = size;
71603831d35Sstevel fc_add_resource(rp, ip);
71703831d35Sstevel
71803831d35Sstevel return (fc_success_op(ap, rp, cp));
71903831d35Sstevel }
72003831d35Sstevel
72103831d35Sstevel /*
72203831d35Sstevel * gfc_release_memory
72303831d35Sstevel *
72425cf1a30Sjl139090 * release-memory ( size vaddr -- )
72503831d35Sstevel */
72603831d35Sstevel static int
gfc_release_memory(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)72703831d35Sstevel gfc_release_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
72803831d35Sstevel {
72903831d35Sstevel int32_t vaddr, size;
73003831d35Sstevel struct fc_resource *ip;
73103831d35Sstevel
73203831d35Sstevel if (fc_cell2int(cp->nargs) != 2)
73303831d35Sstevel return (fc_syntax_error(cp, "nargs must be 2"));
73403831d35Sstevel
73503831d35Sstevel if (fc_cell2int(cp->nresults) != 0)
73603831d35Sstevel return (fc_syntax_error(cp, "nresults must be 0"));
73703831d35Sstevel
73803831d35Sstevel vaddr = fc_cell2int(fc_arg(cp, 1));
73903831d35Sstevel size = fc_cell2int(fc_arg(cp, 0));
74003831d35Sstevel
74103831d35Sstevel FC_DEBUG2(1, CE_CONT, "gfc_release_memory: vaddr=0x%x size=0x%x\n",
74203831d35Sstevel vaddr, size);
74303831d35Sstevel /*
74403831d35Sstevel * Find if this request matches a mapping resource we set up.
74503831d35Sstevel */
74603831d35Sstevel fc_lock_resource_list(rp);
74703831d35Sstevel for (ip = rp->head; ip != NULL; ip = ip->next) {
74803831d35Sstevel if (ip->type != RT_CONTIGIOUS)
74903831d35Sstevel continue;
75003831d35Sstevel if (ip->fc_contig_virt != (void *)(uintptr_t)vaddr)
75103831d35Sstevel continue;
75203831d35Sstevel if (ip->fc_contig_len == size)
75303831d35Sstevel break;
75403831d35Sstevel }
75503831d35Sstevel fc_unlock_resource_list(rp);
75603831d35Sstevel
75703831d35Sstevel if (ip == NULL)
75803831d35Sstevel return (fc_priv_error(cp, "request doesn't match a "
75903831d35Sstevel "known mapping"));
76003831d35Sstevel
76103831d35Sstevel (void) ndi_ra_free(ddi_root_node(), vaddr, size,
76203831d35Sstevel "gptwo-contigousmem", NDI_RA_PASS);
76303831d35Sstevel
76403831d35Sstevel /*
76503831d35Sstevel * remove the resource from the list and release it.
76603831d35Sstevel */
76703831d35Sstevel fc_rem_resource(rp, ip);
76803831d35Sstevel kmem_free(ip, sizeof (struct fc_resource));
76903831d35Sstevel
77003831d35Sstevel cp->nresults = fc_int2cell(0);
77103831d35Sstevel
77203831d35Sstevel return (fc_success_op(ap, rp, cp));
77303831d35Sstevel }
77403831d35Sstevel
77503831d35Sstevel /*
77603831d35Sstevel * gfc_vtop
77703831d35Sstevel *
77825cf1a30Sjl139090 * vtop ( vaddr -- paddr.lo paddr.hi)
77903831d35Sstevel */
78003831d35Sstevel static int
gfc_vtop(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)78103831d35Sstevel gfc_vtop(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
78203831d35Sstevel {
78303831d35Sstevel int vaddr;
78403831d35Sstevel uint64_t paddr;
78503831d35Sstevel struct fc_resource *ip;
78603831d35Sstevel
78703831d35Sstevel if (fc_cell2int(cp->nargs) != 1)
78803831d35Sstevel return (fc_syntax_error(cp, "nargs must be 1"));
78903831d35Sstevel
79003831d35Sstevel if (fc_cell2int(cp->nresults) >= 3)
79103831d35Sstevel return (fc_syntax_error(cp, "nresults must be less than 2"));
79203831d35Sstevel
79303831d35Sstevel vaddr = fc_cell2int(fc_arg(cp, 0));
79403831d35Sstevel
79503831d35Sstevel /*
79603831d35Sstevel * Find if this request matches a mapping resource we set up.
79703831d35Sstevel */
79803831d35Sstevel fc_lock_resource_list(rp);
79903831d35Sstevel for (ip = rp->head; ip != NULL; ip = ip->next) {
80003831d35Sstevel if (ip->type != RT_CONTIGIOUS)
80103831d35Sstevel continue;
80203831d35Sstevel if (ip->fc_contig_virt == (void *)(uintptr_t)vaddr)
80303831d35Sstevel break;
80403831d35Sstevel }
80503831d35Sstevel fc_unlock_resource_list(rp);
80603831d35Sstevel
80703831d35Sstevel if (ip == NULL)
80803831d35Sstevel return (fc_priv_error(cp, "request doesn't match a "
80903831d35Sstevel "known mapping"));
81003831d35Sstevel
81103831d35Sstevel
81203831d35Sstevel paddr = va_to_pa((void *)(uintptr_t)vaddr);
81303831d35Sstevel
81403831d35Sstevel FC_DEBUG2(1, CE_CONT, "gfc_vtop: vaddr=0x%x paddr=0x%x\n",
81503831d35Sstevel vaddr, paddr);
81603831d35Sstevel
81703831d35Sstevel cp->nresults = fc_int2cell(2);
81803831d35Sstevel
81903831d35Sstevel fc_result(cp, 0) = paddr;
82003831d35Sstevel fc_result(cp, 1) = 0;
82103831d35Sstevel
82203831d35Sstevel return (fc_success_op(ap, rp, cp));
82303831d35Sstevel }
82403831d35Sstevel
82503831d35Sstevel static int
gfc_config_child(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)82603831d35Sstevel gfc_config_child(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
82703831d35Sstevel {
82803831d35Sstevel fc_phandle_t h;
82903831d35Sstevel
83003831d35Sstevel if (fc_cell2int(cp->nargs) != 0)
83103831d35Sstevel return (fc_syntax_error(cp, "nargs must be 0"));
83203831d35Sstevel
83303831d35Sstevel if (fc_cell2int(cp->nresults) < 1)
83403831d35Sstevel return (fc_syntax_error(cp, "nresults must be >= 1"));
83503831d35Sstevel
83603831d35Sstevel h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child);
83703831d35Sstevel
83803831d35Sstevel cp->nresults = fc_int2cell(1);
83903831d35Sstevel fc_result(cp, 0) = fc_phandle2cell(h);
84003831d35Sstevel
84103831d35Sstevel return (fc_success_op(ap, rp, cp));
84203831d35Sstevel }
84303831d35Sstevel
84403831d35Sstevel static int
gfc_get_fcode(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)84503831d35Sstevel gfc_get_fcode(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
84603831d35Sstevel {
84703831d35Sstevel caddr_t name_virt, fcode_virt;
84803831d35Sstevel char *name, *fcode;
84903831d35Sstevel int fcode_len, status;
85003831d35Sstevel
85103831d35Sstevel if (fc_cell2int(cp->nargs) != 3)
85203831d35Sstevel return (fc_syntax_error(cp, "nargs must be 3"));
85303831d35Sstevel
85403831d35Sstevel if (fc_cell2int(cp->nresults) < 1)
85503831d35Sstevel return (fc_syntax_error(cp, "nresults must be >= 1"));
85603831d35Sstevel
85703831d35Sstevel name_virt = fc_cell2ptr(fc_arg(cp, 0));
85803831d35Sstevel
85903831d35Sstevel fcode_virt = fc_cell2ptr(fc_arg(cp, 1));
86003831d35Sstevel
86103831d35Sstevel fcode_len = fc_cell2int(fc_arg(cp, 2));
86203831d35Sstevel
86303831d35Sstevel name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
86403831d35Sstevel
86503831d35Sstevel if (copyinstr(fc_cell2ptr(name_virt), name,
86603831d35Sstevel FC_SVC_NAME_LEN - 1, NULL)) {
86703831d35Sstevel FC_DEBUG1(1, CE_CONT, "gfc_get_fcode: "
86803831d35Sstevel "fault copying in drop in name %p\n", name_virt);
86903831d35Sstevel status = 0;
87003831d35Sstevel } else {
87103831d35Sstevel
87203831d35Sstevel fcode = kmem_zalloc(fcode_len, KM_SLEEP);
87303831d35Sstevel
87403831d35Sstevel if ((status = prom_get_fcode(name, fcode)) != 0) {
87503831d35Sstevel
87603831d35Sstevel if (copyout((void *)fcode, (void *)fcode_virt,
87703831d35Sstevel fcode_len)) {
87803831d35Sstevel cmn_err(CE_WARN, " gfc_get_fcode: Unable "
87903831d35Sstevel "to copy out fcode image\n");
88003831d35Sstevel status = 0;
88103831d35Sstevel }
88203831d35Sstevel }
88303831d35Sstevel
88403831d35Sstevel kmem_free(fcode, fcode_len);
88503831d35Sstevel }
88603831d35Sstevel
88703831d35Sstevel kmem_free(name, FC_SVC_NAME_LEN);
88803831d35Sstevel
88903831d35Sstevel cp->nresults = fc_int2cell(1);
89003831d35Sstevel fc_result(cp, 0) = status;
89103831d35Sstevel
89203831d35Sstevel return (fc_success_op(ap, rp, cp));
89303831d35Sstevel }
89403831d35Sstevel
89503831d35Sstevel static int
gfc_get_fcode_size(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)89603831d35Sstevel gfc_get_fcode_size(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
89703831d35Sstevel {
89803831d35Sstevel caddr_t virt;
89903831d35Sstevel char *name;
90003831d35Sstevel int len;
90103831d35Sstevel
90203831d35Sstevel if (fc_cell2int(cp->nargs) != 1)
90303831d35Sstevel return (fc_syntax_error(cp, "nargs must be 1"));
90403831d35Sstevel
90503831d35Sstevel if (fc_cell2int(cp->nresults) < 1)
90603831d35Sstevel return (fc_syntax_error(cp, "nresults must be >= 1"));
90703831d35Sstevel
90803831d35Sstevel virt = fc_cell2ptr(fc_arg(cp, 0));
90903831d35Sstevel
91003831d35Sstevel name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
91103831d35Sstevel
91203831d35Sstevel if (copyinstr(fc_cell2ptr(virt), name,
91303831d35Sstevel FC_SVC_NAME_LEN - 1, NULL)) {
91403831d35Sstevel FC_DEBUG1(1, CE_CONT, "gfc_get_fcode_size: "
91503831d35Sstevel "fault copying in drop in name %p\n", virt);
91603831d35Sstevel len = 0;
91703831d35Sstevel } else {
91803831d35Sstevel
91903831d35Sstevel len = prom_get_fcode_size(name);
92003831d35Sstevel }
92103831d35Sstevel
92203831d35Sstevel kmem_free(name, FC_SVC_NAME_LEN);
92303831d35Sstevel
92403831d35Sstevel cp->nresults = fc_int2cell(1);
92503831d35Sstevel fc_result(cp, 0) = len;
92603831d35Sstevel
92703831d35Sstevel return (fc_success_op(ap, rp, cp));
92803831d35Sstevel }
92903831d35Sstevel
93003831d35Sstevel static int
gp2_map_phys(dev_info_t * dip,struct regspec * phys_spec,caddr_t * addrp,ddi_device_acc_attr_t * accattrp,ddi_acc_handle_t * handlep)93103831d35Sstevel gp2_map_phys(dev_info_t *dip, struct regspec *phys_spec,
93203831d35Sstevel caddr_t *addrp, ddi_device_acc_attr_t *accattrp,
93303831d35Sstevel ddi_acc_handle_t *handlep)
93403831d35Sstevel {
93503831d35Sstevel ddi_map_req_t mr;
93603831d35Sstevel ddi_acc_hdl_t *hp;
93703831d35Sstevel int result;
93803831d35Sstevel struct regspec *ph;
93903831d35Sstevel
94003831d35Sstevel *handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL);
94103831d35Sstevel hp = impl_acc_hdl_get(*handlep);
94203831d35Sstevel hp->ah_vers = VERS_ACCHDL;
94303831d35Sstevel hp->ah_dip = dip;
94403831d35Sstevel hp->ah_rnumber = 0;
94503831d35Sstevel hp->ah_offset = 0;
94603831d35Sstevel hp->ah_len = 0;
94703831d35Sstevel hp->ah_acc = *accattrp;
94803831d35Sstevel ph = kmem_zalloc(sizeof (struct regspec), KM_SLEEP);
94903831d35Sstevel *ph = *phys_spec;
95003831d35Sstevel hp->ah_bus_private = ph; /* cache a copy of the reg spec */
95103831d35Sstevel
95203831d35Sstevel mr.map_op = DDI_MO_MAP_LOCKED;
95303831d35Sstevel mr.map_type = DDI_MT_REGSPEC;
95403831d35Sstevel mr.map_obj.rp = (struct regspec *)phys_spec;
95503831d35Sstevel mr.map_prot = PROT_READ | PROT_WRITE;
95603831d35Sstevel mr.map_flags = DDI_MF_KERNEL_MAPPING;
95703831d35Sstevel mr.map_handlep = hp;
95803831d35Sstevel mr.map_vers = DDI_MAP_VERSION;
95903831d35Sstevel
96003831d35Sstevel result = ddi_map(dip, &mr, 0, 0, addrp);
96103831d35Sstevel
96203831d35Sstevel if (result != DDI_SUCCESS) {
96303831d35Sstevel impl_acc_hdl_free(*handlep);
96403831d35Sstevel *handlep = (ddi_acc_handle_t)NULL;
96503831d35Sstevel } else {
96603831d35Sstevel hp->ah_addr = *addrp;
96703831d35Sstevel }
96803831d35Sstevel
96903831d35Sstevel return (result);
97003831d35Sstevel }
97103831d35Sstevel
97203831d35Sstevel static void
gp2_unmap_phys(ddi_acc_handle_t * handlep)97303831d35Sstevel gp2_unmap_phys(ddi_acc_handle_t *handlep)
97403831d35Sstevel {
97503831d35Sstevel ddi_map_req_t mr;
97603831d35Sstevel ddi_acc_hdl_t *hp;
97703831d35Sstevel struct regspec_t *ph;
97803831d35Sstevel
97903831d35Sstevel hp = impl_acc_hdl_get(*handlep);
98003831d35Sstevel ASSERT(hp);
98103831d35Sstevel ph = hp->ah_bus_private;
98203831d35Sstevel
98303831d35Sstevel mr.map_op = DDI_MO_UNMAP;
98403831d35Sstevel mr.map_type = DDI_MT_REGSPEC;
98503831d35Sstevel mr.map_obj.rp = (struct regspec *)ph;
98603831d35Sstevel mr.map_prot = PROT_READ | PROT_WRITE;
98703831d35Sstevel mr.map_flags = DDI_MF_KERNEL_MAPPING;
98803831d35Sstevel mr.map_handlep = hp;
98903831d35Sstevel mr.map_vers = DDI_MAP_VERSION;
99003831d35Sstevel
99103831d35Sstevel (void) ddi_map(hp->ah_dip, &mr, hp->ah_offset,
99203831d35Sstevel hp->ah_len, &hp->ah_addr);
99303831d35Sstevel
99403831d35Sstevel impl_acc_hdl_free(*handlep);
99503831d35Sstevel kmem_free(ph, sizeof (struct regspec)); /* Free the cached copy */
99603831d35Sstevel *handlep = (ddi_acc_handle_t)NULL;
99703831d35Sstevel }
998