diff options
Diffstat (limited to 'drivers/serial/mpc52xx_uart.c')
-rw-r--r-- | drivers/serial/mpc52xx_uart.c | 177 |
1 files changed, 171 insertions, 6 deletions
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 7ab73fa4a80f..821facd10bbc 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -16,6 +16,9 @@ * Some of the code has been inspired/copied from the 2.4 code written * by Dale Farnsworth <dfarnsworth@mvista.com>. * + * Copyright (C) 2008 Freescale Semiconductor Inc. + * John Rigby <jrigby@gmail.com> + * Added support for MPC5121 * Copyright (C) 2006 Secret Lab Technologies Ltd. * Grant Likely <grant.likely@secretlab.ca> * Copyright (C) 2004-2006 Sylvain Munaut <tnt@246tNt.com> @@ -78,6 +81,7 @@ #endif #include <asm/mpc52xx.h> +#include <asm/mpc512x.h> #include <asm/mpc52xx_psc.h> #if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -129,13 +133,29 @@ static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); #if defined(CONFIG_PPC_MERGE) static struct of_device_id mpc52xx_uart_of_match[] = { - { .type = "serial", .compatible = "fsl,mpc5200-psc-uart", }, - { .type = "serial", .compatible = "mpc5200-psc-uart", }, /* lite5200 */ - { .type = "serial", .compatible = "mpc5200-serial", }, /* efika */ +#ifdef CONFIG_PPC_MPC52xx + { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by old lite5200 device trees: */ + { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by efika: */ + { .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, }, +#endif +#ifdef CONFIG_PPC_MPC512x + { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, + {}, +#endif +}; +#if defined(CONFIG_PPC_MERGE) +static const struct of_device_id mpc52xx_uart_of_match[] = { + {.type = "serial", + .compatible = "mpc5200-psc-uart", +#endif {}, }; #endif +#endif + /* ======================================================================== */ /* PSC fifo operations for isolating differences between 52xx and 512x */ /* ======================================================================== */ @@ -159,6 +179,7 @@ struct psc_ops { unsigned long (*getuartclk)(void *p); }; +#ifdef CONFIG_PPC_MPC52xx #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) static void mpc52xx_psc_fifo_init(struct uart_port *port) { @@ -291,7 +312,145 @@ static struct psc_ops mpc52xx_psc_ops = { .getuartclk = mpc52xx_getuartclk, }; -static struct psc_ops *psc_ops = &mpc52xx_psc_ops; +#endif /* CONFIG_MPC52xx */ + +#ifdef CONFIG_PPC_MPC512x +#define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) +static void mpc512x_psc_fifo_init(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->txalarm, 1); + out_be32(&FIFO_512x(port)->tximr, 0); + + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->rxalarm, 1); + out_be32(&FIFO_512x(port)->rximr, 0); + + out_be32(&FIFO_512x(port)->tximr, MPC512x_PSC_FIFO_ALARM); + out_be32(&FIFO_512x(port)->rximr, MPC512x_PSC_FIFO_ALARM); +} + +static int mpc512x_psc_raw_rx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); +} + +static int mpc512x_psc_raw_tx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->txsr) & MPC512x_PSC_FIFO_FULL); +} + +static int mpc512x_psc_rx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->rxsr) + & in_be32(&FIFO_512x(port)->rximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & in_be32(&FIFO_512x(port)->tximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_empty(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & MPC512x_PSC_FIFO_EMPTY; +} + +static void mpc512x_psc_stop_rx(struct uart_port *port) +{ + unsigned long rx_fifo_imr; + + rx_fifo_imr = in_be32(&FIFO_512x(port)->rximr); + rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->rximr, rx_fifo_imr); +} + +static void mpc512x_psc_start_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_stop_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_rx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->rxisr, in_be32(&FIFO_512x(port)->rxisr)); +} + +static void mpc512x_psc_tx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txisr, in_be32(&FIFO_512x(port)->txisr)); +} + +static void mpc512x_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&FIFO_512x(port)->txdata_8, c); +} + +static unsigned char mpc512x_psc_read_char(struct uart_port *port) +{ + return in_8(&FIFO_512x(port)->rxdata_8); +} + +static void mpc512x_psc_cw_disable_ints(struct uart_port *port) +{ + port->read_status_mask = + in_be32(&FIFO_512x(port)->tximr) << 16 | + in_be32(&FIFO_512x(port)->rximr); + out_be32(&FIFO_512x(port)->tximr, 0); + out_be32(&FIFO_512x(port)->rximr, 0); +} + +static void mpc512x_psc_cw_restore_ints(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->tximr, + (port->read_status_mask >> 16) & 0x7f); + out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); +} + +static unsigned long mpc512x_getuartclk(void *p) +{ + return mpc512x_find_ips_freq(p); +} + +static struct psc_ops mpc512x_psc_ops = { + .fifo_init = mpc512x_psc_fifo_init, + .raw_rx_rdy = mpc512x_psc_raw_rx_rdy, + .raw_tx_rdy = mpc512x_psc_raw_tx_rdy, + .rx_rdy = mpc512x_psc_rx_rdy, + .tx_rdy = mpc512x_psc_tx_rdy, + .tx_empty = mpc512x_psc_tx_empty, + .stop_rx = mpc512x_psc_stop_rx, + .start_tx = mpc512x_psc_start_tx, + .stop_tx = mpc512x_psc_stop_tx, + .rx_clr_irq = mpc512x_psc_rx_clr_irq, + .tx_clr_irq = mpc512x_psc_tx_clr_irq, + .write_char = mpc512x_psc_write_char, + .read_char = mpc512x_psc_read_char, + .cw_disable_ints = mpc512x_psc_cw_disable_ints, + .cw_restore_ints = mpc512x_psc_cw_restore_ints, + .getuartclk = mpc512x_getuartclk, +}; +#endif + +static struct psc_ops *psc_ops; /* ======================================================================== */ /* UART operations */ @@ -381,7 +540,8 @@ mpc52xx_uart_startup(struct uart_port *port) /* Request IRQ */ ret = request_irq(port->irq, mpc52xx_uart_int, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "mpc52xx_psc_uart", port); + IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED, + "mpc52xx_psc_uart", port); if (ret) return ret; @@ -1208,15 +1368,19 @@ mpc52xx_uart_of_enumerate(void) static int enum_done; struct device_node *np; const unsigned int *devno; + const struct of_device_id *match; int i; if (enum_done) return; for_each_node_by_type(np, "serial") { - if (!of_match_node(mpc52xx_uart_of_match, np)) + match = of_match_node(mpc52xx_uart_of_match, np); + if (!match) continue; + psc_ops = match->data; + /* Is a particular device number requested? */ devno = of_get_property(np, "port-number", NULL); mpc52xx_uart_of_assign(np, devno ? *devno : -1); @@ -1277,6 +1441,7 @@ mpc52xx_uart_init(void) return ret; } #else + psc_ops = &mpc52xx_psc_ops; ret = platform_driver_register(&mpc52xx_uart_platform_driver); if (ret) { printk(KERN_ERR "%s: platform_driver_register failed (%i)\n", |