diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc64/kernel/ItLpQueue.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ppc64/kernel/ItLpQueue.c')
-rw-r--r-- | arch/ppc64/kernel/ItLpQueue.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/ItLpQueue.c b/arch/ppc64/kernel/ItLpQueue.c new file mode 100644 index 000000000000..c923a815760e --- /dev/null +++ b/arch/ppc64/kernel/ItLpQueue.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * ItLpQueue.c | ||
3 | * Copyright (C) 2001 Mike Corrigan 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 | |||
11 | #include <linux/stddef.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <asm/system.h> | ||
15 | #include <asm/paca.h> | ||
16 | #include <asm/iSeries/ItLpQueue.h> | ||
17 | #include <asm/iSeries/HvLpEvent.h> | ||
18 | #include <asm/iSeries/HvCallEvent.h> | ||
19 | #include <asm/iSeries/LparData.h> | ||
20 | |||
21 | static __inline__ int set_inUse( struct ItLpQueue * lpQueue ) | ||
22 | { | ||
23 | int t; | ||
24 | u32 * inUseP = &(lpQueue->xInUseWord); | ||
25 | |||
26 | __asm__ __volatile__("\n\ | ||
27 | 1: lwarx %0,0,%2 \n\ | ||
28 | cmpwi 0,%0,0 \n\ | ||
29 | li %0,0 \n\ | ||
30 | bne- 2f \n\ | ||
31 | addi %0,%0,1 \n\ | ||
32 | stwcx. %0,0,%2 \n\ | ||
33 | bne- 1b \n\ | ||
34 | 2: eieio" | ||
35 | : "=&r" (t), "=m" (lpQueue->xInUseWord) | ||
36 | : "r" (inUseP), "m" (lpQueue->xInUseWord) | ||
37 | : "cc"); | ||
38 | |||
39 | return t; | ||
40 | } | ||
41 | |||
42 | static __inline__ void clear_inUse( struct ItLpQueue * lpQueue ) | ||
43 | { | ||
44 | lpQueue->xInUseWord = 0; | ||
45 | } | ||
46 | |||
47 | /* Array of LpEvent handler functions */ | ||
48 | extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; | ||
49 | unsigned long ItLpQueueInProcess = 0; | ||
50 | |||
51 | struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue ) | ||
52 | { | ||
53 | struct HvLpEvent * nextLpEvent = | ||
54 | (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; | ||
55 | if ( nextLpEvent->xFlags.xValid ) { | ||
56 | /* rmb() needed only for weakly consistent machines (regatta) */ | ||
57 | rmb(); | ||
58 | /* Set pointer to next potential event */ | ||
59 | lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 + | ||
60 | LpEventAlign ) / | ||
61 | LpEventAlign ) * | ||
62 | LpEventAlign; | ||
63 | /* Wrap to beginning if no room at end */ | ||
64 | if (lpQueue->xSlicCurEventPtr > lpQueue->xSlicLastValidEventPtr) | ||
65 | lpQueue->xSlicCurEventPtr = lpQueue->xSlicEventStackPtr; | ||
66 | } | ||
67 | else | ||
68 | nextLpEvent = NULL; | ||
69 | |||
70 | return nextLpEvent; | ||
71 | } | ||
72 | |||
73 | int ItLpQueue_isLpIntPending( struct ItLpQueue * lpQueue ) | ||
74 | { | ||
75 | int retval = 0; | ||
76 | struct HvLpEvent * nextLpEvent; | ||
77 | if ( lpQueue ) { | ||
78 | nextLpEvent = (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; | ||
79 | retval = nextLpEvent->xFlags.xValid | lpQueue->xPlicOverflowIntPending; | ||
80 | } | ||
81 | return retval; | ||
82 | } | ||
83 | |||
84 | void ItLpQueue_clearValid( struct HvLpEvent * event ) | ||
85 | { | ||
86 | /* Clear the valid bit of the event | ||
87 | * Also clear bits within this event that might | ||
88 | * look like valid bits (on 64-byte boundaries) | ||
89 | */ | ||
90 | unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) / | ||
91 | LpEventAlign ) - 1; | ||
92 | switch ( extra ) { | ||
93 | case 3: | ||
94 | ((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0; | ||
95 | case 2: | ||
96 | ((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0; | ||
97 | case 1: | ||
98 | ((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0; | ||
99 | case 0: | ||
100 | ; | ||
101 | } | ||
102 | mb(); | ||
103 | event->xFlags.xValid = 0; | ||
104 | } | ||
105 | |||
106 | unsigned ItLpQueue_process( struct ItLpQueue * lpQueue, struct pt_regs *regs ) | ||
107 | { | ||
108 | unsigned numIntsProcessed = 0; | ||
109 | struct HvLpEvent * nextLpEvent; | ||
110 | |||
111 | /* If we have recursed, just return */ | ||
112 | if ( !set_inUse( lpQueue ) ) | ||
113 | return 0; | ||
114 | |||
115 | if (ItLpQueueInProcess == 0) | ||
116 | ItLpQueueInProcess = 1; | ||
117 | else | ||
118 | BUG(); | ||
119 | |||
120 | for (;;) { | ||
121 | nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue ); | ||
122 | if ( nextLpEvent ) { | ||
123 | /* Count events to return to caller | ||
124 | * and count processed events in lpQueue | ||
125 | */ | ||
126 | ++numIntsProcessed; | ||
127 | lpQueue->xLpIntCount++; | ||
128 | /* Call appropriate handler here, passing | ||
129 | * a pointer to the LpEvent. The handler | ||
130 | * must make a copy of the LpEvent if it | ||
131 | * needs it in a bottom half. (perhaps for | ||
132 | * an ACK) | ||
133 | * | ||
134 | * Handlers are responsible for ACK processing | ||
135 | * | ||
136 | * The Hypervisor guarantees that LpEvents will | ||
137 | * only be delivered with types that we have | ||
138 | * registered for, so no type check is necessary | ||
139 | * here! | ||
140 | */ | ||
141 | if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes ) | ||
142 | lpQueue->xLpIntCountByType[nextLpEvent->xType]++; | ||
143 | if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes && | ||
144 | lpEventHandler[nextLpEvent->xType] ) | ||
145 | lpEventHandler[nextLpEvent->xType](nextLpEvent, regs); | ||
146 | else | ||
147 | printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType ); | ||
148 | |||
149 | ItLpQueue_clearValid( nextLpEvent ); | ||
150 | } else if ( lpQueue->xPlicOverflowIntPending ) | ||
151 | /* | ||
152 | * No more valid events. If overflow events are | ||
153 | * pending process them | ||
154 | */ | ||
155 | HvCallEvent_getOverflowLpEvents( lpQueue->xIndex); | ||
156 | else | ||
157 | break; | ||
158 | } | ||
159 | |||
160 | ItLpQueueInProcess = 0; | ||
161 | mb(); | ||
162 | clear_inUse( lpQueue ); | ||
163 | |||
164 | get_paca()->lpevent_count += numIntsProcessed; | ||
165 | |||
166 | return numIntsProcessed; | ||
167 | } | ||