sh-sci.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,633 行 · 第 1/3 页
C
1,633 行
tty->flip.flag_buf_ptr[i] = TTY_FRAME; pr_debug("sci: frame error\n"); } else if (status&SCxSR_PER(port)) { tty->flip.flag_buf_ptr[i] = TTY_PARITY; pr_debug("sci: parity error\n"); } else { tty->flip.flag_buf_ptr[i] = TTY_NORMAL; } } } sci_in(port, SCxSR); /* dummy read */ sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); /* Update the kernel buffer end */ tty->flip.count += count; tty->flip.char_buf_ptr += count; tty->flip.flag_buf_ptr += count; copied += count; port->icount.rx += count; } if (copied) { /* Tell the rest of the system the news. New characters! */ tty_flip_buffer_push(tty); } else { sci_in(port, SCxSR); /* dummy read */ sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); }}#define SCI_BREAK_JIFFIES (HZ/20)/* The sci generates interrupts during the break, * 1 per millisecond or so during the break period, for 9600 baud. * So dont bother disabling interrupts. * But dont want more than 1 break event. * Use a kernel timer to periodically poll the rx line until * the break is finished. */static void sci_schedule_break_timer(struct sci_port *port){ port->break_timer.expires = jiffies + SCI_BREAK_JIFFIES; add_timer(&port->break_timer);}/* Ensure that two consecutive samples find the break over. */static void sci_break_timer(unsigned long data){ struct sci_port * port = (struct sci_port *)data; if(sci_rxd_in(&port->port) == 0) { port->break_flag = 1; sci_schedule_break_timer(port); } else if(port->break_flag == 1){ /* break is over. */ port->break_flag = 2; sci_schedule_break_timer(port); } else port->break_flag = 0;}static inline int sci_handle_errors(struct uart_port *port){ int copied = 0; unsigned short status = sci_in(port, SCxSR); struct tty_struct *tty = port->info->tty; if (status&SCxSR_ORER(port) && tty->flip.count<TTY_FLIPBUF_SIZE) { /* overrun error */ copied++; *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; pr_debug("sci: overrun error\n"); } if (status&SCxSR_FER(port) && tty->flip.count<TTY_FLIPBUF_SIZE) { if (sci_rxd_in(port) == 0) { /* Notify of BREAK */ struct sci_port * sci_port = (struct sci_port *)port; if(!sci_port->break_flag) { sci_port->break_flag = 1; sci_schedule_break_timer((struct sci_port *)port); /* Do sysrq handling. */ if(uart_handle_break(port)) { return 0; } pr_debug("sci: BREAK detected\n"); copied++; *tty->flip.flag_buf_ptr++ = TTY_BREAK; } } else { /* frame error */ copied++; *tty->flip.flag_buf_ptr++ = TTY_FRAME; pr_debug("sci: frame error\n"); } } if (status&SCxSR_PER(port) && tty->flip.count<TTY_FLIPBUF_SIZE) { /* parity error */ copied++; *tty->flip.flag_buf_ptr++ = TTY_PARITY; pr_debug("sci: parity error\n"); } if (copied) { tty->flip.count += copied; tty_flip_buffer_push(tty); } return copied;}static inline int sci_handle_breaks(struct uart_port *port){ int copied = 0; unsigned short status = sci_in(port, SCxSR); struct tty_struct *tty = port->info->tty; struct sci_port *s = &sci_ports[port->line]; if (!s->break_flag && status & SCxSR_BRK(port) && tty->flip.count < TTY_FLIPBUF_SIZE) {#if defined(CONFIG_CPU_SH3) /* Debounce break */ s->break_flag = 1;#endif /* Notify of BREAK */ copied++; *tty->flip.flag_buf_ptr++ = TTY_BREAK; pr_debug("sci: BREAK detected\n"); }#if defined(SCIF_ORER) /* XXX: Handle SCIF overrun error */ if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { sci_out(port, SCLSR, 0); if(tty->flip.count<TTY_FLIPBUF_SIZE) { copied++; *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; pr_debug("sci: overrun error\n"); } }#endif if (copied) { tty->flip.count += copied; tty_flip_buffer_push(tty); } return copied;}static irqreturn_t sci_rx_interrupt(int irq, void *ptr, struct pt_regs *regs){ struct uart_port *port = ptr; /* I think sci_receive_chars has to be called irrespective * of whether the I_IXOFF is set, otherwise, how is the interrupt * to be disabled? */ sci_receive_chars(port, regs); return IRQ_HANDLED;}static irqreturn_t sci_tx_interrupt(int irq, void *ptr, struct pt_regs *regs){ struct uart_port *port = ptr; sci_transmit_chars(port); return IRQ_HANDLED;}static irqreturn_t sci_er_interrupt(int irq, void *ptr, struct pt_regs *regs){ struct uart_port *port = ptr; /* Handle errors */ if (port->type == PORT_SCI) { if (sci_handle_errors(port)) { /* discard character in rx buffer */ sci_in(port, SCxSR); sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); } } else {#if defined(SCIF_ORER) if((sci_in(port, SCLSR) & SCIF_ORER) != 0) { struct tty_struct *tty = port->info->tty; sci_out(port, SCLSR, 0); if(tty->flip.count<TTY_FLIPBUF_SIZE) { *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; tty->flip.count++; tty_flip_buffer_push(tty); pr_debug("scif: overrun error\n"); } }#endif sci_rx_interrupt(irq, ptr, regs); } sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); /* Kick the transmission */ sci_tx_interrupt(irq, ptr, regs); return IRQ_HANDLED;}static irqreturn_t sci_br_interrupt(int irq, void *ptr, struct pt_regs *regs){ struct uart_port *port = ptr; /* Handle BREAKs */ sci_handle_breaks(port); sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); return IRQ_HANDLED;}static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr, struct pt_regs *regs){ unsigned short ssr_status, scr_status; struct uart_port *port = ptr; ssr_status = sci_in(port,SCxSR); scr_status = sci_in(port,SCSCR); /* Tx Interrupt */ if ((ssr_status&0x0020) && (scr_status&0x0080)) sci_tx_interrupt(irq, ptr, regs); /* Rx Interrupt */ if ((ssr_status&0x0002) && (scr_status&0x0040)) sci_rx_interrupt(irq, ptr, regs); /* Error Interrupt */ if ((ssr_status&0x0080) && (scr_status&0x0400)) sci_er_interrupt(irq, ptr, regs); /* Break Interrupt */ if ((ssr_status&0x0010) && (scr_status&0x0200)) sci_br_interrupt(irq, ptr, regs); return IRQ_HANDLED;}#ifdef CONFIG_CPU_FREQ/* * Here we define a transistion notifier so that we can update all of our * ports' baud rate when the peripheral clock changes. */static int sci_notifier(struct notifier_block *self, unsigned long phase, void *p){ struct cpufreq_freqs *freqs = p; int i; if ((phase == CPUFREQ_POSTCHANGE) || (phase == CPUFREQ_RESUMECHANGE)){ for (i = 0; i < SCI_NPORTS; i++) { struct uart_port *port = &sci_ports[i]; /* * Update the uartclk per-port if frequency has * changed, since it will no longer necessarily be * consistent with the old frequency. * * Really we want to be able to do something like * uart_change_speed() or something along those lines * here to implicitly reset the per-port baud rate.. * * Clean this up later.. */ port->uartclk = current_cpu_data.module_clock * 16; } printk("%s: got a postchange notification for cpu %d (old %d, new %d)\n", __FUNCTION__, freqs->cpu, freqs->old, freqs->new); } return NOTIFY_OK;}static struct notifier_block sci_nb = { &sci_notifier, NULL, 0 };#endif /* CONFIG_CPU_FREQ */static int sci_request_irq(struct sci_port *port){ int i; irqreturn_t (*handlers[4])(int irq, void *ptr, struct pt_regs *regs) = { sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, sci_br_interrupt, }; const char *desc[] = { "SCI Receive Error", "SCI Receive Data Full", "SCI Transmit Data Empty", "SCI Break" }; if (port->irqs[0] == port->irqs[1]) { if (!port->irqs[0]) { printk(KERN_ERR "sci: Cannot allocate irq.(IRQ=0)\n"); return -ENODEV; } if (request_irq(port->irqs[0], sci_mpxed_interrupt, SA_INTERRUPT, "sci", port)) { printk(KERN_ERR "sci: Cannot allocate irq.\n"); return -ENODEV; } } else { for (i = 0; i < ARRAY_SIZE(handlers); i++) { if (!port->irqs[i]) continue; if (request_irq(port->irqs[i], handlers[i], SA_INTERRUPT, desc[i], port)) { printk(KERN_ERR "sci: Cannot allocate irq.\n"); return -ENODEV; } } } return 0;}static void sci_free_irq(struct sci_port *port){ int i; if (port->irqs[0] == port->irqs[1]) { if (!port->irqs[0]) printk("sci: sci_free_irq error\n"); else free_irq(port->irqs[0], port); } else { for (i = 0; i < ARRAY_SIZE(port->irqs); i++) { if (!port->irqs[i]) continue; free_irq(port->irqs[i], port); } }}static unsigned int sci_tx_empty(struct uart_port *port){ /* Can't detect */ return TIOCSER_TEMT;}static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl){ /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ /* If you have signals for DTR and DCD, please implement here. */}static unsigned int sci_get_mctrl(struct uart_port *port){ /* This routine is used for geting signals of: DTR, DCD, DSR, RI, and CTS/RTS */ return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR;}static void sci_start_tx(struct uart_port *port, unsigned int tty_start){ struct sci_port *s = &sci_ports[port->line]; disable_irq(s->irqs[SCIx_TXI_IRQ]); sci_transmit_chars(port); enable_irq(s->irqs[SCIx_TXI_IRQ]);}static void sci_stop_tx(struct uart_port *port, unsigned int tty_stop){ unsigned long flags; unsigned short ctrl; /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ local_irq_save(flags); ctrl = sci_in(port, SCSCR); ctrl &= ~SCI_CTRL_FLAGS_TIE; sci_out(port, SCSCR, ctrl); local_irq_restore(flags);}static void sci_start_rx(struct uart_port *port, unsigned int tty_start){ unsigned long flags; unsigned short ctrl; /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ local_irq_save(flags); ctrl = sci_in(port, SCSCR); ctrl |= SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE; sci_out(port, SCSCR, ctrl); local_irq_restore(flags);}static void sci_stop_rx(struct uart_port *port){ unsigned long flags; unsigned short ctrl; /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ local_irq_save(flags); ctrl = sci_in(port, SCSCR); ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); sci_out(port, SCSCR, ctrl); local_irq_restore(flags);}static void sci_enable_ms(struct uart_port *port){ /* Nothing here yet .. */}static void sci_break_ctl(struct uart_port *port, int break_state){ /* Nothing here yet .. */}static int sci_startup(struct uart_port *port){ struct sci_port *s = &sci_ports[port->line];#if defined(__H8300S__) h8300_sci_enable(port, sci_enable);#endif sci_request_irq(s); sci_start_tx(port, 1); sci_start_rx(port, 1); return 0;}static void sci_shutdown(struct uart_port *port){ struct sci_port *s = &sci_ports[port->line]; sci_stop_rx(port); sci_stop_tx(port, 1); sci_free_irq(s);#if defined(__H8300S__) h8300_sci_enable(port, sci_disable);#endif}static void sci_set_termios(struct uart_port *port, struct termios *termios, struct termios *old){ struct sci_port *s = &sci_ports[port->line]; unsigned int status, baud, smr_val; unsigned long flags; int t; baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); spin_lock_irqsave(&port->lock, flags); do { status = sci_in(port, SCxSR); } while (!(status & SCxSR_TEND(port))); sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */#if !defined(SCI_ONLY) if (port->type == PORT_SCIF) { sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); }#endif smr_val = sci_in(port, SCSMR) & 3; if ((termios->c_cflag & CSIZE) == CS7) smr_val |= 0x40; if (termios->c_cflag & PARENB) smr_val |= 0x20; if (termios->c_cflag & PARODD) smr_val |= 0x30; if (termios->c_cflag & CSTOPB) smr_val |= 0x08; uart_update_timeout(port, termios->c_cflag, baud); sci_out(port, SCSMR, smr_val); switch (baud) { case 0: t = -1; break; case 2400: t = BPS_2400; break; case 4800: t = BPS_4800; break; case 9600: t = BPS_9600; break; case 19200: t = BPS_19200; break; case 38400: t = BPS_38400; break; case 57600: t = BPS_57600; break; case 115200: t = BPS_115200; break; default: t = SCBRR_VALUE(baud); break; } if (t > 0) { if(t >= 256) { sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); t >>= 2; } else { sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); } sci_out(port, SCBRR, t); udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ } s->init_pins(port, termios->c_cflag); sci_out(port, SCSCR, SCSCR_INIT(port)); if ((termios->c_cflag & CREAD) != 0) sci_start_rx(port,0); spin_unlock_irqrestore(&port->lock, flags);}static const char *sci_type(struct uart_port *port){ switch (port->type) { case PORT_SCI: return "sci"; case PORT_SCIF: return "scif"; case PORT_IRDA: return "irda"; } return 0;}static void sci_release_port(struct uart_port *port){ /* Nothing here yet .. */}static int sci_request_port(struct uart_port *port){ /* Nothing here yet .. */ return 0;}static void sci_config_port(struct uart_port *port, int flags){ struct sci_port *s = &sci_ports[port->line]; port->type = s->type;#if defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103) if (port->mapbase == 0) port->mapbase = onchip_remap(SCIF_ADDR_SH5, 1024, "SCIF"); port->membase = (void *)port->mapbase;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?