⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vio.c

📁 linux-2.4.29操作系统的源码
💻 C
字号:
/* * IBM PowerPC Virtual I/O Infrastructure Support. * * Dave Engebretsen engebret@us.ibm.com *    Copyright (c) 2003 Dave Engebretsen * *      This program is free software; you can redistribute it and/or *      modify it under the terms of the GNU General Public License *      as published by the Free Software Foundation; either version *      2 of the License, or (at your option) any later version. */#include <linux/init.h>#include <linux/console.h>#include <linux/pci.h>#include <linux/version.h>#include <linux/module.h>#include <asm/rtas.h>#include <asm/pci_dma.h>#include <asm/dma.h>#include <asm/ppcdebug.h>#include <asm/vio.h>#include <asm/hvcall.h>extern struct TceTable *build_tce_table(struct TceTable *tbl);extern dma_addr_t get_tces(struct TceTable *, unsigned order,			   void *page, unsigned numPages, int direction);extern void tce_free(struct TceTable *tbl, dma_addr_t dma_addr,		     unsigned order, unsigned num_pages);static struct vio_bus vio_bus;static LIST_HEAD(registered_vio_drivers);int vio_num_address_cells;EXPORT_SYMBOL(vio_num_address_cells);/** * vio_register_driver: - Register a new vio driver * @drv:	The vio_driver structure to be registered. * * Adds the driver structure to the list of registered drivers * Returns the number of vio devices which were claimed by the driver * during registration.  The driver remains registered even if the * return value is zero. */int vio_register_driver(struct vio_driver *drv){	int count = 0;	struct vio_dev *dev;	const struct vio_device_id* id;	/*	 * Walk the vio_bus, find devices for this driver, and	 * call back into the driver probe interface.	 */	list_for_each_entry(dev, &vio_bus.devices, devices_list) {		id = vio_match_device(drv->id_table, dev);		if(drv && id) {			if (0 == drv->probe(dev, id)) {				dev->driver = drv;				count++;			}		}	}	list_add_tail(&drv->node, &registered_vio_drivers);	return count;}EXPORT_SYMBOL(vio_register_driver);/** * vio_unregister_driver - Remove registration of vio driver. * @driver:	The vio_driver struct to be removed form registration * * Searches for devices that are assigned to the driver and calls * driver->remove() for each one.  Removes the driver from the list * of registered drivers.  Returns the number of devices that were * assigned to that driver. */int vio_unregister_driver(struct vio_driver *driver){	struct vio_dev *dev;	int devices_found = 0;	list_for_each_entry(dev, &vio_bus.devices, devices_list) {		if (dev->driver == driver) {			driver->remove(dev);			dev->driver = NULL;			devices_found++;		}	}	list_del(&driver->node);	return devices_found;}EXPORT_SYMBOL(vio_unregister_driver);/** * vio_match_device: - Tell if a VIO device has a matching VIO device id structure. * @ids: 	array of VIO device id structures to search in * @dev: 	the VIO device structure to match against * * Used by a driver to check whether a VIO device present in the * system is in its list of supported devices. Returns the matching * vio_device_id structure or NULL if there is no match. */const struct vio_device_id *vio_match_device(const struct vio_device_id *ids, const struct vio_dev *dev){	while (ids->type) {		if ((strncmp(dev->archdata->type, ids->type, strlen(ids->type)) == 0) &&			device_is_compatible((struct device_node*)dev->archdata, ids->compat))			return ids;		ids++;	}	return NULL;}/** * vio_bus_init: - Initialize the virtual IO bus */int __initvio_bus_init(void){	struct device_node *node_vroot, *node_vdev;	printk("vio_bus_init: start\n");	INIT_LIST_HEAD(&vio_bus.devices);	/*	 * Create device node entries for each virtual device	 * identified in the device tree.	 * Functionally takes the place of pci_scan_bus	 */	node_vroot = find_devices("vdevice");	if (!node_vroot) {		printk(KERN_WARNING "vio_bus_init: no /vdevice node\n");		return 0;	}	vio_num_address_cells = prom_n_addr_cells(node_vroot->child);	for (node_vdev = node_vroot->child;			node_vdev != NULL;			node_vdev = node_vdev->sibling) {		printk(KERN_INFO "vio_bus_init: processing %p\n", node_vdev);		vio_register_device(node_vdev);	}	printk(KERN_INFO "vio_bus_init: done\n");	return 0;}__initcall(vio_bus_init);/** * vio_register_device: - Register a new vio device. * @archdata:	The OF node for this device. * * Creates and initializes a vio_dev structure from the data in * node_vdev (archdata) 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(struct device_node *node_vdev){	struct vio_dev *dev;	unsigned int *unit_address;	unsigned int *irq_p;	/* guarantee all vio_devs have 'device_type' field*/	if ((NULL == node_vdev->type)) {		printk(KERN_WARNING "vio_register_device: node %s missing 'device_type' "			, node_vdev->name?node_vdev->name:"UNKNOWN");		return NULL;	}	unit_address = (unsigned int *)get_property(node_vdev, "reg", NULL);	if(!unit_address) {		printk(KERN_WARNING "Can't find %s reg property\n", node_vdev->name?node_vdev->name:"UNKNOWN_DEVICE");		return NULL;	}	/* allocate a vio_dev for this node */	dev = kmalloc(sizeof(*dev), GFP_KERNEL);	memset(dev, 0, sizeof(*dev));	dev->archdata = (void*)node_vdev; /* to become of_get_node(node_vdev); */	dev->bus = &vio_bus;	dev->unit_address = *unit_address;	dev->tce_table = vio_build_tce_table(dev);	irq_p = (unsigned int *) get_property(node_vdev, "interrupts", 0);	if(irq_p) {		dev->irq = irq_offset_up(*irq_p);	} else {		dev->irq = (unsigned int) -1;	}	list_add_tail(&dev->devices_list, &vio_bus.devices);	return dev;}/** * vio_find_driver: - Find driver for vio_dev. * @dev:	Device to search for a driver. * * Walks the registered_vio_drivers list calling vio_match_device() * for every driver in the list. If there is a match, calls the * driver's probe(). * Returns a pointer to the matched driver or NULL if driver is not * found.*/struct vio_driver * vio_find_driver(struct vio_dev* dev){	struct vio_driver *driver;	list_for_each_entry(driver, &registered_vio_drivers, node) {		if(driver && vio_match_device(driver->id_table, dev)) {			if (0 < driver->probe(dev, NULL)) {				dev->driver = driver;				return driver;			}		}	}	return NULL;}/** * 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((struct device_node *)vdev->archdata, (char*)which, length);}EXPORT_SYMBOL(vio_get_attribute);/** * vio_build_tce_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.*/struct TceTable * vio_build_tce_table(struct vio_dev *dev){	unsigned int *dma_window;	struct TceTable *newTceTable;	unsigned long offset;	unsigned long size;	int dma_window_property_size;	dma_window = (unsigned int *) get_property((struct device_node *)dev->archdata, "ibm,my-dma-window", &dma_window_property_size);	if(!dma_window) {		return NULL;	}	newTceTable = (struct TceTable *) kmalloc(sizeof(struct TceTable), 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_tce_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->size = size;	/* offset for VIO should always be 0 */	newTceTable->startOffset = offset;	newTceTable->busNumber   = 0;	newTceTable->index       = (unsigned long)dma_window[0];	newTceTable->tceType     = TCE_VB;	return build_tce_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);dma_addr_t vio_map_single(struct vio_dev *dev, void *vaddr,			  size_t size, int direction ){	struct TceTable * tbl;	dma_addr_t dma_handle = NO_TCE;	unsigned long uaddr;	unsigned order, nPages;	if(direction == PCI_DMA_NONE) BUG();	uaddr = (unsigned long)vaddr;	nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK );	order = get_order( nPages & PAGE_MASK );	nPages >>= PAGE_SHIFT;	/* Client asked for way to much space.  This is checked later anyway */	/* It is easier to debug here for the drivers than in the tce tables.*/	if(order >= NUM_TCE_LEVELS) {		printk("VIO_DMA: vio_map_single size to large: 0x%lx \n",size);		return NO_TCE;	}	tbl = dev->tce_table;	if(tbl) {		dma_handle = get_tces(tbl, order, vaddr, nPages, direction);		dma_handle |= (uaddr & ~PAGE_MASK);	}	return dma_handle;}EXPORT_SYMBOL(vio_map_single);void vio_unmap_single(struct vio_dev *dev, dma_addr_t dma_handle,		      size_t size, int direction){	struct TceTable * tbl;	unsigned order, nPages;	if (direction == PCI_DMA_NONE) BUG();	nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK );	order = get_order( nPages & PAGE_MASK );	nPages >>= PAGE_SHIFT;	/* Client asked for way to much space.  This is checked later anyway */	/* It is easier to debug here for the drivers than in the tce tables.*/	if(order >= NUM_TCE_LEVELS) {		printk("VIO_DMA: vio_unmap_single 0x%lx size to large: 0x%lx \n",(unsigned long)dma_handle,(unsigned long)size);		return;	}	tbl = dev->tce_table;	if(tbl) tce_free(tbl, dma_handle, order, nPages);}EXPORT_SYMBOL(vio_unmap_single);int vio_map_sg(struct vio_dev *vdev, struct scatterlist *sglist, int nelems,	       int direction){	int i;	for (i = 0; i < nelems; i++) {		/* 2.4 scsi scatterlists use address field.		   Not sure about other subsystems. */		void *vaddr;		if (sglist->address)			vaddr = sglist->address;		else			vaddr = page_address(sglist->page) + sglist->offset;		sglist->dma_address = vio_map_single(vdev, vaddr,						     sglist->length,						     direction);		sglist->dma_length = sglist->length;		sglist++;	}	return nelems;}EXPORT_SYMBOL(vio_map_sg);void vio_unmap_sg(struct vio_dev *vdev, struct scatterlist *sglist, int nelems,		  int direction){	while (nelems--) {		vio_unmap_single(vdev, sglist->dma_address,				 sglist->dma_length, direction);		sglist++;	}}void *vio_alloc_consistent(struct vio_dev *dev, size_t size,			   dma_addr_t *dma_handle){	struct TceTable * tbl;	void *ret = NULL;	unsigned order, nPages;	dma_addr_t tce;	size = PAGE_ALIGN(size);	order = get_order(size);	nPages = 1 << order;	/* Client asked for way to much space.  This is checked later anyway */	/* It is easier to debug here for the drivers than in the tce tables.*/	if(order >= NUM_TCE_LEVELS) {		printk("VIO_DMA: vio_alloc_consistent size to large: 0x%lx \n",size);		return (void *)NO_TCE;	}	tbl = dev->tce_table;	if ( tbl ) {		/* Alloc enough pages (and possibly more) */		ret = (void *)__get_free_pages( GFP_ATOMIC, order );		if ( ret ) {			/* Page allocation succeeded */			memset(ret, 0, nPages << PAGE_SHIFT);			/* Set up tces to cover the allocated range */			tce = get_tces( tbl, order, ret, nPages, PCI_DMA_BIDIRECTIONAL );			if ( tce == NO_TCE ) {				PPCDBG(PPCDBG_TCE, "vio_alloc_consistent: get_tces failed\n" );				free_pages( (unsigned long)ret, order );				ret = NULL;			}			else				{					*dma_handle = tce;				}		}		else PPCDBG(PPCDBG_TCE, "vio_alloc_consistent: __get_free_pages failed for order = %d\n", order);	}	else PPCDBG(PPCDBG_TCE, "vio_alloc_consistent: get_tce_table failed for 0x%016lx\n", dev);	PPCDBG(PPCDBG_TCE, "\tvio_alloc_consistent: dma_handle = 0x%16.16lx\n", *dma_handle);	PPCDBG(PPCDBG_TCE, "\tvio_alloc_consistent: return     = 0x%16.16lx\n", ret);	return ret;}EXPORT_SYMBOL(vio_alloc_consistent);void vio_free_consistent(struct vio_dev *dev, size_t size,			 void *vaddr, dma_addr_t dma_handle){	struct TceTable * tbl;	unsigned order, nPages;	PPCDBG(PPCDBG_TCE, "vio_free_consistent:\n");	PPCDBG(PPCDBG_TCE, "\tdev = 0x%16.16lx, size = 0x%16.16lx, dma_handle = 0x%16.16lx, vaddr = 0x%16.16lx\n", dev, size, dma_handle, vaddr);	size = PAGE_ALIGN(size);	order = get_order(size);	nPages = 1 << order;	/* Client asked for way to much space.  This is checked later anyway */	/* It is easier to debug here for the drivers than in the tce tables.*/	if(order >= NUM_TCE_LEVELS) {		printk("PCI_DMA: pci_free_consistent size to large: 0x%lx \n",size);		return;	}	tbl = dev->tce_table;	if ( tbl ) {		tce_free(tbl, dma_handle, order, nPages);		free_pages( (unsigned long)vaddr, order );	}}EXPORT_SYMBOL(vio_free_consistent);EXPORT_SYMBOL(plpar_hcall_norets);EXPORT_SYMBOL(plpar_hcall_8arg_2ret);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -