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 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <string.h>
  28 #include <strings.h>
  29 #include <libdevinfo.h>
  30 #include <fm/topo_mod.h>
  31 #include <fm/topo_hc.h>
  32 #include <sys/fm/protocol.h>
  33 #include "opl_topo.h"
  34 
  35 static const topo_pgroup_info_t io_pgroup =
  36         { TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
  37 static const topo_pgroup_info_t pci_pgroup =
  38         { TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
  39 
  40 /*
  41  * Check the root complex device node for a slot-names property.
  42  */
  43 const char *
  44 opl_get_slot_name(topo_mod_t *mod, di_node_t n)
  45 {
  46         di_prom_handle_t ptp = DI_PROM_HANDLE_NIL;
  47         di_prom_prop_t pp = DI_PROM_PROP_NIL;
  48         uchar_t *buf;
  49 
  50         if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_PROP_NIL)
  51                 return (NULL);
  52 
  53         for (pp = di_prom_prop_next(ptp, n, pp);
  54             pp != DI_PROM_PROP_NIL;
  55             pp = di_prom_prop_next(ptp, n, pp)) {
  56                 if (strcmp(di_prom_prop_name(pp), OPL_SLOT_NAMES) == 0) {
  57                         if (di_prom_prop_data(pp, &buf) <= sizeof (uint32_t))
  58                                 continue;
  59                         return ((const char *)&buf[4]);
  60                 }
  61         }
  62         return (NULL);
  63 }
  64 
  65 static tnode_t *
  66 opl_node_create(topo_mod_t *mp, tnode_t *parent, const char *name, int inst,
  67     void *priv)
  68 {
  69         tnode_t *node;
  70         nvlist_t *fmri;
  71         nvlist_t *auth = topo_mod_auth(mp, parent);
  72 
  73         if (parent == NULL || inst < 0) {
  74                 return (NULL);
  75         }
  76 
  77         /* Create FMRI */
  78         if ((fmri = topo_mod_hcfmri(mp, parent, FM_HC_SCHEME_VERSION, name,
  79             inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
  80                 topo_mod_dprintf(mp, "create of tnode for %s failed: %s",
  81                     name, topo_strerror(topo_mod_errno(mp)));
  82                 nvlist_free(auth);
  83                 return (NULL);
  84         }
  85         nvlist_free(auth);
  86 
  87         /* Create and bind node  */
  88         node = topo_node_bind(mp, parent, name, inst, fmri);
  89         if (node == NULL) {
  90                 nvlist_free(fmri);
  91                 topo_mod_dprintf(mp, "unable to bind root complex: %s\n",
  92                     topo_strerror(topo_mod_errno(mp)));
  93                 return (NULL); /* mod_errno already set */
  94         }
  95 
  96         nvlist_free(fmri);
  97         topo_node_setspecific(node, priv);
  98 
  99         return (node);
 100 }
 101 
 102 /*
 103  * Create a root complex node.
 104  */
 105 static tnode_t *
 106 opl_rc_node_create(topo_mod_t *mp, tnode_t *parent, di_node_t dnode, int inst)
 107 {
 108         int err;
 109         tnode_t *rcn;
 110         const char *slot_name;
 111         char *dnpath;
 112         nvlist_t *mod;
 113 
 114         rcn = opl_node_create(mp, parent, PCIEX_ROOT, inst, (void *)dnode);
 115         if (rcn == NULL) {
 116                 return (NULL);
 117         }
 118 
 119         /*
 120          * If this root complex connects to a slot, it will have a
 121          * slot-names property.
 122          */
 123         slot_name = opl_get_slot_name(mp, dnode);
 124         if (slot_name) {
 125                 char fru_str[64];
 126                 nvlist_t *fru_fmri;
 127                 /* Add FRU fmri */
 128                 (void) snprintf(fru_str, sizeof (fru_str), "hc:///component=%s",
 129                     slot_name);
 130                 if (topo_mod_str2nvl(mp, fru_str, &fru_fmri) == 0) {
 131                         (void) topo_node_fru_set(rcn, fru_fmri, 0, &err);
 132                         nvlist_free(fru_fmri);
 133                 }
 134                 /* Add label */
 135                 (void) topo_node_label_set(rcn, (char *)slot_name, &err);
 136         } else {
 137                 /* Inherit parent FRU's label */
 138                 (void) topo_node_fru_set(rcn, NULL, 0, &err);
 139                 (void) topo_node_label_set(rcn, NULL, &err);
 140         }
 141 
 142         /*
 143          * Set ASRU to be the dev-scheme ASRU
 144          */
 145         if ((dnpath = di_devfs_path(dnode)) != NULL) {
 146                 nvlist_t *fmri;
 147 
 148                 fmri = topo_mod_devfmri(mp, FM_DEV_SCHEME_VERSION,
 149                     dnpath, NULL);
 150                 if (fmri == NULL) {
 151                         topo_mod_dprintf(mp,
 152                             "dev:///%s fmri creation failed.\n",
 153                             dnpath);
 154                         (void) topo_mod_seterrno(mp, err);
 155                         di_devfs_path_free(dnpath);
 156                         return (NULL);
 157                 }
 158                 if (topo_node_asru_set(rcn, fmri, 0, &err) < 0) {
 159                         topo_mod_dprintf(mp, "topo_node_asru_set failed\n");
 160                         (void) topo_mod_seterrno(mp, err);
 161                         nvlist_free(fmri);
 162                         di_devfs_path_free(dnpath);
 163                         return (NULL);
 164                 }
 165                 nvlist_free(fmri);
 166         } else {
 167                 topo_mod_dprintf(mp, "NULL di_devfs_path.\n");
 168         }
 169 
 170         /*
 171          * Set pciexrc properties for root complex nodes
 172          */
 173 
 174         /* Add the io and pci property groups */
 175         if (topo_pgroup_create(rcn, &io_pgroup, &err) < 0) {
 176                 topo_mod_dprintf(mp, "topo_pgroup_create failed\n");
 177                 di_devfs_path_free(dnpath);
 178                 (void) topo_mod_seterrno(mp, err);
 179                 return (NULL);
 180         }
 181         if (topo_pgroup_create(rcn, &pci_pgroup, &err) < 0) {
 182                 topo_mod_dprintf(mp, "topo_pgroup_create failed\n");
 183                 di_devfs_path_free(dnpath);
 184                 (void) topo_mod_seterrno(mp, err);
 185                 return (NULL);
 186         }
 187         /* Add the devfs path property */
 188         if (dnpath) {
 189                 if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEV,
 190                     TOPO_PROP_IMMUTABLE, dnpath, &err) != 0) {
 191                         topo_mod_dprintf(mp, "Failed to set DEV property\n");
 192                         di_devfs_path_free(dnpath);
 193                         (void) topo_mod_seterrno(mp, err);
 194                 }
 195                 di_devfs_path_free(dnpath);
 196         }
 197         /* Oberon device type is always "pciex" */
 198         if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEVTYPE,
 199             TOPO_PROP_IMMUTABLE, OPL_PX_DEVTYPE, &err) != 0) {
 200                 topo_mod_dprintf(mp, "Failed to set DEVTYPE property\n");
 201         }
 202         /* Oberon driver is always "px" */
 203         if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DRIVER,
 204             TOPO_PROP_IMMUTABLE, OPL_PX_DRV, &err) != 0) {
 205                 topo_mod_dprintf(mp, "Failed to set DRIVER property\n");
 206         }
 207         if ((mod = topo_mod_modfmri(mp, FM_MOD_SCHEME_VERSION, OPL_PX_DRV))
 208             == NULL || topo_prop_set_fmri(rcn, TOPO_PGROUP_IO,
 209             TOPO_IO_MODULE, TOPO_PROP_IMMUTABLE, mod,  &err) != 0) {
 210                 topo_mod_dprintf(mp, "Failed to set MODULE property\n");
 211         }
 212         nvlist_free(mod);
 213 
 214         /* This is a PCIEX Root Complex */
 215         if (topo_prop_set_string(rcn, TOPO_PGROUP_PCI, TOPO_PCI_EXCAP,
 216             TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err) != 0) {
 217                 topo_mod_dprintf(mp, "Failed to set EXCAP property\n");
 218         }
 219         /* BDF of Oberon root complex is constant */
 220         if (topo_prop_set_string(rcn, TOPO_PGROUP_PCI,
 221             TOPO_PCI_BDF, TOPO_PROP_IMMUTABLE, OPL_PX_BDF, &err) != 0) {
 222                 topo_mod_dprintf(mp, "Failed to set EXCAP property\n");
 223         }
 224 
 225         /* Make room for children */
 226         (void) topo_node_range_create(mp, rcn, PCIEX_BUS, 0, OPL_BUS_MAX);
 227         return (rcn);
 228 }
 229 
 230 /*
 231  * Create a hostbridge node.
 232  */
 233 static tnode_t *
 234 opl_hb_node_create(topo_mod_t *mp, tnode_t *parent, int inst)
 235 {
 236         int err;
 237         tnode_t *hbn;
 238 
 239         hbn = opl_node_create(mp, parent, HOSTBRIDGE, inst, NULL);
 240         if (hbn == NULL) {
 241                 return (NULL);
 242         }
 243 
 244         /* Inherit parent FRU's label */
 245         (void) topo_node_fru_set(hbn, NULL, 0, &err);
 246         (void) topo_node_label_set(hbn, NULL, &err);
 247 
 248         /* Make room for children */
 249         (void) topo_node_range_create(mp, hbn, PCIEX_ROOT, 0, OPL_RC_MAX);
 250 
 251         return (hbn);
 252 }
 253 
 254 /*
 255  * opl_hb_enum gets the ioboard instance passed in, and determines the
 256  * hostbridge and root complex instances numbers based on the bus addresses.
 257  */
 258 int
 259 opl_hb_enum(topo_mod_t *mp, const ioboard_contents_t *iob, tnode_t *ion,
 260     int brd)
 261 {
 262         int hb;
 263         int rc;
 264         di_node_t p;
 265         tnode_t *hbnode;
 266         tnode_t *rcnode;
 267         topo_mod_t *pcimod;
 268 
 269         /* Load the pcibus module. We'll need it later. */
 270         pcimod = topo_mod_load(mp, PCI_BUS, PCI_BUS_VERS);
 271         if (pcimod == NULL) {
 272                 topo_mod_dprintf(mp, "can't load pcibus module: %s\n",
 273                     topo_strerror(topo_mod_errno(mp)));
 274                 return (-1);
 275         }
 276 
 277         /* For each hostbridge on an ioboard... */
 278         for (hb = 0; hb < OPL_HB_MAX; hb++) {
 279                 hbnode = NULL;
 280                 /* For each root complex in a hostbridge... */
 281                 for (rc = 0; rc < OPL_RC_MAX; rc++) {
 282                         p = iob->rcs[hb][rc];
 283                         /* If no root complex, continue */
 284                         if (p == DI_NODE_NIL) {
 285                                 continue;
 286                         }
 287 
 288                         /* The root complex exists! */
 289                         topo_mod_dprintf(mp, "declaring "
 290                             "/chassis=0/ioboard=%d/hostbridge=%d/pciexrc=%d\n",
 291                             brd, hb, rc);
 292 
 293                         /*
 294                          * If we haven't created a hostbridge node yet, do it
 295                          * now.
 296                          */
 297                         if (hbnode == NULL) {
 298                                 hbnode = opl_hb_node_create(mp, ion, hb);
 299                                 if (hbnode == NULL) {
 300                                         topo_mod_dprintf(mp,
 301                                             "unable to create hbnode: %s\n",
 302                                             topo_strerror(topo_mod_errno(mp)));
 303                                         topo_mod_unload(pcimod);
 304                                         return (-1);
 305                                 }
 306 
 307                         }
 308 
 309                         /* Create the root complex node */
 310                         rcnode = opl_rc_node_create(mp, hbnode, p, rc);
 311                         if (rcnode == NULL) {
 312                                 topo_mod_dprintf(mp,
 313                                     "unable to create rcnode: %s\n",
 314                                     topo_strerror(topo_mod_errno(mp)));
 315                                 topo_mod_unload(pcimod);
 316                                 return (-1);
 317                         }
 318 
 319                         /* Enumerate pcibus nodes under the root complex */
 320                         if (topo_mod_enumerate(pcimod, rcnode,
 321                             PCI_BUS, PCIEX_BUS, 0, 255, NULL) != 0) {
 322                                 topo_mod_dprintf(mp,
 323                                     "error enumerating pcibus: %s\n",
 324                                     topo_strerror(topo_mod_errno(mp)));
 325                                 topo_mod_unload(pcimod);
 326                                 return (-1);
 327                         }
 328                 }
 329         }
 330         topo_mod_unload(pcimod);
 331         return (0);
 332 }