📄 vio.c
字号:
/* init generic 'struct device' fields: */ viodev->dev.parent = &vio_bus_device->dev; viodev->dev.bus = &vio_bus_type; viodev->dev.release = vio_dev_release; /* register with generic device framework */ if (device_register(&viodev->dev)) { printk(KERN_ERR "%s: failed to register device %s\n", __FUNCTION__, viodev->dev.bus_id); return NULL; } device_create_file(&viodev->dev, &dev_attr_name); return viodev;}#ifdef CONFIG_PPC_PSERIES/** * vio_register_device_node: - Register a new vio device. * @of_node: The OF node for this device. * * Creates and initializes a vio_dev structure from the data in * of_node (dev.platform_data) and adds it to the list of virtual devices. * Returns a pointer to the created vio_dev or NULL if node has * NULL device_type or compatible fields. */struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node){ struct vio_dev *viodev; unsigned int *unit_address; unsigned int *irq_p; DBGENTER(); /* we need the 'device_type' property, in order to match with drivers */ if ((NULL == of_node->type)) { printk(KERN_WARNING "%s: node %s missing 'device_type'\n", __FUNCTION__, of_node->name ? of_node->name : "<unknown>"); return NULL; } unit_address = (unsigned int *)get_property(of_node, "reg", NULL); if (!unit_address) { printk(KERN_WARNING "%s: node %s missing 'reg'\n", __FUNCTION__, of_node->name ? of_node->name : "<unknown>"); return NULL; } /* allocate a vio_dev for this node */ viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL); if (!viodev) { return NULL; } memset(viodev, 0, sizeof(struct vio_dev)); viodev->dev.platform_data = of_node_get(of_node); viodev->irq = NO_IRQ; irq_p = (unsigned int *)get_property(of_node, "interrupts", NULL); if (irq_p) { int virq = virt_irq_create_mapping(*irq_p); if (virq == NO_IRQ) { printk(KERN_ERR "Unable to allocate interrupt " "number for %s\n", of_node->full_name); } else viodev->irq = irq_offset_up(virq); } snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%x", *unit_address); /* register with generic device framework */ if (vio_register_device_common(viodev, of_node->name, of_node->type, *unit_address, vio_build_iommu_table(viodev)) == NULL) { /* XXX free TCE table */ kfree(viodev); return NULL; } device_create_file(&viodev->dev, &dev_attr_devspec); return viodev;}EXPORT_SYMBOL(vio_register_device_node);#endif#ifdef CONFIG_PPC_ISERIES/** * vio_register_device: - Register a new vio device. * @voidev: The device to register. */static struct vio_dev *__init vio_register_device_iseries(char *type, uint32_t unit_num){ struct vio_dev *viodev; DBGENTER(); /* allocate a vio_dev for this node */ viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL); if (!viodev) return NULL; memset(viodev, 0, sizeof(struct vio_dev)); snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%s%d", type, unit_num); return vio_register_device_common(viodev, viodev->dev.bus_id, type, unit_num, &vio_iommu_table);}#endifvoid __devinit vio_unregister_device(struct vio_dev *viodev){ DBGENTER();#ifdef CONFIG_PPC_PSERIES device_remove_file(&viodev->dev, &dev_attr_devspec);#endif device_remove_file(&viodev->dev, &dev_attr_name); device_unregister(&viodev->dev);}EXPORT_SYMBOL(vio_unregister_device);#ifdef CONFIG_PPC_PSERIES/** * vio_get_attribute: - get attribute for virtual device * @vdev: The vio device to get property. * @which: The property/attribute to be extracted. * @length: Pointer to length of returned data size (unused if NULL). * * Calls prom.c's get_property() to return the value of the * attribute specified by the preprocessor constant @which*/const void * vio_get_attribute(struct vio_dev *vdev, void* which, int* length){ return get_property(vdev->dev.platform_data, (char*)which, length);}EXPORT_SYMBOL(vio_get_attribute);/* vio_find_name() - internal because only vio.c knows how we formatted the * kobject name * XXX once vio_bus_type.devices is actually used as a kset in * drivers/base/bus.c, this function should be removed in favor of * "device_find(kobj_name, &vio_bus_type)" */static struct vio_dev *vio_find_name(const char *kobj_name){ struct kobject *found; found = kset_find_obj(&devices_subsys.kset, kobj_name); if (!found) return NULL; return to_vio_dev(container_of(found, struct device, kobj));}/** * vio_find_node - find an already-registered vio_dev * @vnode: device_node of the virtual device we're looking for */struct vio_dev *vio_find_node(struct device_node *vnode){ uint32_t *unit_address; char kobj_name[BUS_ID_SIZE]; /* construct the kobject name from the device node */ unit_address = (uint32_t *)get_property(vnode, "reg", NULL); if (!unit_address) return NULL; snprintf(kobj_name, BUS_ID_SIZE, "%x", *unit_address); return vio_find_name(kobj_name);}EXPORT_SYMBOL(vio_find_node);/** * vio_build_iommu_table: - gets the dma information from OF and builds the TCE tree. * @dev: the virtual device. * * Returns a pointer to the built tce tree, or NULL if it can't * find property.*/static struct iommu_table * vio_build_iommu_table(struct vio_dev *dev){ unsigned int *dma_window; struct iommu_table *newTceTable; unsigned long offset; unsigned long size; int dma_window_property_size; dma_window = (unsigned int *) get_property(dev->dev.platform_data, "ibm,my-dma-window", &dma_window_property_size); if(!dma_window) { return NULL; } newTceTable = (struct iommu_table *) kmalloc(sizeof(struct iommu_table), GFP_KERNEL); /* RPA docs say that #address-cells is always 1 for virtual devices, but some older boxes' OF returns 2. This should be removed by GA, unless there is legacy OFs that still have 2 for #address-cells */ size = ((dma_window[1+vio_num_address_cells] >> PAGE_SHIFT) << 3) >> PAGE_SHIFT; /* This is just an ugly kludge. Remove as soon as the OF for all machines actually follow the spec and encodes the offset field as phys-encode (that is, #address-cells wide)*/ if (dma_window_property_size == 12) { size = ((dma_window[1] >> PAGE_SHIFT) << 3) >> PAGE_SHIFT; } else if (dma_window_property_size == 20) { size = ((dma_window[4] >> PAGE_SHIFT) << 3) >> PAGE_SHIFT; } else { printk(KERN_WARNING "vio_build_iommu_table: Invalid size of ibm,my-dma-window=%i, using 0x80 for size\n", dma_window_property_size); size = 0x80; } /* There should be some code to extract the phys-encoded offset using prom_n_addr_cells(). However, according to a comment on earlier versions, it's always zero, so we don't bother */ offset = dma_window[1] >> PAGE_SHIFT; /* TCE table size - measured in units of pages of tce table */ newTceTable->it_size = size; /* offset for VIO should always be 0 */ newTceTable->it_offset = offset; newTceTable->it_busno = 0; newTceTable->it_index = (unsigned long)dma_window[0]; newTceTable->it_type = TCE_VB; newTceTable->it_entrysize = sizeof(union tce_entry); return iommu_init_table(newTceTable);}int vio_enable_interrupts(struct vio_dev *dev){ int rc = h_vio_signal(dev->unit_address, VIO_IRQ_ENABLE); if (rc != H_Success) { printk(KERN_ERR "vio: Error 0x%x enabling interrupts\n", rc); } return rc;}EXPORT_SYMBOL(vio_enable_interrupts);int vio_disable_interrupts(struct vio_dev *dev){ int rc = h_vio_signal(dev->unit_address, VIO_IRQ_DISABLE); if (rc != H_Success) { printk(KERN_ERR "vio: Error 0x%x disabling interrupts\n", rc); } return rc;}EXPORT_SYMBOL(vio_disable_interrupts);#endifdma_addr_t vio_map_single(struct vio_dev *dev, void *vaddr, size_t size, enum dma_data_direction direction){ return iommu_map_single(dev->iommu_table, vaddr, size, direction);}EXPORT_SYMBOL(vio_map_single);void vio_unmap_single(struct vio_dev *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction){ iommu_unmap_single(dev->iommu_table, dma_handle, size, direction);}EXPORT_SYMBOL(vio_unmap_single);int vio_map_sg(struct vio_dev *vdev, struct scatterlist *sglist, int nelems, enum dma_data_direction direction){ return iommu_map_sg(&vdev->dev, vdev->iommu_table, sglist, nelems, direction);}EXPORT_SYMBOL(vio_map_sg);void vio_unmap_sg(struct vio_dev *vdev, struct scatterlist *sglist, int nelems, enum dma_data_direction direction){ iommu_unmap_sg(vdev->iommu_table, sglist, nelems, direction);}EXPORT_SYMBOL(vio_unmap_sg);void *vio_alloc_consistent(struct vio_dev *dev, size_t size, dma_addr_t *dma_handle){ return iommu_alloc_consistent(dev->iommu_table, size, dma_handle);}EXPORT_SYMBOL(vio_alloc_consistent);void vio_free_consistent(struct vio_dev *dev, size_t size, void *vaddr, dma_addr_t dma_handle){ iommu_free_consistent(dev->iommu_table, size, vaddr, dma_handle);}EXPORT_SYMBOL(vio_free_consistent);static int vio_bus_match(struct device *dev, struct device_driver *drv){ const struct vio_dev *vio_dev = to_vio_dev(dev); struct vio_driver *vio_drv = to_vio_driver(drv); const struct vio_device_id *ids = vio_drv->id_table; const struct vio_device_id *found_id; DBGENTER(); if (!ids) return 0; found_id = vio_match_device(ids, vio_dev); if (found_id) return 1; return 0;}struct bus_type vio_bus_type = { .name = "vio", .match = vio_bus_match,};EXPORT_SYMBOL(vio_bus_type);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -