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 "cpuboard_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 static tnode_t *
  41 cpuboard_node_create(topo_mod_t *mp, tnode_t *parent, const char *name,
  42     int inst, void *priv)
  43 {
  44         tnode_t *node;
  45         nvlist_t *fmri;
  46         nvlist_t *auth = topo_mod_auth(mp, parent);
  47 
  48         topo_mod_dprintf(mp, "cpuboard_node_create:\n");
  49 
  50         if (parent == NULL || inst < 0) {
  51                 return (NULL);
  52         }
  53 
  54         /* Create FMRI */
  55         if ((fmri = topo_mod_hcfmri(mp, parent, FM_HC_SCHEME_VERSION, name,
  56             inst, NULL, auth, NULL, NULL, NULL)) == NULL) {
  57                 topo_mod_dprintf(mp, "create of tnode for %s failed: %s",
  58                     name, topo_strerror(topo_mod_errno(mp)));
  59                 nvlist_free(auth);
  60                 return (NULL);
  61         }
  62         nvlist_free(auth);
  63 
  64         /* Create and bind node  */
  65         node = topo_node_bind(mp, parent, name, inst, fmri);
  66         if (node == NULL) {
  67                 nvlist_free(fmri);
  68                 topo_mod_dprintf(mp, "unable to bind root complex: %s\n",
  69                     topo_strerror(topo_mod_errno(mp)));
  70                 return (NULL); /* mod_errno already set */
  71         }
  72 
  73         nvlist_free(fmri);
  74         topo_node_setspecific(node, priv);
  75 
  76         return (node);
  77 }
  78 
  79 /*
  80  * cpuboard_rc_node_create()
  81  * Description:
  82  *     Create a root complex node pciexrc
  83  * Parameters:
  84  *     mp: topo module pointer
  85  *     parent: topo parent node of the newly created pciexrc node
  86  *     dnode: Solaris device node of the root complex
  87  *     rcpath: Used to populated the dev property of the topo pciexrc node if
  88  *          the local host does not own the root complex.
  89  */
  90 static tnode_t *
  91 cpuboard_rc_node_create(topo_mod_t *mp, tnode_t *parent, di_node_t dnode,
  92     char *rcpath, int inst)
  93 {
  94         int err;
  95         tnode_t *rcn;
  96         char *dnpath;
  97         nvlist_t *mod;
  98 
  99         topo_mod_dprintf(mp, "cpuboard_rc_node_create:\n");
 100 
 101         rcn = cpuboard_node_create(mp, parent, PCIEX_ROOT, inst, (void *)dnode);
 102         if (rcn == NULL) {
 103                 return (NULL);
 104         }
 105 
 106         /* Inherit parent FRU's label */
 107         (void) topo_node_fru_set(rcn, NULL, 0, &err);
 108         (void) topo_node_label_set(rcn, NULL, &err);
 109 
 110         /*
 111          * Set ASRU to be the dev-scheme ASRU
 112          */
 113         if ((dnpath = di_devfs_path(dnode)) != NULL) {
 114                 nvlist_t *fmri;
 115 
 116                 /*
 117                  * The local host owns the root complex, so use the dev path
 118                  * from the di_devfs_path(), instead of the passed in rcpath,
 119                  * to populate the dev property.
 120                  */
 121                 rcpath = dnpath;
 122                 fmri = topo_mod_devfmri(mp, FM_DEV_SCHEME_VERSION,
 123                     dnpath, NULL);
 124                 if (fmri == NULL) {
 125                         topo_mod_dprintf(mp,
 126                             "dev:///%s fmri creation failed.\n",
 127                             dnpath);
 128                         (void) topo_mod_seterrno(mp, err);
 129                         di_devfs_path_free(dnpath);
 130                         return (NULL);
 131                 }
 132                 if (topo_node_asru_set(rcn, fmri, 0, &err) < 0) {
 133                         topo_mod_dprintf(mp, "topo_node_asru_set failed\n");
 134                         (void) topo_mod_seterrno(mp, err);
 135                         nvlist_free(fmri);
 136                         di_devfs_path_free(dnpath);
 137                         return (NULL);
 138                 }
 139                 nvlist_free(fmri);
 140         } else {
 141                 topo_mod_dprintf(mp, "NULL di_devfs_path.\n");
 142         }
 143 
 144         /*
 145          * Set pciexrc properties for root complex nodes
 146          */
 147 
 148         /* Add the io and pci property groups */
 149         if (topo_pgroup_create(rcn, &io_pgroup, &err) < 0) {
 150                 topo_mod_dprintf(mp, "topo_pgroup_create failed\n");
 151                 di_devfs_path_free(dnpath);
 152                 (void) topo_mod_seterrno(mp, err);
 153                 return (NULL);
 154         }
 155         if (topo_pgroup_create(rcn, &pci_pgroup, &err) < 0) {
 156                 topo_mod_dprintf(mp, "topo_pgroup_create failed\n");
 157                 di_devfs_path_free(dnpath);
 158                 (void) topo_mod_seterrno(mp, err);
 159                 return (NULL);
 160         }
 161         /* Add the devfs path property */
 162         if (rcpath) {
 163                 if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEV,
 164                     TOPO_PROP_IMMUTABLE, rcpath, &err) != 0) {
 165                         topo_mod_dprintf(mp, "Failed to set DEV property\n");
 166                         (void) topo_mod_seterrno(mp, err);
 167                 }
 168         }
 169         if (dnpath) {
 170                 di_devfs_path_free(dnpath);
 171         }
 172         /* T5440 device type is always "pciex" */
 173         if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEVTYPE,
 174             TOPO_PROP_IMMUTABLE, CPUBOARD_PX_DEVTYPE, &err) != 0) {
 175                 topo_mod_dprintf(mp, "Failed to set DEVTYPE property\n");
 176         }
 177         /* T5440 driver is always "px" */
 178         if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DRIVER,
 179             TOPO_PROP_IMMUTABLE, CPUBOARD_PX_DRV, &err) != 0) {
 180                 topo_mod_dprintf(mp, "Failed to set DRIVER property\n");
 181         }
 182         if ((mod = topo_mod_modfmri(mp, FM_MOD_SCHEME_VERSION, CPUBOARD_PX_DRV))
 183             == NULL || topo_prop_set_fmri(rcn, TOPO_PGROUP_IO,
 184             TOPO_IO_MODULE, TOPO_PROP_IMMUTABLE, mod,  &err) != 0) {
 185                 topo_mod_dprintf(mp, "Failed to set MODULE property\n");
 186         }
 187         if (mod != NULL)
 188                 nvlist_free(mod);
 189 
 190         /* This is a PCIEX Root Complex */
 191         if (topo_prop_set_string(rcn, TOPO_PGROUP_PCI, TOPO_PCI_EXCAP,
 192             TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err) != 0) {
 193                 topo_mod_dprintf(mp, "Failed to set EXCAP property\n");
 194         }
 195         /* BDF of T5440 root complex is constant */
 196         if (topo_prop_set_string(rcn, TOPO_PGROUP_PCI,
 197             TOPO_PCI_BDF, TOPO_PROP_IMMUTABLE, CPUBOARD_PX_BDF, &err) != 0) {
 198                 topo_mod_dprintf(mp, "Failed to set EXCAP property\n");
 199         }
 200 
 201         /* Make room for children */
 202         (void) topo_node_range_create(mp, rcn, PCIEX_BUS, 0, CPUBOARD_MAX);
 203         return (rcn);
 204 }
 205 
 206 /*
 207  * Create a hostbridge node.
 208  */
 209 static tnode_t *
 210 cpuboard_hb_node_create(topo_mod_t *mp, tnode_t *parent, int inst)
 211 {
 212         int err;
 213         tnode_t *hbn;
 214 
 215         topo_mod_dprintf(mp, "cpuboard_hb_node_create: parent=%p, inst=%d\n",
 216             parent, inst);
 217 
 218         hbn = cpuboard_node_create(mp, parent, HOSTBRIDGE, inst, NULL);
 219         if (hbn == NULL) {
 220                 topo_mod_dprintf(mp, "cpuboard_hb_node_create: "
 221                     "cpuboard_node_create() failed.\n");
 222                 return (NULL);
 223         }
 224 
 225         /* Inherit parent FRU's label */
 226         (void) topo_node_fru_set(hbn, NULL, 0, &err);
 227         (void) topo_node_label_set(hbn, NULL, &err);
 228 
 229         /* Make room for children */
 230         (void) topo_node_range_create(mp, hbn, PCIEX_ROOT, 0, CPUBOARD_MAX);
 231 
 232         topo_mod_dprintf(mp, "cpuboard_hb_node_create: EXIT hbn=%p\n", hbn);
 233 
 234         return (hbn);
 235 }
 236 
 237 /*
 238  * Enumerate hostbridge on the cpuboard.  Hostbridge and root complex instances
 239  * match the cpuboard instance.
 240  */
 241 int
 242 cpuboard_hb_enum(topo_mod_t *mp, di_node_t dnode, char *rcpath,
 243     tnode_t *cpubn, int brd)
 244 {
 245         int hb;
 246         int rc;
 247         tnode_t *hbnode;
 248         tnode_t *rcnode;
 249         topo_mod_t *pcimod;
 250 
 251         topo_mod_dprintf(mp, "cpuboard_hb_enum: brd: %d, cpubn=%p\n",
 252             brd, cpubn);
 253 
 254         /* Load the pcibus module. We'll need it later. */
 255         pcimod = topo_mod_load(mp, PCI_BUS, PCI_BUS_VERS);
 256         if (pcimod == NULL) {
 257                 topo_mod_dprintf(mp, "can't load pcibus module: %s\n",
 258                     topo_strerror(topo_mod_errno(mp)));
 259                 return (-1);
 260         }
 261         hb = rc = brd;
 262 
 263         /* The root complex exists! */
 264         topo_mod_dprintf(mp, "declaring "
 265             "/motherboard=0/cpuboard=%d/hostbridge=%d/"
 266             "pciexrc=%d\n", brd, hb, rc);
 267 
 268         /* Create the hostbridge node */
 269         hbnode = cpuboard_hb_node_create(mp, cpubn, hb);
 270         if (hbnode == NULL) {
 271                 topo_mod_dprintf(mp,
 272                     "unable to create hbnode: %s\n",
 273                     topo_strerror(topo_mod_errno(mp)));
 274                 topo_mod_unload(pcimod);
 275                 return (-1);
 276         }
 277         /* Create the root complex node */
 278         rcnode = cpuboard_rc_node_create(mp, hbnode, dnode, rcpath, rc);
 279         if (rcnode == NULL) {
 280                 topo_mod_dprintf(mp,
 281                     "unable to create rcnode: %s\n",
 282                     topo_strerror(topo_mod_errno(mp)));
 283                 topo_mod_unload(pcimod);
 284                 return (-1);
 285         }
 286         /*
 287          * If dnode not NULL, enumerate pcibus nodes under the root complex.
 288          * If dnode NULL, skip enumeration.  Condition could occur if the RC
 289          * is assigned to non-control domain.
 290          */
 291         if ((dnode != NULL) && topo_mod_enumerate(pcimod, rcnode,
 292             PCI_BUS, PCIEX_BUS, 0, 255, NULL) != 0) {
 293                 topo_mod_dprintf(mp,
 294                     "error enumerating pcibus: %s\n",
 295                     topo_strerror(topo_mod_errno(mp)));
 296                 topo_mod_unload(pcimod);
 297                 return (-1);
 298         }
 299         topo_mod_unload(pcimod);
 300         return (0);
 301 }