1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2ab4382d2SGreg Kroah-Hartman /*
3ab4382d2SGreg Kroah-Hartman * altera_uart.c -- Altera UART driver
4ab4382d2SGreg Kroah-Hartman *
5ab4382d2SGreg Kroah-Hartman * Based on mcf.c -- Freescale ColdFire UART driver
6ab4382d2SGreg Kroah-Hartman *
7ab4382d2SGreg Kroah-Hartman * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
8ab4382d2SGreg Kroah-Hartman * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw>
9ab4382d2SGreg Kroah-Hartman * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch>
10ab4382d2SGreg Kroah-Hartman */
11ab4382d2SGreg Kroah-Hartman
12ab4382d2SGreg Kroah-Hartman #include <linux/kernel.h>
13ab4382d2SGreg Kroah-Hartman #include <linux/init.h>
14ab4382d2SGreg Kroah-Hartman #include <linux/timer.h>
15ab4382d2SGreg Kroah-Hartman #include <linux/interrupt.h>
16ab4382d2SGreg Kroah-Hartman #include <linux/module.h>
17ab4382d2SGreg Kroah-Hartman #include <linux/console.h>
18ab4382d2SGreg Kroah-Hartman #include <linux/tty.h>
19ab4382d2SGreg Kroah-Hartman #include <linux/tty_flip.h>
20ab4382d2SGreg Kroah-Hartman #include <linux/serial.h>
21ab4382d2SGreg Kroah-Hartman #include <linux/serial_core.h>
22ab4382d2SGreg Kroah-Hartman #include <linux/platform_device.h>
237c9325d7STobias Klauser #include <linux/of.h>
24ab4382d2SGreg Kroah-Hartman #include <linux/io.h>
25ab4382d2SGreg Kroah-Hartman #include <linux/altera_uart.h>
26ab4382d2SGreg Kroah-Hartman
27ab4382d2SGreg Kroah-Hartman #define DRV_NAME "altera_uart"
28ab4382d2SGreg Kroah-Hartman #define SERIAL_ALTERA_MAJOR 204
29ab4382d2SGreg Kroah-Hartman #define SERIAL_ALTERA_MINOR 213
30ab4382d2SGreg Kroah-Hartman
31ab4382d2SGreg Kroah-Hartman /*
32ab4382d2SGreg Kroah-Hartman * Altera UART register definitions according to the Nios UART datasheet:
33ab4382d2SGreg Kroah-Hartman * http://www.altera.com/literature/ds/ds_nios_uart.pdf
34ab4382d2SGreg Kroah-Hartman */
35ab4382d2SGreg Kroah-Hartman
36ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_SIZE 32
37ab4382d2SGreg Kroah-Hartman
38ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_RXDATA_REG 0
39ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_TXDATA_REG 4
40ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_REG 8
41ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_REG 12
42ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_DIVISOR_REG 16
43ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_EOP_REG 20
44ab4382d2SGreg Kroah-Hartman
45ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_PE_MSK 0x0001 /* parity error */
46ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_FE_MSK 0x0002 /* framing error */
47ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_BRK_MSK 0x0004 /* break */
48ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_ROE_MSK 0x0008 /* RX overrun error */
49ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_TOE_MSK 0x0010 /* TX overrun error */
50ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_TMT_MSK 0x0020 /* TX shift register state */
51ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_TRDY_MSK 0x0040 /* TX ready */
52ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_RRDY_MSK 0x0080 /* RX ready */
53ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_E_MSK 0x0100 /* exception condition */
54ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_DCTS_MSK 0x0400 /* CTS logic-level change */
55ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_CTS_MSK 0x0800 /* CTS logic state */
56ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_STATUS_EOP_MSK 0x1000 /* EOP written/read */
57ab4382d2SGreg Kroah-Hartman
58ab4382d2SGreg Kroah-Hartman /* Enable interrupt on... */
59ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_PE_MSK 0x0001 /* ...parity error */
60ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_FE_MSK 0x0002 /* ...framing error */
61ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_BRK_MSK 0x0004 /* ...break */
62ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_ROE_MSK 0x0008 /* ...RX overrun */
63ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_TOE_MSK 0x0010 /* ...TX overrun */
64ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_TMT_MSK 0x0020 /* ...TX shift register empty */
65ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_TRDY_MSK 0x0040 /* ...TX ready */
66ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_RRDY_MSK 0x0080 /* ...RX ready */
67ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_E_MSK 0x0100 /* ...exception*/
68ab4382d2SGreg Kroah-Hartman
69ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_TRBK_MSK 0x0200 /* TX break */
70ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_DCTS_MSK 0x0400 /* Interrupt on CTS change */
71ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_RTS_MSK 0x0800 /* RTS signal */
72ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONTROL_EOP_MSK 0x1000 /* Interrupt on EOP */
73ab4382d2SGreg Kroah-Hartman
74ab4382d2SGreg Kroah-Hartman /*
75ab4382d2SGreg Kroah-Hartman * Local per-uart structure.
76ab4382d2SGreg Kroah-Hartman */
77ab4382d2SGreg Kroah-Hartman struct altera_uart {
78ab4382d2SGreg Kroah-Hartman struct uart_port port;
79ab4382d2SGreg Kroah-Hartman struct timer_list tmr;
80ab4382d2SGreg Kroah-Hartman unsigned int sigs; /* Local copy of line sigs */
81ab4382d2SGreg Kroah-Hartman unsigned short imr; /* Local IMR mirror */
82ab4382d2SGreg Kroah-Hartman };
83ab4382d2SGreg Kroah-Hartman
altera_uart_readl(struct uart_port * port,int reg)84ab4382d2SGreg Kroah-Hartman static u32 altera_uart_readl(struct uart_port *port, int reg)
85ab4382d2SGreg Kroah-Hartman {
862780ad42STobias Klauser return readl(port->membase + (reg << port->regshift));
87ab4382d2SGreg Kroah-Hartman }
88ab4382d2SGreg Kroah-Hartman
altera_uart_writel(struct uart_port * port,u32 dat,int reg)89ab4382d2SGreg Kroah-Hartman static void altera_uart_writel(struct uart_port *port, u32 dat, int reg)
90ab4382d2SGreg Kroah-Hartman {
912780ad42STobias Klauser writel(dat, port->membase + (reg << port->regshift));
92ab4382d2SGreg Kroah-Hartman }
93ab4382d2SGreg Kroah-Hartman
altera_uart_tx_empty(struct uart_port * port)94ab4382d2SGreg Kroah-Hartman static unsigned int altera_uart_tx_empty(struct uart_port *port)
95ab4382d2SGreg Kroah-Hartman {
96ab4382d2SGreg Kroah-Hartman return (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
97ab4382d2SGreg Kroah-Hartman ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0;
98ab4382d2SGreg Kroah-Hartman }
99ab4382d2SGreg Kroah-Hartman
altera_uart_get_mctrl(struct uart_port * port)100ab4382d2SGreg Kroah-Hartman static unsigned int altera_uart_get_mctrl(struct uart_port *port)
101ab4382d2SGreg Kroah-Hartman {
102ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
103ab4382d2SGreg Kroah-Hartman unsigned int sigs;
104ab4382d2SGreg Kroah-Hartman
105ab4382d2SGreg Kroah-Hartman sigs = (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
106ab4382d2SGreg Kroah-Hartman ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0;
107ab4382d2SGreg Kroah-Hartman sigs |= (pp->sigs & TIOCM_RTS);
108ab4382d2SGreg Kroah-Hartman
109ab4382d2SGreg Kroah-Hartman return sigs;
110ab4382d2SGreg Kroah-Hartman }
111ab4382d2SGreg Kroah-Hartman
altera_uart_update_ctrl_reg(struct altera_uart * pp)1122ea6ad8bSUwe Kleine-König static void altera_uart_update_ctrl_reg(struct altera_uart *pp)
1132ea6ad8bSUwe Kleine-König {
1142ea6ad8bSUwe Kleine-König unsigned short imr = pp->imr;
1152ea6ad8bSUwe Kleine-König
1162ea6ad8bSUwe Kleine-König /*
1172ea6ad8bSUwe Kleine-König * If the device doesn't have an irq, ensure that the irq bits are
1182ea6ad8bSUwe Kleine-König * masked out to keep the irq line inactive.
1192ea6ad8bSUwe Kleine-König */
1202ea6ad8bSUwe Kleine-König if (!pp->port.irq)
1212ea6ad8bSUwe Kleine-König imr &= ALTERA_UART_CONTROL_TRBK_MSK | ALTERA_UART_CONTROL_RTS_MSK;
1222ea6ad8bSUwe Kleine-König
1232ea6ad8bSUwe Kleine-König altera_uart_writel(&pp->port, imr, ALTERA_UART_CONTROL_REG);
1242ea6ad8bSUwe Kleine-König }
1252ea6ad8bSUwe Kleine-König
altera_uart_set_mctrl(struct uart_port * port,unsigned int sigs)126ab4382d2SGreg Kroah-Hartman static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
127ab4382d2SGreg Kroah-Hartman {
128ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
129ab4382d2SGreg Kroah-Hartman
130ab4382d2SGreg Kroah-Hartman pp->sigs = sigs;
131ab4382d2SGreg Kroah-Hartman if (sigs & TIOCM_RTS)
132ab4382d2SGreg Kroah-Hartman pp->imr |= ALTERA_UART_CONTROL_RTS_MSK;
133ab4382d2SGreg Kroah-Hartman else
134ab4382d2SGreg Kroah-Hartman pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK;
1352ea6ad8bSUwe Kleine-König altera_uart_update_ctrl_reg(pp);
136ab4382d2SGreg Kroah-Hartman }
137ab4382d2SGreg Kroah-Hartman
altera_uart_start_tx(struct uart_port * port)138ab4382d2SGreg Kroah-Hartman static void altera_uart_start_tx(struct uart_port *port)
139ab4382d2SGreg Kroah-Hartman {
140ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
141ab4382d2SGreg Kroah-Hartman
142ab4382d2SGreg Kroah-Hartman pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK;
1432ea6ad8bSUwe Kleine-König altera_uart_update_ctrl_reg(pp);
144ab4382d2SGreg Kroah-Hartman }
145ab4382d2SGreg Kroah-Hartman
altera_uart_stop_tx(struct uart_port * port)146ab4382d2SGreg Kroah-Hartman static void altera_uart_stop_tx(struct uart_port *port)
147ab4382d2SGreg Kroah-Hartman {
148ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
149ab4382d2SGreg Kroah-Hartman
150ab4382d2SGreg Kroah-Hartman pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
1512ea6ad8bSUwe Kleine-König altera_uart_update_ctrl_reg(pp);
152ab4382d2SGreg Kroah-Hartman }
153ab4382d2SGreg Kroah-Hartman
altera_uart_stop_rx(struct uart_port * port)154ab4382d2SGreg Kroah-Hartman static void altera_uart_stop_rx(struct uart_port *port)
155ab4382d2SGreg Kroah-Hartman {
156ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
157ab4382d2SGreg Kroah-Hartman
158ab4382d2SGreg Kroah-Hartman pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK;
1592ea6ad8bSUwe Kleine-König altera_uart_update_ctrl_reg(pp);
160ab4382d2SGreg Kroah-Hartman }
161ab4382d2SGreg Kroah-Hartman
altera_uart_break_ctl(struct uart_port * port,int break_state)162ab4382d2SGreg Kroah-Hartman static void altera_uart_break_ctl(struct uart_port *port, int break_state)
163ab4382d2SGreg Kroah-Hartman {
164ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
165ab4382d2SGreg Kroah-Hartman unsigned long flags;
166ab4382d2SGreg Kroah-Hartman
167ab4382d2SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags);
168ab4382d2SGreg Kroah-Hartman if (break_state == -1)
169ab4382d2SGreg Kroah-Hartman pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK;
170ab4382d2SGreg Kroah-Hartman else
171ab4382d2SGreg Kroah-Hartman pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK;
1722ea6ad8bSUwe Kleine-König altera_uart_update_ctrl_reg(pp);
173ab4382d2SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags);
174ab4382d2SGreg Kroah-Hartman }
175ab4382d2SGreg Kroah-Hartman
altera_uart_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)176ab4382d2SGreg Kroah-Hartman static void altera_uart_set_termios(struct uart_port *port,
177ab4382d2SGreg Kroah-Hartman struct ktermios *termios,
178bec5b814SIlpo Järvinen const struct ktermios *old)
179ab4382d2SGreg Kroah-Hartman {
180ab4382d2SGreg Kroah-Hartman unsigned long flags;
181ab4382d2SGreg Kroah-Hartman unsigned int baud, baudclk;
182ab4382d2SGreg Kroah-Hartman
183ab4382d2SGreg Kroah-Hartman baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
184ab4382d2SGreg Kroah-Hartman baudclk = port->uartclk / baud;
185ab4382d2SGreg Kroah-Hartman
186ab4382d2SGreg Kroah-Hartman if (old)
187ab4382d2SGreg Kroah-Hartman tty_termios_copy_hw(termios, old);
188ab4382d2SGreg Kroah-Hartman tty_termios_encode_baud_rate(termios, baud, baud);
189ab4382d2SGreg Kroah-Hartman
190ab4382d2SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags);
191ab4382d2SGreg Kroah-Hartman uart_update_timeout(port, termios->c_cflag, baud);
192ab4382d2SGreg Kroah-Hartman altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG);
193ab4382d2SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags);
194ef8b9ddcSPeter Hurley
195ef8b9ddcSPeter Hurley /*
196ef8b9ddcSPeter Hurley * FIXME: port->read_status_mask and port->ignore_status_mask
197ef8b9ddcSPeter Hurley * need to be initialized based on termios settings for
198ef8b9ddcSPeter Hurley * INPCK, IGNBRK, IGNPAR, PARMRK, BRKINT
199ef8b9ddcSPeter Hurley */
200ab4382d2SGreg Kroah-Hartman }
201ab4382d2SGreg Kroah-Hartman
altera_uart_rx_chars(struct uart_port * port)2023af44d9bSJiri Slaby static void altera_uart_rx_chars(struct uart_port *port)
203ab4382d2SGreg Kroah-Hartman {
204ab4382d2SGreg Kroah-Hartman unsigned short status;
205*fd2b55f8SJiri Slaby u8 ch, flag;
206ab4382d2SGreg Kroah-Hartman
207ab4382d2SGreg Kroah-Hartman while ((status = altera_uart_readl(port, ALTERA_UART_STATUS_REG)) &
208ab4382d2SGreg Kroah-Hartman ALTERA_UART_STATUS_RRDY_MSK) {
209ab4382d2SGreg Kroah-Hartman ch = altera_uart_readl(port, ALTERA_UART_RXDATA_REG);
210ab4382d2SGreg Kroah-Hartman flag = TTY_NORMAL;
211ab4382d2SGreg Kroah-Hartman port->icount.rx++;
212ab4382d2SGreg Kroah-Hartman
213ab4382d2SGreg Kroah-Hartman if (status & ALTERA_UART_STATUS_E_MSK) {
214ab4382d2SGreg Kroah-Hartman altera_uart_writel(port, status,
215ab4382d2SGreg Kroah-Hartman ALTERA_UART_STATUS_REG);
216ab4382d2SGreg Kroah-Hartman
217ab4382d2SGreg Kroah-Hartman if (status & ALTERA_UART_STATUS_BRK_MSK) {
218ab4382d2SGreg Kroah-Hartman port->icount.brk++;
219ab4382d2SGreg Kroah-Hartman if (uart_handle_break(port))
220ab4382d2SGreg Kroah-Hartman continue;
221ab4382d2SGreg Kroah-Hartman } else if (status & ALTERA_UART_STATUS_PE_MSK) {
222ab4382d2SGreg Kroah-Hartman port->icount.parity++;
223ab4382d2SGreg Kroah-Hartman } else if (status & ALTERA_UART_STATUS_ROE_MSK) {
224ab4382d2SGreg Kroah-Hartman port->icount.overrun++;
225ab4382d2SGreg Kroah-Hartman } else if (status & ALTERA_UART_STATUS_FE_MSK) {
226ab4382d2SGreg Kroah-Hartman port->icount.frame++;
227ab4382d2SGreg Kroah-Hartman }
228ab4382d2SGreg Kroah-Hartman
229ab4382d2SGreg Kroah-Hartman status &= port->read_status_mask;
230ab4382d2SGreg Kroah-Hartman
231ab4382d2SGreg Kroah-Hartman if (status & ALTERA_UART_STATUS_BRK_MSK)
232ab4382d2SGreg Kroah-Hartman flag = TTY_BREAK;
233ab4382d2SGreg Kroah-Hartman else if (status & ALTERA_UART_STATUS_PE_MSK)
234ab4382d2SGreg Kroah-Hartman flag = TTY_PARITY;
235ab4382d2SGreg Kroah-Hartman else if (status & ALTERA_UART_STATUS_FE_MSK)
236ab4382d2SGreg Kroah-Hartman flag = TTY_FRAME;
237ab4382d2SGreg Kroah-Hartman }
238ab4382d2SGreg Kroah-Hartman
239ab4382d2SGreg Kroah-Hartman if (uart_handle_sysrq_char(port, ch))
240ab4382d2SGreg Kroah-Hartman continue;
241ab4382d2SGreg Kroah-Hartman uart_insert_char(port, status, ALTERA_UART_STATUS_ROE_MSK, ch,
242ab4382d2SGreg Kroah-Hartman flag);
243ab4382d2SGreg Kroah-Hartman }
244ab4382d2SGreg Kroah-Hartman
2452e124b4aSJiri Slaby tty_flip_buffer_push(&port->state->port);
246ab4382d2SGreg Kroah-Hartman }
247ab4382d2SGreg Kroah-Hartman
altera_uart_tx_chars(struct uart_port * port)2483af44d9bSJiri Slaby static void altera_uart_tx_chars(struct uart_port *port)
249ab4382d2SGreg Kroah-Hartman {
2502d141e68SJiri Slaby (SUSE) u8 ch;
251ab4382d2SGreg Kroah-Hartman
2522d141e68SJiri Slaby (SUSE) uart_port_tx(port, ch,
2532d141e68SJiri Slaby (SUSE) altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
2542d141e68SJiri Slaby (SUSE) ALTERA_UART_STATUS_TRDY_MSK,
2552d141e68SJiri Slaby (SUSE) altera_uart_writel(port, ch, ALTERA_UART_TXDATA_REG));
256ab4382d2SGreg Kroah-Hartman }
257ab4382d2SGreg Kroah-Hartman
altera_uart_interrupt(int irq,void * data)258ab4382d2SGreg Kroah-Hartman static irqreturn_t altera_uart_interrupt(int irq, void *data)
259ab4382d2SGreg Kroah-Hartman {
260ab4382d2SGreg Kroah-Hartman struct uart_port *port = data;
261ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
2621307c5d3SGabriel Somlo unsigned long flags;
263ab4382d2SGreg Kroah-Hartman unsigned int isr;
264ab4382d2SGreg Kroah-Hartman
265ab4382d2SGreg Kroah-Hartman isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr;
266ab4382d2SGreg Kroah-Hartman
2671307c5d3SGabriel Somlo spin_lock_irqsave(&port->lock, flags);
268ab4382d2SGreg Kroah-Hartman if (isr & ALTERA_UART_STATUS_RRDY_MSK)
2693af44d9bSJiri Slaby altera_uart_rx_chars(port);
270ab4382d2SGreg Kroah-Hartman if (isr & ALTERA_UART_STATUS_TRDY_MSK)
2713af44d9bSJiri Slaby altera_uart_tx_chars(port);
2721307c5d3SGabriel Somlo spin_unlock_irqrestore(&port->lock, flags);
273ab4382d2SGreg Kroah-Hartman
274ab4382d2SGreg Kroah-Hartman return IRQ_RETVAL(isr);
275ab4382d2SGreg Kroah-Hartman }
276ab4382d2SGreg Kroah-Hartman
altera_uart_timer(struct timer_list * t)277ad0cda7aSKees Cook static void altera_uart_timer(struct timer_list *t)
278ab4382d2SGreg Kroah-Hartman {
279ad0cda7aSKees Cook struct altera_uart *pp = from_timer(pp, t, tmr);
280ad0cda7aSKees Cook struct uart_port *port = &pp->port;
281ab4382d2SGreg Kroah-Hartman
282ab4382d2SGreg Kroah-Hartman altera_uart_interrupt(0, port);
283ab4382d2SGreg Kroah-Hartman mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
284ab4382d2SGreg Kroah-Hartman }
285ab4382d2SGreg Kroah-Hartman
altera_uart_config_port(struct uart_port * port,int flags)286ab4382d2SGreg Kroah-Hartman static void altera_uart_config_port(struct uart_port *port, int flags)
287ab4382d2SGreg Kroah-Hartman {
288ab4382d2SGreg Kroah-Hartman port->type = PORT_ALTERA_UART;
289ab4382d2SGreg Kroah-Hartman
290ab4382d2SGreg Kroah-Hartman /* Clear mask, so no surprise interrupts. */
291ab4382d2SGreg Kroah-Hartman altera_uart_writel(port, 0, ALTERA_UART_CONTROL_REG);
292ab4382d2SGreg Kroah-Hartman /* Clear status register */
293ab4382d2SGreg Kroah-Hartman altera_uart_writel(port, 0, ALTERA_UART_STATUS_REG);
294ab4382d2SGreg Kroah-Hartman }
295ab4382d2SGreg Kroah-Hartman
altera_uart_startup(struct uart_port * port)296ab4382d2SGreg Kroah-Hartman static int altera_uart_startup(struct uart_port *port)
297ab4382d2SGreg Kroah-Hartman {
298ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
299ab4382d2SGreg Kroah-Hartman unsigned long flags;
300ab4382d2SGreg Kroah-Hartman
301ab4382d2SGreg Kroah-Hartman if (!port->irq) {
302ad0cda7aSKees Cook timer_setup(&pp->tmr, altera_uart_timer, 0);
303ab4382d2SGreg Kroah-Hartman mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
304eefadcbcSUwe Kleine-König } else {
305eefadcbcSUwe Kleine-König int ret;
306ab4382d2SGreg Kroah-Hartman
3079cfb5c05SYong Zhang ret = request_irq(port->irq, altera_uart_interrupt, 0,
308ab4382d2SGreg Kroah-Hartman DRV_NAME, port);
309ab4382d2SGreg Kroah-Hartman if (ret) {
310ab4382d2SGreg Kroah-Hartman pr_err(DRV_NAME ": unable to attach Altera UART %d "
311ab4382d2SGreg Kroah-Hartman "interrupt vector=%d\n", port->line, port->irq);
312ab4382d2SGreg Kroah-Hartman return ret;
313ab4382d2SGreg Kroah-Hartman }
314eefadcbcSUwe Kleine-König }
315ab4382d2SGreg Kroah-Hartman
316ab4382d2SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags);
317ab4382d2SGreg Kroah-Hartman
318ab4382d2SGreg Kroah-Hartman /* Enable RX interrupts now */
319ab4382d2SGreg Kroah-Hartman pp->imr = ALTERA_UART_CONTROL_RRDY_MSK;
3202ea6ad8bSUwe Kleine-König altera_uart_update_ctrl_reg(pp);
321ab4382d2SGreg Kroah-Hartman
322ab4382d2SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags);
323ab4382d2SGreg Kroah-Hartman
324ab4382d2SGreg Kroah-Hartman return 0;
325ab4382d2SGreg Kroah-Hartman }
326ab4382d2SGreg Kroah-Hartman
altera_uart_shutdown(struct uart_port * port)327ab4382d2SGreg Kroah-Hartman static void altera_uart_shutdown(struct uart_port *port)
328ab4382d2SGreg Kroah-Hartman {
329ab4382d2SGreg Kroah-Hartman struct altera_uart *pp = container_of(port, struct altera_uart, port);
330ab4382d2SGreg Kroah-Hartman unsigned long flags;
331ab4382d2SGreg Kroah-Hartman
332ab4382d2SGreg Kroah-Hartman spin_lock_irqsave(&port->lock, flags);
333ab4382d2SGreg Kroah-Hartman
334ab4382d2SGreg Kroah-Hartman /* Disable all interrupts now */
335ab4382d2SGreg Kroah-Hartman pp->imr = 0;
3362ea6ad8bSUwe Kleine-König altera_uart_update_ctrl_reg(pp);
337ab4382d2SGreg Kroah-Hartman
338ab4382d2SGreg Kroah-Hartman spin_unlock_irqrestore(&port->lock, flags);
339ab4382d2SGreg Kroah-Hartman
340ab4382d2SGreg Kroah-Hartman if (port->irq)
341ab4382d2SGreg Kroah-Hartman free_irq(port->irq, port);
342ab4382d2SGreg Kroah-Hartman else
343ab4382d2SGreg Kroah-Hartman del_timer_sync(&pp->tmr);
344ab4382d2SGreg Kroah-Hartman }
345ab4382d2SGreg Kroah-Hartman
altera_uart_type(struct uart_port * port)346ab4382d2SGreg Kroah-Hartman static const char *altera_uart_type(struct uart_port *port)
347ab4382d2SGreg Kroah-Hartman {
348ab4382d2SGreg Kroah-Hartman return (port->type == PORT_ALTERA_UART) ? "Altera UART" : NULL;
349ab4382d2SGreg Kroah-Hartman }
350ab4382d2SGreg Kroah-Hartman
altera_uart_request_port(struct uart_port * port)351ab4382d2SGreg Kroah-Hartman static int altera_uart_request_port(struct uart_port *port)
352ab4382d2SGreg Kroah-Hartman {
353ab4382d2SGreg Kroah-Hartman /* UARTs always present */
354ab4382d2SGreg Kroah-Hartman return 0;
355ab4382d2SGreg Kroah-Hartman }
356ab4382d2SGreg Kroah-Hartman
altera_uart_release_port(struct uart_port * port)357ab4382d2SGreg Kroah-Hartman static void altera_uart_release_port(struct uart_port *port)
358ab4382d2SGreg Kroah-Hartman {
359ab4382d2SGreg Kroah-Hartman /* Nothing to release... */
360ab4382d2SGreg Kroah-Hartman }
361ab4382d2SGreg Kroah-Hartman
altera_uart_verify_port(struct uart_port * port,struct serial_struct * ser)362ab4382d2SGreg Kroah-Hartman static int altera_uart_verify_port(struct uart_port *port,
363ab4382d2SGreg Kroah-Hartman struct serial_struct *ser)
364ab4382d2SGreg Kroah-Hartman {
365ab4382d2SGreg Kroah-Hartman if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ALTERA_UART))
366ab4382d2SGreg Kroah-Hartman return -EINVAL;
367ab4382d2SGreg Kroah-Hartman return 0;
368ab4382d2SGreg Kroah-Hartman }
369ab4382d2SGreg Kroah-Hartman
370418a936eSTobias Klauser #ifdef CONFIG_CONSOLE_POLL
altera_uart_poll_get_char(struct uart_port * port)371418a936eSTobias Klauser static int altera_uart_poll_get_char(struct uart_port *port)
372418a936eSTobias Klauser {
373418a936eSTobias Klauser while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
374418a936eSTobias Klauser ALTERA_UART_STATUS_RRDY_MSK))
375418a936eSTobias Klauser cpu_relax();
376418a936eSTobias Klauser
377418a936eSTobias Klauser return altera_uart_readl(port, ALTERA_UART_RXDATA_REG);
378418a936eSTobias Klauser }
379418a936eSTobias Klauser
altera_uart_poll_put_char(struct uart_port * port,unsigned char c)380418a936eSTobias Klauser static void altera_uart_poll_put_char(struct uart_port *port, unsigned char c)
381418a936eSTobias Klauser {
382418a936eSTobias Klauser while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
383418a936eSTobias Klauser ALTERA_UART_STATUS_TRDY_MSK))
384418a936eSTobias Klauser cpu_relax();
385418a936eSTobias Klauser
386418a936eSTobias Klauser altera_uart_writel(port, c, ALTERA_UART_TXDATA_REG);
387418a936eSTobias Klauser }
388418a936eSTobias Klauser #endif
389418a936eSTobias Klauser
390ab4382d2SGreg Kroah-Hartman /*
391ab4382d2SGreg Kroah-Hartman * Define the basic serial functions we support.
392ab4382d2SGreg Kroah-Hartman */
39303bd797fSJulia Lawall static const struct uart_ops altera_uart_ops = {
394ab4382d2SGreg Kroah-Hartman .tx_empty = altera_uart_tx_empty,
395ab4382d2SGreg Kroah-Hartman .get_mctrl = altera_uart_get_mctrl,
396ab4382d2SGreg Kroah-Hartman .set_mctrl = altera_uart_set_mctrl,
397ab4382d2SGreg Kroah-Hartman .start_tx = altera_uart_start_tx,
398ab4382d2SGreg Kroah-Hartman .stop_tx = altera_uart_stop_tx,
399ab4382d2SGreg Kroah-Hartman .stop_rx = altera_uart_stop_rx,
400ab4382d2SGreg Kroah-Hartman .break_ctl = altera_uart_break_ctl,
401ab4382d2SGreg Kroah-Hartman .startup = altera_uart_startup,
402ab4382d2SGreg Kroah-Hartman .shutdown = altera_uart_shutdown,
403ab4382d2SGreg Kroah-Hartman .set_termios = altera_uart_set_termios,
404ab4382d2SGreg Kroah-Hartman .type = altera_uart_type,
405ab4382d2SGreg Kroah-Hartman .request_port = altera_uart_request_port,
406ab4382d2SGreg Kroah-Hartman .release_port = altera_uart_release_port,
407ab4382d2SGreg Kroah-Hartman .config_port = altera_uart_config_port,
408ab4382d2SGreg Kroah-Hartman .verify_port = altera_uart_verify_port,
409418a936eSTobias Klauser #ifdef CONFIG_CONSOLE_POLL
410418a936eSTobias Klauser .poll_get_char = altera_uart_poll_get_char,
411418a936eSTobias Klauser .poll_put_char = altera_uart_poll_put_char,
412418a936eSTobias Klauser #endif
413ab4382d2SGreg Kroah-Hartman };
414ab4382d2SGreg Kroah-Hartman
415ab4382d2SGreg Kroah-Hartman static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS];
416ab4382d2SGreg Kroah-Hartman
417ab4382d2SGreg Kroah-Hartman #if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE)
418ab4382d2SGreg Kroah-Hartman
altera_uart_console_putc(struct uart_port * port,unsigned char c)4193f8bab17SJiri Slaby static void altera_uart_console_putc(struct uart_port *port, unsigned char c)
420ab4382d2SGreg Kroah-Hartman {
421ab4382d2SGreg Kroah-Hartman while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
422ab4382d2SGreg Kroah-Hartman ALTERA_UART_STATUS_TRDY_MSK))
423ab4382d2SGreg Kroah-Hartman cpu_relax();
424ab4382d2SGreg Kroah-Hartman
4250e254963SUwe Kleine-König altera_uart_writel(port, c, ALTERA_UART_TXDATA_REG);
426ab4382d2SGreg Kroah-Hartman }
427ab4382d2SGreg Kroah-Hartman
altera_uart_console_write(struct console * co,const char * s,unsigned int count)428ab4382d2SGreg Kroah-Hartman static void altera_uart_console_write(struct console *co, const char *s,
429ab4382d2SGreg Kroah-Hartman unsigned int count)
430ab4382d2SGreg Kroah-Hartman {
431ab4382d2SGreg Kroah-Hartman struct uart_port *port = &(altera_uart_ports + co->index)->port;
432ab4382d2SGreg Kroah-Hartman
4332970b7f5SDaniel Thompson uart_console_write(port, s, count, altera_uart_console_putc);
434ab4382d2SGreg Kroah-Hartman }
435ab4382d2SGreg Kroah-Hartman
altera_uart_console_setup(struct console * co,char * options)436ab4382d2SGreg Kroah-Hartman static int __init altera_uart_console_setup(struct console *co, char *options)
437ab4382d2SGreg Kroah-Hartman {
438ab4382d2SGreg Kroah-Hartman struct uart_port *port;
439ab4382d2SGreg Kroah-Hartman int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE;
440ab4382d2SGreg Kroah-Hartman int bits = 8;
441ab4382d2SGreg Kroah-Hartman int parity = 'n';
442ab4382d2SGreg Kroah-Hartman int flow = 'n';
443ab4382d2SGreg Kroah-Hartman
444ab4382d2SGreg Kroah-Hartman if (co->index < 0 || co->index >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS)
445ab4382d2SGreg Kroah-Hartman return -EINVAL;
446ab4382d2SGreg Kroah-Hartman port = &altera_uart_ports[co->index].port;
447ab4382d2SGreg Kroah-Hartman if (!port->membase)
448ab4382d2SGreg Kroah-Hartman return -ENODEV;
449ab4382d2SGreg Kroah-Hartman
450ab4382d2SGreg Kroah-Hartman if (options)
451ab4382d2SGreg Kroah-Hartman uart_parse_options(options, &baud, &parity, &bits, &flow);
452ab4382d2SGreg Kroah-Hartman
453ab4382d2SGreg Kroah-Hartman return uart_set_options(port, co, baud, parity, bits, flow);
454ab4382d2SGreg Kroah-Hartman }
455ab4382d2SGreg Kroah-Hartman
456ab4382d2SGreg Kroah-Hartman static struct uart_driver altera_uart_driver;
457ab4382d2SGreg Kroah-Hartman
458ab4382d2SGreg Kroah-Hartman static struct console altera_uart_console = {
459ab4382d2SGreg Kroah-Hartman .name = "ttyAL",
460ab4382d2SGreg Kroah-Hartman .write = altera_uart_console_write,
461ab4382d2SGreg Kroah-Hartman .device = uart_console_device,
462ab4382d2SGreg Kroah-Hartman .setup = altera_uart_console_setup,
463ab4382d2SGreg Kroah-Hartman .flags = CON_PRINTBUFFER,
464ab4382d2SGreg Kroah-Hartman .index = -1,
465ab4382d2SGreg Kroah-Hartman .data = &altera_uart_driver,
466ab4382d2SGreg Kroah-Hartman };
467ab4382d2SGreg Kroah-Hartman
altera_uart_console_init(void)468ab4382d2SGreg Kroah-Hartman static int __init altera_uart_console_init(void)
469ab4382d2SGreg Kroah-Hartman {
470ab4382d2SGreg Kroah-Hartman register_console(&altera_uart_console);
471ab4382d2SGreg Kroah-Hartman return 0;
472ab4382d2SGreg Kroah-Hartman }
473ab4382d2SGreg Kroah-Hartman
474ab4382d2SGreg Kroah-Hartman console_initcall(altera_uart_console_init);
475ab4382d2SGreg Kroah-Hartman
476ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONSOLE (&altera_uart_console)
477ab4382d2SGreg Kroah-Hartman
altera_uart_earlycon_write(struct console * co,const char * s,unsigned int count)4784d9d7d89STobias Klauser static void altera_uart_earlycon_write(struct console *co, const char *s,
4794d9d7d89STobias Klauser unsigned int count)
4804d9d7d89STobias Klauser {
4814d9d7d89STobias Klauser struct earlycon_device *dev = co->data;
4824d9d7d89STobias Klauser
4834d9d7d89STobias Klauser uart_console_write(&dev->port, s, count, altera_uart_console_putc);
4844d9d7d89STobias Klauser }
4854d9d7d89STobias Klauser
altera_uart_earlycon_setup(struct earlycon_device * dev,const char * options)4864d9d7d89STobias Klauser static int __init altera_uart_earlycon_setup(struct earlycon_device *dev,
4874d9d7d89STobias Klauser const char *options)
4884d9d7d89STobias Klauser {
4894d9d7d89STobias Klauser struct uart_port *port = &dev->port;
4904d9d7d89STobias Klauser
4914d9d7d89STobias Klauser if (!port->membase)
4924d9d7d89STobias Klauser return -ENODEV;
4934d9d7d89STobias Klauser
4944d9d7d89STobias Klauser /* Enable RX interrupts now */
4950e254963SUwe Kleine-König altera_uart_writel(port, ALTERA_UART_CONTROL_RRDY_MSK,
4960e254963SUwe Kleine-König ALTERA_UART_CONTROL_REG);
4974d9d7d89STobias Klauser
4984d9d7d89STobias Klauser if (dev->baud) {
4994d9d7d89STobias Klauser unsigned int baudclk = port->uartclk / dev->baud;
5004d9d7d89STobias Klauser
5010e254963SUwe Kleine-König altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG);
5024d9d7d89STobias Klauser }
5034d9d7d89STobias Klauser
5044d9d7d89STobias Klauser dev->con->write = altera_uart_earlycon_write;
5054d9d7d89STobias Klauser return 0;
5064d9d7d89STobias Klauser }
5074d9d7d89STobias Klauser
5084d9d7d89STobias Klauser OF_EARLYCON_DECLARE(uart, "altr,uart-1.0", altera_uart_earlycon_setup);
5094d9d7d89STobias Klauser
510ab4382d2SGreg Kroah-Hartman #else
511ab4382d2SGreg Kroah-Hartman
512ab4382d2SGreg Kroah-Hartman #define ALTERA_UART_CONSOLE NULL
513ab4382d2SGreg Kroah-Hartman
5140656b1a9SValentin Rothberg #endif /* CONFIG_SERIAL_ALTERA_UART_CONSOLE */
515ab4382d2SGreg Kroah-Hartman
516ab4382d2SGreg Kroah-Hartman /*
517ab4382d2SGreg Kroah-Hartman * Define the altera_uart UART driver structure.
518ab4382d2SGreg Kroah-Hartman */
519ab4382d2SGreg Kroah-Hartman static struct uart_driver altera_uart_driver = {
520ab4382d2SGreg Kroah-Hartman .owner = THIS_MODULE,
521ab4382d2SGreg Kroah-Hartman .driver_name = DRV_NAME,
522ab4382d2SGreg Kroah-Hartman .dev_name = "ttyAL",
523ab4382d2SGreg Kroah-Hartman .major = SERIAL_ALTERA_MAJOR,
524ab4382d2SGreg Kroah-Hartman .minor = SERIAL_ALTERA_MINOR,
525ab4382d2SGreg Kroah-Hartman .nr = CONFIG_SERIAL_ALTERA_UART_MAXPORTS,
526ab4382d2SGreg Kroah-Hartman .cons = ALTERA_UART_CONSOLE,
527ab4382d2SGreg Kroah-Hartman };
528ab4382d2SGreg Kroah-Hartman
altera_uart_probe(struct platform_device * pdev)5299671f099SBill Pemberton static int altera_uart_probe(struct platform_device *pdev)
530ab4382d2SGreg Kroah-Hartman {
531574de559SJingoo Han struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev);
532ab4382d2SGreg Kroah-Hartman struct uart_port *port;
533ab4382d2SGreg Kroah-Hartman struct resource *res_mem;
534ab4382d2SGreg Kroah-Hartman int i = pdev->id;
5357c9325d7STobias Klauser int ret;
536ab4382d2SGreg Kroah-Hartman
537a664ec96STobias Klauser /* if id is -1 scan for a free id and use that one */
538a664ec96STobias Klauser if (i == -1) {
539a664ec96STobias Klauser for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS; i++)
540a664ec96STobias Klauser if (altera_uart_ports[i].port.mapbase == 0)
541a664ec96STobias Klauser break;
542a664ec96STobias Klauser }
543ab4382d2SGreg Kroah-Hartman
544a664ec96STobias Klauser if (i < 0 || i >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS)
545ab4382d2SGreg Kroah-Hartman return -EINVAL;
546ab4382d2SGreg Kroah-Hartman
547ab4382d2SGreg Kroah-Hartman port = &altera_uart_ports[i].port;
548ab4382d2SGreg Kroah-Hartman
549ab4382d2SGreg Kroah-Hartman res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
550ab4382d2SGreg Kroah-Hartman if (res_mem)
551ab4382d2SGreg Kroah-Hartman port->mapbase = res_mem->start;
552acede70dSYuriy Kozlov else if (platp)
553ab4382d2SGreg Kroah-Hartman port->mapbase = platp->mapbase;
554ab4382d2SGreg Kroah-Hartman else
555ab4382d2SGreg Kroah-Hartman return -EINVAL;
556ab4382d2SGreg Kroah-Hartman
55725753854SLad Prabhakar ret = platform_get_irq_optional(pdev, 0);
55825753854SLad Prabhakar if (ret < 0 && ret != -ENXIO)
55925753854SLad Prabhakar return ret;
56025753854SLad Prabhakar if (ret > 0)
56125753854SLad Prabhakar port->irq = ret;
562acede70dSYuriy Kozlov else if (platp)
563ab4382d2SGreg Kroah-Hartman port->irq = platp->irq;
564ab4382d2SGreg Kroah-Hartman
5657c9325d7STobias Klauser /* Check platform data first so we can override device node data */
5667c9325d7STobias Klauser if (platp)
5677c9325d7STobias Klauser port->uartclk = platp->uartclk;
5687c9325d7STobias Klauser else {
569d144aff1STobias Klauser ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
570d144aff1STobias Klauser &port->uartclk);
5717c9325d7STobias Klauser if (ret)
5727c9325d7STobias Klauser return ret;
5737c9325d7STobias Klauser }
5747c9325d7STobias Klauser
575ab4382d2SGreg Kroah-Hartman port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE);
576ab4382d2SGreg Kroah-Hartman if (!port->membase)
577ab4382d2SGreg Kroah-Hartman return -ENOMEM;
578ab4382d2SGreg Kroah-Hartman
5792780ad42STobias Klauser if (platp)
5802780ad42STobias Klauser port->regshift = platp->bus_shift;
5812780ad42STobias Klauser else
5822780ad42STobias Klauser port->regshift = 0;
5832780ad42STobias Klauser
584ab4382d2SGreg Kroah-Hartman port->line = i;
585ab4382d2SGreg Kroah-Hartman port->type = PORT_ALTERA_UART;
586ab4382d2SGreg Kroah-Hartman port->iotype = SERIAL_IO_MEM;
587ab4382d2SGreg Kroah-Hartman port->ops = &altera_uart_ops;
588ab4382d2SGreg Kroah-Hartman port->flags = UPF_BOOT_AUTOCONF;
589b820cd76SLey Foon Tan port->dev = &pdev->dev;
590ab4382d2SGreg Kroah-Hartman
5911a16afa2STobias Klauser platform_set_drvdata(pdev, port);
592a664ec96STobias Klauser
593ab4382d2SGreg Kroah-Hartman uart_add_one_port(&altera_uart_driver, port);
594ab4382d2SGreg Kroah-Hartman
595ab4382d2SGreg Kroah-Hartman return 0;
596ab4382d2SGreg Kroah-Hartman }
597ab4382d2SGreg Kroah-Hartman
altera_uart_remove(struct platform_device * pdev)598ae8d8a14SBill Pemberton static int altera_uart_remove(struct platform_device *pdev)
599ab4382d2SGreg Kroah-Hartman {
6001a16afa2STobias Klauser struct uart_port *port = platform_get_drvdata(pdev);
601ab4382d2SGreg Kroah-Hartman
602a664ec96STobias Klauser if (port) {
603ab4382d2SGreg Kroah-Hartman uart_remove_one_port(&altera_uart_driver, port);
604a664ec96STobias Klauser port->mapbase = 0;
60559fe2cc8STobias Klauser iounmap(port->membase);
606a664ec96STobias Klauser }
607e96fabd8STobias Klauser
608ab4382d2SGreg Kroah-Hartman return 0;
609ab4382d2SGreg Kroah-Hartman }
610ab4382d2SGreg Kroah-Hartman
6117c9325d7STobias Klauser #ifdef CONFIG_OF
6124d199a55STobias Klauser static const struct of_device_id altera_uart_match[] = {
6137c9325d7STobias Klauser { .compatible = "ALTR,uart-1.0", },
61413960b47SDinh Nguyen { .compatible = "altr,uart-1.0", },
6157c9325d7STobias Klauser {},
6167c9325d7STobias Klauser };
6177c9325d7STobias Klauser MODULE_DEVICE_TABLE(of, altera_uart_match);
6187c9325d7STobias Klauser #endif /* CONFIG_OF */
6197c9325d7STobias Klauser
620ab4382d2SGreg Kroah-Hartman static struct platform_driver altera_uart_platform_driver = {
621ab4382d2SGreg Kroah-Hartman .probe = altera_uart_probe,
6222d47b716SBill Pemberton .remove = altera_uart_remove,
623ab4382d2SGreg Kroah-Hartman .driver = {
624ab4382d2SGreg Kroah-Hartman .name = DRV_NAME,
62585888069SBen Dooks .of_match_table = of_match_ptr(altera_uart_match),
626ab4382d2SGreg Kroah-Hartman },
627ab4382d2SGreg Kroah-Hartman };
628ab4382d2SGreg Kroah-Hartman
altera_uart_init(void)629ab4382d2SGreg Kroah-Hartman static int __init altera_uart_init(void)
630ab4382d2SGreg Kroah-Hartman {
631ab4382d2SGreg Kroah-Hartman int rc;
632ab4382d2SGreg Kroah-Hartman
633ab4382d2SGreg Kroah-Hartman rc = uart_register_driver(&altera_uart_driver);
634ab4382d2SGreg Kroah-Hartman if (rc)
635ab4382d2SGreg Kroah-Hartman return rc;
636ab4382d2SGreg Kroah-Hartman rc = platform_driver_register(&altera_uart_platform_driver);
63761bc6559STobias Klauser if (rc)
638ab4382d2SGreg Kroah-Hartman uart_unregister_driver(&altera_uart_driver);
639ab4382d2SGreg Kroah-Hartman return rc;
640ab4382d2SGreg Kroah-Hartman }
641ab4382d2SGreg Kroah-Hartman
altera_uart_exit(void)642ab4382d2SGreg Kroah-Hartman static void __exit altera_uart_exit(void)
643ab4382d2SGreg Kroah-Hartman {
644ab4382d2SGreg Kroah-Hartman platform_driver_unregister(&altera_uart_platform_driver);
645ab4382d2SGreg Kroah-Hartman uart_unregister_driver(&altera_uart_driver);
646ab4382d2SGreg Kroah-Hartman }
647ab4382d2SGreg Kroah-Hartman
648ab4382d2SGreg Kroah-Hartman module_init(altera_uart_init);
649ab4382d2SGreg Kroah-Hartman module_exit(altera_uart_exit);
650ab4382d2SGreg Kroah-Hartman
651ab4382d2SGreg Kroah-Hartman MODULE_DESCRIPTION("Altera UART driver");
652ab4382d2SGreg Kroah-Hartman MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
653ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL");
654ab4382d2SGreg Kroah-Hartman MODULE_ALIAS("platform:" DRV_NAME);
655ab4382d2SGreg Kroah-Hartman MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_ALTERA_MAJOR);
656