]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - arch/ppc64/kernel/ItLpQueue.c
Merge upstream (approx. 2.6.12-git8) into 'janitor' branch of netdev-2.6.
[karo-tx-linux.git] / arch / ppc64 / kernel / ItLpQueue.c
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
20 static __inline__ int set_inUse( struct ItLpQueue * lpQueue )
21 {
22         int t;
23         u32 * inUseP = &(lpQueue->xInUseWord);
24
25         __asm__ __volatile__("\n\
26 1:      lwarx   %0,0,%2         \n\
27         cmpwi   0,%0,0          \n\
28         li      %0,0            \n\
29         bne-    2f              \n\
30         addi    %0,%0,1         \n\
31         stwcx.  %0,0,%2         \n\
32         bne-    1b              \n\
33 2:      eieio"
34         : "=&r" (t), "=m" (lpQueue->xInUseWord)
35         : "r" (inUseP), "m" (lpQueue->xInUseWord)
36         : "cc");
37
38         return t;
39 }
40
41 static __inline__ void clear_inUse( struct ItLpQueue * lpQueue )
42 {
43         lpQueue->xInUseWord = 0;
44 }
45
46 /* Array of LpEvent handler functions */
47 extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes];
48 unsigned long ItLpQueueInProcess = 0;
49
50 struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue )
51 {
52         struct HvLpEvent * nextLpEvent = 
53                 (struct HvLpEvent *)lpQueue->xSlicCurEventPtr;
54         if ( nextLpEvent->xFlags.xValid ) {
55                 /* rmb() needed only for weakly consistent machines (regatta) */
56                 rmb();
57                 /* Set pointer to next potential event */
58                 lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 +
59                                       LpEventAlign ) /
60                                       LpEventAlign ) *
61                                       LpEventAlign;
62                 /* Wrap to beginning if no room at end */
63                 if (lpQueue->xSlicCurEventPtr > lpQueue->xSlicLastValidEventPtr)
64                         lpQueue->xSlicCurEventPtr = lpQueue->xSlicEventStackPtr;
65         }
66         else 
67                 nextLpEvent = NULL;
68
69         return nextLpEvent;
70 }
71
72 int ItLpQueue_isLpIntPending( struct ItLpQueue * lpQueue )
73 {
74         int retval = 0;
75         struct HvLpEvent * nextLpEvent;
76         if ( lpQueue ) {
77                 nextLpEvent = (struct HvLpEvent *)lpQueue->xSlicCurEventPtr;
78                 retval = nextLpEvent->xFlags.xValid | lpQueue->xPlicOverflowIntPending;
79         }
80         return retval;
81 }
82
83 void ItLpQueue_clearValid( struct HvLpEvent * event )
84 {
85         /* Clear the valid bit of the event
86          * Also clear bits within this event that might
87          * look like valid bits (on 64-byte boundaries)
88          */
89         unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) /
90                                                  LpEventAlign ) - 1;
91         switch ( extra ) {
92           case 3:
93            ((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0;
94           case 2:
95            ((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0;
96           case 1:
97            ((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0;
98           case 0:
99            ;    
100         }
101         mb();
102         event->xFlags.xValid = 0;
103 }
104
105 unsigned ItLpQueue_process( struct ItLpQueue * lpQueue, struct pt_regs *regs )
106 {
107         unsigned numIntsProcessed = 0;
108         struct HvLpEvent * nextLpEvent;
109
110         /* If we have recursed, just return */
111         if ( !set_inUse( lpQueue ) )
112                 return 0;
113         
114         if (ItLpQueueInProcess == 0)
115                 ItLpQueueInProcess = 1;
116         else
117                 BUG();
118
119         for (;;) {
120                 nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue );
121                 if ( nextLpEvent ) {
122                         /* Count events to return to caller
123                          * and count processed events in lpQueue
124                          */
125                         ++numIntsProcessed;
126                         lpQueue->xLpIntCount++;         
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 ( nextLpEvent->xType < HvLpEvent_Type_NumTypes )
141                                 lpQueue->xLpIntCountByType[nextLpEvent->xType]++;
142                         if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes &&
143                              lpEventHandler[nextLpEvent->xType] ) 
144                                 lpEventHandler[nextLpEvent->xType](nextLpEvent, regs);
145                         else
146                                 printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType );
147                         
148                         ItLpQueue_clearValid( nextLpEvent );
149                 } else if ( lpQueue->xPlicOverflowIntPending )
150                         /*
151                          * No more valid events. If overflow events are
152                          * pending process them
153                          */
154                         HvCallEvent_getOverflowLpEvents( lpQueue->xIndex);
155                 else
156                         break;
157         }
158
159         ItLpQueueInProcess = 0;
160         mb();
161         clear_inUse( lpQueue );
162
163         get_paca()->lpevent_count += numIntsProcessed;
164
165         return numIntsProcessed;
166 }