⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3c2410.c.txt

📁 本源码是对三星S3C24xx系列的UART的驱动程序.
💻 TXT
📖 第 1 页 / 共 5 页
字号:
362                         if (uerstat & S3C2410_UERSTAT_BREAK) {
363                                 dbg("break!\n");
364                                 port->icount.brk++;
365                                 if (uart_handle_break(port))
366                                     goto ignore_char;
367                         }
368 
369                         if (uerstat & S3C2410_UERSTAT_FRAME)
370                                 port->icount.frame++;
371                         if (uerstat & S3C2410_UERSTAT_OVERRUN)
372                                 port->icount.overrun++;
373 
374                         uerstat &= port->read_status_mask;
375 
376                         if (uerstat & S3C2410_UERSTAT_BREAK)
377                                 flag = TTY_BREAK;
378                         else if (uerstat & S3C2410_UERSTAT_PARITY)
379                                 flag = TTY_PARITY;
380                         else if (uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
381                                 flag = TTY_FRAME;
382                 }
383 
384                 if (uart_handle_sysrq_char(port, ch, regs))
385                         goto ignore_char;
386 
387                 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
388 
389         ignore_char:
390                 continue;
391         }
392         tty_flip_buffer_push(tty);
393 
394  out:
395         return IRQ_HANDLED;
396 }
397 
398 static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id, struct pt_regs *regs)
399 {
400         struct s3c24xx_uart_port *ourport = id;
401         struct uart_port *port = &ourport->port;
402         struct circ_buf *xmit = &port->info->xmit;
403         int count = 256;
404 
405         if (port->x_char) {
406                 wr_regb(port, S3C2410_UTXH, port->x_char);
407                 port->icount.tx++;
408                 port->x_char = 0;
409                 goto out;
410         }
411 
412         /* if there isnt anything more to transmit, or the uart is now
413          * stopped, disable the uart and exit
414         */
415 
416         if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
417                 s3c24xx_serial_stop_tx(port);
418                 goto out;
419         }
420 
421         /* try and drain the buffer... */
422 
423         while (!uart_circ_empty(xmit) && count-- > 0) {
424                 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
425                         break;
426 
427                 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
428                 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
429                 port->icount.tx++;
430         }
431 
432         if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
433                 uart_write_wakeup(port);
434 
435         if (uart_circ_empty(xmit))
436                 s3c24xx_serial_stop_tx(port);
437 
438  out:
439         return IRQ_HANDLED;
440 }
441 
442 static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
443 {
444         struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
445         unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
446         unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
447 
448         if (ufcon & S3C2410_UFCON_FIFOMODE) {
449                 if ((ufstat & info->tx_fifomask) != 0 ||
450                     (ufstat & info->tx_fifofull))
451                         return 0;
452 
453                 return 1;
454         }
455 
456         return s3c24xx_serial_txempty_nofifo(port);
457 }
458 
459 /* no modem control lines */
460 static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
461 {
462         unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
463 
464         if (umstat & S3C2410_UMSTAT_CTS)
465                 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
466         else
467                 return TIOCM_CAR | TIOCM_DSR;
468 }
469 
470 static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
471 {
472         /* todo - possibly remove AFC and do manual CTS */
473 }
474 
475 static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
476 {
477         unsigned long flags;
478         unsigned int ucon;
479 
480         spin_lock_irqsave(&port->lock, flags);
481 
482         ucon = rd_regl(port, S3C2410_UCON);
483 
484         if (break_state)
485                 ucon |= S3C2410_UCON_SBREAK;
486         else
487                 ucon &= ~S3C2410_UCON_SBREAK;
488 
489         wr_regl(port, S3C2410_UCON, ucon);
490 
491         spin_unlock_irqrestore(&port->lock, flags);
492 }
493 
494 static void s3c24xx_serial_shutdown(struct uart_port *port)
495 {
496         struct s3c24xx_uart_port *ourport = to_ourport(port);
497 
498         if (ourport->tx_claimed) {
499                 free_irq(TX_IRQ(port), ourport);
500                 tx_enabled(port) = 0;
501                 ourport->tx_claimed = 0;
502         }
503 
504         if (ourport->rx_claimed) {
505                 free_irq(RX_IRQ(port), ourport);
506                 ourport->rx_claimed = 0;
507                 rx_enabled(port) = 0;
508         }
509 }
510 
511 
512 static int s3c24xx_serial_startup(struct uart_port *port)
513 {
514         struct s3c24xx_uart_port *ourport = to_ourport(port);
515         int ret;
516 
517         dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
518             port->mapbase, port->membase);
519 
520         rx_enabled(port) = 1;
521 
522         ret = request_irq(RX_IRQ(port),
523                           s3c24xx_serial_rx_chars, 0,
524                           s3c24xx_serial_portname(port), ourport);
525 
526         if (ret != 0) {
527                 printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
528                 return ret;
529         }
530 
531         ourport->rx_claimed = 1;
532 
533         dbg("requesting tx irq...\n");
534 
535         tx_enabled(port) = 1;
536 
537         ret = request_irq(TX_IRQ(port),
538                           s3c24xx_serial_tx_chars, 0,
539                           s3c24xx_serial_portname(port), ourport);
540 
541         if (ret) {
542                 printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
543                 goto err;
544         }
545 
546         ourport->tx_claimed = 1;
547 
548         dbg("s3c24xx_serial_startup ok\n");
549 
550         /* the port reset code should have done the correct
551          * register setup for the port controls */
552 
553         return ret;
554 
555  err:
556         s3c24xx_serial_shutdown(port);
557         return ret;
558 }
559 
560 /* power power management control */
561 
562 static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
563                               unsigned int old)
564 {
565         struct s3c24xx_uart_port *ourport = to_ourport(port);
566 
567         switch (level) {
568         case 3:
569                 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
570                         clk_disable(ourport->baudclk);
571 
572                 clk_disable(ourport->clk);
573                 break;
574 
575         case 0:
576                 clk_enable(ourport->clk);
577 
578                 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
579                         clk_enable(ourport->baudclk);
580 
581                 break;
582         default:
583                 printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level);
584         }
585 }
586 
587 /* baud rate calculation
588  *
589  * The UARTs on the S3C2410/S3C2440 can take their clocks from a number
590  * of different sources, including the peripheral clock ("pclk") and an
591  * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")
592  * with a programmable extra divisor.
593  *
594  * The following code goes through the clock sources, and calculates the
595  * baud clocks (and the resultant actual baud rates) and then tries to
596  * pick the closest one and select that.
597  *
598 */
599 
600 
601 #define MAX_CLKS (8)
602 
603 static struct s3c24xx_uart_clksrc tmp_clksrc = {
604         .name           = "pclk",
605         .min_baud       = 0,
606         .max_baud       = 0,
607         .divisor        = 1,
608 };
609 
610 static inline int
611 s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
612 {
613         struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
614 
615         return (info->get_clksrc)(port, c);
616 }
617 
618 static inline int
619 s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
620 {
621         struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
622 
623         return (info->set_clksrc)(port, c);
624 }
625 
626 struct baud_calc {
627         struct s3c24xx_uart_clksrc      *clksrc;
628         unsigned int                     calc;
629         unsigned int                     quot;
630         struct clk                      *src;
631 };
632 
633 static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
634                                    struct uart_port *port,
635                                    struct s3c24xx_uart_clksrc *clksrc,
636                                    unsigned int baud)
637 {
638         unsigned long rate;
639 
640         calc->src = clk_get(port->dev, clksrc->name);
641         if (calc->src == NULL || IS_ERR(calc->src))
642                 return 0;
643 
644         rate = clk_get_rate(calc->src);
645         rate /= clksrc->divisor;
646 
647         calc->clksrc = clksrc;
648         calc->quot = (rate + (8 * baud)) / (16 * baud);
649         calc->calc = (rate / (calc->quot * 16));
650 
651         calc->quot--;
652         return 1;
653 }
654 
655 static unsigned int s3c24xx_serial_getclk(struct uart_port *port,
656                                           struct s3c24xx_uart_clksrc **clksrc,
657                                           struct clk **clk,
658                                           unsigned int baud)
659 {
660         struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
661         struct s3c24xx_uart_clksrc *clkp;
662         struct baud_calc res[MAX_CLKS];
663         struct baud_calc *resptr, *best, *sptr;
664         int i;
665 
666         clkp = cfg->clocks;
667         best = NULL;
668 
669         if (cfg->clocks_size < 2) {
670                 if (cfg->clocks_size == 0)
671                         clkp = &tmp_clksrc;
672 
673                 /* check to see if we're sourcing fclk, and if so we're
674                  * going to have to update the clock source
675                  */
676 
677                 if (strcmp(clkp->name, "fclk") == 0) {
678                         struct s3c24xx_uart_clksrc src;
679 
680                         s3c24xx_serial_getsource(port, &src);
681 
682                         /* check that the port already using fclk, and if
683                          * not, then re-select fclk
684                          */
685 
686                         if (strcmp(src.name, clkp->name) == 0) {
687                                 s3c24xx_serial_setsource(port, clkp);
688                                 s3c24xx_serial_getsource(port, &src);
689                         }
690 
691                         clkp->divisor = src.divisor;
692                 }
693 
694                 s3c24xx_serial_calcbaud(res, port, clkp, baud);
695                 best = res;
696                 resptr = best + 1;
697         } else {
698                 resptr = res;
699 
700                 for (i = 0; i < cfg->clocks_size; i++, clkp++) {
701                         if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud))
702                                 resptr++;
703                 }
704         }
705 
706         /* ok, we now need to select the best clock we found */
707 
708         if (!best) {
709                 unsigned int deviation = (1<<30)|((1<<30)-1);
710                 int calc_deviation;
711 
712                 for (sptr = res; sptr < resptr; sptr++) {
713                         printk(KERN_DEBUG
714                                "found clk %p (%s) quot %d, calc %d\n",
715                                sptr->clksrc, sptr->clksrc->name,
716                                sptr->quot, sptr->calc);
717 
718                         calc_deviation = baud - sptr->calc;
719                         if (calc_deviation < 0)
720                                 calc_deviation = -calc_deviation;
721 
722                         if (calc_deviation < deviation) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -