diff options
Diffstat (limited to 'arch/mips/pci/msi-octeon.c')
-rw-r--r-- | arch/mips/pci/msi-octeon.c | 90 |
1 files changed, 88 insertions, 2 deletions
diff --git a/arch/mips/pci/msi-octeon.c b/arch/mips/pci/msi-octeon.c index 03742e647657..1e31526f0e53 100644 --- a/arch/mips/pci/msi-octeon.c +++ b/arch/mips/pci/msi-octeon.c | |||
@@ -249,12 +249,99 @@ static irqreturn_t octeon_msi_interrupt(int cpl, void *dev_id) | |||
249 | return IRQ_NONE; | 249 | return IRQ_NONE; |
250 | } | 250 | } |
251 | 251 | ||
252 | static DEFINE_RAW_SPINLOCK(octeon_irq_msi_lock); | ||
253 | |||
254 | static void octeon_irq_msi_ack(unsigned int irq) | ||
255 | { | ||
256 | if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) { | ||
257 | /* These chips have PCI */ | ||
258 | cvmx_write_csr(CVMX_NPI_NPI_MSI_RCV, | ||
259 | 1ull << (irq - OCTEON_IRQ_MSI_BIT0)); | ||
260 | } else { | ||
261 | /* | ||
262 | * These chips have PCIe. Thankfully the ACK doesn't | ||
263 | * need any locking. | ||
264 | */ | ||
265 | cvmx_write_csr(CVMX_PEXP_NPEI_MSI_RCV0, | ||
266 | 1ull << (irq - OCTEON_IRQ_MSI_BIT0)); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static void octeon_irq_msi_eoi(unsigned int irq) | ||
271 | { | ||
272 | /* Nothing needed */ | ||
273 | } | ||
274 | |||
275 | static void octeon_irq_msi_enable(unsigned int irq) | ||
276 | { | ||
277 | if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) { | ||
278 | /* | ||
279 | * Octeon PCI doesn't have the ability to mask/unmask | ||
280 | * MSI interrupts individually. Instead of | ||
281 | * masking/unmasking them in groups of 16, we simple | ||
282 | * assume MSI devices are well behaved. MSI | ||
283 | * interrupts are always enable and the ACK is assumed | ||
284 | * to be enough. | ||
285 | */ | ||
286 | } else { | ||
287 | /* These chips have PCIe. Note that we only support | ||
288 | * the first 64 MSI interrupts. Unfortunately all the | ||
289 | * MSI enables are in the same register. We use | ||
290 | * MSI0's lock to control access to them all. | ||
291 | */ | ||
292 | uint64_t en; | ||
293 | unsigned long flags; | ||
294 | raw_spin_lock_irqsave(&octeon_irq_msi_lock, flags); | ||
295 | en = cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0); | ||
296 | en |= 1ull << (irq - OCTEON_IRQ_MSI_BIT0); | ||
297 | cvmx_write_csr(CVMX_PEXP_NPEI_MSI_ENB0, en); | ||
298 | cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0); | ||
299 | raw_spin_unlock_irqrestore(&octeon_irq_msi_lock, flags); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | static void octeon_irq_msi_disable(unsigned int irq) | ||
304 | { | ||
305 | if (!octeon_has_feature(OCTEON_FEATURE_PCIE)) { | ||
306 | /* See comment in enable */ | ||
307 | } else { | ||
308 | /* | ||
309 | * These chips have PCIe. Note that we only support | ||
310 | * the first 64 MSI interrupts. Unfortunately all the | ||
311 | * MSI enables are in the same register. We use | ||
312 | * MSI0's lock to control access to them all. | ||
313 | */ | ||
314 | uint64_t en; | ||
315 | unsigned long flags; | ||
316 | raw_spin_lock_irqsave(&octeon_irq_msi_lock, flags); | ||
317 | en = cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0); | ||
318 | en &= ~(1ull << (irq - OCTEON_IRQ_MSI_BIT0)); | ||
319 | cvmx_write_csr(CVMX_PEXP_NPEI_MSI_ENB0, en); | ||
320 | cvmx_read_csr(CVMX_PEXP_NPEI_MSI_ENB0); | ||
321 | raw_spin_unlock_irqrestore(&octeon_irq_msi_lock, flags); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | static struct irq_chip octeon_irq_chip_msi = { | ||
326 | .name = "MSI", | ||
327 | .enable = octeon_irq_msi_enable, | ||
328 | .disable = octeon_irq_msi_disable, | ||
329 | .ack = octeon_irq_msi_ack, | ||
330 | .eoi = octeon_irq_msi_eoi, | ||
331 | }; | ||
252 | 332 | ||
253 | /* | 333 | /* |
254 | * Initializes the MSI interrupt handling code | 334 | * Initializes the MSI interrupt handling code |
255 | */ | 335 | */ |
256 | int octeon_msi_initialize(void) | 336 | static int __init octeon_msi_initialize(void) |
257 | { | 337 | { |
338 | int irq; | ||
339 | |||
340 | for (irq = OCTEON_IRQ_MSI_BIT0; irq <= OCTEON_IRQ_MSI_BIT63; irq++) { | ||
341 | set_irq_chip_and_handler(irq, &octeon_irq_chip_msi, | ||
342 | handle_percpu_irq); | ||
343 | } | ||
344 | |||
258 | if (octeon_has_feature(OCTEON_FEATURE_PCIE)) { | 345 | if (octeon_has_feature(OCTEON_FEATURE_PCIE)) { |
259 | if (request_irq(OCTEON_IRQ_PCI_MSI0, octeon_msi_interrupt, | 346 | if (request_irq(OCTEON_IRQ_PCI_MSI0, octeon_msi_interrupt, |
260 | IRQF_SHARED, | 347 | IRQF_SHARED, |
@@ -284,5 +371,4 @@ int octeon_msi_initialize(void) | |||
284 | } | 371 | } |
285 | return 0; | 372 | return 0; |
286 | } | 373 | } |
287 | |||
288 | subsys_initcall(octeon_msi_initialize); | 374 | subsys_initcall(octeon_msi_initialize); |