代码平台:IMX6DL
代码入口:./drivers/tty/serial/imx.c/module_init(imx_serial_init)
static int __init imx_serial_init(void)
{
int ret = uart_register_driver(&imx_reg); //drivers/tty/serial/serial_core.c/line2389
if (ret)
return ret;
ret = platform_driver_register(&serial_imx_driver);
if (ret != 0)
uart_unregister_driver(&imx_reg);
return ret;
}
上述代码主要完成了两件事:
static struct uart_driver imx_reg = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = DEV_NAME,
.major = SERIAL_IMX_MAJOR,
.minor = MINOR_START,
.nr = ARRAY_SIZE(imx_ports),
.cons = IMX_CONSOLE,
};
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal;
int i, retval;
BUG_ON(drv->state);
/*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle.
*/
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if (!drv->state)
goto out;
normal = alloc_tty_driver(drv->nr);
if (!normal)
goto out_kfree;
drv->tty_driver = normal;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
tty_set_operations(normal, &uart_ops); //drivers/tty/tty_io.c/line3436
/*
* Initialise the UART state(s).
*/
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
tty_port_init(port); //drivers/tty/tty_port.c/line20
port->ops = &uart_port_ops;
}
retval = tty_register_driver(normal); //drivers/tty/tty_io.c/line3452
if (retval >= 0)
return retval;
for (i = 0; i < drv->nr; i++)
tty_port_destroy(&drv->state[i].port);
put_tty_driver(normal); //drivers/tty/tty_io.c/line3443
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
}
上述函数一共完成了6件事:
/*
* Called by a tty driver to register itself.
*/
int tty_register_driver(struct tty_driver *driver)
{
int error;
int i;
dev_t dev;
struct device *d;
if (!driver->major) {
error = alloc_chrdev_region(&dev, driver->minor_start,
driver->num, driver->name);
if (!error) {
driver->major = MAJOR(dev);
driver->minor_start = MINOR(dev);
}
} else {
dev = MKDEV(driver->major, driver->minor_start);
error = register_chrdev_region(dev, driver->num, driver->name);
}
if (error < 0)
goto err;
if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) { //not defined
error = tty_cdev_add(driver, dev, 0, driver->num);
if (error)
goto err_unreg_char;
}
mutex_lock(&tty_mutex);
list_add(&driver->tty_drivers, &tty_drivers);
mutex_unlock(&tty_mutex);
if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) { // !defined equals to not defined
for (i = 0; i < driver->num; i++) {
d = tty_register_device(driver, i, NULL);
if (IS_ERR(d)) {
error = PTR_ERR(d);
goto err_unreg_devs;
}
}
}
proc_tty_register_driver(driver);
driver->flags |= TTY_DRIVER_INSTALLED;
return 0;
err_unreg_devs:
for (i--; i >= 0; i--)
tty_unregister_device(driver, i);
mutex_lock(&tty_mutex);
list_del(&driver->tty_drivers);
mutex_unlock(&tty_mutex);
err_unreg_char:
unregister_chrdev_region(dev, driver->num);
err:
return error;
}
上述函数完成了一下几件事:
static struct platform_driver serial_imx_driver = {
.probe = serial_imx_probe,
.remove = serial_imx_remove,
.suspend = serial_imx_suspend,
.resume = serial_imx_resume,
.id_table = imx_uart_devtype,
.driver = {
.name = "imx-uart",
.of_match_table = imx_uart_dt_ids,
},
};
暂不分析具体的注册函数,当 platform 总线上的 device 与 driver 匹配时,调用 serial_imx_probe 函数。
static int serial_imx_probe(struct platform_device *pdev)
{
struct imx_port *sport;
void __iomem *base;
int ret = 0;
struct resource *res;
int txirq, rxirq, rtsirq;
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
if (!sport)
return -ENOMEM;
ret = serial_imx_probe_dt(sport, pdev);
if (ret > 0)
serial_imx_probe_pdata(sport, pdev);
else if (ret < 0)
return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
rxirq = platform_get_irq(pdev, 0);
txirq = platform_get_irq(pdev, 1);
rtsirq = platform_get_irq(pdev, 2);
sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
sport->port.membase = base;
sport->port.type = PORT_IMX,
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
sport->port.ops = &imx_pops;
sport->port.rs485_config = imx_rs485_config;
sport->port.rs485.flags =
SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
sport->port.flags = UPF_BOOT_AUTOCONF;
init_timer(&sport->timer);
sport->timer.function = imx_timeout;
sport->timer.data = (unsigned long)sport;
sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk_ipg)) {
ret = PTR_ERR(sport->clk_ipg);
dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
return ret;
}
sport->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(sport->clk_per)) {
ret = PTR_ERR(sport->clk_per);
dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
return ret;
}
sport->port.uartclk = clk_get_rate(sport->clk_per);
if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) {
ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE);
if (ret < 0) {
dev_err(&pdev->dev, "clk_set_rate() failed\n");
return ret;
}
}
sport->port.uartclk = clk_get_rate(sport->clk_per);
/*
* Allocate the IRQ(s) i.MX1 has three interrupts whereas later
* chips only have one interrupt.
*/
if (txirq > 0) {
ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,
dev_name(&pdev->dev), sport);
if (ret)
return ret;
ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,
dev_name(&pdev->dev), sport);
if (ret)
return ret;
} else {
ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
dev_name(&pdev->dev), sport);
if (ret)
return ret;
}
imx_ports[sport->port.line] = sport;
platform_set_drvdata(pdev, sport);
return uart_add_one_port(&imx_reg, &sport->port); //drivers/tty/serial/serial_core.c/line2657
}
该函数主要完成以下2件事:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state;
struct tty_port *port;
int ret = 0;
struct device *tty_dev;
int num_groups;
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;
}
/* Link the port to the driver state table and vice versa */
state->uart_port = uport;
uport->state = state;
state->pm_state = UART_PM_STATE_UNDEFINED;
uport->cons = drv->cons;
uport->minor = drv->tty_driver->minor_start + uport->line;
/*
* 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);
}
if (uport->cons && uport->dev)
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
uart_configure_port(drv, state, uport);
num_groups = 2;
if (uport->attr_group)
num_groups++;
uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
GFP_KERNEL);
if (!uport->tty_groups) {
ret = -ENOMEM;
goto out;
}
uport->tty_groups[0] = &tty_dev_attr_group;
if (uport->attr_group)
uport->tty_groups[1] = uport->attr_group;
/*
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this port's parameters.
*/
tty_dev = tty_port_register_device_attr(port, drv->tty_driver, //drivers/tty/tty_port.c/line89
uport->line, uport->dev, port, uport->tty_groups);
if (likely(!IS_ERR(tty_dev))) {
device_set_wakeup_capable(tty_dev, 1);
} else {
dev_err(uport->dev, "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;
}
该函数主要完成以下2件事:
struct device *tty_port_register_device(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device)
{
tty_port_link_device(port, driver, index);
return tty_register_device(driver, index, device);
}
该函数主要完成以下2件事:
struct device *tty_register_device_attr(struct tty_driver *driver,
unsigned index, struct device *device,
void *drvdata,
const struct attribute_group **attr_grp)
{
char name[64];
dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
struct device *dev = NULL;
int retval = -ENODEV;
bool cdev = false;
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) //not defined
pty_line_name(driver, index, name);
else
tty_line_name(driver, index, name);
if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { // ! not defined equals to defined
retval = tty_cdev_add(driver, devt, index, 1);
if (retval)
goto error;
cdev = true;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
dev->devt = devt;
dev->class = tty_class;
dev->parent = device;
dev->release = tty_device_create_release;
dev_set_name(dev, "%s", name);
dev->groups = attr_grp;
dev_set_drvdata(dev, drvdata);
retval = device_register(dev); //drivers/base/core.c/line1137
if (retval)
goto error;
return dev;
error:
put_device(dev);
if (cdev)
cdev_del(&driver->cdevs[index]);
return ERR_PTR(retval);
}
该函数完成以下3件事:
kobject name 与 device name 的关系
static inline const char *dev_name(const struct device *dev)
{
/* Use the init name until the kobject becomes available */
if (dev->init_name)
return dev->init_name;
return kobject_name(&dev->kobj);
}