-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathinterrupts3.html
More file actions
125 lines (117 loc) · 4.88 KB
/
interrupts3.html
File metadata and controls
125 lines (117 loc) · 4.88 KB
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
<html>
<font face="helvetica">
<title>Interrupts (part 3)</title>
<body>
<p align=center><font size=+5>Interrupts (part 3)</font>
<hr>
<a href="contents.html">Contents</a>
<font size=+3>
<ul>
<li>Ok, so how <em>does</em> the device driver know what
IRQ number to use?
<p>It's kind of hardware dependent.
<p>For PCI devices which don't use MSI or MSIX, the struct pci_dev
structure which is passed into your driver's .probe function
will contain the irq number to use:
<pre>
static int __devinit my_drivers_probe_func(struct pci_dev *pdev,
const struct pci_device_id *entry)
{
int myirq = pdev->irq;
blah blah blah
if (!request_irq(myirq, ... ) {
...
}
...
}
</pre>
<p>For PCI devices that use MSI/MSIX (i.e. modern PCI devices),
it's a bit more complicated. Here's an example from the hpsa driver.
<p>But first, from include/linux/pci.h, we need to know about this:
<pre>
struct msix_entry {
u32 vector; /* kernel uses to write allocated vector */
u16 entry; /* driver uses to specify entry, OS writes */
};
</pre>
Then this, from drivers/scsi/hpsa.c (my comments in red):
<pre>
static void __devinit hpsa_interrupt_mode(struct ctlr_info *h)
{
#ifdef CONFIG_PCI_MSI
int err;
</pre> <font size=5 color="red">The particular device this driver deals with has four MSIX vectors. This is just an intrinsic property of the device.</font> <pre>
struct msix_entry hpsa_msix_entries[4] = { {0, 0}, {0, 1},
{0, 2}, {0, 3}
};
/* Some boards advertise MSI but don't really support it */
if ((h->board_id == 0x40700E11) || (h->board_id == 0x40800E11) ||
(h->board_id == 0x40820E11) || (h->board_id == 0x40830E11))
goto default_int_mode;
</pre> <font size=5 color="red">Check if the device supports MSIX</font> <pre>
if (pci_find_capability(h->pdev, PCI_CAP_ID_MSIX)) {
dev_info(&h->pdev->dev, "MSIX\n");
</pre> <font size=5 color="red">Ask the kernel to assign us some MSIX vectors.
They get filled in in the .vector members of elements of the
struct msix_entry array, hpsa_msix_entries[]</font> <pre>
err = pci_enable_msix(h->pdev, hpsa_msix_entries, 4);
if (!err) {
h->intr[0] = hpsa_msix_entries[0].vector;
h->intr[1] = hpsa_msix_entries[1].vector;
h->intr[2] = hpsa_msix_entries[2].vector;
h->intr[3] = hpsa_msix_entries[3].vector;
h->msix_vector = 1;
</pre> <font size=5 color="red">Ok, we got our vectors</font> <pre>
return;
}
</pre> <font size=5 color="red">Or, something bad happened.</font> <pre>
if (err > 0) {
dev_warn(&h->pdev->dev, "only %d MSI-X vectors "
"available\n", err);
goto default_int_mode;
} else {
dev_warn(&h->pdev->dev, "MSI-X init failed %d\n",
err);
goto default_int_mode;
}
}
</pre> <font size=5 color="red">Check if the device supports MSI</font> <pre>
if (pci_find_capability(h->pdev, PCI_CAP_ID_MSI)) {
dev_info(&h->pdev->dev, "MSI\n");
if (!pci_enable_msi(h->pdev))
h->msi_vector = 1;
else
dev_warn(&h->pdev->dev, "MSI init failed\n");
}
default_int_mode:
#endif /* CONFIG_PCI_MSI */
</pre> <font size=5 color="red">No MSI, No MSIX, do it the old fashioned way</font> <pre>
/* if we get here we're going to use the default interrupt mode */
h->intr[PERF_MODE_INT] = h->pdev->irq;
}
</pre>
The part of the hpsa driver which actually requests IRQs is called
after the above code is run. Note that the above code may assign
values to h->msix_vector and h->msi_vector.
<pre>
if (h->msix_vector || h->msi_vector)
rc = request_irq(h->intr[PERF_MODE_INT], do_hpsa_intr_msi,
IRQF_DISABLED, h->devname, h);
else
rc = request_irq(h->intr[PERF_MODE_INT], do_hpsa_intr_intx,
IRQF_DISABLED, h->devname, h);
if (rc) {
dev_err(&pdev->dev, "unable to get irq %d for %s\n",
h->intr[PERF_MODE_INT], h->devname);
goto clean2;
}
</pre>
Note that this driver has different interrupt handlers depending on
whether it's using the old fashioned i/o APIC style interrupts or
MSI/MSIX interrupts. This is because they need to interact with the
device slightly differently depending on which kind of interrupts are
being used.
</ul>
</font>
</body>
</html>