diff options
Diffstat (limited to 'arch/sparc/kernel/vio.c')
-rw-r--r-- | arch/sparc/kernel/vio.c | 210 |
1 files changed, 147 insertions, 63 deletions
diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c index f6bb857254fc..1c8763c9c52b 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; } @@ -105,6 +123,7 @@ static ssize_t devspec_show(struct device *dev, return sprintf(buf, "%s\n", str); } +static DEVICE_ATTR_RO(devspec); static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -112,6 +131,7 @@ static ssize_t type_show(struct device *dev, struct vio_dev *vdev = to_vio_dev(dev); return sprintf(buf, "%s\n", vdev->type); } +static DEVICE_ATTR_RO(type); static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -120,17 +140,19 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "vio:T%sS%s\n", vdev->type, vdev->compat); } +static DEVICE_ATTR_RO(modalias); -static struct device_attribute vio_dev_attrs[] = { - __ATTR_RO(devspec), - __ATTR_RO(type), - __ATTR_RO(modalias), - __ATTR_NULL -}; +static struct attribute *vio_dev_attrs[] = { + &dev_attr_devspec.attr, + &dev_attr_type.attr, + &dev_attr_modalias.attr, + NULL, + }; +ATTRIBUTE_GROUPS(vio_dev); static struct bus_type vio_bus_type = { .name = "vio", - .dev_attrs = vio_dev_attrs, + .dev_groups = vio_dev_groups, .uevent = vio_hotplug, .match = vio_bus_match, .probe = vio_device_probe, @@ -181,11 +203,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 +264,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 +288,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 +305,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 +313,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,22 +338,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; } 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; } 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->port_id = *id; } vdev->dev.parent = parent; @@ -330,7 +376,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) { @@ -346,32 +411,50 @@ 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_remove_node_data { + struct mdesc_handle *hp; + u64 node; +}; + static int vio_md_node_match(struct device *dev, void *arg) { struct vio_dev *vdev = to_vio_dev(dev); + struct vio_remove_node_data *node_data; + u64 node; - if (vdev->mp == (u64) arg) - return 1; + node_data = (struct vio_remove_node_data *)arg; - return 0; + 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) { + struct vio_remove_node_data node_data; struct device *dev; - dev = device_find_child(&root_vdev->dev, (void *) node, + node_data.hp = hp; + node_data.node = node; + + 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)); device_unregister(dev); put_device(dev); + } else { + pr_err("VIO: %s node not found in MDESC\n", node_name); } } @@ -386,7 +469,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; @@ -403,7 +487,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 = { @@ -470,7 +554,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"); |