aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/platforms/Makefile11
-rw-r--r--arch/powerpc/platforms/iseries/Makefile7
-rw-r--r--arch/powerpc/platforms/iseries/htab.c256
-rw-r--r--arch/powerpc/platforms/iseries/hvcall.S93
-rw-r--r--arch/powerpc/platforms/iseries/hvlog.c35
-rw-r--r--arch/powerpc/platforms/iseries/hvlpconfig.c26
-rw-r--r--arch/powerpc/platforms/iseries/iommu.c178
-rw-r--r--arch/powerpc/platforms/iseries/irq.c365
-rw-r--r--arch/powerpc/platforms/iseries/ksyms.c27
-rw-r--r--arch/powerpc/platforms/iseries/lpardata.c227
-rw-r--r--arch/powerpc/platforms/iseries/lpevents.c327
-rw-r--r--arch/powerpc/platforms/iseries/mf.c1316
-rw-r--r--arch/powerpc/platforms/iseries/misc.S55
-rw-r--r--arch/powerpc/platforms/iseries/pci.c912
-rw-r--r--arch/powerpc/platforms/iseries/proc.c115
-rw-r--r--arch/powerpc/platforms/iseries/setup.c1006
-rw-r--r--arch/powerpc/platforms/iseries/setup.h24
-rw-r--r--arch/powerpc/platforms/iseries/smp.c121
-rw-r--r--arch/powerpc/platforms/iseries/vio.c156
-rw-r--r--arch/powerpc/platforms/iseries/viopath.c672
-rw-r--r--arch/powerpc/platforms/iseries/vpdinfo.c266
21 files changed, 6191 insertions, 4 deletions
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index dbc093759a89..7637ff3642c3 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -1,4 +1,7 @@
1obj-$(CONFIG_PPC_PMAC) += powermac/ 1ifeq ($(CONFIG_PPC32),y)
2obj-$(CONFIG_4xx) += 4xx/ 2obj-$(CONFIG_PPC_PMAC) += powermac/
3obj-$(CONFIG_83xx) += 83xx/ 3endif
4obj-$(CONFIG_85xx) += 85xx/ 4obj-$(CONFIG_4xx) += 4xx/
5obj-$(CONFIG_83xx) += 83xx/
6obj-$(CONFIG_85xx) += 85xx/
7obj-$(CONFIG_PPC_ISERIES) += iseries/
diff --git a/arch/powerpc/platforms/iseries/Makefile b/arch/powerpc/platforms/iseries/Makefile
new file mode 100644
index 000000000000..18bf40077561
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/Makefile
@@ -0,0 +1,7 @@
1obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o mf.o lpevents.o \
2 hvcall.o proc.o htab.o iommu.o misc.o
3obj-$(CONFIG_PCI) += pci.o irq.o vpdinfo.o
4obj-$(CONFIG_IBMVIO) += vio.o
5obj-$(CONFIG_SMP) += smp.o
6obj-$(CONFIG_VIOPATH) += viopath.o
7obj-$(CONFIG_MODULES) += ksyms.o
diff --git a/arch/powerpc/platforms/iseries/htab.c b/arch/powerpc/platforms/iseries/htab.c
new file mode 100644
index 000000000000..431b22767d06
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/htab.c
@@ -0,0 +1,256 @@
1/*
2 * iSeries hashtable management.
3 * Derived from pSeries_htab.c
4 *
5 * SMP scalability work:
6 * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13#include <asm/machdep.h>
14#include <asm/pgtable.h>
15#include <asm/mmu.h>
16#include <asm/mmu_context.h>
17#include <asm/iSeries/HvCallHpt.h>
18#include <asm/abs_addr.h>
19#include <linux/spinlock.h>
20
21static spinlock_t iSeries_hlocks[64] __cacheline_aligned_in_smp =
22 { [0 ... 63] = SPIN_LOCK_UNLOCKED};
23
24/*
25 * Very primitive algorithm for picking up a lock
26 */
27static inline void iSeries_hlock(unsigned long slot)
28{
29 if (slot & 0x8)
30 slot = ~slot;
31 spin_lock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
32}
33
34static inline void iSeries_hunlock(unsigned long slot)
35{
36 if (slot & 0x8)
37 slot = ~slot;
38 spin_unlock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
39}
40
41static long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va,
42 unsigned long prpn, unsigned long vflags,
43 unsigned long rflags)
44{
45 unsigned long arpn;
46 long slot;
47 hpte_t lhpte;
48 int secondary = 0;
49
50 /*
51 * The hypervisor tries both primary and secondary.
52 * If we are being called to insert in the secondary,
53 * it means we have already tried both primary and secondary,
54 * so we return failure immediately.
55 */
56 if (vflags & HPTE_V_SECONDARY)
57 return -1;
58
59 iSeries_hlock(hpte_group);
60
61 slot = HvCallHpt_findValid(&lhpte, va >> PAGE_SHIFT);
62 BUG_ON(lhpte.v & HPTE_V_VALID);
63
64 if (slot == -1) { /* No available entry found in either group */
65 iSeries_hunlock(hpte_group);
66 return -1;
67 }
68
69 if (slot < 0) { /* MSB set means secondary group */
70 vflags |= HPTE_V_VALID;
71 secondary = 1;
72 slot &= 0x7fffffffffffffff;
73 }
74
75 arpn = phys_to_abs(prpn << PAGE_SHIFT) >> PAGE_SHIFT;
76
77 lhpte.v = (va >> 23) << HPTE_V_AVPN_SHIFT | vflags | HPTE_V_VALID;
78 lhpte.r = (arpn << HPTE_R_RPN_SHIFT) | rflags;
79
80 /* Now fill in the actual HPTE */
81 HvCallHpt_addValidate(slot, secondary, &lhpte);
82
83 iSeries_hunlock(hpte_group);
84
85 return (secondary << 3) | (slot & 7);
86}
87
88long iSeries_hpte_bolt_or_insert(unsigned long hpte_group,
89 unsigned long va, unsigned long prpn, unsigned long vflags,
90 unsigned long rflags)
91{
92 long slot;
93 hpte_t lhpte;
94
95 slot = HvCallHpt_findValid(&lhpte, va >> PAGE_SHIFT);
96
97 if (lhpte.v & HPTE_V_VALID) {
98 /* Bolt the existing HPTE */
99 HvCallHpt_setSwBits(slot, 0x10, 0);
100 HvCallHpt_setPp(slot, PP_RWXX);
101 return 0;
102 }
103
104 return iSeries_hpte_insert(hpte_group, va, prpn, vflags, rflags);
105}
106
107static unsigned long iSeries_hpte_getword0(unsigned long slot)
108{
109 hpte_t hpte;
110
111 HvCallHpt_get(&hpte, slot);
112 return hpte.v;
113}
114
115static long iSeries_hpte_remove(unsigned long hpte_group)
116{
117 unsigned long slot_offset;
118 int i;
119 unsigned long hpte_v;
120
121 /* Pick a random slot to start at */
122 slot_offset = mftb() & 0x7;
123
124 iSeries_hlock(hpte_group);
125
126 for (i = 0; i < HPTES_PER_GROUP; i++) {
127 hpte_v = iSeries_hpte_getword0(hpte_group + slot_offset);
128
129 if (! (hpte_v & HPTE_V_BOLTED)) {
130 HvCallHpt_invalidateSetSwBitsGet(hpte_group +
131 slot_offset, 0, 0);
132 iSeries_hunlock(hpte_group);
133 return i;
134 }
135
136 slot_offset++;
137 slot_offset &= 0x7;
138 }
139
140 iSeries_hunlock(hpte_group);
141
142 return -1;
143}
144
145/*
146 * The HyperVisor expects the "flags" argument in this form:
147 * bits 0..59 : reserved
148 * bit 60 : N
149 * bits 61..63 : PP2,PP1,PP0
150 */
151static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp,
152 unsigned long va, int large, int local)
153{
154 hpte_t hpte;
155 unsigned long avpn = va >> 23;
156
157 iSeries_hlock(slot);
158
159 HvCallHpt_get(&hpte, slot);
160 if ((HPTE_V_AVPN_VAL(hpte.v) == avpn) && (hpte.v & HPTE_V_VALID)) {
161 /*
162 * Hypervisor expects bits as NPPP, which is
163 * different from how they are mapped in our PP.
164 */
165 HvCallHpt_setPp(slot, (newpp & 0x3) | ((newpp & 0x4) << 1));
166 iSeries_hunlock(slot);
167 return 0;
168 }
169 iSeries_hunlock(slot);
170
171 return -1;
172}
173
174/*
175 * Functions used to find the PTE for a particular virtual address.
176 * Only used during boot when bolting pages.
177 *
178 * Input : vpn : virtual page number
179 * Output: PTE index within the page table of the entry
180 * -1 on failure
181 */
182static long iSeries_hpte_find(unsigned long vpn)
183{
184 hpte_t hpte;
185 long slot;
186
187 /*
188 * The HvCallHpt_findValid interface is as follows:
189 * 0xffffffffffffffff : No entry found.
190 * 0x00000000xxxxxxxx : Entry found in primary group, slot x
191 * 0x80000000xxxxxxxx : Entry found in secondary group, slot x
192 */
193 slot = HvCallHpt_findValid(&hpte, vpn);
194 if (hpte.v & HPTE_V_VALID) {
195 if (slot < 0) {
196 slot &= 0x7fffffffffffffff;
197 slot = -slot;
198 }
199 } else
200 slot = -1;
201 return slot;
202}
203
204/*
205 * Update the page protection bits. Intended to be used to create
206 * guard pages for kernel data structures on pages which are bolted
207 * in the HPT. Assumes pages being operated on will not be stolen.
208 * Does not work on large pages.
209 *
210 * No need to lock here because we should be the only user.
211 */
212static void iSeries_hpte_updateboltedpp(unsigned long newpp, unsigned long ea)
213{
214 unsigned long vsid,va,vpn;
215 long slot;
216
217 vsid = get_kernel_vsid(ea);
218 va = (vsid << 28) | (ea & 0x0fffffff);
219 vpn = va >> PAGE_SHIFT;
220 slot = iSeries_hpte_find(vpn);
221 if (slot == -1)
222 panic("updateboltedpp: Could not find page to bolt\n");
223 HvCallHpt_setPp(slot, newpp);
224}
225
226static void iSeries_hpte_invalidate(unsigned long slot, unsigned long va,
227 int large, int local)
228{
229 unsigned long hpte_v;
230 unsigned long avpn = va >> 23;
231 unsigned long flags;
232
233 local_irq_save(flags);
234
235 iSeries_hlock(slot);
236
237 hpte_v = iSeries_hpte_getword0(slot);
238
239 if ((HPTE_V_AVPN_VAL(hpte_v) == avpn) && (hpte_v & HPTE_V_VALID))
240 HvCallHpt_invalidateSetSwBitsGet(slot, 0, 0);
241
242 iSeries_hunlock(slot);
243
244 local_irq_restore(flags);
245}
246
247void hpte_init_iSeries(void)
248{
249 ppc_md.hpte_invalidate = iSeries_hpte_invalidate;
250 ppc_md.hpte_updatepp = iSeries_hpte_updatepp;
251 ppc_md.hpte_updateboltedpp = iSeries_hpte_updateboltedpp;
252 ppc_md.hpte_insert = iSeries_hpte_insert;
253 ppc_md.hpte_remove = iSeries_hpte_remove;
254
255 htab_finish_init();
256}
diff --git a/arch/powerpc/platforms/iseries/hvcall.S b/arch/powerpc/platforms/iseries/hvcall.S
new file mode 100644
index 000000000000..9901c0ec1415
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/hvcall.S
@@ -0,0 +1,93 @@
1/*
2 * This file contains the code to perform calls to the
3 * iSeries LPAR hypervisor
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 */
10
11#include <asm/ppc_asm.h>
12#include <asm/processor.h>
13
14 .text
15
16/*
17 * Hypervisor call
18 *
19 * Invoke the iSeries hypervisor via the System Call instruction
20 * Parameters are passed to this routine in registers r3 - r10
21 *
22 * r3 contains the HV function to be called
23 * r4-r10 contain the operands to the hypervisor function
24 *
25 */
26
27_GLOBAL(HvCall)
28_GLOBAL(HvCall0)
29_GLOBAL(HvCall1)
30_GLOBAL(HvCall2)
31_GLOBAL(HvCall3)
32_GLOBAL(HvCall4)
33_GLOBAL(HvCall5)
34_GLOBAL(HvCall6)
35_GLOBAL(HvCall7)
36
37
38 mfcr r0
39 std r0,-8(r1)
40 stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1)
41
42 /* r0 = 0xffffffffffffffff indicates a hypervisor call */
43
44 li r0,-1
45
46 /* Invoke the hypervisor */
47
48 sc
49
50 ld r1,0(r1)
51 ld r0,-8(r1)
52 mtcrf 0xff,r0
53
54 /* return to caller, return value in r3 */
55
56 blr
57
58_GLOBAL(HvCall0Ret16)
59_GLOBAL(HvCall1Ret16)
60_GLOBAL(HvCall2Ret16)
61_GLOBAL(HvCall3Ret16)
62_GLOBAL(HvCall4Ret16)
63_GLOBAL(HvCall5Ret16)
64_GLOBAL(HvCall6Ret16)
65_GLOBAL(HvCall7Ret16)
66
67 mfcr r0
68 std r0,-8(r1)
69 std r31,-16(r1)
70 stdu r1,-(STACK_FRAME_OVERHEAD+32)(r1)
71
72 mr r31,r4
73 li r0,-1
74 mr r4,r5
75 mr r5,r6
76 mr r6,r7
77 mr r7,r8
78 mr r8,r9
79 mr r9,r10
80
81 sc
82
83 std r3,0(r31)
84 std r4,8(r31)
85
86 mr r3,r5
87
88 ld r1,0(r1)
89 ld r0,-8(r1)
90 mtcrf 0xff,r0
91 ld r31,-16(r1)
92
93 blr
diff --git a/arch/powerpc/platforms/iseries/hvlog.c b/arch/powerpc/platforms/iseries/hvlog.c
new file mode 100644
index 000000000000..f61e2e9ac9ec
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/hvlog.c
@@ -0,0 +1,35 @@
1/*
2 * Copyright (C) 2001 Mike Corrigan IBM Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#include <asm/page.h>
11#include <asm/abs_addr.h>
12#include <asm/iSeries/HvCall.h>
13#include <asm/iSeries/HvCallSc.h>
14#include <asm/iSeries/HvTypes.h>
15
16
17void HvCall_writeLogBuffer(const void *buffer, u64 len)
18{
19 struct HvLpBufferList hv_buf;
20 u64 left_this_page;
21 u64 cur = virt_to_abs(buffer);
22
23 while (len) {
24 hv_buf.addr = cur;
25 left_this_page = ((cur & PAGE_MASK) + PAGE_SIZE) - cur;
26 if (left_this_page > len)
27 left_this_page = len;
28 hv_buf.len = left_this_page;
29 len -= left_this_page;
30 HvCall2(HvCallBaseWriteLogBuffer,
31 virt_to_abs(&hv_buf),
32 left_this_page);
33 cur = (cur & PAGE_MASK) + PAGE_SIZE;
34 }
35}
diff --git a/arch/powerpc/platforms/iseries/hvlpconfig.c b/arch/powerpc/platforms/iseries/hvlpconfig.c
new file mode 100644
index 000000000000..dc28621aea0d
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/hvlpconfig.c
@@ -0,0 +1,26 @@
1/*
2 * Copyright (C) 2001 Kyle A. Lucke, IBM Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19#include <linux/module.h>
20#include <asm/iSeries/HvLpConfig.h>
21
22HvLpIndex HvLpConfig_getLpIndex_outline(void)
23{
24 return HvLpConfig_getLpIndex();
25}
26EXPORT_SYMBOL(HvLpConfig_getLpIndex_outline);
diff --git a/arch/powerpc/platforms/iseries/iommu.c b/arch/powerpc/platforms/iseries/iommu.c
new file mode 100644
index 000000000000..9ac735d5b817
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/iommu.c
@@ -0,0 +1,178 @@
1/*
2 * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation
3 *
4 * Rewrite, cleanup:
5 *
6 * Copyright (C) 2004 Olof Johansson <olof@austin.ibm.com>, 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/iSeries/HvCallXm.h>
34#include <asm/iSeries/iSeries_pci.h>
35
36extern struct list_head iSeries_Global_Device_List;
37
38
39static void tce_build_iSeries(struct iommu_table *tbl, long index, long npages,
40 unsigned long uaddr, enum dma_data_direction direction)
41{
42 u64 rc;
43 union tce_entry tce;
44
45 while (npages--) {
46 tce.te_word = 0;
47 tce.te_bits.tb_rpn = virt_to_abs(uaddr) >> PAGE_SHIFT;
48
49 if (tbl->it_type == TCE_VB) {
50 /* Virtual Bus */
51 tce.te_bits.tb_valid = 1;
52 tce.te_bits.tb_allio = 1;
53 if (direction != DMA_TO_DEVICE)
54 tce.te_bits.tb_rdwr = 1;
55 } else {
56 /* PCI Bus */
57 tce.te_bits.tb_rdwr = 1; /* Read allowed */
58 if (direction != DMA_TO_DEVICE)
59 tce.te_bits.tb_pciwr = 1;
60 }
61
62 rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index,
63 tce.te_word);
64 if (rc)
65 panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%lx\n",
66 rc);
67 index++;
68 uaddr += PAGE_SIZE;
69 }
70}
71
72static void tce_free_iSeries(struct iommu_table *tbl, long index, long npages)
73{
74 u64 rc;
75
76 while (npages--) {
77 rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, 0);
78 if (rc)
79 panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%lx\n",
80 rc);
81 index++;
82 }
83}
84
85#ifdef CONFIG_PCI
86/*
87 * This function compares the known tables to find an iommu_table
88 * that has already been built for hardware TCEs.
89 */
90static struct iommu_table *iommu_table_find(struct iommu_table * tbl)
91{
92 struct device_node *dp;
93
94 list_for_each_entry(dp, &iSeries_Global_Device_List, Device_List) {
95 struct iommu_table *it = PCI_DN(dp)->iommu_table;
96
97 if ((it != NULL) &&
98 (it->it_type == TCE_PCI) &&
99 (it->it_offset == tbl->it_offset) &&
100 (it->it_index == tbl->it_index) &&
101 (it->it_size == tbl->it_size))
102 return it;
103 }
104 return NULL;
105}
106
107/*
108 * Call Hv with the architected data structure to get TCE table info.
109 * info. Put the returned data into the Linux representation of the
110 * TCE table data.
111 * The Hardware Tce table comes in three flavors.
112 * 1. TCE table shared between Buses.
113 * 2. TCE table per Bus.
114 * 3. TCE Table per IOA.
115 */
116static void iommu_table_getparms(struct device_node *dn,
117 struct iommu_table* tbl)
118{
119 struct iommu_table_cb *parms;
120
121 parms = kmalloc(sizeof(*parms), GFP_KERNEL);
122 if (parms == NULL)
123 panic("PCI_DMA: TCE Table Allocation failed.");
124
125 memset(parms, 0, sizeof(*parms));
126
127 parms->itc_busno = ISERIES_BUS(dn);
128 parms->itc_slotno = PCI_DN(dn)->LogicalSlot;
129 parms->itc_virtbus = 0;
130
131 HvCallXm_getTceTableParms(ISERIES_HV_ADDR(parms));
132
133 if (parms->itc_size == 0)
134 panic("PCI_DMA: parms->size is zero, parms is 0x%p", parms);
135
136 /* itc_size is in pages worth of table, it_size is in # of entries */
137 tbl->it_size = (parms->itc_size * PAGE_SIZE) / sizeof(union tce_entry);
138 tbl->it_busno = parms->itc_busno;
139 tbl->it_offset = parms->itc_offset;
140 tbl->it_index = parms->itc_index;
141 tbl->it_blocksize = 1;
142 tbl->it_type = TCE_PCI;
143
144 kfree(parms);
145}
146
147
148void iommu_devnode_init_iSeries(struct device_node *dn)
149{
150 struct iommu_table *tbl;
151 struct pci_dn *pdn = PCI_DN(dn);
152
153 tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL);
154
155 iommu_table_getparms(dn, tbl);
156
157 /* Look for existing tce table */
158 pdn->iommu_table = iommu_table_find(tbl);
159 if (pdn->iommu_table == NULL)
160 pdn->iommu_table = iommu_init_table(tbl);
161 else
162 kfree(tbl);
163}
164#endif
165
166static void iommu_dev_setup_iSeries(struct pci_dev *dev) { }
167static void iommu_bus_setup_iSeries(struct pci_bus *bus) { }
168
169void iommu_init_early_iSeries(void)
170{
171 ppc_md.tce_build = tce_build_iSeries;
172 ppc_md.tce_free = tce_free_iSeries;
173
174 ppc_md.iommu_dev_setup = iommu_dev_setup_iSeries;
175 ppc_md.iommu_bus_setup = iommu_bus_setup_iSeries;
176
177 pci_iommu_init();
178}
diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c
new file mode 100644
index 000000000000..5a8a0056b31f
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/irq.c
@@ -0,0 +1,365 @@
1/*
2 * This module supports the iSeries PCI bus interrupt handling
3 * Copyright (C) 20yy <Robert L Holtorf> <IBM Corp>
4 * Copyright (C) 2004-2005 IBM Corporation
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the:
18 * Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307 USA
21 *
22 * Change Activity:
23 * Created, December 13, 2000 by Wayne Holm
24 * End Change Activity
25 */
26#include <linux/config.h>
27#include <linux/pci.h>
28#include <linux/init.h>
29#include <linux/threads.h>
30#include <linux/smp.h>
31#include <linux/param.h>
32#include <linux/string.h>
33#include <linux/bootmem.h>
34#include <linux/ide.h>
35#include <linux/irq.h>
36#include <linux/spinlock.h>
37
38#include <asm/ppcdebug.h>
39#include <asm/iSeries/HvTypes.h>
40#include <asm/iSeries/HvLpEvent.h>
41#include <asm/iSeries/HvCallPci.h>
42#include <asm/iSeries/HvCallXm.h>
43#include <asm/iSeries/iSeries_irq.h>
44
45/* This maps virtual irq numbers to real irqs */
46unsigned int virt_irq_to_real_map[NR_IRQS];
47
48/* The next available virtual irq number */
49/* Note: the pcnet32 driver assumes irq numbers < 2 aren't valid. :( */
50static int next_virtual_irq = 2;
51
52static long Pci_Interrupt_Count;
53static long Pci_Event_Count;
54
55enum XmPciLpEvent_Subtype {
56 XmPciLpEvent_BusCreated = 0, // PHB has been created
57 XmPciLpEvent_BusError = 1, // PHB has failed
58 XmPciLpEvent_BusFailed = 2, // Msg to Secondary, Primary failed bus
59 XmPciLpEvent_NodeFailed = 4, // Multi-adapter bridge has failed
60 XmPciLpEvent_NodeRecovered = 5, // Multi-adapter bridge has recovered
61 XmPciLpEvent_BusRecovered = 12, // PHB has been recovered
62 XmPciLpEvent_UnQuiesceBus = 18, // Secondary bus unqiescing
63 XmPciLpEvent_BridgeError = 21, // Bridge Error
64 XmPciLpEvent_SlotInterrupt = 22 // Slot interrupt
65};
66
67struct XmPciLpEvent_BusInterrupt {
68 HvBusNumber busNumber;
69 HvSubBusNumber subBusNumber;
70};
71
72struct XmPciLpEvent_NodeInterrupt {
73 HvBusNumber busNumber;
74 HvSubBusNumber subBusNumber;
75 HvAgentId deviceId;
76};
77
78struct XmPciLpEvent {
79 struct HvLpEvent hvLpEvent;
80
81 union {
82 u64 alignData; // Align on an 8-byte boundary
83
84 struct {
85 u32 fisr;
86 HvBusNumber busNumber;
87 HvSubBusNumber subBusNumber;
88 HvAgentId deviceId;
89 } slotInterrupt;
90
91 struct XmPciLpEvent_BusInterrupt busFailed;
92 struct XmPciLpEvent_BusInterrupt busRecovered;
93 struct XmPciLpEvent_BusInterrupt busCreated;
94
95 struct XmPciLpEvent_NodeInterrupt nodeFailed;
96 struct XmPciLpEvent_NodeInterrupt nodeRecovered;
97
98 } eventData;
99
100};
101
102static void intReceived(struct XmPciLpEvent *eventParm,
103 struct pt_regs *regsParm)
104{
105 int irq;
106
107 ++Pci_Interrupt_Count;
108
109 switch (eventParm->hvLpEvent.xSubtype) {
110 case XmPciLpEvent_SlotInterrupt:
111 irq = eventParm->hvLpEvent.xCorrelationToken;
112 /* Dispatch the interrupt handlers for this irq */
113 ppc_irq_dispatch_handler(regsParm, irq);
114 HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber,
115 eventParm->eventData.slotInterrupt.subBusNumber,
116 eventParm->eventData.slotInterrupt.deviceId);
117 break;
118 /* Ignore error recovery events for now */
119 case XmPciLpEvent_BusCreated:
120 printk(KERN_INFO "intReceived: system bus %d created\n",
121 eventParm->eventData.busCreated.busNumber);
122 break;
123 case XmPciLpEvent_BusError:
124 case XmPciLpEvent_BusFailed:
125 printk(KERN_INFO "intReceived: system bus %d failed\n",
126 eventParm->eventData.busFailed.busNumber);
127 break;
128 case XmPciLpEvent_BusRecovered:
129 case XmPciLpEvent_UnQuiesceBus:
130 printk(KERN_INFO "intReceived: system bus %d recovered\n",
131 eventParm->eventData.busRecovered.busNumber);
132 break;
133 case XmPciLpEvent_NodeFailed:
134 case XmPciLpEvent_BridgeError:
135 printk(KERN_INFO
136 "intReceived: multi-adapter bridge %d/%d/%d failed\n",
137 eventParm->eventData.nodeFailed.busNumber,
138 eventParm->eventData.nodeFailed.subBusNumber,
139 eventParm->eventData.nodeFailed.deviceId);
140 break;
141 case XmPciLpEvent_NodeRecovered:
142 printk(KERN_INFO
143 "intReceived: multi-adapter bridge %d/%d/%d recovered\n",
144 eventParm->eventData.nodeRecovered.busNumber,
145 eventParm->eventData.nodeRecovered.subBusNumber,
146 eventParm->eventData.nodeRecovered.deviceId);
147 break;
148 default:
149 printk(KERN_ERR
150 "intReceived: unrecognized event subtype 0x%x\n",
151 eventParm->hvLpEvent.xSubtype);
152 break;
153 }
154}
155
156static void XmPciLpEvent_handler(struct HvLpEvent *eventParm,
157 struct pt_regs *regsParm)
158{
159#ifdef CONFIG_PCI
160 ++Pci_Event_Count;
161
162 if (eventParm && (eventParm->xType == HvLpEvent_Type_PciIo)) {
163 switch (eventParm->xFlags.xFunction) {
164 case HvLpEvent_Function_Int:
165 intReceived((struct XmPciLpEvent *)eventParm, regsParm);
166 break;
167 case HvLpEvent_Function_Ack:
168 printk(KERN_ERR
169 "XmPciLpEvent_handler: unexpected ack received\n");
170 break;
171 default:
172 printk(KERN_ERR
173 "XmPciLpEvent_handler: unexpected event function %d\n",
174 (int)eventParm->xFlags.xFunction);
175 break;
176 }
177 } else if (eventParm)
178 printk(KERN_ERR
179 "XmPciLpEvent_handler: Unrecognized PCI event type 0x%x\n",
180 (int)eventParm->xType);
181 else
182 printk(KERN_ERR "XmPciLpEvent_handler: NULL event received\n");
183#endif
184}
185
186/*
187 * This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c
188 * It must be called before the bus walk.
189 */
190void __init iSeries_init_IRQ(void)
191{
192 /* Register PCI event handler and open an event path */
193 int xRc;
194
195 xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo,
196 &XmPciLpEvent_handler);
197 if (xRc == 0) {
198 xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0);
199 if (xRc != 0)
200 printk(KERN_ERR "iSeries_init_IRQ: open event path "
201 "failed with rc 0x%x\n", xRc);
202 } else
203 printk(KERN_ERR "iSeries_init_IRQ: register handler "
204 "failed with rc 0x%x\n", xRc);
205}
206
207#define REAL_IRQ_TO_BUS(irq) ((((irq) >> 6) & 0xff) + 1)
208#define REAL_IRQ_TO_IDSEL(irq) ((((irq) >> 3) & 7) + 1)
209#define REAL_IRQ_TO_FUNC(irq) ((irq) & 7)
210
211/*
212 * This will be called by device drivers (via enable_IRQ)
213 * to enable INTA in the bridge interrupt status register.
214 */
215static void iSeries_enable_IRQ(unsigned int irq)
216{
217 u32 bus, deviceId, function, mask;
218 const u32 subBus = 0;
219 unsigned int rirq = virt_irq_to_real_map[irq];
220
221 /* The IRQ has already been locked by the caller */
222 bus = REAL_IRQ_TO_BUS(rirq);
223 function = REAL_IRQ_TO_FUNC(rirq);
224 deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
225
226 /* Unmask secondary INTA */
227 mask = 0x80000000;
228 HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask);
229 PPCDBG(PPCDBG_BUSWALK, "iSeries_enable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
230 bus, subBus, deviceId, irq);
231}
232
233/* This is called by iSeries_activate_IRQs */
234static unsigned int iSeries_startup_IRQ(unsigned int irq)
235{
236 u32 bus, deviceId, function, mask;
237 const u32 subBus = 0;
238 unsigned int rirq = virt_irq_to_real_map[irq];
239
240 bus = REAL_IRQ_TO_BUS(rirq);
241 function = REAL_IRQ_TO_FUNC(rirq);
242 deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
243
244 /* Link the IRQ number to the bridge */
245 HvCallXm_connectBusUnit(bus, subBus, deviceId, irq);
246
247 /* Unmask bridge interrupts in the FISR */
248 mask = 0x01010000 << function;
249 HvCallPci_unmaskFisr(bus, subBus, deviceId, mask);
250 iSeries_enable_IRQ(irq);
251 return 0;
252}
253
254/*
255 * This is called out of iSeries_fixup to activate interrupt
256 * generation for usable slots
257 */
258void __init iSeries_activate_IRQs()
259{
260 int irq;
261 unsigned long flags;
262
263 for_each_irq (irq) {
264 irq_desc_t *desc = get_irq_desc(irq);
265
266 if (desc && desc->handler && desc->handler->startup) {
267 spin_lock_irqsave(&desc->lock, flags);
268 desc->handler->startup(irq);
269 spin_unlock_irqrestore(&desc->lock, flags);
270 }
271 }
272}
273
274/* this is not called anywhere currently */
275static void iSeries_shutdown_IRQ(unsigned int irq)
276{
277 u32 bus, deviceId, function, mask;
278 const u32 subBus = 0;
279 unsigned int rirq = virt_irq_to_real_map[irq];
280
281 /* irq should be locked by the caller */
282 bus = REAL_IRQ_TO_BUS(rirq);
283 function = REAL_IRQ_TO_FUNC(rirq);
284 deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
285
286 /* Invalidate the IRQ number in the bridge */
287 HvCallXm_connectBusUnit(bus, subBus, deviceId, 0);
288
289 /* Mask bridge interrupts in the FISR */
290 mask = 0x01010000 << function;
291 HvCallPci_maskFisr(bus, subBus, deviceId, mask);
292}
293
294/*
295 * This will be called by device drivers (via disable_IRQ)
296 * to disable INTA in the bridge interrupt status register.
297 */
298static void iSeries_disable_IRQ(unsigned int irq)
299{
300 u32 bus, deviceId, function, mask;
301 const u32 subBus = 0;
302 unsigned int rirq = virt_irq_to_real_map[irq];
303
304 /* The IRQ has already been locked by the caller */
305 bus = REAL_IRQ_TO_BUS(rirq);
306 function = REAL_IRQ_TO_FUNC(rirq);
307 deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
308
309 /* Mask secondary INTA */
310 mask = 0x80000000;
311 HvCallPci_maskInterrupts(bus, subBus, deviceId, mask);
312 PPCDBG(PPCDBG_BUSWALK, "iSeries_disable_IRQ 0x%02X.%02X.%02X 0x%04X\n",
313 bus, subBus, deviceId, irq);
314}
315
316/*
317 * Need to define this so ppc_irq_dispatch_handler will NOT call
318 * enable_IRQ at the end of interrupt handling. However, this does
319 * nothing because there is not enough information provided to do
320 * the EOI HvCall. This is done by XmPciLpEvent.c
321 */
322static void iSeries_end_IRQ(unsigned int irq)
323{
324}
325
326static hw_irq_controller iSeries_IRQ_handler = {
327 .typename = "iSeries irq controller",
328 .startup = iSeries_startup_IRQ,
329 .shutdown = iSeries_shutdown_IRQ,
330 .enable = iSeries_enable_IRQ,
331 .disable = iSeries_disable_IRQ,
332 .end = iSeries_end_IRQ
333};
334
335/*
336 * This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot
337 * It calculates the irq value for the slot.
338 * Note that subBusNumber is always 0 (at the moment at least).
339 */
340int __init iSeries_allocate_IRQ(HvBusNumber busNumber,
341 HvSubBusNumber subBusNumber, HvAgentId deviceId)
342{
343 unsigned int realirq, virtirq;
344 u8 idsel = (deviceId >> 4);
345 u8 function = deviceId & 7;
346
347 virtirq = next_virtual_irq++;
348 realirq = ((busNumber - 1) << 6) + ((idsel - 1) << 3) + function;
349 virt_irq_to_real_map[virtirq] = realirq;
350
351 irq_desc[virtirq].handler = &iSeries_IRQ_handler;
352 return virtirq;
353}
354
355int virt_irq_create_mapping(unsigned int real_irq)
356{
357 BUG(); /* Don't call this on iSeries, yet */
358
359 return 0;
360}
361
362void virt_irq_init(void)
363{
364 return;
365}
diff --git a/arch/powerpc/platforms/iseries/ksyms.c b/arch/powerpc/platforms/iseries/ksyms.c
new file mode 100644
index 000000000000..f271b3539721
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/ksyms.c
@@ -0,0 +1,27 @@
1/*
2 * (C) 2001-2005 PPC 64 Team, IBM Corp
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9#include <linux/module.h>
10
11#include <asm/hw_irq.h>
12#include <asm/iSeries/HvCallSc.h>
13
14EXPORT_SYMBOL(HvCall0);
15EXPORT_SYMBOL(HvCall1);
16EXPORT_SYMBOL(HvCall2);
17EXPORT_SYMBOL(HvCall3);
18EXPORT_SYMBOL(HvCall4);
19EXPORT_SYMBOL(HvCall5);
20EXPORT_SYMBOL(HvCall6);
21EXPORT_SYMBOL(HvCall7);
22
23#ifdef CONFIG_SMP
24EXPORT_SYMBOL(local_get_flags);
25EXPORT_SYMBOL(local_irq_disable);
26EXPORT_SYMBOL(local_irq_restore);
27#endif
diff --git a/arch/powerpc/platforms/iseries/lpardata.c b/arch/powerpc/platforms/iseries/lpardata.c
new file mode 100644
index 000000000000..87b7ad8ca465
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/lpardata.c
@@ -0,0 +1,227 @@
1/*
2 * Copyright 2001 Mike Corrigan, IBM Corp
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9#include <linux/config.h>
10#include <linux/types.h>
11#include <linux/threads.h>
12#include <linux/module.h>
13#include <linux/bitops.h>
14#include <asm/processor.h>
15#include <asm/ptrace.h>
16#include <asm/naca.h>
17#include <asm/abs_addr.h>
18#include <asm/iSeries/ItLpNaca.h>
19#include <asm/lppaca.h>
20#include <asm/iSeries/ItLpRegSave.h>
21#include <asm/paca.h>
22#include <asm/iSeries/HvReleaseData.h>
23#include <asm/iSeries/LparMap.h>
24#include <asm/iSeries/ItVpdAreas.h>
25#include <asm/iSeries/ItIplParmsReal.h>
26#include <asm/iSeries/ItExtVpdPanel.h>
27#include <asm/iSeries/ItLpQueue.h>
28#include <asm/iSeries/IoHriProcessorVpd.h>
29#include <asm/iSeries/ItSpCommArea.h>
30
31
32/* The HvReleaseData is the root of the information shared between
33 * the hypervisor and Linux.
34 */
35struct HvReleaseData hvReleaseData = {
36 .xDesc = 0xc8a5d9c4, /* "HvRD" ebcdic */
37 .xSize = sizeof(struct HvReleaseData),
38 .xVpdAreasPtrOffset = offsetof(struct naca_struct, xItVpdAreas),
39 .xSlicNacaAddr = &naca, /* 64-bit Naca address */
40 .xMsNucDataOffset = LPARMAP_PHYS,
41 .xFlags = HVREL_TAGSINACTIVE /* tags inactive */
42 /* 64 bit */
43 /* shared processors */
44 /* HMT allowed */
45 | 6, /* TEMP: This allows non-GA driver */
46 .xVrmIndex = 4, /* We are v5r2m0 */
47 .xMinSupportedPlicVrmIndex = 3, /* v5r1m0 */
48 .xMinCompatablePlicVrmIndex = 3, /* v5r1m0 */
49 .xVrmName = { 0xd3, 0x89, 0x95, 0xa4, /* "Linux 2.4.64" ebcdic */
50 0xa7, 0x40, 0xf2, 0x4b,
51 0xf4, 0x4b, 0xf6, 0xf4 },
52};
53
54/*
55 * The NACA. The first dword of the naca is required by the iSeries
56 * hypervisor to point to itVpdAreas. The hypervisor finds the NACA
57 * through the pointer in hvReleaseData.
58 */
59struct naca_struct naca = {
60 .xItVpdAreas = &itVpdAreas,
61 .xRamDisk = 0,
62 .xRamDiskSize = 0,
63};
64
65extern void system_reset_iSeries(void);
66extern void machine_check_iSeries(void);
67extern void data_access_iSeries(void);
68extern void instruction_access_iSeries(void);
69extern void hardware_interrupt_iSeries(void);
70extern void alignment_iSeries(void);
71extern void program_check_iSeries(void);
72extern void fp_unavailable_iSeries(void);
73extern void decrementer_iSeries(void);
74extern void trap_0a_iSeries(void);
75extern void trap_0b_iSeries(void);
76extern void system_call_iSeries(void);
77extern void single_step_iSeries(void);
78extern void trap_0e_iSeries(void);
79extern void performance_monitor_iSeries(void);
80extern void data_access_slb_iSeries(void);
81extern void instruction_access_slb_iSeries(void);
82
83struct ItLpNaca itLpNaca = {
84 .xDesc = 0xd397d581, /* "LpNa" ebcdic */
85 .xSize = 0x0400, /* size of ItLpNaca */
86 .xIntHdlrOffset = 0x0300, /* offset to int array */
87 .xMaxIntHdlrEntries = 19, /* # ents */
88 .xPrimaryLpIndex = 0, /* Part # of primary */
89 .xServiceLpIndex = 0, /* Part # of serv */
90 .xLpIndex = 0, /* Part # of me */
91 .xMaxLpQueues = 0, /* # of LP queues */
92 .xLpQueueOffset = 0x100, /* offset of start of LP queues */
93 .xPirEnvironMode = 0, /* Piranha stuff */
94 .xPirConsoleMode = 0,
95 .xPirDasdMode = 0,
96 .xLparInstalled = 0,
97 .xSysPartitioned = 0,
98 .xHwSyncedTBs = 0,
99 .xIntProcUtilHmt = 0,
100 .xSpVpdFormat = 0,
101 .xIntProcRatio = 0,
102 .xPlicVrmIndex = 0, /* VRM index of PLIC */
103 .xMinSupportedSlicVrmInd = 0, /* min supported SLIC */
104 .xMinCompatableSlicVrmInd = 0, /* min compat SLIC */
105 .xLoadAreaAddr = 0, /* 64-bit addr of load area */
106 .xLoadAreaChunks = 0, /* chunks for load area */
107 .xPaseSysCallCRMask = 0, /* PASE mask */
108 .xSlicSegmentTablePtr = 0, /* seg table */
109 .xOldLpQueue = { 0 }, /* Old LP Queue */
110 .xInterruptHdlr = {
111 (u64)system_reset_iSeries, /* 0x100 System Reset */
112 (u64)machine_check_iSeries, /* 0x200 Machine Check */
113 (u64)data_access_iSeries, /* 0x300 Data Access */
114 (u64)instruction_access_iSeries, /* 0x400 Instruction Access */
115 (u64)hardware_interrupt_iSeries, /* 0x500 External */
116 (u64)alignment_iSeries, /* 0x600 Alignment */
117 (u64)program_check_iSeries, /* 0x700 Program Check */
118 (u64)fp_unavailable_iSeries, /* 0x800 FP Unavailable */
119 (u64)decrementer_iSeries, /* 0x900 Decrementer */
120 (u64)trap_0a_iSeries, /* 0xa00 Trap 0A */
121 (u64)trap_0b_iSeries, /* 0xb00 Trap 0B */
122 (u64)system_call_iSeries, /* 0xc00 System Call */
123 (u64)single_step_iSeries, /* 0xd00 Single Step */
124 (u64)trap_0e_iSeries, /* 0xe00 Trap 0E */
125 (u64)performance_monitor_iSeries,/* 0xf00 Performance Monitor */
126 0, /* int 0x1000 */
127 0, /* int 0x1010 */
128 0, /* int 0x1020 CPU ctls */
129 (u64)hardware_interrupt_iSeries, /* SC Ret Hdlr */
130 (u64)data_access_slb_iSeries, /* 0x380 D-SLB */
131 (u64)instruction_access_slb_iSeries /* 0x480 I-SLB */
132 }
133};
134EXPORT_SYMBOL(itLpNaca);
135
136/* May be filled in by the hypervisor so cannot end up in the BSS */
137struct ItIplParmsReal xItIplParmsReal __attribute__((__section__(".data")));
138
139/* May be filled in by the hypervisor so cannot end up in the BSS */
140struct ItExtVpdPanel xItExtVpdPanel __attribute__((__section__(".data")));
141EXPORT_SYMBOL(xItExtVpdPanel);
142
143#define maxPhysicalProcessors 32
144
145struct IoHriProcessorVpd xIoHriProcessorVpd[maxPhysicalProcessors] = {
146 {
147 .xInstCacheOperandSize = 32,
148 .xDataCacheOperandSize = 32,
149 .xProcFreq = 50000000,
150 .xTimeBaseFreq = 50000000,
151 .xPVR = 0x3600
152 }
153};
154
155/* Space for Main Store Vpd 27,200 bytes */
156/* May be filled in by the hypervisor so cannot end up in the BSS */
157u64 xMsVpd[3400] __attribute__((__section__(".data")));
158
159/* Space for Recovery Log Buffer */
160/* May be filled in by the hypervisor so cannot end up in the BSS */
161u64 xRecoveryLogBuffer[32] __attribute__((__section__(".data")));
162
163struct SpCommArea xSpCommArea = {
164 .xDesc = 0xE2D7C3C2,
165 .xFormat = 1,
166};
167
168/* The LparMap data is now located at offset 0x6000 in head.S
169 * It was put there so that the HvReleaseData could address it
170 * with a 32-bit offset as required by the iSeries hypervisor
171 *
172 * The Naca has a pointer to the ItVpdAreas. The hypervisor finds
173 * the Naca via the HvReleaseData area. The HvReleaseData has the
174 * offset into the Naca of the pointer to the ItVpdAreas.
175 */
176struct ItVpdAreas itVpdAreas = {
177 .xSlicDesc = 0xc9a3e5c1, /* "ItVA" */
178 .xSlicSize = sizeof(struct ItVpdAreas),
179 .xSlicVpdEntries = ItVpdMaxEntries, /* # VPD array entries */
180 .xSlicDmaEntries = ItDmaMaxEntries, /* # DMA array entries */
181 .xSlicMaxLogicalProcs = NR_CPUS * 2, /* Max logical procs */
182 .xSlicMaxPhysicalProcs = maxPhysicalProcessors, /* Max physical procs */
183 .xSlicDmaToksOffset = offsetof(struct ItVpdAreas, xPlicDmaToks),
184 .xSlicVpdAdrsOffset = offsetof(struct ItVpdAreas, xSlicVpdAdrs),
185 .xSlicDmaLensOffset = offsetof(struct ItVpdAreas, xPlicDmaLens),
186 .xSlicVpdLensOffset = offsetof(struct ItVpdAreas, xSlicVpdLens),
187 .xSlicMaxSlotLabels = 0, /* max slot labels */
188 .xSlicMaxLpQueues = 1, /* max LP queues */
189 .xPlicDmaLens = { 0 }, /* DMA lengths */
190 .xPlicDmaToks = { 0 }, /* DMA tokens */
191 .xSlicVpdLens = { /* VPD lengths */
192 0,0,0, /* 0 - 2 */
193 sizeof(xItExtVpdPanel), /* 3 Extended VPD */
194 sizeof(struct paca_struct), /* 4 length of Paca */
195 0, /* 5 */
196 sizeof(struct ItIplParmsReal),/* 6 length of IPL parms */
197 26992, /* 7 length of MS VPD */
198 0, /* 8 */
199 sizeof(struct ItLpNaca),/* 9 length of LP Naca */
200 0, /* 10 */
201 256, /* 11 length of Recovery Log Buf */
202 sizeof(struct SpCommArea), /* 12 length of SP Comm Area */
203 0,0,0, /* 13 - 15 */
204 sizeof(struct IoHriProcessorVpd),/* 16 length of Proc Vpd */
205 0,0,0,0,0,0, /* 17 - 22 */
206 sizeof(struct hvlpevent_queue), /* 23 length of Lp Queue */
207 0,0 /* 24 - 25 */
208 },
209 .xSlicVpdAdrs = { /* VPD addresses */
210 0,0,0, /* 0 - 2 */
211 &xItExtVpdPanel, /* 3 Extended VPD */
212 &paca[0], /* 4 first Paca */
213 0, /* 5 */
214 &xItIplParmsReal, /* 6 IPL parms */
215 &xMsVpd, /* 7 MS Vpd */
216 0, /* 8 */
217 &itLpNaca, /* 9 LpNaca */
218 0, /* 10 */
219 &xRecoveryLogBuffer, /* 11 Recovery Log Buffer */
220 &xSpCommArea, /* 12 SP Comm Area */
221 0,0,0, /* 13 - 15 */
222 &xIoHriProcessorVpd, /* 16 Proc Vpd */
223 0,0,0,0,0,0, /* 17 - 22 */
224 &hvlpevent_queue, /* 23 Lp Queue */
225 0,0
226 }
227};
diff --git a/arch/powerpc/platforms/iseries/lpevents.c b/arch/powerpc/platforms/iseries/lpevents.c
new file mode 100644
index 000000000000..883603027ccf
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/lpevents.c
@@ -0,0 +1,327 @@
1/*
2 * Copyright (C) 2001 Mike Corrigan IBM Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#include <linux/stddef.h>
11#include <linux/kernel.h>
12#include <linux/sched.h>
13#include <linux/bootmem.h>
14#include <linux/seq_file.h>
15#include <linux/proc_fs.h>
16#include <asm/system.h>
17#include <asm/paca.h>
18#include <asm/iSeries/ItLpQueue.h>
19#include <asm/iSeries/HvLpEvent.h>
20#include <asm/iSeries/HvCallEvent.h>
21#include <asm/iSeries/ItLpNaca.h>
22
23/*
24 * The LpQueue is used to pass event data from the hypervisor to
25 * the partition. This is where I/O interrupt events are communicated.
26 *
27 * It is written to by the hypervisor so cannot end up in the BSS.
28 */
29struct hvlpevent_queue hvlpevent_queue __attribute__((__section__(".data")));
30
31DEFINE_PER_CPU(unsigned long[HvLpEvent_Type_NumTypes], hvlpevent_counts);
32
33static char *event_types[HvLpEvent_Type_NumTypes] = {
34 "Hypervisor",
35 "Machine Facilities",
36 "Session Manager",
37 "SPD I/O",
38 "Virtual Bus",
39 "PCI I/O",
40 "RIO I/O",
41 "Virtual Lan",
42 "Virtual I/O"
43};
44
45/* Array of LpEvent handler functions */
46static LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes];
47static unsigned lpEventHandlerPaths[HvLpEvent_Type_NumTypes];
48
49static struct HvLpEvent * get_next_hvlpevent(void)
50{
51 struct HvLpEvent * event;
52 event = (struct HvLpEvent *)hvlpevent_queue.xSlicCurEventPtr;
53
54 if (event->xFlags.xValid) {
55 /* rmb() needed only for weakly consistent machines (regatta) */
56 rmb();
57 /* Set pointer to next potential event */
58 hvlpevent_queue.xSlicCurEventPtr += ((event->xSizeMinus1 +
59 LpEventAlign) / LpEventAlign) * LpEventAlign;
60
61 /* Wrap to beginning if no room at end */
62 if (hvlpevent_queue.xSlicCurEventPtr >
63 hvlpevent_queue.xSlicLastValidEventPtr) {
64 hvlpevent_queue.xSlicCurEventPtr =
65 hvlpevent_queue.xSlicEventStackPtr;
66 }
67 } else {
68 event = NULL;
69 }
70
71 return event;
72}
73
74static unsigned long spread_lpevents = NR_CPUS;
75
76int hvlpevent_is_pending(void)
77{
78 struct HvLpEvent *next_event;
79
80 if (smp_processor_id() >= spread_lpevents)
81 return 0;
82
83 next_event = (struct HvLpEvent *)hvlpevent_queue.xSlicCurEventPtr;
84
85 return next_event->xFlags.xValid |
86 hvlpevent_queue.xPlicOverflowIntPending;
87}
88
89static void hvlpevent_clear_valid(struct HvLpEvent * event)
90{
91 /* Tell the Hypervisor that we're done with this event.
92 * Also clear bits within this event that might look like valid bits.
93 * ie. on 64-byte boundaries.
94 */
95 struct HvLpEvent *tmp;
96 unsigned extra = ((event->xSizeMinus1 + LpEventAlign) /
97 LpEventAlign) - 1;
98
99 switch (extra) {
100 case 3:
101 tmp = (struct HvLpEvent*)((char*)event + 3 * LpEventAlign);
102 tmp->xFlags.xValid = 0;
103 case 2:
104 tmp = (struct HvLpEvent*)((char*)event + 2 * LpEventAlign);
105 tmp->xFlags.xValid = 0;
106 case 1:
107 tmp = (struct HvLpEvent*)((char*)event + 1 * LpEventAlign);
108 tmp->xFlags.xValid = 0;
109 }
110
111 mb();
112
113 event->xFlags.xValid = 0;
114}
115
116void process_hvlpevents(struct pt_regs *regs)
117{
118 struct HvLpEvent * event;
119
120 /* If we have recursed, just return */
121 if (!spin_trylock(&hvlpevent_queue.lock))
122 return;
123
124 for (;;) {
125 event = get_next_hvlpevent();
126 if (event) {
127 /* Call appropriate handler here, passing
128 * a pointer to the LpEvent. The handler
129 * must make a copy of the LpEvent if it
130 * needs it in a bottom half. (perhaps for
131 * an ACK)
132 *
133 * Handlers are responsible for ACK processing
134 *
135 * The Hypervisor guarantees that LpEvents will
136 * only be delivered with types that we have
137 * registered for, so no type check is necessary
138 * here!
139 */
140 if (event->xType < HvLpEvent_Type_NumTypes)
141 __get_cpu_var(hvlpevent_counts)[event->xType]++;
142 if (event->xType < HvLpEvent_Type_NumTypes &&
143 lpEventHandler[event->xType])
144 lpEventHandler[event->xType](event, regs);
145 else
146 printk(KERN_INFO "Unexpected Lp Event type=%d\n", event->xType );
147
148 hvlpevent_clear_valid(event);
149 } else if (hvlpevent_queue.xPlicOverflowIntPending)
150 /*
151 * No more valid events. If overflow events are
152 * pending process them
153 */
154 HvCallEvent_getOverflowLpEvents(hvlpevent_queue.xIndex);
155 else
156 break;
157 }
158
159 spin_unlock(&hvlpevent_queue.lock);
160}
161
162static int set_spread_lpevents(char *str)
163{
164 unsigned long val = simple_strtoul(str, NULL, 0);
165
166 /*
167 * The parameter is the number of processors to share in processing
168 * lp events.
169 */
170 if (( val > 0) && (val <= NR_CPUS)) {
171 spread_lpevents = val;
172 printk("lpevent processing spread over %ld processors\n", val);
173 } else {
174 printk("invalid spread_lpevents %ld\n", val);
175 }
176
177 return 1;
178}
179__setup("spread_lpevents=", set_spread_lpevents);
180
181void setup_hvlpevent_queue(void)
182{
183 void *eventStack;
184
185 /*
186 * Allocate a page for the Event Stack. The Hypervisor needs the
187 * absolute real address, so we subtract out the KERNELBASE and add
188 * in the absolute real address of the kernel load area.
189 */
190 eventStack = alloc_bootmem_pages(LpEventStackSize);
191 memset(eventStack, 0, LpEventStackSize);
192
193 /* Invoke the hypervisor to initialize the event stack */
194 HvCallEvent_setLpEventStack(0, eventStack, LpEventStackSize);
195
196 hvlpevent_queue.xSlicEventStackPtr = (char *)eventStack;
197 hvlpevent_queue.xSlicCurEventPtr = (char *)eventStack;
198 hvlpevent_queue.xSlicLastValidEventPtr = (char *)eventStack +
199 (LpEventStackSize - LpEventMaxSize);
200 hvlpevent_queue.xIndex = 0;
201}
202
203/* Register a handler for an LpEvent type */
204int HvLpEvent_registerHandler(HvLpEvent_Type eventType, LpEventHandler handler)
205{
206 if (eventType < HvLpEvent_Type_NumTypes) {
207 lpEventHandler[eventType] = handler;
208 return 0;
209 }
210 return 1;
211}
212EXPORT_SYMBOL(HvLpEvent_registerHandler);
213
214int HvLpEvent_unregisterHandler(HvLpEvent_Type eventType)
215{
216 might_sleep();
217
218 if (eventType < HvLpEvent_Type_NumTypes) {
219 if (!lpEventHandlerPaths[eventType]) {
220 lpEventHandler[eventType] = NULL;
221 /*
222 * We now sleep until all other CPUs have scheduled.
223 * This ensures that the deletion is seen by all
224 * other CPUs, and that the deleted handler isn't
225 * still running on another CPU when we return.
226 */
227 synchronize_rcu();
228 return 0;
229 }
230 }
231 return 1;
232}
233EXPORT_SYMBOL(HvLpEvent_unregisterHandler);
234
235/*
236 * lpIndex is the partition index of the target partition.
237 * needed only for VirtualIo, VirtualLan and SessionMgr. Zero
238 * indicates to use our partition index - for the other types.
239 */
240int HvLpEvent_openPath(HvLpEvent_Type eventType, HvLpIndex lpIndex)
241{
242 if ((eventType < HvLpEvent_Type_NumTypes) &&
243 lpEventHandler[eventType]) {
244 if (lpIndex == 0)
245 lpIndex = itLpNaca.xLpIndex;
246 HvCallEvent_openLpEventPath(lpIndex, eventType);
247 ++lpEventHandlerPaths[eventType];
248 return 0;
249 }
250 return 1;
251}
252
253int HvLpEvent_closePath(HvLpEvent_Type eventType, HvLpIndex lpIndex)
254{
255 if ((eventType < HvLpEvent_Type_NumTypes) &&
256 lpEventHandler[eventType] &&
257 lpEventHandlerPaths[eventType]) {
258 if (lpIndex == 0)
259 lpIndex = itLpNaca.xLpIndex;
260 HvCallEvent_closeLpEventPath(lpIndex, eventType);
261 --lpEventHandlerPaths[eventType];
262 return 0;
263 }
264 return 1;
265}
266
267static int proc_lpevents_show(struct seq_file *m, void *v)
268{
269 int cpu, i;
270 unsigned long sum;
271 static unsigned long cpu_totals[NR_CPUS];
272
273 /* FIXME: do we care that there's no locking here? */
274 sum = 0;
275 for_each_online_cpu(cpu) {
276 cpu_totals[cpu] = 0;
277 for (i = 0; i < HvLpEvent_Type_NumTypes; i++) {
278 cpu_totals[cpu] += per_cpu(hvlpevent_counts, cpu)[i];
279 }
280 sum += cpu_totals[cpu];
281 }
282
283 seq_printf(m, "LpEventQueue 0\n");
284 seq_printf(m, " events processed:\t%lu\n", sum);
285
286 for (i = 0; i < HvLpEvent_Type_NumTypes; ++i) {
287 sum = 0;
288 for_each_online_cpu(cpu) {
289 sum += per_cpu(hvlpevent_counts, cpu)[i];
290 }
291
292 seq_printf(m, " %-20s %10lu\n", event_types[i], sum);
293 }
294
295 seq_printf(m, "\n events processed by processor:\n");
296
297 for_each_online_cpu(cpu) {
298 seq_printf(m, " CPU%02d %10lu\n", cpu, cpu_totals[cpu]);
299 }
300
301 return 0;
302}
303
304static int proc_lpevents_open(struct inode *inode, struct file *file)
305{
306 return single_open(file, proc_lpevents_show, NULL);
307}
308
309static struct file_operations proc_lpevents_operations = {
310 .open = proc_lpevents_open,
311 .read = seq_read,
312 .llseek = seq_lseek,
313 .release = single_release,
314};
315
316static int __init proc_lpevents_init(void)
317{
318 struct proc_dir_entry *e;
319
320 e = create_proc_entry("iSeries/lpevents", S_IFREG|S_IRUGO, NULL);
321 if (e)
322 e->proc_fops = &proc_lpevents_operations;
323
324 return 0;
325}
326__initcall(proc_lpevents_init);
327
diff --git a/arch/powerpc/platforms/iseries/mf.c b/arch/powerpc/platforms/iseries/mf.c
new file mode 100644
index 000000000000..82f5abab9afa
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/mf.c
@@ -0,0 +1,1316 @@
1/*
2 * Copyright (C) 2001 Troy D. Armstrong IBM Corporation
3 * Copyright (C) 2004-2005 Stephen Rothwell IBM Corporation
4 *
5 * This modules exists as an interface between a Linux secondary partition
6 * running on an iSeries and the primary partition's Virtual Service
7 * Processor (VSP) object. The VSP has final authority over powering on/off
8 * all partitions in the iSeries. It also provides miscellaneous low-level
9 * machine facility type operations.
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include <linux/types.h>
28#include <linux/errno.h>
29#include <linux/kernel.h>
30#include <linux/init.h>
31#include <linux/completion.h>
32#include <linux/delay.h>
33#include <linux/dma-mapping.h>
34#include <linux/bcd.h>
35
36#include <asm/time.h>
37#include <asm/uaccess.h>
38#include <asm/paca.h>
39#include <asm/iSeries/vio.h>
40#include <asm/iSeries/mf.h>
41#include <asm/iSeries/HvLpConfig.h>
42#include <asm/iSeries/ItLpQueue.h>
43
44#include "setup.h"
45
46extern int piranha_simulator;
47
48/*
49 * This is the structure layout for the Machine Facilites LPAR event
50 * flows.
51 */
52struct vsp_cmd_data {
53 u64 token;
54 u16 cmd;
55 HvLpIndex lp_index;
56 u8 result_code;
57 u32 reserved;
58 union {
59 u64 state; /* GetStateOut */
60 u64 ipl_type; /* GetIplTypeOut, Function02SelectIplTypeIn */
61 u64 ipl_mode; /* GetIplModeOut, Function02SelectIplModeIn */
62 u64 page[4]; /* GetSrcHistoryIn */
63 u64 flag; /* GetAutoIplWhenPrimaryIplsOut,
64 SetAutoIplWhenPrimaryIplsIn,
65 WhiteButtonPowerOffIn,
66 Function08FastPowerOffIn,
67 IsSpcnRackPowerIncompleteOut */
68 struct {
69 u64 token;
70 u64 address_type;
71 u64 side;
72 u32 length;
73 u32 offset;
74 } kern; /* SetKernelImageIn, GetKernelImageIn,
75 SetKernelCmdLineIn, GetKernelCmdLineIn */
76 u32 length_out; /* GetKernelImageOut, GetKernelCmdLineOut */
77 u8 reserved[80];
78 } sub_data;
79};
80
81struct vsp_rsp_data {
82 struct completion com;
83 struct vsp_cmd_data *response;
84};
85
86struct alloc_data {
87 u16 size;
88 u16 type;
89 u32 count;
90 u16 reserved1;
91 u8 reserved2;
92 HvLpIndex target_lp;
93};
94
95struct ce_msg_data;
96
97typedef void (*ce_msg_comp_hdlr)(void *token, struct ce_msg_data *vsp_cmd_rsp);
98
99struct ce_msg_comp_data {
100 ce_msg_comp_hdlr handler;
101 void *token;
102};
103
104struct ce_msg_data {
105 u8 ce_msg[12];
106 char reserved[4];
107 struct ce_msg_comp_data *completion;
108};
109
110struct io_mf_lp_event {
111 struct HvLpEvent hp_lp_event;
112 u16 subtype_result_code;
113 u16 reserved1;
114 u32 reserved2;
115 union {
116 struct alloc_data alloc;
117 struct ce_msg_data ce_msg;
118 struct vsp_cmd_data vsp_cmd;
119 } data;
120};
121
122#define subtype_data(a, b, c, d) \
123 (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
124
125/*
126 * All outgoing event traffic is kept on a FIFO queue. The first
127 * pointer points to the one that is outstanding, and all new
128 * requests get stuck on the end. Also, we keep a certain number of
129 * preallocated pending events so that we can operate very early in
130 * the boot up sequence (before kmalloc is ready).
131 */
132struct pending_event {
133 struct pending_event *next;
134 struct io_mf_lp_event event;
135 MFCompleteHandler hdlr;
136 char dma_data[72];
137 unsigned dma_data_length;
138 unsigned remote_address;
139};
140static spinlock_t pending_event_spinlock;
141static struct pending_event *pending_event_head;
142static struct pending_event *pending_event_tail;
143static struct pending_event *pending_event_avail;
144static struct pending_event pending_event_prealloc[16];
145
146/*
147 * Put a pending event onto the available queue, so it can get reused.
148 * Attention! You must have the pending_event_spinlock before calling!
149 */
150static void free_pending_event(struct pending_event *ev)
151{
152 if (ev != NULL) {
153 ev->next = pending_event_avail;
154 pending_event_avail = ev;
155 }
156}
157
158/*
159 * Enqueue the outbound event onto the stack. If the queue was
160 * empty to begin with, we must also issue it via the Hypervisor
161 * interface. There is a section of code below that will touch
162 * the first stack pointer without the protection of the pending_event_spinlock.
163 * This is OK, because we know that nobody else will be modifying
164 * the first pointer when we do this.
165 */
166static int signal_event(struct pending_event *ev)
167{
168 int rc = 0;
169 unsigned long flags;
170 int go = 1;
171 struct pending_event *ev1;
172 HvLpEvent_Rc hv_rc;
173
174 /* enqueue the event */
175 if (ev != NULL) {
176 ev->next = NULL;
177 spin_lock_irqsave(&pending_event_spinlock, flags);
178 if (pending_event_head == NULL)
179 pending_event_head = ev;
180 else {
181 go = 0;
182 pending_event_tail->next = ev;
183 }
184 pending_event_tail = ev;
185 spin_unlock_irqrestore(&pending_event_spinlock, flags);
186 }
187
188 /* send the event */
189 while (go) {
190 go = 0;
191
192 /* any DMA data to send beforehand? */
193 if (pending_event_head->dma_data_length > 0)
194 HvCallEvent_dmaToSp(pending_event_head->dma_data,
195 pending_event_head->remote_address,
196 pending_event_head->dma_data_length,
197 HvLpDma_Direction_LocalToRemote);
198
199 hv_rc = HvCallEvent_signalLpEvent(
200 &pending_event_head->event.hp_lp_event);
201 if (hv_rc != HvLpEvent_Rc_Good) {
202 printk(KERN_ERR "mf.c: HvCallEvent_signalLpEvent() "
203 "failed with %d\n", (int)hv_rc);
204
205 spin_lock_irqsave(&pending_event_spinlock, flags);
206 ev1 = pending_event_head;
207 pending_event_head = pending_event_head->next;
208 if (pending_event_head != NULL)
209 go = 1;
210 spin_unlock_irqrestore(&pending_event_spinlock, flags);
211
212 if (ev1 == ev)
213 rc = -EIO;
214 else if (ev1->hdlr != NULL)
215 (*ev1->hdlr)((void *)ev1->event.hp_lp_event.xCorrelationToken, -EIO);
216
217 spin_lock_irqsave(&pending_event_spinlock, flags);
218 free_pending_event(ev1);
219 spin_unlock_irqrestore(&pending_event_spinlock, flags);
220 }
221 }
222
223 return rc;
224}
225
226/*
227 * Allocate a new pending_event structure, and initialize it.
228 */
229static struct pending_event *new_pending_event(void)
230{
231 struct pending_event *ev = NULL;
232 HvLpIndex primary_lp = HvLpConfig_getPrimaryLpIndex();
233 unsigned long flags;
234 struct HvLpEvent *hev;
235
236 spin_lock_irqsave(&pending_event_spinlock, flags);
237 if (pending_event_avail != NULL) {
238 ev = pending_event_avail;
239 pending_event_avail = pending_event_avail->next;
240 }
241 spin_unlock_irqrestore(&pending_event_spinlock, flags);
242 if (ev == NULL) {
243 ev = kmalloc(sizeof(struct pending_event), GFP_ATOMIC);
244 if (ev == NULL) {
245 printk(KERN_ERR "mf.c: unable to kmalloc %ld bytes\n",
246 sizeof(struct pending_event));
247 return NULL;
248 }
249 }
250 memset(ev, 0, sizeof(struct pending_event));
251 hev = &ev->event.hp_lp_event;
252 hev->xFlags.xValid = 1;
253 hev->xFlags.xAckType = HvLpEvent_AckType_ImmediateAck;
254 hev->xFlags.xAckInd = HvLpEvent_AckInd_DoAck;
255 hev->xFlags.xFunction = HvLpEvent_Function_Int;
256 hev->xType = HvLpEvent_Type_MachineFac;
257 hev->xSourceLp = HvLpConfig_getLpIndex();
258 hev->xTargetLp = primary_lp;
259 hev->xSizeMinus1 = sizeof(ev->event) - 1;
260 hev->xRc = HvLpEvent_Rc_Good;
261 hev->xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primary_lp,
262 HvLpEvent_Type_MachineFac);
263 hev->xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primary_lp,
264 HvLpEvent_Type_MachineFac);
265
266 return ev;
267}
268
269static int signal_vsp_instruction(struct vsp_cmd_data *vsp_cmd)
270{
271 struct pending_event *ev = new_pending_event();
272 int rc;
273 struct vsp_rsp_data response;
274
275 if (ev == NULL)
276 return -ENOMEM;
277
278 init_completion(&response.com);
279 response.response = vsp_cmd;
280 ev->event.hp_lp_event.xSubtype = 6;
281 ev->event.hp_lp_event.x.xSubtypeData =
282 subtype_data('M', 'F', 'V', 'I');
283 ev->event.data.vsp_cmd.token = (u64)&response;
284 ev->event.data.vsp_cmd.cmd = vsp_cmd->cmd;
285 ev->event.data.vsp_cmd.lp_index = HvLpConfig_getLpIndex();
286 ev->event.data.vsp_cmd.result_code = 0xFF;
287 ev->event.data.vsp_cmd.reserved = 0;
288 memcpy(&(ev->event.data.vsp_cmd.sub_data),
289 &(vsp_cmd->sub_data), sizeof(vsp_cmd->sub_data));
290 mb();
291
292 rc = signal_event(ev);
293 if (rc == 0)
294 wait_for_completion(&response.com);
295 return rc;
296}
297
298
299/*
300 * Send a 12-byte CE message to the primary partition VSP object
301 */
302static int signal_ce_msg(char *ce_msg, struct ce_msg_comp_data *completion)
303{
304 struct pending_event *ev = new_pending_event();
305
306 if (ev == NULL)
307 return -ENOMEM;
308
309 ev->event.hp_lp_event.xSubtype = 0;
310 ev->event.hp_lp_event.x.xSubtypeData =
311 subtype_data('M', 'F', 'C', 'E');
312 memcpy(ev->event.data.ce_msg.ce_msg, ce_msg, 12);
313 ev->event.data.ce_msg.completion = completion;
314 return signal_event(ev);
315}
316
317/*
318 * Send a 12-byte CE message (with no data) to the primary partition VSP object
319 */
320static int signal_ce_msg_simple(u8 ce_op, struct ce_msg_comp_data *completion)
321{
322 u8 ce_msg[12];
323
324 memset(ce_msg, 0, sizeof(ce_msg));
325 ce_msg[3] = ce_op;
326 return signal_ce_msg(ce_msg, completion);
327}
328
329/*
330 * Send a 12-byte CE message and DMA data to the primary partition VSP object
331 */
332static int dma_and_signal_ce_msg(char *ce_msg,
333 struct ce_msg_comp_data *completion, void *dma_data,
334 unsigned dma_data_length, unsigned remote_address)
335{
336 struct pending_event *ev = new_pending_event();
337
338 if (ev == NULL)
339 return -ENOMEM;
340
341 ev->event.hp_lp_event.xSubtype = 0;
342 ev->event.hp_lp_event.x.xSubtypeData =
343 subtype_data('M', 'F', 'C', 'E');
344 memcpy(ev->event.data.ce_msg.ce_msg, ce_msg, 12);
345 ev->event.data.ce_msg.completion = completion;
346 memcpy(ev->dma_data, dma_data, dma_data_length);
347 ev->dma_data_length = dma_data_length;
348 ev->remote_address = remote_address;
349 return signal_event(ev);
350}
351
352/*
353 * Initiate a nice (hopefully) shutdown of Linux. We simply are
354 * going to try and send the init process a SIGINT signal. If
355 * this fails (why?), we'll simply force it off in a not-so-nice
356 * manner.
357 */
358static int shutdown(void)
359{
360 int rc = kill_proc(1, SIGINT, 1);
361
362 if (rc) {
363 printk(KERN_ALERT "mf.c: SIGINT to init failed (%d), "
364 "hard shutdown commencing\n", rc);
365 mf_power_off();
366 } else
367 printk(KERN_INFO "mf.c: init has been successfully notified "
368 "to proceed with shutdown\n");
369 return rc;
370}
371
372/*
373 * The primary partition VSP object is sending us a new
374 * event flow. Handle it...
375 */
376static void handle_int(struct io_mf_lp_event *event)
377{
378 struct ce_msg_data *ce_msg_data;
379 struct ce_msg_data *pce_msg_data;
380 unsigned long flags;
381 struct pending_event *pev;
382
383 /* ack the interrupt */
384 event->hp_lp_event.xRc = HvLpEvent_Rc_Good;
385 HvCallEvent_ackLpEvent(&event->hp_lp_event);
386
387 /* process interrupt */
388 switch (event->hp_lp_event.xSubtype) {
389 case 0: /* CE message */
390 ce_msg_data = &event->data.ce_msg;
391 switch (ce_msg_data->ce_msg[3]) {
392 case 0x5B: /* power control notification */
393 if ((ce_msg_data->ce_msg[5] & 0x20) != 0) {
394 printk(KERN_INFO "mf.c: Commencing partition shutdown\n");
395 if (shutdown() == 0)
396 signal_ce_msg_simple(0xDB, NULL);
397 }
398 break;
399 case 0xC0: /* get time */
400 spin_lock_irqsave(&pending_event_spinlock, flags);
401 pev = pending_event_head;
402 if (pev != NULL)
403 pending_event_head = pending_event_head->next;
404 spin_unlock_irqrestore(&pending_event_spinlock, flags);
405 if (pev == NULL)
406 break;
407 pce_msg_data = &pev->event.data.ce_msg;
408 if (pce_msg_data->ce_msg[3] != 0x40)
409 break;
410 if (pce_msg_data->completion != NULL) {
411 ce_msg_comp_hdlr handler =
412 pce_msg_data->completion->handler;
413 void *token = pce_msg_data->completion->token;
414
415 if (handler != NULL)
416 (*handler)(token, ce_msg_data);
417 }
418 spin_lock_irqsave(&pending_event_spinlock, flags);
419 free_pending_event(pev);
420 spin_unlock_irqrestore(&pending_event_spinlock, flags);
421 /* send next waiting event */
422 if (pending_event_head != NULL)
423 signal_event(NULL);
424 break;
425 }
426 break;
427 case 1: /* IT sys shutdown */
428 printk(KERN_INFO "mf.c: Commencing system shutdown\n");
429 shutdown();
430 break;
431 }
432}
433
434/*
435 * The primary partition VSP object is acknowledging the receipt
436 * of a flow we sent to them. If there are other flows queued
437 * up, we must send another one now...
438 */
439static void handle_ack(struct io_mf_lp_event *event)
440{
441 unsigned long flags;
442 struct pending_event *two = NULL;
443 unsigned long free_it = 0;
444 struct ce_msg_data *ce_msg_data;
445 struct ce_msg_data *pce_msg_data;
446 struct vsp_rsp_data *rsp;
447
448 /* handle current event */
449 if (pending_event_head == NULL) {
450 printk(KERN_ERR "mf.c: stack empty for receiving ack\n");
451 return;
452 }
453
454 switch (event->hp_lp_event.xSubtype) {
455 case 0: /* CE msg */
456 ce_msg_data = &event->data.ce_msg;
457 if (ce_msg_data->ce_msg[3] != 0x40) {
458 free_it = 1;
459 break;
460 }
461 if (ce_msg_data->ce_msg[2] == 0)
462 break;
463 free_it = 1;
464 pce_msg_data = &pending_event_head->event.data.ce_msg;
465 if (pce_msg_data->completion != NULL) {
466 ce_msg_comp_hdlr handler =
467 pce_msg_data->completion->handler;
468 void *token = pce_msg_data->completion->token;
469
470 if (handler != NULL)
471 (*handler)(token, ce_msg_data);
472 }
473 break;
474 case 4: /* allocate */
475 case 5: /* deallocate */
476 if (pending_event_head->hdlr != NULL)
477 (*pending_event_head->hdlr)((void *)event->hp_lp_event.xCorrelationToken, event->data.alloc.count);
478 free_it = 1;
479 break;
480 case 6:
481 free_it = 1;
482 rsp = (struct vsp_rsp_data *)event->data.vsp_cmd.token;
483 if (rsp == NULL) {
484 printk(KERN_ERR "mf.c: no rsp\n");
485 break;
486 }
487 if (rsp->response != NULL)
488 memcpy(rsp->response, &event->data.vsp_cmd,
489 sizeof(event->data.vsp_cmd));
490 complete(&rsp->com);
491 break;
492 }
493
494 /* remove from queue */
495 spin_lock_irqsave(&pending_event_spinlock, flags);
496 if ((pending_event_head != NULL) && (free_it == 1)) {
497 struct pending_event *oldHead = pending_event_head;
498
499 pending_event_head = pending_event_head->next;
500 two = pending_event_head;
501 free_pending_event(oldHead);
502 }
503 spin_unlock_irqrestore(&pending_event_spinlock, flags);
504
505 /* send next waiting event */
506 if (two != NULL)
507 signal_event(NULL);
508}
509
510/*
511 * This is the generic event handler we are registering with
512 * the Hypervisor. Ensure the flows are for us, and then
513 * parse it enough to know if it is an interrupt or an
514 * acknowledge.
515 */
516static void hv_handler(struct HvLpEvent *event, struct pt_regs *regs)
517{
518 if ((event != NULL) && (event->xType == HvLpEvent_Type_MachineFac)) {
519 switch(event->xFlags.xFunction) {
520 case HvLpEvent_Function_Ack:
521 handle_ack((struct io_mf_lp_event *)event);
522 break;
523 case HvLpEvent_Function_Int:
524 handle_int((struct io_mf_lp_event *)event);
525 break;
526 default:
527 printk(KERN_ERR "mf.c: non ack/int event received\n");
528 break;
529 }
530 } else
531 printk(KERN_ERR "mf.c: alien event received\n");
532}
533
534/*
535 * Global kernel interface to allocate and seed events into the
536 * Hypervisor.
537 */
538void mf_allocate_lp_events(HvLpIndex target_lp, HvLpEvent_Type type,
539 unsigned size, unsigned count, MFCompleteHandler hdlr,
540 void *user_token)
541{
542 struct pending_event *ev = new_pending_event();
543 int rc;
544
545 if (ev == NULL) {
546 rc = -ENOMEM;
547 } else {
548 ev->event.hp_lp_event.xSubtype = 4;
549 ev->event.hp_lp_event.xCorrelationToken = (u64)user_token;
550 ev->event.hp_lp_event.x.xSubtypeData =
551 subtype_data('M', 'F', 'M', 'A');
552 ev->event.data.alloc.target_lp = target_lp;
553 ev->event.data.alloc.type = type;
554 ev->event.data.alloc.size = size;
555 ev->event.data.alloc.count = count;
556 ev->hdlr = hdlr;
557 rc = signal_event(ev);
558 }
559 if ((rc != 0) && (hdlr != NULL))
560 (*hdlr)(user_token, rc);
561}
562EXPORT_SYMBOL(mf_allocate_lp_events);
563
564/*
565 * Global kernel interface to unseed and deallocate events already in
566 * Hypervisor.
567 */
568void mf_deallocate_lp_events(HvLpIndex target_lp, HvLpEvent_Type type,
569 unsigned count, MFCompleteHandler hdlr, void *user_token)
570{
571 struct pending_event *ev = new_pending_event();
572 int rc;
573
574 if (ev == NULL)
575 rc = -ENOMEM;
576 else {
577 ev->event.hp_lp_event.xSubtype = 5;
578 ev->event.hp_lp_event.xCorrelationToken = (u64)user_token;
579 ev->event.hp_lp_event.x.xSubtypeData =
580 subtype_data('M', 'F', 'M', 'D');
581 ev->event.data.alloc.target_lp = target_lp;
582 ev->event.data.alloc.type = type;
583 ev->event.data.alloc.count = count;
584 ev->hdlr = hdlr;
585 rc = signal_event(ev);
586 }
587 if ((rc != 0) && (hdlr != NULL))
588 (*hdlr)(user_token, rc);
589}
590EXPORT_SYMBOL(mf_deallocate_lp_events);
591
592/*
593 * Global kernel interface to tell the VSP object in the primary
594 * partition to power this partition off.
595 */
596void mf_power_off(void)
597{
598 printk(KERN_INFO "mf.c: Down it goes...\n");
599 signal_ce_msg_simple(0x4d, NULL);
600 for (;;)
601 ;
602}
603
604/*
605 * Global kernel interface to tell the VSP object in the primary
606 * partition to reboot this partition.
607 */
608void mf_reboot(void)
609{
610 printk(KERN_INFO "mf.c: Preparing to bounce...\n");
611 signal_ce_msg_simple(0x4e, NULL);
612 for (;;)
613 ;
614}
615
616/*
617 * Display a single word SRC onto the VSP control panel.
618 */
619void mf_display_src(u32 word)
620{
621 u8 ce[12];
622
623 memset(ce, 0, sizeof(ce));
624 ce[3] = 0x4a;
625 ce[7] = 0x01;
626 ce[8] = word >> 24;
627 ce[9] = word >> 16;
628 ce[10] = word >> 8;
629 ce[11] = word;
630 signal_ce_msg(ce, NULL);
631}
632
633/*
634 * Display a single word SRC of the form "PROGXXXX" on the VSP control panel.
635 */
636void mf_display_progress(u16 value)
637{
638 u8 ce[12];
639 u8 src[72];
640
641 memcpy(ce, "\x00\x00\x04\x4A\x00\x00\x00\x48\x00\x00\x00\x00", 12);
642 memcpy(src, "\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
643 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
644 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
645 "\x00\x00\x00\x00PROGxxxx ",
646 72);
647 src[6] = value >> 8;
648 src[7] = value & 255;
649 src[44] = "0123456789ABCDEF"[(value >> 12) & 15];
650 src[45] = "0123456789ABCDEF"[(value >> 8) & 15];
651 src[46] = "0123456789ABCDEF"[(value >> 4) & 15];
652 src[47] = "0123456789ABCDEF"[value & 15];
653 dma_and_signal_ce_msg(ce, NULL, src, sizeof(src), 9 * 64 * 1024);
654}
655
656/*
657 * Clear the VSP control panel. Used to "erase" an SRC that was
658 * previously displayed.
659 */
660void mf_clear_src(void)
661{
662 signal_ce_msg_simple(0x4b, NULL);
663}
664
665/*
666 * Initialization code here.
667 */
668void mf_init(void)
669{
670 int i;
671
672 /* initialize */
673 spin_lock_init(&pending_event_spinlock);
674 for (i = 0;
675 i < sizeof(pending_event_prealloc) / sizeof(*pending_event_prealloc);
676 ++i)
677 free_pending_event(&pending_event_prealloc[i]);
678 HvLpEvent_registerHandler(HvLpEvent_Type_MachineFac, &hv_handler);
679
680 /* virtual continue ack */
681 signal_ce_msg_simple(0x57, NULL);
682
683 /* initialization complete */
684 printk(KERN_NOTICE "mf.c: iSeries Linux LPAR Machine Facilities "
685 "initialized\n");
686}
687
688struct rtc_time_data {
689 struct completion com;
690 struct ce_msg_data ce_msg;
691 int rc;
692};
693
694static void get_rtc_time_complete(void *token, struct ce_msg_data *ce_msg)
695{
696 struct rtc_time_data *rtc = token;
697
698 memcpy(&rtc->ce_msg, ce_msg, sizeof(rtc->ce_msg));
699 rtc->rc = 0;
700 complete(&rtc->com);
701}
702
703static int rtc_set_tm(int rc, u8 *ce_msg, struct rtc_time *tm)
704{
705 tm->tm_wday = 0;
706 tm->tm_yday = 0;
707 tm->tm_isdst = 0;
708 if (rc) {
709 tm->tm_sec = 0;
710 tm->tm_min = 0;
711 tm->tm_hour = 0;
712 tm->tm_mday = 15;
713 tm->tm_mon = 5;
714 tm->tm_year = 52;
715 return rc;
716 }
717
718 if ((ce_msg[2] == 0xa9) ||
719 (ce_msg[2] == 0xaf)) {
720 /* TOD clock is not set */
721 tm->tm_sec = 1;
722 tm->tm_min = 1;
723 tm->tm_hour = 1;
724 tm->tm_mday = 10;
725 tm->tm_mon = 8;
726 tm->tm_year = 71;
727 mf_set_rtc(tm);
728 }
729 {
730 u8 year = ce_msg[5];
731 u8 sec = ce_msg[6];
732 u8 min = ce_msg[7];
733 u8 hour = ce_msg[8];
734 u8 day = ce_msg[10];
735 u8 mon = ce_msg[11];
736
737 BCD_TO_BIN(sec);
738 BCD_TO_BIN(min);
739 BCD_TO_BIN(hour);
740 BCD_TO_BIN(day);
741 BCD_TO_BIN(mon);
742 BCD_TO_BIN(year);
743
744 if (year <= 69)
745 year += 100;
746
747 tm->tm_sec = sec;
748 tm->tm_min = min;
749 tm->tm_hour = hour;
750 tm->tm_mday = day;
751 tm->tm_mon = mon;
752 tm->tm_year = year;
753 }
754
755 return 0;
756}
757
758int mf_get_rtc(struct rtc_time *tm)
759{
760 struct ce_msg_comp_data ce_complete;
761 struct rtc_time_data rtc_data;
762 int rc;
763
764 memset(&ce_complete, 0, sizeof(ce_complete));
765 memset(&rtc_data, 0, sizeof(rtc_data));
766 init_completion(&rtc_data.com);
767 ce_complete.handler = &get_rtc_time_complete;
768 ce_complete.token = &rtc_data;
769 rc = signal_ce_msg_simple(0x40, &ce_complete);
770 if (rc)
771 return rc;
772 wait_for_completion(&rtc_data.com);
773 return rtc_set_tm(rtc_data.rc, rtc_data.ce_msg.ce_msg, tm);
774}
775
776struct boot_rtc_time_data {
777 int busy;
778 struct ce_msg_data ce_msg;
779 int rc;
780};
781
782static void get_boot_rtc_time_complete(void *token, struct ce_msg_data *ce_msg)
783{
784 struct boot_rtc_time_data *rtc = token;
785
786 memcpy(&rtc->ce_msg, ce_msg, sizeof(rtc->ce_msg));
787 rtc->rc = 0;
788 rtc->busy = 0;
789}
790
791int mf_get_boot_rtc(struct rtc_time *tm)
792{
793 struct ce_msg_comp_data ce_complete;
794 struct boot_rtc_time_data rtc_data;
795 int rc;
796
797 memset(&ce_complete, 0, sizeof(ce_complete));
798 memset(&rtc_data, 0, sizeof(rtc_data));
799 rtc_data.busy = 1;
800 ce_complete.handler = &get_boot_rtc_time_complete;
801 ce_complete.token = &rtc_data;
802 rc = signal_ce_msg_simple(0x40, &ce_complete);
803 if (rc)
804 return rc;
805 /* We need to poll here as we are not yet taking interrupts */
806 while (rtc_data.busy) {
807 if (hvlpevent_is_pending())
808 process_hvlpevents(NULL);
809 }
810 return rtc_set_tm(rtc_data.rc, rtc_data.ce_msg.ce_msg, tm);
811}
812
813int mf_set_rtc(struct rtc_time *tm)
814{
815 char ce_time[12];
816 u8 day, mon, hour, min, sec, y1, y2;
817 unsigned year;
818
819 year = 1900 + tm->tm_year;
820 y1 = year / 100;
821 y2 = year % 100;
822
823 sec = tm->tm_sec;
824 min = tm->tm_min;
825 hour = tm->tm_hour;
826 day = tm->tm_mday;
827 mon = tm->tm_mon + 1;
828
829 BIN_TO_BCD(sec);
830 BIN_TO_BCD(min);
831 BIN_TO_BCD(hour);
832 BIN_TO_BCD(mon);
833 BIN_TO_BCD(day);
834 BIN_TO_BCD(y1);
835 BIN_TO_BCD(y2);
836
837 memset(ce_time, 0, sizeof(ce_time));
838 ce_time[3] = 0x41;
839 ce_time[4] = y1;
840 ce_time[5] = y2;
841 ce_time[6] = sec;
842 ce_time[7] = min;
843 ce_time[8] = hour;
844 ce_time[10] = day;
845 ce_time[11] = mon;
846
847 return signal_ce_msg(ce_time, NULL);
848}
849
850#ifdef CONFIG_PROC_FS
851
852static int proc_mf_dump_cmdline(char *page, char **start, off_t off,
853 int count, int *eof, void *data)
854{
855 int len;
856 char *p;
857 struct vsp_cmd_data vsp_cmd;
858 int rc;
859 dma_addr_t dma_addr;
860
861 /* The HV appears to return no more than 256 bytes of command line */
862 if (off >= 256)
863 return 0;
864 if ((off + count) > 256)
865 count = 256 - off;
866
867 dma_addr = dma_map_single(iSeries_vio_dev, page, off + count,
868 DMA_FROM_DEVICE);
869 if (dma_mapping_error(dma_addr))
870 return -ENOMEM;
871 memset(page, 0, off + count);
872 memset(&vsp_cmd, 0, sizeof(vsp_cmd));
873 vsp_cmd.cmd = 33;
874 vsp_cmd.sub_data.kern.token = dma_addr;
875 vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex;
876 vsp_cmd.sub_data.kern.side = (u64)data;
877 vsp_cmd.sub_data.kern.length = off + count;
878 mb();
879 rc = signal_vsp_instruction(&vsp_cmd);
880 dma_unmap_single(iSeries_vio_dev, dma_addr, off + count,
881 DMA_FROM_DEVICE);
882 if (rc)
883 return rc;
884 if (vsp_cmd.result_code != 0)
885 return -ENOMEM;
886 p = page;
887 len = 0;
888 while (len < (off + count)) {
889 if ((*p == '\0') || (*p == '\n')) {
890 if (*p == '\0')
891 *p = '\n';
892 p++;
893 len++;
894 *eof = 1;
895 break;
896 }
897 p++;
898 len++;
899 }
900
901 if (len < off) {
902 *eof = 1;
903 len = 0;
904 }
905 return len;
906}
907
908#if 0
909static int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side)
910{
911 struct vsp_cmd_data vsp_cmd;
912 int rc;
913 int len = *size;
914 dma_addr_t dma_addr;
915
916 dma_addr = dma_map_single(iSeries_vio_dev, buffer, len,
917 DMA_FROM_DEVICE);
918 memset(buffer, 0, len);
919 memset(&vsp_cmd, 0, sizeof(vsp_cmd));
920 vsp_cmd.cmd = 32;
921 vsp_cmd.sub_data.kern.token = dma_addr;
922 vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex;
923 vsp_cmd.sub_data.kern.side = side;
924 vsp_cmd.sub_data.kern.offset = offset;
925 vsp_cmd.sub_data.kern.length = len;
926 mb();
927 rc = signal_vsp_instruction(&vsp_cmd);
928 if (rc == 0) {
929 if (vsp_cmd.result_code == 0)
930 *size = vsp_cmd.sub_data.length_out;
931 else
932 rc = -ENOMEM;
933 }
934
935 dma_unmap_single(iSeries_vio_dev, dma_addr, len, DMA_FROM_DEVICE);
936
937 return rc;
938}
939
940static int proc_mf_dump_vmlinux(char *page, char **start, off_t off,
941 int count, int *eof, void *data)
942{
943 int sizeToGet = count;
944
945 if (!capable(CAP_SYS_ADMIN))
946 return -EACCES;
947
948 if (mf_getVmlinuxChunk(page, &sizeToGet, off, (u64)data) == 0) {
949 if (sizeToGet != 0) {
950 *start = page + off;
951 return sizeToGet;
952 }
953 *eof = 1;
954 return 0;
955 }
956 *eof = 1;
957 return 0;
958}
959#endif
960
961static int proc_mf_dump_side(char *page, char **start, off_t off,
962 int count, int *eof, void *data)
963{
964 int len;
965 char mf_current_side = ' ';
966 struct vsp_cmd_data vsp_cmd;
967
968 memset(&vsp_cmd, 0, sizeof(vsp_cmd));
969 vsp_cmd.cmd = 2;
970 vsp_cmd.sub_data.ipl_type = 0;
971 mb();
972
973 if (signal_vsp_instruction(&vsp_cmd) == 0) {
974 if (vsp_cmd.result_code == 0) {
975 switch (vsp_cmd.sub_data.ipl_type) {
976 case 0: mf_current_side = 'A';
977 break;
978 case 1: mf_current_side = 'B';
979 break;
980 case 2: mf_current_side = 'C';
981 break;
982 default: mf_current_side = 'D';
983 break;
984 }
985 }
986 }
987
988 len = sprintf(page, "%c\n", mf_current_side);
989
990 if (len <= (off + count))
991 *eof = 1;
992 *start = page + off;
993 len -= off;
994 if (len > count)
995 len = count;
996 if (len < 0)
997 len = 0;
998 return len;
999}
1000
1001static int proc_mf_change_side(struct file *file, const char __user *buffer,
1002 unsigned long count, void *data)
1003{
1004 char side;
1005 u64 newSide;
1006 struct vsp_cmd_data vsp_cmd;
1007
1008 if (!capable(CAP_SYS_ADMIN))
1009 return -EACCES;
1010
1011 if (count == 0)
1012 return 0;
1013
1014 if (get_user(side, buffer))
1015 return -EFAULT;
1016
1017 switch (side) {
1018 case 'A': newSide = 0;
1019 break;
1020 case 'B': newSide = 1;
1021 break;
1022 case 'C': newSide = 2;
1023 break;
1024 case 'D': newSide = 3;
1025 break;
1026 default:
1027 printk(KERN_ERR "mf_proc.c: proc_mf_change_side: invalid side\n");
1028 return -EINVAL;
1029 }
1030
1031 memset(&vsp_cmd, 0, sizeof(vsp_cmd));
1032 vsp_cmd.sub_data.ipl_type = newSide;
1033 vsp_cmd.cmd = 10;
1034
1035 (void)signal_vsp_instruction(&vsp_cmd);
1036
1037 return count;
1038}
1039
1040#if 0
1041static void mf_getSrcHistory(char *buffer, int size)
1042{
1043 struct IplTypeReturnStuff return_stuff;
1044 struct pending_event *ev = new_pending_event();
1045 int rc = 0;
1046 char *pages[4];
1047
1048 pages[0] = kmalloc(4096, GFP_ATOMIC);
1049 pages[1] = kmalloc(4096, GFP_ATOMIC);
1050 pages[2] = kmalloc(4096, GFP_ATOMIC);
1051 pages[3] = kmalloc(4096, GFP_ATOMIC);
1052 if ((ev == NULL) || (pages[0] == NULL) || (pages[1] == NULL)
1053 || (pages[2] == NULL) || (pages[3] == NULL))
1054 return -ENOMEM;
1055
1056 return_stuff.xType = 0;
1057 return_stuff.xRc = 0;
1058 return_stuff.xDone = 0;
1059 ev->event.hp_lp_event.xSubtype = 6;
1060 ev->event.hp_lp_event.x.xSubtypeData =
1061 subtype_data('M', 'F', 'V', 'I');
1062 ev->event.data.vsp_cmd.xEvent = &return_stuff;
1063 ev->event.data.vsp_cmd.cmd = 4;
1064 ev->event.data.vsp_cmd.lp_index = HvLpConfig_getLpIndex();
1065 ev->event.data.vsp_cmd.result_code = 0xFF;
1066 ev->event.data.vsp_cmd.reserved = 0;
1067 ev->event.data.vsp_cmd.sub_data.page[0] = ISERIES_HV_ADDR(pages[0]);
1068 ev->event.data.vsp_cmd.sub_data.page[1] = ISERIES_HV_ADDR(pages[1]);
1069 ev->event.data.vsp_cmd.sub_data.page[2] = ISERIES_HV_ADDR(pages[2]);
1070 ev->event.data.vsp_cmd.sub_data.page[3] = ISERIES_HV_ADDR(pages[3]);
1071 mb();
1072 if (signal_event(ev) != 0)
1073 return;
1074
1075 while (return_stuff.xDone != 1)
1076 udelay(10);
1077 if (return_stuff.xRc == 0)
1078 memcpy(buffer, pages[0], size);
1079 kfree(pages[0]);
1080 kfree(pages[1]);
1081 kfree(pages[2]);
1082 kfree(pages[3]);
1083}
1084#endif
1085
1086static int proc_mf_dump_src(char *page, char **start, off_t off,
1087 int count, int *eof, void *data)
1088{
1089#if 0
1090 int len;
1091
1092 mf_getSrcHistory(page, count);
1093 len = count;
1094 len -= off;
1095 if (len < count) {
1096 *eof = 1;
1097 if (len <= 0)
1098 return 0;
1099 } else
1100 len = count;
1101 *start = page + off;
1102 return len;
1103#else
1104 return 0;
1105#endif
1106}
1107
1108static int proc_mf_change_src(struct file *file, const char __user *buffer,
1109 unsigned long count, void *data)
1110{
1111 char stkbuf[10];
1112
1113 if (!capable(CAP_SYS_ADMIN))
1114 return -EACCES;
1115
1116 if ((count < 4) && (count != 1)) {
1117 printk(KERN_ERR "mf_proc: invalid src\n");
1118 return -EINVAL;
1119 }
1120
1121 if (count > (sizeof(stkbuf) - 1))
1122 count = sizeof(stkbuf) - 1;
1123 if (copy_from_user(stkbuf, buffer, count))
1124 return -EFAULT;
1125
1126 if ((count == 1) && (*stkbuf == '\0'))
1127 mf_clear_src();
1128 else
1129 mf_display_src(*(u32 *)stkbuf);
1130
1131 return count;
1132}
1133
1134static int proc_mf_change_cmdline(struct file *file, const char __user *buffer,
1135 unsigned long count, void *data)
1136{
1137 struct vsp_cmd_data vsp_cmd;
1138 dma_addr_t dma_addr;
1139 char *page;
1140 int ret = -EACCES;
1141
1142 if (!capable(CAP_SYS_ADMIN))
1143 goto out;
1144
1145 dma_addr = 0;
1146 page = dma_alloc_coherent(iSeries_vio_dev, count, &dma_addr,
1147 GFP_ATOMIC);
1148 ret = -ENOMEM;
1149 if (page == NULL)
1150 goto out;
1151
1152 ret = -EFAULT;
1153 if (copy_from_user(page, buffer, count))
1154 goto out_free;
1155
1156 memset(&vsp_cmd, 0, sizeof(vsp_cmd));
1157 vsp_cmd.cmd = 31;
1158 vsp_cmd.sub_data.kern.token = dma_addr;
1159 vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex;
1160 vsp_cmd.sub_data.kern.side = (u64)data;
1161 vsp_cmd.sub_data.kern.length = count;
1162 mb();
1163 (void)signal_vsp_instruction(&vsp_cmd);
1164 ret = count;
1165
1166out_free:
1167 dma_free_coherent(iSeries_vio_dev, count, page, dma_addr);
1168out:
1169 return ret;
1170}
1171
1172static ssize_t proc_mf_change_vmlinux(struct file *file,
1173 const char __user *buf,
1174 size_t count, loff_t *ppos)
1175{
1176 struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
1177 ssize_t rc;
1178 dma_addr_t dma_addr;
1179 char *page;
1180 struct vsp_cmd_data vsp_cmd;
1181
1182 rc = -EACCES;
1183 if (!capable(CAP_SYS_ADMIN))
1184 goto out;
1185
1186 dma_addr = 0;
1187 page = dma_alloc_coherent(iSeries_vio_dev, count, &dma_addr,
1188 GFP_ATOMIC);
1189 rc = -ENOMEM;
1190 if (page == NULL) {
1191 printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n");
1192 goto out;
1193 }
1194 rc = -EFAULT;
1195 if (copy_from_user(page, buf, count))
1196 goto out_free;
1197
1198 memset(&vsp_cmd, 0, sizeof(vsp_cmd));
1199 vsp_cmd.cmd = 30;
1200 vsp_cmd.sub_data.kern.token = dma_addr;
1201 vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex;
1202 vsp_cmd.sub_data.kern.side = (u64)dp->data;
1203 vsp_cmd.sub_data.kern.offset = *ppos;
1204 vsp_cmd.sub_data.kern.length = count;
1205 mb();
1206 rc = signal_vsp_instruction(&vsp_cmd);
1207 if (rc)
1208 goto out_free;
1209 rc = -ENOMEM;
1210 if (vsp_cmd.result_code != 0)
1211 goto out_free;
1212
1213 *ppos += count;
1214 rc = count;
1215out_free:
1216 dma_free_coherent(iSeries_vio_dev, count, page, dma_addr);
1217out:
1218 return rc;
1219}
1220
1221static struct file_operations proc_vmlinux_operations = {
1222 .write = proc_mf_change_vmlinux,
1223};
1224
1225static int __init mf_proc_init(void)
1226{
1227 struct proc_dir_entry *mf_proc_root;
1228 struct proc_dir_entry *ent;
1229 struct proc_dir_entry *mf;
1230 char name[2];
1231 int i;
1232
1233 mf_proc_root = proc_mkdir("iSeries/mf", NULL);
1234 if (!mf_proc_root)
1235 return 1;
1236
1237 name[1] = '\0';
1238 for (i = 0; i < 4; i++) {
1239 name[0] = 'A' + i;
1240 mf = proc_mkdir(name, mf_proc_root);
1241 if (!mf)
1242 return 1;
1243
1244 ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf);
1245 if (!ent)
1246 return 1;
1247 ent->nlink = 1;
1248 ent->data = (void *)(long)i;
1249 ent->read_proc = proc_mf_dump_cmdline;
1250 ent->write_proc = proc_mf_change_cmdline;
1251
1252 if (i == 3) /* no vmlinux entry for 'D' */
1253 continue;
1254
1255 ent = create_proc_entry("vmlinux", S_IFREG|S_IWUSR, mf);
1256 if (!ent)
1257 return 1;
1258 ent->nlink = 1;
1259 ent->data = (void *)(long)i;
1260 ent->proc_fops = &proc_vmlinux_operations;
1261 }
1262
1263 ent = create_proc_entry("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root);
1264 if (!ent)
1265 return 1;
1266 ent->nlink = 1;
1267 ent->data = (void *)0;
1268 ent->read_proc = proc_mf_dump_side;
1269 ent->write_proc = proc_mf_change_side;
1270
1271 ent = create_proc_entry("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root);
1272 if (!ent)
1273 return 1;
1274 ent->nlink = 1;
1275 ent->data = (void *)0;
1276 ent->read_proc = proc_mf_dump_src;
1277 ent->write_proc = proc_mf_change_src;
1278
1279 return 0;
1280}
1281
1282__initcall(mf_proc_init);
1283
1284#endif /* CONFIG_PROC_FS */
1285
1286/*
1287 * Get the RTC from the virtual service processor
1288 * This requires flowing LpEvents to the primary partition
1289 */
1290void iSeries_get_rtc_time(struct rtc_time *rtc_tm)
1291{
1292 if (piranha_simulator)
1293 return;
1294
1295 mf_get_rtc(rtc_tm);
1296 rtc_tm->tm_mon--;
1297}
1298
1299/*
1300 * Set the RTC in the virtual service processor
1301 * This requires flowing LpEvents to the primary partition
1302 */
1303int iSeries_set_rtc_time(struct rtc_time *tm)
1304{
1305 mf_set_rtc(tm);
1306 return 0;
1307}
1308
1309void iSeries_get_boot_time(struct rtc_time *tm)
1310{
1311 if (piranha_simulator)
1312 return;
1313
1314 mf_get_boot_rtc(tm);
1315 tm->tm_mon -= 1;
1316}
diff --git a/arch/powerpc/platforms/iseries/misc.S b/arch/powerpc/platforms/iseries/misc.S
new file mode 100644
index 000000000000..09f14522e176
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/misc.S
@@ -0,0 +1,55 @@
1/*
2 * This file contains miscellaneous low-level functions.
3 * Copyright (C) 1995-2005 IBM Corp
4 *
5 * Largely rewritten by Cort Dougan (cort@cs.nmt.edu)
6 * and Paul Mackerras.
7 * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com)
8 * PPC64 updates by Dave Engebretsen (engebret@us.ibm.com)
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version
13 * 2 of the License, or (at your option) any later version.
14 */
15
16#include <asm/processor.h>
17#include <asm/asm-offsets.h>
18
19 .text
20
21/* unsigned long local_save_flags(void) */
22_GLOBAL(local_get_flags)
23 lbz r3,PACAPROCENABLED(r13)
24 blr
25
26/* unsigned long local_irq_disable(void) */
27_GLOBAL(local_irq_disable)
28 lbz r3,PACAPROCENABLED(r13)
29 li r4,0
30 stb r4,PACAPROCENABLED(r13)
31 blr /* Done */
32
33/* void local_irq_restore(unsigned long flags) */
34_GLOBAL(local_irq_restore)
35 lbz r5,PACAPROCENABLED(r13)
36 /* Check if things are setup the way we want _already_. */
37 cmpw 0,r3,r5
38 beqlr
39 /* are we enabling interrupts? */
40 cmpdi 0,r3,0
41 stb r3,PACAPROCENABLED(r13)
42 beqlr
43 /* Check pending interrupts */
44 /* A decrementer, IPI or PMC interrupt may have occurred
45 * while we were in the hypervisor (which enables) */
46 ld r4,PACALPPACA+LPPACAANYINT(r13)
47 cmpdi r4,0
48 beqlr
49
50 /*
51 * Handle pending interrupts in interrupt context
52 */
53 li r0,0x5555
54 sc
55 blr
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c
new file mode 100644
index 000000000000..501b1dcbfac5
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/pci.c
@@ -0,0 +1,912 @@
1/*
2 * Copyright (C) 2001 Allan Trautman, IBM Corporation
3 *
4 * iSeries specific routines for PCI.
5 *
6 * Based on code from pci.c and iSeries_pci.c 32bit
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22#include <linux/kernel.h>
23#include <linux/list.h>
24#include <linux/string.h>
25#include <linux/init.h>
26#include <linux/module.h>
27#include <linux/ide.h>
28#include <linux/pci.h>
29
30#include <asm/io.h>
31#include <asm/irq.h>
32#include <asm/prom.h>
33#include <asm/machdep.h>
34#include <asm/pci-bridge.h>
35#include <asm/ppcdebug.h>
36#include <asm/iommu.h>
37
38#include <asm/iSeries/HvCallPci.h>
39#include <asm/iSeries/HvCallXm.h>
40#include <asm/iSeries/iSeries_irq.h>
41#include <asm/iSeries/iSeries_pci.h>
42#include <asm/iSeries/mf.h>
43
44#include <asm/ppc-pci.h>
45
46extern unsigned long io_page_mask;
47
48/*
49 * Forward declares of prototypes.
50 */
51static struct device_node *find_Device_Node(int bus, int devfn);
52static void scan_PHB_slots(struct pci_controller *Phb);
53static void scan_EADS_bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel);
54static int scan_bridge_slot(HvBusNumber Bus, struct HvCallPci_BridgeInfo *Info);
55
56LIST_HEAD(iSeries_Global_Device_List);
57
58static int DeviceCount;
59
60/* Counters and control flags. */
61static long Pci_Io_Read_Count;
62static long Pci_Io_Write_Count;
63#if 0
64static long Pci_Cfg_Read_Count;
65static long Pci_Cfg_Write_Count;
66#endif
67static long Pci_Error_Count;
68
69static int Pci_Retry_Max = 3; /* Only retry 3 times */
70static int Pci_Error_Flag = 1; /* Set Retry Error on. */
71
72static struct pci_ops iSeries_pci_ops;
73
74/*
75 * Table defines
76 * Each Entry size is 4 MB * 1024 Entries = 4GB I/O address space.
77 */
78#define IOMM_TABLE_MAX_ENTRIES 1024
79#define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL
80#define BASE_IO_MEMORY 0xE000000000000000UL
81
82static unsigned long max_io_memory = 0xE000000000000000UL;
83static long current_iomm_table_entry;
84
85/*
86 * Lookup Tables.
87 */
88static struct device_node **iomm_table;
89static u8 *iobar_table;
90
91/*
92 * Static and Global variables
93 */
94static char *pci_io_text = "iSeries PCI I/O";
95static DEFINE_SPINLOCK(iomm_table_lock);
96
97/*
98 * iomm_table_initialize
99 *
100 * Allocates and initalizes the Address Translation Table and Bar
101 * Tables to get them ready for use. Must be called before any
102 * I/O space is handed out to the device BARs.
103 */
104static void iomm_table_initialize(void)
105{
106 spin_lock(&iomm_table_lock);
107 iomm_table = kmalloc(sizeof(*iomm_table) * IOMM_TABLE_MAX_ENTRIES,
108 GFP_KERNEL);
109 iobar_table = kmalloc(sizeof(*iobar_table) * IOMM_TABLE_MAX_ENTRIES,
110 GFP_KERNEL);
111 spin_unlock(&iomm_table_lock);
112 if ((iomm_table == NULL) || (iobar_table == NULL))
113 panic("PCI: I/O tables allocation failed.\n");
114}
115
116/*
117 * iomm_table_allocate_entry
118 *
119 * Adds pci_dev entry in address translation table
120 *
121 * - Allocates the number of entries required in table base on BAR
122 * size.
123 * - Allocates starting at BASE_IO_MEMORY and increases.
124 * - The size is round up to be a multiple of entry size.
125 * - CurrentIndex is incremented to keep track of the last entry.
126 * - Builds the resource entry for allocated BARs.
127 */
128static void iomm_table_allocate_entry(struct pci_dev *dev, int bar_num)
129{
130 struct resource *bar_res = &dev->resource[bar_num];
131 long bar_size = pci_resource_len(dev, bar_num);
132
133 /*
134 * No space to allocate, quick exit, skip Allocation.
135 */
136 if (bar_size == 0)
137 return;
138 /*
139 * Set Resource values.
140 */
141 spin_lock(&iomm_table_lock);
142 bar_res->name = pci_io_text;
143 bar_res->start =
144 IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry;
145 bar_res->start += BASE_IO_MEMORY;
146 bar_res->end = bar_res->start + bar_size - 1;
147 /*
148 * Allocate the number of table entries needed for BAR.
149 */
150 while (bar_size > 0 ) {
151 iomm_table[current_iomm_table_entry] = dev->sysdata;
152 iobar_table[current_iomm_table_entry] = bar_num;
153 bar_size -= IOMM_TABLE_ENTRY_SIZE;
154 ++current_iomm_table_entry;
155 }
156 max_io_memory = BASE_IO_MEMORY +
157 (IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry);
158 spin_unlock(&iomm_table_lock);
159}
160
161/*
162 * allocate_device_bars
163 *
164 * - Allocates ALL pci_dev BAR's and updates the resources with the
165 * BAR value. BARS with zero length will have the resources
166 * The HvCallPci_getBarParms is used to get the size of the BAR
167 * space. It calls iomm_table_allocate_entry to allocate
168 * each entry.
169 * - Loops through The Bar resources(0 - 5) including the ROM
170 * is resource(6).
171 */
172static void allocate_device_bars(struct pci_dev *dev)
173{
174 struct resource *bar_res;
175 int bar_num;
176
177 for (bar_num = 0; bar_num <= PCI_ROM_RESOURCE; ++bar_num) {
178 bar_res = &dev->resource[bar_num];
179 iomm_table_allocate_entry(dev, bar_num);
180 }
181}
182
183/*
184 * Log error information to system console.
185 * Filter out the device not there errors.
186 * PCI: EADs Connect Failed 0x18.58.10 Rc: 0x00xx
187 * PCI: Read Vendor Failed 0x18.58.10 Rc: 0x00xx
188 * PCI: Connect Bus Unit Failed 0x18.58.10 Rc: 0x00xx
189 */
190static void pci_Log_Error(char *Error_Text, int Bus, int SubBus,
191 int AgentId, int HvRc)
192{
193 if (HvRc == 0x0302)
194 return;
195 printk(KERN_ERR "PCI: %s Failed: 0x%02X.%02X.%02X Rc: 0x%04X",
196 Error_Text, Bus, SubBus, AgentId, HvRc);
197}
198
199/*
200 * build_device_node(u16 Bus, int SubBus, u8 DevFn)
201 */
202static struct device_node *build_device_node(HvBusNumber Bus,
203 HvSubBusNumber SubBus, int AgentId, int Function)
204{
205 struct device_node *node;
206 struct pci_dn *pdn;
207
208 PPCDBG(PPCDBG_BUSWALK,
209 "-build_device_node 0x%02X.%02X.%02X Function: %02X\n",
210 Bus, SubBus, AgentId, Function);
211
212 node = kmalloc(sizeof(struct device_node), GFP_KERNEL);
213 if (node == NULL)
214 return NULL;
215 memset(node, 0, sizeof(struct device_node));
216 pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
217 if (pdn == NULL) {
218 kfree(node);
219 return NULL;
220 }
221 node->data = pdn;
222 list_add_tail(&node->Device_List, &iSeries_Global_Device_List);
223#if 0
224 pdn->DsaAddr = ((u64)Bus << 48) + ((u64)SubBus << 40) + ((u64)0x10 << 32);
225#endif
226 pdn->DsaAddr.DsaAddr = 0;
227 pdn->DsaAddr.Dsa.busNumber = Bus;
228 pdn->DsaAddr.Dsa.subBusNumber = SubBus;
229 pdn->DsaAddr.Dsa.deviceId = 0x10;
230 pdn->devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(AgentId), Function);
231 return node;
232}
233
234/*
235 * unsigned long __init find_and_init_phbs(void)
236 *
237 * Description:
238 * This function checks for all possible system PCI host bridges that connect
239 * PCI buses. The system hypervisor is queried as to the guest partition
240 * ownership status. A pci_controller is built for any bus which is partially
241 * owned or fully owned by this guest partition.
242 */
243unsigned long __init find_and_init_phbs(void)
244{
245 struct pci_controller *phb;
246 HvBusNumber bus;
247
248 PPCDBG(PPCDBG_BUSWALK, "find_and_init_phbs Entry\n");
249
250 /* Check all possible buses. */
251 for (bus = 0; bus < 256; bus++) {
252 int ret = HvCallXm_testBus(bus);
253 if (ret == 0) {
254 printk("bus %d appears to exist\n", bus);
255
256 phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), GFP_KERNEL);
257 if (phb == NULL)
258 return -ENOMEM;
259 pci_setup_pci_controller(phb);
260
261 phb->pci_mem_offset = phb->local_number = bus;
262 phb->first_busno = bus;
263 phb->last_busno = bus;
264 phb->ops = &iSeries_pci_ops;
265
266 PPCDBG(PPCDBG_BUSWALK, "PCI:Create iSeries pci_controller(%p), Bus: %04X\n",
267 phb, bus);
268
269 /* Find and connect the devices. */
270 scan_PHB_slots(phb);
271 }
272 /*
273 * Check for Unexpected Return code, a clue that something
274 * has gone wrong.
275 */
276 else if (ret != 0x0301)
277 printk(KERN_ERR "Unexpected Return on Probe(0x%04X): 0x%04X",
278 bus, ret);
279 }
280 return 0;
281}
282
283/*
284 * iSeries_pcibios_init
285 *
286 * Chance to initialize and structures or variable before PCI Bus walk.
287 */
288void iSeries_pcibios_init(void)
289{
290 PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_init Entry.\n");
291 iomm_table_initialize();
292 find_and_init_phbs();
293 io_page_mask = -1;
294 PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_init Exit.\n");
295}
296
297/*
298 * iSeries_pci_final_fixup(void)
299 */
300void __init iSeries_pci_final_fixup(void)
301{
302 struct pci_dev *pdev = NULL;
303 struct device_node *node;
304 int DeviceCount = 0;
305
306 PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_fixup Entry.\n");
307
308 /* Fix up at the device node and pci_dev relationship */
309 mf_display_src(0xC9000100);
310
311 printk("pcibios_final_fixup\n");
312 for_each_pci_dev(pdev) {
313 node = find_Device_Node(pdev->bus->number, pdev->devfn);
314 printk("pci dev %p (%x.%x), node %p\n", pdev,
315 pdev->bus->number, pdev->devfn, node);
316
317 if (node != NULL) {
318 ++DeviceCount;
319 pdev->sysdata = (void *)node;
320 PCI_DN(node)->pcidev = pdev;
321 PPCDBG(PPCDBG_BUSWALK,
322 "pdev 0x%p <==> DevNode 0x%p\n",
323 pdev, node);
324 allocate_device_bars(pdev);
325 iSeries_Device_Information(pdev, DeviceCount);
326 iommu_devnode_init_iSeries(node);
327 } else
328 printk("PCI: Device Tree not found for 0x%016lX\n",
329 (unsigned long)pdev);
330 pdev->irq = PCI_DN(node)->Irq;
331 }
332 iSeries_activate_IRQs();
333 mf_display_src(0xC9000200);
334}
335
336void pcibios_fixup_bus(struct pci_bus *PciBus)
337{
338 PPCDBG(PPCDBG_BUSWALK, "iSeries_pcibios_fixup_bus(0x%04X) Entry.\n",
339 PciBus->number);
340}
341
342void pcibios_fixup_resources(struct pci_dev *pdev)
343{
344 PPCDBG(PPCDBG_BUSWALK, "fixup_resources pdev %p\n", pdev);
345}
346
347/*
348 * Loop through each node function to find usable EADs bridges.
349 */
350static void scan_PHB_slots(struct pci_controller *Phb)
351{
352 struct HvCallPci_DeviceInfo *DevInfo;
353 HvBusNumber bus = Phb->local_number; /* System Bus */
354 const HvSubBusNumber SubBus = 0; /* EADs is always 0. */
355 int HvRc = 0;
356 int IdSel;
357 const int MaxAgents = 8;
358
359 DevInfo = (struct HvCallPci_DeviceInfo*)
360 kmalloc(sizeof(struct HvCallPci_DeviceInfo), GFP_KERNEL);
361 if (DevInfo == NULL)
362 return;
363
364 /*
365 * Probe for EADs Bridges
366 */
367 for (IdSel = 1; IdSel < MaxAgents; ++IdSel) {
368 HvRc = HvCallPci_getDeviceInfo(bus, SubBus, IdSel,
369 ISERIES_HV_ADDR(DevInfo),
370 sizeof(struct HvCallPci_DeviceInfo));
371 if (HvRc == 0) {
372 if (DevInfo->deviceType == HvCallPci_NodeDevice)
373 scan_EADS_bridge(bus, SubBus, IdSel);
374 else
375 printk("PCI: Invalid System Configuration(0x%02X)"
376 " for bus 0x%02x id 0x%02x.\n",
377 DevInfo->deviceType, bus, IdSel);
378 }
379 else
380 pci_Log_Error("getDeviceInfo", bus, SubBus, IdSel, HvRc);
381 }
382 kfree(DevInfo);
383}
384
385static void scan_EADS_bridge(HvBusNumber bus, HvSubBusNumber SubBus,
386 int IdSel)
387{
388 struct HvCallPci_BridgeInfo *BridgeInfo;
389 HvAgentId AgentId;
390 int Function;
391 int HvRc;
392
393 BridgeInfo = (struct HvCallPci_BridgeInfo *)
394 kmalloc(sizeof(struct HvCallPci_BridgeInfo), GFP_KERNEL);
395 if (BridgeInfo == NULL)
396 return;
397
398 /* Note: hvSubBus and irq is always be 0 at this level! */
399 for (Function = 0; Function < 8; ++Function) {
400 AgentId = ISERIES_PCI_AGENTID(IdSel, Function);
401 HvRc = HvCallXm_connectBusUnit(bus, SubBus, AgentId, 0);
402 if (HvRc == 0) {
403 printk("found device at bus %d idsel %d func %d (AgentId %x)\n",
404 bus, IdSel, Function, AgentId);
405 /* Connect EADs: 0x18.00.12 = 0x00 */
406 PPCDBG(PPCDBG_BUSWALK,
407 "PCI:Connect EADs: 0x%02X.%02X.%02X\n",
408 bus, SubBus, AgentId);
409 HvRc = HvCallPci_getBusUnitInfo(bus, SubBus, AgentId,
410 ISERIES_HV_ADDR(BridgeInfo),
411 sizeof(struct HvCallPci_BridgeInfo));
412 if (HvRc == 0) {
413 printk("bridge info: type %x subbus %x maxAgents %x maxsubbus %x logslot %x\n",
414 BridgeInfo->busUnitInfo.deviceType,
415 BridgeInfo->subBusNumber,
416 BridgeInfo->maxAgents,
417 BridgeInfo->maxSubBusNumber,
418 BridgeInfo->logicalSlotNumber);
419 PPCDBG(PPCDBG_BUSWALK,
420 "PCI: BridgeInfo, Type:0x%02X, SubBus:0x%02X, MaxAgents:0x%02X, MaxSubBus: 0x%02X, LSlot: 0x%02X\n",
421 BridgeInfo->busUnitInfo.deviceType,
422 BridgeInfo->subBusNumber,
423 BridgeInfo->maxAgents,
424 BridgeInfo->maxSubBusNumber,
425 BridgeInfo->logicalSlotNumber);
426
427 if (BridgeInfo->busUnitInfo.deviceType ==
428 HvCallPci_BridgeDevice) {
429 /* Scan_Bridge_Slot...: 0x18.00.12 */
430 scan_bridge_slot(bus, BridgeInfo);
431 } else
432 printk("PCI: Invalid Bridge Configuration(0x%02X)",
433 BridgeInfo->busUnitInfo.deviceType);
434 }
435 } else if (HvRc != 0x000B)
436 pci_Log_Error("EADs Connect",
437 bus, SubBus, AgentId, HvRc);
438 }
439 kfree(BridgeInfo);
440}
441
442/*
443 * This assumes that the node slot is always on the primary bus!
444 */
445static int scan_bridge_slot(HvBusNumber Bus,
446 struct HvCallPci_BridgeInfo *BridgeInfo)
447{
448 struct device_node *node;
449 HvSubBusNumber SubBus = BridgeInfo->subBusNumber;
450 u16 VendorId = 0;
451 int HvRc = 0;
452 u8 Irq = 0;
453 int IdSel = ISERIES_GET_DEVICE_FROM_SUBBUS(SubBus);
454 int Function = ISERIES_GET_FUNCTION_FROM_SUBBUS(SubBus);
455 HvAgentId EADsIdSel = ISERIES_PCI_AGENTID(IdSel, Function);
456
457 /* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */
458 Irq = iSeries_allocate_IRQ(Bus, 0, EADsIdSel);
459 PPCDBG(PPCDBG_BUSWALK,
460 "PCI:- allocate and assign IRQ 0x%02X.%02X.%02X = 0x%02X\n",
461 Bus, 0, EADsIdSel, Irq);
462
463 /*
464 * Connect all functions of any device found.
465 */
466 for (IdSel = 1; IdSel <= BridgeInfo->maxAgents; ++IdSel) {
467 for (Function = 0; Function < 8; ++Function) {
468 HvAgentId AgentId = ISERIES_PCI_AGENTID(IdSel, Function);
469 HvRc = HvCallXm_connectBusUnit(Bus, SubBus,
470 AgentId, Irq);
471 if (HvRc != 0) {
472 pci_Log_Error("Connect Bus Unit",
473 Bus, SubBus, AgentId, HvRc);
474 continue;
475 }
476
477 HvRc = HvCallPci_configLoad16(Bus, SubBus, AgentId,
478 PCI_VENDOR_ID, &VendorId);
479 if (HvRc != 0) {
480 pci_Log_Error("Read Vendor",
481 Bus, SubBus, AgentId, HvRc);
482 continue;
483 }
484 printk("read vendor ID: %x\n", VendorId);
485
486 /* FoundDevice: 0x18.28.10 = 0x12AE */
487 PPCDBG(PPCDBG_BUSWALK,
488 "PCI:- FoundDevice: 0x%02X.%02X.%02X = 0x%04X, irq %d\n",
489 Bus, SubBus, AgentId, VendorId, Irq);
490 HvRc = HvCallPci_configStore8(Bus, SubBus, AgentId,
491 PCI_INTERRUPT_LINE, Irq);
492 if (HvRc != 0)
493 pci_Log_Error("PciCfgStore Irq Failed!",
494 Bus, SubBus, AgentId, HvRc);
495
496 ++DeviceCount;
497 node = build_device_node(Bus, SubBus, EADsIdSel, Function);
498 PCI_DN(node)->Irq = Irq;
499 PCI_DN(node)->LogicalSlot = BridgeInfo->logicalSlotNumber;
500
501 } /* for (Function = 0; Function < 8; ++Function) */
502 } /* for (IdSel = 1; IdSel <= MaxAgents; ++IdSel) */
503 return HvRc;
504}
505
506/*
507 * I/0 Memory copy MUST use mmio commands on iSeries
508 * To do; For performance, include the hv call directly
509 */
510void iSeries_memset_io(volatile void __iomem *dest, char c, size_t Count)
511{
512 u8 ByteValue = c;
513 long NumberOfBytes = Count;
514
515 while (NumberOfBytes > 0) {
516 iSeries_Write_Byte(ByteValue, dest++);
517 -- NumberOfBytes;
518 }
519}
520EXPORT_SYMBOL(iSeries_memset_io);
521
522void iSeries_memcpy_toio(volatile void __iomem *dest, void *source, size_t count)
523{
524 char *src = source;
525 long NumberOfBytes = count;
526
527 while (NumberOfBytes > 0) {
528 iSeries_Write_Byte(*src++, dest++);
529 -- NumberOfBytes;
530 }
531}
532EXPORT_SYMBOL(iSeries_memcpy_toio);
533
534void iSeries_memcpy_fromio(void *dest, const volatile void __iomem *src, size_t count)
535{
536 char *dst = dest;
537 long NumberOfBytes = count;
538
539 while (NumberOfBytes > 0) {
540 *dst++ = iSeries_Read_Byte(src++);
541 -- NumberOfBytes;
542 }
543}
544EXPORT_SYMBOL(iSeries_memcpy_fromio);
545
546/*
547 * Look down the chain to find the matching Device Device
548 */
549static struct device_node *find_Device_Node(int bus, int devfn)
550{
551 struct list_head *pos;
552
553 list_for_each(pos, &iSeries_Global_Device_List) {
554 struct device_node *node =
555 list_entry(pos, struct device_node, Device_List);
556
557 if ((bus == ISERIES_BUS(node)) &&
558 (devfn == PCI_DN(node)->devfn))
559 return node;
560 }
561 return NULL;
562}
563
564#if 0
565/*
566 * Returns the device node for the passed pci_dev
567 * Sanity Check Node PciDev to passed pci_dev
568 * If none is found, returns a NULL which the client must handle.
569 */
570static struct device_node *get_Device_Node(struct pci_dev *pdev)
571{
572 struct device_node *node;
573
574 node = pdev->sysdata;
575 if (node == NULL || PCI_DN(node)->pcidev != pdev)
576 node = find_Device_Node(pdev->bus->number, pdev->devfn);
577 return node;
578}
579#endif
580
581/*
582 * Config space read and write functions.
583 * For now at least, we look for the device node for the bus and devfn
584 * that we are asked to access. It may be possible to translate the devfn
585 * to a subbus and deviceid more directly.
586 */
587static u64 hv_cfg_read_func[4] = {
588 HvCallPciConfigLoad8, HvCallPciConfigLoad16,
589 HvCallPciConfigLoad32, HvCallPciConfigLoad32
590};
591
592static u64 hv_cfg_write_func[4] = {
593 HvCallPciConfigStore8, HvCallPciConfigStore16,
594 HvCallPciConfigStore32, HvCallPciConfigStore32
595};
596
597/*
598 * Read PCI config space
599 */
600static int iSeries_pci_read_config(struct pci_bus *bus, unsigned int devfn,
601 int offset, int size, u32 *val)
602{
603 struct device_node *node = find_Device_Node(bus->number, devfn);
604 u64 fn;
605 struct HvCallPci_LoadReturn ret;
606
607 if (node == NULL)
608 return PCIBIOS_DEVICE_NOT_FOUND;
609 if (offset > 255) {
610 *val = ~0;
611 return PCIBIOS_BAD_REGISTER_NUMBER;
612 }
613
614 fn = hv_cfg_read_func[(size - 1) & 3];
615 HvCall3Ret16(fn, &ret, PCI_DN(node)->DsaAddr.DsaAddr, offset, 0);
616
617 if (ret.rc != 0) {
618 *val = ~0;
619 return PCIBIOS_DEVICE_NOT_FOUND; /* or something */
620 }
621
622 *val = ret.value;
623 return 0;
624}
625
626/*
627 * Write PCI config space
628 */
629
630static int iSeries_pci_write_config(struct pci_bus *bus, unsigned int devfn,
631 int offset, int size, u32 val)
632{
633 struct device_node *node = find_Device_Node(bus->number, devfn);
634 u64 fn;
635 u64 ret;
636
637 if (node == NULL)
638 return PCIBIOS_DEVICE_NOT_FOUND;
639 if (offset > 255)
640 return PCIBIOS_BAD_REGISTER_NUMBER;
641
642 fn = hv_cfg_write_func[(size - 1) & 3];
643 ret = HvCall4(fn, PCI_DN(node)->DsaAddr.DsaAddr, offset, val, 0);
644
645 if (ret != 0)
646 return PCIBIOS_DEVICE_NOT_FOUND;
647
648 return 0;
649}
650
651static struct pci_ops iSeries_pci_ops = {
652 .read = iSeries_pci_read_config,
653 .write = iSeries_pci_write_config
654};
655
656/*
657 * Check Return Code
658 * -> On Failure, print and log information.
659 * Increment Retry Count, if exceeds max, panic partition.
660 *
661 * PCI: Device 23.90 ReadL I/O Error( 0): 0x1234
662 * PCI: Device 23.90 ReadL Retry( 1)
663 * PCI: Device 23.90 ReadL Retry Successful(1)
664 */
665static int CheckReturnCode(char *TextHdr, struct device_node *DevNode,
666 int *retry, u64 ret)
667{
668 if (ret != 0) {
669 struct pci_dn *pdn = PCI_DN(DevNode);
670
671 ++Pci_Error_Count;
672 (*retry)++;
673 printk("PCI: %s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X\n",
674 TextHdr, pdn->DsaAddr.Dsa.busNumber, pdn->devfn,
675 *retry, (int)ret);
676 /*
677 * Bump the retry and check for retry count exceeded.
678 * If, Exceeded, panic the system.
679 */
680 if (((*retry) > Pci_Retry_Max) &&
681 (Pci_Error_Flag > 0)) {
682 mf_display_src(0xB6000103);
683 panic_timeout = 0;
684 panic("PCI: Hardware I/O Error, SRC B6000103, "
685 "Automatic Reboot Disabled.\n");
686 }
687 return -1; /* Retry Try */
688 }
689 return 0;
690}
691
692/*
693 * Translate the I/O Address into a device node, bar, and bar offset.
694 * Note: Make sure the passed variable end up on the stack to avoid
695 * the exposure of being device global.
696 */
697static inline struct device_node *xlate_iomm_address(
698 const volatile void __iomem *IoAddress,
699 u64 *dsaptr, u64 *BarOffsetPtr)
700{
701 unsigned long OrigIoAddr;
702 unsigned long BaseIoAddr;
703 unsigned long TableIndex;
704 struct device_node *DevNode;
705
706 OrigIoAddr = (unsigned long __force)IoAddress;
707 if ((OrigIoAddr < BASE_IO_MEMORY) || (OrigIoAddr >= max_io_memory))
708 return NULL;
709 BaseIoAddr = OrigIoAddr - BASE_IO_MEMORY;
710 TableIndex = BaseIoAddr / IOMM_TABLE_ENTRY_SIZE;
711 DevNode = iomm_table[TableIndex];
712
713 if (DevNode != NULL) {
714 int barnum = iobar_table[TableIndex];
715 *dsaptr = PCI_DN(DevNode)->DsaAddr.DsaAddr | (barnum << 24);
716 *BarOffsetPtr = BaseIoAddr % IOMM_TABLE_ENTRY_SIZE;
717 } else
718 panic("PCI: Invalid PCI IoAddress detected!\n");
719 return DevNode;
720}
721
722/*
723 * Read MM I/O Instructions for the iSeries
724 * On MM I/O error, all ones are returned and iSeries_pci_IoError is cal
725 * else, data is returned in big Endian format.
726 *
727 * iSeries_Read_Byte = Read Byte ( 8 bit)
728 * iSeries_Read_Word = Read Word (16 bit)
729 * iSeries_Read_Long = Read Long (32 bit)
730 */
731u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress)
732{
733 u64 BarOffset;
734 u64 dsa;
735 int retry = 0;
736 struct HvCallPci_LoadReturn ret;
737 struct device_node *DevNode =
738 xlate_iomm_address(IoAddress, &dsa, &BarOffset);
739
740 if (DevNode == NULL) {
741 static unsigned long last_jiffies;
742 static int num_printed;
743
744 if ((jiffies - last_jiffies) > 60 * HZ) {
745 last_jiffies = jiffies;
746 num_printed = 0;
747 }
748 if (num_printed++ < 10)
749 printk(KERN_ERR "iSeries_Read_Byte: invalid access at IO address %p\n", IoAddress);
750 return 0xff;
751 }
752 do {
753 ++Pci_Io_Read_Count;
754 HvCall3Ret16(HvCallPciBarLoad8, &ret, dsa, BarOffset, 0);
755 } while (CheckReturnCode("RDB", DevNode, &retry, ret.rc) != 0);
756
757 return (u8)ret.value;
758}
759EXPORT_SYMBOL(iSeries_Read_Byte);
760
761u16 iSeries_Read_Word(const volatile void __iomem *IoAddress)
762{
763 u64 BarOffset;
764 u64 dsa;
765 int retry = 0;
766 struct HvCallPci_LoadReturn ret;
767 struct device_node *DevNode =
768 xlate_iomm_address(IoAddress, &dsa, &BarOffset);
769
770 if (DevNode == NULL) {
771 static unsigned long last_jiffies;
772 static int num_printed;
773
774 if ((jiffies - last_jiffies) > 60 * HZ) {
775 last_jiffies = jiffies;
776 num_printed = 0;
777 }
778 if (num_printed++ < 10)
779 printk(KERN_ERR "iSeries_Read_Word: invalid access at IO address %p\n", IoAddress);
780 return 0xffff;
781 }
782 do {
783 ++Pci_Io_Read_Count;
784 HvCall3Ret16(HvCallPciBarLoad16, &ret, dsa,
785 BarOffset, 0);
786 } while (CheckReturnCode("RDW", DevNode, &retry, ret.rc) != 0);
787
788 return swab16((u16)ret.value);
789}
790EXPORT_SYMBOL(iSeries_Read_Word);
791
792u32 iSeries_Read_Long(const volatile void __iomem *IoAddress)
793{
794 u64 BarOffset;
795 u64 dsa;
796 int retry = 0;
797 struct HvCallPci_LoadReturn ret;
798 struct device_node *DevNode =
799 xlate_iomm_address(IoAddress, &dsa, &BarOffset);
800
801 if (DevNode == NULL) {
802 static unsigned long last_jiffies;
803 static int num_printed;
804
805 if ((jiffies - last_jiffies) > 60 * HZ) {
806 last_jiffies = jiffies;
807 num_printed = 0;
808 }
809 if (num_printed++ < 10)
810 printk(KERN_ERR "iSeries_Read_Long: invalid access at IO address %p\n", IoAddress);
811 return 0xffffffff;
812 }
813 do {
814 ++Pci_Io_Read_Count;
815 HvCall3Ret16(HvCallPciBarLoad32, &ret, dsa,
816 BarOffset, 0);
817 } while (CheckReturnCode("RDL", DevNode, &retry, ret.rc) != 0);
818
819 return swab32((u32)ret.value);
820}
821EXPORT_SYMBOL(iSeries_Read_Long);
822
823/*
824 * Write MM I/O Instructions for the iSeries
825 *
826 * iSeries_Write_Byte = Write Byte (8 bit)
827 * iSeries_Write_Word = Write Word(16 bit)
828 * iSeries_Write_Long = Write Long(32 bit)
829 */
830void iSeries_Write_Byte(u8 data, volatile void __iomem *IoAddress)
831{
832 u64 BarOffset;
833 u64 dsa;
834 int retry = 0;
835 u64 rc;
836 struct device_node *DevNode =
837 xlate_iomm_address(IoAddress, &dsa, &BarOffset);
838
839 if (DevNode == NULL) {
840 static unsigned long last_jiffies;
841 static int num_printed;
842
843 if ((jiffies - last_jiffies) > 60 * HZ) {
844 last_jiffies = jiffies;
845 num_printed = 0;
846 }
847 if (num_printed++ < 10)
848 printk(KERN_ERR "iSeries_Write_Byte: invalid access at IO address %p\n", IoAddress);
849 return;
850 }
851 do {
852 ++Pci_Io_Write_Count;
853 rc = HvCall4(HvCallPciBarStore8, dsa, BarOffset, data, 0);
854 } while (CheckReturnCode("WWB", DevNode, &retry, rc) != 0);
855}
856EXPORT_SYMBOL(iSeries_Write_Byte);
857
858void iSeries_Write_Word(u16 data, volatile void __iomem *IoAddress)
859{
860 u64 BarOffset;
861 u64 dsa;
862 int retry = 0;
863 u64 rc;
864 struct device_node *DevNode =
865 xlate_iomm_address(IoAddress, &dsa, &BarOffset);
866
867 if (DevNode == NULL) {
868 static unsigned long last_jiffies;
869 static int num_printed;
870
871 if ((jiffies - last_jiffies) > 60 * HZ) {
872 last_jiffies = jiffies;
873 num_printed = 0;
874 }
875 if (num_printed++ < 10)
876 printk(KERN_ERR "iSeries_Write_Word: invalid access at IO address %p\n", IoAddress);
877 return;
878 }
879 do {
880 ++Pci_Io_Write_Count;
881 rc = HvCall4(HvCallPciBarStore16, dsa, BarOffset, swab16(data), 0);
882 } while (CheckReturnCode("WWW", DevNode, &retry, rc) != 0);
883}
884EXPORT_SYMBOL(iSeries_Write_Word);
885
886void iSeries_Write_Long(u32 data, volatile void __iomem *IoAddress)
887{
888 u64 BarOffset;
889 u64 dsa;
890 int retry = 0;
891 u64 rc;
892 struct device_node *DevNode =
893 xlate_iomm_address(IoAddress, &dsa, &BarOffset);
894
895 if (DevNode == NULL) {
896 static unsigned long last_jiffies;
897 static int num_printed;
898
899 if ((jiffies - last_jiffies) > 60 * HZ) {
900 last_jiffies = jiffies;
901 num_printed = 0;
902 }
903 if (num_printed++ < 10)
904 printk(KERN_ERR "iSeries_Write_Long: invalid access at IO address %p\n", IoAddress);
905 return;
906 }
907 do {
908 ++Pci_Io_Write_Count;
909 rc = HvCall4(HvCallPciBarStore32, dsa, BarOffset, swab32(data), 0);
910 } while (CheckReturnCode("WWL", DevNode, &retry, rc) != 0);
911}
912EXPORT_SYMBOL(iSeries_Write_Long);
diff --git a/arch/powerpc/platforms/iseries/proc.c b/arch/powerpc/platforms/iseries/proc.c
new file mode 100644
index 000000000000..d46b473ce4dd
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/proc.c
@@ -0,0 +1,115 @@
1/*
2 * Copyright (C) 2001 Kyle A. Lucke IBM Corporation
3 * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen IBM Corporation
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <linux/init.h>
20#include <linux/proc_fs.h>
21#include <linux/seq_file.h>
22#include <linux/param.h> /* for HZ */
23#include <asm/paca.h>
24#include <asm/processor.h>
25#include <asm/time.h>
26#include <asm/lppaca.h>
27#include <asm/iSeries/ItLpQueue.h>
28#include <asm/iSeries/HvCallXm.h>
29#include <asm/iSeries/IoHriMainStore.h>
30#include <asm/iSeries/IoHriProcessorVpd.h>
31
32static int __init iseries_proc_create(void)
33{
34 struct proc_dir_entry *e = proc_mkdir("iSeries", 0);
35 if (!e)
36 return 1;
37
38 return 0;
39}
40core_initcall(iseries_proc_create);
41
42static unsigned long startTitan = 0;
43static unsigned long startTb = 0;
44
45static int proc_titantod_show(struct seq_file *m, void *v)
46{
47 unsigned long tb0, titan_tod;
48
49 tb0 = get_tb();
50 titan_tod = HvCallXm_loadTod();
51
52 seq_printf(m, "Titan\n" );
53 seq_printf(m, " time base = %016lx\n", tb0);
54 seq_printf(m, " titan tod = %016lx\n", titan_tod);
55 seq_printf(m, " xProcFreq = %016x\n",
56 xIoHriProcessorVpd[0].xProcFreq);
57 seq_printf(m, " xTimeBaseFreq = %016x\n",
58 xIoHriProcessorVpd[0].xTimeBaseFreq);
59 seq_printf(m, " tb_ticks_per_jiffy = %lu\n", tb_ticks_per_jiffy);
60 seq_printf(m, " tb_ticks_per_usec = %lu\n", tb_ticks_per_usec);
61
62 if (!startTitan) {
63 startTitan = titan_tod;
64 startTb = tb0;
65 } else {
66 unsigned long titan_usec = (titan_tod - startTitan) >> 12;
67 unsigned long tb_ticks = (tb0 - startTb);
68 unsigned long titan_jiffies = titan_usec / (1000000/HZ);
69 unsigned long titan_jiff_usec = titan_jiffies * (1000000/HZ);
70 unsigned long titan_jiff_rem_usec =
71 titan_usec - titan_jiff_usec;
72 unsigned long tb_jiffies = tb_ticks / tb_ticks_per_jiffy;
73 unsigned long tb_jiff_ticks = tb_jiffies * tb_ticks_per_jiffy;
74 unsigned long tb_jiff_rem_ticks = tb_ticks - tb_jiff_ticks;
75 unsigned long tb_jiff_rem_usec =
76 tb_jiff_rem_ticks / tb_ticks_per_usec;
77 unsigned long new_tb_ticks_per_jiffy =
78 (tb_ticks * (1000000/HZ))/titan_usec;
79
80 seq_printf(m, " titan elapsed = %lu uSec\n", titan_usec);
81 seq_printf(m, " tb elapsed = %lu ticks\n", tb_ticks);
82 seq_printf(m, " titan jiffies = %lu.%04lu \n", titan_jiffies,
83 titan_jiff_rem_usec);
84 seq_printf(m, " tb jiffies = %lu.%04lu\n", tb_jiffies,
85 tb_jiff_rem_usec);
86 seq_printf(m, " new tb_ticks_per_jiffy = %lu\n",
87 new_tb_ticks_per_jiffy);
88 }
89
90 return 0;
91}
92
93static int proc_titantod_open(struct inode *inode, struct file *file)
94{
95 return single_open(file, proc_titantod_show, NULL);
96}
97
98static struct file_operations proc_titantod_operations = {
99 .open = proc_titantod_open,
100 .read = seq_read,
101 .llseek = seq_lseek,
102 .release = single_release,
103};
104
105static int __init iseries_proc_init(void)
106{
107 struct proc_dir_entry *e;
108
109 e = create_proc_entry("iSeries/titanTod", S_IFREG|S_IRUGO, NULL);
110 if (e)
111 e->proc_fops = &proc_titantod_operations;
112
113 return 0;
114}
115__initcall(iseries_proc_init);
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c
new file mode 100644
index 000000000000..ad78c8581a5a
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/setup.c
@@ -0,0 +1,1006 @@
1/*
2 * Copyright (c) 2000 Mike Corrigan <mikejc@us.ibm.com>
3 * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
4 *
5 * Description:
6 * Architecture- / platform-specific boot-time initialization code for
7 * the IBM iSeries LPAR. Adapted from original code by Grant Erickson and
8 * code by Gary Thomas, Cort Dougan <cort@fsmlabs.com>, and Dan Malek
9 * <dan@net4x.com>.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version
14 * 2 of the License, or (at your option) any later version.
15 */
16
17#undef DEBUG
18
19#include <linux/config.h>
20#include <linux/init.h>
21#include <linux/threads.h>
22#include <linux/smp.h>
23#include <linux/param.h>
24#include <linux/string.h>
25#include <linux/initrd.h>
26#include <linux/seq_file.h>
27#include <linux/kdev_t.h>
28#include <linux/major.h>
29#include <linux/root_dev.h>
30
31#include <asm/processor.h>
32#include <asm/machdep.h>
33#include <asm/page.h>
34#include <asm/mmu.h>
35#include <asm/pgtable.h>
36#include <asm/mmu_context.h>
37#include <asm/cputable.h>
38#include <asm/sections.h>
39#include <asm/iommu.h>
40#include <asm/firmware.h>
41
42#include <asm/time.h>
43#include <asm/naca.h>
44#include <asm/paca.h>
45#include <asm/cache.h>
46#include <asm/sections.h>
47#include <asm/abs_addr.h>
48#include <asm/iSeries/HvCallHpt.h>
49#include <asm/iSeries/HvLpConfig.h>
50#include <asm/iSeries/HvCallEvent.h>
51#include <asm/iSeries/HvCallSm.h>
52#include <asm/iSeries/HvCallXm.h>
53#include <asm/iSeries/ItLpQueue.h>
54#include <asm/iSeries/IoHriMainStore.h>
55#include <asm/iSeries/mf.h>
56#include <asm/iSeries/HvLpEvent.h>
57#include <asm/iSeries/iSeries_irq.h>
58#include <asm/iSeries/IoHriProcessorVpd.h>
59#include <asm/iSeries/ItVpdAreas.h>
60#include <asm/iSeries/LparMap.h>
61
62#include "setup.h"
63
64extern void hvlog(char *fmt, ...);
65
66#ifdef DEBUG
67#define DBG(fmt...) hvlog(fmt)
68#else
69#define DBG(fmt...)
70#endif
71
72/* Function Prototypes */
73extern void ppcdbg_initialize(void);
74
75static void build_iSeries_Memory_Map(void);
76static int iseries_shared_idle(void);
77static int iseries_dedicated_idle(void);
78#ifdef CONFIG_PCI
79extern void iSeries_pci_final_fixup(void);
80#else
81static void iSeries_pci_final_fixup(void) { }
82#endif
83
84/* Global Variables */
85int piranha_simulator;
86
87extern int rd_size; /* Defined in drivers/block/rd.c */
88extern unsigned long klimit;
89extern unsigned long embedded_sysmap_start;
90extern unsigned long embedded_sysmap_end;
91
92extern unsigned long iSeries_recal_tb;
93extern unsigned long iSeries_recal_titan;
94
95static int mf_initialized;
96
97struct MemoryBlock {
98 unsigned long absStart;
99 unsigned long absEnd;
100 unsigned long logicalStart;
101 unsigned long logicalEnd;
102};
103
104/*
105 * Process the main store vpd to determine where the holes in memory are
106 * and return the number of physical blocks and fill in the array of
107 * block data.
108 */
109static unsigned long iSeries_process_Condor_mainstore_vpd(
110 struct MemoryBlock *mb_array, unsigned long max_entries)
111{
112 unsigned long holeFirstChunk, holeSizeChunks;
113 unsigned long numMemoryBlocks = 1;
114 struct IoHriMainStoreSegment4 *msVpd =
115 (struct IoHriMainStoreSegment4 *)xMsVpd;
116 unsigned long holeStart = msVpd->nonInterleavedBlocksStartAdr;
117 unsigned long holeEnd = msVpd->nonInterleavedBlocksEndAdr;
118 unsigned long holeSize = holeEnd - holeStart;
119
120 printk("Mainstore_VPD: Condor\n");
121 /*
122 * Determine if absolute memory has any
123 * holes so that we can interpret the
124 * access map we get back from the hypervisor
125 * correctly.
126 */
127 mb_array[0].logicalStart = 0;
128 mb_array[0].logicalEnd = 0x100000000;
129 mb_array[0].absStart = 0;
130 mb_array[0].absEnd = 0x100000000;
131
132 if (holeSize) {
133 numMemoryBlocks = 2;
134 holeStart = holeStart & 0x000fffffffffffff;
135 holeStart = addr_to_chunk(holeStart);
136 holeFirstChunk = holeStart;
137 holeSize = addr_to_chunk(holeSize);
138 holeSizeChunks = holeSize;
139 printk( "Main store hole: start chunk = %0lx, size = %0lx chunks\n",
140 holeFirstChunk, holeSizeChunks );
141 mb_array[0].logicalEnd = holeFirstChunk;
142 mb_array[0].absEnd = holeFirstChunk;
143 mb_array[1].logicalStart = holeFirstChunk;
144 mb_array[1].logicalEnd = 0x100000000 - holeSizeChunks;
145 mb_array[1].absStart = holeFirstChunk + holeSizeChunks;
146 mb_array[1].absEnd = 0x100000000;
147 }
148 return numMemoryBlocks;
149}
150
151#define MaxSegmentAreas 32
152#define MaxSegmentAdrRangeBlocks 128
153#define MaxAreaRangeBlocks 4
154
155static unsigned long iSeries_process_Regatta_mainstore_vpd(
156 struct MemoryBlock *mb_array, unsigned long max_entries)
157{
158 struct IoHriMainStoreSegment5 *msVpdP =
159 (struct IoHriMainStoreSegment5 *)xMsVpd;
160 unsigned long numSegmentBlocks = 0;
161 u32 existsBits = msVpdP->msAreaExists;
162 unsigned long area_num;
163
164 printk("Mainstore_VPD: Regatta\n");
165
166 for (area_num = 0; area_num < MaxSegmentAreas; ++area_num ) {
167 unsigned long numAreaBlocks;
168 struct IoHriMainStoreArea4 *currentArea;
169
170 if (existsBits & 0x80000000) {
171 unsigned long block_num;
172
173 currentArea = &msVpdP->msAreaArray[area_num];
174 numAreaBlocks = currentArea->numAdrRangeBlocks;
175 printk("ms_vpd: processing area %2ld blocks=%ld",
176 area_num, numAreaBlocks);
177 for (block_num = 0; block_num < numAreaBlocks;
178 ++block_num ) {
179 /* Process an address range block */
180 struct MemoryBlock tempBlock;
181 unsigned long i;
182
183 tempBlock.absStart =
184 (unsigned long)currentArea->xAdrRangeBlock[block_num].blockStart;
185 tempBlock.absEnd =
186 (unsigned long)currentArea->xAdrRangeBlock[block_num].blockEnd;
187 tempBlock.logicalStart = 0;
188 tempBlock.logicalEnd = 0;
189 printk("\n block %ld absStart=%016lx absEnd=%016lx",
190 block_num, tempBlock.absStart,
191 tempBlock.absEnd);
192
193 for (i = 0; i < numSegmentBlocks; ++i) {
194 if (mb_array[i].absStart ==
195 tempBlock.absStart)
196 break;
197 }
198 if (i == numSegmentBlocks) {
199 if (numSegmentBlocks == max_entries)
200 panic("iSeries_process_mainstore_vpd: too many memory blocks");
201 mb_array[numSegmentBlocks] = tempBlock;
202 ++numSegmentBlocks;
203 } else
204 printk(" (duplicate)");
205 }
206 printk("\n");
207 }
208 existsBits <<= 1;
209 }
210 /* Now sort the blocks found into ascending sequence */
211 if (numSegmentBlocks > 1) {
212 unsigned long m, n;
213
214 for (m = 0; m < numSegmentBlocks - 1; ++m) {
215 for (n = numSegmentBlocks - 1; m < n; --n) {
216 if (mb_array[n].absStart <
217 mb_array[n-1].absStart) {
218 struct MemoryBlock tempBlock;
219
220 tempBlock = mb_array[n];
221 mb_array[n] = mb_array[n-1];
222 mb_array[n-1] = tempBlock;
223 }
224 }
225 }
226 }
227 /*
228 * Assign "logical" addresses to each block. These
229 * addresses correspond to the hypervisor "bitmap" space.
230 * Convert all addresses into units of 256K chunks.
231 */
232 {
233 unsigned long i, nextBitmapAddress;
234
235 printk("ms_vpd: %ld sorted memory blocks\n", numSegmentBlocks);
236 nextBitmapAddress = 0;
237 for (i = 0; i < numSegmentBlocks; ++i) {
238 unsigned long length = mb_array[i].absEnd -
239 mb_array[i].absStart;
240
241 mb_array[i].logicalStart = nextBitmapAddress;
242 mb_array[i].logicalEnd = nextBitmapAddress + length;
243 nextBitmapAddress += length;
244 printk(" Bitmap range: %016lx - %016lx\n"
245 " Absolute range: %016lx - %016lx\n",
246 mb_array[i].logicalStart,
247 mb_array[i].logicalEnd,
248 mb_array[i].absStart, mb_array[i].absEnd);
249 mb_array[i].absStart = addr_to_chunk(mb_array[i].absStart &
250 0x000fffffffffffff);
251 mb_array[i].absEnd = addr_to_chunk(mb_array[i].absEnd &
252 0x000fffffffffffff);
253 mb_array[i].logicalStart =
254 addr_to_chunk(mb_array[i].logicalStart);
255 mb_array[i].logicalEnd = addr_to_chunk(mb_array[i].logicalEnd);
256 }
257 }
258
259 return numSegmentBlocks;
260}
261
262static unsigned long iSeries_process_mainstore_vpd(struct MemoryBlock *mb_array,
263 unsigned long max_entries)
264{
265 unsigned long i;
266 unsigned long mem_blocks = 0;
267
268 if (cpu_has_feature(CPU_FTR_SLB))
269 mem_blocks = iSeries_process_Regatta_mainstore_vpd(mb_array,
270 max_entries);
271 else
272 mem_blocks = iSeries_process_Condor_mainstore_vpd(mb_array,
273 max_entries);
274
275 printk("Mainstore_VPD: numMemoryBlocks = %ld \n", mem_blocks);
276 for (i = 0; i < mem_blocks; ++i) {
277 printk("Mainstore_VPD: block %3ld logical chunks %016lx - %016lx\n"
278 " abs chunks %016lx - %016lx\n",
279 i, mb_array[i].logicalStart, mb_array[i].logicalEnd,
280 mb_array[i].absStart, mb_array[i].absEnd);
281 }
282 return mem_blocks;
283}
284
285static void __init iSeries_get_cmdline(void)
286{
287 char *p, *q;
288
289 /* copy the command line parameter from the primary VSP */
290 HvCallEvent_dmaToSp(cmd_line, 2 * 64* 1024, 256,
291 HvLpDma_Direction_RemoteToLocal);
292
293 p = cmd_line;
294 q = cmd_line + 255;
295 while(p < q) {
296 if (!*p || *p == '\n')
297 break;
298 ++p;
299 }
300 *p = 0;
301}
302
303static void __init iSeries_init_early(void)
304{
305 extern unsigned long memory_limit;
306
307 DBG(" -> iSeries_init_early()\n");
308
309 ppc64_firmware_features = FW_FEATURE_ISERIES;
310
311 ppcdbg_initialize();
312
313 ppc64_interrupt_controller = IC_ISERIES;
314
315#if defined(CONFIG_BLK_DEV_INITRD)
316 /*
317 * If the init RAM disk has been configured and there is
318 * a non-zero starting address for it, set it up
319 */
320 if (naca.xRamDisk) {
321 initrd_start = (unsigned long)__va(naca.xRamDisk);
322 initrd_end = initrd_start + naca.xRamDiskSize * PAGE_SIZE;
323 initrd_below_start_ok = 1; // ramdisk in kernel space
324 ROOT_DEV = Root_RAM0;
325 if (((rd_size * 1024) / PAGE_SIZE) < naca.xRamDiskSize)
326 rd_size = (naca.xRamDiskSize * PAGE_SIZE) / 1024;
327 } else
328#endif /* CONFIG_BLK_DEV_INITRD */
329 {
330 /* ROOT_DEV = MKDEV(VIODASD_MAJOR, 1); */
331 }
332
333 iSeries_recal_tb = get_tb();
334 iSeries_recal_titan = HvCallXm_loadTod();
335
336 /*
337 * Initialize the hash table management pointers
338 */
339 hpte_init_iSeries();
340
341 /*
342 * Initialize the DMA/TCE management
343 */
344 iommu_init_early_iSeries();
345
346 iSeries_get_cmdline();
347
348 /* Save unparsed command line copy for /proc/cmdline */
349 strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE);
350
351 /* Parse early parameters, in particular mem=x */
352 parse_early_param();
353
354 if (memory_limit) {
355 if (memory_limit < systemcfg->physicalMemorySize)
356 systemcfg->physicalMemorySize = memory_limit;
357 else {
358 printk("Ignoring mem=%lu >= ram_top.\n", memory_limit);
359 memory_limit = 0;
360 }
361 }
362
363 /* Initialize machine-dependency vectors */
364#ifdef CONFIG_SMP
365 smp_init_iSeries();
366#endif
367 if (itLpNaca.xPirEnvironMode == 0)
368 piranha_simulator = 1;
369
370 /* Associate Lp Event Queue 0 with processor 0 */
371 HvCallEvent_setLpEventQueueInterruptProc(0, 0);
372
373 mf_init();
374 mf_initialized = 1;
375 mb();
376
377 /* If we were passed an initrd, set the ROOT_DEV properly if the values
378 * look sensible. If not, clear initrd reference.
379 */
380#ifdef CONFIG_BLK_DEV_INITRD
381 if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE &&
382 initrd_end > initrd_start)
383 ROOT_DEV = Root_RAM0;
384 else
385 initrd_start = initrd_end = 0;
386#endif /* CONFIG_BLK_DEV_INITRD */
387
388 DBG(" <- iSeries_init_early()\n");
389}
390
391struct mschunks_map mschunks_map = {
392 /* XXX We don't use these, but Piranha might need them. */
393 .chunk_size = MSCHUNKS_CHUNK_SIZE,
394 .chunk_shift = MSCHUNKS_CHUNK_SHIFT,
395 .chunk_mask = MSCHUNKS_OFFSET_MASK,
396};
397EXPORT_SYMBOL(mschunks_map);
398
399void mschunks_alloc(unsigned long num_chunks)
400{
401 klimit = _ALIGN(klimit, sizeof(u32));
402 mschunks_map.mapping = (u32 *)klimit;
403 klimit += num_chunks * sizeof(u32);
404 mschunks_map.num_chunks = num_chunks;
405}
406
407/*
408 * The iSeries may have very large memories ( > 128 GB ) and a partition
409 * may get memory in "chunks" that may be anywhere in the 2**52 real
410 * address space. The chunks are 256K in size. To map this to the
411 * memory model Linux expects, the AS/400 specific code builds a
412 * translation table to translate what Linux thinks are "physical"
413 * addresses to the actual real addresses. This allows us to make
414 * it appear to Linux that we have contiguous memory starting at
415 * physical address zero while in fact this could be far from the truth.
416 * To avoid confusion, I'll let the words physical and/or real address
417 * apply to the Linux addresses while I'll use "absolute address" to
418 * refer to the actual hardware real address.
419 *
420 * build_iSeries_Memory_Map gets information from the Hypervisor and
421 * looks at the Main Store VPD to determine the absolute addresses
422 * of the memory that has been assigned to our partition and builds
423 * a table used to translate Linux's physical addresses to these
424 * absolute addresses. Absolute addresses are needed when
425 * communicating with the hypervisor (e.g. to build HPT entries)
426 */
427
428static void __init build_iSeries_Memory_Map(void)
429{
430 u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize;
431 u32 nextPhysChunk;
432 u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages;
433 u32 num_ptegs;
434 u32 totalChunks,moreChunks;
435 u32 currChunk, thisChunk, absChunk;
436 u32 currDword;
437 u32 chunkBit;
438 u64 map;
439 struct MemoryBlock mb[32];
440 unsigned long numMemoryBlocks, curBlock;
441
442 /* Chunk size on iSeries is 256K bytes */
443 totalChunks = (u32)HvLpConfig_getMsChunks();
444 mschunks_alloc(totalChunks);
445
446 /*
447 * Get absolute address of our load area
448 * and map it to physical address 0
449 * This guarantees that the loadarea ends up at physical 0
450 * otherwise, it might not be returned by PLIC as the first
451 * chunks
452 */
453
454 loadAreaFirstChunk = (u32)addr_to_chunk(itLpNaca.xLoadAreaAddr);
455 loadAreaSize = itLpNaca.xLoadAreaChunks;
456
457 /*
458 * Only add the pages already mapped here.
459 * Otherwise we might add the hpt pages
460 * The rest of the pages of the load area
461 * aren't in the HPT yet and can still
462 * be assigned an arbitrary physical address
463 */
464 if ((loadAreaSize * 64) > HvPagesToMap)
465 loadAreaSize = HvPagesToMap / 64;
466
467 loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1;
468
469 /*
470 * TODO Do we need to do something if the HPT is in the 64MB load area?
471 * This would be required if the itLpNaca.xLoadAreaChunks includes
472 * the HPT size
473 */
474
475 printk("Mapping load area - physical addr = 0000000000000000\n"
476 " absolute addr = %016lx\n",
477 chunk_to_addr(loadAreaFirstChunk));
478 printk("Load area size %dK\n", loadAreaSize * 256);
479
480 for (nextPhysChunk = 0; nextPhysChunk < loadAreaSize; ++nextPhysChunk)
481 mschunks_map.mapping[nextPhysChunk] =
482 loadAreaFirstChunk + nextPhysChunk;
483
484 /*
485 * Get absolute address of our HPT and remember it so
486 * we won't map it to any physical address
487 */
488 hptFirstChunk = (u32)addr_to_chunk(HvCallHpt_getHptAddress());
489 hptSizePages = (u32)HvCallHpt_getHptPages();
490 hptSizeChunks = hptSizePages >> (MSCHUNKS_CHUNK_SHIFT - PAGE_SHIFT);
491 hptLastChunk = hptFirstChunk + hptSizeChunks - 1;
492
493 printk("HPT absolute addr = %016lx, size = %dK\n",
494 chunk_to_addr(hptFirstChunk), hptSizeChunks * 256);
495
496 /* Fill in the hashed page table hash mask */
497 num_ptegs = hptSizePages *
498 (PAGE_SIZE / (sizeof(hpte_t) * HPTES_PER_GROUP));
499 htab_hash_mask = num_ptegs - 1;
500
501 /*
502 * The actual hashed page table is in the hypervisor,
503 * we have no direct access
504 */
505 htab_address = NULL;
506
507 /*
508 * Determine if absolute memory has any
509 * holes so that we can interpret the
510 * access map we get back from the hypervisor
511 * correctly.
512 */
513 numMemoryBlocks = iSeries_process_mainstore_vpd(mb, 32);
514
515 /*
516 * Process the main store access map from the hypervisor
517 * to build up our physical -> absolute translation table
518 */
519 curBlock = 0;
520 currChunk = 0;
521 currDword = 0;
522 moreChunks = totalChunks;
523
524 while (moreChunks) {
525 map = HvCallSm_get64BitsOfAccessMap(itLpNaca.xLpIndex,
526 currDword);
527 thisChunk = currChunk;
528 while (map) {
529 chunkBit = map >> 63;
530 map <<= 1;
531 if (chunkBit) {
532 --moreChunks;
533 while (thisChunk >= mb[curBlock].logicalEnd) {
534 ++curBlock;
535 if (curBlock >= numMemoryBlocks)
536 panic("out of memory blocks");
537 }
538 if (thisChunk < mb[curBlock].logicalStart)
539 panic("memory block error");
540
541 absChunk = mb[curBlock].absStart +
542 (thisChunk - mb[curBlock].logicalStart);
543 if (((absChunk < hptFirstChunk) ||
544 (absChunk > hptLastChunk)) &&
545 ((absChunk < loadAreaFirstChunk) ||
546 (absChunk > loadAreaLastChunk))) {
547 mschunks_map.mapping[nextPhysChunk] =
548 absChunk;
549 ++nextPhysChunk;
550 }
551 }
552 ++thisChunk;
553 }
554 ++currDword;
555 currChunk += 64;
556 }
557
558 /*
559 * main store size (in chunks) is
560 * totalChunks - hptSizeChunks
561 * which should be equal to
562 * nextPhysChunk
563 */
564 systemcfg->physicalMemorySize = chunk_to_addr(nextPhysChunk);
565}
566
567/*
568 * Document me.
569 */
570static void __init iSeries_setup_arch(void)
571{
572 unsigned procIx = get_paca()->lppaca.dyn_hv_phys_proc_index;
573
574 if (get_paca()->lppaca.shared_proc) {
575 ppc_md.idle_loop = iseries_shared_idle;
576 printk(KERN_INFO "Using shared processor idle loop\n");
577 } else {
578 ppc_md.idle_loop = iseries_dedicated_idle;
579 printk(KERN_INFO "Using dedicated idle loop\n");
580 }
581
582 /* Setup the Lp Event Queue */
583 setup_hvlpevent_queue();
584
585 printk("Max logical processors = %d\n",
586 itVpdAreas.xSlicMaxLogicalProcs);
587 printk("Max physical processors = %d\n",
588 itVpdAreas.xSlicMaxPhysicalProcs);
589
590 systemcfg->processor = xIoHriProcessorVpd[procIx].xPVR;
591 printk("Processor version = %x\n", systemcfg->processor);
592}
593
594static void iSeries_get_cpuinfo(struct seq_file *m)
595{
596 seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n");
597}
598
599/*
600 * Document me.
601 * and Implement me.
602 */
603static int iSeries_get_irq(struct pt_regs *regs)
604{
605 /* -2 means ignore this interrupt */
606 return -2;
607}
608
609/*
610 * Document me.
611 */
612static void iSeries_restart(char *cmd)
613{
614 mf_reboot();
615}
616
617/*
618 * Document me.
619 */
620static void iSeries_power_off(void)
621{
622 mf_power_off();
623}
624
625/*
626 * Document me.
627 */
628static void iSeries_halt(void)
629{
630 mf_power_off();
631}
632
633static void __init iSeries_progress(char * st, unsigned short code)
634{
635 printk("Progress: [%04x] - %s\n", (unsigned)code, st);
636 if (!piranha_simulator && mf_initialized) {
637 if (code != 0xffff)
638 mf_display_progress(code);
639 else
640 mf_clear_src();
641 }
642}
643
644static void __init iSeries_fixup_klimit(void)
645{
646 /*
647 * Change klimit to take into account any ram disk
648 * that may be included
649 */
650 if (naca.xRamDisk)
651 klimit = KERNELBASE + (u64)naca.xRamDisk +
652 (naca.xRamDiskSize * PAGE_SIZE);
653 else {
654 /*
655 * No ram disk was included - check and see if there
656 * was an embedded system map. Change klimit to take
657 * into account any embedded system map
658 */
659 if (embedded_sysmap_end)
660 klimit = KERNELBASE + ((embedded_sysmap_end + 4095) &
661 0xfffffffffffff000);
662 }
663}
664
665static int __init iSeries_src_init(void)
666{
667 /* clear the progress line */
668 ppc_md.progress(" ", 0xffff);
669 return 0;
670}
671
672late_initcall(iSeries_src_init);
673
674static inline void process_iSeries_events(void)
675{
676 asm volatile ("li 0,0x5555; sc" : : : "r0", "r3");
677}
678
679static void yield_shared_processor(void)
680{
681 unsigned long tb;
682
683 HvCall_setEnabledInterrupts(HvCall_MaskIPI |
684 HvCall_MaskLpEvent |
685 HvCall_MaskLpProd |
686 HvCall_MaskTimeout);
687
688 tb = get_tb();
689 /* Compute future tb value when yield should expire */
690 HvCall_yieldProcessor(HvCall_YieldTimed, tb+tb_ticks_per_jiffy);
691
692 /*
693 * The decrementer stops during the yield. Force a fake decrementer
694 * here and let the timer_interrupt code sort out the actual time.
695 */
696 get_paca()->lppaca.int_dword.fields.decr_int = 1;
697 process_iSeries_events();
698}
699
700static int iseries_shared_idle(void)
701{
702 while (1) {
703 while (!need_resched() && !hvlpevent_is_pending()) {
704 local_irq_disable();
705 ppc64_runlatch_off();
706
707 /* Recheck with irqs off */
708 if (!need_resched() && !hvlpevent_is_pending())
709 yield_shared_processor();
710
711 HMT_medium();
712 local_irq_enable();
713 }
714
715 ppc64_runlatch_on();
716
717 if (hvlpevent_is_pending())
718 process_iSeries_events();
719
720 schedule();
721 }
722
723 return 0;
724}
725
726static int iseries_dedicated_idle(void)
727{
728 long oldval;
729
730 while (1) {
731 oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED);
732
733 if (!oldval) {
734 set_thread_flag(TIF_POLLING_NRFLAG);
735
736 while (!need_resched()) {
737 ppc64_runlatch_off();
738 HMT_low();
739
740 if (hvlpevent_is_pending()) {
741 HMT_medium();
742 ppc64_runlatch_on();
743 process_iSeries_events();
744 }
745 }
746
747 HMT_medium();
748 clear_thread_flag(TIF_POLLING_NRFLAG);
749 } else {
750 set_need_resched();
751 }
752
753 ppc64_runlatch_on();
754 schedule();
755 }
756
757 return 0;
758}
759
760#ifndef CONFIG_PCI
761void __init iSeries_init_IRQ(void) { }
762#endif
763
764static int __init iseries_probe(int platform)
765{
766 return PLATFORM_ISERIES_LPAR == platform;
767}
768
769struct machdep_calls __initdata iseries_md = {
770 .setup_arch = iSeries_setup_arch,
771 .get_cpuinfo = iSeries_get_cpuinfo,
772 .init_IRQ = iSeries_init_IRQ,
773 .get_irq = iSeries_get_irq,
774 .init_early = iSeries_init_early,
775 .pcibios_fixup = iSeries_pci_final_fixup,
776 .restart = iSeries_restart,
777 .power_off = iSeries_power_off,
778 .halt = iSeries_halt,
779 .get_boot_time = iSeries_get_boot_time,
780 .set_rtc_time = iSeries_set_rtc_time,
781 .get_rtc_time = iSeries_get_rtc_time,
782 .calibrate_decr = generic_calibrate_decr,
783 .progress = iSeries_progress,
784 .probe = iseries_probe,
785 /* XXX Implement enable_pmcs for iSeries */
786};
787
788struct blob {
789 unsigned char data[PAGE_SIZE];
790 unsigned long next;
791};
792
793struct iseries_flat_dt {
794 struct boot_param_header header;
795 u64 reserve_map[2];
796 struct blob dt;
797 struct blob strings;
798};
799
800struct iseries_flat_dt iseries_dt;
801
802void dt_init(struct iseries_flat_dt *dt)
803{
804 dt->header.off_mem_rsvmap =
805 offsetof(struct iseries_flat_dt, reserve_map);
806 dt->header.off_dt_struct = offsetof(struct iseries_flat_dt, dt);
807 dt->header.off_dt_strings = offsetof(struct iseries_flat_dt, strings);
808 dt->header.totalsize = sizeof(struct iseries_flat_dt);
809 dt->header.dt_strings_size = sizeof(struct blob);
810
811 /* There is no notion of hardware cpu id on iSeries */
812 dt->header.boot_cpuid_phys = smp_processor_id();
813
814 dt->dt.next = (unsigned long)&dt->dt.data;
815 dt->strings.next = (unsigned long)&dt->strings.data;
816
817 dt->header.magic = OF_DT_HEADER;
818 dt->header.version = 0x10;
819 dt->header.last_comp_version = 0x10;
820
821 dt->reserve_map[0] = 0;
822 dt->reserve_map[1] = 0;
823}
824
825void dt_check_blob(struct blob *b)
826{
827 if (b->next >= (unsigned long)&b->next) {
828 DBG("Ran out of space in flat device tree blob!\n");
829 BUG();
830 }
831}
832
833void dt_push_u32(struct iseries_flat_dt *dt, u32 value)
834{
835 *((u32*)dt->dt.next) = value;
836 dt->dt.next += sizeof(u32);
837
838 dt_check_blob(&dt->dt);
839}
840
841void dt_push_u64(struct iseries_flat_dt *dt, u64 value)
842{
843 *((u64*)dt->dt.next) = value;
844 dt->dt.next += sizeof(u64);
845
846 dt_check_blob(&dt->dt);
847}
848
849unsigned long dt_push_bytes(struct blob *blob, char *data, int len)
850{
851 unsigned long start = blob->next - (unsigned long)blob->data;
852
853 memcpy((char *)blob->next, data, len);
854 blob->next = _ALIGN(blob->next + len, 4);
855
856 dt_check_blob(blob);
857
858 return start;
859}
860
861void dt_start_node(struct iseries_flat_dt *dt, char *name)
862{
863 dt_push_u32(dt, OF_DT_BEGIN_NODE);
864 dt_push_bytes(&dt->dt, name, strlen(name) + 1);
865}
866
867#define dt_end_node(dt) dt_push_u32(dt, OF_DT_END_NODE)
868
869void dt_prop(struct iseries_flat_dt *dt, char *name, char *data, int len)
870{
871 unsigned long offset;
872
873 dt_push_u32(dt, OF_DT_PROP);
874
875 /* Length of the data */
876 dt_push_u32(dt, len);
877
878 /* Put the property name in the string blob. */
879 offset = dt_push_bytes(&dt->strings, name, strlen(name) + 1);
880
881 /* The offset of the properties name in the string blob. */
882 dt_push_u32(dt, (u32)offset);
883
884 /* The actual data. */
885 dt_push_bytes(&dt->dt, data, len);
886}
887
888void dt_prop_str(struct iseries_flat_dt *dt, char *name, char *data)
889{
890 dt_prop(dt, name, data, strlen(data) + 1); /* + 1 for NULL */
891}
892
893void dt_prop_u32(struct iseries_flat_dt *dt, char *name, u32 data)
894{
895 dt_prop(dt, name, (char *)&data, sizeof(u32));
896}
897
898void dt_prop_u64(struct iseries_flat_dt *dt, char *name, u64 data)
899{
900 dt_prop(dt, name, (char *)&data, sizeof(u64));
901}
902
903void dt_prop_u64_list(struct iseries_flat_dt *dt, char *name, u64 *data, int n)
904{
905 dt_prop(dt, name, (char *)data, sizeof(u64) * n);
906}
907
908void dt_prop_empty(struct iseries_flat_dt *dt, char *name)
909{
910 dt_prop(dt, name, NULL, 0);
911}
912
913void dt_cpus(struct iseries_flat_dt *dt)
914{
915 unsigned char buf[32];
916 unsigned char *p;
917 unsigned int i, index;
918 struct IoHriProcessorVpd *d;
919
920 /* yuck */
921 snprintf(buf, 32, "PowerPC,%s", cur_cpu_spec->cpu_name);
922 p = strchr(buf, ' ');
923 if (!p) p = buf + strlen(buf);
924
925 dt_start_node(dt, "cpus");
926 dt_prop_u32(dt, "#address-cells", 1);
927 dt_prop_u32(dt, "#size-cells", 0);
928
929 for (i = 0; i < NR_CPUS; i++) {
930 if (paca[i].lppaca.dyn_proc_status >= 2)
931 continue;
932
933 snprintf(p, 32 - (p - buf), "@%d", i);
934 dt_start_node(dt, buf);
935
936 dt_prop_str(dt, "device_type", "cpu");
937
938 index = paca[i].lppaca.dyn_hv_phys_proc_index;
939 d = &xIoHriProcessorVpd[index];
940
941 dt_prop_u32(dt, "i-cache-size", d->xInstCacheSize * 1024);
942 dt_prop_u32(dt, "i-cache-line-size", d->xInstCacheOperandSize);
943
944 dt_prop_u32(dt, "d-cache-size", d->xDataL1CacheSizeKB * 1024);
945 dt_prop_u32(dt, "d-cache-line-size", d->xDataCacheOperandSize);
946
947 /* magic conversions to Hz copied from old code */
948 dt_prop_u32(dt, "clock-frequency",
949 ((1UL << 34) * 1000000) / d->xProcFreq);
950 dt_prop_u32(dt, "timebase-frequency",
951 ((1UL << 32) * 1000000) / d->xTimeBaseFreq);
952
953 dt_prop_u32(dt, "reg", i);
954
955 dt_end_node(dt);
956 }
957
958 dt_end_node(dt);
959}
960
961void build_flat_dt(struct iseries_flat_dt *dt)
962{
963 u64 tmp[2];
964
965 dt_init(dt);
966
967 dt_start_node(dt, "");
968
969 dt_prop_u32(dt, "#address-cells", 2);
970 dt_prop_u32(dt, "#size-cells", 2);
971
972 /* /memory */
973 dt_start_node(dt, "memory@0");
974 dt_prop_str(dt, "name", "memory");
975 dt_prop_str(dt, "device_type", "memory");
976 tmp[0] = 0;
977 tmp[1] = systemcfg->physicalMemorySize;
978 dt_prop_u64_list(dt, "reg", tmp, 2);
979 dt_end_node(dt);
980
981 /* /chosen */
982 dt_start_node(dt, "chosen");
983 dt_prop_u32(dt, "linux,platform", PLATFORM_ISERIES_LPAR);
984 dt_end_node(dt);
985
986 dt_cpus(dt);
987
988 dt_end_node(dt);
989
990 dt_push_u32(dt, OF_DT_END);
991}
992
993void * __init iSeries_early_setup(void)
994{
995 iSeries_fixup_klimit();
996
997 /*
998 * Initialize the table which translate Linux physical addresses to
999 * AS/400 absolute addresses
1000 */
1001 build_iSeries_Memory_Map();
1002
1003 build_flat_dt(&iseries_dt);
1004
1005 return (void *) __pa(&iseries_dt);
1006}
diff --git a/arch/powerpc/platforms/iseries/setup.h b/arch/powerpc/platforms/iseries/setup.h
new file mode 100644
index 000000000000..6da89ae991ce
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/setup.h
@@ -0,0 +1,24 @@
1/*
2 * Copyright (c) 2000 Mike Corrigan <mikejc@us.ibm.com>
3 * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
4 *
5 * Description:
6 * Architecture- / platform-specific boot-time initialization code for
7 * the IBM AS/400 LPAR. Adapted from original code by Grant Erickson and
8 * code by Gary Thomas, Cort Dougan <cort@cs.nmt.edu>, and Dan Malek
9 * <dan@netx4.com>.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version
14 * 2 of the License, or (at your option) any later version.
15 */
16
17#ifndef __ISERIES_SETUP_H__
18#define __ISERIES_SETUP_H__
19
20extern void iSeries_get_boot_time(struct rtc_time *tm);
21extern int iSeries_set_rtc_time(struct rtc_time *tm);
22extern void iSeries_get_rtc_time(struct rtc_time *tm);
23
24#endif /* __ISERIES_SETUP_H__ */
diff --git a/arch/powerpc/platforms/iseries/smp.c b/arch/powerpc/platforms/iseries/smp.c
new file mode 100644
index 000000000000..f720916682f6
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/smp.c
@@ -0,0 +1,121 @@
1/*
2 * SMP support for iSeries machines.
3 *
4 * Dave Engebretsen, Peter Bergner, and
5 * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com
6 *
7 * Plus various changes from other IBM teams...
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 */
14
15#undef DEBUG
16
17#include <linux/config.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/sched.h>
21#include <linux/smp.h>
22#include <linux/smp_lock.h>
23#include <linux/interrupt.h>
24#include <linux/kernel_stat.h>
25#include <linux/delay.h>
26#include <linux/init.h>
27#include <linux/spinlock.h>
28#include <linux/cache.h>
29#include <linux/err.h>
30#include <linux/sysdev.h>
31#include <linux/cpu.h>
32
33#include <asm/ptrace.h>
34#include <asm/atomic.h>
35#include <asm/irq.h>
36#include <asm/page.h>
37#include <asm/pgtable.h>
38#include <asm/io.h>
39#include <asm/smp.h>
40#include <asm/paca.h>
41#include <asm/iSeries/HvCall.h>
42#include <asm/time.h>
43#include <asm/ppcdebug.h>
44#include <asm/machdep.h>
45#include <asm/cputable.h>
46#include <asm/system.h>
47
48static unsigned long iSeries_smp_message[NR_CPUS];
49
50void iSeries_smp_message_recv(struct pt_regs *regs)
51{
52 int cpu = smp_processor_id();
53 int msg;
54
55 if (num_online_cpus() < 2)
56 return;
57
58 for (msg = 0; msg < 4; msg++)
59 if (test_and_clear_bit(msg, &iSeries_smp_message[cpu]))
60 smp_message_recv(msg, regs);
61}
62
63static inline void smp_iSeries_do_message(int cpu, int msg)
64{
65 set_bit(msg, &iSeries_smp_message[cpu]);
66 HvCall_sendIPI(&(paca[cpu]));
67}
68
69static void smp_iSeries_message_pass(int target, int msg)
70{
71 int i;
72
73 if (target < NR_CPUS)
74 smp_iSeries_do_message(target, msg);
75 else {
76 for_each_online_cpu(i) {
77 if ((target == MSG_ALL_BUT_SELF) &&
78 (i == smp_processor_id()))
79 continue;
80 smp_iSeries_do_message(i, msg);
81 }
82 }
83}
84
85static int smp_iSeries_probe(void)
86{
87 return cpus_weight(cpu_possible_map);
88}
89
90static void smp_iSeries_kick_cpu(int nr)
91{
92 BUG_ON((nr < 0) || (nr >= NR_CPUS));
93
94 /* Verify that our partition has a processor nr */
95 if (paca[nr].lppaca.dyn_proc_status >= 2)
96 return;
97
98 /* The processor is currently spinning, waiting
99 * for the cpu_start field to become non-zero
100 * After we set cpu_start, the processor will
101 * continue on to secondary_start in iSeries_head.S
102 */
103 paca[nr].cpu_start = 1;
104}
105
106static void __devinit smp_iSeries_setup_cpu(int nr)
107{
108}
109
110static struct smp_ops_t iSeries_smp_ops = {
111 .message_pass = smp_iSeries_message_pass,
112 .probe = smp_iSeries_probe,
113 .kick_cpu = smp_iSeries_kick_cpu,
114 .setup_cpu = smp_iSeries_setup_cpu,
115};
116
117/* This is called very early. */
118void __init smp_init_iSeries(void)
119{
120 smp_ops = &iSeries_smp_ops;
121}
diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c
new file mode 100644
index 000000000000..c0f7d2e9153f
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/vio.c
@@ -0,0 +1,156 @@
1/*
2 * IBM PowerPC iSeries Virtual I/O Infrastructure Support.
3 *
4 * Copyright (c) 2005 Stephen Rothwell, IBM Corp.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11#include <linux/types.h>
12#include <linux/device.h>
13#include <linux/init.h>
14
15#include <asm/vio.h>
16#include <asm/iommu.h>
17#include <asm/tce.h>
18#include <asm/abs_addr.h>
19#include <asm/page.h>
20#include <asm/iSeries/vio.h>
21#include <asm/iSeries/HvTypes.h>
22#include <asm/iSeries/HvLpConfig.h>
23#include <asm/iSeries/HvCallXm.h>
24
25struct device *iSeries_vio_dev = &vio_bus_device.dev;
26EXPORT_SYMBOL(iSeries_vio_dev);
27
28static struct iommu_table veth_iommu_table;
29static struct iommu_table vio_iommu_table;
30
31static void __init iommu_vio_init(void)
32{
33 struct iommu_table *t;
34 struct iommu_table_cb cb;
35 unsigned long cbp;
36 unsigned long itc_entries;
37
38 cb.itc_busno = 255; /* Bus 255 is the virtual bus */
39 cb.itc_virtbus = 0xff; /* Ask for virtual bus */
40
41 cbp = virt_to_abs(&cb);
42 HvCallXm_getTceTableParms(cbp);
43
44 itc_entries = cb.itc_size * PAGE_SIZE / sizeof(union tce_entry);
45 veth_iommu_table.it_size = itc_entries / 2;
46 veth_iommu_table.it_busno = cb.itc_busno;
47 veth_iommu_table.it_offset = cb.itc_offset;
48 veth_iommu_table.it_index = cb.itc_index;
49 veth_iommu_table.it_type = TCE_VB;
50 veth_iommu_table.it_blocksize = 1;
51
52 t = iommu_init_table(&veth_iommu_table);
53
54 if (!t)
55 printk("Virtual Bus VETH TCE table failed.\n");
56
57 vio_iommu_table.it_size = itc_entries - veth_iommu_table.it_size;
58 vio_iommu_table.it_busno = cb.itc_busno;
59 vio_iommu_table.it_offset = cb.itc_offset +
60 veth_iommu_table.it_size;
61 vio_iommu_table.it_index = cb.itc_index;
62 vio_iommu_table.it_type = TCE_VB;
63 vio_iommu_table.it_blocksize = 1;
64
65 t = iommu_init_table(&vio_iommu_table);
66
67 if (!t)
68 printk("Virtual Bus VIO TCE table failed.\n");
69}
70
71/**
72 * vio_register_device_iseries: - Register a new iSeries vio device.
73 * @voidev: The device to register.
74 */
75static struct vio_dev *__init vio_register_device_iseries(char *type,
76 uint32_t unit_num)
77{
78 struct vio_dev *viodev;
79
80 /* allocate a vio_dev for this device */
81 viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL);
82 if (!viodev)
83 return NULL;
84 memset(viodev, 0, sizeof(struct vio_dev));
85
86 snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%s%d", type, unit_num);
87
88 viodev->name = viodev->dev.bus_id;
89 viodev->type = type;
90 viodev->unit_address = unit_num;
91 viodev->iommu_table = &vio_iommu_table;
92 if (vio_register_device(viodev) == NULL) {
93 kfree(viodev);
94 return NULL;
95 }
96 return viodev;
97}
98
99void __init probe_bus_iseries(void)
100{
101 HvLpIndexMap vlan_map;
102 struct vio_dev *viodev;
103 int i;
104
105 /* there is only one of each of these */
106 vio_register_device_iseries("viocons", 0);
107 vio_register_device_iseries("vscsi", 0);
108
109 vlan_map = HvLpConfig_getVirtualLanIndexMap();
110 for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) {
111 if ((vlan_map & (0x8000 >> i)) == 0)
112 continue;
113 viodev = vio_register_device_iseries("vlan", i);
114 /* veth is special and has it own iommu_table */
115 viodev->iommu_table = &veth_iommu_table;
116 }
117 for (i = 0; i < HVMAXARCHITECTEDVIRTUALDISKS; i++)
118 vio_register_device_iseries("viodasd", i);
119 for (i = 0; i < HVMAXARCHITECTEDVIRTUALCDROMS; i++)
120 vio_register_device_iseries("viocd", i);
121 for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++)
122 vio_register_device_iseries("viotape", i);
123}
124
125/**
126 * vio_match_device_iseries: - Tell if a iSeries VIO device matches a
127 * vio_device_id
128 */
129static int vio_match_device_iseries(const struct vio_device_id *id,
130 const struct vio_dev *dev)
131{
132 return strncmp(dev->type, id->type, strlen(id->type)) == 0;
133}
134
135static struct vio_bus_ops vio_bus_ops_iseries = {
136 .match = vio_match_device_iseries,
137};
138
139/**
140 * vio_bus_init_iseries: - Initialize the iSeries virtual IO bus
141 */
142static int __init vio_bus_init_iseries(void)
143{
144 int err;
145
146 err = vio_bus_init(&vio_bus_ops_iseries);
147 if (err == 0) {
148 iommu_vio_init();
149 vio_bus_device.iommu_table = &vio_iommu_table;
150 iSeries_vio_dev = &vio_bus_device.dev;
151 probe_bus_iseries();
152 }
153 return err;
154}
155
156__initcall(vio_bus_init_iseries);
diff --git a/arch/powerpc/platforms/iseries/viopath.c b/arch/powerpc/platforms/iseries/viopath.c
new file mode 100644
index 000000000000..c0c767bd37f1
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/viopath.c
@@ -0,0 +1,672 @@
1/* -*- linux-c -*-
2 *
3 * iSeries Virtual I/O Message Path code
4 *
5 * Authors: Dave Boutcher <boutcher@us.ibm.com>
6 * Ryan Arnold <ryanarn@us.ibm.com>
7 * Colin Devilbiss <devilbis@us.ibm.com>
8 *
9 * (C) Copyright 2000-2005 IBM Corporation
10 *
11 * This code is used by the iSeries virtual disk, cd,
12 * tape, and console to communicate with OS/400 in another
13 * partition.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License as
17 * published by the Free Software Foundation; either version 2 of the
18 * License, or (at your option) anyu later version.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software Foundation,
27 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 *
29 */
30#include <linux/module.h>
31#include <linux/kernel.h>
32#include <linux/errno.h>
33#include <linux/vmalloc.h>
34#include <linux/string.h>
35#include <linux/proc_fs.h>
36#include <linux/dma-mapping.h>
37#include <linux/wait.h>
38#include <linux/seq_file.h>
39#include <linux/smp_lock.h>
40#include <linux/interrupt.h>
41
42#include <asm/system.h>
43#include <asm/uaccess.h>
44#include <asm/iSeries/HvTypes.h>
45#include <asm/iSeries/ItExtVpdPanel.h>
46#include <asm/iSeries/HvLpEvent.h>
47#include <asm/iSeries/HvLpConfig.h>
48#include <asm/iSeries/mf.h>
49#include <asm/iSeries/vio.h>
50
51/* Status of the path to each other partition in the system.
52 * This is overkill, since we will only ever establish connections
53 * to our hosting partition and the primary partition on the system.
54 * But this allows for other support in the future.
55 */
56static struct viopathStatus {
57 int isOpen; /* Did we open the path? */
58 int isActive; /* Do we have a mon msg outstanding */
59 int users[VIO_MAX_SUBTYPES];
60 HvLpInstanceId mSourceInst;
61 HvLpInstanceId mTargetInst;
62 int numberAllocated;
63} viopathStatus[HVMAXARCHITECTEDLPS];
64
65static DEFINE_SPINLOCK(statuslock);
66
67/*
68 * For each kind of event we allocate a buffer that is
69 * guaranteed not to cross a page boundary
70 */
71static unsigned char event_buffer[VIO_MAX_SUBTYPES * 256] __page_aligned;
72static atomic_t event_buffer_available[VIO_MAX_SUBTYPES];
73static int event_buffer_initialised;
74
75static void handleMonitorEvent(struct HvLpEvent *event);
76
77/*
78 * We use this structure to handle asynchronous responses. The caller
79 * blocks on the semaphore and the handler posts the semaphore. However,
80 * if system_state is not SYSTEM_RUNNING, then wait_atomic is used ...
81 */
82struct alloc_parms {
83 struct semaphore sem;
84 int number;
85 atomic_t wait_atomic;
86 int used_wait_atomic;
87};
88
89/* Put a sequence number in each mon msg. The value is not
90 * important. Start at something other than 0 just for
91 * readability. wrapping this is ok.
92 */
93static u8 viomonseq = 22;
94
95/* Our hosting logical partition. We get this at startup
96 * time, and different modules access this variable directly.
97 */
98HvLpIndex viopath_hostLp = HvLpIndexInvalid;
99EXPORT_SYMBOL(viopath_hostLp);
100HvLpIndex viopath_ourLp = HvLpIndexInvalid;
101EXPORT_SYMBOL(viopath_ourLp);
102
103/* For each kind of incoming event we set a pointer to a
104 * routine to call.
105 */
106static vio_event_handler_t *vio_handler[VIO_MAX_SUBTYPES];
107
108#define VIOPATH_KERN_WARN KERN_WARNING "viopath: "
109#define VIOPATH_KERN_INFO KERN_INFO "viopath: "
110
111static int proc_viopath_show(struct seq_file *m, void *v)
112{
113 char *buf;
114 u16 vlanMap;
115 dma_addr_t handle;
116 HvLpEvent_Rc hvrc;
117 DECLARE_MUTEX_LOCKED(Semaphore);
118
119 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
120 if (!buf)
121 return 0;
122 memset(buf, 0, PAGE_SIZE);
123
124 handle = dma_map_single(iSeries_vio_dev, buf, PAGE_SIZE,
125 DMA_FROM_DEVICE);
126
127 hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
128 HvLpEvent_Type_VirtualIo,
129 viomajorsubtype_config | vioconfigget,
130 HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
131 viopath_sourceinst(viopath_hostLp),
132 viopath_targetinst(viopath_hostLp),
133 (u64)(unsigned long)&Semaphore, VIOVERSION << 16,
134 ((u64)handle) << 32, PAGE_SIZE, 0, 0);
135
136 if (hvrc != HvLpEvent_Rc_Good)
137 printk(VIOPATH_KERN_WARN "hv error on op %d\n", (int)hvrc);
138
139 down(&Semaphore);
140
141 vlanMap = HvLpConfig_getVirtualLanIndexMap();
142
143 buf[PAGE_SIZE-1] = '\0';
144 seq_printf(m, "%s", buf);
145 seq_printf(m, "AVAILABLE_VETH=%x\n", vlanMap);
146 seq_printf(m, "SRLNBR=%c%c%c%c%c%c%c\n",
147 e2a(xItExtVpdPanel.mfgID[2]),
148 e2a(xItExtVpdPanel.mfgID[3]),
149 e2a(xItExtVpdPanel.systemSerial[1]),
150 e2a(xItExtVpdPanel.systemSerial[2]),
151 e2a(xItExtVpdPanel.systemSerial[3]),
152 e2a(xItExtVpdPanel.systemSerial[4]),
153 e2a(xItExtVpdPanel.systemSerial[5]));
154
155 dma_unmap_single(iSeries_vio_dev, handle, PAGE_SIZE, DMA_FROM_DEVICE);
156 kfree(buf);
157
158 return 0;
159}
160
161static int proc_viopath_open(struct inode *inode, struct file *file)
162{
163 return single_open(file, proc_viopath_show, NULL);
164}
165
166static struct file_operations proc_viopath_operations = {
167 .open = proc_viopath_open,
168 .read = seq_read,
169 .llseek = seq_lseek,
170 .release = single_release,
171};
172
173static int __init vio_proc_init(void)
174{
175 struct proc_dir_entry *e;
176
177 e = create_proc_entry("iSeries/config", 0, NULL);
178 if (e)
179 e->proc_fops = &proc_viopath_operations;
180
181 return 0;
182}
183__initcall(vio_proc_init);
184
185/* See if a given LP is active. Allow for invalid lps to be passed in
186 * and just return invalid
187 */
188int viopath_isactive(HvLpIndex lp)
189{
190 if (lp == HvLpIndexInvalid)
191 return 0;
192 if (lp < HVMAXARCHITECTEDLPS)
193 return viopathStatus[lp].isActive;
194 else
195 return 0;
196}
197EXPORT_SYMBOL(viopath_isactive);
198
199/*
200 * We cache the source and target instance ids for each
201 * partition.
202 */
203HvLpInstanceId viopath_sourceinst(HvLpIndex lp)
204{
205 return viopathStatus[lp].mSourceInst;
206}
207EXPORT_SYMBOL(viopath_sourceinst);
208
209HvLpInstanceId viopath_targetinst(HvLpIndex lp)
210{
211 return viopathStatus[lp].mTargetInst;
212}
213EXPORT_SYMBOL(viopath_targetinst);
214
215/*
216 * Send a monitor message. This is a message with the acknowledge
217 * bit on that the other side will NOT explicitly acknowledge. When
218 * the other side goes down, the hypervisor will acknowledge any
219 * outstanding messages....so we will know when the other side dies.
220 */
221static void sendMonMsg(HvLpIndex remoteLp)
222{
223 HvLpEvent_Rc hvrc;
224
225 viopathStatus[remoteLp].mSourceInst =
226 HvCallEvent_getSourceLpInstanceId(remoteLp,
227 HvLpEvent_Type_VirtualIo);
228 viopathStatus[remoteLp].mTargetInst =
229 HvCallEvent_getTargetLpInstanceId(remoteLp,
230 HvLpEvent_Type_VirtualIo);
231
232 /*
233 * Deliberately ignore the return code here. if we call this
234 * more than once, we don't care.
235 */
236 vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent);
237
238 hvrc = HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo,
239 viomajorsubtype_monitor, HvLpEvent_AckInd_DoAck,
240 HvLpEvent_AckType_DeferredAck,
241 viopathStatus[remoteLp].mSourceInst,
242 viopathStatus[remoteLp].mTargetInst,
243 viomonseq++, 0, 0, 0, 0, 0);
244
245 if (hvrc == HvLpEvent_Rc_Good)
246 viopathStatus[remoteLp].isActive = 1;
247 else {
248 printk(VIOPATH_KERN_WARN "could not connect to partition %d\n",
249 remoteLp);
250 viopathStatus[remoteLp].isActive = 0;
251 }
252}
253
254static void handleMonitorEvent(struct HvLpEvent *event)
255{
256 HvLpIndex remoteLp;
257 int i;
258
259 /*
260 * This handler is _also_ called as part of the loop
261 * at the end of this routine, so it must be able to
262 * ignore NULL events...
263 */
264 if (!event)
265 return;
266
267 /*
268 * First see if this is just a normal monitor message from the
269 * other partition
270 */
271 if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
272 remoteLp = event->xSourceLp;
273 if (!viopathStatus[remoteLp].isActive)
274 sendMonMsg(remoteLp);
275 return;
276 }
277
278 /*
279 * This path is for an acknowledgement; the other partition
280 * died
281 */
282 remoteLp = event->xTargetLp;
283 if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) ||
284 (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) {
285 printk(VIOPATH_KERN_WARN "ignoring ack....mismatched instances\n");
286 return;
287 }
288
289 printk(VIOPATH_KERN_WARN "partition %d ended\n", remoteLp);
290
291 viopathStatus[remoteLp].isActive = 0;
292
293 /*
294 * For each active handler, pass them a NULL
295 * message to indicate that the other partition
296 * died
297 */
298 for (i = 0; i < VIO_MAX_SUBTYPES; i++) {
299 if (vio_handler[i] != NULL)
300 (*vio_handler[i])(NULL);
301 }
302}
303
304int vio_setHandler(int subtype, vio_event_handler_t *beh)
305{
306 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
307 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
308 return -EINVAL;
309 if (vio_handler[subtype] != NULL)
310 return -EBUSY;
311 vio_handler[subtype] = beh;
312 return 0;
313}
314EXPORT_SYMBOL(vio_setHandler);
315
316int vio_clearHandler(int subtype)
317{
318 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
319 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
320 return -EINVAL;
321 if (vio_handler[subtype] == NULL)
322 return -EAGAIN;
323 vio_handler[subtype] = NULL;
324 return 0;
325}
326EXPORT_SYMBOL(vio_clearHandler);
327
328static void handleConfig(struct HvLpEvent *event)
329{
330 if (!event)
331 return;
332 if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
333 printk(VIOPATH_KERN_WARN
334 "unexpected config request from partition %d",
335 event->xSourceLp);
336
337 if ((event->xFlags.xFunction == HvLpEvent_Function_Int) &&
338 (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) {
339 event->xRc = HvLpEvent_Rc_InvalidSubtype;
340 HvCallEvent_ackLpEvent(event);
341 }
342 return;
343 }
344
345 up((struct semaphore *)event->xCorrelationToken);
346}
347
348/*
349 * Initialization of the hosting partition
350 */
351void vio_set_hostlp(void)
352{
353 /*
354 * If this has already been set then we DON'T want to either change
355 * it or re-register the proc file system
356 */
357 if (viopath_hostLp != HvLpIndexInvalid)
358 return;
359
360 /*
361 * Figure out our hosting partition. This isn't allowed to change
362 * while we're active
363 */
364 viopath_ourLp = HvLpConfig_getLpIndex();
365 viopath_hostLp = HvLpConfig_getHostingLpIndex(viopath_ourLp);
366
367 if (viopath_hostLp != HvLpIndexInvalid)
368 vio_setHandler(viomajorsubtype_config, handleConfig);
369}
370EXPORT_SYMBOL(vio_set_hostlp);
371
372static void vio_handleEvent(struct HvLpEvent *event, struct pt_regs *regs)
373{
374 HvLpIndex remoteLp;
375 int subtype = (event->xSubtype & VIOMAJOR_SUBTYPE_MASK)
376 >> VIOMAJOR_SUBTYPE_SHIFT;
377
378 if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
379 remoteLp = event->xSourceLp;
380 /*
381 * The isActive is checked because if the hosting partition
382 * went down and came back up it would not be active but it
383 * would have different source and target instances, in which
384 * case we'd want to reset them. This case really protects
385 * against an unauthorized active partition sending interrupts
386 * or acks to this linux partition.
387 */
388 if (viopathStatus[remoteLp].isActive
389 && (event->xSourceInstanceId !=
390 viopathStatus[remoteLp].mTargetInst)) {
391 printk(VIOPATH_KERN_WARN
392 "message from invalid partition. "
393 "int msg rcvd, source inst (%d) doesnt match (%d)\n",
394 viopathStatus[remoteLp].mTargetInst,
395 event->xSourceInstanceId);
396 return;
397 }
398
399 if (viopathStatus[remoteLp].isActive
400 && (event->xTargetInstanceId !=
401 viopathStatus[remoteLp].mSourceInst)) {
402 printk(VIOPATH_KERN_WARN
403 "message from invalid partition. "
404 "int msg rcvd, target inst (%d) doesnt match (%d)\n",
405 viopathStatus[remoteLp].mSourceInst,
406 event->xTargetInstanceId);
407 return;
408 }
409 } else {
410 remoteLp = event->xTargetLp;
411 if (event->xSourceInstanceId !=
412 viopathStatus[remoteLp].mSourceInst) {
413 printk(VIOPATH_KERN_WARN
414 "message from invalid partition. "
415 "ack msg rcvd, source inst (%d) doesnt match (%d)\n",
416 viopathStatus[remoteLp].mSourceInst,
417 event->xSourceInstanceId);
418 return;
419 }
420
421 if (event->xTargetInstanceId !=
422 viopathStatus[remoteLp].mTargetInst) {
423 printk(VIOPATH_KERN_WARN
424 "message from invalid partition. "
425 "viopath: ack msg rcvd, target inst (%d) doesnt match (%d)\n",
426 viopathStatus[remoteLp].mTargetInst,
427 event->xTargetInstanceId);
428 return;
429 }
430 }
431
432 if (vio_handler[subtype] == NULL) {
433 printk(VIOPATH_KERN_WARN
434 "unexpected virtual io event subtype %d from partition %d\n",
435 event->xSubtype, remoteLp);
436 /* No handler. Ack if necessary */
437 if ((event->xFlags.xFunction == HvLpEvent_Function_Int) &&
438 (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) {
439 event->xRc = HvLpEvent_Rc_InvalidSubtype;
440 HvCallEvent_ackLpEvent(event);
441 }
442 return;
443 }
444
445 /* This innocuous little line is where all the real work happens */
446 (*vio_handler[subtype])(event);
447}
448
449static void viopath_donealloc(void *parm, int number)
450{
451 struct alloc_parms *parmsp = parm;
452
453 parmsp->number = number;
454 if (parmsp->used_wait_atomic)
455 atomic_set(&parmsp->wait_atomic, 0);
456 else
457 up(&parmsp->sem);
458}
459
460static int allocateEvents(HvLpIndex remoteLp, int numEvents)
461{
462 struct alloc_parms parms;
463
464 if (system_state != SYSTEM_RUNNING) {
465 parms.used_wait_atomic = 1;
466 atomic_set(&parms.wait_atomic, 1);
467 } else {
468 parms.used_wait_atomic = 0;
469 init_MUTEX_LOCKED(&parms.sem);
470 }
471 mf_allocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo, 250, /* It would be nice to put a real number here! */
472 numEvents, &viopath_donealloc, &parms);
473 if (system_state != SYSTEM_RUNNING) {
474 while (atomic_read(&parms.wait_atomic))
475 mb();
476 } else
477 down(&parms.sem);
478 return parms.number;
479}
480
481int viopath_open(HvLpIndex remoteLp, int subtype, int numReq)
482{
483 int i;
484 unsigned long flags;
485 int tempNumAllocated;
486
487 if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid))
488 return -EINVAL;
489
490 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
491 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
492 return -EINVAL;
493
494 spin_lock_irqsave(&statuslock, flags);
495
496 if (!event_buffer_initialised) {
497 for (i = 0; i < VIO_MAX_SUBTYPES; i++)
498 atomic_set(&event_buffer_available[i], 1);
499 event_buffer_initialised = 1;
500 }
501
502 viopathStatus[remoteLp].users[subtype]++;
503
504 if (!viopathStatus[remoteLp].isOpen) {
505 viopathStatus[remoteLp].isOpen = 1;
506 HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo);
507
508 /*
509 * Don't hold the spinlock during an operation that
510 * can sleep.
511 */
512 spin_unlock_irqrestore(&statuslock, flags);
513 tempNumAllocated = allocateEvents(remoteLp, 1);
514 spin_lock_irqsave(&statuslock, flags);
515
516 viopathStatus[remoteLp].numberAllocated += tempNumAllocated;
517
518 if (viopathStatus[remoteLp].numberAllocated == 0) {
519 HvCallEvent_closeLpEventPath(remoteLp,
520 HvLpEvent_Type_VirtualIo);
521
522 spin_unlock_irqrestore(&statuslock, flags);
523 return -ENOMEM;
524 }
525
526 viopathStatus[remoteLp].mSourceInst =
527 HvCallEvent_getSourceLpInstanceId(remoteLp,
528 HvLpEvent_Type_VirtualIo);
529 viopathStatus[remoteLp].mTargetInst =
530 HvCallEvent_getTargetLpInstanceId(remoteLp,
531 HvLpEvent_Type_VirtualIo);
532 HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo,
533 &vio_handleEvent);
534 sendMonMsg(remoteLp);
535 printk(VIOPATH_KERN_INFO "opening connection to partition %d, "
536 "setting sinst %d, tinst %d\n",
537 remoteLp, viopathStatus[remoteLp].mSourceInst,
538 viopathStatus[remoteLp].mTargetInst);
539 }
540
541 spin_unlock_irqrestore(&statuslock, flags);
542 tempNumAllocated = allocateEvents(remoteLp, numReq);
543 spin_lock_irqsave(&statuslock, flags);
544 viopathStatus[remoteLp].numberAllocated += tempNumAllocated;
545 spin_unlock_irqrestore(&statuslock, flags);
546
547 return 0;
548}
549EXPORT_SYMBOL(viopath_open);
550
551int viopath_close(HvLpIndex remoteLp, int subtype, int numReq)
552{
553 unsigned long flags;
554 int i;
555 int numOpen;
556 struct alloc_parms parms;
557
558 if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid))
559 return -EINVAL;
560
561 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
562 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
563 return -EINVAL;
564
565 spin_lock_irqsave(&statuslock, flags);
566 /*
567 * If the viopath_close somehow gets called before a
568 * viopath_open it could decrement to -1 which is a non
569 * recoverable state so we'll prevent this from
570 * happening.
571 */
572 if (viopathStatus[remoteLp].users[subtype] > 0)
573 viopathStatus[remoteLp].users[subtype]--;
574
575 spin_unlock_irqrestore(&statuslock, flags);
576
577 parms.used_wait_atomic = 0;
578 init_MUTEX_LOCKED(&parms.sem);
579 mf_deallocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo,
580 numReq, &viopath_donealloc, &parms);
581 down(&parms.sem);
582
583 spin_lock_irqsave(&statuslock, flags);
584 for (i = 0, numOpen = 0; i < VIO_MAX_SUBTYPES; i++)
585 numOpen += viopathStatus[remoteLp].users[i];
586
587 if ((viopathStatus[remoteLp].isOpen) && (numOpen == 0)) {
588 printk(VIOPATH_KERN_INFO "closing connection to partition %d",
589 remoteLp);
590
591 HvCallEvent_closeLpEventPath(remoteLp,
592 HvLpEvent_Type_VirtualIo);
593 viopathStatus[remoteLp].isOpen = 0;
594 viopathStatus[remoteLp].isActive = 0;
595
596 for (i = 0; i < VIO_MAX_SUBTYPES; i++)
597 atomic_set(&event_buffer_available[i], 0);
598 event_buffer_initialised = 0;
599 }
600 spin_unlock_irqrestore(&statuslock, flags);
601 return 0;
602}
603EXPORT_SYMBOL(viopath_close);
604
605void *vio_get_event_buffer(int subtype)
606{
607 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
608 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
609 return NULL;
610
611 if (atomic_dec_if_positive(&event_buffer_available[subtype]) == 0)
612 return &event_buffer[subtype * 256];
613 else
614 return NULL;
615}
616EXPORT_SYMBOL(vio_get_event_buffer);
617
618void vio_free_event_buffer(int subtype, void *buffer)
619{
620 subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
621 if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) {
622 printk(VIOPATH_KERN_WARN
623 "unexpected subtype %d freeing event buffer\n", subtype);
624 return;
625 }
626
627 if (atomic_read(&event_buffer_available[subtype]) != 0) {
628 printk(VIOPATH_KERN_WARN
629 "freeing unallocated event buffer, subtype %d\n",
630 subtype);
631 return;
632 }
633
634 if (buffer != &event_buffer[subtype * 256]) {
635 printk(VIOPATH_KERN_WARN
636 "freeing invalid event buffer, subtype %d\n", subtype);
637 }
638
639 atomic_set(&event_buffer_available[subtype], 1);
640}
641EXPORT_SYMBOL(vio_free_event_buffer);
642
643static const struct vio_error_entry vio_no_error =
644 { 0, 0, "Non-VIO Error" };
645static const struct vio_error_entry vio_unknown_error =
646 { 0, EIO, "Unknown Error" };
647
648static const struct vio_error_entry vio_default_errors[] = {
649 {0x0001, EIO, "No Connection"},
650 {0x0002, EIO, "No Receiver"},
651 {0x0003, EIO, "No Buffer Available"},
652 {0x0004, EBADRQC, "Invalid Message Type"},
653 {0x0000, 0, NULL},
654};
655
656const struct vio_error_entry *vio_lookup_rc(
657 const struct vio_error_entry *local_table, u16 rc)
658{
659 const struct vio_error_entry *cur;
660
661 if (!rc)
662 return &vio_no_error;
663 if (local_table)
664 for (cur = local_table; cur->rc; ++cur)
665 if (cur->rc == rc)
666 return cur;
667 for (cur = vio_default_errors; cur->rc; ++cur)
668 if (cur->rc == rc)
669 return cur;
670 return &vio_unknown_error;
671}
672EXPORT_SYMBOL(vio_lookup_rc);
diff --git a/arch/powerpc/platforms/iseries/vpdinfo.c b/arch/powerpc/platforms/iseries/vpdinfo.c
new file mode 100644
index 000000000000..d8a6796924e2
--- /dev/null
+++ b/arch/powerpc/platforms/iseries/vpdinfo.c
@@ -0,0 +1,266 @@
1/*
2 * This code gets the card location of the hardware
3 * Copyright (C) 2001 <Allan H Trautman> <IBM Corp>
4 * Copyright (C) 2005 Stephen Rothwel, IBM Corp
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the:
18 * Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307 USA
21 *
22 * Change Activity:
23 * Created, Feb 2, 2001
24 * Ported to ppc64, August 20, 2001
25 * End Change Activity
26 */
27#include <linux/init.h>
28#include <linux/module.h>
29#include <linux/pci.h>
30#include <asm/types.h>
31#include <asm/resource.h>
32
33#include <asm/iSeries/HvCallPci.h>
34#include <asm/iSeries/HvTypes.h>
35#include <asm/iSeries/iSeries_pci.h>
36
37/*
38 * Size of Bus VPD data
39 */
40#define BUS_VPDSIZE 1024
41
42/*
43 * Bus Vpd Tags
44 */
45#define VpdEndOfAreaTag 0x79
46#define VpdIdStringTag 0x82
47#define VpdVendorAreaTag 0x84
48
49/*
50 * Mfg Area Tags
51 */
52#define VpdFruFrameId 0x4649 // "FI"
53#define VpdSlotMapFormat 0x4D46 // "MF"
54#define VpdSlotMap 0x534D // "SM"
55
56/*
57 * Structures of the areas
58 */
59struct MfgVpdAreaStruct {
60 u16 Tag;
61 u8 TagLength;
62 u8 AreaData1;
63 u8 AreaData2;
64};
65typedef struct MfgVpdAreaStruct MfgArea;
66#define MFG_ENTRY_SIZE 3
67
68struct SlotMapStruct {
69 u8 AgentId;
70 u8 SecondaryAgentId;
71 u8 PhbId;
72 char CardLocation[3];
73 char Parms[8];
74 char Reserved[2];
75};
76typedef struct SlotMapStruct SlotMap;
77#define SLOT_ENTRY_SIZE 16
78
79/*
80 * Parse the Slot Area
81 */
82static void __init iSeries_Parse_SlotArea(SlotMap *MapPtr, int MapLen,
83 HvAgentId agent, u8 *PhbId, char card[4])
84{
85 int SlotMapLen = MapLen;
86 SlotMap *SlotMapPtr = MapPtr;
87
88 /*
89 * Parse Slot label until we find the one requested
90 */
91 while (SlotMapLen > 0) {
92 if (SlotMapPtr->AgentId == agent) {
93 /*
94 * If Phb wasn't found, grab the entry first one found.
95 */
96 if (*PhbId == 0xff)
97 *PhbId = SlotMapPtr->PhbId;
98 /* Found it, extract the data. */
99 if (SlotMapPtr->PhbId == *PhbId) {
100 memcpy(card, &SlotMapPtr->CardLocation, 3);
101 card[3] = 0;
102 break;
103 }
104 }
105 /* Point to the next Slot */
106 SlotMapPtr = (SlotMap *)((char *)SlotMapPtr + SLOT_ENTRY_SIZE);
107 SlotMapLen -= SLOT_ENTRY_SIZE;
108 }
109}
110
111/*
112 * Parse the Mfg Area
113 */
114static void __init iSeries_Parse_MfgArea(u8 *AreaData, int AreaLen,
115 HvAgentId agent, u8 *PhbId,
116 u8 *frame, char card[4])
117{
118 MfgArea *MfgAreaPtr = (MfgArea *)AreaData;
119 int MfgAreaLen = AreaLen;
120 u16 SlotMapFmt = 0;
121
122 /* Parse Mfg Data */
123 while (MfgAreaLen > 0) {
124 int MfgTagLen = MfgAreaPtr->TagLength;
125 /* Frame ID (FI 4649020310 ) */
126 if (MfgAreaPtr->Tag == VpdFruFrameId) /* FI */
127 *frame = MfgAreaPtr->AreaData1;
128 /* Slot Map Format (MF 4D46020004 ) */
129 else if (MfgAreaPtr->Tag == VpdSlotMapFormat) /* MF */
130 SlotMapFmt = (MfgAreaPtr->AreaData1 * 256)
131 + MfgAreaPtr->AreaData2;
132 /* Slot Map (SM 534D90 */
133 else if (MfgAreaPtr->Tag == VpdSlotMap) { /* SM */
134 SlotMap *SlotMapPtr;
135
136 if (SlotMapFmt == 0x1004)
137 SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr
138 + MFG_ENTRY_SIZE + 1);
139 else
140 SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr
141 + MFG_ENTRY_SIZE);
142 iSeries_Parse_SlotArea(SlotMapPtr, MfgTagLen,
143 agent, PhbId, card);
144 }
145 /*
146 * Point to the next Mfg Area
147 * Use defined size, sizeof give wrong answer
148 */
149 MfgAreaPtr = (MfgArea *)((char *)MfgAreaPtr + MfgTagLen
150 + MFG_ENTRY_SIZE);
151 MfgAreaLen -= (MfgTagLen + MFG_ENTRY_SIZE);
152 }
153}
154
155/*
156 * Look for "BUS".. Data is not Null terminated.
157 * PHBID of 0xFF indicates PHB was not found in VPD Data.
158 */
159static int __init iSeries_Parse_PhbId(u8 *AreaPtr, int AreaLength)
160{
161 u8 *PhbPtr = AreaPtr;
162 int DataLen = AreaLength;
163 char PhbId = 0xFF;
164
165 while (DataLen > 0) {
166 if ((*PhbPtr == 'B') && (*(PhbPtr + 1) == 'U')
167 && (*(PhbPtr + 2) == 'S')) {
168 PhbPtr += 3;
169 while (*PhbPtr == ' ')
170 ++PhbPtr;
171 PhbId = (*PhbPtr & 0x0F);
172 break;
173 }
174 ++PhbPtr;
175 --DataLen;
176 }
177 return PhbId;
178}
179
180/*
181 * Parse out the VPD Areas
182 */
183static void __init iSeries_Parse_Vpd(u8 *VpdData, int VpdDataLen,
184 HvAgentId agent, u8 *frame, char card[4])
185{
186 u8 *TagPtr = VpdData;
187 int DataLen = VpdDataLen - 3;
188 u8 PhbId;
189
190 while ((*TagPtr != VpdEndOfAreaTag) && (DataLen > 0)) {
191 int AreaLen = *(TagPtr + 1) + (*(TagPtr + 2) * 256);
192 u8 *AreaData = TagPtr + 3;
193
194 if (*TagPtr == VpdIdStringTag)
195 PhbId = iSeries_Parse_PhbId(AreaData, AreaLen);
196 else if (*TagPtr == VpdVendorAreaTag)
197 iSeries_Parse_MfgArea(AreaData, AreaLen,
198 agent, &PhbId, frame, card);
199 /* Point to next Area. */
200 TagPtr = AreaData + AreaLen;
201 DataLen -= AreaLen;
202 }
203}
204
205static void __init iSeries_Get_Location_Code(u16 bus, HvAgentId agent,
206 u8 *frame, char card[4])
207{
208 int BusVpdLen = 0;
209 u8 *BusVpdPtr = kmalloc(BUS_VPDSIZE, GFP_KERNEL);
210
211 if (BusVpdPtr == NULL) {
212 printk("PCI: Bus VPD Buffer allocation failure.\n");
213 return;
214 }
215 BusVpdLen = HvCallPci_getBusVpd(bus, ISERIES_HV_ADDR(BusVpdPtr),
216 BUS_VPDSIZE);
217 if (BusVpdLen == 0) {
218 printk("PCI: Bus VPD Buffer zero length.\n");
219 goto out_free;
220 }
221 /* printk("PCI: BusVpdPtr: %p, %d\n",BusVpdPtr, BusVpdLen); */
222 /* Make sure this is what I think it is */
223 if (*BusVpdPtr != VpdIdStringTag) { /* 0x82 */
224 printk("PCI: Bus VPD Buffer missing starting tag.\n");
225 goto out_free;
226 }
227 iSeries_Parse_Vpd(BusVpdPtr, BusVpdLen, agent, frame, card);
228out_free:
229 kfree(BusVpdPtr);
230}
231
232/*
233 * Prints the device information.
234 * - Pass in pci_dev* pointer to the device.
235 * - Pass in the device count
236 *
237 * Format:
238 * PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet
239 * controller
240 */
241void __init iSeries_Device_Information(struct pci_dev *PciDev, int count)
242{
243 struct device_node *DevNode = PciDev->sysdata;
244 u16 bus;
245 u8 frame;
246 char card[4];
247 HvSubBusNumber subbus;
248 HvAgentId agent;
249
250 if (DevNode == NULL) {
251 printk("%d. PCI: iSeries_Device_Information DevNode is NULL\n",
252 count);
253 return;
254 }
255
256 bus = ISERIES_BUS(DevNode);
257 subbus = ISERIES_SUBBUS(DevNode);
258 agent = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(subbus),
259 ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus));
260 iSeries_Get_Location_Code(bus, agent, &frame, card);
261
262 printk("%d. PCI: Bus%3d, Device%3d, Vendor %04X Frame%3d, Card %4s ",
263 count, bus, PCI_SLOT(PciDev->devfn), PciDev->vendor,
264 frame, card);
265 printk("0x%04X\n", (int)(PciDev->class >> 8));
266}