X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fserial%2Fimx.c;h=5f0be40dfdab7292ae65b4c763f4ee4105e36e24;hb=e76afc4e7816a0a5300073098cdac93a994eb5ca;hp=f7596641f121a31b908be26e773ea86daf085966;hpb=0d3c3938ff9855d8996db4083efd62e86b8987eb;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index f759664..5f0be40 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -40,11 +40,12 @@ #include #include #include +#include #include #include -#include -#include +#include +#include /* Register definitions */ #define URXD0 0x0 /* Receiver Register */ @@ -61,6 +62,11 @@ #define UBIR 0xa4 /* BRM Incremental Register */ #define UBMR 0xa8 /* BRM Modulator Register */ #define UBRC 0xac /* Baud Rate Count Register */ +#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2 +#define ONEMS 0xb0 /* One Millisecond register */ +#define UTS 0xb4 /* UART Test Register */ +#endif +#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1) #define BIPR1 0xb0 /* Incremental Preset Register 1 */ #define BIPR2 0xb4 /* Incremental Preset Register 2 */ #define BIPR3 0xb8 /* Incremental Preset Register 3 */ @@ -70,6 +76,7 @@ #define BMPR3 0xc8 /* BRM Modulator Register 3 */ #define BMPR4 0xcc /* BRM Modulator Register 4 */ #define UTS 0xd0 /* UART Test Register */ +#endif /* UART Control Register Bit Fields.*/ #define URXD_CHARRDY (1<<15) @@ -89,7 +96,12 @@ #define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ #define UCR1_SNDBRK (1<<4) /* Send break */ #define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ +#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1) #define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */ +#endif +#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2 +#define UCR1_UARTCLKEN (0) /* not present on mx2/mx3 */ +#endif #define UCR1_DOZE (1<<1) /* Doze */ #define UCR1_UARTEN (1<<0) /* UART enabled */ #define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ @@ -115,8 +127,13 @@ #define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ #define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ #define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ -#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz */ -#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz */ +#ifdef CONFIG_ARCH_IMX +#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */ +#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */ +#endif +#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3 +#define UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */ +#endif #define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ #define UCR3_BPEN (1<<0) /* Preset registers enable */ #define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */ @@ -163,13 +180,19 @@ #define UTS_SOFTRST (1<<0) /* Software reset */ /* We've been assigned a range on the "Low-density serial ports" major */ +#ifdef CONFIG_ARCH_IMX #define SERIAL_IMX_MAJOR 204 #define MINOR_START 41 +#define DEV_NAME "ttySMX" +#define MAX_INTERNAL_IRQ IMX_IRQS +#endif -/* - * This is the size of our serial port register set. - */ -#define UART_PORT_SIZE 0x100 +#ifdef CONFIG_ARCH_MXC +#define SERIAL_IMX_MAJOR 207 +#define MINOR_START 16 +#define DEV_NAME "ttymxc" +#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS +#endif /* * This determines how often we check the modem status signals @@ -181,12 +204,15 @@ #define DRIVER_NAME "IMX-uart" +#define UART_NR 8 + struct imx_port { struct uart_port port; struct timer_list timer; unsigned int old_status; int txirq,rxirq,rtsirq; int have_rtscts:1; + struct clk *clk; }; /* @@ -351,7 +377,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int rx,flg,ignored = 0; - struct tty_struct *tty = sport->port.info->tty; + struct tty_struct *tty = sport->port.info->port.tty; unsigned long flags, temp; spin_lock_irqsave(&sport->port.lock,flags); @@ -410,6 +436,26 @@ out: return IRQ_HANDLED; } +static irqreturn_t imx_int(int irq, void *dev_id) +{ + struct imx_port *sport = dev_id; + unsigned int sts; + + sts = readl(sport->port.membase + USR1); + + if (sts & USR1_RRDY) + imx_rxint(irq, dev_id); + + if (sts & USR1_TRDY && + readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) + imx_txint(irq, dev_id); + + if (sts & USR1_RTSD) + imx_rtsint(irq, dev_id); + + return IRQ_HANDLED; +} + /* * Return TIOCSER_TEMT when transmitter is not busy. */ @@ -482,7 +528,8 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) * RFDIV is set such way to satisfy requested uartclk value */ val = TXTL << 10 | RXTL; - ufcr_rfdiv = (imx_get_perclk1() + sport->port.uartclk / 2) / sport->port.uartclk; + ufcr_rfdiv = (clk_get_rate(sport->clk) + sport->port.uartclk / 2) + / sport->port.uartclk; if(!ufcr_rfdiv) ufcr_rfdiv = 1; @@ -514,21 +561,34 @@ static int imx_startup(struct uart_port *port) writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); /* - * Allocate the IRQ + * Allocate the IRQ(s) i.MX1 has three interrupts whereas later + * chips only have one interrupt. */ - retval = request_irq(sport->rxirq, imx_rxint, 0, - DRIVER_NAME, sport); - if (retval) goto error_out1; - - retval = request_irq(sport->txirq, imx_txint, 0, - DRIVER_NAME, sport); - if (retval) goto error_out2; - - retval = request_irq(sport->rtsirq, imx_rtsint, - (sport->rtsirq < IMX_IRQS) ? 0 : + if (sport->txirq > 0) { + retval = request_irq(sport->rxirq, imx_rxint, 0, + DRIVER_NAME, sport); + if (retval) + goto error_out1; + + retval = request_irq(sport->txirq, imx_txint, 0, + DRIVER_NAME, sport); + if (retval) + goto error_out2; + + retval = request_irq(sport->rtsirq, imx_rtsint, + (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - DRIVER_NAME, sport); - if (retval) goto error_out3; + DRIVER_NAME, sport); + if (retval) + goto error_out3; + } else { + retval = request_irq(sport->port.irq, imx_int, 0, + DRIVER_NAME, sport); + if (retval) { + free_irq(sport->port.irq, sport); + goto error_out1; + } + } /* * Finally, clear and enable interrupts @@ -536,13 +596,19 @@ static int imx_startup(struct uart_port *port) writel(USR1_RTSD, sport->port.membase + USR1); temp = readl(sport->port.membase + UCR1); - temp |= (UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); + temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; writel(temp, sport->port.membase + UCR1); temp = readl(sport->port.membase + UCR2); temp |= (UCR2_RXEN | UCR2_TXEN); writel(temp, sport->port.membase + UCR2); +#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3 + temp = readl(sport->port.membase + UCR3); + temp |= UCR3_RXDMUXSEL; + writel(temp, sport->port.membase + UCR3); +#endif + /* * Enable modem status interrupts */ @@ -553,9 +619,11 @@ static int imx_startup(struct uart_port *port) return 0; error_out3: - free_irq(sport->txirq, sport); + if (sport->txirq) + free_irq(sport->txirq, sport); error_out2: - free_irq(sport->rxirq, sport); + if (sport->rxirq) + free_irq(sport->rxirq, sport); error_out1: return retval; } @@ -573,9 +641,12 @@ static void imx_shutdown(struct uart_port *port) /* * Free the interrupts */ - free_irq(sport->rtsirq, sport); - free_irq(sport->txirq, sport); - free_irq(sport->rxirq, sport); + if (sport->txirq > 0) { + free_irq(sport->rtsirq, sport); + free_irq(sport->txirq, sport); + free_irq(sport->rxirq, sport); + } else + free_irq(sport->port.irq, sport); /* * Disable all interrupts, port and break condition. @@ -594,6 +665,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long flags; unsigned int ucr2, old_ucr1, old_txrxen, baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned int div, num, denom, ufcr; /* * If we don't support modem control lines, don't allow @@ -639,7 +711,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); quot = uart_get_divisor(port, baud); spin_lock_irqsave(&sport->port.lock, flags); @@ -689,14 +761,41 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, sport->port.membase + UCR2); old_txrxen &= (UCR2_TXEN | UCR2_RXEN); - /* set the baud rate. We assume uartclk = 16 MHz - * - * baud * 16 UBIR - 1 - * --------- = -------- - * uartclk UBMR - 1 - */ - writel((baud / 100) - 1, sport->port.membase + UBIR); - writel(10000 - 1, sport->port.membase + UBMR); + div = sport->port.uartclk / (baud * 16); + if (div > 7) + div = 7; + if (!div) + div = 1; + + num = baud; + denom = port->uartclk / div / 16; + + /* shift num and denom right until they fit into 16 bits */ + while (num > 0x10000 || denom > 0x10000) { + num >>= 1; + denom >>= 1; + } + if (num > 0) + num -= 1; + if (denom > 0) + denom -= 1; + + writel(num, sport->port.membase + UBIR); + writel(denom, sport->port.membase + UBMR); + + if (div == 7) + div = 6; /* 6 in RFDIV means divide by 7 */ + else + div = 6 - div; + + ufcr = readl(sport->port.membase + UFCR); + ufcr = (ufcr & (~UFCR_RFDIV)) | + (div << 7); + writel(ufcr, sport->port.membase + UFCR); + +#ifdef ONEMS + writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS); +#endif writel(old_ucr1, sport->port.membase + UCR1); @@ -721,9 +820,11 @@ static const char *imx_type(struct uart_port *port) */ static void imx_release_port(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *mmres; - release_mem_region(sport->port.mapbase, UART_PORT_SIZE); + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mmres->start, mmres->end - mmres->start + 1); } /* @@ -731,10 +832,18 @@ static void imx_release_port(struct uart_port *port) */ static int imx_request_port(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *mmres; + void *ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mmres) + return -ENODEV; + + ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1, + "imx-uart"); - return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, - "imx-uart") != NULL ? 0 : -EBUSY; + return ret ? 0 : -EBUSY; } /* @@ -796,65 +905,7 @@ static struct uart_ops imx_pops = { .verify_port = imx_verify_port, }; -static struct imx_port imx_ports[] = { - { - .txirq = UART1_MINT_TX, - .rxirq = UART1_MINT_RX, - .rtsirq = UART1_MINT_RTS, - .port = { - .type = PORT_IMX, - .iotype = UPIO_MEM, - .membase = (void *)IMX_UART1_BASE, - .mapbase = IMX_UART1_BASE, /* FIXME */ - .irq = UART1_MINT_RX, - .uartclk = 16000000, - .fifosize = 32, - .flags = UPF_BOOT_AUTOCONF, - .ops = &imx_pops, - .line = 0, - }, - }, { - .txirq = UART2_MINT_TX, - .rxirq = UART2_MINT_RX, - .rtsirq = UART2_MINT_RTS, - .port = { - .type = PORT_IMX, - .iotype = UPIO_MEM, - .membase = (void *)IMX_UART2_BASE, - .mapbase = IMX_UART2_BASE, /* FIXME */ - .irq = UART2_MINT_RX, - .uartclk = 16000000, - .fifosize = 32, - .flags = UPF_BOOT_AUTOCONF, - .ops = &imx_pops, - .line = 1, - }, - } -}; - -/* - * Setup the IMX serial ports. - * Note also that we support "console=ttySMXx" where "x" is either 0 or 1. - * Which serial port this ends up being depends on the machine you're - * running this kernel on. I'm not convinced that this is a good idea, - * but that's the way it traditionally works. - * - */ -static void __init imx_init_ports(void) -{ - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - for (i = 0; i < ARRAY_SIZE(imx_ports); i++) { - init_timer(&imx_ports[i].timer); - imx_ports[i].timer.function = imx_timeout; - imx_ports[i].timer.data = (unsigned long)&imx_ports[i]; - } -} +static struct imx_port *imx_ports[UART_NR]; #ifdef CONFIG_SERIAL_IMX_CONSOLE static void imx_console_putchar(struct uart_port *port, int ch) @@ -873,7 +924,7 @@ static void imx_console_putchar(struct uart_port *port, int ch) static void imx_console_write(struct console *co, const char *s, unsigned int count) { - struct imx_port *sport = &imx_ports[co->index]; + struct imx_port *sport = imx_ports[co->index]; unsigned int old_ucr1, old_ucr2; /* @@ -939,7 +990,7 @@ imx_console_get_options(struct imx_port *sport, int *baud, else ucfr_rfdiv = 6 - ucfr_rfdiv; - uartclk = imx_get_perclk1(); + uartclk = clk_get_rate(sport->clk); uartclk /= ucfr_rfdiv; { /* @@ -979,7 +1030,9 @@ imx_console_setup(struct console *co, char *options) */ if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports)) co->index = 0; - sport = &imx_ports[co->index]; + sport = imx_ports[co->index]; + if(sport == NULL) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -993,7 +1046,7 @@ imx_console_setup(struct console *co, char *options) static struct uart_driver imx_reg; static struct console imx_console = { - .name = "ttySMX", + .name = DEV_NAME, .write = imx_console_write, .device = uart_console_device, .setup = imx_console_setup, @@ -1002,14 +1055,6 @@ static struct console imx_console = { .data = &imx_reg, }; -static int __init imx_rs_console_init(void) -{ - imx_init_ports(); - register_console(&imx_console); - return 0; -} -console_initcall(imx_rs_console_init); - #define IMX_CONSOLE &imx_console #else #define IMX_CONSOLE NULL @@ -1018,7 +1063,7 @@ console_initcall(imx_rs_console_init); static struct uart_driver imx_reg = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, - .dev_name = "ttySMX", + .dev_name = DEV_NAME, .major = SERIAL_IMX_MAJOR, .minor = MINOR_START, .nr = ARRAY_SIZE(imx_ports), @@ -1045,29 +1090,104 @@ static int serial_imx_resume(struct platform_device *dev) return 0; } -static int serial_imx_probe(struct platform_device *dev) +static int serial_imx_probe(struct platform_device *pdev) { + struct imx_port *sport; struct imxuart_platform_data *pdata; + void __iomem *base; + int ret = 0; + struct resource *res; - imx_ports[dev->id].port.dev = &dev->dev; + sport = kzalloc(sizeof(*sport), GFP_KERNEL); + if (!sport) + return -ENOMEM; - pdata = (struct imxuart_platform_data *)dev->dev.platform_data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto free; + } + + base = ioremap(res->start, PAGE_SIZE); + if (!base) { + ret = -ENOMEM; + goto free; + } + + 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 = platform_get_irq(pdev, 0); + sport->rxirq = platform_get_irq(pdev, 0); + sport->txirq = platform_get_irq(pdev, 1); + sport->rtsirq = platform_get_irq(pdev, 2); + sport->port.fifosize = 32; + sport->port.ops = &imx_pops; + sport->port.flags = UPF_BOOT_AUTOCONF; + sport->port.line = pdev->id; + init_timer(&sport->timer); + sport->timer.function = imx_timeout; + sport->timer.data = (unsigned long)sport; + + sport->clk = clk_get(&pdev->dev, "uart"); + if (IS_ERR(sport->clk)) { + ret = PTR_ERR(sport->clk); + goto unmap; + } + clk_enable(sport->clk); + + sport->port.uartclk = clk_get_rate(sport->clk); + + imx_ports[pdev->id] = sport; + + pdata = pdev->dev.platform_data; if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) - imx_ports[dev->id].have_rtscts = 1; + sport->have_rtscts = 1; + + if (pdata->init) { + ret = pdata->init(pdev); + if (ret) + goto clkput; + } + + uart_add_one_port(&imx_reg, &sport->port); + platform_set_drvdata(pdev, &sport->port); - uart_add_one_port(&imx_reg, &imx_ports[dev->id].port); - platform_set_drvdata(dev, &imx_ports[dev->id]); return 0; +clkput: + clk_put(sport->clk); + clk_disable(sport->clk); +unmap: + iounmap(sport->port.membase); +free: + kfree(sport); + + return ret; } -static int serial_imx_remove(struct platform_device *dev) +static int serial_imx_remove(struct platform_device *pdev) { - struct imx_port *sport = platform_get_drvdata(dev); + struct imxuart_platform_data *pdata; + struct imx_port *sport = platform_get_drvdata(pdev); + + pdata = pdev->dev.platform_data; - platform_set_drvdata(dev, NULL); + platform_set_drvdata(pdev, NULL); - if (sport) + if (sport) { uart_remove_one_port(&imx_reg, &sport->port); + clk_put(sport->clk); + } + + clk_disable(sport->clk); + + if (pdata->exit) + pdata->exit(pdev); + + iounmap(sport->port.membase); + kfree(sport); return 0; } @@ -1080,6 +1200,7 @@ static struct platform_driver serial_imx_driver = { .resume = serial_imx_resume, .driver = { .name = "imx-uart", + .owner = THIS_MODULE, }, }; @@ -1089,8 +1210,6 @@ static int __init imx_serial_init(void) printk(KERN_INFO "Serial: IMX driver\n"); - imx_init_ports(); - ret = uart_register_driver(&imx_reg); if (ret) return ret; @@ -1114,3 +1233,4 @@ module_exit(imx_serial_exit); MODULE_AUTHOR("Sascha Hauer"); MODULE_DESCRIPTION("IMX generic serial port driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx-uart");