📄 mx21_16c2552.c
字号:
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
* interval should also be less than the timeout.
*
* Note: we have to use pretty tight timings here to satisfy
* the NIST-PCTS.
*/
char_time = (info->timeout - HZ/50) / info->port->fifosize;
char_time = char_time / 5;
if (char_time == 0)
char_time = 1;
if (timeout && timeout < char_time)
char_time = timeout;
/*
* If the transmitter hasn't cleared in twice the approximate
* amount of time to send the entire FIFO, it probably won't
* ever clear. This assumes the UART isn't doing flow
* control, which is currently the case. Hence, if it ever
* takes longer than info->timeout, this is probably due to a
* UART bug of some kind. So, we clamp the timeout parameter at
* 2*info->timeout.
*/
if (!timeout || timeout > 2 * info->timeout)
timeout = 2 * info->timeout;
expire = jiffies + timeout;
#if DEBUG
printk("ambauart_wait_until_sent(%d), jiff=%lu, expire=%lu...\n",
MINOR(tty->device) - tty->driver.minor_start, jiffies,
expire);
#endif
while (UART_GET_FR(info->port) & AMBA_UARTFR_BUSY) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(char_time);
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, expire))
break;
status = UART_GET_FR(info->port);
}
set_current_state(TASK_RUNNING);
}
static void ambauart_hangup(struct tty_struct *tty)
{
struct amba_info *info = tty->driver_data;
struct amba_state *state = info->state;
ambauart_flush_buffer(tty);
if (info->flags & ASYNC_CLOSING)
return;
ambauart_shutdown(info);
info->event = 0;
state->count = 0;
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
info->tty = NULL;
wake_up_interruptible(&info->open_wait);
}
static int block_til_ready(struct tty_struct *tty, struct file *filp,
struct amba_info *info)
{
DECLARE_WAITQUEUE(wait, current);
struct amba_state *state = info->state;
unsigned long flags;
int do_clocal = 0, extra_count = 0, retval;
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
return (info->flags & ASYNC_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS;
}
/*
* If this is a callout device, then just make sure the normal
* device isn't being used.
*/
if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
if (info->flags & ASYNC_NORMAL_ACTIVE)
return -EBUSY;
if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_SESSION_LOCKOUT) &&
(info->session != current->session))
return -EBUSY;
if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_PGRP_LOCKOUT) &&
(info->pgrp != current->pgrp))
return -EBUSY;
info->flags |= ASYNC_CALLOUT_ACTIVE;
return 0;
}
/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
if (info->flags & ASYNC_CALLOUT_ACTIVE)
return -EBUSY;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
if (info->flags & ASYNC_CALLOUT_ACTIVE) {
if (state->normal_termios.c_cflag & CLOCAL)
do_clocal = 1;
} else {
if (tty->termios->c_cflag & CLOCAL)
do_clocal = 1;
}
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, state->count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&info->open_wait, &wait);
save_flags(flags); cli();
if (!tty_hung_up_p(filp)) {
extra_count = 1;
state->count--;
}
restore_flags(flags);
info->blocked_open++;
while (1) {
save_flags(flags); cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
(tty->termios->c_cflag & CBAUD)) {
info->mctrl = TIOCM_DTR | TIOCM_RTS;
info->port->set_mctrl(info->port, info->mctrl);
}
restore_flags(flags);
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(info->flags & ASYNC_INITIALIZED)) {
if (info->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
!(info->flags & ASYNC_CLOSING) &&
(do_clocal || (UART_GET_FR(info->port) & AMBA_UARTFR_DCD)))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
if (extra_count)
state->count++;
info->blocked_open--;
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
static struct amba_info *ambauart_get(int line)
{
struct amba_info *info;
struct amba_state *state = amba_state + line;
state->count++;
if (state->info)
return state->info;
info = kmalloc(sizeof(struct amba_info), GFP_KERNEL);
if (info) {
memset(info, 0, sizeof(struct amba_info));
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->close_wait);
init_waitqueue_head(&info->delta_msr_wait);
info->flags = state->flags;
info->state = state;
info->port = amba_ports + line;
tasklet_init(&info->tlet, ambauart_tasklet_action,
(unsigned long)info);
}
if (state->info) {
kfree(info);
return state->info;
}
state->info = info;
return info;
}
static int ambauart_open(struct tty_struct *tty, struct file *filp)
{
struct amba_info *info;
int retval, line = MINOR(tty->device) - tty->driver.minor_start;
#if DEBUG
printk("ambauart_open(%d) called\n", line);
#endif
MOD_INC_USE_COUNT;
if (line >= SERIAL_AMBA_NR) {
MOD_DEC_USE_COUNT;
return -ENODEV;
}
info = ambauart_get(line);
if (!info)
return -ENOMEM;
tty->driver_data = info;
info->tty = tty;
info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
/*
* Make sure we have the temporary buffer allocated
*/
if (!tmp_buf) {
unsigned long page = get_zeroed_page(GFP_KERNEL);
if (tmp_buf)
free_page(page);
else if (!page) {
MOD_DEC_USE_COUNT;
return -ENOMEM;
}
tmp_buf = (u_char *)page;
}
/*
* If the port is in the middle of closing, bail out now.
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
MOD_DEC_USE_COUNT;
return -EAGAIN;
}
//#ifdef CONFIG_SERIAL_AMBA_CONSOLE
#ifdef CONFIG_MX1_EXT_UART
/*
* Copy across the serial console cflag setting
*/
if (ambauart_cons.cflag && ambauart_cons.index == line) {
tty->termios->c_cflag = ambauart_cons.cflag;
ambauart_cons.cflag = 0;
}
#endif
/*
* Start up the serial port
*/
retval = ambauart_startup(info);
if (retval) {
MOD_DEC_USE_COUNT;
return retval;
}
retval = block_til_ready(tty, filp, info);
if (retval) {
MOD_DEC_USE_COUNT;
return retval;
}
if ((info->state->count == 1) &&
(info->flags & ASYNC_SPLIT_TERMIOS)) {
if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
*tty->termios = info->state->normal_termios;
else
*tty->termios = info->state->callout_termios;
ambauart_change_speed(info, NULL);
}
info->session = current->session;
info->pgrp = current->pgrp;
return 0;
}
int __init ambauart_init(void)
{
int i;
ambanormal_driver.magic = TTY_DRIVER_MAGIC;
ambanormal_driver.driver_name = "serial_amba";
ambanormal_driver.name = SERIAL_AMBA_NAME;
ambanormal_driver.major = SERIAL_AMBA_MAJOR;
ambanormal_driver.minor_start = SERIAL_AMBA_MINOR;
ambanormal_driver.num = SERIAL_AMBA_NR;
ambanormal_driver.type = TTY_DRIVER_TYPE_SERIAL;
ambanormal_driver.subtype = SERIAL_TYPE_NORMAL;
ambanormal_driver.init_termios = tty_std_termios;
ambanormal_driver.init_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
ambanormal_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
ambanormal_driver.refcount = &ambauart_refcount;
ambanormal_driver.table = ambauart_table;
ambanormal_driver.termios = ambauart_termios;
ambanormal_driver.termios_locked = ambauart_termios_locked;
ambanormal_driver.open = ambauart_open;
ambanormal_driver.close = ambauart_close;
ambanormal_driver.write = ambauart_write;
ambanormal_driver.put_char = ambauart_put_char;
ambanormal_driver.flush_chars = ambauart_flush_chars;
ambanormal_driver.write_room = ambauart_write_room;
ambanormal_driver.chars_in_buffer = ambauart_chars_in_buffer;
ambanormal_driver.flush_buffer = ambauart_flush_buffer;
ambanormal_driver.ioctl = ambauart_ioctl;
ambanormal_driver.throttle = ambauart_throttle;
ambanormal_driver.unthrottle = ambauart_unthrottle;
ambanormal_driver.send_xchar = ambauart_send_xchar;
ambanormal_driver.set_termios = ambauart_set_termios;
ambanormal_driver.stop = ambauart_stop;
ambanormal_driver.start = ambauart_start;
ambanormal_driver.hangup = ambauart_hangup;
ambanormal_driver.break_ctl = ambauart_break_ctl;
ambanormal_driver.wait_until_sent = ambauart_wait_until_sent;
ambanormal_driver.read_proc = NULL;
/*
* The callout device is just like the normal device except for
* the major number and the subtype code.
*/
ambacallout_driver = ambanormal_driver;
ambacallout_driver.name = CALLOUT_AMBA_NAME;
ambacallout_driver.major = CALLOUT_AMBA_MAJOR;
ambacallout_driver.subtype = SERIAL_TYPE_CALLOUT;
ambacallout_driver.read_proc = NULL;
ambacallout_driver.proc_entry = NULL;
if (tty_register_driver(&ambanormal_driver))
panic("Couldn't register AMBA serial driver\n");
if (tty_register_driver(&ambacallout_driver))
panic("Couldn't register AMBA callout driver\n");
for (i = 0; i < SERIAL_AMBA_NR; i++) {
struct amba_state *state = amba_state + i;
state->line = i;
state->close_delay = 5 * HZ / 10;
state->closing_wait = 30 * HZ;
state->callout_termios = ambacallout_driver.init_termios;
state->normal_termios = ambanormal_driver.init_termios;
}
return 0;
}
static int st16c2552_gpio_init(void)
{
/*
* PC17-SAP_RXD : RESET
* PC18-SAP_TXD : PORTA_INT
*/
_reg_GPIO_GIUS(GPIOC) |= 0x00060000;
_reg_GPIO_OCR2(GPIOC) |= 0x0000003C;
_reg_GPIO_DDIR(GPIOC) &= ~(0x00040000);
_reg_GPIO_DDIR(GPIOC) |= 0x00020000;
_reg_GPIO_IMR(GPIOC) &= ~(0x00040000);
_reg_GPIO_ICR1(GPIOC) &= 0xffffffcf;
return 0;
}
static int st16c2552_gpio_enable_inter(void)
{
_reg_GPIO_IMR(GPIOC) |= 0x00040000;
return 0;
}
static int st16c2552_gpio_disable_inter(void)
{
_reg_GPIO_IMR(GPIOC) &= ~(0x00040000);
return 0;
}
static int st16c2552_reset(void)
{
int i;
_reg_GPIO_DR(GPIOC) |= 0x00020000;
for(i=0;i<10000;i++);
_reg_GPIO_DR(GPIOC) &= ~0x00020000;
return 0;
}
int __exit st16c2552_cleanup(void)
{
/*Do some cleanup work*/
free_irq(8,"mx21_16c2552");
//tasklet_kill(&ambauart_tasklet_action);
if (tty_unregister_driver(&ambanormal_driver))
panic("Couldn't unregister AMBA serial driver\n");
if (tty_unregister_driver(&ambacallout_driver))
panic("Couldn't unregister AMBA callout driver\n");
return 0;
}
int init_module(void)
{
u8 a[2];
_reg_WEIM_CSL(CS1) = 0x11118301; //buswidth=8
st16c2552_gpio_init();
st16c2552_reset();
st16c2552_gpio_enable_inter();
//IO_WRITE(MX21_EXT_UART_BASE+UART16C2552_LCR,0x80);
//IO_WRITE(MX21_EXT_UART_BASE+UART16C2552_DLL,0x80);
//IO_WRITE(MX21_EXT_UART_BASE+UART16C2552_DLM,0x10);
//a[0] = IO_READ(MX21_EXT_UART_BASE+UART16C2552_DLL);
//a[1] = IO_READ(MX21_EXT_UART_BASE+UART16C2552_DLM);
//printk("dll=%x, dlm=%x\n",a[0],a[1]);
return ambauart_init();
}
void cleanup_module(void)
{
_reg_WEIM_CSL(CS1) = 0x11118501; //buswidth=16
st16c2552_cleanup();
}
MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -