📄 omap24xx-uart.c
字号:
/* * arch/arm/mach-omap2/omap24xx-uart.c * * Support functions for the OMAP24xx uart controller. * * Copyright (C) 2004-2006 Texas Instruments, Inc. * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/serial.h>#include <linux/dma-mapping.h>#include <asm/system.h>#include <asm/mach/irq.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/setup.h>#include <asm/arch/clock.h>#include <asm/arch/serial.h>#include <asm/arch/omap24xx-uart.h>#include <asm/arch/dmtimer.h>#define HSUART_LAT_CONST 1static struct constraint_handle *constr_handle;static struct constraint_id cnstr_id = { .type = RES_LATENCY_CO, .data = (void *)"latency",};/* Enable DEBUG_MODE for Non DMA operation *//* #define DEBUG_MODE *//* ----- debug defines ----------------------------------------------- *//* Debug - four macros: * FN_IN, FN_OUT(value),D1,D2,D3 enabled based on log level *//* Log level standard used here: * Log level 3 all messages * Log level 2 all entry-exit points * Log level 1 major messages * Log level 0 no messages */#define OMAP24XX_UART_LOG_LEVEL 0/* detail - 0 - no detail * 1 - function name * 2 - function name, line number * prefix is added to every log message */#define OMAP24XX_UART_DETAIL 4/* kernel log level*///#define OMAP24XX_UART_K_LOG_LEVEL KERN_DEBUG#define OMAP24XX_UART_K_LOG_LEVEL#define DL1 "%s "#define DR1 ,__FUNCTION__#define DEBUG#define DL2 "[%d] "#define DR2 ,__LINE__#define D(format,...)\ printk(OMAP24XX_UART_K_LOG_LEVEL DL1 DL2 format "\n" DR1 DR2, ## __VA_ARGS__)#define D1(ARGS...)#define D2(ARGS...)#define D3(ARGS...)#define FN_IN#define FN_OUT(ARG)#define CONSOLE_NAME "console="#define FREE 0#define USED 1#define MAX_BUF_SIZE 12000/* forward declartation */static void uart_rx_dma_callback(int lch, u16 ch_status, void *data);static void omap24xx_timeout_isr(unsigned long uart_no); /* structure for storing UART DMA info */struct omap24xx_uart { u8 uart_no; int rx_dma_channel; int tx_dma_channel; dma_addr_t rx_buf_dma_phys; /* Physical adress of RX DMA buffer */ dma_addr_t tx_buf_dma_phys; /* Physical adress of TX DMA buffer */ void *rx_buf_dma_virt; /* Virtual adress of RX DMA buffer */ void *tx_buf_dma_virt; /* Virtual adress of TX DMA buffer */ u8 tx_buf_state; u8 rx_buf_state; spinlock_t uart_lock; int in_use; /* timer to poll variable size release */ struct timer_list timer; unsigned int timer_rate; u32 timeout; u32 timer_active;};static int get_uart_clocks(struct clk **uarti, struct clk **uartf, u8 uart_no);static int uart_irq[MAX_UARTS + 1] = { INT_24XX_UART1_IRQ, INT_24XX_UART2_IRQ, INT_24XX_UART3_IRQ };static u8 uart_dma_rx[MAX_UARTS + 1] = { OMAP24XX_DMA_UART1_RX, OMAP24XX_DMA_UART2_RX, OMAP24XX_DMA_UART3_RX };static u8 uart_dma_tx[MAX_UARTS + 1] = { OMAP24XX_DMA_UART1_TX, OMAP24XX_DMA_UART2_TX, OMAP24XX_DMA_UART3_TX };static struct omap24xx_uart ui[MAX_UARTS + 1];static struct uart_callback uart_cb[MAX_UARTS + 1];static u8 uart_mode[MAX_UARTS + 1];/** * brief get_uart_clocks * used to enable/disable UART clocks. * param *uarti * param *uartf * param uart_no * * return */static int get_uart_clocks(struct clk **uarti, struct clk **uartf, u8 uart_no){ char *iname = NULL, *fname = NULL; switch (uart_no) { case UART1: iname = "uart1_ick"; fname = "uart1_fck"; break; case UART2: iname = "uart2_ick"; fname = "uart2_fck"; break; case UART3: iname = "uart3_ick"; fname = "uart3_fck"; break; default: return -EPERM; } *uarti = clk_get(NULL, iname); if (*uarti < 0) { printk(KERN_ERR "Error : %s : No entry in the clock table \n", iname); return -ENOENT; } *uartf = clk_get(NULL, fname); if (*uartf < 0) { printk(KERN_ERR "Error : %s : No entry in the clock table \n", fname); return -ENOENT; } return 0;}/* * omap24xxx_timeout_isr * Looks at DMA destination register and calls receive * callback if the destination register is the same on * two timer isr. */static voidomap24xx_timeout_isr(unsigned long uart_no){ u32 w = 0; int lch; int curr_pos; static int prev_pos = 0; struct omap24xx_uart *hsuart = &ui[uart_no]; FN_IN; lch = hsuart->rx_dma_channel; curr_pos = omap_get_dma_dst_pos(lch); if ((curr_pos == prev_pos) && (curr_pos != hsuart->rx_buf_dma_phys)) { omap_stop_dma(lch); w = OMAP_DMA_CSR_REG(lch); uart_rx_dma_callback(lch, w, uart_cb[hsuart->uart_no].dev); prev_pos = 0; } else { prev_pos = curr_pos; mod_timer(&hsuart->timer, jiffies + msecs_to_jiffies(hsuart->timeout)); } FN_OUT(0);}/** * brief omap24xx_uart_isr * Identifes the source of interrupt(UART1, UART2, UART3) * and reads respective IIR register data. * Sends IIR data to the user driver. * param irq * param dev_id * param regs */static irqreturn_tomap24xx_uart_isr(int irq, void *dev_id, struct pt_regs *regs){ u8 iir_data; u32 uart_base; int uart_no; FN_IN; switch (irq) { case INT_24XX_UART1_IRQ: uart_base = UART_MODULE_BASE(UART1); uart_no = UART1; break; case INT_24XX_UART2_IRQ: uart_base = UART_MODULE_BASE(UART2); uart_no = UART2; break; case INT_24XX_UART3_IRQ: uart_base = UART_MODULE_BASE(UART3); uart_no = UART3; break; default: printk("UART interrupt from unknown source\n"); return IRQ_NONE; } iir_data = readb(uart_base + REG_IIR); uart_cb[uart_no].int_callback(iir_data, dev_id); FN_OUT(0); return IRQ_HANDLED;}/** * brief uart_rx_dma_callback * * param lch * param ch_status * param data */static void uart_rx_dma_callback(int lch, u16 ch_status, void *data){ int uart_no = 0; FN_IN; if (lch == ui[UART1].rx_dma_channel) uart_no = UART1; else if (lch == ui[UART2].rx_dma_channel) uart_no = UART2; else if (lch == ui[UART3].rx_dma_channel) uart_no = UART3; del_timer(&ui[uart_no].timer); ui[uart_no].timer_active = 0; uart_cb[uart_no].uart_rx_dma_callback(lch, ch_status, data); ui[uart_no].rx_buf_state = FREE; FN_OUT(0);}/** * brief uart_tx_dma_callback * * param lch * param ch_status * param data */static void uart_tx_dma_callback(int lch, u16 ch_status, void *data){ FN_IN; if (lch == ui[UART1].tx_dma_channel) { uart_cb[UART1].uart_tx_dma_callback(lch, ch_status, data); ui[UART1].tx_buf_state = FREE; } else if (lch == ui[UART2].tx_dma_channel) { uart_cb[UART2].uart_tx_dma_callback(lch, ch_status, data); ui[UART2].tx_buf_state = FREE; } else if (lch == ui[UART3].tx_dma_channel) { uart_cb[UART3].uart_tx_dma_callback(lch, ch_status, data); ui[UART3].tx_buf_state = FREE; } FN_OUT(0);}/** * brief omap24xx_uart_get_parms * reads requested register data * param uart_no * param data * param reg * param lcr_mode * * return */int omap24xx_uart_get_parms(int uart_no, u8 * data, u8 reg, u8 lcr_mode){ u32 uart_base = UART_MODULE_BASE(uart_no); u8 lcr_data; FN_IN; if (unlikely(uart_no < 0 || uart_no > MAX_UARTS)) { D3(KERN_INFO "C Bad uart id %d \n", uart_no); FN_OUT(EPERM); return -EPERM; } lcr_data = readb(uart_base + REG_LCR); outb(lcr_mode, uart_base + REG_LCR); *data = readb(uart_base + reg); /* Restore LCR data */ outb(lcr_data, uart_base + REG_LCR); return 0; FN_OUT(0);}EXPORT_SYMBOL(omap24xx_uart_get_parms);/** * brief omap24xx_uart_set_parms * writes values into requested UART register * param uart_no * param uart_set * * return */int omap24xx_uart_set_parms(int uart_no, struct uart_setparm *uart_set){ u32 uart_base = UART_MODULE_BASE(uart_no); u8 lcr_data; FN_IN; if (unlikely(uart_no < 0 || uart_no > MAX_UARTS)) { D3(KERN_INFO "C Bad uart id %d \n", uart_no); FN_OUT(EPERM); return -EPERM; } spin_lock(&(ui[uart_no].uart_lock)); lcr_data = readb(uart_base + REG_LCR); outb(uart_set->lcr, uart_base + REG_LCR); outb(uart_set->reg_data, uart_base + uart_set->reg); /* Restore LCR data */ outb(lcr_data, uart_base + REG_LCR); spin_unlock(&(ui[uart_no].uart_lock)); return 0; FN_OUT(0);}EXPORT_SYMBOL(omap24xx_uart_set_parms);/** * brief omap24xx_uart_get_speed * reads DLL and DLH register values and * calculates UART speed. * param uart_no * param speed * * return */int omap24xx_uart_get_speed(int uart_no, int *speed){ u32 uart_base = UART_MODULE_BASE(uart_no); u8 reg = 0; u8 dll, dlh; u16 divisor = 0; FN_IN; if (unlikely(uart_no < 0 || uart_no > MAX_UARTS)) { D3(KERN_INFO "C Bad uart id %d \n", uart_no); return -EPERM; } spin_lock(&(ui[uart_no].uart_lock)); reg = LCR_MODE2; outb(reg, uart_base + REG_LCR); dll = readb(uart_base + REG_DLL); dlh = readb(uart_base + REG_DLH); divisor = (dlh << 8) + dll; if (!divisor) { printk(KERN_WARNING "DLL and DLH read error !!!!!!!!!!!"); return -EPERM; } *speed = (BASE_CLK) / 16 * divisor; spin_unlock(&(ui[uart_no].uart_lock)); return 0; FN_OUT(0);}EXPORT_SYMBOL(omap24xx_uart_get_speed);/** * brief omap24xx_uart_set_speed * used to set the UART speed. * param uart_no * param speed * * return */int omap24xx_uart_set_speed(int uart_no, int speed){ u32 uart_base = UART_MODULE_BASE(uart_no); u8 lcr_data, mdr1_data; int divisor; FN_IN; if (unlikely(uart_no < 0 || uart_no > MAX_UARTS)) { D3(KERN_INFO "C Bad uart id %d \n", uart_no); return -EPERM; } spin_lock(&(ui[uart_no].uart_lock)); /* Disable UART before changing the clock speed - TRM - 18-52 */ mdr1_data = readb(uart_base + REG_MDR1); outb(UART_DISABLE, uart_base + REG_MDR1); /* Enable access to DLL and DLH registers */ lcr_data = readb(uart_base + REG_LCR); outb(LCR_MODE2, uart_base + REG_LCR); /* Only UART3 supports IrDA mode */ if ((uart_no == UART3) && (uart_mode[uart_no] == IRDA_MODE)) { if (speed <= IR_SIR_SPEED) { printk(KERN_INFO "SIR Mode : Changing UART speed to %d....", speed); divisor = BASE_CLK / (16 * speed); outb(divisor & 0xFF, uart_base + REG_DLL); outb(divisor >> 8, uart_base + REG_DLH); mdr1_data &= MODE_SELECT_MASK; mdr1_data |= UART_SIR_MODE; outb(mdr1_data, uart_base + REG_MDR1); printk("Done\n"); } else if (speed <= IR_MIR_SPEED) { printk(KERN_INFO "MIR Mode : Changing UART speed to %d....", speed); divisor = BASE_CLK / (41 * speed); outb(divisor & 0xFF, uart_base + REG_DLL); outb(divisor >> 8, uart_base + REG_DLH); mdr1_data &= MODE_SELECT_MASK; mdr1_data |= UART_MIR_MODE; outb(mdr1_data, uart_base + REG_MDR1); printk("Done\n"); } else if (speed <= IR_FIR_SPEED) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -