]>
Commit | Line | Data |
---|---|---|
0a34fb31 BS |
1 | /* |
2 | * Copyright 2015 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs <bskeggs@redhat.com> | |
23 | */ | |
24 | #include "priv.h" | |
340b0e7c | 25 | #include "agp.h" |
0a34fb31 | 26 | |
2b700825 BS |
27 | #include <core/option.h> |
28 | #include <core/pci.h> | |
29 | #include <subdev/mc.h> | |
30 | ||
0a34fb31 BS |
31 | u32 |
32 | nvkm_pci_rd32(struct nvkm_pci *pci, u16 addr) | |
33 | { | |
34 | return pci->func->rd32(pci, addr); | |
35 | } | |
36 | ||
37 | void | |
38 | nvkm_pci_wr08(struct nvkm_pci *pci, u16 addr, u8 data) | |
39 | { | |
40 | pci->func->wr08(pci, addr, data); | |
41 | } | |
42 | ||
43 | void | |
44 | nvkm_pci_wr32(struct nvkm_pci *pci, u16 addr, u32 data) | |
45 | { | |
46 | pci->func->wr32(pci, addr, data); | |
47 | } | |
48 | ||
5d5b43f5 PM |
49 | u32 |
50 | nvkm_pci_mask(struct nvkm_pci *pci, u16 addr, u32 mask, u32 value) | |
51 | { | |
52 | u32 data = pci->func->rd32(pci, addr); | |
53 | pci->func->wr32(pci, addr, (data & ~mask) | value); | |
54 | return data; | |
55 | } | |
56 | ||
0a34fb31 BS |
57 | void |
58 | nvkm_pci_rom_shadow(struct nvkm_pci *pci, bool shadow) | |
59 | { | |
60 | u32 data = nvkm_pci_rd32(pci, 0x0050); | |
61 | if (shadow) | |
62 | data |= 0x00000001; | |
63 | else | |
64 | data &= ~0x00000001; | |
65 | nvkm_pci_wr32(pci, 0x0050, data); | |
66 | } | |
67 | ||
2b700825 BS |
68 | static irqreturn_t |
69 | nvkm_pci_intr(int irq, void *arg) | |
70 | { | |
71 | struct nvkm_pci *pci = arg; | |
d3981190 | 72 | struct nvkm_device *device = pci->subdev.device; |
2b700825 | 73 | bool handled = false; |
0fd189a9 LP |
74 | |
75 | if (pci->irq < 0) | |
76 | return IRQ_HANDLED; | |
77 | ||
d3981190 BS |
78 | nvkm_mc_intr_unarm(device); |
79 | if (pci->msi) | |
80 | pci->func->msi_rearm(pci); | |
81 | nvkm_mc_intr(device, &handled); | |
82 | nvkm_mc_intr_rearm(device); | |
2b700825 BS |
83 | return handled ? IRQ_HANDLED : IRQ_NONE; |
84 | } | |
85 | ||
86 | static int | |
87 | nvkm_pci_fini(struct nvkm_subdev *subdev, bool suspend) | |
0a34fb31 | 88 | { |
2b700825 | 89 | struct nvkm_pci *pci = nvkm_pci(subdev); |
340b0e7c | 90 | |
340b0e7c BS |
91 | if (pci->agp.bridge) |
92 | nvkm_agp_fini(pci); | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | static int | |
98 | nvkm_pci_preinit(struct nvkm_subdev *subdev) | |
99 | { | |
100 | struct nvkm_pci *pci = nvkm_pci(subdev); | |
101 | if (pci->agp.bridge) | |
102 | nvkm_agp_preinit(pci); | |
2b700825 BS |
103 | return 0; |
104 | } | |
105 | ||
bcc19d9b KH |
106 | static int |
107 | nvkm_pci_oneinit(struct nvkm_subdev *subdev) | |
108 | { | |
109 | struct nvkm_pci *pci = nvkm_pci(subdev); | |
0fd189a9 LP |
110 | struct pci_dev *pdev = pci->pdev; |
111 | int ret; | |
112 | ||
113 | if (pci_is_pcie(pci->pdev)) { | |
114 | ret = nvkm_pcie_oneinit(pci); | |
115 | if (ret) | |
116 | return ret; | |
117 | } | |
118 | ||
119 | ret = request_irq(pdev->irq, nvkm_pci_intr, IRQF_SHARED, "nvkm", pci); | |
120 | if (ret) | |
121 | return ret; | |
122 | ||
123 | pci->irq = pdev->irq; | |
bcc19d9b KH |
124 | return 0; |
125 | } | |
126 | ||
2b700825 BS |
127 | static int |
128 | nvkm_pci_init(struct nvkm_subdev *subdev) | |
129 | { | |
130 | struct nvkm_pci *pci = nvkm_pci(subdev); | |
2b700825 BS |
131 | int ret; |
132 | ||
340b0e7c BS |
133 | if (pci->agp.bridge) { |
134 | ret = nvkm_agp_init(pci); | |
135 | if (ret) | |
136 | return ret; | |
bcc19d9b KH |
137 | } else if (pci_is_pcie(pci->pdev)) { |
138 | nvkm_pcie_init(pci); | |
340b0e7c BS |
139 | } |
140 | ||
779d16aa BS |
141 | if (pci->func->init) |
142 | pci->func->init(pci); | |
143 | ||
a121027d KH |
144 | /* Ensure MSI interrupts are armed, for the case where there are |
145 | * already interrupts pending (for whatever reason) at load time. | |
146 | */ | |
147 | if (pci->msi) | |
148 | pci->func->msi_rearm(pci); | |
149 | ||
0fd189a9 | 150 | return 0; |
0a34fb31 BS |
151 | } |
152 | ||
153 | static void * | |
154 | nvkm_pci_dtor(struct nvkm_subdev *subdev) | |
155 | { | |
2b700825 | 156 | struct nvkm_pci *pci = nvkm_pci(subdev); |
0fd189a9 | 157 | |
340b0e7c | 158 | nvkm_agp_dtor(pci); |
0fd189a9 LP |
159 | |
160 | if (pci->irq >= 0) { | |
161 | /* freq_irq() will call the handler, we use pci->irq == -1 | |
162 | * to signal that it's been torn down and should be a noop. | |
163 | */ | |
164 | int irq = pci->irq; | |
165 | pci->irq = -1; | |
166 | free_irq(irq, pci); | |
167 | } | |
168 | ||
2b700825 BS |
169 | if (pci->msi) |
170 | pci_disable_msi(pci->pdev); | |
0fd189a9 | 171 | |
0a34fb31 BS |
172 | return nvkm_pci(subdev); |
173 | } | |
174 | ||
175 | static const struct nvkm_subdev_func | |
176 | nvkm_pci_func = { | |
177 | .dtor = nvkm_pci_dtor, | |
bcc19d9b | 178 | .oneinit = nvkm_pci_oneinit, |
340b0e7c | 179 | .preinit = nvkm_pci_preinit, |
2b700825 BS |
180 | .init = nvkm_pci_init, |
181 | .fini = nvkm_pci_fini, | |
0a34fb31 BS |
182 | }; |
183 | ||
184 | int | |
185 | nvkm_pci_new_(const struct nvkm_pci_func *func, struct nvkm_device *device, | |
9b70cd54 | 186 | enum nvkm_subdev_type type, int inst, struct nvkm_pci **ppci) |
0a34fb31 BS |
187 | { |
188 | struct nvkm_pci *pci; | |
2b700825 | 189 | |
0a34fb31 BS |
190 | if (!(pci = *ppci = kzalloc(sizeof(**ppci), GFP_KERNEL))) |
191 | return -ENOMEM; | |
9b70cd54 | 192 | nvkm_subdev_ctor(&nvkm_pci_func, device, type, inst, &pci->subdev); |
0a34fb31 | 193 | pci->func = func; |
2b700825 BS |
194 | pci->pdev = device->func->pci(device)->pdev; |
195 | pci->irq = -1; | |
bcc19d9b KH |
196 | pci->pcie.speed = -1; |
197 | pci->pcie.width = -1; | |
2b700825 | 198 | |
340b0e7c BS |
199 | if (device->type == NVKM_DEVICE_AGP) |
200 | nvkm_agp_ctor(pci); | |
201 | ||
2b700825 BS |
202 | switch (pci->pdev->device & 0x0ff0) { |
203 | case 0x00f0: | |
204 | case 0x02e0: | |
205 | /* BR02? NFI how these would be handled yet exactly */ | |
206 | break; | |
207 | default: | |
208 | switch (device->chipset) { | |
209 | case 0xaa: | |
210 | /* reported broken, nv also disable it */ | |
211 | break; | |
212 | default: | |
213 | pci->msi = true; | |
214 | break; | |
215 | } | |
216 | } | |
217 | ||
bc60c90f IM |
218 | #ifdef __BIG_ENDIAN |
219 | pci->msi = false; | |
220 | #endif | |
221 | ||
2b700825 BS |
222 | pci->msi = nvkm_boolopt(device->cfgopt, "NvMSI", pci->msi); |
223 | if (pci->msi && func->msi_rearm) { | |
224 | pci->msi = pci_enable_msi(pci->pdev) == 0; | |
225 | if (pci->msi) | |
226 | nvkm_debug(&pci->subdev, "MSI enabled\n"); | |
227 | } else { | |
228 | pci->msi = false; | |
229 | } | |
230 | ||
0a34fb31 BS |
231 | return 0; |
232 | } |