aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/pci/msi-octeon.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/pci/msi-octeon.c')
-rw-r--r--arch/mips/pci/msi-octeon.c90
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
252static DEFINE_RAW_SPINLOCK(octeon_irq_msi_lock);
253
254static 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
270static void octeon_irq_msi_eoi(unsigned int irq)
271{
272 /* Nothing needed */
273}
274
275static 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
303static 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
325static 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 */
256int octeon_msi_initialize(void) 336static 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
288subsys_initcall(octeon_msi_initialize); 374subsys_initcall(octeon_msi_initialize);