summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sparc/include/asm/mdesc.h24
-rw-r--r--arch/sparc/include/asm/vio.h13
-rw-r--r--arch/sparc/kernel/ldc.c10
-rw-r--r--arch/sparc/kernel/mdesc.c331
-rw-r--r--arch/sparc/kernel/vio.c244
-rw-r--r--arch/sparc/kernel/viohs.c24
6 files changed, 479 insertions, 167 deletions
diff --git a/arch/sparc/include/asm/mdesc.h b/arch/sparc/include/asm/mdesc.h
index aebeb88f70db..e8a4c413a1c7 100644
--- a/arch/sparc/include/asm/mdesc.h
+++ b/arch/sparc/include/asm/mdesc.h
@@ -16,6 +16,7 @@ struct mdesc_handle *mdesc_grab(void);
void mdesc_release(struct mdesc_handle *);
#define MDESC_NODE_NULL (~(u64)0)
+#define MDESC_MAX_STR_LEN 256
u64 mdesc_node_by_name(struct mdesc_handle *handle,
u64 from_node, const char *name);
@@ -62,15 +63,32 @@ u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc);
void mdesc_update(void);
struct mdesc_notifier_client {
- void (*add)(struct mdesc_handle *handle, u64 node);
- void (*remove)(struct mdesc_handle *handle, u64 node);
-
+ void (*add)(struct mdesc_handle *handle, u64 node,
+ const char *node_name);
+ void (*remove)(struct mdesc_handle *handle, u64 node,
+ const char *node_name);
const char *node_name;
struct mdesc_notifier_client *next;
};
void mdesc_register_notifier(struct mdesc_notifier_client *client);
+union md_node_info {
+ struct vdev_port {
+ u64 id; /* id */
+ u64 parent_cfg_hdl; /* parent config handle */
+ const char *name; /* name (property) */
+ } vdev_port;
+ struct ds_port {
+ u64 id; /* id */
+ } ds_port;
+};
+
+u64 mdesc_get_node(struct mdesc_handle *hp, const char *node_name,
+ union md_node_info *node_info);
+int mdesc_get_node_info(struct mdesc_handle *hp, u64 node,
+ const char *node_name, union md_node_info *node_info);
+
void mdesc_fill_in_cpu_data(cpumask_t *mask);
void mdesc_populate_present_mask(cpumask_t *mask);
void mdesc_get_page_sizes(cpumask_t *mask, unsigned long *pgsz_mask);
diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h
index 9dca7a892978..d1c47e9f0090 100644
--- a/arch/sparc/include/asm/vio.h
+++ b/arch/sparc/include/asm/vio.h
@@ -316,24 +316,33 @@ static inline u32 vio_dring_prev(struct vio_dring_state *dr, u32 index)
}
#define VIO_MAX_TYPE_LEN 32
+#define VIO_MAX_NAME_LEN 32
#define VIO_MAX_COMPAT_LEN 64
struct vio_dev {
u64 mp;
struct device_node *dp;
+ char node_name[VIO_MAX_NAME_LEN];
char type[VIO_MAX_TYPE_LEN];
char compat[VIO_MAX_COMPAT_LEN];
int compat_len;
u64 dev_no;
- u64 id;
+ unsigned long port_id;
unsigned long channel_id;
unsigned int tx_irq;
unsigned int rx_irq;
u64 rx_ino;
+ u64 tx_ino;
+
+ /* Handle to the root of "channel-devices" sub-tree in MDESC */
+ u64 cdev_handle;
+
+ /* MD specific data used to identify the vdev in MD */
+ union md_node_info md_node_info;
struct device dev;
};
@@ -347,6 +356,7 @@ struct vio_driver {
void (*shutdown)(struct vio_dev *dev);
unsigned long driver_data;
struct device_driver driver;
+ bool no_irq;
};
struct vio_version {
@@ -490,5 +500,6 @@ int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev,
void vio_port_up(struct vio_driver_state *vio);
int vio_set_intr(unsigned long dev_ino, int state);
+u64 vio_vdev_node(struct mdesc_handle *hp, struct vio_dev *vdev);
#endif /* _SPARC64_VIO_H */
diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c
index 97a5743b04e2..840e0b21bfe3 100644
--- a/arch/sparc/kernel/ldc.c
+++ b/arch/sparc/kernel/ldc.c
@@ -1347,6 +1347,14 @@ int ldc_bind(struct ldc_channel *lp)
lp->hs_state = LDC_HS_OPEN;
ldc_set_state(lp, LDC_STATE_BOUND);
+ if (lp->cfg.mode == LDC_MODE_RAW) {
+ /*
+ * There is no handshake in RAW mode, so handshake
+ * is completed.
+ */
+ lp->hs_state = LDC_HS_COMPLETE;
+ }
+
spin_unlock_irqrestore(&lp->lock, flags);
return 0;
@@ -1460,11 +1468,13 @@ void ldc_set_state(struct ldc_channel *lp, u8 state)
lp->state = state;
}
+EXPORT_SYMBOL(ldc_set_state);
int ldc_mode(struct ldc_channel *lp)
{
return lp->cfg.mode;
}
+EXPORT_SYMBOL(ldc_mode);
int ldc_rx_reset(struct ldc_channel *lp)
{
diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
index c0765bbf60ea..e4b4e790bf89 100644
--- a/arch/sparc/kernel/mdesc.c
+++ b/arch/sparc/kernel/mdesc.c
@@ -75,6 +75,74 @@ struct mdesc_handle {
struct mdesc_hdr mdesc;
};
+typedef int (*mdesc_node_info_get_f)(struct mdesc_handle *, u64,
+ union md_node_info *);
+typedef void (*mdesc_node_info_rel_f)(union md_node_info *);
+typedef bool (*mdesc_node_match_f)(union md_node_info *, union md_node_info *);
+
+struct md_node_ops {
+ char *name;
+ mdesc_node_info_get_f get_info;
+ mdesc_node_info_rel_f rel_info;
+ mdesc_node_match_f node_match;
+};
+
+static int get_vdev_port_node_info(struct mdesc_handle *md, u64 node,
+ union md_node_info *node_info);
+static void rel_vdev_port_node_info(union md_node_info *node_info);
+static bool vdev_port_node_match(union md_node_info *a_node_info,
+ union md_node_info *b_node_info);
+
+static int get_ds_port_node_info(struct mdesc_handle *md, u64 node,
+ union md_node_info *node_info);
+static void rel_ds_port_node_info(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);
+
+/* supported node types which can be registered */
+static struct md_node_ops md_node_ops_table[] = {
+ {"virtual-device-port", get_vdev_port_node_info,
+ rel_vdev_port_node_info, vdev_port_node_match},
+ {"domain-services-port", get_ds_port_node_info,
+ rel_ds_port_node_info, ds_port_node_match},
+ {NULL, NULL, NULL, NULL}
+};
+
+static void mdesc_get_node_ops(const char *node_name,
+ mdesc_node_info_get_f *get_info_f,
+ mdesc_node_info_rel_f *rel_info_f,
+ mdesc_node_match_f *match_f)
+{
+ int i;
+
+ if (get_info_f)
+ *get_info_f = NULL;
+
+ if (rel_info_f)
+ *rel_info_f = NULL;
+
+ if (match_f)
+ *match_f = NULL;
+
+ if (!node_name)
+ return;
+
+ for (i = 0; md_node_ops_table[i].name != NULL; i++) {
+ if (strcmp(md_node_ops_table[i].name, node_name) == 0) {
+ if (get_info_f)
+ *get_info_f = md_node_ops_table[i].get_info;
+
+ if (rel_info_f)
+ *rel_info_f = md_node_ops_table[i].rel_info;
+
+ if (match_f)
+ *match_f = md_node_ops_table[i].node_match;
+
+ break;
+ }
+ }
+}
+
static void mdesc_handle_init(struct mdesc_handle *hp,
unsigned int handle_size,
void *base)
@@ -137,12 +205,10 @@ static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size)
handle_size = (sizeof(struct mdesc_handle) -
sizeof(struct mdesc_hdr) +
mdesc_size);
+ base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_REPEAT);
+ if (!base)
+ return NULL;
- /*
- * Allocation has to succeed because mdesc update would be missed
- * and such events are not retransmitted.
- */
- base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL);
addr = (unsigned long)base;
addr = (addr + 15UL) & ~15UL;
hp = (struct mdesc_handle *) addr;
@@ -218,14 +284,31 @@ static struct mdesc_notifier_client *client_list;
void mdesc_register_notifier(struct mdesc_notifier_client *client)
{
+ bool supported = false;
u64 node;
+ int i;
mutex_lock(&mdesc_mutex);
+
+ /* check to see if the node is supported for registration */
+ for (i = 0; md_node_ops_table[i].name != NULL; i++) {
+ if (strcmp(md_node_ops_table[i].name, client->node_name) == 0) {
+ supported = true;
+ break;
+ }
+ }
+
+ if (!supported) {
+ pr_err("MD: %s node not supported\n", client->node_name);
+ mutex_unlock(&mdesc_mutex);
+ return;
+ }
+
client->next = client_list;
client_list = client;
mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name)
- client->add(cur_mdesc, node);
+ client->add(cur_mdesc, node, client->node_name);
mutex_unlock(&mdesc_mutex);
}
@@ -249,59 +332,145 @@ static const u64 *parent_cfg_handle(struct mdesc_handle *hp, u64 node)
return id;
}
+static int get_vdev_port_node_info(struct mdesc_handle *md, u64 node,
+ union md_node_info *node_info)
+{
+ const u64 *parent_cfg_hdlp;
+ const char *name;
+ const u64 *idp;
+
+ /*
+ * Virtual device nodes are distinguished by:
+ * 1. "id" property
+ * 2. "name" property
+ * 3. parent node "cfg-handle" property
+ */
+ idp = mdesc_get_property(md, node, "id", NULL);
+ name = mdesc_get_property(md, node, "name", NULL);
+ parent_cfg_hdlp = parent_cfg_handle(md, node);
+
+ if (!idp || !name || !parent_cfg_hdlp)
+ return -1;
+
+ node_info->vdev_port.id = *idp;
+ node_info->vdev_port.name = kstrdup_const(name, GFP_KERNEL);
+ node_info->vdev_port.parent_cfg_hdl = *parent_cfg_hdlp;
+
+ return 0;
+}
+
+static void rel_vdev_port_node_info(union md_node_info *node_info)
+{
+ if (node_info && node_info->vdev_port.name) {
+ kfree_const(node_info->vdev_port.name);
+ node_info->vdev_port.name = NULL;
+ }
+}
+
+static bool vdev_port_node_match(union md_node_info *a_node_info,
+ union md_node_info *b_node_info)
+{
+ if (a_node_info->vdev_port.id != b_node_info->vdev_port.id)
+ return false;
+
+ if (a_node_info->vdev_port.parent_cfg_hdl !=
+ b_node_info->vdev_port.parent_cfg_hdl)
+ return false;
+
+ if (strncmp(a_node_info->vdev_port.name,
+ b_node_info->vdev_port.name, MDESC_MAX_STR_LEN) != 0)
+ return false;
+
+ return true;
+}
+
+static int get_ds_port_node_info(struct mdesc_handle *md, u64 node,
+ union md_node_info *node_info)
+{
+ const u64 *idp;
+
+ /* DS port nodes use the "id" property to distinguish them */
+ idp = mdesc_get_property(md, node, "id", NULL);
+ if (!idp)
+ return -1;
+
+ node_info->ds_port.id = *idp;
+
+ return 0;
+}
+
+static void rel_ds_port_node_info(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)
+{
+ if (a_node_info->ds_port.id != b_node_info->ds_port.id)
+ 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,
struct mdesc_handle *b,
- void (*func)(struct mdesc_handle *, u64))
+ void (*func)(struct mdesc_handle *, u64,
+ const char *node_name))
{
- u64 node;
+ mdesc_node_info_get_f get_info_func;
+ mdesc_node_info_rel_f rel_info_func;
+ mdesc_node_match_f node_match_func;
+ union md_node_info a_node_info;
+ union md_node_info b_node_info;
+ bool found;
+ u64 a_node;
+ u64 b_node;
+ int rv;
- mdesc_for_each_node_by_name(a, node, name) {
- int found = 0, is_vdc_port = 0;
- const char *name_prop;
- const u64 *id;
- u64 fnode;
-
- name_prop = mdesc_get_property(a, node, "name", NULL);
- if (name_prop && !strcmp(name_prop, "vdc-port")) {
- is_vdc_port = 1;
- id = parent_cfg_handle(a, node);
- } else
- id = mdesc_get_property(a, node, "id", NULL);
-
- if (!id) {
- printk(KERN_ERR "MD: Cannot find ID for %s node.\n",
- (name_prop ? name_prop : name));
+ /*
+ * Find the get_info, rel_info and node_match ops for the given
+ * node name
+ */
+ mdesc_get_node_ops(name, &get_info_func, &rel_info_func,
+ &node_match_func);
+
+ /* If we didn't find a match, the node type is not supported */
+ if (!get_info_func || !rel_info_func || !node_match_func) {
+ pr_err("MD: %s node type is not supported\n", name);
+ return;
+ }
+
+ mdesc_for_each_node_by_name(a, a_node, name) {
+ found = false;
+
+ rv = get_info_func(a, a_node, &a_node_info);
+ if (rv != 0) {
+ pr_err("MD: Cannot find 1 or more required match properties for %s node.\n",
+ name);
continue;
}
- mdesc_for_each_node_by_name(b, fnode, name) {
- const u64 *fid;
-
- if (is_vdc_port) {
- name_prop = mdesc_get_property(b, fnode,
- "name", NULL);
- if (!name_prop ||
- strcmp(name_prop, "vdc-port"))
- continue;
- fid = parent_cfg_handle(b, fnode);
- if (!fid) {
- printk(KERN_ERR "MD: Cannot find ID "
- "for vdc-port node.\n");
- continue;
- }
- } else
- fid = mdesc_get_property(b, fnode,
- "id", NULL);
-
- if (*id == *fid) {
- found = 1;
+ /* Check each node in B for node matching a_node */
+ mdesc_for_each_node_by_name(b, b_node, name) {
+ rv = get_info_func(b, b_node, &b_node_info);
+ if (rv != 0)
+ continue;
+
+ if (node_match_func(&a_node_info, &b_node_info)) {
+ found = true;
+ rel_info_func(&b_node_info);
break;
}
+
+ rel_info_func(&b_node_info);
}
+
+ rel_info_func(&a_node_info);
+
if (!found)
- func(a, node);
+ func(a, a_node, name);
}
}
@@ -367,6 +536,76 @@ out:
mutex_unlock(&mdesc_mutex);
}
+u64 mdesc_get_node(struct mdesc_handle *hp, const char *node_name,
+ union md_node_info *node_info)
+{
+ mdesc_node_info_get_f get_info_func;
+ mdesc_node_info_rel_f rel_info_func;
+ mdesc_node_match_f node_match_func;
+ union md_node_info hp_node_info;
+ u64 hp_node;
+ int rv;
+
+ if (hp == NULL || node_name == NULL || node_info == NULL)
+ return MDESC_NODE_NULL;
+
+ /* Find the ops for the given node name */
+ mdesc_get_node_ops(node_name, &get_info_func, &rel_info_func,
+ &node_match_func);
+
+ /* If we didn't find ops for the given node name, it is not supported */
+ if (!get_info_func || !rel_info_func || !node_match_func) {
+ pr_err("MD: %s node is not supported\n", node_name);
+ return -EINVAL;
+ }
+
+ mdesc_for_each_node_by_name(hp, hp_node, node_name) {
+ rv = get_info_func(hp, hp_node, &hp_node_info);
+ if (rv != 0)
+ continue;
+
+ if (node_match_func(node_info, &hp_node_info))
+ break;
+
+ rel_info_func(&hp_node_info);
+ }
+
+ rel_info_func(&hp_node_info);
+
+ return hp_node;
+}
+EXPORT_SYMBOL(mdesc_get_node);
+
+int mdesc_get_node_info(struct mdesc_handle *hp, u64 node,
+ const char *node_name, union md_node_info *node_info)
+{
+ mdesc_node_info_get_f get_info_func;
+ int rv;
+
+ if (hp == NULL || node == MDESC_NODE_NULL ||
+ node_name == NULL || node_info == NULL)
+ return -EINVAL;
+
+ /* Find the get_info op for the given node name */
+ mdesc_get_node_ops(node_name, &get_info_func, NULL, NULL);
+
+ /* If we didn't find a get_info_func, the node name is not supported */
+ if (get_info_func == NULL) {
+ pr_err("MD: %s node is not supported\n", node_name);
+ return -EINVAL;
+ }
+
+ rv = get_info_func(hp, node, node_info);
+ if (rv != 0) {
+ pr_err("MD: Cannot find 1 or more required match properties for %s node.\n",
+ node_name);
+ return -1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mdesc_get_node_info);
+
static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)
{
return (struct mdesc_elem *) (mdesc + 1);
diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c
index 075d38980dee..6715fc36e0e9 100644
--- a/arch/sparc/kernel/vio.c
+++ b/arch/sparc/kernel/vio.c
@@ -70,15 +70,26 @@ static int vio_device_probe(struct device *dev)
struct vio_dev *vdev = to_vio_dev(dev);
struct vio_driver *drv = to_vio_driver(dev->driver);
const struct vio_device_id *id;
- int error = -ENODEV;
- if (drv->probe) {
- id = vio_match_device(drv->id_table, vdev);
- if (id)
- error = drv->probe(vdev, id);
+ if (!drv->probe)
+ return -ENODEV;
+
+ id = vio_match_device(drv->id_table, vdev);
+ if (!id)
+ return -ENODEV;
+
+ /* alloc irqs (unless the driver specified not to) */
+ if (!drv->no_irq) {
+ if (vdev->tx_irq == 0 && vdev->tx_ino != ~0UL)
+ vdev->tx_irq = sun4v_build_virq(vdev->cdev_handle,
+ vdev->tx_ino);
+
+ if (vdev->rx_irq == 0 && vdev->rx_ino != ~0UL)
+ vdev->rx_irq = sun4v_build_virq(vdev->cdev_handle,
+ vdev->rx_ino);
}
- return error;
+ return drv->probe(vdev, id);
}
static int vio_device_remove(struct device *dev)
@@ -86,8 +97,15 @@ static int vio_device_remove(struct device *dev)
struct vio_dev *vdev = to_vio_dev(dev);
struct vio_driver *drv = to_vio_driver(dev->driver);
- if (drv->remove)
+ if (drv->remove) {
+ /*
+ * Ideally, we would remove/deallocate tx/rx virqs
+ * here - however, there are currently no support
+ * routines to do so at the moment. TBD
+ */
+
return drv->remove(vdev);
+ }
return 1;
}
@@ -181,11 +199,58 @@ static struct device_node *cdev_node;
static struct vio_dev *root_vdev;
static u64 cdev_cfg_handle;
+static const u64 *vio_cfg_handle(struct mdesc_handle *hp, u64 node)
+{
+ const u64 *cfg_handle = NULL;
+ u64 a;
+
+ mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
+ u64 target;
+
+ target = mdesc_arc_target(hp, a);
+ cfg_handle = mdesc_get_property(hp, target,
+ "cfg-handle", NULL);
+ if (cfg_handle)
+ break;
+ }
+
+ return cfg_handle;
+}
+
+/**
+ * vio_vdev_node() - Find VDEV node in MD
+ * @hp: Handle to the MD
+ * @vdev: Pointer to VDEV
+ *
+ * Find the node in the current MD which matches the given vio_dev. This
+ * must be done dynamically since the node value can change if the MD
+ * is updated.
+ *
+ * NOTE: the MD must be locked, using mdesc_grab(), when calling this routine
+ *
+ * Return: The VDEV node in MDESC
+ */
+u64 vio_vdev_node(struct mdesc_handle *hp, struct vio_dev *vdev)
+{
+ u64 node;
+
+ if (vdev == NULL)
+ return MDESC_NODE_NULL;
+
+ node = mdesc_get_node(hp, (const char *)vdev->node_name,
+ &vdev->md_node_info);
+
+ return node;
+}
+
static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
struct vio_dev *vdev)
{
u64 a;
+ vdev->tx_ino = ~0UL;
+ vdev->rx_ino = ~0UL;
+ vdev->channel_id = ~0UL;
mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
const u64 *chan_id;
const u64 *irq;
@@ -195,18 +260,18 @@ static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
irq = mdesc_get_property(hp, target, "tx-ino", NULL);
if (irq)
- vdev->tx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);
+ vdev->tx_ino = *irq;
irq = mdesc_get_property(hp, target, "rx-ino", NULL);
- if (irq) {
- vdev->rx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);
+ if (irq)
vdev->rx_ino = *irq;
- }
chan_id = mdesc_get_property(hp, target, "id", NULL);
if (chan_id)
vdev->channel_id = *chan_id;
}
+
+ vdev->cdev_handle = cdev_cfg_handle;
}
int vio_set_intr(unsigned long dev_ino, int state)
@@ -219,14 +284,14 @@ int vio_set_intr(unsigned long dev_ino, int state)
EXPORT_SYMBOL(vio_set_intr);
static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
+ const char *node_name,
struct device *parent)
{
- const char *type, *compat, *bus_id_name;
+ const char *type, *compat;
struct device_node *dp;
struct vio_dev *vdev;
int err, tlen, clen;
const u64 *id, *cfg_handle;
- u64 a;
type = mdesc_get_property(hp, mp, "device-type", &tlen);
if (!type) {
@@ -236,7 +301,7 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
tlen = strlen(type) + 1;
}
}
- if (tlen > VIO_MAX_TYPE_LEN) {
+ if (tlen > VIO_MAX_TYPE_LEN || strlen(type) >= VIO_MAX_TYPE_LEN) {
printk(KERN_ERR "VIO: Type string [%s] is too long.\n",
type);
return NULL;
@@ -244,31 +309,7 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
id = mdesc_get_property(hp, mp, "id", NULL);
- cfg_handle = NULL;
- mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
- u64 target;
-
- target = mdesc_arc_target(hp, a);
- cfg_handle = mdesc_get_property(hp, target,
- "cfg-handle", NULL);
- if (cfg_handle)
- break;
- }
-
- bus_id_name = type;
- if (!strcmp(type, "domain-services-port"))
- bus_id_name = "ds";
-
- /*
- * 20 char is the old driver-core name size limit, which is no more.
- * This check can probably be removed after review and possible
- * adaption of the vio users name length handling.
- */
- if (strlen(bus_id_name) >= 20 - 4) {
- printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n",
- bus_id_name);
- return NULL;
- }
+ cfg_handle = vio_cfg_handle(hp, mp);
compat = mdesc_get_property(hp, mp, "device-type", &clen);
if (!compat) {
@@ -293,25 +334,23 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
memset(vdev->compat, 0, sizeof(vdev->compat));
vdev->compat_len = clen;
- vdev->channel_id = ~0UL;
- vdev->tx_irq = ~0;
- vdev->rx_irq = ~0;
+ vdev->port_id = ~0UL;
+ vdev->tx_irq = 0;
+ vdev->rx_irq = 0;
vio_fill_channel_info(hp, mp, vdev);
if (!id) {
- dev_set_name(&vdev->dev, "%s", bus_id_name);
+ dev_set_name(&vdev->dev, "%s", type);
vdev->dev_no = ~(u64)0;
- vdev->id = ~(u64)0;
} else if (!cfg_handle) {
- dev_set_name(&vdev->dev, "%s-%llu", bus_id_name, *id);
+ dev_set_name(&vdev->dev, "%s-%llu", type, *id);
vdev->dev_no = *id;
- vdev->id = ~(u64)0;
} else {
- dev_set_name(&vdev->dev, "%s-%llu-%llu", bus_id_name,
+ dev_set_name(&vdev->dev, "%s-%llu-%llu", type,
*cfg_handle, *id);
vdev->dev_no = *cfg_handle;
- vdev->id = *id;
+ vdev->port_id = *id;
}
vdev->dev.parent = parent;
@@ -333,7 +372,26 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
}
vdev->dp = dp;
- printk(KERN_INFO "VIO: Adding device %s\n", dev_name(&vdev->dev));
+ /*
+ * node_name is NULL for the parent/channel-devices node and
+ * the parent doesn't require the MD node info.
+ */
+ if (node_name != NULL) {
+ (void) snprintf(vdev->node_name, VIO_MAX_NAME_LEN, "%s",
+ node_name);
+
+ err = mdesc_get_node_info(hp, mp, node_name,
+ &vdev->md_node_info);
+ if (err) {
+ pr_err("VIO: Could not get MD node info %s, err=%d\n",
+ dev_name(&vdev->dev), err);
+ kfree(vdev);
+ return NULL;
+ }
+ }
+
+ pr_info("VIO: Adding device %s (tx_ino = %llx, rx_ino = %llx)\n",
+ dev_name(&vdev->dev), vdev->tx_ino, vdev->rx_ino);
err = device_register(&vdev->dev);
if (err) {
@@ -349,73 +407,42 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
return vdev;
}
-static void vio_add(struct mdesc_handle *hp, u64 node)
+static void vio_add(struct mdesc_handle *hp, u64 node,
+ const char *node_name)
{
- (void) vio_create_one(hp, node, &root_vdev->dev);
+ (void) vio_create_one(hp, node, node_name, &root_vdev->dev);
}
-struct vio_md_node_query {
- const char *type;
- u64 dev_no;
- u64 id;
+struct vio_remove_node_data {
+ struct mdesc_handle *hp;
+ u64 node;
};
static int vio_md_node_match(struct device *dev, void *arg)
{
- struct vio_md_node_query *query = (struct vio_md_node_query *) arg;
struct vio_dev *vdev = to_vio_dev(dev);
+ struct vio_remove_node_data *node_data;
+ u64 node;
- if (vdev->dev_no != query->dev_no)
- return 0;
- if (vdev->id != query->id)
- return 0;
- if (strcmp(vdev->type, query->type))
- return 0;
+ node_data = (struct vio_remove_node_data *)arg;
- return 1;
+ node = vio_vdev_node(node_data->hp, vdev);
+
+ if (node == node_data->node)
+ return 1;
+ else
+ return 0;
}
-static void vio_remove(struct mdesc_handle *hp, u64 node)
+static void vio_remove(struct mdesc_handle *hp, u64 node, const char *node_name)
{
- const char *type;
- const u64 *id, *cfg_handle;
- u64 a;
- struct vio_md_node_query query;
+ struct vio_remove_node_data node_data;
struct device *dev;
- type = mdesc_get_property(hp, node, "device-type", NULL);
- if (!type) {
- type = mdesc_get_property(hp, node, "name", NULL);
- if (!type)
- type = mdesc_node_name(hp, node);
- }
-
- query.type = type;
-
- id = mdesc_get_property(hp, node, "id", NULL);
- cfg_handle = NULL;
- mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
- u64 target;
-
- target = mdesc_arc_target(hp, a);
- cfg_handle = mdesc_get_property(hp, target,
- "cfg-handle", NULL);
- if (cfg_handle)
- break;
- }
-
- if (!id) {
- query.dev_no = ~(u64)0;
- query.id = ~(u64)0;
- } else if (!cfg_handle) {
- query.dev_no = *id;
- query.id = ~(u64)0;
- } else {
- query.dev_no = *cfg_handle;
- query.id = *id;
- }
+ node_data.hp = hp;
+ node_data.node = node;
- dev = device_find_child(&root_vdev->dev, &query,
+ dev = device_find_child(&root_vdev->dev, (void *)&node_data,
vio_md_node_match);
if (dev) {
printk(KERN_INFO "VIO: Removing device %s\n", dev_name(dev));
@@ -423,15 +450,7 @@ static void vio_remove(struct mdesc_handle *hp, u64 node)
device_unregister(dev);
put_device(dev);
} else {
- if (!id)
- printk(KERN_ERR "VIO: Removed unknown %s node.\n",
- type);
- else if (!cfg_handle)
- printk(KERN_ERR "VIO: Removed unknown %s node %llu.\n",
- type, *id);
- else
- printk(KERN_ERR "VIO: Removed unknown %s node %llu-%llu.\n",
- type, *cfg_handle, *id);
+ pr_err("VIO: %s node not found in MDESC\n", node_name);
}
}
@@ -446,7 +465,8 @@ static struct mdesc_notifier_client vio_device_notifier = {
* under "openboot" that we should not mess with as aparently that is
* reserved exclusively for OBP use.
*/
-static void vio_add_ds(struct mdesc_handle *hp, u64 node)
+static void vio_add_ds(struct mdesc_handle *hp, u64 node,
+ const char *node_name)
{
int found;
u64 a;
@@ -463,7 +483,7 @@ static void vio_add_ds(struct mdesc_handle *hp, u64 node)
}
if (found)
- (void) vio_create_one(hp, node, &root_vdev->dev);
+ (void) vio_create_one(hp, node, node_name, &root_vdev->dev);
}
static struct mdesc_notifier_client vio_ds_notifier = {
@@ -530,7 +550,7 @@ static int __init vio_init(void)
cdev_cfg_handle = *cfg_handle;
- root_vdev = vio_create_one(hp, root, NULL);
+ root_vdev = vio_create_one(hp, root, NULL, NULL);
err = -ENODEV;
if (!root_vdev) {
printk(KERN_ERR "VIO: Could not create root device.\n");
diff --git a/arch/sparc/kernel/viohs.c b/arch/sparc/kernel/viohs.c
index b30b30ab3ddd..d4f13c037a40 100644
--- a/arch/sparc/kernel/viohs.c
+++ b/arch/sparc/kernel/viohs.c
@@ -223,6 +223,9 @@ static int send_rdx(struct vio_driver_state *vio)
static int send_attr(struct vio_driver_state *vio)
{
+ if (!vio->ops)
+ return -EINVAL;
+
return vio->ops->send_attr(vio);
}
@@ -283,6 +286,7 @@ static int process_ver_info(struct vio_driver_state *vio,
ver.minor = vap->minor;
pkt->minor = ver.minor;
pkt->tag.stype = VIO_SUBTYPE_ACK;
+ pkt->dev_class = vio->dev_class;
viodbg(HS, "SEND VERSION ACK maj[%u] min[%u]\n",
pkt->major, pkt->minor);
err = send_ctrl(vio, &pkt->tag, sizeof(*pkt));
@@ -374,6 +378,9 @@ static int process_attr(struct vio_driver_state *vio, void *pkt)
if (!(vio->hs_state & VIO_HS_GOTVERS))
return handshake_failure(vio);
+ if (!vio->ops)
+ return 0;
+
err = vio->ops->handle_attr(vio, pkt);
if (err < 0) {
return handshake_failure(vio);
@@ -388,6 +395,7 @@ static int process_attr(struct vio_driver_state *vio, void *pkt)
vio->hs_state |= VIO_HS_SENT_DREG;
}
}
+
return 0;
}
@@ -647,10 +655,13 @@ int vio_control_pkt_engine(struct vio_driver_state *vio, void *pkt)
err = process_unknown(vio, pkt);
break;
}
+
if (!err &&
vio->hs_state != prev_state &&
- (vio->hs_state & VIO_HS_COMPLETE))
- vio->ops->handshake_complete(vio);
+ (vio->hs_state & VIO_HS_COMPLETE)) {
+ if (vio->ops)
+ vio->ops->handshake_complete(vio);
+ }
return err;
}
@@ -765,7 +776,11 @@ void vio_port_up(struct vio_driver_state *vio)
}
if (!err) {
- err = ldc_connect(vio->lp);
+ if (ldc_mode(vio->lp) == LDC_MODE_RAW)
+ ldc_set_state(vio->lp, LDC_STATE_CONNECTED);
+ else
+ err = ldc_connect(vio->lp);
+
if (err)
printk(KERN_WARNING "%s: Port %lu connect failed, "
"err=%d\n",
@@ -805,8 +820,7 @@ int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev,
return -EINVAL;
}
- if (!ops->send_attr ||
- !ops->handle_attr ||
+ if (!ops || !ops->send_attr || !ops->handle_attr ||
!ops->handshake_complete)
return -EINVAL;