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 <linux/random.h>
17 #include <asm/time.h>
18 #include <asm/iSeries/ItLpQueue.h>
19 #include <asm/iSeries/HvLpEvent.h>
20 #include <asm/iSeries/HvCallEvent.h>
21 #include <asm/iSeries/LparData.h>
22 
set_inUse(struct ItLpQueue * lpQueue)23 static __inline__ int set_inUse( struct ItLpQueue * lpQueue )
24 {
25 	int t;
26 	u32 * inUseP = &(lpQueue->xInUseWord);
27 
28 	__asm__ __volatile__("\n\
29 1:	lwarx	%0,0,%2		\n\
30 	cmpi	0,%0,0		\n\
31 	li	%0,0		\n\
32 	bne-	2f		\n\
33 	addi	%0,%0,1		\n\
34 	stwcx.	%0,0,%2		\n\
35 	bne-	1b		\n\
36 2:	eieio"
37 	: "=&r" (t), "=m" (lpQueue->xInUseWord)
38 	: "r" (inUseP), "m" (lpQueue->xInUseWord)
39 	: "cc");
40 
41 	return t;
42 }
43 
clear_inUse(struct ItLpQueue * lpQueue)44 static __inline__ void clear_inUse( struct ItLpQueue * lpQueue )
45 {
46 	lpQueue->xInUseWord = 0;
47 }
48 
49 /* Array of LpEvent handler functions */
50 extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes];
51 unsigned long ItLpQueueInProcess = 0;
52 
ItLpQueue_getNextLpEvent(struct ItLpQueue * lpQueue)53 struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue )
54 {
55 	struct HvLpEvent * nextLpEvent =
56 		(struct HvLpEvent *)lpQueue->xSlicCurEventPtr;
57 	if ( nextLpEvent->xFlags.xValid ) {
58 		/* rmb() needed only for weakly consistent machines (regatta) */
59 		rmb();
60 		/* Set pointer to next potential event */
61 		lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 +
62 				      LpEventAlign ) /
63 				      LpEventAlign ) *
64 				      LpEventAlign;
65 		/* Wrap to beginning if no room at end */
66 		if (lpQueue->xSlicCurEventPtr > lpQueue->xSlicLastValidEventPtr)
67 			lpQueue->xSlicCurEventPtr = lpQueue->xSlicEventStackPtr;
68 	}
69 	else
70 		nextLpEvent = NULL;
71 
72 	return nextLpEvent;
73 }
74 
ItLpQueue_isLpIntPending(struct ItLpQueue * lpQueue)75 int ItLpQueue_isLpIntPending( struct ItLpQueue * lpQueue )
76 {
77 	int retval = 0;
78 	struct HvLpEvent * nextLpEvent;
79 	if ( lpQueue ) {
80 		nextLpEvent = (struct HvLpEvent *)lpQueue->xSlicCurEventPtr;
81 		retval = nextLpEvent->xFlags.xValid | lpQueue->xPlicOverflowIntPending;
82 	}
83 	return retval;
84 }
85 
ItLpQueue_clearValid(struct HvLpEvent * event)86 void ItLpQueue_clearValid( struct HvLpEvent * event )
87 {
88 	/* Clear the valid bit of the event
89 	 * Also clear bits within this event that might
90 	 * look like valid bits (on 64-byte boundaries)
91    	 */
92 	unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) /
93 						 LpEventAlign ) - 1;
94 	switch ( extra ) {
95 	  case 3:
96 	   ((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0;
97 	  case 2:
98 	   ((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0;
99 	  case 1:
100 	   ((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0;
101 	  case 0:
102 	   ;
103 	}
104 	mb();
105 	event->xFlags.xValid = 0;
106 }
107 
ItLpQueue_process(struct ItLpQueue * lpQueue,struct pt_regs * regs)108 unsigned ItLpQueue_process( struct ItLpQueue * lpQueue, struct pt_regs *regs )
109 {
110 	unsigned numIntsProcessed = 0;
111 	struct HvLpEvent * nextLpEvent;
112 
113 	/* If we have recursed, just return */
114 	if ( !set_inUse( lpQueue ) )
115 		return 0;
116 
117 	if (ItLpQueueInProcess == 0)
118 		ItLpQueueInProcess = 1;
119 	else
120 		BUG();
121 
122 	for (;;) {
123 		nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue );
124 		if ( nextLpEvent ) {
125 			/* Count events to return to caller
126 			 * and count processed events in lpQueue
127  			 */
128 			++numIntsProcessed;
129 			lpQueue->xLpIntCount++;
130 			/* Call appropriate handler here, passing
131 			 * a pointer to the LpEvent.  The handler
132 			 * must make a copy of the LpEvent if it
133 			 * needs it in a bottom half. (perhaps for
134 			 * an ACK)
135 			 *
136 			 *  Handlers are responsible for ACK processing
137 			 *
138 			 * The Hypervisor guarantees that LpEvents will
139 			 * only be delivered with types that we have
140 			 * registered for, so no type check is necessary
141 			 * here!
142   			 */
143 			if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes )
144 				lpQueue->xLpIntCountByType[nextLpEvent->xType]++;
145 			if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes &&
146 			     lpEventHandler[nextLpEvent->xType] )
147 				lpEventHandler[nextLpEvent->xType](nextLpEvent, regs);
148 			else
149 				printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType );
150 
151 			ItLpQueue_clearValid( nextLpEvent );
152 		}
153 		else 	/* No more valid events
154 			 * If overflow events are pending
155 			 * process them
156 			 */
157 			if ( lpQueue->xPlicOverflowIntPending ) {
158 				HvCallEvent_getOverflowLpEvents(
159 						lpQueue->xIndex);
160 			}
161 			else	/* If nothing left then we are done */
162 				break;
163 	}
164 
165 	ItLpQueueInProcess = 0;
166 	mb();
167 	clear_inUse( lpQueue );
168 
169 	get_paca()->lpEvent_count += numIntsProcessed;
170 
171 	/* Use LPEvents as a source of randomness.  Since there
172 	 * Isn't an LPEvent Randomness call, pretend these are
173 	 * mouse events (which is fair since we don't have mice
174 	 * on the iSeries)
175 	 */
176 	add_mouse_randomness(get_tb());
177 
178 	return numIntsProcessed;
179 }
180