/* mdesc.c: Sun4V machine description handling.
*
* Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2017 Oracle. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/types.h>
union md_node_info *node_info);
static bool ds_port_node_match(union md_node_info *a_node_info,
union md_node_info *b_node_info);
+static int get_vdev_node_info(struct mdesc_handle *md, u64 node,
+ union md_node_info *node_info);
+static bool vdev_node_match(union md_node_info *a_node_info,
+ union md_node_info *b_node_info);
/* supported node types which can be registered */
static struct md_node_ops md_node_ops_table[] = {
{"virtual-device-port", get_vdev_port_node_info, vdev_port_node_match},
{"domain-services-port", get_ds_port_node_info, ds_port_node_match},
+ {"virtual-device", get_vdev_node_info, vdev_node_match},
{NULL, NULL, NULL}
};
const char *name;
/*
- * Virtual device nodes are distinguished by:
+ * Virtual device port nodes are distinguished by:
* 1. "id" property
* 2. "name" property
* 3. parent node "cfg-handle" property
return true;
}
+static int get_vdev_node_info(struct mdesc_handle *md, u64 node,
+ union md_node_info *node_info)
+{
+ const u64 *cfg_hdlp;
+ const char *name;
+
+ /*
+ * Virtual device nodes are distinguished by:
+ * 1. "cfg-handle" property
+ * 2. "name" property
+ */
+ cfg_hdlp = mdesc_get_property(md, node, "cfg-handle", NULL);
+ name = mdesc_get_property(md, node, "name", NULL);
+
+ if (!cfg_hdlp || !name)
+ return -1;
+
+ strncpy(node_info->vdev.name, name, MDESC_MAX_STR_LEN);
+ node_info->vdev.cfg_hdl = *cfg_hdlp;
+
+ return 0;
+}
+
+static bool vdev_node_match(union md_node_info *a_node_info,
+ union md_node_info *b_node_info)
+{
+ if (a_node_info->vdev.cfg_hdl != b_node_info->vdev.cfg_hdl)
+ return false;
+
+ if (strncmp(a_node_info->vdev.name,
+ b_node_info->vdev.name, MDESC_MAX_STR_LEN) != 0)
+ return false;
+
+ return true;
+}
+
/* Run 'func' on nodes which are in A but not in B. */
static void invoke_on_missing(const char *name,
struct mdesc_handle *a,
* Stephen Rothwell
*
* Adapted to sparc64 by David S. Miller davem@davemloft.net
+ * Copyright (C) 2017 Oracle. All rights reserved.
*/
#include <linux/kernel.h>
str = "vnet";
else if (!strcmp(vdev->type, "vdc-port"))
str = "vdisk";
+ else if (vdev->devalias)
+ str = vdev->devalias;
return sprintf(buf, "%s\n", str);
}
const u64 *cfg_handle;
u64 a;
- cfg_handle = NULL;
+ /* Find the first cfg-handle property for the node starting with
+ * the current node and then, if not found, try the parent nodes.
+ */
+
+ cfg_handle = mdesc_get_property(hp, node, "cfg-handle", NULL);
+ if (cfg_handle)
+ return cfg_handle;
+
mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
u64 target;
struct vio_dev *vdev;
int err, tlen, clen;
const u64 *id, *cfg_handle;
-
+ int devalias_len;
+ const char *devalias;
+
+ /* We use the device-type property to name the device if
+ * present. If not present, fallback to use the name property.
+ * NOTE - since there seem to be several different
+ * virtual-device nodes with a generic device-type of "serial",
+ * we use name property instead for these devices as they
+ * are more descriptive and unique.
+ */
type = mdesc_get_property(hp, mp, "device-type", &tlen);
- if (!type) {
+ if (!type || !strcmp(type, "serial")) {
type = mdesc_get_property(hp, mp, "name", &tlen);
if (!type) {
type = mdesc_node_name(hp, mp);
cfg_handle = vio_cfg_handle(hp, mp);
compat = mdesc_get_property(hp, mp, "device-type", &clen);
- if (!compat) {
- clen = 0;
- } else if (clen > VIO_MAX_COMPAT_LEN) {
+ if (compat && clen > VIO_MAX_COMPAT_LEN) {
printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n",
clen, type);
return NULL;
}
+ devalias = mdesc_get_property(hp, mp, "devalias", &devalias_len);
+ if (devalias && devalias_len > VIO_MAX_STR_LEN) {
+ printk(KERN_ERR "VIO: devalias len %d for [%s] is too long.\n",
+ devalias_len, type);
+ return NULL;
+ }
+
vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
if (!vdev) {
printk(KERN_ERR "VIO: Could not allocate vio_dev\n");
}
memcpy(vdev->type, type, tlen);
- if (compat)
+ if (compat) {
memcpy(vdev->compat, compat, clen);
- else
- memset(vdev->compat, 0, sizeof(vdev->compat));
- vdev->compat_len = clen;
+ vdev->compat_len = clen;
+ }
+
+ if (devalias)
+ memcpy(vdev->devalias, devalias, devalias_len);
vdev->port_id = ~0UL;
vdev->tx_irq = 0;
vio_fill_channel_info(hp, mp, vdev);
- if (!id) {
+ /* Create the final name for the device.
+ * If there was no parent given or no id
+ * and no cfg-handle was found, just use the type
+ * for the device name. If we found an id and/or
+ * a cfg-handle, append them to the name to help
+ * make the name as unique as possible.
+ */
+ if (parent == NULL || (!id && !cfg_handle)) {
dev_set_name(&vdev->dev, "%s", type);
vdev->dev_no = ~(u64)0;
} else if (!cfg_handle) {
dev_set_name(&vdev->dev, "%s-%llu", type, *id);
vdev->dev_no = *id;
+ } else if (!id) {
+ dev_set_name(&vdev->dev, "%s-%llu", type, *cfg_handle);
+ vdev->dev_no = *cfg_handle;
} else {
dev_set_name(&vdev->dev, "%s-%llu-%llu", type,
*cfg_handle, *id);
}
}
-static struct mdesc_notifier_client vio_device_notifier = {
+static struct mdesc_notifier_client vio_device_port_notifier = {
.add = vio_add,
.remove = vio_remove,
.node_name = "virtual-device-port",
};
+/* We are only interested in virtual device ports under the
+ * "channel-devices" node.
+ */
+static void vio_add_vdev(struct mdesc_handle *hp, u64 node,
+ const char *node_name)
+{
+ int found;
+ u64 a;
+
+ found = 0;
+ mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
+ u64 target = mdesc_arc_target(hp, a);
+ const char *name = mdesc_node_name(hp, target);
+
+ if (!strcmp(name, "channel-devices")) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ (void) vio_create_one(hp, node, (char *)node_name,
+ &root_vdev->dev);
+}
+
+static struct mdesc_notifier_client vio_device_notifier = {
+ .add = vio_add_vdev,
+ .remove = vio_remove,
+ .node_name = "virtual-device",
+};
+
/* We are only interested in domain service ports under the
* "domain-services" node. On control nodes there is another port
* under "openboot" that we should not mess with as aparently that is
}
mdesc_register_notifier(&vio_device_notifier);
+ mdesc_register_notifier(&vio_device_port_notifier);
mdesc_register_notifier(&vio_ds_notifier);
mdesc_release(hp);