X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Ftty%2Fserial%2Fclps711x.c;h=d37460965ba7bf800f2e00d29a4e4d94d7976cd4;hb=cf03a884b9f4a63d4bcf29614fe03ca3f8299138;hp=d0f719fafc841f07022adb734b0d2187360e6d05;hpb=1347a2cebcb4cd6ca94eda0ebc8c5c6825bc4544;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index d0f719fafc84..d37460965ba7 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -37,44 +37,53 @@ #include #include #include +#include +#include #include #include -#define UART_NR 2 +#define UART_CLPS711X_NAME "uart-clps711x" +#define UART_CLPS711X_NR 2 +#define UART_CLPS711X_MAJOR 204 +#define UART_CLPS711X_MINOR 40 -#define SERIAL_CLPS711X_MAJOR 204 -#define SERIAL_CLPS711X_MINOR 40 -#define SERIAL_CLPS711X_NR UART_NR - -/* - * We use the relevant SYSCON register as a base address for these ports. - */ -#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1) -#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1) -#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1) -#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1) - -#define TX_IRQ(port) ((port)->irq) -#define RX_IRQ(port) ((port)->irq + 1) +#define UBRLCR(port) ((port)->line ? UBRLCR2 : UBRLCR1) +#define UARTDR(port) ((port)->line ? UARTDR2 : UARTDR1) +#define SYSFLG(port) ((port)->line ? SYSFLG2 : SYSFLG1) +#define SYSCON(port) ((port)->line ? SYSCON2 : SYSCON1) +#define TX_IRQ(port) ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1) +#define RX_IRQ(port) ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1) #define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) -#define tx_enabled(port) ((port)->unused[0]) +struct clps711x_port { + struct uart_driver uart; + struct clk *uart_clk; + struct uart_port port[UART_CLPS711X_NR]; + int tx_enabled[UART_CLPS711X_NR]; +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + struct console console; +#endif +}; static void clps711xuart_stop_tx(struct uart_port *port) { - if (tx_enabled(port)) { + struct clps711x_port *s = dev_get_drvdata(port->dev); + + if (s->tx_enabled[port->line]) { disable_irq(TX_IRQ(port)); - tx_enabled(port) = 0; + s->tx_enabled[port->line] = 0; } } static void clps711xuart_start_tx(struct uart_port *port) { - if (!tx_enabled(port)) { + struct clps711x_port *s = dev_get_drvdata(port->dev); + + if (!s->tx_enabled[port->line]) { enable_irq(TX_IRQ(port)); - tx_enabled(port) = 1; + s->tx_enabled[port->line] = 1; } } @@ -144,8 +153,8 @@ static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) { struct uart_port *port = dev_id; + struct clps711x_port *s = dev_get_drvdata(port->dev); struct circ_buf *xmit = &port->state->xmit; - int count; if (port->x_char) { clps_writel(port->x_char, UARTDR(port)); @@ -154,27 +163,23 @@ static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) return IRQ_HANDLED; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - goto disable_tx_irq; + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + disable_irq_nosync(TX_IRQ(port)); + s->tx_enabled[port->line] = 0; + return IRQ_HANDLED; + } - count = port->fifosize >> 1; - do { - clps_writel(xmit->buf[xmit->tail], UARTDR(port)); + while (!uart_circ_empty(xmit)) { + clps_writew(xmit->buf[xmit->tail], UARTDR(port)); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; - if (uart_circ_empty(xmit)) + if (clps_readl(SYSFLG(port) & SYSFLG_UTXFF)) break; - } while (--count > 0); + } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { - disable_tx_irq: - disable_irq_nosync(TX_IRQ(port)); - tx_enabled(port) = 0; - } - return IRQ_HANDLED; } @@ -226,10 +231,11 @@ static void clps711xuart_break_ctl(struct uart_port *port, int break_state) static int clps711xuart_startup(struct uart_port *port) { + struct clps711x_port *s = dev_get_drvdata(port->dev); unsigned int syscon; int retval; - tx_enabled(port) = 1; + s->tx_enabled[port->line] = 1; /* * Allocate the IRQs @@ -293,10 +299,9 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, */ termios->c_cflag |= CREAD; - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + /* Ask the core to calculate the divisor for us */ + baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096, + port->uartclk / 16); quot = uart_get_divisor(port, baud); switch (termios->c_cflag & CSIZE) { @@ -320,8 +325,9 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, if (!(termios->c_cflag & PARODD)) ubrlcr |= UBRLCR_EVENPRT; } - if (port->fifosize > 1) - ubrlcr |= UBRLCR_FIFOEN; + + /* Enable FIFO */ + ubrlcr |= UBRLCR_FIFOEN; spin_lock_irqsave(&port->lock, flags); @@ -379,7 +385,7 @@ static int clps711xuart_request_port(struct uart_port *port) return 0; } -static struct uart_ops clps711x_pops = { +static struct uart_ops uart_clps711x_ops = { .tx_empty = clps711xuart_tx_empty, .set_mctrl = clps711xuart_set_mctrl_null, .get_mctrl = clps711xuart_get_mctrl, @@ -397,72 +403,39 @@ static struct uart_ops clps711x_pops = { .request_port = clps711xuart_request_port, }; -static struct uart_port clps711x_ports[UART_NR] = { - { - .iobase = SYSCON1, - .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */ - .uartclk = 3686400, - .fifosize = 16, - .ops = &clps711x_pops, - .line = 0, - .flags = UPF_BOOT_AUTOCONF, - }, - { - .iobase = SYSCON2, - .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */ - .uartclk = 3686400, - .fifosize = 16, - .ops = &clps711x_pops, - .line = 1, - .flags = UPF_BOOT_AUTOCONF, - } -}; - #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE -static void clps711xuart_console_putchar(struct uart_port *port, int ch) +static void uart_clps711x_console_putchar(struct uart_port *port, int ch) { while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF) barrier(); - clps_writel(ch, UARTDR(port)); + + clps_writew(ch, UARTDR(port)); } -/* - * 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. - * - * Note that this is called with interrupts already disabled - */ -static void -clps711xuart_console_write(struct console *co, const char *s, - unsigned int count) +static void uart_clps711x_console_write(struct console *co, const char *c, + unsigned n) { - struct uart_port *port = clps711x_ports + co->index; - unsigned int status, syscon; + struct clps711x_port *s = (struct clps711x_port *)co->data; + struct uart_port *port = &s->port[co->index]; + u32 syscon; - /* - * Ensure that the port is enabled. - */ + /* Ensure that the port is enabled */ syscon = clps_readl(SYSCON(port)); clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); - uart_console_write(port, s, count, clps711xuart_console_putchar); + uart_console_write(port, c, n, uart_clps711x_console_putchar); - /* - * Finally, wait for transmitter to become empty - * and restore the uart state. - */ - do { - status = clps_readl(SYSFLG(port)); - } while (status & SYSFLG_UBUSY); + /* Wait for transmitter to become empty */ + while (clps_readl(SYSFLG(port)) & SYSFLG_UBUSY) + barrier(); + /* Restore the uart state */ clps_writel(syscon, SYSCON(port)); } -static void __init -clps711xuart_console_get_options(struct uart_port *port, int *baud, - int *parity, int *bits) +static void uart_clps711x_console_get_options(struct uart_port *port, + int *baud, int *parity, + int *bits) { if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { unsigned int ubrlcr, quot; @@ -487,92 +460,124 @@ clps711xuart_console_get_options(struct uart_port *port, int *baud, } } -static int __init clps711xuart_console_setup(struct console *co, char *options) +static int uart_clps711x_console_setup(struct console *co, char *options) { - struct uart_port *port; - int baud = 38400; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - port = uart_get_console(clps711x_ports, UART_NR, co); + int baud = 38400, bits = 8, parity = 'n', flow = 'n'; + struct clps711x_port *s = (struct clps711x_port *)co->data; + struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0]; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - clps711xuart_console_get_options(port, &baud, &parity, &bits); + uart_clps711x_console_get_options(port, &baud, &parity, &bits); return uart_set_options(port, co, baud, parity, bits, flow); } +#endif -static struct uart_driver clps711x_reg; -static struct console clps711x_console = { - .name = "ttyCL", - .write = clps711xuart_console_write, - .device = uart_console_device, - .setup = clps711xuart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &clps711x_reg, -}; - -static int __init clps711xuart_console_init(void) +static int __devinit uart_clps711x_probe(struct platform_device *pdev) { - register_console(&clps711x_console); - return 0; -} -console_initcall(clps711xuart_console_init); + struct clps711x_port *s; + int ret, i; + + s = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_port), GFP_KERNEL); + if (!s) { + dev_err(&pdev->dev, "Error allocating port structure\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, s); + + s->uart_clk = devm_clk_get(&pdev->dev, "uart"); + if (IS_ERR(s->uart_clk)) { + dev_err(&pdev->dev, "Can't get UART clocks\n"); + ret = PTR_ERR(s->uart_clk); + goto err_out; + } -#define CLPS711X_CONSOLE &clps711x_console -#else -#define CLPS711X_CONSOLE NULL + s->uart.owner = THIS_MODULE; + s->uart.dev_name = "ttyCL"; + s->uart.major = UART_CLPS711X_MAJOR; + s->uart.minor = UART_CLPS711X_MINOR; + s->uart.nr = UART_CLPS711X_NR; +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + s->uart.cons = &s->console; + s->uart.cons->device = uart_console_device; + s->uart.cons->write = uart_clps711x_console_write; + s->uart.cons->setup = uart_clps711x_console_setup; + s->uart.cons->flags = CON_PRINTBUFFER; + s->uart.cons->index = -1; + s->uart.cons->data = s; + strcpy(s->uart.cons->name, "ttyCL"); #endif + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(&pdev->dev, "Registering UART driver failed\n"); + devm_clk_put(&pdev->dev, s->uart_clk); + goto err_out; + } -static struct uart_driver clps711x_reg = { - .driver_name = "ttyCL", - .dev_name = "ttyCL", - .major = SERIAL_CLPS711X_MAJOR, - .minor = SERIAL_CLPS711X_MINOR, - .nr = UART_NR, + for (i = 0; i < UART_CLPS711X_NR; i++) { + s->port[i].line = i; + s->port[i].dev = &pdev->dev; + s->port[i].irq = TX_IRQ(&s->port[i]); + s->port[i].iobase = SYSCON(&s->port[i]); + s->port[i].type = PORT_CLPS711X; + s->port[i].fifosize = 16; + s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; + s->port[i].uartclk = clk_get_rate(s->uart_clk); + s->port[i].ops = &uart_clps711x_ops; + WARN_ON(uart_add_one_port(&s->uart, &s->port[i])); + } - .cons = CLPS711X_CONSOLE, -}; + return 0; -static int __init clps711xuart_init(void) -{ - int ret, i; +err_out: + platform_set_drvdata(pdev, NULL); - printk(KERN_INFO "Serial: CLPS711x driver\n"); + return ret; +} + +static int __devexit uart_clps711x_remove(struct platform_device *pdev) +{ + struct clps711x_port *s = platform_get_drvdata(pdev); + int i; - ret = uart_register_driver(&clps711x_reg); - if (ret) - return ret; + for (i = 0; i < UART_CLPS711X_NR; i++) + uart_remove_one_port(&s->uart, &s->port[i]); - for (i = 0; i < UART_NR; i++) - uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); + devm_clk_put(&pdev->dev, s->uart_clk); + uart_unregister_driver(&s->uart); + platform_set_drvdata(pdev, NULL); return 0; } -static void __exit clps711xuart_exit(void) -{ - int i; +static struct platform_driver clps711x_uart_driver = { + .driver = { + .name = UART_CLPS711X_NAME, + .owner = THIS_MODULE, + }, + .probe = uart_clps711x_probe, + .remove = __devexit_p(uart_clps711x_remove), +}; +module_platform_driver(clps711x_uart_driver); - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); +static struct platform_device clps711x_uart_device = { + .name = UART_CLPS711X_NAME, +}; - uart_unregister_driver(&clps711x_reg); +static int __init uart_clps711x_init(void) +{ + return platform_device_register(&clps711x_uart_device); } +module_init(uart_clps711x_init); -module_init(clps711xuart_init); -module_exit(clps711xuart_exit); +static void __exit uart_clps711x_exit(void) +{ + platform_device_unregister(&clps711x_uart_device); +} +module_exit(uart_clps711x_exit); MODULE_AUTHOR("Deep Blue Solutions Ltd"); -MODULE_DESCRIPTION("CLPS-711x generic serial driver"); +MODULE_DESCRIPTION("CLPS711X serial driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);