aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ppc64/kernel/XmPciLpEvent.c
blob: 809c9bc6678ba55de89df310b55c33feaab4bca7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * File XmPciLpEvent.h created by Wayne Holm on Mon Jan 15 2001.
 *
 * This module handles PCI interrupt events sent by the iSeries Hypervisor.
*/

#include <linux/config.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/smp.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/bootmem.h>
#include <linux/ide.h>

#include <asm/iSeries/HvTypes.h>
#include <asm/iSeries/HvLpEvent.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/XmPciLpEvent.h>
#include <asm/ppcdebug.h>

static long Pci_Interrupt_Count;
static long Pci_Event_Count;

enum XmPciLpEvent_Subtype {
	XmPciLpEvent_BusCreated	   = 0,		// PHB has been created
	XmPciLpEvent_BusError	   = 1,		// PHB has failed
	XmPciLpEvent_BusFailed	   = 2,		// Msg to Secondary, Primary failed bus
	XmPciLpEvent_NodeFailed	   = 4,		// Multi-adapter bridge has failed
	XmPciLpEvent_NodeRecovered = 5,		// Multi-adapter bridge has recovered
	XmPciLpEvent_BusRecovered  = 12,	// PHB has been recovered
	XmPciLpEvent_UnQuiesceBus  = 18,	// Secondary bus unqiescing
	XmPciLpEvent_BridgeError   = 21,	// Bridge Error
	XmPciLpEvent_SlotInterrupt = 22		// Slot interrupt
};

struct XmPciLpEvent_BusInterrupt {
	HvBusNumber	busNumber;
	HvSubBusNumber	subBusNumber;
};

struct XmPciLpEvent_NodeInterrupt {
	HvBusNumber	busNumber;
	HvSubBusNumber	subBusNumber;
	HvAgentId	deviceId;
};

struct XmPciLpEvent {
	struct HvLpEvent hvLpEvent;

	union {
		u64 alignData;			// Align on an 8-byte boundary

		struct {
			u32		fisr;
			HvBusNumber	busNumber;
			HvSubBusNumber	subBusNumber;
			HvAgentId	deviceId;
		} slotInterrupt;

		struct XmPciLpEvent_BusInterrupt busFailed;
		struct XmPciLpEvent_BusInterrupt busRecovered;
		struct XmPciLpEvent_BusInterrupt busCreated;

		struct XmPciLpEvent_NodeInterrupt nodeFailed;
		struct XmPciLpEvent_NodeInterrupt nodeRecovered;

	} eventData;

};

static void intReceived(struct XmPciLpEvent *eventParm,
		struct pt_regs *regsParm);

static void XmPciLpEvent_handler(struct HvLpEvent *eventParm,
		struct pt_regs *regsParm)
{
#ifdef CONFIG_PCI
#if 0
	PPCDBG(PPCDBG_BUSWALK, "XmPciLpEvent_handler, type 0x%x\n",
			eventParm->xType);
#endif
	++Pci_Event_Count;

	if (eventParm && (eventParm->xType == HvLpEvent_Type_PciIo)) {
		switch (eventParm->xFlags.xFunction) {
		case HvLpEvent_Function_Int:
			intReceived((struct XmPciLpEvent *)eventParm, regsParm);
			break;
		case HvLpEvent_Function_Ack:
			printk(KERN_ERR
				"XmPciLpEvent.c: unexpected ack received\n");
			break;
		default:
			printk(KERN_ERR
				"XmPciLpEvent.c: unexpected event function %d\n",
				(int)eventParm->xFlags.xFunction);
			break;
		}
	} else if (eventParm)
		printk(KERN_ERR
			"XmPciLpEvent.c: Unrecognized PCI event type 0x%x\n",
			(int)eventParm->xType);
	else
		printk(KERN_ERR "XmPciLpEvent.c: NULL event received\n");
#endif
}

static void intReceived(struct XmPciLpEvent *eventParm,
		struct pt_regs *regsParm)
{
	int irq;

	++Pci_Interrupt_Count;
#if 0
	PPCDBG(PPCDBG_BUSWALK, "PCI: XmPciLpEvent.c: intReceived\n");
#endif

	switch (eventParm->hvLpEvent.xSubtype) {
	case XmPciLpEvent_SlotInterrupt:
		irq = eventParm->hvLpEvent.xCorrelationToken;
		/* Dispatch the interrupt handlers for this irq */
		ppc_irq_dispatch_handler(regsParm, irq);
		HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber,
			eventParm->eventData.slotInterrupt.subBusNumber,
			eventParm->eventData.slotInterrupt.deviceId);
		break;
		/* Ignore error recovery events for now */
	case XmPciLpEvent_BusCreated:
		printk(KERN_INFO "XmPciLpEvent.c: system bus %d created\n",
			eventParm->eventData.busCreated.busNumber);
		break;
	case XmPciLpEvent_BusError:
	case XmPciLpEvent_BusFailed:
		printk(KERN_INFO "XmPciLpEvent.c: system bus %d failed\n",
			eventParm->eventData.busFailed.busNumber);
		break;
	case XmPciLpEvent_BusRecovered:
	case XmPciLpEvent_UnQuiesceBus:
		printk(KERN_INFO "XmPciLpEvent.c: system bus %d recovered\n",
			eventParm->eventData.busRecovered.busNumber);
		break;
	case XmPciLpEvent_NodeFailed:
	case XmPciLpEvent_BridgeError:
		printk(KERN_INFO
			"XmPciLpEvent.c: multi-adapter bridge %d/%d/%d failed\n",
			eventParm->eventData.nodeFailed.busNumber,
			eventParm->eventData.nodeFailed.subBusNumber,
			eventParm->eventData.nodeFailed.deviceId);
		break;
	case XmPciLpEvent_NodeRecovered:
		printk(KERN_INFO
			"XmPciLpEvent.c: multi-adapter bridge %d/%d/%d recovered\n",
			eventParm->eventData.nodeRecovered.busNumber,
			eventParm->eventData.nodeRecovered.subBusNumber,
			eventParm->eventData.nodeRecovered.deviceId);
		break;
	default:
		printk(KERN_ERR
			"XmPciLpEvent.c: unrecognized event subtype 0x%x\n",
			eventParm->hvLpEvent.xSubtype);
		break;
	}
}


/* This should be called sometime prior to buswalk (init_IRQ would be good) */
int XmPciLpEvent_init()
{
	int xRc;

	PPCDBG(PPCDBG_BUSWALK,
			"XmPciLpEvent_init, Register Event type 0x%04X\n",
			HvLpEvent_Type_PciIo);

	xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo,
			&XmPciLpEvent_handler);
	if (xRc == 0) {
		xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0);
		if (xRc != 0)
			printk(KERN_ERR
				"XmPciLpEvent.c: open event path failed with rc 0x%x\n",
				xRc);
	} else
		printk(KERN_ERR
			"XmPciLpEvent.c: register handler failed with rc 0x%x\n",
			xRc);
	return xRc;
}