125cf1a30Sjl /*
225cf1a30Sjl * CDDL HEADER START
325cf1a30Sjl *
425cf1a30Sjl * The contents of this file are subject to the terms of the
525cf1a30Sjl * Common Development and Distribution License (the "License").
625cf1a30Sjl * You may not use this file except in compliance with the License.
725cf1a30Sjl *
825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl * See the License for the specific language governing permissions
1125cf1a30Sjl * and limitations under the License.
1225cf1a30Sjl *
1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl *
1925cf1a30Sjl * CDDL HEADER END
2025cf1a30Sjl */
2107d06da5SSurya Prakki
2225cf1a30Sjl /*
2307d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2425cf1a30Sjl * Use is subject to license terms.
2525cf1a30Sjl */
2625cf1a30Sjl
2725cf1a30Sjl /*
2825cf1a30Sjl * CMU-CH nexus utility routines:
2925cf1a30Sjl * property and config routines for attach()
3025cf1a30Sjl * reg/intr/range/assigned-address property routines for bus_map()
3125cf1a30Sjl * init_child()
3225cf1a30Sjl * fault handling
3325cf1a30Sjl * debug functions
3425cf1a30Sjl */
3525cf1a30Sjl
3625cf1a30Sjl #include <sys/types.h>
3725cf1a30Sjl #include <sys/kmem.h>
3825cf1a30Sjl #include <sys/async.h>
3925cf1a30Sjl #include <sys/sysmacros.h>
4025cf1a30Sjl #include <sys/sunddi.h>
4125cf1a30Sjl #include <sys/sunndi.h>
4225cf1a30Sjl #include <sys/fm/protocol.h>
4325cf1a30Sjl #include <sys/fm/io/pci.h>
4425cf1a30Sjl #include <sys/fm/util.h>
4525cf1a30Sjl #include <sys/ddi_impldefs.h>
4625cf1a30Sjl #include <sys/pcicmu/pcicmu.h>
4725cf1a30Sjl #include <sys/promif.h>
4825cf1a30Sjl
4925cf1a30Sjl /*
5025cf1a30Sjl * get_pcmu_properties
5125cf1a30Sjl *
5225cf1a30Sjl * This function is called from the attach routine to get the key
5325cf1a30Sjl * properties of the pci nodes.
5425cf1a30Sjl *
5525cf1a30Sjl * used by: pcmu_attach()
5625cf1a30Sjl *
5725cf1a30Sjl * return value: DDI_FAILURE on failure
5825cf1a30Sjl */
5925cf1a30Sjl int
get_pcmu_properties(pcmu_t * pcmu_p,dev_info_t * dip)6025cf1a30Sjl get_pcmu_properties(pcmu_t *pcmu_p, dev_info_t *dip)
6125cf1a30Sjl {
6225cf1a30Sjl int i;
6325cf1a30Sjl
6425cf1a30Sjl /*
6525cf1a30Sjl * Get the device's port id.
6625cf1a30Sjl */
6725cf1a30Sjl if ((pcmu_p->pcmu_id = (uint32_t)pcmu_get_portid(dip)) == -1u) {
6825cf1a30Sjl cmn_err(CE_WARN, "%s%d: no portid property\n",
6925cf1a30Sjl ddi_driver_name(dip), ddi_get_instance(dip));
7025cf1a30Sjl return (DDI_FAILURE);
7125cf1a30Sjl }
7225cf1a30Sjl
7325cf1a30Sjl /*
7425cf1a30Sjl * Get the bus-ranges property.
7525cf1a30Sjl */
7625cf1a30Sjl i = sizeof (pcmu_p->pcmu_bus_range);
7725cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
7825cf1a30Sjl "bus-range", (caddr_t)&pcmu_p->pcmu_bus_range, &i) != DDI_SUCCESS) {
7925cf1a30Sjl cmn_err(CE_WARN, "%s%d: no bus-range property\n",
8025cf1a30Sjl ddi_driver_name(dip), ddi_get_instance(dip));
8125cf1a30Sjl return (DDI_FAILURE);
8225cf1a30Sjl }
8325cf1a30Sjl PCMU_DBG2(PCMU_DBG_ATTACH, dip,
8425cf1a30Sjl "get_pcmu_properties: bus-range (%x,%x)\n",
8525cf1a30Sjl pcmu_p->pcmu_bus_range.lo, pcmu_p->pcmu_bus_range.hi);
8625cf1a30Sjl
8725cf1a30Sjl /*
8825cf1a30Sjl * Get the ranges property.
8925cf1a30Sjl */
9025cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
9125cf1a30Sjl (caddr_t)&pcmu_p->pcmu_ranges, &pcmu_p->pcmu_ranges_length) !=
9225cf1a30Sjl DDI_SUCCESS) {
9325cf1a30Sjl cmn_err(CE_WARN, "%s%d: no ranges property\n",
9425cf1a30Sjl ddi_driver_name(dip), ddi_get_instance(dip));
9525cf1a30Sjl return (DDI_FAILURE);
9625cf1a30Sjl }
9725cf1a30Sjl pcmu_fix_ranges(pcmu_p->pcmu_ranges,
9825cf1a30Sjl pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t));
9925cf1a30Sjl
10025cf1a30Sjl /*
10125cf1a30Sjl * Determine the number upa slot interrupts.
10225cf1a30Sjl */
10325cf1a30Sjl pcmu_p->pcmu_numproxy = pcmu_get_numproxy(pcmu_p->pcmu_dip);
10425cf1a30Sjl PCMU_DBG1(PCMU_DBG_ATTACH, dip, "get_pcmu_properties: numproxy=%d\n",
10525cf1a30Sjl pcmu_p->pcmu_numproxy);
10625cf1a30Sjl return (DDI_SUCCESS);
10725cf1a30Sjl }
10825cf1a30Sjl
10925cf1a30Sjl /*
11025cf1a30Sjl * free_pcmu_properties:
11125cf1a30Sjl *
11225cf1a30Sjl * This routine frees the memory used to cache the
11325cf1a30Sjl * "ranges" properties of the pci bus device node.
11425cf1a30Sjl *
11525cf1a30Sjl * used by: pcmu_detach()
11625cf1a30Sjl *
11725cf1a30Sjl * return value: none
11825cf1a30Sjl */
11925cf1a30Sjl void
free_pcmu_properties(pcmu_t * pcmu_p)12025cf1a30Sjl free_pcmu_properties(pcmu_t *pcmu_p)
12125cf1a30Sjl {
12225cf1a30Sjl kmem_free(pcmu_p->pcmu_ranges, pcmu_p->pcmu_ranges_length);
12325cf1a30Sjl }
12425cf1a30Sjl
12525cf1a30Sjl /*
12625cf1a30Sjl * pcmu_reloc_reg
12725cf1a30Sjl *
12825cf1a30Sjl * If the "reg" entry (*pcmu_rp) is relocatable, lookup "assigned-addresses"
12925cf1a30Sjl * property to fetch corresponding relocated address.
13025cf1a30Sjl *
13125cf1a30Sjl * used by: pcmu_map()
13225cf1a30Sjl *
13325cf1a30Sjl * return value:
13425cf1a30Sjl *
13525cf1a30Sjl * DDI_SUCCESS - on success
13625cf1a30Sjl * DDI_ME_INVAL - regspec is invalid
13725cf1a30Sjl */
13825cf1a30Sjl int
pcmu_reloc_reg(dev_info_t * dip,dev_info_t * rdip,pcmu_t * pcmu_p,pci_regspec_t * rp)13925cf1a30Sjl pcmu_reloc_reg(dev_info_t *dip, dev_info_t *rdip, pcmu_t *pcmu_p,
140*9b65801eSToomas Soome pci_regspec_t *rp)
14125cf1a30Sjl {
14225cf1a30Sjl int assign_len, assign_entries, i;
14325cf1a30Sjl pci_regspec_t *assign_p;
14425cf1a30Sjl register uint32_t phys_hi = rp->pci_phys_hi;
14525cf1a30Sjl register uint32_t mask = PCI_REG_ADDR_M | PCI_CONF_ADDR_MASK;
14625cf1a30Sjl register uint32_t phys_addr = phys_hi & mask;
14725cf1a30Sjl
14825cf1a30Sjl PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip,
14925cf1a30Sjl "\tpcmu_reloc_reg fr: %x.%x.%x %x.%x\n",
15025cf1a30Sjl rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
15125cf1a30Sjl rp->pci_size_hi, rp->pci_size_low);
15225cf1a30Sjl
15325cf1a30Sjl if ((phys_hi & PCI_RELOCAT_B) || !(phys_hi & PCI_ADDR_MASK)) {
15425cf1a30Sjl return (DDI_SUCCESS);
15525cf1a30Sjl }
15625cf1a30Sjl
15725cf1a30Sjl /* phys_mid must be 0 regardless space type. XXX-64 bit mem space */
15825cf1a30Sjl if (rp->pci_phys_mid != 0 || rp->pci_size_hi != 0) {
15925cf1a30Sjl PCMU_DBG0(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip,
16025cf1a30Sjl "phys_mid or size_hi not 0\n");
16125cf1a30Sjl return (DDI_ME_INVAL);
16225cf1a30Sjl }
16325cf1a30Sjl
16425cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
16525cf1a30Sjl "assigned-addresses", (caddr_t)&assign_p, &assign_len)) {
16625cf1a30Sjl return (DDI_ME_INVAL);
16725cf1a30Sjl }
16825cf1a30Sjl
16925cf1a30Sjl assign_entries = assign_len / sizeof (pci_regspec_t);
17025cf1a30Sjl for (i = 0; i < assign_entries; i++, assign_p++) {
17125cf1a30Sjl if ((assign_p->pci_phys_hi & mask) == phys_addr) {
17225cf1a30Sjl rp->pci_phys_low += assign_p->pci_phys_low;
17325cf1a30Sjl break;
17425cf1a30Sjl }
17525cf1a30Sjl }
17625cf1a30Sjl kmem_free(assign_p - i, assign_len);
17725cf1a30Sjl PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip,
17825cf1a30Sjl "\tpcmu_reloc_reg to: %x.%x.%x %x.%x\n",
17925cf1a30Sjl rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
18025cf1a30Sjl rp->pci_size_hi, rp->pci_size_low);
18125cf1a30Sjl return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL);
18225cf1a30Sjl }
18325cf1a30Sjl
18425cf1a30Sjl /*
18525cf1a30Sjl * use "ranges" to translate relocated pci regspec into parent space
18625cf1a30Sjl */
18725cf1a30Sjl int
pcmu_xlate_reg(pcmu_t * pcmu_p,pci_regspec_t * pcmu_rp,struct regspec * new_rp)18825cf1a30Sjl pcmu_xlate_reg(pcmu_t *pcmu_p, pci_regspec_t *pcmu_rp, struct regspec *new_rp)
18925cf1a30Sjl {
19025cf1a30Sjl int n;
19125cf1a30Sjl pcmu_ranges_t *rng_p = pcmu_p->pcmu_ranges;
19225cf1a30Sjl int rng_n = pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t);
19325cf1a30Sjl
19425cf1a30Sjl uint32_t space_type = PCI_REG_ADDR_G(pcmu_rp->pci_phys_hi);
19525cf1a30Sjl uint32_t reg_end, reg_begin = pcmu_rp->pci_phys_low;
19625cf1a30Sjl uint32_t sz = pcmu_rp->pci_size_low;
19725cf1a30Sjl
19825cf1a30Sjl uint32_t rng_begin, rng_end;
19925cf1a30Sjl
20025cf1a30Sjl if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
20125cf1a30Sjl if (reg_begin > PCI_CONF_HDR_SIZE) {
20225cf1a30Sjl return (DDI_ME_INVAL);
20325cf1a30Sjl }
20425cf1a30Sjl sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE;
20525cf1a30Sjl reg_begin += pcmu_rp->pci_phys_hi;
20625cf1a30Sjl }
20725cf1a30Sjl reg_end = reg_begin + sz - 1;
20825cf1a30Sjl
20925cf1a30Sjl for (n = 0; n < rng_n; n++, rng_p++) {
21025cf1a30Sjl if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) {
21125cf1a30Sjl continue; /* not the same space type */
21225cf1a30Sjl }
21325cf1a30Sjl
21425cf1a30Sjl rng_begin = rng_p->child_low;
21525cf1a30Sjl if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
21625cf1a30Sjl rng_begin += rng_p->child_high;
21725cf1a30Sjl }
21825cf1a30Sjl rng_end = rng_begin + rng_p->size_low - 1;
21925cf1a30Sjl if (reg_begin >= rng_begin && reg_end <= rng_end) {
22025cf1a30Sjl break;
22125cf1a30Sjl }
22225cf1a30Sjl }
22325cf1a30Sjl if (n >= rng_n) {
22425cf1a30Sjl return (DDI_ME_REGSPEC_RANGE);
22525cf1a30Sjl }
22625cf1a30Sjl
22725cf1a30Sjl new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low;
22825cf1a30Sjl new_rp->regspec_bustype = rng_p->parent_high;
22925cf1a30Sjl new_rp->regspec_size = sz;
23025cf1a30Sjl PCMU_DBG4(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip,
23125cf1a30Sjl "\tpcmu_xlate_reg: entry %d new_rp %x.%x %x\n",
23225cf1a30Sjl n, new_rp->regspec_bustype, new_rp->regspec_addr, sz);
23325cf1a30Sjl return (DDI_SUCCESS);
23425cf1a30Sjl }
23525cf1a30Sjl
23625cf1a30Sjl
23725cf1a30Sjl /*
23825cf1a30Sjl * pcmu_report_dev
23925cf1a30Sjl *
24025cf1a30Sjl * This function is called from our control ops routine on a
24125cf1a30Sjl * DDI_CTLOPS_REPORTDEV request.
24225cf1a30Sjl *
24325cf1a30Sjl * The display format is
24425cf1a30Sjl *
24525cf1a30Sjl * <name><inst> at <pname><pinst> device <dev> function <func>
24625cf1a30Sjl *
24725cf1a30Sjl * where
24825cf1a30Sjl *
24925cf1a30Sjl * <name> this device's name property
25025cf1a30Sjl * <inst> this device's instance number
25125cf1a30Sjl * <name> parent device's name property
25225cf1a30Sjl * <inst> parent device's instance number
25325cf1a30Sjl * <dev> this device's device number
25425cf1a30Sjl * <func> this device's function number
25525cf1a30Sjl */
25625cf1a30Sjl int
pcmu_report_dev(dev_info_t * dip)25725cf1a30Sjl pcmu_report_dev(dev_info_t *dip)
25825cf1a30Sjl {
25925cf1a30Sjl if (dip == (dev_info_t *)0) {
26025cf1a30Sjl return (DDI_FAILURE);
26125cf1a30Sjl }
26225cf1a30Sjl cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", ddi_node_name(dip),
26325cf1a30Sjl ddi_get_name_addr(dip), ddi_driver_name(dip),
26425cf1a30Sjl ddi_get_instance(dip));
26525cf1a30Sjl return (DDI_SUCCESS);
26625cf1a30Sjl }
26725cf1a30Sjl
26825cf1a30Sjl /*
26925cf1a30Sjl * name_child
27025cf1a30Sjl *
27125cf1a30Sjl * This function is called from pcmu_init_child to name a node. It is
27225cf1a30Sjl * also passed as a callback for node merging functions.
27325cf1a30Sjl *
27425cf1a30Sjl * return value: DDI_SUCCESS, DDI_FAILURE
27525cf1a30Sjl */
27625cf1a30Sjl static int
name_child(dev_info_t * child,char * name,int namelen)27725cf1a30Sjl name_child(dev_info_t *child, char *name, int namelen)
27825cf1a30Sjl {
27925cf1a30Sjl pci_regspec_t *pcmu_rp;
28025cf1a30Sjl int reglen;
28125cf1a30Sjl uint_t func;
28225cf1a30Sjl char **unit_addr;
28325cf1a30Sjl uint_t n;
28425cf1a30Sjl
28525cf1a30Sjl /*
28625cf1a30Sjl * Set the address portion of the node name based on
28725cf1a30Sjl * unit-address property, if it exists.
28825cf1a30Sjl * The interpretation of the unit-address is DD[,F]
28925cf1a30Sjl * where DD is the device id and F is the function.
29025cf1a30Sjl */
29125cf1a30Sjl if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
29225cf1a30Sjl DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) ==
29325cf1a30Sjl DDI_PROP_SUCCESS) {
29425cf1a30Sjl if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
29525cf1a30Sjl cmn_err(CE_WARN, "unit-address property in %s.conf"
29625cf1a30Sjl " not well-formed", ddi_driver_name(child));
29725cf1a30Sjl ddi_prop_free(unit_addr);
29825cf1a30Sjl return (DDI_FAILURE);
29925cf1a30Sjl }
30025cf1a30Sjl (void) snprintf(name, namelen, "%s", *unit_addr);
30125cf1a30Sjl ddi_prop_free(unit_addr);
30225cf1a30Sjl return (DDI_SUCCESS);
30325cf1a30Sjl }
30425cf1a30Sjl
30525cf1a30Sjl /*
30625cf1a30Sjl * The unit-address property is does not exist. Set the address
30725cf1a30Sjl * portion of the node name based on the function and device number.
30825cf1a30Sjl */
30925cf1a30Sjl if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
31025cf1a30Sjl "reg", (int **)&pcmu_rp, (uint_t *)®len) == DDI_SUCCESS) {
31125cf1a30Sjl if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) {
31225cf1a30Sjl cmn_err(CE_WARN, "reg property not well-formed");
31325cf1a30Sjl return (DDI_FAILURE);
31425cf1a30Sjl }
31525cf1a30Sjl
31625cf1a30Sjl func = PCI_REG_FUNC_G(pcmu_rp[0].pci_phys_hi);
31725cf1a30Sjl if (func != 0) {
31825cf1a30Sjl (void) snprintf(name, namelen, "%x,%x",
31907d06da5SSurya Prakki PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi), func);
32025cf1a30Sjl } else {
32125cf1a30Sjl (void) snprintf(name, namelen, "%x",
32207d06da5SSurya Prakki PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi));
32325cf1a30Sjl }
32425cf1a30Sjl ddi_prop_free(pcmu_rp);
32525cf1a30Sjl return (DDI_SUCCESS);
32625cf1a30Sjl }
32725cf1a30Sjl cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child));
32825cf1a30Sjl return (DDI_FAILURE);
32925cf1a30Sjl }
33025cf1a30Sjl
33125cf1a30Sjl int
pcmu_uninit_child(pcmu_t * pcmu_p,dev_info_t * child)33225cf1a30Sjl pcmu_uninit_child(pcmu_t *pcmu_p, dev_info_t *child)
33325cf1a30Sjl {
33425cf1a30Sjl PCMU_DBG2(PCMU_DBG_CTLOPS, pcmu_p->pcmu_dip,
33525cf1a30Sjl "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n",
33625cf1a30Sjl ddi_driver_name(child), ddi_get_instance(child));
33725cf1a30Sjl
33825cf1a30Sjl ddi_set_name_addr(child, NULL);
33925cf1a30Sjl ddi_remove_minor_node(child, NULL);
34025cf1a30Sjl impl_rem_dev_props(child);
34125cf1a30Sjl
34225cf1a30Sjl PCMU_DBG0(PCMU_DBG_PWR, ddi_get_parent(child), "\n\n");
34325cf1a30Sjl return (DDI_SUCCESS);
34425cf1a30Sjl }
34525cf1a30Sjl
34625cf1a30Sjl /*
34725cf1a30Sjl * pcmu_init_child
34825cf1a30Sjl *
34925cf1a30Sjl * This function is called from our control ops routine on a
35025cf1a30Sjl * DDI_CTLOPS_INITCHILD request. It builds and sets the device's
35125cf1a30Sjl * parent private data area.
35225cf1a30Sjl *
35325cf1a30Sjl * used by: pcmu_ctlops()
35425cf1a30Sjl *
35525cf1a30Sjl * return value: none
35625cf1a30Sjl */
35725cf1a30Sjl int
pcmu_init_child(pcmu_t * pcmu_p,dev_info_t * child)35825cf1a30Sjl pcmu_init_child(pcmu_t *pcmu_p, dev_info_t *child)
35925cf1a30Sjl {
36025cf1a30Sjl char name[10];
36125cf1a30Sjl ddi_acc_handle_t config_handle;
36225cf1a30Sjl uint8_t bcr;
36325cf1a30Sjl uint8_t header_type;
36425cf1a30Sjl
36525cf1a30Sjl if (name_child(child, name, 10) != DDI_SUCCESS)
36625cf1a30Sjl return (DDI_FAILURE);
36725cf1a30Sjl ddi_set_name_addr(child, name);
36825cf1a30Sjl
36925cf1a30Sjl PCMU_DBG2(PCMU_DBG_PWR, ddi_get_parent(child),
37025cf1a30Sjl "INITCHILD: config regs setup for %s@%s\n",
37125cf1a30Sjl ddi_node_name(child), ddi_get_name_addr(child));
37225cf1a30Sjl
37325cf1a30Sjl /*
37425cf1a30Sjl * Map the child configuration space to for initialization.
37525cf1a30Sjl * We assume the obp will do the following in the devices
37625cf1a30Sjl * config space:
37725cf1a30Sjl *
37825cf1a30Sjl * Set the latency-timer register to values appropriate
37925cf1a30Sjl * for the devices on the bus (based on other devices
38025cf1a30Sjl * MIN_GNT and MAX_LAT registers.
38125cf1a30Sjl *
38225cf1a30Sjl * Set the fast back-to-back enable bit in the command
38325cf1a30Sjl * register if it's supported and all devices on the bus
38425cf1a30Sjl * have the capability.
38525cf1a30Sjl *
38625cf1a30Sjl */
38725cf1a30Sjl if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
38825cf1a30Sjl ddi_set_name_addr(child, NULL);
38925cf1a30Sjl return (DDI_FAILURE);
39025cf1a30Sjl }
39125cf1a30Sjl
39225cf1a30Sjl /*
39325cf1a30Sjl * Determine the configuration header type.
39425cf1a30Sjl */
39525cf1a30Sjl header_type = pci_config_get8(config_handle, PCI_CONF_HEADER);
39625cf1a30Sjl PCMU_DBG2(PCMU_DBG_INIT_CLD, pcmu_p->pcmu_dip, "%s: header_type=%x\n",
39725cf1a30Sjl ddi_driver_name(child), header_type);
39825cf1a30Sjl
39925cf1a30Sjl /*
40025cf1a30Sjl * If the device has a bus control register then program it
40125cf1a30Sjl * based on the settings in the command register.
40225cf1a30Sjl */
40325cf1a30Sjl if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
40425cf1a30Sjl bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL);
40525cf1a30Sjl if (pcmu_command_default & PCI_COMM_PARITY_DETECT)
40625cf1a30Sjl bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE;
40725cf1a30Sjl if (pcmu_command_default & PCI_COMM_SERR_ENABLE)
40825cf1a30Sjl bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE;
40925cf1a30Sjl bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE;
41025cf1a30Sjl pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr);
41125cf1a30Sjl }
41225cf1a30Sjl
41325cf1a30Sjl pci_config_teardown(&config_handle);
41425cf1a30Sjl return (DDI_SUCCESS);
41525cf1a30Sjl }
41625cf1a30Sjl
41725cf1a30Sjl /*
41825cf1a30Sjl * pcmu_get_reg_set_size
41925cf1a30Sjl *
42025cf1a30Sjl * Given a dev info pointer to a pci child and a register number, this
42125cf1a30Sjl * routine returns the size element of that reg set property.
42225cf1a30Sjl *
42325cf1a30Sjl * used by: pcmu_ctlops() - DDI_CTLOPS_REGSIZE
42425cf1a30Sjl *
42525cf1a30Sjl * return value: size of reg set on success, zero on error
42625cf1a30Sjl */
42725cf1a30Sjl off_t
pcmu_get_reg_set_size(dev_info_t * child,int rnumber)42825cf1a30Sjl pcmu_get_reg_set_size(dev_info_t *child, int rnumber)
42925cf1a30Sjl {
43025cf1a30Sjl pci_regspec_t *pcmu_rp;
43125cf1a30Sjl off_t size;
43225cf1a30Sjl int i;
43325cf1a30Sjl
43425cf1a30Sjl if (rnumber < 0) {
43525cf1a30Sjl return (0);
43625cf1a30Sjl }
43725cf1a30Sjl
43825cf1a30Sjl /*
43925cf1a30Sjl * Get the reg property for the device.
44025cf1a30Sjl */
44125cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
44225cf1a30Sjl (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) {
44325cf1a30Sjl return (0);
44425cf1a30Sjl }
44525cf1a30Sjl
44625cf1a30Sjl if (rnumber >= (i / (int)sizeof (pci_regspec_t))) {
44725cf1a30Sjl kmem_free(pcmu_rp, i);
44825cf1a30Sjl return (0);
44925cf1a30Sjl }
45025cf1a30Sjl
45125cf1a30Sjl size = pcmu_rp[rnumber].pci_size_low |
45207d06da5SSurya Prakki ((uint64_t)pcmu_rp[rnumber].pci_size_hi << 32);
45325cf1a30Sjl kmem_free(pcmu_rp, i);
45425cf1a30Sjl return (size);
45525cf1a30Sjl }
45625cf1a30Sjl
45725cf1a30Sjl
45825cf1a30Sjl /*
45925cf1a30Sjl * pcmu_get_nreg_set
46025cf1a30Sjl *
46125cf1a30Sjl * Given a dev info pointer to a pci child, this routine returns the
46225cf1a30Sjl * number of sets in its "reg" property.
46325cf1a30Sjl *
46425cf1a30Sjl * used by: pcmu_ctlops() - DDI_CTLOPS_NREGS
46525cf1a30Sjl *
46625cf1a30Sjl * return value: # of reg sets on success, zero on error
46725cf1a30Sjl */
46825cf1a30Sjl uint_t
pcmu_get_nreg_set(dev_info_t * child)46925cf1a30Sjl pcmu_get_nreg_set(dev_info_t *child)
47025cf1a30Sjl {
47125cf1a30Sjl pci_regspec_t *pcmu_rp;
47225cf1a30Sjl int i, n;
47325cf1a30Sjl
47425cf1a30Sjl /*
47525cf1a30Sjl * Get the reg property for the device.
47625cf1a30Sjl */
47725cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
47825cf1a30Sjl (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) {
47925cf1a30Sjl return (0);
48025cf1a30Sjl }
48125cf1a30Sjl n = i / (int)sizeof (pci_regspec_t);
48225cf1a30Sjl kmem_free(pcmu_rp, i);
48325cf1a30Sjl return (n);
48425cf1a30Sjl }
48525cf1a30Sjl
48625cf1a30Sjl int
pcmu_cfg_report(dev_info_t * dip,ddi_fm_error_t * derr,pcmu_errstate_t * pcmu_err_p,int caller,uint32_t prierr)48725cf1a30Sjl pcmu_cfg_report(dev_info_t *dip, ddi_fm_error_t *derr,
48825cf1a30Sjl pcmu_errstate_t *pcmu_err_p, int caller, uint32_t prierr)
48925cf1a30Sjl {
49025cf1a30Sjl int fatal = 0;
49125cf1a30Sjl int nonfatal = 0;
49225cf1a30Sjl int i;
49325cf1a30Sjl pcmu_t *pcmu_p;
49425cf1a30Sjl int instance = ddi_get_instance(dip);
49525cf1a30Sjl
49625cf1a30Sjl ASSERT(dip);
49725cf1a30Sjl
49825cf1a30Sjl pcmu_p = get_pcmu_soft_state(instance);
49925cf1a30Sjl
50025cf1a30Sjl derr->fme_ena = derr->fme_ena ? derr->fme_ena :
50125cf1a30Sjl fm_ena_generate(0, FM_ENA_FMT1);
50225cf1a30Sjl
50325cf1a30Sjl for (i = 0; pci_err_tbl[i].err_class != NULL; i++) {
50425cf1a30Sjl if (pcmu_err_p->pcmu_cfg_stat & pci_err_tbl[i].reg_bit) {
50525cf1a30Sjl char buf[FM_MAX_CLASS];
50625cf1a30Sjl char *aux_msg = NULL;
50725cf1a30Sjl
50825cf1a30Sjl switch (pci_err_tbl[i].reg_bit) {
50925cf1a30Sjl case PCI_STAT_R_MAST_AB:
510*9b65801eSToomas Soome aux_msg = "Receieved Master Abort";
511*9b65801eSToomas Soome /* FALLTHROUGH */
51225cf1a30Sjl case PCI_STAT_R_TARG_AB:
51325cf1a30Sjl if (aux_msg != NULL)
514*9b65801eSToomas Soome aux_msg = "Receieved Target Abort";
51525cf1a30Sjl if (prierr) {
51625cf1a30Sjl /*
51725cf1a30Sjl * piow case are already handled in
51825cf1a30Sjl * pcmu_pbm_afsr_report()
51925cf1a30Sjl */
52025cf1a30Sjl break;
52125cf1a30Sjl }
52225cf1a30Sjl if (caller != PCI_TRAP_CALL) {
52325cf1a30Sjl /*
52425cf1a30Sjl * if we haven't come from trap handler
52525cf1a30Sjl * we won't have an address
52625cf1a30Sjl */
52725cf1a30Sjl fatal++;
52825cf1a30Sjl }
52925cf1a30Sjl break;
53025cf1a30Sjl default:
53125cf1a30Sjl /*
53225cf1a30Sjl * dpe on dma write or ta on dma
53325cf1a30Sjl */
53425cf1a30Sjl nonfatal++;
53525cf1a30Sjl break;
53625cf1a30Sjl }
53725cf1a30Sjl (void) snprintf(buf, FM_MAX_CLASS, "%s %s: %s %s",
53825cf1a30Sjl (pcmu_p->pcmu_pcbm_p)->pcbm_nameinst_str,
53925cf1a30Sjl (pcmu_p->pcmu_pcbm_p)->pcbm_nameaddr_str,
54025cf1a30Sjl "PCI config space:", aux_msg);
54107d06da5SSurya Prakki cmn_err(CE_WARN, "%s %s=0x%p", buf, "pbm-csr",
54207d06da5SSurya Prakki (void *)(pcmu_p->pcmu_pcbm_p)->pcbm_ctrl_reg);
54325cf1a30Sjl }
54425cf1a30Sjl }
54525cf1a30Sjl
54625cf1a30Sjl if (fatal)
54725cf1a30Sjl return (DDI_FM_FATAL);
54825cf1a30Sjl else if (nonfatal)
54925cf1a30Sjl return (DDI_FM_NONFATAL);
55025cf1a30Sjl
55125cf1a30Sjl return (DDI_FM_OK);
55225cf1a30Sjl }
55325cf1a30Sjl
55425cf1a30Sjl void
pcmu_child_cfg_save(dev_info_t * dip)55525cf1a30Sjl pcmu_child_cfg_save(dev_info_t *dip)
55625cf1a30Sjl {
55725cf1a30Sjl dev_info_t *cdip;
55825cf1a30Sjl int ret = DDI_SUCCESS;
55925cf1a30Sjl
56025cf1a30Sjl /*
56125cf1a30Sjl * Save the state of the configuration headers of child
56225cf1a30Sjl * nodes.
56325cf1a30Sjl */
56425cf1a30Sjl
56525cf1a30Sjl for (cdip = ddi_get_child(dip); cdip != NULL;
56625cf1a30Sjl cdip = ddi_get_next_sibling(cdip)) {
56725cf1a30Sjl
56825cf1a30Sjl /*
56925cf1a30Sjl * Not interested in children who are not already
57025cf1a30Sjl * init'ed. They will be set up in pcmu_init_child().
57125cf1a30Sjl */
57225cf1a30Sjl if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
57325cf1a30Sjl PCMU_DBG2(PCMU_DBG_DETACH, dip, "DDI_SUSPEND: skipping "
57425cf1a30Sjl "%s%d not in CF1\n", ddi_driver_name(cdip),
57525cf1a30Sjl ddi_get_instance(cdip));
57625cf1a30Sjl
57725cf1a30Sjl continue;
57825cf1a30Sjl }
57925cf1a30Sjl
58025cf1a30Sjl /*
58125cf1a30Sjl * Only save config registers if not already saved by child.
58225cf1a30Sjl */
58325cf1a30Sjl if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
58425cf1a30Sjl SAVED_CONFIG_REGS) == 1) {
58525cf1a30Sjl
58625cf1a30Sjl continue;
58725cf1a30Sjl }
58825cf1a30Sjl
58925cf1a30Sjl /*
59025cf1a30Sjl * The nexus needs to save config registers. Create a property
59125cf1a30Sjl * so it knows to restore on resume.
59225cf1a30Sjl */
59325cf1a30Sjl ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
59425cf1a30Sjl "nexus-saved-config-regs");
59525cf1a30Sjl
59625cf1a30Sjl if (ret != DDI_PROP_SUCCESS) {
59725cf1a30Sjl cmn_err(CE_WARN, "%s%d can't update prop %s",
59825cf1a30Sjl ddi_driver_name(cdip), ddi_get_instance(cdip),
59925cf1a30Sjl "nexus-saved-config-regs");
60025cf1a30Sjl }
60125cf1a30Sjl
60225cf1a30Sjl (void) pci_save_config_regs(cdip);
60325cf1a30Sjl }
60425cf1a30Sjl }
60525cf1a30Sjl
60625cf1a30Sjl void
pcmu_child_cfg_restore(dev_info_t * dip)60725cf1a30Sjl pcmu_child_cfg_restore(dev_info_t *dip)
60825cf1a30Sjl {
60925cf1a30Sjl dev_info_t *cdip;
61025cf1a30Sjl
61125cf1a30Sjl /*
61225cf1a30Sjl * Restore config registers for children that did not save
61325cf1a30Sjl * their own registers. Children pwr states are UNKNOWN after
61425cf1a30Sjl * a resume since it is possible for the PM framework to call
61525cf1a30Sjl * resume without an actual power cycle. (ie if suspend fails).
61625cf1a30Sjl */
61725cf1a30Sjl for (cdip = ddi_get_child(dip); cdip != NULL;
61825cf1a30Sjl cdip = ddi_get_next_sibling(cdip)) {
61925cf1a30Sjl
62025cf1a30Sjl /*
62125cf1a30Sjl * Not interested in children who are not already
62225cf1a30Sjl * init'ed. They will be set up by pcmu_init_child().
62325cf1a30Sjl */
62425cf1a30Sjl if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
62525cf1a30Sjl PCMU_DBG2(PCMU_DBG_DETACH, dip,
62625cf1a30Sjl "DDI_RESUME: skipping %s%d not in CF1\n",
62725cf1a30Sjl ddi_driver_name(cdip), ddi_get_instance(cdip));
62825cf1a30Sjl continue;
62925cf1a30Sjl }
63025cf1a30Sjl
63125cf1a30Sjl /*
63225cf1a30Sjl * Only restore config registers if saved by nexus.
63325cf1a30Sjl */
63425cf1a30Sjl if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
63525cf1a30Sjl "nexus-saved-config-regs") == 1) {
63625cf1a30Sjl (void) pci_restore_config_regs(cdip);
63725cf1a30Sjl
63825cf1a30Sjl PCMU_DBG2(PCMU_DBG_PWR, dip,
63925cf1a30Sjl "DDI_RESUME: nexus restoring %s%d config regs\n",
64025cf1a30Sjl ddi_driver_name(cdip), ddi_get_instance(cdip));
64125cf1a30Sjl
64225cf1a30Sjl if (ndi_prop_remove(DDI_DEV_T_NONE, cdip,
64325cf1a30Sjl "nexus-saved-config-regs") != DDI_PROP_SUCCESS) {
64425cf1a30Sjl cmn_err(CE_WARN, "%s%d can't remove prop %s",
64525cf1a30Sjl ddi_driver_name(cdip),
64625cf1a30Sjl ddi_get_instance(cdip),
64725cf1a30Sjl "nexus-saved-config-regs");
64825cf1a30Sjl }
64925cf1a30Sjl }
65025cf1a30Sjl }
65125cf1a30Sjl }
65225cf1a30Sjl
65325cf1a30Sjl #ifdef DEBUG
65425cf1a30Sjl extern uint64_t pcmu_debug_flags;
65525cf1a30Sjl
65625cf1a30Sjl pcmu_dflag_to_str_t pcmu_dflag_strings [] = {
65725cf1a30Sjl {PCMU_DBG_ATTACH, "pcmu_attach"},
65825cf1a30Sjl {PCMU_DBG_DETACH, "pcmu_detach"},
65925cf1a30Sjl {PCMU_DBG_MAP, "pcmu_map"},
66025cf1a30Sjl {PCMU_DBG_A_INTX, "pcmu_add_intx"},
66125cf1a30Sjl {PCMU_DBG_R_INTX, "pcmu_rem_intx"},
66225cf1a30Sjl {PCMU_DBG_INIT_CLD, "pcmu_init_child"},
66325cf1a30Sjl {PCMU_DBG_CTLOPS, "pcmu_ctlops"},
66425cf1a30Sjl {PCMU_DBG_INTR, "pcmu_intr_wrapper"},
66525cf1a30Sjl {PCMU_DBG_ERR_INTR, "pcmu_pbm_error_intr"},
66625cf1a30Sjl {PCMU_DBG_BUS_FAULT, "pcmu_fault"},
66725cf1a30Sjl {PCMU_DBG_IB, "pcmu_ib"},
66825cf1a30Sjl {PCMU_DBG_CB, "pcmu_cb"},
66925cf1a30Sjl {PCMU_DBG_PBM, "pcmu_pbm"},
67025cf1a30Sjl {PCMU_DBG_OPEN, "pcmu_open"},
67125cf1a30Sjl {PCMU_DBG_CLOSE, "pcmu_close"},
67225cf1a30Sjl {PCMU_DBG_IOCTL, "pcmu_ioctl"},
67325cf1a30Sjl {PCMU_DBG_PWR, "pcmu_pwr"}
67425cf1a30Sjl };
67525cf1a30Sjl
67625cf1a30Sjl void
pcmu_debug(uint64_t flag,dev_info_t * dip,char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)67725cf1a30Sjl pcmu_debug(uint64_t flag, dev_info_t *dip, char *fmt,
678*9b65801eSToomas Soome uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
67925cf1a30Sjl {
68025cf1a30Sjl char *s = "pcmu unknown";
68125cf1a30Sjl uint_t cont = 0;
68225cf1a30Sjl int i;
68325cf1a30Sjl int no_rec = (sizeof (pcmu_dflag_strings) /
68425cf1a30Sjl sizeof (pcmu_dflag_to_str_t));
68525cf1a30Sjl
68625cf1a30Sjl if (flag & PCMU_DBG_CONT) {
68725cf1a30Sjl flag &= ~PCMU_DBG_CONT;
68825cf1a30Sjl cont = 1;
68925cf1a30Sjl }
69025cf1a30Sjl if ((pcmu_debug_flags & flag) == flag) {
69125cf1a30Sjl for (i = 0; i < no_rec; i++) {
69225cf1a30Sjl if (pcmu_dflag_strings[i].flag == flag) {
69325cf1a30Sjl s = pcmu_dflag_strings[i].string;
69425cf1a30Sjl break;
69525cf1a30Sjl }
69625cf1a30Sjl }
69725cf1a30Sjl if (s && cont == 0) {
69825cf1a30Sjl prom_printf("%s(%d): %s: ", ddi_driver_name(dip),
69925cf1a30Sjl ddi_get_instance(dip), s);
70025cf1a30Sjl }
70125cf1a30Sjl prom_printf(fmt, a1, a2, a3, a4, a5);
70225cf1a30Sjl }
70325cf1a30Sjl }
70425cf1a30Sjl #endif
705