diff options
Diffstat (limited to 'drivers/serial/bfin_5xx.c')
-rw-r--r-- | drivers/serial/bfin_5xx.c | 651 |
1 files changed, 396 insertions, 255 deletions
diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index a9eff2b18ea..e381b895b04 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -1,7 +1,7 @@ /* * Blackfin On-Chip Serial Driver * - * Copyright 2006-2008 Analog Devices Inc. + * Copyright 2006-2010 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -12,6 +12,9 @@ #define SUPPORT_SYSRQ #endif +#define DRIVER_NAME "bfin-uart" +#define pr_fmt(fmt) DRIVER_NAME ": " fmt + #include <linux/module.h> #include <linux/ioport.h> #include <linux/gfp.h> @@ -23,22 +26,20 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial_core.h> - -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) +#include <linux/gpio.h> +#include <linux/irq.h> #include <linux/kgdb.h> -#include <asm/irq_regs.h> -#endif - -#include <asm/gpio.h> -#include <mach/bfin_serial_5xx.h> - -#ifdef CONFIG_SERIAL_BFIN_DMA +#include <linux/slab.h> #include <linux/dma-mapping.h> -#include <asm/io.h> -#include <asm/irq.h> + +#include <asm/portmux.h> #include <asm/cacheflush.h> -#endif +#include <asm/dma.h> + +#define port_membase(uart) (((struct bfin_serial_port *)(uart))->port.membase) +#define get_lsr_cache(uart) (((struct bfin_serial_port *)(uart))->lsr) +#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v)) +#include <asm/bfin_serial.h> #ifdef CONFIG_SERIAL_BFIN_MODULE # undef CONFIG_EARLY_PRINTK @@ -49,12 +50,11 @@ #endif /* UART name and device definitions */ -#define BFIN_SERIAL_NAME "ttyBF" +#define BFIN_SERIAL_DEV_NAME "ttyBF" #define BFIN_SERIAL_MAJOR 204 #define BFIN_SERIAL_MINOR 64 -static struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; -static int nr_active_ports = ARRAY_SIZE(bfin_serial_resource); +static struct bfin_serial_port *bfin_serial_ports[BFIN_UART_NR_PORTS]; #if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) @@ -360,7 +360,6 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); uart->port.icount.tx++; - SSYNC(); } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -688,6 +687,13 @@ static int bfin_serial_startup(struct uart_port *port) # ifdef CONFIG_BF54x { + /* + * UART2 and UART3 on BF548 share interrupt PINs and DMA + * controllers with SPORT2 and SPORT3. UART rx and tx + * interrupts are generated in PIO mode only when configure + * their peripheral mapping registers properly, which means + * request corresponding DMA channels in PIO mode as well. + */ unsigned uart_dma_ch_rx, uart_dma_ch_tx; switch (uart->port.irq) { @@ -734,21 +740,19 @@ static int bfin_serial_startup(struct uart_port *port) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED, "BFIN_UART_CTS", uart)) { uart->cts_pin = -1; - pr_info("Unable to attach BlackFin UART CTS interrupt.\ - So, disable it.\n"); + pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n"); } } if (uart->rts_pin >= 0) { - gpio_request(uart->rts_pin, DRIVER_NAME); gpio_direction_output(uart->rts_pin, 0); } #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (request_irq(uart->status_irq, + if (uart->cts_pin >= 0 && request_irq(uart->status_irq, bfin_serial_mctrl_cts_int, IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) { - pr_info("Unable to attach BlackFin UART Modem \ - Status interrupt.\n"); + uart->cts_pin = -1; + pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n"); } /* CTS RTS PINs are negative assertive. */ @@ -793,11 +797,9 @@ static void bfin_serial_shutdown(struct uart_port *port) #ifdef CONFIG_SERIAL_BFIN_CTSRTS if (uart->cts_pin >= 0) free_irq(gpio_to_irq(uart->cts_pin), uart); - if (uart->rts_pin >= 0) - gpio_free(uart->rts_pin); #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (UART_GET_IER(uart) & EDSSI) + if (uart->cts_pin >= 0) free_irq(uart->status_irq, uart); #endif } @@ -846,6 +848,8 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_cflag & CMSPAR) lcr |= STP; + spin_lock_irqsave(&uart->port.lock, flags); + port->read_status_mask = OE; if (termios->c_iflag & INPCK) port->read_status_mask |= (FE | PE); @@ -875,8 +879,6 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_line != N_IRDA) quot -= ANOMALY_05000230; - spin_lock_irqsave(&uart->port.lock, flags); - UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15); /* Disable UART */ @@ -959,33 +961,33 @@ bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser) */ static void bfin_serial_set_ldisc(struct uart_port *port, int ld) { - int line = port->line; + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; unsigned short val; switch (ld) { case N_IRDA: - val = UART_GET_GCTL(&bfin_serial_ports[line]); + val = UART_GET_GCTL(uart); val |= (IREN | RPOLC); - UART_PUT_GCTL(&bfin_serial_ports[line], val); + UART_PUT_GCTL(uart, val); break; default: - val = UART_GET_GCTL(&bfin_serial_ports[line]); + val = UART_GET_GCTL(uart); val &= ~(IREN | RPOLC); - UART_PUT_GCTL(&bfin_serial_ports[line], val); + UART_PUT_GCTL(uart, val); } } static void bfin_serial_reset_irda(struct uart_port *port) { - int line = port->line; + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; unsigned short val; - val = UART_GET_GCTL(&bfin_serial_ports[line]); + val = UART_GET_GCTL(uart); val &= ~(IREN | RPOLC); - UART_PUT_GCTL(&bfin_serial_ports[line], val); + UART_PUT_GCTL(uart, val); SSYNC(); val |= (IREN | RPOLC); - UART_PUT_GCTL(&bfin_serial_ports[line], val); + UART_PUT_GCTL(uart, val); SSYNC(); } @@ -1067,85 +1069,6 @@ static struct uart_ops bfin_serial_pops = { #endif }; -static void __init bfin_serial_hw_init(void) -{ -#ifdef CONFIG_SERIAL_BFIN_UART0 - peripheral_request(P_UART0_TX, DRIVER_NAME); - peripheral_request(P_UART0_RX, DRIVER_NAME); -#endif - -#ifdef CONFIG_SERIAL_BFIN_UART1 - peripheral_request(P_UART1_TX, DRIVER_NAME); - peripheral_request(P_UART1_RX, DRIVER_NAME); - -# if defined(CONFIG_BFIN_UART1_CTSRTS) && defined(CONFIG_BF54x) - peripheral_request(P_UART1_RTS, DRIVER_NAME); - peripheral_request(P_UART1_CTS, DRIVER_NAME); -# endif -#endif - -#ifdef CONFIG_SERIAL_BFIN_UART2 - peripheral_request(P_UART2_TX, DRIVER_NAME); - peripheral_request(P_UART2_RX, DRIVER_NAME); -#endif - -#ifdef CONFIG_SERIAL_BFIN_UART3 - peripheral_request(P_UART3_TX, DRIVER_NAME); - peripheral_request(P_UART3_RX, DRIVER_NAME); - -# if defined(CONFIG_BFIN_UART3_CTSRTS) && defined(CONFIG_BF54x) - peripheral_request(P_UART3_RTS, DRIVER_NAME); - peripheral_request(P_UART3_CTS, DRIVER_NAME); -# endif -#endif -} - -static void __init bfin_serial_init_ports(void) -{ - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - bfin_serial_hw_init(); - - for (i = 0; i < nr_active_ports; i++) { - spin_lock_init(&bfin_serial_ports[i].port.lock); - bfin_serial_ports[i].port.uartclk = get_sclk(); - bfin_serial_ports[i].port.fifosize = BFIN_UART_TX_FIFO_SIZE; - bfin_serial_ports[i].port.ops = &bfin_serial_pops; - bfin_serial_ports[i].port.line = i; - bfin_serial_ports[i].port.iotype = UPIO_MEM; - bfin_serial_ports[i].port.membase = - (void __iomem *)bfin_serial_resource[i].uart_base_addr; - bfin_serial_ports[i].port.mapbase = - bfin_serial_resource[i].uart_base_addr; - bfin_serial_ports[i].port.irq = - bfin_serial_resource[i].uart_irq; - bfin_serial_ports[i].status_irq = - bfin_serial_resource[i].uart_status_irq; - bfin_serial_ports[i].port.flags = UPF_BOOT_AUTOCONF; -#ifdef CONFIG_SERIAL_BFIN_DMA - bfin_serial_ports[i].tx_done = 1; - bfin_serial_ports[i].tx_count = 0; - bfin_serial_ports[i].tx_dma_channel = - bfin_serial_resource[i].uart_tx_dma_channel; - bfin_serial_ports[i].rx_dma_channel = - bfin_serial_resource[i].uart_rx_dma_channel; - init_timer(&(bfin_serial_ports[i].rx_dma_timer)); -#endif -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) - bfin_serial_ports[i].cts_pin = - bfin_serial_resource[i].uart_cts_pin; - bfin_serial_ports[i].rts_pin = - bfin_serial_resource[i].uart_rts_pin; -#endif - } -} - #if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK) /* * If the port was already initialised (eg, by a boot loader), @@ -1193,6 +1116,34 @@ bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, static struct uart_driver bfin_serial_reg; +static void bfin_serial_console_putchar(struct uart_port *port, int ch) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + while (!(UART_GET_LSR(uart) & THRE)) + barrier(); + UART_PUT_CHAR(uart, ch); +} + +#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || + defined (CONFIG_EARLY_PRINTK) */ + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE +#define CLASS_BFIN_CONSOLE "bfin-console" +/* + * Interrupts are disabled on entering + */ +static void +bfin_serial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct bfin_serial_port *uart = bfin_serial_ports[co->index]; + unsigned long flags; + + spin_lock_irqsave(&uart->port.lock, flags); + uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); + spin_unlock_irqrestore(&uart->port.lock, flags); + +} + static int __init bfin_serial_console_setup(struct console *co, char *options) { @@ -1212,9 +1163,12 @@ bfin_serial_console_setup(struct console *co, char *options) * if so, search for the first available port that does have * console support. */ - if (co->index == -1 || co->index >= nr_active_ports) - co->index = 0; - uart = &bfin_serial_ports[co->index]; + if (co->index < 0 || co->index >= BFIN_UART_NR_PORTS) + return -ENODEV; + + uart = bfin_serial_ports[co->index]; + if (!uart) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -1223,36 +1177,9 @@ bfin_serial_console_setup(struct console *co, char *options) return uart_set_options(&uart->port, co, baud, parity, bits, flow); } -#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || - defined (CONFIG_EARLY_PRINTK) */ - -#ifdef CONFIG_SERIAL_BFIN_CONSOLE -static void bfin_serial_console_putchar(struct uart_port *port, int ch) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - while (!(UART_GET_LSR(uart) & THRE)) - barrier(); - UART_PUT_CHAR(uart, ch); - SSYNC(); -} - -/* - * Interrupts are disabled on entering - */ -static void -bfin_serial_console_write(struct console *co, const char *s, unsigned int count) -{ - struct bfin_serial_port *uart = &bfin_serial_ports[co->index]; - unsigned long flags; - - spin_lock_irqsave(&uart->port.lock, flags); - uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); - spin_unlock_irqrestore(&uart->port.lock, flags); - -} static struct console bfin_serial_console = { - .name = BFIN_SERIAL_NAME, + .name = BFIN_SERIAL_DEV_NAME, .write = bfin_serial_console_write, .device = uart_console_device, .setup = bfin_serial_console_setup, @@ -1260,44 +1187,30 @@ static struct console bfin_serial_console = { .index = -1, .data = &bfin_serial_reg, }; - -static int __init bfin_serial_rs_console_init(void) -{ - bfin_serial_init_ports(); - register_console(&bfin_serial_console); - - return 0; -} -console_initcall(bfin_serial_rs_console_init); - #define BFIN_SERIAL_CONSOLE &bfin_serial_console #else #define BFIN_SERIAL_CONSOLE NULL #endif /* CONFIG_SERIAL_BFIN_CONSOLE */ +#ifdef CONFIG_EARLY_PRINTK +static struct bfin_serial_port bfin_earlyprintk_port; +#define CLASS_BFIN_EARLYPRINTK "bfin-earlyprintk" -#ifdef CONFIG_EARLY_PRINTK -static __init void early_serial_putc(struct uart_port *port, int ch) +/* + * Interrupts are disabled on entering + */ +static void +bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int count) { - unsigned timeout = 0xffff; - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - while ((!(UART_GET_LSR(uart) & THRE)) && --timeout) - cpu_relax(); - UART_PUT_CHAR(uart, ch); -} + unsigned long flags; -static __init void early_serial_write(struct console *con, const char *s, - unsigned int n) -{ - struct bfin_serial_port *uart = &bfin_serial_ports[con->index]; - unsigned int i; + if (bfin_earlyprintk_port.port.line != co->index) + return; - for (i = 0; i < n; i++, s++) { - if (*s == '\n') - early_serial_putc(&uart->port, '\r'); - early_serial_putc(&uart->port, *s); - } + spin_lock_irqsave(&bfin_earlyprintk_port.port.lock, flags); + uart_console_write(&bfin_earlyprintk_port.port, s, count, + bfin_serial_console_putchar); + spin_unlock_irqrestore(&bfin_earlyprintk_port.port.lock, flags); } /* @@ -1308,105 +1221,204 @@ static __init void early_serial_write(struct console *con, const char *s, */ static struct __initdata console bfin_early_serial_console = { .name = "early_BFuart", - .write = early_serial_write, + .write = bfin_earlyprintk_console_write, .device = uart_console_device, .flags = CON_PRINTBUFFER, .index = -1, .data = &bfin_serial_reg, }; - -struct console __init *bfin_earlyserial_init(unsigned int port, - unsigned int cflag) -{ - struct bfin_serial_port *uart; - struct ktermios t; - - if (port == -1 || port >= nr_active_ports) - port = 0; - bfin_serial_init_ports(); - bfin_early_serial_console.index = port; - uart = &bfin_serial_ports[port]; - t.c_cflag = cflag; - t.c_iflag = 0; - t.c_oflag = 0; - t.c_lflag = ICANON; - t.c_line = port; - bfin_serial_set_termios(&uart->port, &t, &t); - return &bfin_early_serial_console; -} - -#endif /* CONFIG_EARLY_PRINTK */ +#endif static struct uart_driver bfin_serial_reg = { .owner = THIS_MODULE, - .driver_name = "bfin-uart", - .dev_name = BFIN_SERIAL_NAME, + .driver_name = DRIVER_NAME, + .dev_name = BFIN_SERIAL_DEV_NAME, .major = BFIN_SERIAL_MAJOR, .minor = BFIN_SERIAL_MINOR, .nr = BFIN_UART_NR_PORTS, .cons = BFIN_SERIAL_CONSOLE, }; -static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state) +static int bfin_serial_suspend(struct platform_device *pdev, pm_message_t state) { - int i; + struct bfin_serial_port *uart = platform_get_drvdata(pdev); - for (i = 0; i < nr_active_ports; i++) { - if (bfin_serial_ports[i].port.dev != &dev->dev) - continue; - uart_suspend_port(&bfin_serial_reg, &bfin_serial_ports[i].port); - } + return uart_suspend_port(&bfin_serial_reg, &uart->port); +} - return 0; +static int bfin_serial_resume(struct platform_device *pdev) +{ + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + return uart_resume_port(&bfin_serial_reg, &uart->port); } -static int bfin_serial_resume(struct platform_device *dev) +static int bfin_serial_probe(struct platform_device *pdev) { - int i; + struct resource *res; + struct bfin_serial_port *uart = NULL; + int ret = 0; - for (i = 0; i < nr_active_ports; i++) { - if (bfin_serial_ports[i].port.dev != &dev->dev) - continue; - uart_resume_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { + dev_err(&pdev->dev, "Wrong bfin uart platform device id.\n"); + return -ENOENT; } - return 0; -} + if (bfin_serial_ports[pdev->id] == NULL) { -static int bfin_serial_probe(struct platform_device *dev) -{ - struct resource *res = dev->resource; - int i; + uart = kzalloc(sizeof(*uart), GFP_KERNEL); + if (!uart) { + dev_err(&pdev->dev, + "fail to malloc bfin_serial_port\n"); + return -ENOMEM; + } + bfin_serial_ports[pdev->id] = uart; - for (i = 0; i < dev->num_resources; i++, res++) - if (res->flags & IORESOURCE_MEM) - break; +#ifdef CONFIG_EARLY_PRINTK + if (!(bfin_earlyprintk_port.port.membase + && bfin_earlyprintk_port.port.line == pdev->id)) { + /* + * If the peripheral PINs of current port is allocated + * in earlyprintk probe stage, don't do it again. + */ +#endif + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, + "fail to request bfin serial peripherals\n"); + goto out_error_free_mem; + } +#ifdef CONFIG_EARLY_PRINTK + } +#endif + + spin_lock_init(&uart->port.lock); + uart->port.uartclk = get_sclk(); + uart->port.fifosize = BFIN_UART_TX_FIFO_SIZE; + uart->port.ops = &bfin_serial_pops; + uart->port.line = pdev->id; + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } - if (i < dev->num_resources) { - for (i = 0; i < nr_active_ports; i++, res++) { - if (bfin_serial_ports[i].port.mapbase != res->start) - continue; - bfin_serial_ports[i].port.dev = &dev->dev; - uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + uart->port.membase = ioremap(res->start, + res->end - res->start); + if (!uart->port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; } + uart->port.mapbase = res->start; + + uart->port.irq = platform_get_irq(pdev, 0); + if (uart->port.irq < 0) { + dev_err(&pdev->dev, "No uart RX/TX IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + + uart->status_irq = platform_get_irq(pdev, 1); + if (uart->status_irq < 0) { + dev_err(&pdev->dev, "No uart status IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + +#ifdef CONFIG_SERIAL_BFIN_DMA + uart->tx_done = 1; + uart->tx_count = 0; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No uart TX DMA channel specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + uart->tx_dma_channel = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res == NULL) { + dev_err(&pdev->dev, "No uart RX DMA channel specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + uart->rx_dma_channel = res->start; + + init_timer(&(uart->rx_dma_timer)); +#endif + +#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ + defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + uart->cts_pin = -1; + else + uart->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + uart->rts_pin = -1; + else + uart->rts_pin = res->start; +# if defined(CONFIG_SERIAL_BFIN_CTSRTS) + if (uart->rts_pin >= 0) + gpio_request(uart->rts_pin, DRIVER_NAME); +# endif +#endif } - return 0; +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + if (!is_early_platform_device(pdev)) { +#endif + uart = bfin_serial_ports[pdev->id]; + uart->port.dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, uart); + ret = uart_add_one_port(&bfin_serial_reg, &uart->port); +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + } +#endif + + if (!ret) + return 0; + + if (uart) { +out_error_unmap: + iounmap(uart->port.membase); +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); +out_error_free_mem: + kfree(uart); + bfin_serial_ports[pdev->id] = NULL; + } + + return ret; } -static int bfin_serial_remove(struct platform_device *dev) +static int __devexit bfin_serial_remove(struct platform_device *pdev) { - int i; - - for (i = 0; i < nr_active_ports; i++) { - if (bfin_serial_ports[i].port.dev != &dev->dev) - continue; - uart_remove_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); - bfin_serial_ports[i].port.dev = NULL; -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) - gpio_free(bfin_serial_ports[i].cts_pin); - gpio_free(bfin_serial_ports[i].rts_pin); + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + dev_set_drvdata(&pdev->dev, NULL); + + if (uart) { + uart_remove_one_port(&bfin_serial_reg, &uart->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (uart->rts_pin >= 0) + gpio_free(uart->rts_pin); #endif + iounmap(uart->port.membase); + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + kfree(uart); + bfin_serial_ports[pdev->id] = NULL; } return 0; @@ -1414,31 +1426,160 @@ static int bfin_serial_remove(struct platform_device *dev) static struct platform_driver bfin_serial_driver = { .probe = bfin_serial_probe, - .remove = bfin_serial_remove, + .remove = __devexit_p(bfin_serial_remove), .suspend = bfin_serial_suspend, .resume = bfin_serial_resume, .driver = { - .name = "bfin-uart", + .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; -static int __init bfin_serial_init(void) +#if defined(CONFIG_SERIAL_BFIN_CONSOLE) +static __initdata struct early_platform_driver early_bfin_serial_driver = { + .class_str = CLASS_BFIN_CONSOLE, + .pdrv = &bfin_serial_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +static int __init bfin_serial_rs_console_init(void) { + early_platform_driver_register(&early_bfin_serial_driver, DRIVER_NAME); + + early_platform_driver_probe(CLASS_BFIN_CONSOLE, BFIN_UART_NR_PORTS, 0); + + register_console(&bfin_serial_console); + + return 0; +} +console_initcall(bfin_serial_rs_console_init); +#endif + +#ifdef CONFIG_EARLY_PRINTK +/* + * Memory can't be allocated dynamically during earlyprink init stage. + * So, do individual probe for earlyprink with a static uart port variable. + */ +static int bfin_earlyprintk_probe(struct platform_device *pdev) +{ + struct resource *res; int ret; - pr_info("Serial: Blackfin serial driver\n"); + if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { + dev_err(&pdev->dev, "Wrong earlyprintk platform device id.\n"); + return -ENOENT; + } + + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, + "fail to request bfin serial peripherals\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + bfin_earlyprintk_port.port.membase = ioremap(res->start, + res->end - res->start); + if (!bfin_earlyprintk_port.port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + bfin_earlyprintk_port.port.mapbase = res->start; + bfin_earlyprintk_port.port.line = pdev->id; + bfin_earlyprintk_port.port.uartclk = get_sclk(); + bfin_earlyprintk_port.port.fifosize = BFIN_UART_TX_FIFO_SIZE; + spin_lock_init(&bfin_earlyprintk_port.port.lock); + + return 0; + +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + + return ret; +} + +static struct platform_driver bfin_earlyprintk_driver = { + .probe = bfin_earlyprintk_probe, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = { + .class_str = CLASS_BFIN_EARLYPRINTK, + .pdrv = &bfin_earlyprintk_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +struct console __init *bfin_earlyserial_init(unsigned int port, + unsigned int cflag) +{ + struct ktermios t; + char port_name[20]; + + if (port < 0 || port >= BFIN_UART_NR_PORTS) + return NULL; - bfin_serial_init_ports(); + /* + * Only probe resource of the given port in earlyprintk boot arg. + * The expected port id should be indicated in port name string. + */ + snprintf(port_name, 20, DRIVER_NAME ".%d", port); + early_platform_driver_register(&early_bfin_earlyprintk_driver, + port_name); + early_platform_driver_probe(CLASS_BFIN_EARLYPRINTK, 1, 0); + + if (!bfin_earlyprintk_port.port.membase) + return NULL; + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + /* + * If we are using early serial, don't let the normal console rewind + * log buffer, since that causes things to be printed multiple times + */ + bfin_serial_console.flags &= ~CON_PRINTBUFFER; +#endif + + bfin_early_serial_console.index = port; + t.c_cflag = cflag; + t.c_iflag = 0; + t.c_oflag = 0; + t.c_lflag = ICANON; + t.c_line = port; + bfin_serial_set_termios(&bfin_earlyprintk_port.port, &t, &t); + + return &bfin_early_serial_console; +} +#endif /* CONFIG_EARLY_PRINTK */ + +static int __init bfin_serial_init(void) +{ + int ret; + + pr_info("Blackfin serial driver\n"); ret = uart_register_driver(&bfin_serial_reg); - if (ret == 0) { - ret = platform_driver_register(&bfin_serial_driver); - if (ret) { - pr_debug("uart register failed\n"); - uart_unregister_driver(&bfin_serial_reg); - } + if (ret) { + pr_err("failed to register %s:%d\n", + bfin_serial_reg.driver_name, ret); + } + + ret = platform_driver_register(&bfin_serial_driver); + if (ret) { + pr_err("fail to register bfin uart\n"); + uart_unregister_driver(&bfin_serial_reg); } + return ret; } @@ -1452,7 +1593,7 @@ static void __exit bfin_serial_exit(void) module_init(bfin_serial_init); module_exit(bfin_serial_exit); -MODULE_AUTHOR("Aubrey.Li <aubrey.li@analog.com>"); +MODULE_AUTHOR("Sonic Zhang, Aubrey Li"); MODULE_DESCRIPTION("Blackfin generic serial port driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR); |