]> git.karo-electronics.de Git - linux-beck.git/blob - arch/powerpc/platforms/iseries/iommu.c
[PATCH] powerpc: merge the rest of the vio code
[linux-beck.git] / arch / powerpc / platforms / iseries / iommu.c
1 /*
2  * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation
3  *
4  * Rewrite, cleanup:
5  *
6  * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation
7  *
8  * Dynamic DMA mapping support, iSeries-specific parts.
9  *
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
24  */
25
26 #include <linux/types.h>
27 #include <linux/dma-mapping.h>
28 #include <linux/list.h>
29
30 #include <asm/iommu.h>
31 #include <asm/tce.h>
32 #include <asm/machdep.h>
33 #include <asm/abs_addr.h>
34 #include <asm/pci-bridge.h>
35 #include <asm/iseries/hv_call_xm.h>
36 #include <asm/iseries/iommu.h>
37
38 extern struct list_head iSeries_Global_Device_List;
39
40
41 static void tce_build_iSeries(struct iommu_table *tbl, long index, long npages,
42                 unsigned long uaddr, enum dma_data_direction direction)
43 {
44         u64 rc;
45         union tce_entry tce;
46
47         index <<= TCE_PAGE_FACTOR;
48         npages <<= TCE_PAGE_FACTOR;
49
50         while (npages--) {
51                 tce.te_word = 0;
52                 tce.te_bits.tb_rpn = virt_to_abs(uaddr) >> TCE_SHIFT;
53
54                 if (tbl->it_type == TCE_VB) {
55                         /* Virtual Bus */
56                         tce.te_bits.tb_valid = 1;
57                         tce.te_bits.tb_allio = 1;
58                         if (direction != DMA_TO_DEVICE)
59                                 tce.te_bits.tb_rdwr = 1;
60                 } else {
61                         /* PCI Bus */
62                         tce.te_bits.tb_rdwr = 1; /* Read allowed */
63                         if (direction != DMA_TO_DEVICE)
64                                 tce.te_bits.tb_pciwr = 1;
65                 }
66
67                 rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index,
68                                 tce.te_word);
69                 if (rc)
70                         panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%lx\n",
71                                         rc);
72                 index++;
73                 uaddr += TCE_PAGE_SIZE;
74         }
75 }
76
77 static void tce_free_iSeries(struct iommu_table *tbl, long index, long npages)
78 {
79         u64 rc;
80
81         npages <<= TCE_PAGE_FACTOR;
82         index <<= TCE_PAGE_FACTOR;
83
84         while (npages--) {
85                 rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, 0);
86                 if (rc)
87                         panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%lx\n",
88                                         rc);
89                 index++;
90         }
91 }
92
93 /*
94  * Call Hv with the architected data structure to get TCE table info.
95  * info. Put the returned data into the Linux representation of the
96  * TCE table data.
97  * The Hardware Tce table comes in three flavors.
98  * 1. TCE table shared between Buses.
99  * 2. TCE table per Bus.
100  * 3. TCE Table per IOA.
101  */
102 void iommu_table_getparms_iSeries(unsigned long busno,
103                                   unsigned char slotno,
104                                   unsigned char virtbus,
105                                   struct iommu_table* tbl)
106 {
107         struct iommu_table_cb *parms;
108
109         parms = kmalloc(sizeof(*parms), GFP_KERNEL);
110         if (parms == NULL)
111                 panic("PCI_DMA: TCE Table Allocation failed.");
112
113         memset(parms, 0, sizeof(*parms));
114
115         parms->itc_busno = busno;
116         parms->itc_slotno = slotno;
117         parms->itc_virtbus = virtbus;
118
119         HvCallXm_getTceTableParms(iseries_hv_addr(parms));
120
121         if (parms->itc_size == 0)
122                 panic("PCI_DMA: parms->size is zero, parms is 0x%p", parms);
123
124         /* itc_size is in pages worth of table, it_size is in # of entries */
125         tbl->it_size = ((parms->itc_size * TCE_PAGE_SIZE) /
126                         sizeof(union tce_entry)) >> TCE_PAGE_FACTOR;
127         tbl->it_busno = parms->itc_busno;
128         tbl->it_offset = parms->itc_offset >> TCE_PAGE_FACTOR;
129         tbl->it_index = parms->itc_index;
130         tbl->it_blocksize = 1;
131         tbl->it_type = virtbus ? TCE_VB : TCE_PCI;
132
133         kfree(parms);
134 }
135
136
137 #ifdef CONFIG_PCI
138 /*
139  * This function compares the known tables to find an iommu_table
140  * that has already been built for hardware TCEs.
141  */
142 static struct iommu_table *iommu_table_find(struct iommu_table * tbl)
143 {
144         struct pci_dn *pdn;
145
146         list_for_each_entry(pdn, &iSeries_Global_Device_List, Device_List) {
147                 struct iommu_table *it = pdn->iommu_table;
148                 if ((it != NULL) &&
149                     (it->it_type == TCE_PCI) &&
150                     (it->it_offset == tbl->it_offset) &&
151                     (it->it_index == tbl->it_index) &&
152                     (it->it_size == tbl->it_size))
153                         return it;
154         }
155         return NULL;
156 }
157
158
159 void iommu_devnode_init_iSeries(struct device_node *dn)
160 {
161         struct iommu_table *tbl;
162         struct pci_dn *pdn = PCI_DN(dn);
163
164         tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL);
165
166         iommu_table_getparms_iSeries(pdn->busno, pdn->LogicalSlot, 0, tbl);
167
168         /* Look for existing tce table */
169         pdn->iommu_table = iommu_table_find(tbl);
170         if (pdn->iommu_table == NULL)
171                 pdn->iommu_table = iommu_init_table(tbl);
172         else
173                 kfree(tbl);
174 }
175 #endif
176
177 static void iommu_dev_setup_iSeries(struct pci_dev *dev) { }
178 static void iommu_bus_setup_iSeries(struct pci_bus *bus) { }
179
180 void iommu_init_early_iSeries(void)
181 {
182         ppc_md.tce_build = tce_build_iSeries;
183         ppc_md.tce_free  = tce_free_iSeries;
184
185         ppc_md.iommu_dev_setup = iommu_dev_setup_iSeries;
186         ppc_md.iommu_bus_setup = iommu_bus_setup_iSeries;
187
188         pci_iommu_init();
189 }