Chinaunix首页 | 论坛 | 博客
  • 博客访问: 749098
  • 博文数量: 869
  • 博客积分: 201
  • 博客等级: 入伍新兵
  • 技术积分: 3376
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-21 19:42
文章分类

全部博文(869)

文章存档

2014年(4)

2013年(415)

2012年(453)

我的朋友

分类:

2012-10-25 14:36:17

原文地址:8250.c分析(初始化) 作者:lwchsz

static struct uart_driver serial8250_reg = {
    .owner            = THIS_MODULE,
    .driver_name        = "serial",
    .dev_name        = "ttyS",
    .major            = TTY_MAJOR,
    .minor            = 64,
    .cons            = SERIAL8250_CONSOLE,
};

static struct platform_driver serial8250_isa_driver = {
    .probe        = serial8250_probe,
    .remove        = __devexit_p(serial8250_remove),
#if !defined(CONFIG_ARCH_CNS3XXX)
    .suspend    = serial8250_suspend,
    .resume        = serial8250_resume,
#endif /* CONFIG_ARCH_CNS3XXX */
    .driver        = {
        .name    = "serial8250",
        .owner    = THIS_MODULE,
    },
};

static int __init serial8250_init(void)
{
    int ret;

    if (nr_uarts > UART_NR)
        nr_uarts = UART_NR;

    printk(KERN_INFO "Serial: 8250/16550 driver, "
        "%d ports, IRQ sharing %sabled\n", nr_uarts,
        share_irqs ? "en" : "dis");

#ifdef CONFIG_SPARC
    ret = sunserial_register_minors(&serial8250_reg, UART_NR);
#else
    serial8250_reg.nr = UART_NR;
    ret = uart_register_driver(&serial8250_reg);
#endif
    if (ret)
        goto out;

    serial8250_isa_devs = platform_device_alloc("serial8250",
                            PLAT8250_DEV_LEGACY);
    if (!serial8250_isa_devs) {
        ret = -ENOMEM;
        goto unreg_uart_drv;
    }

    ret = platform_device_add(serial8250_isa_devs);
    if (ret)
        goto put_dev;

    serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
//注册设备驱动,这样会调用serial8250_probe
    ret = platform_driver_register(&serial8250_isa_driver);
    if (ret == 0)
        goto out;

    platform_device_del(serial8250_isa_devs);
put_dev:
    platform_device_put(serial8250_isa_devs);
unreg_uart_drv:
#ifdef CONFIG_SPARC
    sunserial_unregister_minors(&serial8250_reg, UART_NR);
#else
    uart_unregister_driver(&serial8250_reg);
#endif
out:
    return ret;
}

static void __init
serial8250_register_ports(struct uart_driver *drv, struct device *dev)
{
    int i;

    for (i = 0; i < nr_uarts; i++) {
        struct uart_8250_port *up = &serial8250_ports[i];
        up->cur_iotype = 0xFF;//初始化当前cur_iotype
    }

    serial8250_isa_init_ports();

    for (i = 0; i < nr_uarts; i++) {
        struct uart_8250_port *up = &serial8250_ports[i];

        up->port.dev = dev;//设置端口的父设备,这里是serial8250_isa_devs
        uart_add_one_port(drv, &up->port);//添加端口
    }
}

int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{
    struct uart_state *state;
    int ret = 0;
    struct device *tty_dev;

    BUG_ON(in_interrupt());

    if (port->line >= drv->nr)
        return -EINVAL;

    state = drv->state + port->line;

    mutex_lock(&port_mutex);
    mutex_lock(&state->mutex);
    if (state->port) {
        ret = -EINVAL;
        goto out;
    }

    state->port = port;
    state->pm_state = -1;//初始化电源管理状态

    port->cons = drv->cons;
    port->info = &state->info;

    /*
     * If this port is a console, then the spinlock is already
     * initialised.
     */
    if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
        spin_lock_init(&port->lock);
        lockdep_set_class(&port->lock, &port_lock_key);
    }

    uart_configure_port(drv, state, port);//配置端口

    /*
     * Register the port whether it's detected or not.  This allows
     * setserial to be used to alter this ports parameters.
     */
    tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev);//注册设备
    if (likely(!IS_ERR(tty_dev))) {
        device_init_wakeup(tty_dev, 1);
        device_set_wakeup_enable(tty_dev, 0);
    } else
        printk(KERN_ERR "Cannot register tty device on line %d\n",
               port->line);

    /*
     * Ensure UPF_DEAD is not set.
     */
    port->flags &= ~UPF_DEAD;

 out:
    mutex_unlock(&state->mutex);
    mutex_unlock(&port_mutex);

    return ret;
}

static void
uart_configure_port(struct uart_driver *drv, struct uart_state *state,
            struct uart_port *port)
{
    unsigned int flags;

    /*
     * If there isn't a port here, don't do anything further.
     */
    if (!port->iobase && !port->mapbase && !port->membase)
        return;

    /*
     * Now do the auto configuration stuff.  Note that config_port
     * is expected to claim the resources and map the port for us.
     */
    flags = 0;
    if (port->flags & UPF_AUTO_IRQ)//FALSE
        flags |= UART_CONFIG_IRQ;
    if (port->flags & UPF_BOOT_AUTOCONF) {//条件为真
        if (!(port->flags & UPF_FIXED_TYPE)) {//条件为假
//设置type为 PORT_UNKNOWN
            port->type = PORT_UNKNOWN;
            flags |= UART_CONFIG_TYPE;
        }
//调用具体设备的config_port,对8250就是serial8250_config_port
        port->ops->config_port(port, flags);
    }
//此时type 为PORT_16550A
    if (port->type != PORT_UNKNOWN) {
        unsigned long flags;
//打印相关信息
        uart_report_port(drv, port);
//改变串口电源管理状态,若有必要需对串口上电
        /* Power up port for set_mctrl() */
        uart_change_pm(state, 0);

        /*
         * Ensure that the modem control lines are de-activated.
         * keep the DTR setting that is set in uart_set_options()
         * We probably don't need a spinlock around this, but
         */
        spin_lock_irqsave(&port->lock, flags);
        port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);//设置模式寄存器
        spin_unlock_irqrestore(&port->lock, flags);

        /*
         * If this driver supports console, and it hasn't been
         * successfully registered yet, try to re-register it.
         * It may be that the port was not available.
         */
        if (port->cons && !(port->cons->flags & CON_ENABLED))//如果port->cons不为空,且控制台没有使能,则注册,因为在前面控制台已经注册,所以该条件为假
            register_console(port->cons);

        /*
         * Power down all ports by default, except the
         * console if we have one.
         */
        if (!uart_console(port))//如果不是控制台,则给端口设备下电
            uart_change_pm(state, 3);
    }
}

static void serial8250_config_port(struct uart_port *port, int flags)
{
    struct uart_8250_port *up = (struct uart_8250_port *)port;
    int probeflags = PROBE_ANY;
    int ret;

    /*
     * Find the region that we can probe for.  This in turn
     * tells us whether we can probe for the type of port.
     */
    ret = serial8250_request_std_resource(up);//注册登记内存资源,这样我们可以通过/proc/iomem查看
    if (ret < 0)
        return;

    ret = serial8250_request_rsa_resource(up);
    if (ret < 0)
        probeflags &= ~PROBE_RSA;

    if (up->port.iotype != up->cur_iotype)//iotype为UPIO_MEM值为2,cur_iotype在//serial8250_register_ports开始被初始化为0xff
        set_io_from_upio(port);//在该函数中cur_iotype被赋值为iotype

    if (flags & UART_CONFIG_TYPE)//该flags 含有该UART_CONFIG_TYPE
        autoconfig(up, probeflags);
    if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)//flags 无//UART_CONFIG_IRQ
        autoconfig_irq(up);

    if (up->port.type != PORT_RSA && probeflags & PROBE_RSA)
        serial8250_release_rsa_resource(up);
    if (up->port.type == PORT_UNKNOWN)
        serial8250_release_std_resource(up);
}
//对CNS3XXX来说,autoconfig实现如下
static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
{
    unsigned char c;

    up->port.type = PORT_16550A;//设置端口类型为PORT_16550A
    /* set clock source => 24MHz */
    c = serial_in(up, UART_LCR);
    serial_out(up, UART_LCR, c | UART_LCR_DLAB);//设置DLAB为方便设置UART_PSR寄存器
    serial_out(up, UART_PSR, UART_PSR_CLK_24000000);//设置24M时钟源
    serial_out(up, UART_LCR, c & ~UART_LCR_DLAB);

    return;
}

static inline void
uart_report_port(struct uart_driver *drv, struct uart_port *port)
{
    char address[64];

    switch (port->iotype) {
    case UPIO_PORT:
        snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase);
        break;
    case UPIO_HUB6:
        snprintf(address, sizeof(address),
             "I/O 0x%lx offset 0x%x", port->iobase, port->hub6);
        break;
    case UPIO_MEM:
    case UPIO_MEM32:
    case UPIO_AU:
    case UPIO_TSI:
    case UPIO_DWAPB:
        snprintf(address, sizeof(address),
             "MMIO 0x%llx", (unsigned long long)port->mapbase);
        break;
    default:
        strlcpy(address, "*unknown*", sizeof(address));
        break;
    }
//port->dev指向serial8250_isa_devs,所以dev_name(port->dev) 就是serial8250
//drv->dev_name为ttyS, drv->tty_driver->name_base + port->line值为0
//uart_type如下
    printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n",
           port->dev ? dev_name(port->dev) : "",
           port->dev ? ": " : "",
           drv->dev_name,
           drv->tty_driver->name_base + port->line,
           address, port->irq, uart_type(port));
}

static const char *uart_type(struct uart_port *port)
{
    const char *str = NULL;

    if (port->ops->type)
        str = port->ops->type(port);//调用8250.c中的serial8250_type得到str="16550A"

    if (!str)
        str = "unknown";

    return str;
}

static const char *
serial8250_type(struct uart_port *port)
{
    int type = port->type;

    if (type >= ARRAY_SIZE(uart_config))//uart_config如下
        type = 0;
    return uart_config[type].name;
}
/*
 * Here we define the default xmit fifo size used for each type of UART.
 */
static const struct serial8250_config uart_config[] = {
    [PORT_UNKNOWN] = {
        .name        = "unknown",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_8250] = {
        .name        = "8250",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16450] = {
        .name        = "16450",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16550] = {
        .name        = "16550",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16550A] = {
        .name        = "16550A",
        .fifo_size    = 16,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_CIRRUS] = {
        .name        = "Cirrus",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16650] = {
        .name        = "ST16650",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
        .flags        = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
    },
    [PORT_16650V2] = {
        .name        = "ST16650V2",
        .fifo_size    = 32,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
                  UART_FCR_T_TRIG_00,
        .flags        = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
    },
    [PORT_16750] = {
        .name        = "TI16750",
        .fifo_size    = 64,
        .tx_loadsz    = 64,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
                  UART_FCR7_64BYTE,
        .flags        = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE,
    },
    [PORT_STARTECH] = {
        .name        = "Startech",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16C950] = {
        .name        = "16C950/954",
        .fifo_size    = 128,
        .tx_loadsz    = 128,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_16654] = {
        .name        = "ST16654",
        .fifo_size    = 64,
        .tx_loadsz    = 32,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
                  UART_FCR_T_TRIG_10,
        .flags        = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
    },
    [PORT_16850] = {
        .name        = "XR16850",
        .fifo_size    = 128,
        .tx_loadsz    = 128,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
    },
    [PORT_RSA] = {
        .name        = "RSA",
        .fifo_size    = 2048,
        .tx_loadsz    = 2048,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_NS16550A] = {
        .name        = "NS16550A",
        .fifo_size    = 16,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO | UART_NATSEMI,
    },
    [PORT_XSCALE] = {
        .name        = "XScale",
        .fifo_size    = 32,
        .tx_loadsz    = 32,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO | UART_CAP_UUE,
    },
    [PORT_RM9000] = {
        .name        = "RM9000",
        .fifo_size    = 16,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_OCTEON] = {
        .name        = "OCTEON",
        .fifo_size    = 64,
        .tx_loadsz    = 64,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_AR7] = {
        .name        = "AR7",
        .fifo_size    = 16,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
        .flags        = UART_CAP_FIFO | UART_CAP_AFE,
    },
};

static void uart_change_pm(struct uart_state *state, int pm_state)
{
    struct uart_port *port = state->port;
//判断当前的电源管理状态与新状态是否相同,因为在uart_add_one_port中初始值为-1,现在新值为0,所以调用具体设备的pm函数,这里是serial8250_pm
    if (state->pm_state != pm_state) {
        if (port->ops->pm)
            port->ops->pm(port, pm_state, state->pm_state);
        state->pm_state = pm_state;//保存新的状态
    }

static void
serial8250_pm(struct uart_port *port, unsigned int state,
          unsigned int oldstate)
{
    struct uart_8250_port *p = (struct uart_8250_port *)port;

    serial8250_set_sleep(p, state != 0);

    if (p->pm)//pm为NULL
        p->pm(port, state, oldstate);
}
}

static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
{
    if (p->capabilities & UART_CAP_SLEEP) {//p->capabilities为0
        if (p->capabilities & UART_CAP_EFR) {
            serial_outp(p, UART_LCR, 0xBF);
            serial_outp(p, UART_EFR, UART_EFR_ECB);
            serial_outp(p, UART_LCR, 0);
        }
        serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0);
        if (p->capabilities & UART_CAP_EFR) {
            serial_outp(p, UART_LCR, 0xBF);
            serial_outp(p, UART_EFR, 0);
            serial_outp(p, UART_LCR, 0);
        }
    }
}

static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
    struct uart_8250_port *up = (struct uart_8250_port *)port;
    unsigned char mcr = 0;

    if (mctrl & TIOCM_RTS)
        mcr |= UART_MCR_RTS;
    if (mctrl & TIOCM_DTR)
        mcr |= UART_MCR_DTR;
    if (mctrl & TIOCM_OUT1)
        mcr |= UART_MCR_OUT1;
    if (mctrl & TIOCM_OUT2)
        mcr |= UART_MCR_OUT2;
    if (mctrl & TIOCM_LOOP)
        mcr |= UART_MCR_LOOP;

    mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;

    serial_out(up, UART_MCR, mcr);
}
阅读(501) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册