summary refs log tree commit diff
path: root/drivers/serial
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-08-05 09:03:46 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-08-05 09:03:46 -0700
commitcdd854bc42b5e6c79bbbc40c6600d995ffe6e747 (patch)
tree18c4dcc07bbb8aeb2b23bc812cd60cc293f36cd8 /drivers/serial
parentbbc4fd12a635492ad9d12bb418124fa2d5f0d734 (diff)
parent42a0ae2282b512d1a8f6f020327f5f7b8f31a5ea (diff)
downloadlinux-cdd854bc42b5e6c79bbbc40c6600d995ffe6e747.tar.gz
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (79 commits)
  powerpc/8xx: Add support for the MPC8xx based boards from TQC
  powerpc/85xx: Introduce support for the Freescale P1022DS reference board
  powerpc/85xx: Adding DTS for the STx GP3-SSA MPC8555 board
  powerpc/85xx: Change deprecated binding for 85xx-based boards
  powerpc/tqm85xx: add a quirk for ti1520 PCMCIA bridge
  powerpc/tqm85xx: update PCI interrupt-map attribute
  powerpc/mpc8308rdb: support for MPC8308RDB board from Freescale
  powerpc/fsl_pci: add quirk for mpc8308 pcie bridge
  powerpc/85xx: Cleanup QE initialization for MPC85xxMDS boards
  powerpc/85xx: Fix booting for P1021MDS boards
  powerpc/85xx: Fix SWIOTLB initalization for MPC85xxMDS boards
  powerpc/85xx: kexec for SMP 85xx BookE systems
  powerpc/5200/i2c: improve i2c bus error recovery
  of/xilinxfb: update tft compatible versions
  powerpc/fsl-diu-fb: Support setting display mode using EDID
  powerpc/5121: doc/dts-bindings: update doc of FSL DIU bindings
  powerpc/5121: shared DIU framebuffer support
  powerpc/5121: move fsl-diu-fb.h to include/linux
  powerpc/5121: fsl-diu-fb: fix issue with re-enabling DIU area descriptor
  powerpc/512x: add clock structure for Video-IN (VIU) unit
  ...
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/mpc52xx_uart.c145
1 files changed, 116 insertions, 29 deletions
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
index 84a35f699016..1a88b363005c 100644
--- a/drivers/serial/mpc52xx_uart.c
+++ b/drivers/serial/mpc52xx_uart.c
@@ -113,7 +113,9 @@ struct psc_ops {
 	unsigned char	(*read_char)(struct uart_port *port);
 	void		(*cw_disable_ints)(struct uart_port *port);
 	void		(*cw_restore_ints)(struct uart_port *port);
-	unsigned long	(*getuartclk)(void *p);
+	unsigned int	(*set_baudrate)(struct uart_port *port,
+					struct ktermios *new,
+					struct ktermios *old);
 	int		(*clock)(struct uart_port *port, int enable);
 	int		(*fifoc_init)(void);
 	void		(*fifoc_uninit)(void);
@@ -121,6 +123,16 @@ struct psc_ops {
 	irqreturn_t	(*handle_irq)(struct uart_port *port);
 };
 
+/* setting the prescaler and divisor reg is common for all chips */
+static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc,
+				       u16 prescaler, unsigned int divisor)
+{
+	/* select prescaler */
+	out_be16(&psc->mpc52xx_psc_clock_select, prescaler);
+	out_8(&psc->ctur, divisor >> 8);
+	out_8(&psc->ctlr, divisor & 0xff);
+}
+
 #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)
@@ -128,9 +140,6 @@ static void mpc52xx_psc_fifo_init(struct uart_port *port)
 	struct mpc52xx_psc __iomem *psc = PSC(port);
 	struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port);
 
-	/* /32 prescaler */
-	out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00);
-
 	out_8(&fifo->rfcntl, 0x00);
 	out_be16(&fifo->rfalarm, 0x1ff);
 	out_8(&fifo->tfcntl, 0x07);
@@ -219,15 +228,47 @@ static void mpc52xx_psc_cw_restore_ints(struct uart_port *port)
 	out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
 }
 
-/* Search for bus-frequency property in this node or a parent */
-static unsigned long mpc52xx_getuartclk(void *p)
+static unsigned int mpc5200_psc_set_baudrate(struct uart_port *port,
+					     struct ktermios *new,
+					     struct ktermios *old)
 {
-	/*
-	 * 5200 UARTs have a / 32 prescaler
-	 * but the generic serial code assumes 16
-	 * so return ipb freq / 2
-	 */
-	return mpc5xxx_get_bus_frequency(p) / 2;
+	unsigned int baud;
+	unsigned int divisor;
+
+	/* The 5200 has a fixed /32 prescaler, uartclk contains the ipb freq */
+	baud = uart_get_baud_rate(port, new, old,
+				  port->uartclk / (32 * 0xffff) + 1,
+				  port->uartclk / 32);
+	divisor = (port->uartclk + 16 * baud) / (32 * baud);
+
+	/* enable the /32 prescaler and set the divisor */
+	mpc52xx_set_divisor(PSC(port), 0xdd00, divisor);
+	return baud;
+}
+
+static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port,
+					      struct ktermios *new,
+					      struct ktermios *old)
+{
+	unsigned int baud;
+	unsigned int divisor;
+	u16 prescaler;
+
+	/* The 5200B has a selectable /4 or /32 prescaler, uartclk contains the
+	 * ipb freq */
+	baud = uart_get_baud_rate(port, new, old,
+				  port->uartclk / (32 * 0xffff) + 1,
+				  port->uartclk / 4);
+	divisor = (port->uartclk + 2 * baud) / (4 * baud);
+
+	/* select the proper prescaler and set the divisor */
+	if (divisor > 0xffff) {
+		divisor = (divisor + 4) / 8;
+		prescaler = 0xdd00; /* /32 */
+	} else
+		prescaler = 0xff00; /* /4 */
+	mpc52xx_set_divisor(PSC(port), prescaler, divisor);
+	return baud;
 }
 
 static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np)
@@ -258,7 +299,28 @@ static struct psc_ops mpc52xx_psc_ops = {
 	.read_char = mpc52xx_psc_read_char,
 	.cw_disable_ints = mpc52xx_psc_cw_disable_ints,
 	.cw_restore_ints = mpc52xx_psc_cw_restore_ints,
-	.getuartclk = mpc52xx_getuartclk,
+	.set_baudrate = mpc5200_psc_set_baudrate,
+	.get_irq = mpc52xx_psc_get_irq,
+	.handle_irq = mpc52xx_psc_handle_irq,
+};
+
+static struct psc_ops mpc5200b_psc_ops = {
+	.fifo_init = mpc52xx_psc_fifo_init,
+	.raw_rx_rdy = mpc52xx_psc_raw_rx_rdy,
+	.raw_tx_rdy = mpc52xx_psc_raw_tx_rdy,
+	.rx_rdy = mpc52xx_psc_rx_rdy,
+	.tx_rdy = mpc52xx_psc_tx_rdy,
+	.tx_empty = mpc52xx_psc_tx_empty,
+	.stop_rx = mpc52xx_psc_stop_rx,
+	.start_tx = mpc52xx_psc_start_tx,
+	.stop_tx = mpc52xx_psc_stop_tx,
+	.rx_clr_irq = mpc52xx_psc_rx_clr_irq,
+	.tx_clr_irq = mpc52xx_psc_tx_clr_irq,
+	.write_char = mpc52xx_psc_write_char,
+	.read_char = mpc52xx_psc_read_char,
+	.cw_disable_ints = mpc52xx_psc_cw_disable_ints,
+	.cw_restore_ints = mpc52xx_psc_cw_restore_ints,
+	.set_baudrate = mpc5200b_psc_set_baudrate,
 	.get_irq = mpc52xx_psc_get_irq,
 	.handle_irq = mpc52xx_psc_handle_irq,
 };
@@ -392,9 +454,35 @@ static void mpc512x_psc_cw_restore_ints(struct uart_port *port)
 	out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f);
 }
 
-static unsigned long mpc512x_getuartclk(void *p)
+static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port,
+					     struct ktermios *new,
+					     struct ktermios *old)
 {
-	return mpc5xxx_get_bus_frequency(p);
+	unsigned int baud;
+	unsigned int divisor;
+
+	/*
+	 * The "MPC5121e Microcontroller Reference Manual, Rev. 3" says on
+	 * pg. 30-10 that the chip supports a /32 and a /10 prescaler.
+	 * Furthermore, it states that "After reset, the prescaler by 10
+	 * for the UART mode is selected", but the reset register value is
+	 * 0x0000 which means a /32 prescaler. This is wrong.
+	 *
+	 * In reality using /32 prescaler doesn't work, as it is not supported!
+	 * Use /16 or /10 prescaler, see "MPC5121e Hardware Design Guide",
+	 * Chapter 4.1 PSC in UART Mode.
+	 * Calculate with a /16 prescaler here.
+	 */
+
+	/* uartclk contains the ips freq */
+	baud = uart_get_baud_rate(port, new, old,
+				  port->uartclk / (16 * 0xffff) + 1,
+				  port->uartclk / 16);
+	divisor = (port->uartclk + 8 * baud) / (16 * baud);
+
+	/* enable the /16 prescaler and set the divisor */
+	mpc52xx_set_divisor(PSC(port), 0xdd00, divisor);
+	return baud;
 }
 
 /* Init PSC FIFO Controller */
@@ -498,7 +586,7 @@ static struct psc_ops mpc512x_psc_ops = {
 	.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,
+	.set_baudrate = mpc512x_psc_set_baudrate,
 	.clock = mpc512x_psc_clock,
 	.fifoc_init = mpc512x_psc_fifoc_init,
 	.fifoc_uninit = mpc512x_psc_fifoc_uninit,
@@ -666,8 +754,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
 	struct mpc52xx_psc __iomem *psc = PSC(port);
 	unsigned long flags;
 	unsigned char mr1, mr2;
-	unsigned short ctr;
-	unsigned int j, baud, quot;
+	unsigned int j;
+	unsigned int baud;
 
 	/* Prepare what we're gonna write */
 	mr1 = 0;
@@ -704,16 +792,9 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
 		mr2 |= MPC52xx_PSC_MODE_TXCTS;
 	}
 
-	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
-	quot = uart_get_divisor(port, baud);
-	ctr = quot & 0xffff;
-
 	/* Get the lock */
 	spin_lock_irqsave(&port->lock, flags);
 
-	/* Update the per-port timeout */
-	uart_update_timeout(port, new->c_cflag, baud);
-
 	/* Do our best to flush TX & RX, so we don't lose anything */
 	/* But we don't wait indefinitely ! */
 	j = 5000000;	/* Maximum wait */
@@ -737,8 +818,10 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
 	out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
 	out_8(&psc->mode, mr1);
 	out_8(&psc->mode, mr2);
-	out_8(&psc->ctur, ctr >> 8);
-	out_8(&psc->ctlr, ctr & 0xff);
+	baud = psc_ops->set_baudrate(port, new, old);
+
+	/* Update the per-port timeout */
+	uart_update_timeout(port, new->c_cflag, baud);
 
 	if (UART_ENABLE_MS(port, new->c_cflag))
 		mpc52xx_uart_enable_ms(port);
@@ -1118,7 +1201,7 @@ mpc52xx_console_setup(struct console *co, char *options)
 		return ret;
 	}
 
-	uartclk = psc_ops->getuartclk(np);
+	uartclk = mpc5xxx_get_bus_frequency(np);
 	if (uartclk == 0) {
 		pr_debug("Could not find uart clock frequency!\n");
 		return -EINVAL;
@@ -1201,6 +1284,7 @@ static struct uart_driver mpc52xx_uart_driver = {
 
 static struct of_device_id mpc52xx_uart_of_match[] = {
 #ifdef CONFIG_PPC_MPC52xx
+	{ .compatible = "fsl,mpc5200b-psc-uart", .data = &mpc5200b_psc_ops, },
 	{ .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, },
 	/* binding used by old lite5200 device trees: */
 	{ .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, },
@@ -1233,7 +1317,10 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
 	pr_debug("Found %s assigned to ttyPSC%x\n",
 		 mpc52xx_uart_nodes[idx]->full_name, idx);
 
-	uartclk = psc_ops->getuartclk(op->dev.of_node);
+	/* set the uart clock to the input clock of the psc, the different
+	 * prescalers are taken into account in the set_baudrate() methods
+	 * of the respective chip */
+	uartclk = mpc5xxx_get_bus_frequency(op->dev.of_node);
 	if (uartclk == 0) {
 		dev_dbg(&op->dev, "Could not find uart clock frequency!\n");
 		return -EINVAL;