📄 pci_dma.c
字号:
dn = dn->parent; if (dn) { mydn->tce_table = dn->tce_table; } }}/* * iSeries token = iSeries_device_Node* * pSeries token = pci_controller* * */void create_pci_bus_tce_table( unsigned long token ) { struct TceTable * newTceTable; PPCDBG(PPCDBG_TCE, "Entering create_pci_bus_tce_table.\n"); PPCDBG(PPCDBG_TCE, "\ttoken = 0x%lx\n", token); newTceTable = (struct TceTable *)kmalloc( sizeof(struct TceTable), GFP_KERNEL ); /*****************************************************************/ /* For the iSeries machines, the HvTce Table can be one of three */ /* flavors, */ /* - Single bus TCE table, */ /* - Tce Table Share between buses, */ /* - Tce Table per logical slot. */ /*****************************************************************/ if(naca->platform == PLATFORM_ISERIES_LPAR) { struct iSeries_Device_Node* DevNode = (struct iSeries_Device_Node*)token; getTceTableParmsiSeries(DevNode,newTceTable); /* Look for existing TCE table for this device. */ DevNode->DevTceTable = findHwTceTable(newTceTable ); if( DevNode->DevTceTable == NULL) { DevNode->DevTceTable = build_tce_table( newTceTable ); } else { /* We're using a shared table, free this new one. */ kfree(newTceTable); } printk("Pci Device 0x%p TceTable: %p\n",DevNode,DevNode->DevTceTable); return; } /* pSeries Leg */ else { struct device_node *dn; struct pci_controller *phb; dn = (struct device_node *)token; phb = dn->phb; if (naca->platform == PLATFORM_PSERIES) getTceTableParmsPSeries(phb, dn, newTceTable); else getTceTableParmsPSeriesLP(phb, dn, newTceTable); dn->tce_table = build_tce_table( newTceTable ); }}/***********************************************************************//* This function compares the known Tce tables to find a TceTable that *//* has already been built for hardware TCEs. *//* Search the complete(all devices) for a TCE table assigned. If the *//* startOffset, index, and size match, then the TCE for this device has*//* already been built and it should be shared with this device *//***********************************************************************/static struct TceTable* findHwTceTable(struct TceTable * newTceTable ){ struct list_head* Device_Node_Ptr = iSeries_Global_Device_List.next; /* Cache the compare values. */ u64 startOffset = newTceTable->startOffset; u64 index = newTceTable->index; u64 size = newTceTable->size; while(Device_Node_Ptr != &iSeries_Global_Device_List) { struct iSeries_Device_Node* CmprNode = (struct iSeries_Device_Node*)Device_Node_Ptr; if( CmprNode->DevTceTable != NULL && CmprNode->DevTceTable->tceType == TCE_PCI) { if( CmprNode->DevTceTable->startOffset == startOffset && CmprNode->DevTceTable->index == index && CmprNode->DevTceTable->size == size ) { printk("PCI TCE table matches 0x%p \n",CmprNode->DevTceTable); return CmprNode->DevTceTable; } } /* Get next Device Node in List */ Device_Node_Ptr = Device_Node_Ptr->next; } return NULL;}/***********************************************************************//* Call Hv with the architected data structure to get TCE table info. *//* info. Put the returned data into the Linux representation of the *//* TCE table data. *//* The Hardware Tce table comes in three flavors. */ /* 1. TCE table shared between Buses. *//* 2. TCE table per Bus. *//* 3. TCE Table per IOA. *//***********************************************************************/static void getTceTableParmsiSeries(struct iSeries_Device_Node* DevNode, struct TceTable* newTceTable ){ struct TceTableManagerCB* pciBusTceTableParms = (struct TceTableManagerCB*)kmalloc( sizeof(struct TceTableManagerCB), GFP_KERNEL ); if(pciBusTceTableParms == NULL) panic("PCI_DMA: TCE Table Allocation failed."); memset( (void*)pciBusTceTableParms,0,sizeof(struct TceTableManagerCB) ); pciBusTceTableParms->busNumber = ISERIES_BUS(DevNode); pciBusTceTableParms->logicalSlot = DevNode->LogicalSlot; pciBusTceTableParms->virtualBusFlag = 0; HvCallXm_getTceTableParms( REALADDR(pciBusTceTableParms) ); /* PciTceTableParms Bus:0x18 Slot:0x04 Start:0x000000 Offset:0x04c000 Size:0x0020 */ printk("PciTceTableParms Bus:0x%02lx Slot:0x%02x Start:0x%06lx Offset:0x%06lx Size:0x%04lx\n", pciBusTceTableParms->busNumber, pciBusTceTableParms->logicalSlot, pciBusTceTableParms->start, pciBusTceTableParms->startOffset, pciBusTceTableParms->size); if(pciBusTceTableParms->size == 0) { printk("PCI_DMA: Possible Structure mismatch, 0x%p\n",pciBusTceTableParms); panic( "PCI_DMA: pciBusTceTableParms->size is zero, halt here!"); } newTceTable->size = pciBusTceTableParms->size; newTceTable->busNumber = pciBusTceTableParms->busNumber; newTceTable->startOffset = pciBusTceTableParms->startOffset; newTceTable->index = pciBusTceTableParms->index; newTceTable->tceType = TCE_PCI; kfree(pciBusTceTableParms);}static void getTceTableParmsPSeries(struct pci_controller *phb, struct device_node *dn, struct TceTable *newTceTable ) { phandle node; unsigned long i; node = ((struct device_node *)(phb->arch_data))->node; PPCDBG(PPCDBG_TCEINIT, "getTceTableParms: start\n"); PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table = 0x%lx\n", of_tce_table); PPCDBG(PPCDBG_TCEINIT, "\tphb = 0x%lx\n", phb); PPCDBG(PPCDBG_TCEINIT, "\tdn = 0x%lx\n", dn); PPCDBG(PPCDBG_TCEINIT, "\tdn->name = %s\n", dn->name); PPCDBG(PPCDBG_TCEINIT, "\tdn->full_name= %s\n", dn->full_name); PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable = 0x%lx\n", newTceTable); PPCDBG(PPCDBG_TCEINIT, "\tdma_window_size = 0x%lx\n", phb->dma_window_size); i = 0; while(of_tce_table[i].node) { PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].node = 0x%lx\n", i, of_tce_table[i].node); PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].base = 0x%lx\n", i, of_tce_table[i].base); PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].size = 0x%lx\n", i, of_tce_table[i].size >> PAGE_SHIFT); PPCDBG(PPCDBG_TCEINIT, "\tphb->arch_data->node = 0x%lx\n", node); if(of_tce_table[i].node == node) { memset((void *)of_tce_table[i].base, 0, of_tce_table[i].size); newTceTable->busNumber = phb->bus->number; /* Units of tce entries. */ newTceTable->startOffset = phb->dma_window_base_cur; /* Adjust the current table offset to the next */ /* region. Measured in TCE entries. Force an */ /* alignment to the size alloted per IOA. This */ /* makes it easier to remove the 1st 16MB. */ phb->dma_window_base_cur += (phb->dma_window_size>>3); phb->dma_window_base_cur &= ~((phb->dma_window_size>>3)-1); /* Set the tce table size - measured in units */ /* of pages of tce table. */ newTceTable->size = ((phb->dma_window_base_cur - newTceTable->startOffset) << 3) >> PAGE_SHIFT; /* Test if we are going over 2GB of DMA space. */ if(phb->dma_window_base_cur > (1 << 19)) { panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); } newTceTable->base = of_tce_table[i].base; newTceTable->index = 0; PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->base = 0x%lx\n", newTceTable->base); PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->startOffset = 0x%lx" "(# tce entries)\n", newTceTable->startOffset); PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->size = 0x%lx" "(# pages of tce table)\n", newTceTable->size); } i++; }}/* * getTceTableParmsPSeriesLP * * Function: On pSeries LPAR systems, return TCE table info, given a pci bus. * * ToDo: properly interpret the ibm,dma-window property. The definition is: * logical-bus-number (1 word) * phys-address (#address-cells words) * size (#cell-size words) * * Currently we hard code these sizes (more or less). */static void getTceTableParmsPSeriesLP(struct pci_controller *phb, struct device_node *dn, struct TceTable *newTceTable ) { u32 *dma_window = (u32 *)get_property(dn, "ibm,dma-window", 0); if (!dma_window) { panic("PCI_DMA: getTceTableParmsPSeriesLP: device %s has no ibm,dma-window property!\n", dn->full_name); } newTceTable->busNumber = dn->busno; newTceTable->size = (((((unsigned long)dma_window[4] << 32) | (unsigned long)dma_window[5]) >> PAGE_SHIFT) << 3) >> PAGE_SHIFT; newTceTable->startOffset = ((((unsigned long)dma_window[2] << 32) | (unsigned long)dma_window[3]) >> 12); newTceTable->base = 0; newTceTable->index = dma_window[0]; PPCDBG(PPCDBG_TCEINIT, "getTceTableParmsPSeriesLP for bus 0x%lx:\n", dn->busno); PPCDBG(PPCDBG_TCEINIT, "\tDevice = %s\n", dn->full_name); PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->index = 0x%lx\n", newTceTable->index); PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->startOffset = 0x%lx\n", newTceTable->startOffset); PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable->size = 0x%lx\n", newTceTable->size);}/* Allocates a contiguous real buffer and creates TCEs over it. * Returns the virtual address of the buffer and sets dma_handle * to the dma address (tce) of the first page. */void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle){ struct TceTable * tbl; void *ret = NULL; unsigned order, nPages; dma_addr_t tce; PPCDBG(PPCDBG_TCE, "pci_alloc_consistent:\n"); PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx\n", hwdev); PPCDBG(PPCDBG_TCE, "\tsize = 0x%16.16lx\n", size); PPCDBG(PPCDBG_TCE, "\tdma_handle = 0x%16.16lx\n", dma_handle); 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_alloc_consistent size to large: 0x%lx \n",size); return (void *)NO_TCE; } tbl = get_tce_table(hwdev); 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, "pci_alloc_consistent: get_tces failed\n" ); free_pages( (unsigned long)ret, order ); ret = NULL; } else { *dma_handle = tce; } } else PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); } else PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: get_tce_table failed for 0x%016lx\n", hwdev); PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: dma_handle = 0x%16.16lx\n", *dma_handle); PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: return = 0x%16.16lx\n", ret); return ret;}void pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle){ struct TceTable * tbl; unsigned order, nPages; PPCDBG(PPCDBG_TCE, "pci_free_consistent:\n"); PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, dma_handle = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, 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 = get_tce_table(hwdev); if ( tbl ) { tce_free(tbl, dma_handle, order, nPages); free_pages( (unsigned long)vaddr, order ); }}/* Creates TCEs for a user provided buffer. The user buffer must be * contiguous real kernel storage (not vmalloc). The address of the buffer * passed here is the kernel (virtual) address of the buffer. The buffer * need not be page aligned, the dma_addr_t returned will point to the same * byte within the page as vaddr. */dma_addr_t pci_map_single(struct pci_dev *hwdev, void *vaddr, size_t size, int direction ){ struct TceTable * tbl; dma_addr_t dma_handle = NO_TCE; unsigned long uaddr; unsigned order, nPages; PPCDBG(PPCDBG_TCE, "pci_map_single:\n"); PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, direction, vaddr); 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("PCI_DMA: pci_map_single size to large: 0x%lx \n",size); return NO_TCE; } tbl = get_tce_table(hwdev); if ( tbl ) { dma_handle = get_tces( tbl, order, vaddr, nPages, direction ); dma_handle |= ( uaddr & ~PAGE_MASK ); } return dma_handle;}void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ){ struct TceTable * tbl; unsigned order, nPages; PPCDBG(PPCDBG_TCE, "pci_unmap_single:\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -