X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fpowerpc%2Fplatforms%2Fpseries%2Fiommu.c;h=2643078433f0123f8997c7f98823934e7b1feadb;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=d67af2c657544ef5b0a6386a9c45a31de2fb4681;hpb=41689045f6a3cbe0550e1d34e9cc20d2e8c432ba;p=linux-2.6.git diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index d67af2c65..264307843 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1,29 +1,29 @@ /* * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation * - * Rewrite, cleanup: + * Rewrite, cleanup: * * Copyright (C) 2004 Olof Johansson , IBM Corporation - * Copyright (C) 2006 Olof Johansson * * Dynamic DMA mapping support, pSeries-specific parts, both SMP and LPAR. * - * + * * 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. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -49,101 +49,97 @@ #define DBG(fmt...) -static void tce_build_pSeries(struct iommu_table *tbl, long index, - long npages, unsigned long uaddr, +static void tce_build_pSeries(struct iommu_table *tbl, long index, + long npages, unsigned long uaddr, enum dma_data_direction direction) { - u64 proto_tce; - u64 *tcep; - u64 rpn; + union tce_entry t; + union tce_entry *tp; index <<= TCE_PAGE_FACTOR; npages <<= TCE_PAGE_FACTOR; - proto_tce = TCE_PCI_READ; // Read allowed + t.te_word = 0; + t.te_rdwr = 1; // Read allowed if (direction != DMA_TO_DEVICE) - proto_tce |= TCE_PCI_WRITE; + t.te_pciwr = 1; - tcep = ((u64 *)tbl->it_base) + index; + tp = ((union tce_entry *)tbl->it_base) + index; while (npages--) { /* can't move this out since we might cross LMB boundary */ - rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; - *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; + t.te_rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; + + tp->te_word = t.te_word; uaddr += TCE_PAGE_SIZE; - tcep++; + tp++; } } static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) { - u64 *tcep; + union tce_entry t; + union tce_entry *tp; npages <<= TCE_PAGE_FACTOR; index <<= TCE_PAGE_FACTOR; - tcep = ((u64 *)tbl->it_base) + index; - - while (npages--) - *(tcep++) = 0; + t.te_word = 0; + tp = ((union tce_entry *)tbl->it_base) + index; + + while (npages--) { + tp->te_word = t.te_word; + + tp++; + } } -static unsigned long tce_get_pseries(struct iommu_table *tbl, long index) -{ - u64 *tcep; - - index <<= TCE_PAGE_FACTOR; - tcep = ((u64 *)tbl->it_base) + index; - - return *tcep; -} static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages, unsigned long uaddr, enum dma_data_direction direction) { u64 rc; - u64 proto_tce, tce; - u64 rpn; + union tce_entry tce; tcenum <<= TCE_PAGE_FACTOR; npages <<= TCE_PAGE_FACTOR; - rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; - proto_tce = TCE_PCI_READ; + tce.te_word = 0; + tce.te_rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; + tce.te_rdwr = 1; if (direction != DMA_TO_DEVICE) - proto_tce |= TCE_PCI_WRITE; + tce.te_pciwr = 1; while (npages--) { - tce = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; - rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, tce); - + rc = plpar_tce_put((u64)tbl->it_index, + (u64)tcenum << 12, + tce.te_word ); + if (rc && printk_ratelimit()) { printk("tce_build_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); printk("\tindex = 0x%lx\n", (u64)tbl->it_index); printk("\ttcenum = 0x%lx\n", (u64)tcenum); - printk("\ttce val = 0x%lx\n", tce ); + printk("\ttce val = 0x%lx\n", tce.te_word ); show_stack(current, (unsigned long *)__get_SP()); } - + tcenum++; - rpn++; + tce.te_rpn++; } } -static DEFINE_PER_CPU(u64 *, tce_page) = NULL; +static DEFINE_PER_CPU(void *, tce_page) = NULL; static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages, unsigned long uaddr, enum dma_data_direction direction) { u64 rc; - u64 proto_tce; - u64 *tcep; - u64 rpn; + union tce_entry tce, *tcep; long l, limit; if (TCE_PAGE_FACTOR == 0 && npages == 1) @@ -156,7 +152,7 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, * from iommu_alloc{,_sg}() */ if (!tcep) { - tcep = (u64 *)__get_free_page(GFP_ATOMIC); + tcep = (void *)__get_free_page(GFP_ATOMIC); /* If allocation fails, fall back to the loop implementation */ if (!tcep) return tce_build_pSeriesLP(tbl, tcenum, npages, @@ -167,10 +163,11 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, tcenum <<= TCE_PAGE_FACTOR; npages <<= TCE_PAGE_FACTOR; - rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; - proto_tce = TCE_PCI_READ; + tce.te_word = 0; + tce.te_rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; + tce.te_rdwr = 1; if (direction != DMA_TO_DEVICE) - proto_tce |= TCE_PCI_WRITE; + tce.te_pciwr = 1; /* We can map max one pageful of TCEs at a time */ do { @@ -178,11 +175,11 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, * Set up the page with TCE data, looping through and setting * the values. */ - limit = min_t(long, npages, 4096/TCE_ENTRY_SIZE); + limit = min_t(long, npages, 4096/sizeof(union tce_entry)); for (l = 0; l < limit; l++) { - tcep[l] = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; - rpn++; + tcep[l] = tce; + tce.te_rpn++; } rc = plpar_tce_put_indirect((u64)tbl->it_index, @@ -198,7 +195,7 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, printk("tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); printk("\tindex = 0x%lx\n", (u64)tbl->it_index); printk("\tnpages = 0x%lx\n", (u64)npages); - printk("\ttce[0] val = 0x%lx\n", tcep[0]); + printk("\ttce[0] val = 0x%lx\n", tcep[0].te_word); show_stack(current, (unsigned long *)__get_SP()); } } @@ -206,17 +203,23 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) { u64 rc; + union tce_entry tce; tcenum <<= TCE_PAGE_FACTOR; npages <<= TCE_PAGE_FACTOR; + tce.te_word = 0; + while (npages--) { - rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, 0); + rc = plpar_tce_put((u64)tbl->it_index, + (u64)tcenum << 12, + tce.te_word); if (rc && printk_ratelimit()) { printk("tce_free_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); printk("\tindex = 0x%lx\n", (u64)tbl->it_index); printk("\ttcenum = 0x%lx\n", (u64)tcenum); + printk("\ttce val = 0x%lx\n", tce.te_word ); show_stack(current, (unsigned long *)__get_SP()); } @@ -228,43 +231,31 @@ static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) { u64 rc; + union tce_entry tce; tcenum <<= TCE_PAGE_FACTOR; npages <<= TCE_PAGE_FACTOR; - rc = plpar_tce_stuff((u64)tbl->it_index, (u64)tcenum << 12, 0, npages); + tce.te_word = 0; + + rc = plpar_tce_stuff((u64)tbl->it_index, + (u64)tcenum << 12, + tce.te_word, + npages); if (rc && printk_ratelimit()) { printk("tce_freemulti_pSeriesLP: plpar_tce_stuff failed\n"); printk("\trc = %ld\n", rc); printk("\tindex = 0x%lx\n", (u64)tbl->it_index); printk("\tnpages = 0x%lx\n", (u64)npages); + printk("\ttce val = 0x%lx\n", tce.te_word ); show_stack(current, (unsigned long *)__get_SP()); } } -static unsigned long tce_get_pSeriesLP(struct iommu_table *tbl, long tcenum) -{ - u64 rc; - unsigned long tce_ret; - - tcenum <<= TCE_PAGE_FACTOR; - rc = plpar_tce_get((u64)tbl->it_index, (u64)tcenum << 12, &tce_ret); - - if (rc && printk_ratelimit()) { - printk("tce_get_pSeriesLP: plpar_tce_get failed. rc=%ld\n", - rc); - printk("\tindex = 0x%lx\n", (u64)tbl->it_index); - printk("\ttcenum = 0x%lx\n", (u64)tcenum); - show_stack(current, (unsigned long *)__get_SP()); - } - - return tce_ret; -} - static void iommu_table_setparms(struct pci_controller *phb, struct device_node *dn, - struct iommu_table *tbl) + struct iommu_table *tbl) { struct device_node *node; unsigned long *basep; @@ -281,22 +272,19 @@ static void iommu_table_setparms(struct pci_controller *phb, } tbl->it_base = (unsigned long)__va(*basep); - -#ifndef CONFIG_CRASH_DUMP memset((void *)tbl->it_base, 0, *sizep); -#endif tbl->it_busno = phb->bus->number; - + /* Units of tce entries */ tbl->it_offset = phb->dma_window_base_cur >> PAGE_SHIFT; - + /* Test if we are going over 2GB of DMA space */ if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) { udbg_printf("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); - panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); + panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); } - + phb->dma_window_base_cur += phb->dma_window_size; /* Set the tce table size - measured in entries */ @@ -311,22 +299,30 @@ static void iommu_table_setparms(struct pci_controller *phb, * iommu_table_setparms_lpar * * 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 iommu_table_setparms_lpar(struct pci_controller *phb, struct device_node *dn, struct iommu_table *tbl, - unsigned char *dma_window) + unsigned int *dma_window) { - unsigned long offset, size; - tbl->it_busno = PCI_DN(dn)->bussubno; - of_parse_dma_window(dn, dma_window, &tbl->it_index, &offset, &size); + /* TODO: Parse field size properties properly. */ + tbl->it_size = (((unsigned long)dma_window[4] << 32) | + (unsigned long)dma_window[5]) >> PAGE_SHIFT; + tbl->it_offset = (((unsigned long)dma_window[2] << 32) | + (unsigned long)dma_window[3]) >> PAGE_SHIFT; tbl->it_base = 0; + tbl->it_index = dma_window[0]; tbl->it_blocksize = 16; tbl->it_type = TCE_PCI; - tbl->it_offset = offset >> PAGE_SHIFT; - tbl->it_size = size >> PAGE_SHIFT; } static void iommu_bus_setup_pSeries(struct pci_bus *bus) @@ -361,9 +357,13 @@ static void iommu_bus_setup_pSeries(struct pci_bus *bus) if (isa_dn_orig) of_node_put(isa_dn_orig); - /* Count number of direct PCI children of the PHB. */ + /* Count number of direct PCI children of the PHB. + * All PCI device nodes have class-code property, so it's + * an easy way to find them. + */ for (children = 0, tmp = dn->child; tmp; tmp = tmp->sibling) - children++; + if (get_property(tmp, "class-code", NULL)) + children++; DBG("Children: %d\n", children); @@ -394,11 +394,10 @@ static void iommu_bus_setup_pSeries(struct pci_bus *bus) pci->phb->dma_window_size = 0x8000000ul; pci->phb->dma_window_base_cur = 0x8000000ul; - tbl = kmalloc_node(sizeof(struct iommu_table), GFP_KERNEL, - pci->phb->node); + tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); iommu_table_setparms(pci->phb, dn, tbl); - pci->iommu_table = iommu_init_table(tbl, pci->phb->node); + pci->iommu_table = iommu_init_table(tbl); /* Divide the rest (1.75GB) among the children */ pci->phb->dma_window_size = 0x80000000ul; @@ -415,7 +414,7 @@ static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus) struct iommu_table *tbl; struct device_node *dn, *pdn; struct pci_dn *ppci; - unsigned char *dma_window = NULL; + unsigned int *dma_window = NULL; DBG("iommu_bus_setup_pSeriesLP, bus %p, bus->self %p\n", bus, bus->self); @@ -423,7 +422,7 @@ static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus) /* Find nearest ibm,dma-window, walking up the device tree */ for (pdn = dn; pdn != NULL; pdn = pdn->parent) { - dma_window = get_property(pdn, "ibm,dma-window", NULL); + dma_window = (unsigned int *)get_property(pdn, "ibm,dma-window", NULL); if (dma_window != NULL) break; } @@ -441,12 +440,12 @@ static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus) ppci->bussubno = bus->number; - tbl = kmalloc_node(sizeof(struct iommu_table), GFP_KERNEL, - ppci->phb->node); - + tbl = (struct iommu_table *)kmalloc(sizeof(struct iommu_table), + GFP_KERNEL); + iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window); - ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node); + ppci->iommu_table = iommu_init_table(tbl); } if (pdn != dn) @@ -469,11 +468,9 @@ static void iommu_dev_setup_pSeries(struct pci_dev *dev) */ if (!dev->bus->self) { DBG(" --> first child, no bridge. Allocating iommu table.\n"); - tbl = kmalloc_node(sizeof(struct iommu_table), GFP_KERNEL, - PCI_DN(dn)->phb->node); + tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); iommu_table_setparms(PCI_DN(dn)->phb, dn, tbl); - PCI_DN(dn)->iommu_table = iommu_init_table(tbl, - PCI_DN(dn)->phb->node); + PCI_DN(mydn)->iommu_table = iommu_init_table(tbl); return; } @@ -519,7 +516,7 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) { struct device_node *pdn, *dn; struct iommu_table *tbl; - unsigned char *dma_window = NULL; + int *dma_window = NULL; struct pci_dn *pci; DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, pci_name(dev)); @@ -534,7 +531,8 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table; pdn = pdn->parent) { - dma_window = get_property(pdn, "ibm,dma-window", NULL); + dma_window = (unsigned int *) + get_property(pdn, "ibm,dma-window", NULL); if (dma_window) break; } @@ -555,12 +553,12 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) /* iommu_table_setparms_lpar needs bussubno. */ pci->bussubno = pci->phb->bus->number; - tbl = kmalloc_node(sizeof(struct iommu_table), GFP_KERNEL, - pci->phb->node); + tbl = (struct iommu_table *)kmalloc(sizeof(struct iommu_table), + GFP_KERNEL); iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window); - pci->iommu_table = iommu_init_table(tbl, pci->phb->node); + pci->iommu_table = iommu_init_table(tbl); } if (pdn != dn) @@ -590,13 +588,11 @@ void iommu_init_early_pSeries(void) ppc_md.tce_build = tce_build_pSeriesLP; ppc_md.tce_free = tce_free_pSeriesLP; } - ppc_md.tce_get = tce_get_pSeriesLP; ppc_md.iommu_bus_setup = iommu_bus_setup_pSeriesLP; ppc_md.iommu_dev_setup = iommu_dev_setup_pSeriesLP; } else { ppc_md.tce_build = tce_build_pSeries; ppc_md.tce_free = tce_free_pSeries; - ppc_md.tce_get = tce_get_pseries; ppc_md.iommu_bus_setup = iommu_bus_setup_pSeries; ppc_md.iommu_dev_setup = iommu_dev_setup_pSeries; }