diff mbox

[v2,8/8] tty/serial: add asm9260-serial driver

Message ID 1411325151-15107-7-git-send-email-linux@rempel-privat.de
State New
Headers show

Commit Message

Oleksij Rempel Sept. 21, 2014, 6:45 p.m. UTC
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
---
 drivers/tty/serial/Kconfig          |   17 +
 drivers/tty/serial/Makefile         |    1 +
 drivers/tty/serial/asm9260_serial.c | 1485 +++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h    |    2 +
 4 files changed, 1505 insertions(+)
 create mode 100644 drivers/tty/serial/asm9260_serial.c

Comments

Arnd Bergmann Sept. 22, 2014, 3:26 p.m. UTC | #1
On Sunday 21 September 2014 20:45:51 Oleksij Rempel wrote:
> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> ---
>  drivers/tty/serial/Kconfig          |   17 +
>  drivers/tty/serial/Makefile         |    1 +
>  drivers/tty/serial/asm9260_serial.c | 1485 +++++++++++++++++++++++++++++++++++
>  include/uapi/linux/serial_core.h    |    2 +
>  4 files changed, 1505 insertions(+)
>  create mode 100644 drivers/tty/serial/asm9260_serial.c
> 

Similar to the irqchip driver, this one in turns seems to be a duplicate
of drivers/tty/serial/mxs-auart.c. Please see if you can make that one
work instead of adding a new copy.

	Arnd
Oleksij Rempel Sept. 22, 2014, 4:04 p.m. UTC | #2
Am 22.09.2014 um 17:26 schrieb Arnd Bergmann:
> On Sunday 21 September 2014 20:45:51 Oleksij Rempel wrote:
>> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
>> ---
>>  drivers/tty/serial/Kconfig          |   17 +
>>  drivers/tty/serial/Makefile         |    1 +
>>  drivers/tty/serial/asm9260_serial.c | 1485 +++++++++++++++++++++++++++++++++++
>>  include/uapi/linux/serial_core.h    |    2 +
>>  4 files changed, 1505 insertions(+)
>>  create mode 100644 drivers/tty/serial/asm9260_serial.c
>>
> 
> Similar to the irqchip driver, this one in turns seems to be a duplicate
> of drivers/tty/serial/mxs-auart.c. Please see if you can make that one
> work instead of adding a new copy.

Ok, thank you for reviewing :)
Oleksij Rempel Sept. 24, 2014, 9:24 a.m. UTC | #3
Am 22.09.2014 um 17:26 schrieb Arnd Bergmann:
> On Sunday 21 September 2014 20:45:51 Oleksij Rempel wrote:
>> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
>> ---
>>  drivers/tty/serial/Kconfig          |   17 +
>>  drivers/tty/serial/Makefile         |    1 +
>>  drivers/tty/serial/asm9260_serial.c | 1485 +++++++++++++++++++++++++++++++++++
>>  include/uapi/linux/serial_core.h    |    2 +
>>  4 files changed, 1505 insertions(+)
>>  create mode 100644 drivers/tty/serial/asm9260_serial.c
>>
> 
> Similar to the irqchip driver, this one in turns seems to be a duplicate
> of drivers/tty/serial/mxs-auart.c. Please see if you can make that one
> work instead of adding a new copy.

Hmmm.... they are identical in some parts, but have some different
offsets, and in some cases different bitmask. I think it will look
messy. Asm9260 has some more extras: RS485, CTRL3, ISO7816. Should it be
moved to one file? Or may be better to create some common-dma.c and
common-pio.c libs?
Arnd Bergmann Sept. 24, 2014, 10:20 a.m. UTC | #4
On Wednesday 24 September 2014 11:24:37 Oleksij Rempel wrote:
> m 22.09.2014 um 17:26 schrieb Arnd Bergmann:
> > On Sunday 21 September 2014 20:45:51 Oleksij Rempel wrote:
> >> Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
> >> ---
> >>  drivers/tty/serial/Kconfig          |   17 +
> >>  drivers/tty/serial/Makefile         |    1 +
> >>  drivers/tty/serial/asm9260_serial.c | 1485 +++++++++++++++++++++++++++++++++++
> >>  include/uapi/linux/serial_core.h    |    2 +
> >>  4 files changed, 1505 insertions(+)
> >>  create mode 100644 drivers/tty/serial/asm9260_serial.c
> >>
> > 
> > Similar to the irqchip driver, this one in turns seems to be a duplicate
> > of drivers/tty/serial/mxs-auart.c. Please see if you can make that one
> > work instead of adding a new copy.
> 
> Hmmm.... they are identical in some parts, but have some different
> offsets, and in some cases different bitmask. 

It really depends on how much is different.

> I think it will look
> messy. Asm9260 has some more extras: RS485, CTRL3, ISO7816.

None of these are used in your driver though. It's possible that
mxs has the same registers but the driver author did not document
them.

> Should it be moved to one file? Or may be better to create some
> common-dma.c and common-pio.c libs?

I'd suggest trying to do it in one file first, only splitting things
out if it gets too messy.
You will probably have to replace the .driver_data in mxs_auart_devtype
with a structure that has all the differences, either using function
pointers for accessing the registers that are not identical, or using
some data that can be used to look up the the actual registers.

	Arnd
diff mbox

Patch

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 26cec64..9d80268 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1552,6 +1552,23 @@  config SERIAL_MEN_Z135
 	  This driver can also be build as a module. If so, the module will be called
 	  men_z135_uart.ko
 
+config SERIAL_ASM9260
+	tristate "ASM9260 controller serial support"
+	depends on MACH_ASM9260
+	select SERIAL_CORE
+	help
+		If you have an asm9260 based Base IO card
+		and wish to use the serial ports on this card, say Y.
+		Otherwise, say N.
+
+config SERIAL_ASM9260_CONSOLE
+	bool "ASM9260 console support"
+	depends on SERIAL_ASM9260
+	select SERIAL_CORE_CONSOLE
+	help
+		If you want to support serial console on borad asm9260
+		say Y. Otherwise, say N.
+
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 0080cc3..80940d4 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -95,3 +95,4 @@  obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
+obj-$(CONFIG_SERIAL_ASM9260) += asm9260_serial.o
diff --git a/drivers/tty/serial/asm9260_serial.c b/drivers/tty/serial/asm9260_serial.c
new file mode 100644
index 0000000..dc95478
--- /dev/null
+++ b/drivers/tty/serial/asm9260_serial.c
@@ -0,0 +1,1485 @@ 
+/*
+ * asm9260_serial.c Alphascale ASM9260 UART driver
+ *
+ * Mostly rewritten with irq_thread, clk and DT suppor:
+ * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ *		 2014 Cleaned up by Du Huanpeng <u74147@gmail.com>
+ *
+ * Copyright (C) 2013, Alphascale Tech. Co., Ltd.
+ * Initial code has been inspired/copied from atmel_serial.c (kernel v2.4) and
+ * adopted for asm9260 (kernel v2.4) by Chen Dongdong.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ * Note: This driver was tested and written for Alphascale ASM9260T. Since
+ * documentation for this SoC currently available only in Chinese, parts of it
+ * was recovered from similar devices. For example Alphascale ASAP1826 – which
+ * has identical offsets, but no support for RS485 and autoboud. And NXP
+ * Semiconductors LPC13xx (UM10375) – with identical registers and mostly
+ * identical functionality but different offsets.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#define SERIAL_ASM9260_MAJOR		204
+#define MINOR_START			64
+#define ASM9260_DEVICENAME		"ttyS"
+#define DRIVER_NAME			"asm9260_uart"
+#define ASM9260_UART_FIFOSIZE		16
+#define ASM9260_BUS_RATE		100000000
+#define ASM9260_MAX_UART		10
+
+#define UART_BAUD_DIVINT_MASK		((unsigned int)0x003FFFC0)
+#define UART_BAUD_DIVFRAC_MASK		((unsigned int)0x0000003F)
+#define	UART_BAUD_DIV_MAX		0x3FFFFF
+
+/*
+ * this device provide 4 offsets for each register:
+ * 0x0 - plain read write mode
+ * 0x4 - set mode, OR logic.
+ * 0x8 - clr mode, XOR logic.
+ * 0xc - togle mode.
+ */
+#define SET_REG				0x4
+#define CLR_REG				0x8
+
+/* RX ctrl register */
+#define HW_CTRL0			0x0000
+/* RW. Set to zero for normal operation. */
+#define BM_CTRL0_SFTRST			BIT(31)
+/*
+ * RW. 0 for normal operation; 1 gates all of the block level clocks off for
+ * miniminizing AC energy consumption.
+ */
+#define BM_CTRL0_CLKGATE		BIT(30)
+/*
+ * RW. Tell the UART to execute the RX DMA Command. The
+ * UART will clear this bit at the end of receive execution.
+ */
+#define BM_CTRL0_RXDMA_RUN		BIT(28)
+/* RW. 0 use FIFO for status register; 1 use DMA */
+#define BM_CTRL0_RXTO_SOURCE_STATUS	BIT(25)
+/*
+ * RW. RX TIMEOUT Enable. Valid for FIFO and DMA.
+ * Warning: If this bit is set to 0, the RX timeout will not affect receive DMA
+ * operation. If this bit is set to 1, a receive timeout will cause the receive
+ * DMA logic to terminate by filling the remaining DMA bytes with garbage data.
+ */
+#define BM_CTRL0_RXTO_ENABLE		BIT(24)
+/*
+ * RW. Receive Timeout Counter Value: number of 8-bit-time to wait before
+ * asserting timeout on the RX input. If the RXFIFO is not empty and the RX
+ * input is idle, then the watchdog counter will decrement each bit-time. Note
+ * 7-bit-time is added to the programmed value, so a value of zero will set
+ * the counter to 7-bit-time, a value of 0x1 gives 15-bit-time and so on. Also
+ * note that the counter is reloaded at the end of each frame, so if the frame
+ * is 10 bits long and the timeout counter value is zero, then timeout will
+ * occur (when FIFO is not empty) even if the RX input is not idle. The default
+ * value is 0x3 (31 bit-time).
+ */
+#define BM_CTRL0_RXTO_MASK		(0xff<<16)
+/* TIMEOUT = (100*7+1)*(1/BAUD) */
+#define BM_CTRL0_DEFAULT_RXTIMEOUT	(20<<16)
+/* RW. Number of bytes to receive. This must be a multiple of 4 */
+#define BM_CTRL0_RXDMA_COUNT_MASK	(0xffff<<0)
+
+/* TX ctrl register */
+#define HW_CTRL1			0x0010
+/*
+ * RW. Tell the UART to execute the TX DMA Command. The
+ * UART will clear this bit at the end of transmit execution.
+ */
+#define BM_CTRL1_TXDMA_RUN		BIT(28)
+/* RW. Number of bytes to transmit. */
+#define BM_CTRL1_TXDMA_COUNT_MASK	(0xffff << 0)
+
+#define HW_CTRL2			0x0020
+/*
+ * RW. Receive dma will terminate on error. (Cmd_end signal may not be asserted
+ * when this occurs.)
+ */
+#define BM_CTRL2_DMAONERROR		BIT(26)
+/*
+ * RW. Transmit DMA Enable. Data Register can be loaded with up to 4 bytes per
+ * write. TXFIFO must be enabled in TXDMA mode.
+ */
+#define BM_CTRL2_TXDMAE			BIT(25)
+/*
+ * RW. Receive DMA Enable. Data Register can be contain up to 4 bytes per read.
+ * RXFIFO must be enabled in RXDMA mode.
+ */
+#define BM_CTRL2_RXDMAE			BIT(24)
+/*
+ * RW. Receive Interrupt FIFO Level Select.
+ * The trigger points for the receive interrupt are as follows:
+ * ONE_EIGHTHS = 0x0 Trigger on FIFO full to at least 2 of 16 entries.
+ * ONE_QUARTER = 0x1 Trigger on FIFO full to at least 4 of 16 entries.
+ * ONE_HALF = 0x2 Trigger on FIFO full to at least 8 of 16 entries.
+ * THREE_QUARTERS = 0x3 Trigger on FIFO full to at least 12 of 16 entries.
+ * SEVEN_EIGHTHS = 0x4 Trigger on FIFO full to at least 14 of 16 entries.
+ */
+#define BM_CTRL2_RXIFLSEL		(7<<20)
+#define BM_CTRL2_DEFAULT_RXIFLSEL	(3<<20)
+/* RW. Same as RXIFLSEL */
+#define BM_CTRL2_TXIFLSEL		(7<<16)
+#define BM_CTRL2_DEFAULT_TXIFLSEL	(2<<16)
+/* RW. CTS Enable */
+#define BM_CTRL2_CTSE			BIT(15)
+/* RW. RTS Enable */
+#define BM_CTRL2_RTSE			BIT(14)
+/*
+ * RW. Manually trigger RTS. Works only if BM_CTRL2_RTSE = 0.
+ * When this bit is 1, the output is 0.
+ */
+#define BM_CTRL2_RTS			BIT(11)
+/* RW. Set DTR. When this bit is 1, the output is 0. */
+#define BM_CTRL2_DTR			BIT(10)
+/* RW. RX Enable */
+#define BM_CTRL2_RXE			BIT(9)
+/* RW. TX Enable */
+#define BM_CTRL2_TXE			BIT(8)
+/* RW. Loop Back Enable */
+#define BM_CTRL2_LBE			BIT(7)
+#define BM_CTRL2_PORT_ENABLE		BIT(0)
+
+#define HW_LINECTRL			0x0030
+#define BM_LCTRL_BAUD_DIVINT		(0xFFFF<<16)
+#define BM_LCTRL_BAUD_DIVFRA		(0x3F<<8)
+/*
+ * RW. Stick Parity Select. When bits 1, 2, and 7 of this register are set, the
+ * parity bit is transmitted and checked as a 0. When bits 1 and 7 are set,
+ * and bit 2 is 0, the parity bit is transmitted and checked as a 1. When this
+ * bit is cleared stick parity is disabled.
+ */
+#define BM_LCTRL_SPS			BIT(7)
+/* RW. Word length */
+#define BM_LCTRL_WLEN			(3<<5)
+#define BM_LCTRL_CHRL_5			(0<<5)
+#define BM_LCTRL_CHRL_6			(1<<5)
+#define BM_LCTRL_CHRL_7			(2<<5)
+#define BM_LCTRL_CHRL_8			(3<<5)
+/*
+ * RW. Enable FIFOs. If this bit is set to 1, transmit and receive FIFO buffers
+ * are enabled (FIFO mode). When cleared to 0, the FIFOs are disabled (character
+ * mode); that is, the FIFOs become 1-byte-deep holding registers.
+ */
+#define BM_LCTRL_FEN			BIT(4)
+/*
+ * RW. Two Stop Bits Select. If this bit is set to 1, two stop bits are
+ * transmitted at the end of the frame. The receive logic does not check for
+ * two stop bits being received.
+ */
+#define BM_LCTRL_STP2			BIT(3)
+#define BM_LCTRL_NBSTOP_1		(0<<3)
+#define BM_LCTRL_NBSTOP_2		(1<<3)
+/* RW. Even Parity Select. If disabled, then odd parity is performed. */
+#define BM_LCTRL_EPS			BIT(2)
+/* Parity Enable. */
+#define BM_LCTRL_PEN			BIT(1)
+#define BM_LCTRL_PAR_MARK		((3<<1) | (1<<7))
+#define BM_LCTRL_PAR_SPACE		((1<<1) | (1<<7))
+#define BM_LCTRL_PAR_ODD		((1<<1) | (0<<7))
+#define BM_LCTRL_PAR_EVEN		((3<<1) | (0<<7))
+#define BM_LCTRL_PAR_NONE		(0<<1)
+/*
+ * RW. Send Break. If this bit is set to 1, a low-level is continually output on
+ * the UARTTXD output, after completing transmission of the current character.
+ * For the proper execution of the break command, the software must set this bit
+ * for at least two complete frames. For normal use, this bit must be cleared
+ * to 0.
+ */
+#define BM_LCTRL_BREAK			BIT(0)
+
+/*
+ * Interrupt register.
+ * contains the interrupt enables and the interrupt status bits
+ */
+#define HW_INTR				0x0040
+/* Tx FIFO EMPTY Raw Interrupt enable */
+#define BM_INTR_TFEIEN			BIT(27)
+/* Overrun Error Interrupt Enable. */
+#define BM_INTR_OEIEN			BIT(26)
+/* Break Error Interrupt Enable. */
+#define BM_INTR_BEIEN			BIT(25)
+/* Parity Error Interrupt Enable. */
+#define BM_INTR_PEIEN			BIT(24)
+/* Framing Error Interrupt Enable. */
+#define BM_INTR_FEIEN			BIT(23)
+/*
+ * RW. Receive Timeout Interrupt Enable.
+ * If not set and FIFO is enabled, then RX will be triggered only
+ * if FIFO is full.
+ */
+#define BM_INTR_RTIEN			BIT(22)
+/* Transmit Interrupt Enable. */
+#define BM_INTR_TXIEN			BIT(21)
+/* Receive Interrupt Enable. */
+#define BM_INTR_RXIEN			BIT(20)
+/* nUARTDSR Modem Interrupt Enable. */
+#define BM_INTR_DSRMIEN			BIT(19)
+/* nUARTDCD Modem Interrupt Enable. */
+#define BM_INTR_DCDMIEN			BIT(18)
+/* nUARTCTS Modem Interrupt Enable. */
+#define BM_INTR_CTSMIEN			BIT(17)
+/* nUARTRI Modem Interrupt Enable. */
+#define BM_INTR_RIMIEN			BIT(16)
+/* Auto-Boud Timeout */
+#define BM_INTR_ABTO			BIT(13)
+#define BM_INTR_ABEO			BIT(12)
+/* Tx FIFO EMPTY Raw Interrupt state */
+#define BM_INTR_TFEIS			BIT(11)
+/* Overrun Error */
+#define BM_INTR_OEIS			BIT(10)
+/* Break Error */
+#define BM_INTR_BEIS			BIT(9)
+/* Parity Error */
+#define BM_INTR_PEIS			BIT(8)
+/* Framing Error */
+#define BM_INTR_FEIS			BIT(7)
+/* Receive Timeout */
+#define BM_INTR_RTIS			BIT(6)
+/* Transmit done */
+#define BM_INTR_TXIS			BIT(5)
+/* Receive done */
+#define BM_INTR_RXIS			BIT(4)
+#define BM_INTR_DSRMIS			BIT(3)
+#define BM_INTR_DCDMIS			BIT(2)
+#define BM_INTR_CTSMIS			BIT(1)
+#define BM_INTR_RIMIS			BIT(0)
+#define BM_INTR_DEF_MASK	(BM_INTR_RXIEN | BM_INTR_TXIEN | BM_INTR_RTIEN \
+		| BM_INTR_FEIEN | BM_INTR_PEIEN | BM_INTR_BEIEN | BM_INTR_OEIEN)
+#define BM_INTR_DEF_IS_MASK		(BM_INTR_DEF_MASK >> 16)
+#define BM_INTR_EN_MASK			(0x3fff0000)
+#define BM_INTR_IS_MASK			(0x00003fff)
+
+/*
+ * RW. In DMA mode, up to 4 Received/Transmit characters can be accessed at a
+ * time. In PIO mode, only one character can be accessed at a time. The status
+ * register contains the receive data flags and valid bits.
+ */
+#define HW_DATA				0x0050
+
+#define HW_STAT				0x0060
+/* RO. If 1, UARTAPP is present in this product. */
+#define BM_STAT_PRESENT			BIT(31)
+/* RO. If 1, HISPEED is present in this product. */
+#define BM_STAT_HISPEED			BIT(30)
+/* RO. UART Busy. */
+#define BM_STAT_BUSY			BIT(29)
+/* RO. Clear To Send. */
+#define BM_STAT_CTS			BIT(28)
+/* RO. Transmit FIFO/PIO Empty */
+#define BM_STAT_TXEMPTY			BIT(27)
+/* RO. Receive FIFO Full. */
+#define BM_STAT_RXFULL			BIT(26)
+/* RO. Transmit FIFO Full. */
+#define BM_STAT_TXFULL			BIT(25)
+/* RO. Receive FIFO Empty. */
+#define BM_STAT_RXEMPTY			BIT(24)
+/*
+ * RW. The invalid state of the last read of Receive Data. Each
+ * bit corresponds to one byte of the RX data. (1 = invalid.)
+ */
+#define BM_STAT_RXBYTE_INVALID_MASK	(0xf<<20)
+/*
+ * RO. Overrun Error. This bit is set to 1 if data is received and the FIFO is
+ * already full. This bit is cleared to 0 by any write to the Status Register.
+ * The FIFO contents remain valid since no further data is written when the
+ * FIFO is full; only the contents of the shift register are overwritten. The
+ * CPU must now read the data in order to empty the FIFO.
+ */
+#define BM_STAT_OVERRUNERR		BIT(19)
+/*
+ * RW. Break Error. For PIO mode, this is for the last character read from the
+ * data register. For DMA mode, it will be set to 1 if any received character
+ * for a particular RXDMA command had a Break Error. To clear this bit, write a
+ * zero to it. Note that clearing this bit does not affect the interrupt status,
+ * which must be cleared by writing the interrupt register.
+ */
+#define BM_STAT_BREAKERR		BIT(18)
+/* RW. Parity Error. Same as BREAKERR. */
+#define BM_STAT_PARITYERR		BIT(17)
+/* RW. Framing Erro. Same as BREAKERR. */
+#define BM_STAT_FRAMEERR		BIT(16)
+/* RO. Number of bytes received during a Receive DMA command. */
+#define BM_STAT_RXCOUNT_MASK		(0xffff<<0)
+
+/* RO. The UART Debug Register contains the state of the DMA signals. */
+#define HW_DEBUG			0x0070
+/* DMA Command Run Status */
+#define BM_DEBUG_TXDMARUN		BIT(5)
+#define BM_DEBUG_RXDMARUN		BIT(4)
+/* DMA Command End Status */
+#define BM_DEBUG_TXCMDEND		BIT(3)
+#define BM_DEBUG_RXCMDEND		BIT(2)
+/* DMA Request Status */
+#define BM_DEBUG_TXDMARQ		BIT(1)
+#define BM_DEBUG_RXDMARQ		BIT(0)
+
+#define HW_ILPR				0x0080
+
+#define HW_RS485CTRL			0x0090
+/*
+ * RW. This bit reverses the polarity of the direction control signal on the RTS
+ * (or DTR) pin.
+ * If 0, The direction control pin will be driven to logic ‘0’ when the
+ * transmitter has data to be sent. It will be driven to logic ‘1’ after the
+ * last bit of data has been transmitted.
+ */
+#define	BM_RS485CTRL_ONIV		BIT(5)
+/* RW. Enable Auto Direction Control. */
+#define	BM_RS485CTRL_DIR_CTRL		BIT(4)
+/*
+ * RW. If 0 and DIR_CTRL = 1, pin RTS is used for direction control.
+ * If 1 and DIR_CTRL = 1, pin DTR is used for direction control.
+ */
+#define	BM_RS485CTRL_PINSEL		BIT(3)
+/* RW. Enable Auto Address Detect (AAD). */
+#define	BM_RS485CTRL_AADEN		BIT(2)
+/* RW. Disable receiver. */
+#define	BM_RS485CTRL_RXDIS		BIT(1)
+/* RW. Enable RS-485/EIA-485 Normal Multidrop Mode (NMM) */
+#define	BM_RS485CTRL_RS485EN		BIT(0)
+
+#define HW_RS485ADRMATCH		0x00a0
+/* Contains the address match value. */
+#define BM_RS485ADRMATCH_MASK		(0xff<<0)
+
+#define HW_RS485DLY			0x00b0
+/*
+ * RW. Contains the direction control (RTS or DTR) delay value. This delay time
+ * is in periods of the baud clock.
+ */
+#define BM_RS485DLY_MASK		(0xff<<0)
+
+#define HW_AUTOBAUD			0x00c0
+/* WO. Auto-baud time-out interrupt clear bit. */
+#define BM_AUTOBAUD_ABTOIntClr		BIT(9)
+/* WO. End of auto-baud interrupt clear bit. */
+#define BM_AUTOBAUD_ABEOIntClr		BIT(8)
+/* Restart in case of timeout (counter restarts at next UART Rx falling edge) */
+#define BM_AUTOBAUD_AUTORESTART		BIT(2)
+/* Auto-baud mode select bit. 0 - Mode 0, 1 - Mode 1. */
+#define BM_AUTOBAUD_MODE		BIT(1)
+/*
+ * Auto-baud start (auto-baud is running). Auto-baud run bit. This bit is
+ * automatically cleared after auto-baud completion.
+ */
+#define BM_AUTOBAUD_START		BIT(0)
+
+#define HW_CTRL3			0x00d0
+#define BM_CTRL3_OUTCLK_DIV_MASK	(0xffff<<16)
+/*
+ * RW. Provide clk over OUTCLK pin. In case of asm9260 it can be configured on
+ * pins 137 and 144.
+ */
+#define BM_CTRL3_MASTERMODE		BIT(6)
+/* RW. Baud Rate Mode: 1 - Enable sync mode. 0 - async mode. */
+#define BM_CTRL3_SYNCMODE		BIT(4)
+/* RW. 1 - MSB bit send frist; 0 - LSB bit frist. */
+#define BM_CTRL3_MSBF			BIT(2)
+/* RW. 1 - sample rate = 8 x Baudrate; 0 - sample rate = 16 x Baudrate. */
+#define BM_CTRL3_BAUD8			BIT(1)
+/* RW. 1 - Set word lenght to 9bit. 0 - use BM_LCTRL_WLEN */
+#define BM_CTRL3_9BIT			BIT(0)
+
+#define HW_ISO7816_CTRL			0x00e0
+/* RW. Enable High Speed mode. */
+#define BM_ISO7816CTRL_HS		BIT(12)
+/* Disable Successive Receive NACK */
+#define BM_ISO7816CTRL_DS_NACK		BIT(8)
+#define BM_ISO7816CTRL_MAX_ITER_MASK	(0xff<<4)
+/* Receive NACK Inhibit */
+#define BM_ISO7816CTRL_INACK		BIT(3)
+#define BM_ISO7816CTRL_NEG_DATA		BIT(2)
+/* RW. 1 - ISO7816 mode; 0 - USART mode */
+#define BM_ISO7816CTRL_ENABLE		BIT(0)
+
+#define HW_ISO7816_ERRCNT		0x00f0
+/* Parity error counter. Will be cleared after reading */
+#define BM_ISO7816_NB_ERRORS_MASK	(0xff<<0)
+
+#define HW_ISO7816_STATUS		0x0100
+/* Max number of Repetitions Reached */
+#define BM_ISO7816_STAT_ITERATION	BIT(0)
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct asm9260_uart_port {
+	struct clk		*clk;		/* uart clock */
+	struct clk		*clk_ahb;
+	int			clk_on;
+	int			init_ok;
+	struct uart_port	uart;		/* uart */
+	struct serial_rs485	rs485;		/* rs485 settings */
+};
+
+static struct asm9260_uart_port *asm9260_ports;
+static int asm9260_ports_num;
+
+static void asm9260_start_rx(struct uart_port *uport);
+static void asm9260_tx_chars(struct uart_port *uport);
+static int asm9260_get_of_clks(struct asm9260_uart_port *port,
+		struct device_node *np);
+static void asm9260_enable_clks(struct asm9260_uart_port *port);
+static void asm9260_uart_of_enumerate(void);
+
+static inline struct asm9260_uart_port *
+to_asm9260_uart_port(struct uart_port *uart)
+{
+	return container_of(uart, struct asm9260_uart_port, uart);
+}
+
+static inline void asm9260_intr_mask(struct uart_port *uport)
+{
+	iowrite32(BM_INTR_DEF_MASK,
+			uport->membase + HW_INTR + CLR_REG);
+}
+
+static inline void asm9260_intr_unmask(struct uart_port *uport)
+{
+	iowrite32(BM_INTR_DEF_MASK,
+			uport->membase + HW_INTR + SET_REG);
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
+ */
+static u_int asm9260_tx_empty(struct uart_port *uport)
+{
+	return (ioread32(uport->membase + HW_STAT)
+			& BM_STAT_TXEMPTY) ? TIOCSER_TEMT : 0;
+}
+
+static void asm9260_set_mctrl(struct uart_port *uport, u_int mctrl)
+{
+}
+
+static u_int asm9260_get_mctrl(struct uart_port *uport)
+{
+	return 0;
+}
+
+/*
+ * Stop transmitting.
+ */
+static void asm9260_stop_tx(struct uart_port *uport)
+{
+	struct asm9260_uart_port *asm9260_port = to_asm9260_uart_port(uport);
+
+	/* seems like it was take over from atmel_serial.c
+	 * do we need it too? */
+	if ((asm9260_port->rs485.flags & SER_RS485_ENABLED) &&
+	    !(asm9260_port->rs485.flags & SER_RS485_RX_DURING_TX))
+		asm9260_start_rx(uport);
+}
+
+static void asm9260_start_tx(struct uart_port *uport)
+{
+	asm9260_tx_chars(uport);
+}
+
+static void asm9260_start_rx(struct uart_port *uport)
+{
+	/* enable receive */
+	iowrite32(BM_CTRL2_RXE,
+			uport->membase + HW_CTRL2 + SET_REG);
+}
+
+static void asm9260_stop_rx(struct uart_port *uport)
+{
+	/* disable receive */
+	iowrite32(BM_CTRL2_RXE,
+			uport->membase + HW_CTRL2 + CLR_REG);
+}
+
+static void asm9260_enable_ms(struct uart_port *uport)
+{
+}
+
+/*
+ * Control the transmission of a break signal
+ */
+static void asm9260_break_ctl(struct uart_port *uport, int break_state)
+{
+	if (break_state != 0)
+		iowrite32(BM_LCTRL_BREAK,
+				uport->membase + HW_LINECTRL + SET_REG);
+	else
+		iowrite32(BM_LCTRL_BREAK,
+				uport->membase + HW_LINECTRL + CLR_REG);
+}
+
+static void asm9260_rx_chars(struct uart_port *uport, unsigned int intr)
+{
+	unsigned int status, ch;
+
+	status = ioread32(uport->membase + HW_STAT);
+	while (!(status & BM_STAT_RXEMPTY)) {
+		unsigned int flg;
+
+		ch = ioread32(uport->membase + HW_DATA);
+
+		uport->icount.rx++;
+		flg = TTY_NORMAL;
+
+		if (unlikely(intr & (BM_INTR_PEIS | BM_INTR_FEIS
+				       | BM_INTR_OEIS | BM_INTR_BEIS))) {
+
+			/* clear error */
+			iowrite32(0, uport->membase + HW_STAT);
+
+			if (intr & BM_INTR_BEIS) {
+				uport->icount.brk++;
+				if (uart_handle_break(uport))
+					continue;
+			} else if (intr & BM_INTR_PEIS)
+				uport->icount.parity++;
+			else if (intr & BM_INTR_FEIS)
+				uport->icount.frame++;
+
+			if (intr & BM_INTR_OEIS)
+				uport->icount.overrun++;
+
+			intr &= uport->read_status_mask;
+
+			if (intr & BM_INTR_BEIS)
+				flg = TTY_BREAK;
+			else if (intr & BM_INTR_PEIS)
+				flg = TTY_PARITY;
+			else if (intr & BM_INTR_FEIS)
+				flg = TTY_FRAME;
+
+		}
+
+		if (uart_handle_sysrq_char(uport, ch))
+			continue;
+
+		uart_insert_char(uport, intr, BM_INTR_OEIS, ch, flg);
+		status = ioread32(uport->membase + HW_STAT);
+	}
+
+	tty_flip_buffer_push(&uport->state->port);
+}
+
+static void asm9260_tx_chars(struct uart_port *uport)
+{
+	struct circ_buf *xmit = &uport->state->xmit;
+
+	if (uport->x_char && !(ioread32(uport->membase + HW_STAT)
+				& BM_STAT_TXFULL)) {
+		iowrite32(uport->x_char, uport->membase + HW_DATA);
+		uport->icount.tx++;
+		uport->x_char = 0;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(uport))
+		return;
+
+	while (!uart_circ_empty(xmit)) {
+		if (ioread32(uport->membase + HW_STAT)
+				& BM_STAT_TXFULL)
+			break;
+
+		iowrite32(xmit->buf[xmit->tail],
+				uport->membase + HW_DATA);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		uport->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(uport);
+}
+
+static void
+asm9260_handle_receive(struct uart_port *uport, unsigned int pending)
+{
+	/* Interrupt receive */
+	if ((pending & BM_INTR_RXIS) || (pending & BM_INTR_RTIS))
+		asm9260_rx_chars(uport, pending);
+	else if (pending & BM_INTR_BEIS) {
+		/*
+		 * End of break detected. If it came along with a
+		 * character, asm9260_rx_chars will handle it.
+		 */
+		iowrite32(0, uport->membase + HW_STAT);
+	}
+}
+
+static void
+asm9260_handle_transmit(struct uart_port *uport, unsigned int pending)
+{
+	if (pending & BM_INTR_TXIS)
+		asm9260_tx_chars(uport);
+}
+
+/*
+ * Interrupt handler
+ */
+static irqreturn_t asm9260_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *uport = dev_id;
+	unsigned int status;
+
+	status = ioread32(uport->membase + HW_INTR);
+	status &= BM_INTR_DEF_IS_MASK;
+
+	asm9260_handle_receive(uport, status);
+	asm9260_handle_transmit(uport, status);
+
+	iowrite32(status,
+			uport->membase + HW_INTR + CLR_REG);
+
+	asm9260_intr_unmask(uport);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t asm9260_fast_int(int irq, void *dev_id)
+{
+	struct uart_port *uport = dev_id;
+	unsigned int status;
+
+	status = ioread32(uport->membase + HW_INTR);
+	if (!(status & BM_INTR_DEF_IS_MASK))
+		return IRQ_NONE;
+
+	asm9260_intr_mask(uport);
+	return IRQ_WAKE_THREAD;
+}
+
+/*
+ * Perform initialization and enable port for reception
+ */
+static int asm9260_startup(struct uart_port *uport)
+{
+	int retval;
+
+	/*
+	 * Ensure that no interrupts are enabled otherwise when
+	 * request_irq() is called we could get stuck trying to
+	 * handle an unexpected interrupt
+	 */
+	iowrite32(0, uport->membase + HW_INTR);
+
+	retval = devm_request_threaded_irq(uport->dev, uport->irq,
+			asm9260_fast_int, asm9260_interrupt, IRQF_SHARED,
+			dev_name(uport->dev), uport);
+	if (retval) {
+		dev_err(uport->dev, "Can't get irq\n");
+		return retval;
+	}
+
+	/* enable rx timeout */
+	iowrite32(BM_CTRL0_RXTO_MASK | BM_CTRL0_RXTO_SOURCE_STATUS,
+			uport->membase + HW_CTRL0 + CLR_REG);
+	iowrite32(BM_CTRL0_DEFAULT_RXTIMEOUT | BM_CTRL0_RXTO_ENABLE,
+			uport->membase + HW_CTRL0 + SET_REG);
+
+
+	/*
+	 * Finally, enable the serial port
+	 * enable tx & rx
+	 */
+	iowrite32(BM_CTRL2_RXIFLSEL | BM_CTRL2_TXIFLSEL,
+			uport->membase + HW_CTRL2 + CLR_REG);
+	iowrite32(BM_CTRL2_PORT_ENABLE | BM_CTRL2_TXE | BM_CTRL2_RXE |
+			BM_CTRL2_DEFAULT_TXIFLSEL |
+			BM_CTRL2_DEFAULT_RXIFLSEL,
+			uport->membase + HW_CTRL2);
+
+	asm9260_intr_unmask(uport);
+	return 0;
+}
+
+/*
+ * Disable the port
+ */
+static void asm9260_shutdown(struct uart_port *uport)
+{
+	int timeout = 10000;
+
+	/*wait for controller finish tx*/
+	while (!(ioread32(uport->membase + HW_STAT)
+				& BM_STAT_TXEMPTY)) {
+		if (--timeout < 0)
+			break;
+	}
+
+	/*
+	 * Ensure everything is stopped.
+	 */
+	asm9260_stop_tx(uport);
+	asm9260_stop_rx(uport);
+}
+
+/*
+ * Flush any TX data submitted for DMA. Called when the TX circular
+ * buffer is reset.
+ */
+static void asm9260_flush_buffer(struct uart_port *uport)
+{
+}
+
+/*
+ * Power / Clock management.
+ */
+static void asm9260_serial_pm(struct uart_port *uport, unsigned int state,
+			    unsigned int oldstate)
+{
+}
+
+static void asm9260_set_rs485(struct uart_port *uport)
+{
+	struct asm9260_uart_port *port = to_asm9260_uart_port(uport);
+	unsigned int rs485_ctrl;
+	/* set RS485 */
+	rs485_ctrl = ioread32(uport->membase + HW_RS485CTRL);
+
+	/* Resetting serial mode to RS232 (0x0) */
+	rs485_ctrl &= ~BM_RS485CTRL_RS485EN;
+
+	if (port->rs485.flags & SER_RS485_ENABLED) {
+		dev_dbg(uport->dev, "Setting UART to RS485\n");
+		if ((port->rs485.delay_rts_after_send) > 0) {
+			/*
+			 * delay is (rs485conf->delay_rts_after_send *
+			 *  Bit Period * 1/16)
+			 */
+			iowrite32(port->rs485.delay_rts_after_send,
+					uport->membase + HW_RS485DLY);
+		}
+
+		if ((port->rs485.flags & SER_RS485_RTS_ON_SEND) &&
+			!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND)) {
+			/*
+			 * Set logical level for RTS pin equal to 1 when
+			 * sending, and set logical level for RTS pin equal
+			 * to 0 after sending
+			 */
+			rs485_ctrl |= BM_RS485CTRL_ONIV;
+		} else if (!(port->rs485.flags & SER_RS485_RTS_ON_SEND) &&
+			(port->rs485.flags & SER_RS485_RTS_AFTER_SEND)) {
+			/*
+			 * Set logical level for RTS pin equal to 0 when
+			 * sending, and set logical level for RTS pin equal
+			 * to 1 after sending
+			 */
+			rs485_ctrl &= ~BM_RS485CTRL_ONIV;
+		} else
+			dev_info(uport->dev,
+					"Please view RS485CTRL register in datasheet for more details.\n");
+
+		/*
+		 * Enable RS485 and RTS is used to control direction
+		 * automatically,
+		 */
+		rs485_ctrl |= BM_RS485CTRL_RS485EN | BM_RS485CTRL_DIR_CTRL;
+		rs485_ctrl &= ~BM_RS485CTRL_PINSEL;
+
+		if (port->rs485.flags & SER_RS485_RX_DURING_TX)
+			dev_dbg(uport->dev, "hardware should support SER_RS485_RX_DURING_TX.\n");
+	} else
+		dev_dbg(uport->dev, "Setting UART to RS232\n");
+
+	iowrite32(rs485_ctrl, uport->membase + HW_RS485CTRL);
+}
+/*
+ * Change the port parameters
+ */
+static void asm9260_set_termios(struct uart_port *uport,
+		struct ktermios *termios, struct ktermios *old)
+{
+	unsigned int mode, baud;
+	unsigned int bauddivint, bauddivfrac;
+
+
+	asm9260_intr_mask(uport);
+
+	/*
+	 * We don't support modem control lines.
+	*/
+	termios->c_cflag &= ~(HUPCL | CMSPAR);
+	termios->c_cflag |= CLOCAL;
+
+	/* Get current mode register */
+	mode = ioread32(uport->membase + HW_LINECTRL);
+	mode &= ~(BM_LCTRL_PEN | BM_LCTRL_EPS
+			| BM_LCTRL_STP2 | BM_LCTRL_FEN
+			| BM_LCTRL_WLEN | BM_LCTRL_SPS
+			| BM_LCTRL_BAUD_DIVFRA | BM_LCTRL_BAUD_DIVINT);
+
+	baud = uart_get_baud_rate(uport, termios, old,
+			uport->uartclk * 4 / UART_BAUD_DIV_MAX,
+			uport->uartclk / 16);
+	bauddivint =
+		(((uport->uartclk << 2) / baud) & UART_BAUD_DIVINT_MASK) << 10;
+	bauddivfrac =
+		(((uport->uartclk << 2) / baud) & UART_BAUD_DIVFRAC_MASK) << 8;
+	/* byte size */
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		mode |= BM_LCTRL_CHRL_5;
+		break;
+	case CS6:
+		mode |= BM_LCTRL_CHRL_6;
+		break;
+	case CS7:
+		mode |= BM_LCTRL_CHRL_7;
+		break;
+	default:
+		mode |= BM_LCTRL_CHRL_8;
+		break;
+	}
+
+	/* enable fifo */
+	mode |= BM_LCTRL_FEN;
+
+	/* stop bits */
+	if (termios->c_cflag & CSTOPB)
+		mode |= BM_LCTRL_NBSTOP_2;
+	else
+		mode |= BM_LCTRL_NBSTOP_1;
+
+	/* parity */
+	if (termios->c_cflag & PARENB) {
+		/* Mark or Space parity */
+		if (termios->c_cflag & CMSPAR) {
+			if (termios->c_cflag & PARODD)
+				mode |= BM_LCTRL_PAR_MARK;
+			else
+				mode |= BM_LCTRL_PAR_SPACE;
+		} else if (termios->c_cflag & PARODD)
+			mode |= BM_LCTRL_PAR_ODD;
+		else
+			mode |= BM_LCTRL_PAR_EVEN;
+	} else
+		mode |= BM_LCTRL_PAR_NONE;
+
+	spin_lock(&uport->lock);
+
+	uport->read_status_mask = BM_INTR_OEIS;
+	if (termios->c_iflag & INPCK)
+		uport->read_status_mask |= (BM_INTR_FEIS | BM_INTR_PEIS);
+	if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
+		uport->read_status_mask |= BM_INTR_BEIS;
+
+	/*
+	 * Characters to ignore
+	 */
+	uport->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		uport->ignore_status_mask |=
+			(BM_INTR_FEIS  | BM_INTR_PEIS);
+	if (termios->c_iflag & IGNBRK) {
+		uport->ignore_status_mask |= BM_INTR_BEIS;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			uport->ignore_status_mask |= BM_INTR_OEIS;
+	}
+
+	/* update the per-port timeout */
+	uart_update_timeout(uport, termios->c_cflag, baud);
+
+	/* drain transmitter */
+	while (!(ioread32(uport->membase + HW_STAT)
+				& BM_STAT_TXEMPTY))
+		cpu_relax();
+
+	while (!(ioread32(uport->membase + HW_STAT)
+				& BM_STAT_RXEMPTY))
+		ioread32(uport->membase + HW_DATA);
+
+	asm9260_set_rs485(uport);
+
+	/* set hardware flow control */
+	if (termios->c_cflag & CRTSCTS)
+		iowrite32(BM_CTRL2_CTSE | BM_CTRL2_RTSE,
+				uport->membase + HW_CTRL2 + SET_REG);
+	else
+		iowrite32(BM_CTRL2_CTSE | BM_CTRL2_RTSE,
+				uport->membase + HW_CTRL2 + CLR_REG);
+
+	/* set the parity, stop bits, data size and baud rate*/
+	iowrite32(mode | bauddivint | bauddivfrac,
+			uport->membase + HW_LINECTRL);
+
+	/* CTS flow-control and modem-status interrupts */
+	if (UART_ENABLE_MS(uport, termios->c_cflag))
+		uport->ops->enable_ms(uport);
+
+	spin_unlock(&uport->lock);
+
+	dev_dbg(uport->dev,
+			"mode:0x%x, baud:%d, bauddivint:0x%x, bauddivfrac:0x%x, ctrl2:0x%x\n",
+			mode, baud, bauddivint, bauddivfrac,
+			ioread32(uport->membase + HW_CTRL2));
+
+	asm9260_intr_unmask(uport);
+}
+
+/*
+ * Return string describing the specified port
+ */
+static const char *asm9260_type(struct uart_port *uport)
+{
+	return (uport->type == PORT_ASM9260) ? DRIVER_NAME : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void asm9260_release_port(struct uart_port *uport)
+{
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int asm9260_request_port(struct uart_port *uport)
+{
+	return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void asm9260_config_port(struct uart_port *uport, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		uport->type = PORT_ASM9260;
+		asm9260_request_port(uport);
+	}
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int asm9260_verify_port(struct uart_port *uport,
+		struct serial_struct *ser)
+{
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_ASM9260)
+		ret = -EINVAL;
+	if (uport->irq != ser->irq)
+		ret = -EINVAL;
+	if (ser->io_type != SERIAL_IO_MEM)
+		ret = -EINVAL;
+	if (uport->uartclk / 16 != ser->baud_base)
+		ret = -EINVAL;
+	if ((void *)uport->mapbase != ser->iomem_base)
+		ret = -EINVAL;
+	if (uport->iobase != ser->port)
+		ret = -EINVAL;
+	if (ser->hub6 != 0)
+		ret = -EINVAL;
+	return ret;
+}
+
+/* Enable or disable the rs485 support */
+void asm9260_config_rs485(struct uart_port *uport,
+		struct serial_rs485 *rs485conf)
+{
+	struct asm9260_uart_port *port = to_asm9260_uart_port(uport);
+
+	asm9260_intr_mask(uport);
+	spin_lock(&uport->lock);
+
+	/* Disable interrupts */
+
+	port->rs485 = *rs485conf;
+
+	asm9260_set_rs485(uport);
+
+	/* Enable tx interrupts */
+	spin_unlock(&uport->lock);
+	asm9260_intr_unmask(uport);
+
+}
+
+static int asm9260_ioctl(struct uart_port *uport,
+		unsigned int cmd, unsigned long arg)
+{
+	struct serial_rs485 rs485conf;
+
+	switch (cmd) {
+	case TIOCSRS485:
+		if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg,
+					sizeof(rs485conf)))
+			return -EFAULT;
+
+		asm9260_config_rs485(uport, &rs485conf);
+		break;
+
+	case TIOCGRS485:
+		if (copy_to_user((struct serial_rs485 *) arg,
+					&(to_asm9260_uart_port(uport)->rs485),
+					sizeof(rs485conf)))
+			return -EFAULT;
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct uart_ops asm9260_pops = {
+	.tx_empty	= asm9260_tx_empty,
+	.set_mctrl	= asm9260_set_mctrl,
+	.get_mctrl	= asm9260_get_mctrl,
+	.stop_tx	= asm9260_stop_tx,
+	.start_tx	= asm9260_start_tx,
+	.stop_rx	= asm9260_stop_rx,
+	.enable_ms	= asm9260_enable_ms,
+	.break_ctl	= asm9260_break_ctl,
+	.startup	= asm9260_startup,
+	.shutdown	= asm9260_shutdown,
+	.flush_buffer	= asm9260_flush_buffer,
+	.set_termios	= asm9260_set_termios,
+	.type		= asm9260_type,
+	.release_port	= asm9260_release_port,
+	.request_port	= asm9260_request_port,
+	.config_port	= asm9260_config_port,
+	.verify_port	= asm9260_verify_port,
+	.pm		= asm9260_serial_pm,
+	.ioctl	= asm9260_ioctl,
+};
+
+#ifdef CONFIG_SERIAL_ASM9260_CONSOLE
+
+static struct asm9260_uart_port *get_asm9260_uart_port(int line);
+static struct console asm9260_console;
+
+static void asm9260_console_putchar(struct uart_port *uport, int ch)
+{
+	while (ioread32(uport->membase + HW_STAT)
+			& BM_STAT_TXFULL)
+		cpu_relax();
+	iowrite32(ch, uport->membase + HW_DATA);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void asm9260_console_write(struct console *co, const char *s,
+		u_int count)
+{
+	struct uart_port *uport;
+	struct asm9260_uart_port *port;
+	unsigned int status;
+	int locked = 1;
+
+	port = get_asm9260_uart_port(co->index);
+	uport = &port->uart;
+
+	asm9260_intr_mask(uport);
+
+	if (oops_in_progress)
+		locked = spin_trylock(&uport->lock);
+	else
+		spin_lock(&uport->lock);
+
+
+	uart_console_write(uport, s, count, asm9260_console_putchar);
+
+	/*
+	 * Finally, wait for transmitter to become empty
+	 * and restore IMR
+	 */
+	do {
+		status = ioread32(uport->membase + HW_STAT);
+	} while (!(status & BM_STAT_TXEMPTY));
+
+	if (locked)
+		spin_unlock(&uport->lock);
+
+	asm9260_intr_unmask(uport);
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init asm9260_console_get_options(struct uart_port *port,
+		int *baud, int *parity, int *bits)
+{
+	unsigned int mr, quot, linectrl, bauddivint, bauddivfrc;
+
+	/*
+	 * If the baud rate generator isn't running, the port wasn't
+	 * initialized by the boot loader.
+	 */
+	linectrl = ioread32(port->membase + HW_LINECTRL);
+	bauddivint = (linectrl & BM_LCTRL_BAUD_DIVINT) >> 16;
+	bauddivfrc = (linectrl & BM_LCTRL_BAUD_DIVFRA) >> 8;
+	quot = (bauddivint << 6) | bauddivfrc;
+
+	if (!quot)
+		return;
+
+	mr = linectrl & BM_LCTRL_WLEN;
+	if (mr == BM_LCTRL_CHRL_8)
+		*bits = 8;
+	else
+		*bits = 7;
+
+	mr = linectrl &
+		(BM_LCTRL_PEN | BM_LCTRL_EPS | BM_LCTRL_SPS);
+	if (mr == BM_LCTRL_PAR_EVEN)
+		*parity = 'e';
+	else if (mr == BM_LCTRL_PAR_ODD)
+		*parity = 'o';
+
+	/*
+	 * The serial core only rounds down when matching this to a
+	 * supported baud rate. Make sure we don't end up slightly
+	 * lower than one of those, as it would make us fall through
+	 * to a much lower baud rate than we really want.
+	 */
+	*baud = (port->uartclk * 4) / quot;
+}
+
+static int __init asm9260_console_setup(struct console *co, char *options)
+{
+	struct uart_port *uport;
+	struct asm9260_uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	asm9260_uart_of_enumerate();
+
+	port = get_asm9260_uart_port(co->index);
+	uport = &port->uart;
+
+	asm9260_enable_clks(port);
+
+	iowrite32(BM_CTRL2_TXE | BM_CTRL2_RXE | BM_CTRL2_PORT_ENABLE,
+			uport->membase + HW_CTRL2 + SET_REG);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		asm9260_console_get_options(uport, &baud, &parity, &bits);
+
+	return uart_set_options(uport, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver asm9260_uart;
+
+static struct console asm9260_console = {
+	.name		= ASM9260_DEVICENAME,
+	.write		= asm9260_console_write,
+	.device		= uart_console_device,
+	.setup		= asm9260_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &asm9260_uart,
+};
+
+#define ASM9260_CONSOLE_DEVICE	(&asm9260_console)
+
+/*
+ * Early console initialization (before VM subsystem initialized).
+ */
+static int __init asm9260_console_init(void)
+{
+	register_console(&asm9260_console);
+	return 0;
+}
+
+console_initcall(asm9260_console_init);
+#else
+#define ASM9260_CONSOLE_DEVICE	NULL
+#endif
+
+static struct uart_driver asm9260_uart = {
+	.owner			= THIS_MODULE,
+	.driver_name		= DRIVER_NAME,
+	.dev_name		= ASM9260_DEVICENAME,
+	.nr			= ASM9260_MAX_UART,
+	.cons			= ASM9260_CONSOLE_DEVICE,
+};
+
+/* Match table for of_platform binding */
+static struct of_device_id asm9260_of_match[] = {
+	{ .compatible = "alphascale,asm9260-uart", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, asm9260_of_match);
+
+static void asm9260_enable_clks(struct asm9260_uart_port *port)
+{
+	struct uart_port *uport = &port->uart;
+	int err;
+
+	if (port->clk_on)
+		return;
+
+	err = clk_set_rate(port->clk, ASM9260_BUS_RATE);
+	if (err)
+		dev_err(uport->dev, "Failed to set rate!\n");
+
+	err = clk_prepare_enable(port->clk);
+	if (err)
+		dev_err(uport->dev, "Failed to enable clk!\n");
+
+	err = clk_prepare_enable(port->clk_ahb);
+	if (err)
+		dev_err(uport->dev, "Failed to enable ahb_clk!\n");
+
+	uport->uartclk = clk_get_rate(port->clk);
+	port->clk_on = 1;
+}
+
+
+/* get devicetree clocks, if some thing wrong, warn about it */
+static int asm9260_get_of_clks(struct asm9260_uart_port *port,
+		struct device_node *np)
+{
+	int clk_idx = 0;
+
+	port->clk = of_clk_get(np, clk_idx);
+	if (IS_ERR(port->clk))
+		goto out_err;
+
+	/* configure AHB clock */
+	clk_idx = 1;
+	port->clk_ahb = of_clk_get(np, clk_idx);
+	if (IS_ERR(port->clk_ahb))
+		goto out_err;
+
+	return 0;
+out_err:
+	pr_err("%s: Failed to get clk (%i)\n", __func__, clk_idx);
+	return 1;
+}
+
+static int asm9260_get_count_of_nodes(const struct of_device_id *matches)
+{
+	int count = 0;
+	struct device_node *np;
+
+	for_each_matching_node(np, matches)
+		count++;
+
+	return count;
+}
+
+static struct asm9260_uart_port *get_asm9260_uart_port(int line)
+{
+	if (line >= asm9260_ports_num) {
+		pr_err("%s: Line number overflow. Check DeviceTree!!",
+				__func__);
+		return NULL;
+	}
+
+	return &asm9260_ports[line];
+}
+
+static void asm9260_uart_of_enumerate(void)
+{
+	static int enum_done;
+	struct device_node *np;
+
+	if (enum_done)
+		return;
+
+	asm9260_ports_num = asm9260_get_count_of_nodes(asm9260_of_match);
+	asm9260_ports = kcalloc(asm9260_ports_num,
+				sizeof(struct asm9260_uart_port), GFP_KERNEL);
+
+	for_each_matching_node(np, asm9260_of_match) {
+		struct uart_port *uport;
+		struct asm9260_uart_port *port;
+		int line;
+
+		line = of_alias_get_id(np, "serial");
+		if (line < 0) {
+			pr_err("Error! Devicetree has no \"serial\" aliases\n");
+			continue;
+		}
+
+		port = get_asm9260_uart_port(line);
+		if (!port)
+			continue;
+
+		uport = &port->uart;
+		if (asm9260_get_of_clks(port, np))
+			return;
+
+		uport->iotype	= UPIO_MEM;
+		uport->flags	= UPF_BOOT_AUTOCONF;
+		uport->ops	= &asm9260_pops;
+		uport->fifosize	= ASM9260_UART_FIFOSIZE;
+		uport->line	= line;
+
+		/* Since of_map don't do actual request of memory region,
+		 * it is save to use it for all, enabled and disabled uarts. */
+		uport->membase = of_iomap(np, 0);
+		if (!uport->membase) {
+			pr_err("Unable to map registers\n");
+			continue;
+		}
+		port->init_ok = 1;
+	}
+
+	enum_done = 1;
+}
+
+/*
+ * Configure the port from the platform device resource info.
+ */
+static void asm9260_init_port(struct asm9260_uart_port *asm9260_port,
+				      struct platform_device *pdev)
+{
+	struct uart_port *uport = &asm9260_port->uart;
+	struct device_node *np = pdev->dev.of_node;
+	struct resource res;
+
+	uport->dev = &pdev->dev;
+
+	uport->irq = irq_of_parse_and_map(np, 0);
+
+	of_address_to_resource(np, 0, &res);
+	if (!devm_request_mem_region(uport->dev, res.start,
+				resource_size(&res), dev_name(uport->dev)))
+		dev_err(uport->dev, "unable to request mem region\n");
+
+	uport->mapbase	= res.start;
+
+	asm9260_enable_clks(asm9260_port);
+}
+
+static int asm9260_serial_probe(struct platform_device *pdev)
+{
+	struct asm9260_uart_port *port;
+	struct device_node *np = pdev->dev.of_node;
+	int ret, line;
+
+	asm9260_uart_of_enumerate();
+
+	if (!np) {
+		dev_err(&pdev->dev, "Error! We support only DeviceTree!\n");
+		return -EPERM;
+	}
+
+	line = of_alias_get_id(np, "serial");
+	if (line < 0) {
+		dev_err(&pdev->dev,
+				"Error! Devicetree has no \"serial\" aliases\n");
+		return -EPERM;
+	}
+
+	port = get_asm9260_uart_port(line);
+
+	if (!port->init_ok)
+		dev_err(&pdev->dev, "Bad init!\n");
+
+	asm9260_init_port(port, pdev);
+
+	ret = uart_add_one_port(&asm9260_uart, &port->uart);
+	if (ret) {
+		dev_err(&pdev->dev, "Filed to add uart port\n");
+		goto err_add_port;
+	}
+
+	platform_set_drvdata(pdev, port);
+
+	return 0;
+
+err_add_port:
+	if (!uart_console(&port->uart)) {
+		clk_put(port->clk);
+		port->clk = NULL;
+	}
+	dev_err(&pdev->dev, "Filed to probe device\n");
+	return ret;
+}
+
+static int asm9260_serial_remove(struct platform_device *pdev)
+{
+	struct uart_port *port = platform_get_drvdata(pdev);
+	struct asm9260_uart_port *asm9260_port = to_asm9260_uart_port(port);
+	int ret = 0;
+
+	uart_remove_one_port(&asm9260_uart, port);
+	uart_unregister_driver(&asm9260_uart);
+
+	/* TODO: how should we handle clks here */
+	clk_put(asm9260_port->clk);
+
+	return ret;
+}
+
+static struct platform_driver asm9260_serial_driver = {
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(asm9260_of_match),
+	},
+	.probe		= asm9260_serial_probe,
+	.remove		= asm9260_serial_remove,
+};
+
+static int __init asm9260_serial_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&asm9260_uart);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&asm9260_serial_driver);
+	if (ret)
+		uart_unregister_driver(&asm9260_uart);
+
+	return ret;
+}
+
+static void __exit asm9260_serial_exit(void)
+{
+	platform_driver_unregister(&asm9260_serial_driver);
+	uart_unregister_driver(&asm9260_uart);
+}
+
+module_init(asm9260_serial_init);
+module_exit(asm9260_serial_exit);
+
+MODULE_DESCRIPTION("ASM9260 serial port driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 5820269..ed68009 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -244,4 +244,6 @@ 
 /* SC16IS74xx */
 #define PORT_SC16IS7XX   108
 
+/* Alpscale ASM9260 */
+#define PORT_ASM9260	109
 #endif /* _UAPILINUX_SERIAL_CORE_H */