// SPDX-License-Identifier: GPL-2.0-or-later/* * Freescale MPC85xx/MPC86xx RapidIO RMU support * * Copyright 2009 Sysgo AG * Thomas Moll <thomas.moll@sysgo.com> * - fixed maintenance access routines, check for aligned access * * Copyright 2009 Integrated Device Technology, Inc. * Alex Bounine <alexandre.bounine@idt.com> * - Added Port-Write message handling * - Added Machine Check exception handling * * Copyright (C) 2007, 2008, 2010, 2011 Freescale Semiconductor, Inc. * Zhang Wei <wei.zhang@freescale.com> * Lian Minghuan-B31939 <Minghuan.Lian@freescale.com> * Liu Gang <Gang.Liu@freescale.com> * * Copyright 2005 MontaVista Software, Inc. * Matt Porter <mporter@kernel.crashing.org> */ #include <linux/types.h>#include <linux/dma-mapping.h>#include <linux/interrupt.h>#include <linux/of_address.h>#include <linux/of_irq.h>#include <linux/slab.h> #include "fsl_rio.h" #define GET_RMM_HANDLE(mport) \ (((struct rio_priv *)(mport->priv))->rmm_handle) /* RapidIO definition irq, which read from OF-tree */#define IRQ_RIO_PW(m) (((struct fsl_rio_pw *)(m))->pwirq)#define IRQ_RIO_BELL(m) (((struct fsl_rio_dbell *)(m))->bellirq)#define IRQ_RIO_TX(m) (((struct fsl_rmu *)(GET_RMM_HANDLE(m)))->txirq)#define IRQ_RIO_RX(m) (((struct fsl_rmu *)(GET_RMM_HANDLE(m)))->rxirq) #define RIO_MIN_TX_RING_SIZE 2#define RIO_MAX_TX_RING_SIZE 2048#define RIO_MIN_RX_RING_SIZE 2#define RIO_MAX_RX_RING_SIZE 2048 #define RIO_IPWMR_SEN 0x00100000#define RIO_IPWMR_QFIE 0x00000100#define RIO_IPWMR_EIE 0x00000020#define RIO_IPWMR_CQ 0x00000002#define RIO_IPWMR_PWE 0x00000001 #define RIO_IPWSR_QF 0x00100000#define RIO_IPWSR_TE 0x00000080#define RIO_IPWSR_QFI 0x00000010#define RIO_IPWSR_PWD 0x00000008#define RIO_IPWSR_PWB 0x00000004 #define RIO_EPWISR 0x10010/* EPWISR Error match value */#define RIO_EPWISR_PINT1 0x80000000#define RIO_EPWISR_PINT2 0x40000000#define RIO_EPWISR_MU 0x00000002#define RIO_EPWISR_PW 0x00000001 #define IPWSR_CLEAR 0x98#define OMSR_CLEAR 0x1cb3#define IMSR_CLEAR 0x491#define IDSR_CLEAR 0x91#define ODSR_CLEAR 0x1c00#define LTLEECSR_ENABLE_ALL 0xFFC000FC#define RIO_LTLEECSR 0x060c #define RIO_IM0SR 0x64#define RIO_IM1SR 0x164#define RIO_OM0SR 0x4#define RIO_OM1SR 0x104 #define RIO_DBELL_WIN_SIZE 0x1000 #define RIO_MSG_OMR_MUI 0x00000002#define RIO_MSG_OSR_TE 0x00000080#define RIO_MSG_OSR_QOI 0x00000020#define RIO_MSG_OSR_QFI 0x00000010#define RIO_MSG_OSR_MUB 0x00000004#define RIO_MSG_OSR_EOMI 0x00000002#define RIO_MSG_OSR_QEI 0x00000001 #define RIO_MSG_IMR_MI 0x00000002#define RIO_MSG_ISR_TE 0x00000080#define RIO_MSG_ISR_QFI 0x00000010#define RIO_MSG_ISR_DIQI 0x00000001 #define RIO_MSG_DESC_SIZE 32#define RIO_MSG_BUFFER_SIZE 4096 #define DOORBELL_DMR_DI 0x00000002#define DOORBELL_DSR_TE 0x00000080#define DOORBELL_DSR_QFI 0x00000010#define DOORBELL_DSR_DIQI 0x00000001 #define DOORBELL_MESSAGE_SIZE 0x08 static DEFINE_SPINLOCK(fsl_rio_doorbell_lock); struct rio_msg_regs { u32 omr; u32 osr; u32 pad1; u32 odqdpar; u32 pad2; u32 osar; u32 odpr; u32 odatr; u32 odcr; u32 pad3; u32 odqepar; u32 pad4[13]; u32 imr; u32 isr; u32 pad5; u32 ifqdpar; u32 pad6; u32 ifqepar;}; struct rio_dbell_regs { u32 odmr; u32 odsr; u32 pad1[4]; u32 oddpr; u32 oddatr; u32 pad2[3]; u32 odretcr; u32 pad3[12]; u32 dmr; u32 dsr; u32 pad4; u32 dqdpar; u32 pad5; u32 dqepar;}; struct rio_pw_regs { u32 pwmr; u32 pwsr; u32 epwqbar; u32 pwqbar;}; struct rio_tx_desc { u32 pad1; u32 saddr; u32 dport; u32 dattr; u32 pad2; u32 pad3; u32 dwcnt; u32 pad4;}; struct rio_msg_tx_ring { void *virt; dma_addr_t phys; void *virt_buffer[RIO_MAX_TX_RING_SIZE]; dma_addr_t phys_buffer[RIO_MAX_TX_RING_SIZE]; int tx_slot; int size; void *dev_id;}; struct rio_msg_rx_ring { void *virt; dma_addr_t phys; void *virt_buffer[RIO_MAX_RX_RING_SIZE]; int rx_slot; int size; void *dev_id;}; struct fsl_rmu { struct rio_msg_regs __iomem *msg_regs; struct rio_msg_tx_ring msg_tx_ring; struct rio_msg_rx_ring msg_rx_ring; int txirq; int rxirq;}; struct rio_dbell_msg { u16 pad1; u16 tid; u16 sid; u16 info;}; /** * fsl_rio_tx_handler - MPC85xx outbound message interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * * Handles outbound message interrupts. Executes a register outbound * mailbox event handler and acks the interrupt occurrence. */static irqreturn_tfsl_rio_tx_handler(int irq, void *dev_instance){ int osr; struct rio_mport *port = (struct rio_mport *)dev_instance; struct fsl_rmu *rmu = GET_RMM_HANDLE(port); osr = in_be32(&rmu->msg_regs->osr); if (osr & RIO_MSG_OSR_TE) { pr_info("RIO: outbound message transmission error\n"); out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_TE); goto out; } if (osr & RIO_MSG_OSR_QOI) { pr_info("RIO: outbound message queue overflow\n"); out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_QOI); goto out; } if (osr & RIO_MSG_OSR_EOMI) { u32 dqp = in_be32(&rmu->msg_regs->odqdpar); int slot = (dqp - rmu->msg_tx_ring.phys) >> 5; if (port->outb_msg[0].mcback != NULL) { port->outb_msg[0].mcback(port, rmu->msg_tx_ring.dev_id, -1, slot); } /* Ack the end-of-message interrupt */ out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_EOMI); } out: return IRQ_HANDLED;} /** * fsl_rio_rx_handler - MPC85xx inbound message interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * * Handles inbound message interrupts. Executes a registered inbound * mailbox event handler and acks the interrupt occurrence. */static irqreturn_tfsl_rio_rx_handler(int irq, void *dev_instance){ int isr; struct rio_mport *port = (struct rio_mport *)dev_instance; struct fsl_rmu *rmu = GET_RMM_HANDLE(port); isr = in_be32(&rmu->msg_regs->isr); if (isr & RIO_MSG_ISR_TE) { pr_info("RIO: inbound message reception error\n"); out_be32((void *)&rmu->msg_regs->isr, RIO_MSG_ISR_TE); goto out; } /* XXX Need to check/dispatch until queue empty */ if (isr & RIO_MSG_ISR_DIQI) { /* * Can receive messages for any mailbox/letter to that * mailbox destination. So, make the callback with an * unknown/invalid mailbox number argument. */ if (port->inb_msg[0].mcback != NULL) port->inb_msg[0].mcback(port, rmu->msg_rx_ring.dev_id, -1, -1); /* Ack the queueing interrupt */ out_be32(&rmu->msg_regs->isr, RIO_MSG_ISR_DIQI); } out: return IRQ_HANDLED;} /** * fsl_rio_dbell_handler - MPC85xx doorbell interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * * Handles doorbell interrupts. Parses a list of registered * doorbell event handlers and executes a matching event handler. */static irqreturn_tfsl_rio_dbell_handler(int irq, void *dev_instance){ int dsr; struct fsl_rio_dbell *fsl_dbell = (struct fsl_rio_dbell *)dev_instance; int i; dsr = in_be32(&fsl_dbell->dbell_regs->dsr); if (dsr & DOORBELL_DSR_TE) { pr_info("RIO: doorbell reception error\n"); out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_TE); goto out; } if (dsr & DOORBELL_DSR_QFI) { pr_info("RIO: doorbell queue full\n"); out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_QFI); } /* XXX Need to check/dispatch until queue empty */ if (dsr & DOORBELL_DSR_DIQI) { struct rio_dbell_msg *dmsg = fsl_dbell->dbell_ring.virt + (in_be32(&fsl_dbell->dbell_regs->dqdpar) & 0xfff); struct rio_dbell *dbell; int found = 0; pr_debug ("RIO: processing doorbell," " sid %2.2x tid %2.2x info %4.4x\n", dmsg->sid, dmsg->tid, dmsg->info); for (i = 0; i < MAX_PORT_NUM; i++) { if (fsl_dbell->mport[i]) { list_for_each_entry(dbell, &fsl_dbell->mport[i]->dbells, node) { if ((dbell->res->start <= dmsg->info) && (dbell->res->end >= dmsg->info)) { found = 1; break; } } if (found && dbell->dinb) { dbell->dinb(fsl_dbell->mport[i], dbell->dev_id, dmsg->sid, dmsg->tid, dmsg->info); break; } } } if (!found) { pr_debug ("RIO: spurious doorbell," " sid %2.2x tid %2.2x info %4.4x\n", dmsg->sid, dmsg->tid, dmsg->info); } setbits32(&fsl_dbell->dbell_regs->dmr, DOORBELL_DMR_DI); out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_DIQI); } out: return IRQ_HANDLED;} static void msg_unit_error_handler(void){ /*XXX: Error recovery is not implemented, we just clear errors */ out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0); out_be32((u32 *)(rmu_regs_win + RIO_IM0SR), IMSR_CLEAR); out_be32((u32 *)(rmu_regs_win + RIO_IM1SR), IMSR_CLEAR); out_be32((u32 *)(rmu_regs_win + RIO_OM0SR), OMSR_CLEAR); out_be32((u32 *)(rmu_regs_win + RIO_OM1SR), OMSR_CLEAR); out_be32(&dbell->dbell_regs->odsr, ODSR_CLEAR); out_be32(&dbell->dbell_regs->dsr, IDSR_CLEAR); out_be32(&pw->pw_regs->pwsr, IPWSR_CLEAR);} /** * fsl_rio_port_write_handler - MPC85xx port write interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * * Handles port write interrupts. Parses a list of registered * port write event handlers and executes a matching event handler. */static irqreturn_tfsl_rio_port_write_handler(int irq, void *dev_instance){ u32 ipwmr, ipwsr; struct fsl_rio_pw *pw = (struct fsl_rio_pw *)dev_instance; u32 epwisr, tmp; epwisr = in_be32(rio_regs_win + RIO_EPWISR); if (!(epwisr & RIO_EPWISR_PW)) goto pw_done; ipwmr = in_be32(&pw->pw_regs->pwmr); ipwsr = in_be32(&pw->pw_regs->pwsr); #ifdef DEBUG_PW pr_debug("PW Int->IPWMR: 0x%08x IPWSR: 0x%08x (", ipwmr, ipwsr); if (ipwsr & RIO_IPWSR_QF) pr_debug(" QF"); if (ipwsr & RIO_IPWSR_TE) pr_debug(" TE"); if (ipwsr & RIO_IPWSR_QFI) pr_debug(" QFI"); if (ipwsr & RIO_IPWSR_PWD) pr_debug(" PWD"); if (ipwsr & RIO_IPWSR_PWB) pr_debug(" PWB"); pr_debug(" )\n");#endif /* Schedule deferred processing if PW was received */ if (ipwsr & RIO_IPWSR_QFI) { /* Save PW message (if there is room in FIFO), * otherwise discard it. */ if (kfifo_avail(&pw->pw_fifo) >= RIO_PW_MSG_SIZE) { pw->port_write_msg.msg_count++; kfifo_in(&pw->pw_fifo, pw->port_write_msg.virt, RIO_PW_MSG_SIZE); } else { pw->port_write_msg.discard_count++; pr_debug("RIO: ISR Discarded Port-Write Msg(s) (%d)\n", pw->port_write_msg.discard_count); } /* Clear interrupt and issue Clear Queue command. This allows * another port-write to be received. */ out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_QFI); out_be32(&pw->pw_regs->pwmr, ipwmr | RIO_IPWMR_CQ); schedule_work(&pw->pw_work); } if ((ipwmr & RIO_IPWMR_EIE) && (ipwsr & RIO_IPWSR_TE)) { pw->port_write_msg.err_count++; pr_debug("RIO: Port-Write Transaction Err (%d)\n", pw->port_write_msg.err_count); /* Clear Transaction Error: port-write controller should be * disabled when clearing this error */ out_be32(&pw->pw_regs->pwmr, ipwmr & ~RIO_IPWMR_PWE); out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_TE); out_be32(&pw->pw_regs->pwmr, ipwmr); } if (ipwsr & RIO_IPWSR_PWD) { pw->port_write_msg.discard_count++; pr_debug("RIO: Port Discarded Port-Write Msg(s) (%d)\n", pw->port_write_msg.discard_count); out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_PWD); } pw_done: if (epwisr & RIO_EPWISR_PINT1) { tmp = in_be32(rio_regs_win + RIO_LTLEDCSR); pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); fsl_rio_port_error_handler(0); } if (epwisr & RIO_EPWISR_PINT2) { tmp = in_be32(rio_regs_win + RIO_LTLEDCSR); pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); fsl_rio_port_error_handler(1); } if (epwisr & RIO_EPWISR_MU) { tmp = in_be32(rio_regs_win + RIO_LTLEDCSR); pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); msg_unit_error_handler(); } return IRQ_HANDLED;} static void fsl_pw_dpc(struct work_struct *work){ struct fsl_rio_pw *pw = container_of(work, struct fsl_rio_pw, pw_work); union rio_pw_msg msg_buffer; int i; /* * Process port-write messages */ while (kfifo_out_spinlocked(&pw->pw_fifo, (unsigned char *)&msg_buffer, RIO_PW_MSG_SIZE, &pw->pw_fifo_lock)) {#ifdef DEBUG_PW { u32 i; pr_debug("%s : Port-Write Message:", __func__); for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32); i++) { if ((i%4) == 0) pr_debug("\n0x%02x: 0x%08x", i*4, msg_buffer.raw[i]); else pr_debug(" 0x%08x", msg_buffer.raw[i]); }Lines 1–500 of 1107. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Freescale MPC85xx/MPC86xx RapidIO RMU support * * Copyright 2009 Sysgo AG * Thomas Moll <thomas.moll@sysgo.com> * - fixed maintenance access routines, check for aligned access * * Copyright 2009 Integrated Device Technology, Inc. * Alex Bounine <alexandre.bounine@idt.com> * - Added Port-Write message handling * - Added Machine Check exception handling * * Copyright (C) 2007, 2008, 2010, 2011 Freescale Semiconductor, Inc. * Zhang Wei <wei.zhang@freescale.com> * Lian Minghuan-B31939 <Minghuan.Lian@freescale.com> * Liu Gang <Gang.Liu@freescale.com> * * Copyright 2005 MontaVista Software, Inc. * Matt Porter <mporter@kernel.crashing.org> */ #include <linux/types.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/slab.h> #include "fsl_rio.h" #define GET_RMM_HANDLE(mport) \ (((struct rio_priv *)(mport->priv))->rmm_handle) /* RapidIO definition irq, which read from OF-tree */ #define IRQ_RIO_PW(m) (((struct fsl_rio_pw *)(m))->pwirq) #define IRQ_RIO_BELL(m) (((struct fsl_rio_dbell *)(m))->bellirq) #define IRQ_RIO_TX(m) (((struct fsl_rmu *)(GET_RMM_HANDLE(m)))->txirq) #define IRQ_RIO_RX(m) (((struct fsl_rmu *)(GET_RMM_HANDLE(m)))->rxirq) #define RIO_MIN_TX_RING_SIZE 2 #define RIO_MAX_TX_RING_SIZE 2048 #define RIO_MIN_RX_RING_SIZE 2 #define RIO_MAX_RX_RING_SIZE 2048 #define RIO_IPWMR_SEN 0x00100000 #define RIO_IPWMR_QFIE 0x00000100 #define RIO_IPWMR_EIE 0x00000020 #define RIO_IPWMR_CQ 0x00000002 #define RIO_IPWMR_PWE 0x00000001 #define RIO_IPWSR_QF 0x00100000 #define RIO_IPWSR_TE 0x00000080 #define RIO_IPWSR_QFI 0x00000010 #define RIO_IPWSR_PWD 0x00000008 #define RIO_IPWSR_PWB 0x00000004 #define RIO_EPWISR 0x10010 /* EPWISR Error match value */ #define RIO_EPWISR_PINT1 0x80000000 #define RIO_EPWISR_PINT2 0x40000000 #define RIO_EPWISR_MU 0x00000002 #define RIO_EPWISR_PW 0x00000001 #define IPWSR_CLEAR 0x98 #define OMSR_CLEAR 0x1cb3 #define IMSR_CLEAR 0x491 #define IDSR_CLEAR 0x91 #define ODSR_CLEAR 0x1c00 #define LTLEECSR_ENABLE_ALL 0xFFC000FC #define RIO_LTLEECSR 0x060c #define RIO_IM0SR 0x64 #define RIO_IM1SR 0x164 #define RIO_OM0SR 0x4 #define RIO_OM1SR 0x104 #define RIO_DBELL_WIN_SIZE 0x1000 #define RIO_MSG_OMR_MUI 0x00000002 #define RIO_MSG_OSR_TE 0x00000080 #define RIO_MSG_OSR_QOI 0x00000020 #define RIO_MSG_OSR_QFI 0x00000010 #define RIO_MSG_OSR_MUB 0x00000004 #define RIO_MSG_OSR_EOMI 0x00000002 #define RIO_MSG_OSR_QEI 0x00000001 #define RIO_MSG_IMR_MI 0x00000002 #define RIO_MSG_ISR_TE 0x00000080 #define RIO_MSG_ISR_QFI 0x00000010 #define RIO_MSG_ISR_DIQI 0x00000001 #define RIO_MSG_DESC_SIZE 32 #define RIO_MSG_BUFFER_SIZE 4096 #define DOORBELL_DMR_DI 0x00000002 #define DOORBELL_DSR_TE 0x00000080 #define DOORBELL_DSR_QFI 0x00000010 #define DOORBELL_DSR_DIQI 0x00000001 #define DOORBELL_MESSAGE_SIZE 0x08 static DEFINE_SPINLOCK(fsl_rio_doorbell_lock); struct rio_msg_regs { u32 omr; u32 osr; u32 pad1; u32 odqdpar; u32 pad2; u32 osar; u32 odpr; u32 odatr; u32 odcr; u32 pad3; u32 odqepar; u32 pad4[13]; u32 imr; u32 isr; u32 pad5; u32 ifqdpar; u32 pad6; u32 ifqepar; }; struct rio_dbell_regs { u32 odmr; u32 odsr; u32 pad1[4]; u32 oddpr; u32 oddatr; u32 pad2[3]; u32 odretcr; u32 pad3[12]; u32 dmr; u32 dsr; u32 pad4; u32 dqdpar; u32 pad5; u32 dqepar; }; struct rio_pw_regs { u32 pwmr; u32 pwsr; u32 epwqbar; u32 pwqbar; }; struct rio_tx_desc { u32 pad1; u32 saddr; u32 dport; u32 dattr; u32 pad2; u32 pad3; u32 dwcnt; u32 pad4; }; struct rio_msg_tx_ring { void *virt; dma_addr_t phys; void *virt_buffer[RIO_MAX_TX_RING_SIZE]; dma_addr_t phys_buffer[RIO_MAX_TX_RING_SIZE]; int tx_slot; int size; void *dev_id; }; struct rio_msg_rx_ring { void *virt; dma_addr_t phys; void *virt_buffer[RIO_MAX_RX_RING_SIZE]; int rx_slot; int size; void *dev_id; }; struct fsl_rmu { struct rio_msg_regs __iomem *msg_regs; struct rio_msg_tx_ring msg_tx_ring; struct rio_msg_rx_ring msg_rx_ring; int txirq; int rxirq; }; struct rio_dbell_msg { u16 pad1; u16 tid; u16 sid; u16 info; }; /** * fsl_rio_tx_handler - MPC85xx outbound message interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * * Handles outbound message interrupts. Executes a register outbound * mailbox event handler and acks the interrupt occurrence. */ static irqreturn_t fsl_rio_tx_handler(int irq, void *dev_instance) { int osr; struct rio_mport *port = (struct rio_mport *)dev_instance; struct fsl_rmu *rmu = GET_RMM_HANDLE(port); osr = in_be32(&rmu->msg_regs->osr); if (osr & RIO_MSG_OSR_TE) { pr_info("RIO: outbound message transmission error\n"); out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_TE); goto out; } if (osr & RIO_MSG_OSR_QOI) { pr_info("RIO: outbound message queue overflow\n"); out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_QOI); goto out; } if (osr & RIO_MSG_OSR_EOMI) { u32 dqp = in_be32(&rmu->msg_regs->odqdpar); int slot = (dqp - rmu->msg_tx_ring.phys) >> 5; if (port->outb_msg[0].mcback != NULL) { port->outb_msg[0].mcback(port, rmu->msg_tx_ring.dev_id, -1, slot); } /* Ack the end-of-message interrupt */ out_be32(&rmu->msg_regs->osr, RIO_MSG_OSR_EOMI); } out: return IRQ_HANDLED; } /** * fsl_rio_rx_handler - MPC85xx inbound message interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * * Handles inbound message interrupts. Executes a registered inbound * mailbox event handler and acks the interrupt occurrence. */ static irqreturn_t fsl_rio_rx_handler(int irq, void *dev_instance) { int isr; struct rio_mport *port = (struct rio_mport *)dev_instance; struct fsl_rmu *rmu = GET_RMM_HANDLE(port); isr = in_be32(&rmu->msg_regs->isr); if (isr & RIO_MSG_ISR_TE) { pr_info("RIO: inbound message reception error\n"); out_be32((void *)&rmu->msg_regs->isr, RIO_MSG_ISR_TE); goto out; } /* XXX Need to check/dispatch until queue empty */ if (isr & RIO_MSG_ISR_DIQI) { /* * Can receive messages for any mailbox/letter to that * mailbox destination. So, make the callback with an * unknown/invalid mailbox number argument. */ if (port->inb_msg[0].mcback != NULL) port->inb_msg[0].mcback(port, rmu->msg_rx_ring.dev_id, -1, -1); /* Ack the queueing interrupt */ out_be32(&rmu->msg_regs->isr, RIO_MSG_ISR_DIQI); } out: return IRQ_HANDLED; } /** * fsl_rio_dbell_handler - MPC85xx doorbell interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * * Handles doorbell interrupts. Parses a list of registered * doorbell event handlers and executes a matching event handler. */ static irqreturn_t fsl_rio_dbell_handler(int irq, void *dev_instance) { int dsr; struct fsl_rio_dbell *fsl_dbell = (struct fsl_rio_dbell *)dev_instance; int i; dsr = in_be32(&fsl_dbell->dbell_regs->dsr); if (dsr & DOORBELL_DSR_TE) { pr_info("RIO: doorbell reception error\n"); out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_TE); goto out; } if (dsr & DOORBELL_DSR_QFI) { pr_info("RIO: doorbell queue full\n"); out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_QFI); } /* XXX Need to check/dispatch until queue empty */ if (dsr & DOORBELL_DSR_DIQI) { struct rio_dbell_msg *dmsg = fsl_dbell->dbell_ring.virt + (in_be32(&fsl_dbell->dbell_regs->dqdpar) & 0xfff); struct rio_dbell *dbell; int found = 0; pr_debug ("RIO: processing doorbell," " sid %2.2x tid %2.2x info %4.4x\n", dmsg->sid, dmsg->tid, dmsg->info); for (i = 0; i < MAX_PORT_NUM; i++) { if (fsl_dbell->mport[i]) { list_for_each_entry(dbell, &fsl_dbell->mport[i]->dbells, node) { if ((dbell->res->start <= dmsg->info) && (dbell->res->end >= dmsg->info)) { found = 1; break; } } if (found && dbell->dinb) { dbell->dinb(fsl_dbell->mport[i], dbell->dev_id, dmsg->sid, dmsg->tid, dmsg->info); break; } } } if (!found) { pr_debug ("RIO: spurious doorbell," " sid %2.2x tid %2.2x info %4.4x\n", dmsg->sid, dmsg->tid, dmsg->info); } setbits32(&fsl_dbell->dbell_regs->dmr, DOORBELL_DMR_DI); out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_DIQI); } out: return IRQ_HANDLED; } static void msg_unit_error_handler(void) { /*XXX: Error recovery is not implemented, we just clear errors */ out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0); out_be32((u32 *)(rmu_regs_win + RIO_IM0SR), IMSR_CLEAR); out_be32((u32 *)(rmu_regs_win + RIO_IM1SR), IMSR_CLEAR); out_be32((u32 *)(rmu_regs_win + RIO_OM0SR), OMSR_CLEAR); out_be32((u32 *)(rmu_regs_win + RIO_OM1SR), OMSR_CLEAR); out_be32(&dbell->dbell_regs->odsr, ODSR_CLEAR); out_be32(&dbell->dbell_regs->dsr, IDSR_CLEAR); out_be32(&pw->pw_regs->pwsr, IPWSR_CLEAR); } /** * fsl_rio_port_write_handler - MPC85xx port write interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * * Handles port write interrupts. Parses a list of registered * port write event handlers and executes a matching event handler. */ static irqreturn_t fsl_rio_port_write_handler(int irq, void *dev_instance) { u32 ipwmr, ipwsr; struct fsl_rio_pw *pw = (struct fsl_rio_pw *)dev_instance; u32 epwisr, tmp; epwisr = in_be32(rio_regs_win + RIO_EPWISR); if (!(epwisr & RIO_EPWISR_PW)) goto pw_done; ipwmr = in_be32(&pw->pw_regs->pwmr); ipwsr = in_be32(&pw->pw_regs->pwsr); #ifdef DEBUG_PW pr_debug("PW Int->IPWMR: 0x%08x IPWSR: 0x%08x (", ipwmr, ipwsr); if (ipwsr & RIO_IPWSR_QF) pr_debug(" QF"); if (ipwsr & RIO_IPWSR_TE) pr_debug(" TE"); if (ipwsr & RIO_IPWSR_QFI) pr_debug(" QFI"); if (ipwsr & RIO_IPWSR_PWD) pr_debug(" PWD"); if (ipwsr & RIO_IPWSR_PWB) pr_debug(" PWB"); pr_debug(" )\n"); #endif /* Schedule deferred processing if PW was received */ if (ipwsr & RIO_IPWSR_QFI) { /* Save PW message (if there is room in FIFO), * otherwise discard it. */ if (kfifo_avail(&pw->pw_fifo) >= RIO_PW_MSG_SIZE) { pw->port_write_msg.msg_count++; kfifo_in(&pw->pw_fifo, pw->port_write_msg.virt, RIO_PW_MSG_SIZE); } else { pw->port_write_msg.discard_count++; pr_debug("RIO: ISR Discarded Port-Write Msg(s) (%d)\n", pw->port_write_msg.discard_count); } /* Clear interrupt and issue Clear Queue command. This allows * another port-write to be received. */ out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_QFI); out_be32(&pw->pw_regs->pwmr, ipwmr | RIO_IPWMR_CQ); schedule_work(&pw->pw_work); } if ((ipwmr & RIO_IPWMR_EIE) && (ipwsr & RIO_IPWSR_TE)) { pw->port_write_msg.err_count++; pr_debug("RIO: Port-Write Transaction Err (%d)\n", pw->port_write_msg.err_count); /* Clear Transaction Error: port-write controller should be * disabled when clearing this error */ out_be32(&pw->pw_regs->pwmr, ipwmr & ~RIO_IPWMR_PWE); out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_TE); out_be32(&pw->pw_regs->pwmr, ipwmr); } if (ipwsr & RIO_IPWSR_PWD) { pw->port_write_msg.discard_count++; pr_debug("RIO: Port Discarded Port-Write Msg(s) (%d)\n", pw->port_write_msg.discard_count); out_be32(&pw->pw_regs->pwsr, RIO_IPWSR_PWD); } pw_done: if (epwisr & RIO_EPWISR_PINT1) { tmp = in_be32(rio_regs_win + RIO_LTLEDCSR); pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); fsl_rio_port_error_handler(0); } if (epwisr & RIO_EPWISR_PINT2) { tmp = in_be32(rio_regs_win + RIO_LTLEDCSR); pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); fsl_rio_port_error_handler(1); } if (epwisr & RIO_EPWISR_MU) { tmp = in_be32(rio_regs_win + RIO_LTLEDCSR); pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); msg_unit_error_handler(); } return IRQ_HANDLED; } static void fsl_pw_dpc(struct work_struct *work) { struct fsl_rio_pw *pw = container_of(work, struct fsl_rio_pw, pw_work); union rio_pw_msg msg_buffer; int i; /* * Process port-write messages */ while (kfifo_out_spinlocked(&pw->pw_fifo, (unsigned char *)&msg_buffer, RIO_PW_MSG_SIZE, &pw->pw_fifo_lock)) { #ifdef DEBUG_PW { u32 i; pr_debug("%s : Port-Write Message:", __func__); for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32); i++) { if ((i%4) == 0) pr_debug("\n0x%02x: 0x%08x", i*4, msg_buffer.raw[i]); else pr_debug(" 0x%08x", msg_buffer.raw[i]); }