📄 mpsc.c
字号:
/* * 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 + -