位于 /drivers/serial/mfd.c 中
1. 初始化:
static int __init hsu_pci_init(void)
{
int ret;
hsu_global_init(); //初始化
ret = uart_register_driver(&serial_hsu_reg); //注册UART 驱动
if (ret)
return ret;
return pci_register_driver(&hsu_pci_driver); //注册pci驱动
}
static void __exit hsu_pci_exit(void)
{
pci_unregister_driver(&hsu_pci_driver);
uart_unregister_driver(&serial_hsu_reg);
hsu_debugfs_remove(phsu);
kfree(phsu);
}
module_init(hsu_pci_init);
module_exit(hsu_pci_exit);
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:medfield-hsu");
2. hsu_global_init(), 初始化3个UART 串口。
static void hsu_global_init(void)
{
struct hsu_port *hsu;
struct uart_hsu_port *uport;
struct hsu_dma_chan *dchan;
int i, ret;
hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL);
if (!hsu)
return;
/* Get basic io resource and map it */
hsu->paddr = 0xffa28000;
hsu->iolen = 0x1000;
if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global")))
pr_warning("HSU: error in request mem region\n");
hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen);
if (!hsu->reg) {
pr_err("HSU: error in ioremap\n");
ret = -ENOMEM;
goto err_free_region;
}
/* Initialise the 3 UART ports */
uport = hsu->port;
for (i = 0; i < 3; i++) {
uport->port.type = PORT_MFD;
uport->port.iotype = UPIO_MEM;
uport->port.mapbase = (resource_size_t)hsu->paddr
+ HSU_PORT_REG_OFFSET
+ i * HSU_PORT_REG_LENGTH;
uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET
+ i * HSU_PORT_REG_LENGTH;
sprintf(uport->name, "hsu_port%d", i);
uport->port.fifosize = 64;
uport->port.ops = &serial_hsu_pops;
uport->port.line = i;
uport->port.flags = UPF_IOREMAP;
/* set the scalable maxim support rate to 2746800 bps */
uport->port.uartclk = 115200 * 24 * 16;
uport->running = 0;
uport->txc = &hsu->chans[i * 2];
uport->rxc = &hsu->chans[i * 2 + 1];
serial_hsu_ports[i] = uport;
uport->index = i;
uport++;
}
/* Initialise 6 dma channels */
dchan = hsu->chans;
for (i = 0; i < 6; i++) {
dchan->id = i;
dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
dchan->uport = &hsu->port[i/2];
dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET +
i * HSU_DMA_CHANS_REG_LENGTH;
/* Work around for RX */
if (dmarx_need_timer() && dchan->dirt == DMA_FROM_DEVICE) {
init_timer(&dchan->rx_timer);
dchan->rx_timer.function = hsu_dma_rx_timeout;
dchan->rx_timer.data = (unsigned long)dchan;
}
dchan++;
}
phsu = hsu;
hsu_debugfs_init(hsu);
return;
err_free_region:
release_mem_region(hsu->paddr, hsu->iolen);
kfree(hsu);
return;
}
3. 串口信息配置:
static struct uart_driver serial_hsu_reg = {
.owner = THIS_MODULE,
.driver_name = "MFD serial",
.dev_name = "ttyMFD",
.major = TTY_MAJOR,
.minor = 128,
.nr = 3,
};
4. pci 驱动信息:
static struct pci_driver hsu_pci_driver = {
.name = "HSU serial",
.id_table = pci_ids,
.probe = serial_hsu_probe,
.remove = __devexit_p(serial_hsu_remove),
.suspend = serial_hsu_suspend,
.resume = serial_hsu_resume,
};
5. serial_hsu_probe :
static int serial_hsu_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct uart_hsu_port *uport;
int index, ret;
printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n",
pdev->vendor, pdev->device);
switch (pdev->device) {
case 0x081B:
index = 0;
break;
case 0x081C:
index = 1;
break;
case 0x081D:
index = 2;
break;
case 0x081E:
/* internal DMA controller */
index = 3;
break;
default:
dev_err(&pdev->dev, "HSU: out of index!");
return -ENODEV;
}
ret = pci_enable_device(pdev);
if (ret)
return ret;
if (index == 3) {
/* DMA controller */
ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu);
if (ret) {
dev_err(&pdev->dev, "can not get IRQ\n");
goto err_disable;
}
pci_set_drvdata(pdev, phsu);
} else {
/* UART port 0~2 */
uport = &phsu->port[index];
uport->port.irq = pdev->irq;
uport->port.dev = &pdev->dev;
uport->dev = &pdev->dev;
ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport);
if (ret) {
dev_err(&pdev->dev, "can not get IRQ\n");
goto err_disable;
}
uart_add_one_port(&serial_hsu_reg, &uport->port);
#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
if (index == 1) {
register_console(&serial_hsu_console);
uport->port.cons = &serial_hsu_console;
}
#endif
pci_set_drvdata(pdev, uport);
}
return 0;
err_disable:
pci_disable_device(pdev);
return ret;
}
serial_hsu_suspend() 和 serial_hsu_resume().
#ifdef CONFIG_PM
static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state)
{
void *priv = pci_get_drvdata(pdev);
struct uart_hsu_port *up;
/* Make sure this is not the internal dma controller */
if (priv && (pdev->device != 0x081E)) {
up = priv;
uart_suspend_port(&serial_hsu_reg, &up->port);
}
pci_save_state(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
static int serial_hsu_resume(struct pci_dev *pdev)
{
void *priv = pci_get_drvdata(pdev);
struct uart_hsu_port *up;
int ret;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
ret = pci_enable_device(pdev);
if (ret)
dev_warn(&pdev->dev,
"HSU: can't re-enable device, try to continue\n");
if (priv && (pdev->device != 0x081E)) {
up = priv;
uart_resume_port(&serial_hsu_reg, &up->port);
}
return 0;
}
#else
#define serial_hsu_suspend NULL
#define serial_hsu_resume NULL
#endif
6 . serial_hsu_pops, 实现了 serial hsu 的各种串口操作。
struct uart_ops serial_hsu_pops = {
.tx_empty = serial_hsu_tx_empty,
.set_mctrl = serial_hsu_set_mctrl,
.get_mctrl = serial_hsu_get_mctrl,
.stop_tx = serial_hsu_stop_tx,
.start_tx = serial_hsu_start_tx,
.stop_rx = serial_hsu_stop_rx,
.enable_ms = serial_hsu_enable_ms,
.break_ctl = serial_hsu_break_ctl,
.startup = serial_hsu_startup,
.shutdown = serial_hsu_shutdown,
.set_termios = serial_hsu_set_termios,
.pm = serial_hsu_pm,
.type = serial_hsu_type,
.release_port = serial_hsu_release_port,
.request_port = serial_hsu_request_port,
.config_port = serial_hsu_config_port,
.verify_port = serial_hsu_verify_port,
};
7. serial_hsu_console 结构体, 实现了console功能,注意里面有个 serail_hsu_reg
static struct console serial_hsu_console = {
.name = "ttyMFD",
.write = serial_hsu_console_write,
.device = uart_console_device,
.setup = serial_hsu_console_setup,
.flags = CON_PRINTBUFFER,
.index = 1,
.data = &serial_hsu_reg, //串口 hsu 信息数据
};
#endif
static void serial_hsu_console_putchar(struct uart_port *port, int ch)
{
struct uart_hsu_port *up =
container_of(port, struct uart_hsu_port, port);
wait_for_xmitr(up);
serial_out(up, UART_TX, ch);
}
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
*
* The console_lock must be held when we get here.
*/
static void
serial_hsu_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_hsu_port *up = serial_hsu_ports[co->index];
unsigned long flags;
unsigned int ier;
int locked = 1;
local_irq_save(flags);
if (up->port.sysrq)
locked = 0;
else if (oops_in_progress) {
locked = spin_trylock(&up->port.lock);
} else
spin_lock(&up->port.lock);
/* First save the IER then disable the interrupts */
ier = serial_in(up, UART_IER);
serial_out(up, UART_IER, 0);
uart_console_write(&up->port, s, count, serial_hsu_console_putchar);
/*
* Finally, wait for transmitter to become empty
* and restore the IER
*/
wait_for_xmitr(up);
serial_out(up, UART_IER, ier);
if (locked)
spin_unlock(&up->port.lock);
local_irq_restore(flags);
}
static struct console serial_hsu_console;
static int __init
serial_hsu_console_setup(struct console *co, char *options)
{
struct uart_hsu_port *up;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
int ret;
if (co->index == -1 || co->index >= serial_hsu_reg.nr)
co->index = 0;
up = serial_hsu_ports[co->index];
if (!up)
return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
ret = uart_set_options(&up->port, co, baud, parity, bits, flow);
return ret;
}