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 + -
显示快捷键?