1107 lines · 28.7 KB · C
RawDownload
// 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.