⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mpsc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240, * GT64260, MV64340, MV64360, GT96100, ... ). * * Author: Mark A. Greer <mgreer@mvista.com> * * Based on an old MPSC driver that was in the linuxppc tree.  It appears to * have been created by Chris Zankel (formerly of MontaVista) but there * is no proper Copyright so I'm not sure.  Apparently, parts were also * taken from PPCBoot (now U-Boot).  Also based on drivers/serial/8250.c * by Russell King. * * 2004 (c) MontaVista, Software, Inc.  This file is licensed under * the terms of the GNU General Public License version 2.  This program * is licensed "as is" without any warranty of any kind, whether express * or implied. *//* * The MPSC interface is much like a typical network controller's interface. * That is, you set up separate rings of descriptors for transmitting and * receiving data.  There is also a pool of buffers with (one buffer per * descriptor) that incoming data are dma'd into or outgoing data are dma'd * out of. * * The MPSC requires two other controllers to be able to work.  The Baud Rate * Generator (BRG) provides a clock at programmable frequencies which determines * the baud rate.  The Serial DMA Controller (SDMA) takes incoming data from the * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the * MPSC.  It is actually the SDMA interrupt that the driver uses to keep the * transmit and receive "engines" going (i.e., indicate data has been * transmitted or received). * * NOTES: * * 1) Some chips have an erratum where several regs cannot be * read.  To work around that, we keep a local copy of those regs in * 'mpsc_port_info'. * * 2) Some chips have an erratum where the ctlr will hang when the SDMA ctlr * accesses system mem with coherency enabled.  For that reason, the driver * assumes that coherency for that ctlr has been disabled.  This means * that when in a cache coherent system, the driver has to manually manage * the data cache on the areas that it touches because the dma_* macro are * basically no-ops. * * 3) There is an erratum (on PPC) where you can't use the instruction to do * a DMA_TO_DEVICE/cache clean so DMA_BIDIRECTIONAL/flushes are used in places * where a DMA_TO_DEVICE/clean would have [otherwise] sufficed. * * 4) AFAICT, hardware flow control isn't supported by the controller --MAG. */#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/dma-mapping.h>#include <linux/mv643xx.h>#include <linux/platform_device.h>#include <asm/io.h>#include <asm/irq.h>#define	MPSC_NUM_CTLRS		2/* * Descriptors and buffers must be cache line aligned. * Buffers lengths must be multiple of cache line size. * Number of Tx & Rx descriptors must be powers of 2. */#define	MPSC_RXR_ENTRIES	32#define	MPSC_RXRE_SIZE		dma_get_cache_alignment()#define	MPSC_RXR_SIZE		(MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE)#define	MPSC_RXBE_SIZE		dma_get_cache_alignment()#define	MPSC_RXB_SIZE		(MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE)#define	MPSC_TXR_ENTRIES	32#define	MPSC_TXRE_SIZE		dma_get_cache_alignment()#define	MPSC_TXR_SIZE		(MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE)#define	MPSC_TXBE_SIZE		dma_get_cache_alignment()#define	MPSC_TXB_SIZE		(MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE)#define	MPSC_DMA_ALLOC_SIZE	(MPSC_RXR_SIZE + MPSC_RXB_SIZE + MPSC_TXR_SIZE \		+ MPSC_TXB_SIZE + dma_get_cache_alignment() /* for alignment */)/* Rx and Tx Ring entry descriptors -- assume entry size is <= cacheline size */struct mpsc_rx_desc {	u16 bufsize;	u16 bytecnt;	u32 cmdstat;	u32 link;	u32 buf_ptr;} __attribute((packed));struct mpsc_tx_desc {	u16 bytecnt;	u16 shadow;	u32 cmdstat;	u32 link;	u32 buf_ptr;} __attribute((packed));/* * Some regs that have the erratum that you can't read them are are shared * between the two MPSC controllers.  This struct contains those shared regs. */struct mpsc_shared_regs {	phys_addr_t mpsc_routing_base_p;	phys_addr_t sdma_intr_base_p;	void __iomem *mpsc_routing_base;	void __iomem *sdma_intr_base;	u32 MPSC_MRR_m;	u32 MPSC_RCRR_m;	u32 MPSC_TCRR_m;	u32 SDMA_INTR_CAUSE_m;	u32 SDMA_INTR_MASK_m;};/* The main driver data structure */struct mpsc_port_info {	struct uart_port port;	/* Overlay uart_port structure */	/* Internal driver state for this ctlr */	u8 ready;	u8 rcv_data;	tcflag_t c_iflag;	/* save termios->c_iflag */	tcflag_t c_cflag;	/* save termios->c_cflag */	/* Info passed in from platform */	u8 mirror_regs;		/* Need to mirror regs? */	u8 cache_mgmt;		/* Need manual cache mgmt? */	u8 brg_can_tune;	/* BRG has baud tuning? */	u32 brg_clk_src;	u16 mpsc_max_idle;	int default_baud;	int default_bits;	int default_parity;	int default_flow;	/* Physical addresses of various blocks of registers (from platform) */	phys_addr_t mpsc_base_p;	phys_addr_t sdma_base_p;	phys_addr_t brg_base_p;	/* Virtual addresses of various blocks of registers (from platform) */	void __iomem *mpsc_base;	void __iomem *sdma_base;	void __iomem *brg_base;	/* Descriptor ring and buffer allocations */	void *dma_region;	dma_addr_t dma_region_p;	dma_addr_t rxr;		/* Rx descriptor ring */	dma_addr_t rxr_p;	/* Phys addr of rxr */	u8 *rxb;		/* Rx Ring I/O buf */	u8 *rxb_p;		/* Phys addr of rxb */	u32 rxr_posn;		/* First desc w/ Rx data */	dma_addr_t txr;		/* Tx descriptor ring */	dma_addr_t txr_p;	/* Phys addr of txr */	u8 *txb;		/* Tx Ring I/O buf */	u8 *txb_p;		/* Phys addr of txb */	int txr_head;		/* Where new data goes */	int txr_tail;		/* Where sent data comes off */	spinlock_t tx_lock;	/* transmit lock */	/* Mirrored values of regs we can't read (if 'mirror_regs' set) */	u32 MPSC_MPCR_m;	u32 MPSC_CHR_1_m;	u32 MPSC_CHR_2_m;	u32 MPSC_CHR_10_m;	u32 BRG_BCR_m;	struct mpsc_shared_regs *shared_regs;};/* Hooks to platform-specific code */int mpsc_platform_register_driver(void);void mpsc_platform_unregister_driver(void);/* Hooks back in to mpsc common to be called by platform-specific code */struct mpsc_port_info *mpsc_device_probe(int index);struct mpsc_port_info *mpsc_device_remove(int index);/* Main MPSC Configuration Register Offsets */#define	MPSC_MMCRL			0x0000#define	MPSC_MMCRH			0x0004#define	MPSC_MPCR			0x0008#define	MPSC_CHR_1			0x000c#define	MPSC_CHR_2			0x0010#define	MPSC_CHR_3			0x0014#define	MPSC_CHR_4			0x0018#define	MPSC_CHR_5			0x001c#define	MPSC_CHR_6			0x0020#define	MPSC_CHR_7			0x0024#define	MPSC_CHR_8			0x0028#define	MPSC_CHR_9			0x002c#define	MPSC_CHR_10			0x0030#define	MPSC_CHR_11			0x0034#define	MPSC_MPCR_FRZ			(1 << 9)#define	MPSC_MPCR_CL_5			0#define	MPSC_MPCR_CL_6			1#define	MPSC_MPCR_CL_7			2#define	MPSC_MPCR_CL_8			3#define	MPSC_MPCR_SBL_1			0#define	MPSC_MPCR_SBL_2			1#define	MPSC_CHR_2_TEV			(1<<1)#define	MPSC_CHR_2_TA			(1<<7)#define	MPSC_CHR_2_TTCS			(1<<9)#define	MPSC_CHR_2_REV			(1<<17)#define	MPSC_CHR_2_RA			(1<<23)#define	MPSC_CHR_2_CRD			(1<<25)#define	MPSC_CHR_2_EH			(1<<31)#define	MPSC_CHR_2_PAR_ODD		0#define	MPSC_CHR_2_PAR_SPACE		1#define	MPSC_CHR_2_PAR_EVEN		2#define	MPSC_CHR_2_PAR_MARK		3/* MPSC Signal Routing */#define	MPSC_MRR			0x0000#define	MPSC_RCRR			0x0004#define	MPSC_TCRR			0x0008/* Serial DMA Controller Interface Registers */#define	SDMA_SDC			0x0000#define	SDMA_SDCM			0x0008#define	SDMA_RX_DESC			0x0800#define	SDMA_RX_BUF_PTR			0x0808#define	SDMA_SCRDP			0x0810#define	SDMA_TX_DESC			0x0c00#define	SDMA_SCTDP			0x0c10#define	SDMA_SFTDP			0x0c14#define	SDMA_DESC_CMDSTAT_PE		(1<<0)#define	SDMA_DESC_CMDSTAT_CDL		(1<<1)#define	SDMA_DESC_CMDSTAT_FR		(1<<3)#define	SDMA_DESC_CMDSTAT_OR		(1<<6)#define	SDMA_DESC_CMDSTAT_BR		(1<<9)#define	SDMA_DESC_CMDSTAT_MI		(1<<10)#define	SDMA_DESC_CMDSTAT_A		(1<<11)#define	SDMA_DESC_CMDSTAT_AM		(1<<12)#define	SDMA_DESC_CMDSTAT_CT		(1<<13)#define	SDMA_DESC_CMDSTAT_C		(1<<14)#define	SDMA_DESC_CMDSTAT_ES		(1<<15)#define	SDMA_DESC_CMDSTAT_L		(1<<16)#define	SDMA_DESC_CMDSTAT_F		(1<<17)#define	SDMA_DESC_CMDSTAT_P		(1<<18)#define	SDMA_DESC_CMDSTAT_EI		(1<<23)#define	SDMA_DESC_CMDSTAT_O		(1<<31)#define SDMA_DESC_DFLT			(SDMA_DESC_CMDSTAT_O \		| SDMA_DESC_CMDSTAT_EI)#define	SDMA_SDC_RFT			(1<<0)#define	SDMA_SDC_SFM			(1<<1)#define	SDMA_SDC_BLMR			(1<<6)#define	SDMA_SDC_BLMT			(1<<7)#define	SDMA_SDC_POVR			(1<<8)#define	SDMA_SDC_RIFB			(1<<9)#define	SDMA_SDCM_ERD			(1<<7)#define	SDMA_SDCM_AR			(1<<15)#define	SDMA_SDCM_STD			(1<<16)#define	SDMA_SDCM_TXD			(1<<23)#define	SDMA_SDCM_AT			(1<<31)#define	SDMA_0_CAUSE_RXBUF		(1<<0)#define	SDMA_0_CAUSE_RXERR		(1<<1)#define	SDMA_0_CAUSE_TXBUF		(1<<2)#define	SDMA_0_CAUSE_TXEND		(1<<3)#define	SDMA_1_CAUSE_RXBUF		(1<<8)#define	SDMA_1_CAUSE_RXERR		(1<<9)#define	SDMA_1_CAUSE_TXBUF		(1<<10)#define	SDMA_1_CAUSE_TXEND		(1<<11)#define	SDMA_CAUSE_RX_MASK	(SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR \		| SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR)#define	SDMA_CAUSE_TX_MASK	(SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND \		| SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND)/* SDMA Interrupt registers */#define	SDMA_INTR_CAUSE			0x0000#define	SDMA_INTR_MASK			0x0080/* Baud Rate Generator Interface Registers */#define	BRG_BCR				0x0000#define	BRG_BTR				0x0004/* * Define how this driver is known to the outside (we've been assigned a * range on the "Low-density serial ports" major). */#define MPSC_MAJOR			204#define MPSC_MINOR_START		44#define	MPSC_DRIVER_NAME		"MPSC"#define	MPSC_DEV_NAME			"ttyMM"#define	MPSC_VERSION			"1.00"static struct mpsc_port_info mpsc_ports[MPSC_NUM_CTLRS];static struct mpsc_shared_regs mpsc_shared_regs;static struct uart_driver mpsc_reg;static void mpsc_start_rx(struct mpsc_port_info *pi);static void mpsc_free_ring_mem(struct mpsc_port_info *pi);static void mpsc_release_port(struct uart_port *port);/* ****************************************************************************** * * Baud Rate Generator Routines (BRG) * ****************************************************************************** */static void mpsc_brg_init(struct mpsc_port_info *pi, u32 clk_src){	u32	v;	v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);	v = (v & ~(0xf << 18)) | ((clk_src & 0xf) << 18);	if (pi->brg_can_tune)		v &= ~(1 << 25);	if (pi->mirror_regs)		pi->BRG_BCR_m = v;	writel(v, pi->brg_base + BRG_BCR);	writel(readl(pi->brg_base + BRG_BTR) & 0xffff0000,		pi->brg_base + BRG_BTR);}static void mpsc_brg_enable(struct mpsc_port_info *pi){	u32	v;	v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);	v |= (1 << 16);	if (pi->mirror_regs)		pi->BRG_BCR_m = v;	writel(v, pi->brg_base + BRG_BCR);}static void mpsc_brg_disable(struct mpsc_port_info *pi){	u32	v;	v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);	v &= ~(1 << 16);	if (pi->mirror_regs)		pi->BRG_BCR_m = v;	writel(v, pi->brg_base + BRG_BCR);}/* * To set the baud, we adjust the CDV field in the BRG_BCR reg. * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1. * However, the input clock is divided by 16 in the MPSC b/c of how * 'MPSC_MMCRH' was set up so we have to divide the 'clk' used in our * calculation by 16 to account for that.  So the real calculation * that accounts for the way the mpsc is set up is: * CDV = (clk / (baud*2*16)) - 1 ==> CDV = (clk / (baud << 5)) - 1. */static void mpsc_set_baudrate(struct mpsc_port_info *pi, u32 baud){	u32	cdv = (pi->port.uartclk / (baud << 5)) - 1;	u32	v;	mpsc_brg_disable(pi);	v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);	v = (v & 0xffff0000) | (cdv & 0xffff);	if (pi->mirror_regs)		pi->BRG_BCR_m = v;	writel(v, pi->brg_base + BRG_BCR);	mpsc_brg_enable(pi);}/* ****************************************************************************** * * Serial DMA Routines (SDMA) * ****************************************************************************** */static void mpsc_sdma_burstsize(struct mpsc_port_info *pi, u32 burst_size){	u32	v;	pr_debug("mpsc_sdma_burstsize[%d]: burst_size: %d\n",			pi->port.line, burst_size);	burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */	if (burst_size < 2)		v = 0x0;	/* 1 64-bit word */	else if (burst_size < 4)		v = 0x1;	/* 2 64-bit words */	else if (burst_size < 8)		v = 0x2;	/* 4 64-bit words */	else		v = 0x3;	/* 8 64-bit words */	writel((readl(pi->sdma_base + SDMA_SDC) & (0x3 << 12)) | (v << 12),		pi->sdma_base + SDMA_SDC);}static void mpsc_sdma_init(struct mpsc_port_info *pi, u32 burst_size){	pr_debug("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line,		burst_size);	writel((readl(pi->sdma_base + SDMA_SDC) & 0x3ff) | 0x03f,		pi->sdma_base + SDMA_SDC);	mpsc_sdma_burstsize(pi, burst_size);}static u32 mpsc_sdma_intr_mask(struct mpsc_port_info *pi, u32 mask){	u32	old, v;	pr_debug("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask);	old = v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m :		readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);	mask &= 0xf;	if (pi->port.line)		mask <<= 8;	v &= ~mask;	if (pi->mirror_regs)		pi->shared_regs->SDMA_INTR_MASK_m = v;	writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);	if (pi->port.line)		old >>= 8;	return old & 0xf;}static void mpsc_sdma_intr_unmask(struct mpsc_port_info *pi, u32 mask){	u32	v;	pr_debug("mpsc_sdma_intr_unmask[%d]: mask: 0x%x\n", pi->port.line,mask);	v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m		: readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);	mask &= 0xf;	if (pi->port.line)		mask <<= 8;	v |= mask;	if (pi->mirror_regs)		pi->shared_regs->SDMA_INTR_MASK_m = v;	writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);}static void mpsc_sdma_intr_ack(struct mpsc_port_info *pi){	pr_debug("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line);	if (pi->mirror_regs)		pi->shared_regs->SDMA_INTR_CAUSE_m = 0;	writeb(0x00, pi->shared_regs->sdma_intr_base + SDMA_INTR_CAUSE			+ pi->port.line);}static void mpsc_sdma_set_rx_ring(struct mpsc_port_info *pi,		struct mpsc_rx_desc *rxre_p){	pr_debug("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n",		pi->port.line, (u32)rxre_p);	writel((u32)rxre_p, pi->sdma_base + SDMA_SCRDP);}static void mpsc_sdma_set_tx_ring(struct mpsc_port_info *pi,		struct mpsc_tx_desc *txre_p){	writel((u32)txre_p, pi->sdma_base + SDMA_SFTDP);	writel((u32)txre_p, pi->sdma_base + SDMA_SCTDP);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -