ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.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 #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         cmpi    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 }