/************************************************************
* am335x uart分析
* 本文记录am335x uart驱动的注册过程。
* 参考:
* http://www.cnblogs.com/helloworldtoyou/p/5385595.html
* 涉及文件:
* arch/arm/mach-omap2/board-am335xevm.c
* drivers/tty/serial/omap-serial.c
*
* Tony Liu, 2016-5-2, Shenzhen
***********************************************************/
MACHINE_START(AM335XEVM, "am335xevm")
/* Maintainer: Texas Instruments */
.atag_offset = 0x100,
.map_io = am335x_evm_map_io,
.init_early = am33xx_init_early,
.init_irq = ti81xx_init_irq,
.handle_irq = omap3_intc_handle_irq,
.timer = &omap3_am33xx_timer,
.init_machine = am335x_evm_init, ---+
MACHINE_END |
|
static void __init am335x_evm_init(void) <--+
{
am33xx_cpuidle_init();
am33xx_mux_init(board_mux);
omap_serial_init();
am335x_evm_i2c_init();
omap_sdrc_init(NULL, NULL);
usb_musb_init(&musb_board_data);
omap_board_config = am335x_evm_config;
omap_board_config_size = ARRAY_SIZE(am335x_evm_config);
daughter_brd_detected = false;
setup_xxx_xxxx(); --+
...... |
} |
|
V
static void setup_xxx_xxxx(void)
{
/*which doesn't have Write Protect pin LAN8710A_PHY_ID */
am335x_mmc[].gpio_wp = -EINVAL;
int ret;
//配置引脚复用
_configure_device(EVM_SK, xxx_xxxx_dev_cfg, PROFILE_NONE); -----+
| |
} | |
V |
static void _configure_device(int evm_id, struct evm_dev_cfg *dev_cfg, |
int profile) |
{ |
int i; |
|
am335x_evm_set_id(evm_id); |
|
if (profile == PROFILE_NONE) { |
for (i = ; dev_cfg->device_init != NULL; dev_cfg++) { |
if (dev_cfg->device_on == DEV_ON_BASEBOARD) |
dev_cfg->device_init(evm_id, profile); |
else if (daughter_brd_detected == true) |
dev_cfg->device_init(evm_id, profile); |
} |
} else { |
for (i = ; dev_cfg->device_init != NULL; dev_cfg++) { |
if (dev_cfg->profile & profile) { |
if (dev_cfg->device_on == DEV_ON_BASEBOARD) |
dev_cfg->device_init(evm_id, profile); |
else if (daughter_brd_detected == true) |
dev_cfg->device_init(evm_id, profile); |
} |
} |
} |
} |
|
static struct evm_dev_cfg xxx_xxxx_dev_cfg[] = { <--------+
......
{uart0_init, DEV_ON_BASEBOARD, PROFILE_ALL},//fixed ---+
{uart1_init, DEV_ON_BASEBOARD, PROFILE_ALL},//fixed |
{uart4_init, DEV_ON_BASEBOARD, PROFILE_ALL},//fixed |
...... |
{NULL, , }, |
}; |
|
static void uart0_init(int evm_id, int profile) <---+
{
setup_pin_mux(uart0_pin_mux); ------+
return; | |
} | |
V |
static void setup_pin_mux(struct pinmux_config *pin_mux) |
{ |
int i; |
|
for (i = ; pin_mux->string_name != NULL; pin_mux++) |
omap_mux_init_signal(pin_mux->string_name, pin_mux->val); |
} |
|
static struct pinmux_config uart0_pin_mux[] = { <-----+
{"uart0_rxd.uart0_rxd", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"uart0_txd.uart0_txd", OMAP_MUX_MODE0 | AM33XX_PULL_ENBL},
{"uart0_ctsn.uart0_ctsn", OMAP_MUX_MODE0 | AM33XX_PULL_ENBL},
{NULL, },
};
drivers/tty/serial/omap-serial.c
static int __init serial_omap_init(void)
{
int ret;
ret = uart_register_driver(&serial_omap_reg); -------+
if (ret != ) |
return ret; |
ret = platform_driver_register(&serial_omap_driver); --+ |
if (ret != ) | |
uart_unregister_driver(&serial_omap_reg); | |
return ret; | |
} | |
| |
static struct uart_driver serial_omap_reg = { | |
.owner = THIS_MODULE, | |
.driver_name = "OMAP-SERIAL", | |
.dev_name = OMAP_SERIAL_NAME, | |
.nr = OMAP_MAX_HSUART_PORTS, | |
.cons = OMAP_CONSOLE, | |
}; | |
| |
#define OMAP_SERIAL_NAME "ttyO" | |
#define OMAP_MAX_HSUART_PORTS 6 | |
| |
#define OMAP_CONSOLE (&serial_omap_console) | |
| |
static struct console serial_omap_console = { | <------+
.name = OMAP_SERIAL_NAME, |
.write = serial_omap_console_write, |
.device = uart_console_device, |
.setup = serial_omap_console_setup, |
.flags = CON_PRINTBUFFER, |
.index = -, |
.data = &serial_omap_reg, |
}; |
|
static struct platform_driver serial_omap_driver = { <--+
.probe = serial_omap_probe,
.remove = serial_omap_remove,
.driver = {
.name = DRIVER_NAME,
.pm = &serial_omap_dev_pm_ops,
.of_match_table = of_match_ptr(omap_serial_of_match),
},
};
static int serial_omap_probe(struct platform_device *pdev)
{
struct uart_omap_port *up;
struct resource *mem, *irq, *dma_tx, *dma_rx;
struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
int ret = -ENOSPC;
if (pdev->dev.of_node)
omap_up_info = of_get_uart_port_info(&pdev->dev);
......
up->pdev = pdev;
up->port.dev = &pdev->dev;
up->port.type = PORT_OMAP;
up->port.iotype = UPIO_MEM;
up->port.irq = irq->start;
up->port.regshift = ;
up->port.fifosize = ;
up->port.ops = &serial_omap_pops; --------+
|
if (pdev->dev.of_node) |
up->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); |
else |
up->port.line = pdev->id; |
|
if (up->port.line < ) { |
dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", |
up->port.line); |
ret = -ENODEV; |
goto err; |
} |
|
sprintf(up->name, "OMAP UART%d", up->port.line); |
up->port.mapbase = mem->start; |
up->port.membase = ioremap(mem->start, resource_size(mem)); |
if (!up->port.membase) { |
dev_err(&pdev->dev, "can't ioremap UART\n"); |
ret = -ENOMEM; |
goto err; |
} |
|
up->port.flags = omap_up_info->flags; |
up->port.uartclk = omap_up_info->uartclk; |
if (!up->port.uartclk) { |
up->port.uartclk = DEFAULT_CLK_SPEED; |
dev_warn(&pdev->dev, "No clock speed specified: using default:" |
"%d\n", DEFAULT_CLK_SPEED); |
} |
up->uart_dma.uart_base = mem->start; |
up->errata = omap_up_info->errata; |
|
if (omap_up_info->dma_enabled) { |
up->uart_dma.uart_dma_tx = dma_tx->start; |
up->uart_dma.uart_dma_rx = dma_rx->start; |
up->use_dma = ; |
up->uart_dma.rx_buf_size = omap_up_info->dma_rx_buf_size; |
up->uart_dma.rx_timeout = omap_up_info->dma_rx_timeout; |
up->uart_dma.rx_poll_rate = omap_up_info->dma_rx_poll_rate; |
spin_lock_init(&(up->uart_dma.tx_lock)); |
spin_lock_init(&(up->uart_dma.rx_lock)); |
up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; |
up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; |
} |
|
up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; |
up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; |
pm_qos_add_request(&up->pm_qos_request, |
PM_QOS_CPU_DMA_LATENCY, up->latency); |
serial_omap_uart_wq = create_singlethread_workqueue(up->name); |
INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); |
|
pm_runtime_use_autosuspend(&pdev->dev); |
pm_runtime_set_autosuspend_delay(&pdev->dev, |
omap_up_info->autosuspend_timeout); |
|
pm_runtime_irq_safe(&pdev->dev); |
pm_runtime_enable(&pdev->dev); |
pm_runtime_get_sync(&pdev->dev); |
|
ui[up->port.line] = up; |
serial_omap_add_console_port(up); |
|
ret = uart_add_one_port(&serial_omap_reg, &up->port); --------------+ |
if (ret != ) | |
goto do_release_region; | |
| |
pm_runtime_put(&pdev->dev); | |
platform_set_drvdata(pdev, up); | |
return ; | |
err: | |
dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", | |
pdev->id, __func__, ret); | |
do_release_region: | |
release_mem_region(mem->start, resource_size(mem)); | |
return ret; | |
} | |
| |
static struct uart_ops serial_omap_pops = { <--------|--+
.tx_empty = serial_omap_tx_empty, |
.set_mctrl = serial_omap_set_mctrl, |
.get_mctrl = serial_omap_get_mctrl, |
.stop_tx = serial_omap_stop_tx, |
.start_tx = serial_omap_start_tx, |
.stop_rx = serial_omap_stop_rx, |
.enable_ms = serial_omap_enable_ms, |
.break_ctl = serial_omap_break_ctl, |
.startup = serial_omap_startup, |
.shutdown = serial_omap_shutdown, |
.set_termios = serial_omap_set_termios, |
.pm = serial_omap_pm, |
.type = serial_omap_type, |
.release_port = serial_omap_release_port, |
.request_port = serial_omap_request_port, |
.config_port = serial_omap_config_port, |
.verify_port = serial_omap_verify_port, |
#ifdef CONFIG_CONSOLE_POLL |
.poll_put_char = serial_omap_poll_put_char, |
.poll_get_char = serial_omap_poll_get_char, |
#endif |
}; |
|
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) <-+
{
struct uart_state *state;
struct tty_port *port;
int ret = ;
struct device *tty_dev;
BUG_ON(in_interrupt());
if (uport->line >= drv->nr)
return -EINVAL;
state = drv->state + uport->line;
port = &state->port;
mutex_lock(&port_mutex);
mutex_lock(&port->mutex);
if (state->uart_port) {
ret = -EINVAL;
goto out;
}
state->uart_port = uport;
state->pm_state = -;
uport->cons = drv->cons;
uport->state = state;
/*
* If this port is a console, then the spinlock is already
* initialised.
*/
if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
spin_lock_init(&uport->lock);
lockdep_set_class(&uport->lock, &port_lock_key);
}
uart_configure_port(drv, state, uport);
/*
* 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, uport->line, uport->dev); -----+
if (likely(!IS_ERR(tty_dev))) { |
device_init_wakeup(tty_dev, ); |
device_set_wakeup_enable(tty_dev, ); |
} else |
printk(KERN_ERR "Cannot register tty device on line %d\n", |
uport->line); |
|
/* |
* Ensure UPF_DEAD is not set. |
*/ |
uport->flags &= ~UPF_DEAD; |
|
out: |
mutex_unlock(&port->mutex); |
mutex_unlock(&port_mutex); |
|
return ret; |
} |
|
struct device *tty_register_device(struct tty_driver *driver, unsigned index, <---+
struct device *device)
{
char name[];
dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
if (index >= driver->num) {
printk(KERN_ERR "Attempt to register invalid tty line number "
" (%d).\n", index);
return ERR_PTR(-EINVAL);
}
if (driver->type == TTY_DRIVER_TYPE_PTY)
pty_line_name(driver, index, name);
else
tty_line_name(driver, index, name); ---+
|
return device_create(tty_class, device, dev, NULL, name); |
} |
// 设备名 |
static void tty_line_name(struct tty_driver *driver, int index, char *p) <--+
{
sprintf(p, "%s%d", driver->name, index + driver->name_base);
}