[PATCH] ppc32 CPM_UART: Fixed odd address translations

Current address translation methods can produce wrong results, because
virt_to_bus and vice versa may not produce correct offsets on dma-allocated
memory. The right way is, while tracking both phys and virt address of the
window that has been allocated for boffer descriptors, and use those
numbers to compute the offset and make translation properly.

Signed-off-by: Vitaly Bordug <vbordug@ru.mvista.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h
index 17f2c7a..aa5eb7d 100644
--- a/drivers/serial/cpm_uart/cpm_uart.h
+++ b/drivers/serial/cpm_uart/cpm_uart.h
@@ -66,6 +66,7 @@
 	uint			 dp_addr;
 	void			*mem_addr;
 	dma_addr_t		 dma_addr;
+	u32			mem_size;
 	/* helpers */
 	int			 baud;
 	int			 bits;
@@ -92,4 +93,36 @@
 void scc3_lineif(struct uart_cpm_port *pinfo);
 void scc4_lineif(struct uart_cpm_port *pinfo);
 
+/*
+   virtual to phys transtalion
+*/
+static inline unsigned long cpu2cpm_addr(void* addr, struct uart_cpm_port *pinfo)
+{
+	int offset;
+	u32 val = (u32)addr;
+	/* sane check */
+	if ((val >= (u32)pinfo->mem_addr) &&
+			(val<((u32)pinfo->mem_addr + pinfo->mem_size))) {
+		offset = val - (u32)pinfo->mem_addr;
+		return pinfo->dma_addr+offset;
+	}
+	printk("%s(): address %x to translate out of range!\n", __FUNCTION__, val);
+	return 0;
+}
+
+static inline void *cpm2cpu_addr(unsigned long addr, struct uart_cpm_port *pinfo)
+{
+	int offset;
+	u32 val = addr;
+	/* sane check */
+	if ((val >= pinfo->dma_addr) &&
+			(val<(pinfo->dma_addr + pinfo->mem_size))) {
+		offset = val - (u32)pinfo->dma_addr;
+		return (void*)(pinfo->mem_addr+offset);
+	}
+	printk("%s(): address %x to translate out of range!\n", __FUNCTION__, val);
+	return 0;
+}
+
+
 #endif /* CPM_UART_H */
diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c
index 9a5b044..ced193b 100644
--- a/drivers/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/serial/cpm_uart/cpm_uart_core.c
@@ -72,19 +72,6 @@
 
 /**************************************************************/
 
-static inline unsigned long cpu2cpm_addr(void *addr)
-{
-	if ((unsigned long)addr >= CPM_ADDR)
-		return (unsigned long)addr;
-	return virt_to_bus(addr);
-}
-
-static inline void *cpm2cpu_addr(unsigned long addr)
-{
-	if (addr >= CPM_ADDR)
-		return (void *)addr;
-	return bus_to_virt(addr);
-}
 
 /* Place-holder for board-specific stuff */
 struct platform_device* __attribute__ ((weak)) __init
@@ -290,7 +277,7 @@
 		}
 
 		/* get pointer */
-		cp = cpm2cpu_addr(bdp->cbd_bufaddr);
+		cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
 
 		/* loop through the buffer */
 		while (i-- > 0) {
@@ -633,7 +620,7 @@
 		/* Pick next descriptor and fill from buffer */
 		bdp = pinfo->tx_cur;
 
-		p = cpm2cpu_addr(bdp->cbd_bufaddr);
+		p = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
 
 		*p++ = port->x_char;
 		bdp->cbd_datlen = 1;
@@ -660,7 +647,7 @@
 
 	while (!(bdp->cbd_sc & BD_SC_READY) && (xmit->tail != xmit->head)) {
 		count = 0;
-		p = cpm2cpu_addr(bdp->cbd_bufaddr);
+		p = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
 		while (count < pinfo->tx_fifosize) {
 			*p++ = xmit->buf[xmit->tail];
 			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -709,12 +696,12 @@
 	mem_addr = pinfo->mem_addr;
 	bdp = pinfo->rx_cur = pinfo->rx_bd_base;
 	for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) {
-		bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr);
+		bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo);
 		bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
 		mem_addr += pinfo->rx_fifosize;
 	}
 
-	bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr);
+	bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo);
 	bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
 
 	/* Set the physical address of the host memory
@@ -724,12 +711,12 @@
 	mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
 	bdp = pinfo->tx_cur = pinfo->tx_bd_base;
 	for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) {
-		bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr);
+		bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo);
 		bdp->cbd_sc = BD_SC_INTRPT;
 		mem_addr += pinfo->tx_fifosize;
 	}
 
-	bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr);
+	bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo);
 	bdp->cbd_sc = BD_SC_WRAP | BD_SC_INTRPT;
 }
 
@@ -1099,7 +1086,7 @@
 		 * If the buffer address is in the CPM DPRAM, don't
 		 * convert it.
 		 */
-		cp = cpm2cpu_addr(bdp->cbd_bufaddr);
+		cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
 
 		*cp = *s;
 
@@ -1116,7 +1103,7 @@
 			while ((bdp->cbd_sc & BD_SC_READY) != 0)
 				;
 
-			cp = cpm2cpu_addr(bdp->cbd_bufaddr);
+			cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
 
 			*cp = 13;
 			bdp->cbd_datlen = 1;
diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c
index 31223aa..a5a3062 100644
--- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c
+++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c
@@ -144,7 +144,7 @@
 		/* was hostalloc but changed cause it blows away the */
 		/* large tlb mapping when pinning the kernel area    */
 		mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8));
-		dma_addr = 0;
+		dma_addr = (u32)mem_addr;
 	} else
 		mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr,
 					      GFP_KERNEL);
@@ -157,8 +157,9 @@
 	}
 
 	pinfo->dp_addr = dp_offset;
-	pinfo->mem_addr = mem_addr;
-	pinfo->dma_addr = dma_addr;
+	pinfo->mem_addr = mem_addr;             /*  virtual address*/
+	pinfo->dma_addr = dma_addr;             /*  physical address*/
+	pinfo->mem_size = memsz;
 
 	pinfo->rx_buf = mem_addr;
 	pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos
diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c
index c9c3b1d..7c6b07a 100644
--- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c
+++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c
@@ -209,8 +209,10 @@
 
 	memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
 	    L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
-	if (is_con)
+	if (is_con) {
 		mem_addr = alloc_bootmem(memsz);
+		dma_addr = mem_addr;
+	}
 	else
 		mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr,
 					      GFP_KERNEL);
@@ -225,6 +227,7 @@
 	pinfo->dp_addr = dp_offset;
 	pinfo->mem_addr = mem_addr;
 	pinfo->dma_addr = dma_addr;
+	pinfo->mem_size = memsz;
 
 	pinfo->rx_buf = mem_addr;
 	pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos