1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2e36361d7SAndreas Färber /*
3e36361d7SAndreas Färber * Actions Semi Owl family serial console
4e36361d7SAndreas Färber *
5e36361d7SAndreas Färber * Copyright 2013 Actions Semi Inc.
6e36361d7SAndreas Färber * Author: Actions Semi, Inc.
7e36361d7SAndreas Färber *
8e36361d7SAndreas Färber * Copyright (c) 2016-2017 Andreas Färber
9e36361d7SAndreas Färber */
10e36361d7SAndreas Färber
11fc60a8b6SAndreas Färber #include <linux/clk.h>
12e36361d7SAndreas Färber #include <linux/console.h>
13e36361d7SAndreas Färber #include <linux/delay.h>
14e36361d7SAndreas Färber #include <linux/io.h>
159335e23dSCristian Ciocaltea #include <linux/iopoll.h>
16e36361d7SAndreas Färber #include <linux/module.h>
17e36361d7SAndreas Färber #include <linux/of.h>
18e36361d7SAndreas Färber #include <linux/platform_device.h>
19e36361d7SAndreas Färber #include <linux/serial.h>
20e36361d7SAndreas Färber #include <linux/serial_core.h>
21fc60a8b6SAndreas Färber #include <linux/tty.h>
22fc60a8b6SAndreas Färber #include <linux/tty_flip.h>
23fc60a8b6SAndreas Färber
24fc60a8b6SAndreas Färber #define OWL_UART_PORT_NUM 7
25fc60a8b6SAndreas Färber #define OWL_UART_DEV_NAME "ttyOWL"
26e36361d7SAndreas Färber
27e36361d7SAndreas Färber #define OWL_UART_CTL 0x000
28fc60a8b6SAndreas Färber #define OWL_UART_RXDAT 0x004
29e36361d7SAndreas Färber #define OWL_UART_TXDAT 0x008
30e36361d7SAndreas Färber #define OWL_UART_STAT 0x00c
31e36361d7SAndreas Färber
32fc60a8b6SAndreas Färber #define OWL_UART_CTL_DWLS_MASK GENMASK(1, 0)
33fc60a8b6SAndreas Färber #define OWL_UART_CTL_DWLS_5BITS (0x0 << 0)
34fc60a8b6SAndreas Färber #define OWL_UART_CTL_DWLS_6BITS (0x1 << 0)
35fc60a8b6SAndreas Färber #define OWL_UART_CTL_DWLS_7BITS (0x2 << 0)
36fc60a8b6SAndreas Färber #define OWL_UART_CTL_DWLS_8BITS (0x3 << 0)
37fc60a8b6SAndreas Färber #define OWL_UART_CTL_STPS_2BITS BIT(2)
38fc60a8b6SAndreas Färber #define OWL_UART_CTL_PRS_MASK GENMASK(6, 4)
39fc60a8b6SAndreas Färber #define OWL_UART_CTL_PRS_NONE (0x0 << 4)
40fc60a8b6SAndreas Färber #define OWL_UART_CTL_PRS_ODD (0x4 << 4)
41fc60a8b6SAndreas Färber #define OWL_UART_CTL_PRS_MARK (0x5 << 4)
42fc60a8b6SAndreas Färber #define OWL_UART_CTL_PRS_EVEN (0x6 << 4)
43fc60a8b6SAndreas Färber #define OWL_UART_CTL_PRS_SPACE (0x7 << 4)
44fc60a8b6SAndreas Färber #define OWL_UART_CTL_AFE BIT(12)
45e36361d7SAndreas Färber #define OWL_UART_CTL_TRFS_TX BIT(14)
46e36361d7SAndreas Färber #define OWL_UART_CTL_EN BIT(15)
47fc60a8b6SAndreas Färber #define OWL_UART_CTL_RXDE BIT(16)
48fc60a8b6SAndreas Färber #define OWL_UART_CTL_TXDE BIT(17)
49e36361d7SAndreas Färber #define OWL_UART_CTL_RXIE BIT(18)
50e36361d7SAndreas Färber #define OWL_UART_CTL_TXIE BIT(19)
51fc60a8b6SAndreas Färber #define OWL_UART_CTL_LBEN BIT(20)
52e36361d7SAndreas Färber
53e36361d7SAndreas Färber #define OWL_UART_STAT_RIP BIT(0)
54e36361d7SAndreas Färber #define OWL_UART_STAT_TIP BIT(1)
55fc60a8b6SAndreas Färber #define OWL_UART_STAT_RXER BIT(2)
56fc60a8b6SAndreas Färber #define OWL_UART_STAT_TFER BIT(3)
57fc60a8b6SAndreas Färber #define OWL_UART_STAT_RXST BIT(4)
58fc60a8b6SAndreas Färber #define OWL_UART_STAT_RFEM BIT(5)
59e36361d7SAndreas Färber #define OWL_UART_STAT_TFFU BIT(6)
60fc60a8b6SAndreas Färber #define OWL_UART_STAT_CTSS BIT(7)
61fc60a8b6SAndreas Färber #define OWL_UART_STAT_RTSS BIT(8)
62fc60a8b6SAndreas Färber #define OWL_UART_STAT_TFES BIT(10)
63fc60a8b6SAndreas Färber #define OWL_UART_STAT_TRFL_MASK GENMASK(16, 11)
64e36361d7SAndreas Färber #define OWL_UART_STAT_UTBB BIT(17)
65e36361d7SAndreas Färber
669335e23dSCristian Ciocaltea #define OWL_UART_POLL_USEC 5
679335e23dSCristian Ciocaltea #define OWL_UART_TIMEOUT_USEC 10000
689335e23dSCristian Ciocaltea
69fc60a8b6SAndreas Färber static struct uart_driver owl_uart_driver;
70fc60a8b6SAndreas Färber
71fc60a8b6SAndreas Färber struct owl_uart_info {
72fc60a8b6SAndreas Färber unsigned int tx_fifosize;
73fc60a8b6SAndreas Färber };
74fc60a8b6SAndreas Färber
75fc60a8b6SAndreas Färber struct owl_uart_port {
76fc60a8b6SAndreas Färber struct uart_port port;
77fc60a8b6SAndreas Färber struct clk *clk;
78fc60a8b6SAndreas Färber };
79fc60a8b6SAndreas Färber
80fc60a8b6SAndreas Färber #define to_owl_uart_port(prt) container_of(prt, struct owl_uart_port, prt)
81fc60a8b6SAndreas Färber
82fc60a8b6SAndreas Färber static struct owl_uart_port *owl_uart_ports[OWL_UART_PORT_NUM];
83fc60a8b6SAndreas Färber
owl_uart_write(struct uart_port * port,u32 val,unsigned int off)84e36361d7SAndreas Färber static inline void owl_uart_write(struct uart_port *port, u32 val, unsigned int off)
85e36361d7SAndreas Färber {
86e36361d7SAndreas Färber writel(val, port->membase + off);
87e36361d7SAndreas Färber }
88e36361d7SAndreas Färber
owl_uart_read(struct uart_port * port,unsigned int off)89e36361d7SAndreas Färber static inline u32 owl_uart_read(struct uart_port *port, unsigned int off)
90e36361d7SAndreas Färber {
91e36361d7SAndreas Färber return readl(port->membase + off);
92e36361d7SAndreas Färber }
93e36361d7SAndreas Färber
owl_uart_set_mctrl(struct uart_port * port,unsigned int mctrl)94fc60a8b6SAndreas Färber static void owl_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
95fc60a8b6SAndreas Färber {
96fc60a8b6SAndreas Färber u32 ctl;
97fc60a8b6SAndreas Färber
98fc60a8b6SAndreas Färber ctl = owl_uart_read(port, OWL_UART_CTL);
99fc60a8b6SAndreas Färber
100fc60a8b6SAndreas Färber if (mctrl & TIOCM_LOOP)
101fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_LBEN;
102fc60a8b6SAndreas Färber else
103fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_LBEN;
104fc60a8b6SAndreas Färber
105fc60a8b6SAndreas Färber owl_uart_write(port, ctl, OWL_UART_CTL);
106fc60a8b6SAndreas Färber }
107fc60a8b6SAndreas Färber
owl_uart_get_mctrl(struct uart_port * port)108fc60a8b6SAndreas Färber static unsigned int owl_uart_get_mctrl(struct uart_port *port)
109fc60a8b6SAndreas Färber {
110fc60a8b6SAndreas Färber unsigned int mctrl = TIOCM_CAR | TIOCM_DSR;
111fc60a8b6SAndreas Färber u32 stat, ctl;
112fc60a8b6SAndreas Färber
113fc60a8b6SAndreas Färber ctl = owl_uart_read(port, OWL_UART_CTL);
114fc60a8b6SAndreas Färber stat = owl_uart_read(port, OWL_UART_STAT);
115fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RTSS)
116fc60a8b6SAndreas Färber mctrl |= TIOCM_RTS;
117fc60a8b6SAndreas Färber if ((stat & OWL_UART_STAT_CTSS) || !(ctl & OWL_UART_CTL_AFE))
118fc60a8b6SAndreas Färber mctrl |= TIOCM_CTS;
119fc60a8b6SAndreas Färber return mctrl;
120fc60a8b6SAndreas Färber }
121fc60a8b6SAndreas Färber
owl_uart_tx_empty(struct uart_port * port)122fc60a8b6SAndreas Färber static unsigned int owl_uart_tx_empty(struct uart_port *port)
123fc60a8b6SAndreas Färber {
124fc60a8b6SAndreas Färber unsigned long flags;
125fc60a8b6SAndreas Färber u32 val;
126fc60a8b6SAndreas Färber unsigned int ret;
127fc60a8b6SAndreas Färber
12868dcb369SThomas Gleixner uart_port_lock_irqsave(port, &flags);
129fc60a8b6SAndreas Färber
130fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_STAT);
131fc60a8b6SAndreas Färber ret = (val & OWL_UART_STAT_TFES) ? TIOCSER_TEMT : 0;
132fc60a8b6SAndreas Färber
13368dcb369SThomas Gleixner uart_port_unlock_irqrestore(port, flags);
134fc60a8b6SAndreas Färber
135fc60a8b6SAndreas Färber return ret;
136fc60a8b6SAndreas Färber }
137fc60a8b6SAndreas Färber
owl_uart_stop_rx(struct uart_port * port)138fc60a8b6SAndreas Färber static void owl_uart_stop_rx(struct uart_port *port)
139fc60a8b6SAndreas Färber {
140fc60a8b6SAndreas Färber u32 val;
141fc60a8b6SAndreas Färber
142fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_CTL);
143fc60a8b6SAndreas Färber val &= ~(OWL_UART_CTL_RXIE | OWL_UART_CTL_RXDE);
144fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
145fc60a8b6SAndreas Färber
146fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_STAT);
147fc60a8b6SAndreas Färber val |= OWL_UART_STAT_RIP;
148fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_STAT);
149fc60a8b6SAndreas Färber }
150fc60a8b6SAndreas Färber
owl_uart_stop_tx(struct uart_port * port)151fc60a8b6SAndreas Färber static void owl_uart_stop_tx(struct uart_port *port)
152fc60a8b6SAndreas Färber {
153fc60a8b6SAndreas Färber u32 val;
154fc60a8b6SAndreas Färber
155fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_CTL);
156fc60a8b6SAndreas Färber val &= ~(OWL_UART_CTL_TXIE | OWL_UART_CTL_TXDE);
157fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
158fc60a8b6SAndreas Färber
159fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_STAT);
160fc60a8b6SAndreas Färber val |= OWL_UART_STAT_TIP;
161fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_STAT);
162fc60a8b6SAndreas Färber }
163fc60a8b6SAndreas Färber
owl_uart_start_tx(struct uart_port * port)164fc60a8b6SAndreas Färber static void owl_uart_start_tx(struct uart_port *port)
165fc60a8b6SAndreas Färber {
166fc60a8b6SAndreas Färber u32 val;
167fc60a8b6SAndreas Färber
168fc60a8b6SAndreas Färber if (uart_tx_stopped(port)) {
169fc60a8b6SAndreas Färber owl_uart_stop_tx(port);
170fc60a8b6SAndreas Färber return;
171fc60a8b6SAndreas Färber }
172fc60a8b6SAndreas Färber
173fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_STAT);
174fc60a8b6SAndreas Färber val |= OWL_UART_STAT_TIP;
175fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_STAT);
176fc60a8b6SAndreas Färber
177fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_CTL);
178fc60a8b6SAndreas Färber val |= OWL_UART_CTL_TXIE;
179fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
180fc60a8b6SAndreas Färber }
181fc60a8b6SAndreas Färber
owl_uart_send_chars(struct uart_port * port)182fc60a8b6SAndreas Färber static void owl_uart_send_chars(struct uart_port *port)
183fc60a8b6SAndreas Färber {
1842d141e68SJiri Slaby (SUSE) u8 ch;
185fc60a8b6SAndreas Färber
1862d141e68SJiri Slaby (SUSE) uart_port_tx(port, ch,
1872d141e68SJiri Slaby (SUSE) !(owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU),
1882d141e68SJiri Slaby (SUSE) owl_uart_write(port, ch, OWL_UART_TXDAT));
189fc60a8b6SAndreas Färber }
190fc60a8b6SAndreas Färber
owl_uart_receive_chars(struct uart_port * port)191fc60a8b6SAndreas Färber static void owl_uart_receive_chars(struct uart_port *port)
192fc60a8b6SAndreas Färber {
193fc60a8b6SAndreas Färber u32 stat, val;
194fc60a8b6SAndreas Färber
195fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_CTL);
196fc60a8b6SAndreas Färber val &= ~OWL_UART_CTL_TRFS_TX;
197fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
198fc60a8b6SAndreas Färber
199fc60a8b6SAndreas Färber stat = owl_uart_read(port, OWL_UART_STAT);
200fc60a8b6SAndreas Färber while (!(stat & OWL_UART_STAT_RFEM)) {
201fc60a8b6SAndreas Färber char flag = TTY_NORMAL;
2022af52148SSebastian Andrzej Siewior bool sysrq;
203fc60a8b6SAndreas Färber
204fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RXER)
205fc60a8b6SAndreas Färber port->icount.overrun++;
206fc60a8b6SAndreas Färber
207fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RXST) {
208fc60a8b6SAndreas Färber /* We are not able to distinguish the error type. */
209fc60a8b6SAndreas Färber port->icount.brk++;
210fc60a8b6SAndreas Färber port->icount.frame++;
211fc60a8b6SAndreas Färber
212fc60a8b6SAndreas Färber stat &= port->read_status_mask;
213fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RXST)
214fc60a8b6SAndreas Färber flag = TTY_PARITY;
215fc60a8b6SAndreas Färber } else
216fc60a8b6SAndreas Färber port->icount.rx++;
217fc60a8b6SAndreas Färber
218fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_RXDAT);
219fc60a8b6SAndreas Färber val &= 0xff;
220fc60a8b6SAndreas Färber
2212af52148SSebastian Andrzej Siewior sysrq = uart_prepare_sysrq_char(port, val);
2222af52148SSebastian Andrzej Siewior
2232af52148SSebastian Andrzej Siewior if (!sysrq && (stat & port->ignore_status_mask) == 0)
224fc60a8b6SAndreas Färber tty_insert_flip_char(&port->state->port, val, flag);
225fc60a8b6SAndreas Färber
226fc60a8b6SAndreas Färber stat = owl_uart_read(port, OWL_UART_STAT);
227fc60a8b6SAndreas Färber }
228fc60a8b6SAndreas Färber
229fc60a8b6SAndreas Färber tty_flip_buffer_push(&port->state->port);
230fc60a8b6SAndreas Färber }
231fc60a8b6SAndreas Färber
owl_uart_irq(int irq,void * dev_id)232fc60a8b6SAndreas Färber static irqreturn_t owl_uart_irq(int irq, void *dev_id)
233fc60a8b6SAndreas Färber {
234fc60a8b6SAndreas Färber struct uart_port *port = dev_id;
235fc60a8b6SAndreas Färber u32 stat;
236fc60a8b6SAndreas Färber
2372af52148SSebastian Andrzej Siewior uart_port_lock(port);
238fc60a8b6SAndreas Färber
239fc60a8b6SAndreas Färber stat = owl_uart_read(port, OWL_UART_STAT);
240fc60a8b6SAndreas Färber
241fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RIP)
242fc60a8b6SAndreas Färber owl_uart_receive_chars(port);
243fc60a8b6SAndreas Färber
244fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_TIP)
245fc60a8b6SAndreas Färber owl_uart_send_chars(port);
246fc60a8b6SAndreas Färber
247fc60a8b6SAndreas Färber stat = owl_uart_read(port, OWL_UART_STAT);
248fc60a8b6SAndreas Färber stat |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP;
249fc60a8b6SAndreas Färber owl_uart_write(port, stat, OWL_UART_STAT);
250fc60a8b6SAndreas Färber
2512af52148SSebastian Andrzej Siewior uart_unlock_and_check_sysrq(port);
252fc60a8b6SAndreas Färber
253fc60a8b6SAndreas Färber return IRQ_HANDLED;
254fc60a8b6SAndreas Färber }
255fc60a8b6SAndreas Färber
owl_uart_shutdown(struct uart_port * port)256fc60a8b6SAndreas Färber static void owl_uart_shutdown(struct uart_port *port)
257fc60a8b6SAndreas Färber {
258fc60a8b6SAndreas Färber u32 val;
259fc60a8b6SAndreas Färber unsigned long flags;
260fc60a8b6SAndreas Färber
26168dcb369SThomas Gleixner uart_port_lock_irqsave(port, &flags);
262fc60a8b6SAndreas Färber
263fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_CTL);
264fc60a8b6SAndreas Färber val &= ~(OWL_UART_CTL_TXIE | OWL_UART_CTL_RXIE
265fc60a8b6SAndreas Färber | OWL_UART_CTL_TXDE | OWL_UART_CTL_RXDE | OWL_UART_CTL_EN);
266fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
267fc60a8b6SAndreas Färber
26868dcb369SThomas Gleixner uart_port_unlock_irqrestore(port, flags);
269fc60a8b6SAndreas Färber
270fc60a8b6SAndreas Färber free_irq(port->irq, port);
271fc60a8b6SAndreas Färber }
272fc60a8b6SAndreas Färber
owl_uart_startup(struct uart_port * port)273fc60a8b6SAndreas Färber static int owl_uart_startup(struct uart_port *port)
274fc60a8b6SAndreas Färber {
275fc60a8b6SAndreas Färber u32 val;
276fc60a8b6SAndreas Färber unsigned long flags;
277fc60a8b6SAndreas Färber int ret;
278fc60a8b6SAndreas Färber
279fc60a8b6SAndreas Färber ret = request_irq(port->irq, owl_uart_irq, IRQF_TRIGGER_HIGH,
280fc60a8b6SAndreas Färber "owl-uart", port);
281fc60a8b6SAndreas Färber if (ret)
282fc60a8b6SAndreas Färber return ret;
283fc60a8b6SAndreas Färber
28468dcb369SThomas Gleixner uart_port_lock_irqsave(port, &flags);
285fc60a8b6SAndreas Färber
286fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_STAT);
287fc60a8b6SAndreas Färber val |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP
288fc60a8b6SAndreas Färber | OWL_UART_STAT_RXER | OWL_UART_STAT_TFER | OWL_UART_STAT_RXST;
289fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_STAT);
290fc60a8b6SAndreas Färber
291fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_CTL);
292fc60a8b6SAndreas Färber val |= OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE;
293fc60a8b6SAndreas Färber val |= OWL_UART_CTL_EN;
294fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
295fc60a8b6SAndreas Färber
29668dcb369SThomas Gleixner uart_port_unlock_irqrestore(port, flags);
297fc60a8b6SAndreas Färber
298fc60a8b6SAndreas Färber return 0;
299fc60a8b6SAndreas Färber }
300fc60a8b6SAndreas Färber
owl_uart_change_baudrate(struct owl_uart_port * owl_port,unsigned long baud)301fc60a8b6SAndreas Färber static void owl_uart_change_baudrate(struct owl_uart_port *owl_port,
302fc60a8b6SAndreas Färber unsigned long baud)
303fc60a8b6SAndreas Färber {
304fc60a8b6SAndreas Färber clk_set_rate(owl_port->clk, baud * 8);
305fc60a8b6SAndreas Färber }
306fc60a8b6SAndreas Färber
owl_uart_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)307fc60a8b6SAndreas Färber static void owl_uart_set_termios(struct uart_port *port,
308fc60a8b6SAndreas Färber struct ktermios *termios,
309bec5b814SIlpo Järvinen const struct ktermios *old)
310fc60a8b6SAndreas Färber {
311fc60a8b6SAndreas Färber struct owl_uart_port *owl_port = to_owl_uart_port(port);
312fc60a8b6SAndreas Färber unsigned int baud;
313fc60a8b6SAndreas Färber u32 ctl;
314fc60a8b6SAndreas Färber unsigned long flags;
315fc60a8b6SAndreas Färber
31668dcb369SThomas Gleixner uart_port_lock_irqsave(port, &flags);
317fc60a8b6SAndreas Färber
318fc60a8b6SAndreas Färber ctl = owl_uart_read(port, OWL_UART_CTL);
319fc60a8b6SAndreas Färber
320fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_DWLS_MASK;
321fc60a8b6SAndreas Färber switch (termios->c_cflag & CSIZE) {
322fc60a8b6SAndreas Färber case CS5:
323fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_DWLS_5BITS;
324fc60a8b6SAndreas Färber break;
325fc60a8b6SAndreas Färber case CS6:
326fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_DWLS_6BITS;
327fc60a8b6SAndreas Färber break;
328fc60a8b6SAndreas Färber case CS7:
329fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_DWLS_7BITS;
330fc60a8b6SAndreas Färber break;
331fc60a8b6SAndreas Färber case CS8:
332fc60a8b6SAndreas Färber default:
333fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_DWLS_8BITS;
334fc60a8b6SAndreas Färber break;
335fc60a8b6SAndreas Färber }
336fc60a8b6SAndreas Färber
337fc60a8b6SAndreas Färber if (termios->c_cflag & CSTOPB)
338fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_STPS_2BITS;
339fc60a8b6SAndreas Färber else
340fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_STPS_2BITS;
341fc60a8b6SAndreas Färber
342fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_PRS_MASK;
343fc60a8b6SAndreas Färber if (termios->c_cflag & PARENB) {
344fc60a8b6SAndreas Färber if (termios->c_cflag & CMSPAR) {
345fc60a8b6SAndreas Färber if (termios->c_cflag & PARODD)
346fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_MARK;
347fc60a8b6SAndreas Färber else
348fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_SPACE;
349fc60a8b6SAndreas Färber } else if (termios->c_cflag & PARODD)
350fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_ODD;
351fc60a8b6SAndreas Färber else
352fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_EVEN;
353fc60a8b6SAndreas Färber } else
354fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_NONE;
355fc60a8b6SAndreas Färber
356fc60a8b6SAndreas Färber if (termios->c_cflag & CRTSCTS)
357fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_AFE;
358fc60a8b6SAndreas Färber else
359fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_AFE;
360fc60a8b6SAndreas Färber
361fc60a8b6SAndreas Färber owl_uart_write(port, ctl, OWL_UART_CTL);
362fc60a8b6SAndreas Färber
363fc60a8b6SAndreas Färber baud = uart_get_baud_rate(port, termios, old, 9600, 3200000);
364fc60a8b6SAndreas Färber owl_uart_change_baudrate(owl_port, baud);
365fc60a8b6SAndreas Färber
366fc60a8b6SAndreas Färber /* Don't rewrite B0 */
367fc60a8b6SAndreas Färber if (tty_termios_baud_rate(termios))
368fc60a8b6SAndreas Färber tty_termios_encode_baud_rate(termios, baud, baud);
369fc60a8b6SAndreas Färber
370fc60a8b6SAndreas Färber port->read_status_mask |= OWL_UART_STAT_RXER;
371fc60a8b6SAndreas Färber if (termios->c_iflag & INPCK)
372fc60a8b6SAndreas Färber port->read_status_mask |= OWL_UART_STAT_RXST;
373fc60a8b6SAndreas Färber
374fc60a8b6SAndreas Färber uart_update_timeout(port, termios->c_cflag, baud);
375fc60a8b6SAndreas Färber
37668dcb369SThomas Gleixner uart_port_unlock_irqrestore(port, flags);
377fc60a8b6SAndreas Färber }
378fc60a8b6SAndreas Färber
owl_uart_release_port(struct uart_port * port)379fc60a8b6SAndreas Färber static void owl_uart_release_port(struct uart_port *port)
380fc60a8b6SAndreas Färber {
381fc60a8b6SAndreas Färber struct platform_device *pdev = to_platform_device(port->dev);
382fc60a8b6SAndreas Färber struct resource *res;
383fc60a8b6SAndreas Färber
384fc60a8b6SAndreas Färber res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
385fc60a8b6SAndreas Färber if (!res)
386fc60a8b6SAndreas Färber return;
387fc60a8b6SAndreas Färber
388fc60a8b6SAndreas Färber if (port->flags & UPF_IOREMAP) {
389fc60a8b6SAndreas Färber devm_release_mem_region(port->dev, port->mapbase,
390fc60a8b6SAndreas Färber resource_size(res));
391fc60a8b6SAndreas Färber devm_iounmap(port->dev, port->membase);
392fc60a8b6SAndreas Färber port->membase = NULL;
393fc60a8b6SAndreas Färber }
394fc60a8b6SAndreas Färber }
395fc60a8b6SAndreas Färber
owl_uart_request_port(struct uart_port * port)396fc60a8b6SAndreas Färber static int owl_uart_request_port(struct uart_port *port)
397fc60a8b6SAndreas Färber {
398fc60a8b6SAndreas Färber struct platform_device *pdev = to_platform_device(port->dev);
399fc60a8b6SAndreas Färber struct resource *res;
400fc60a8b6SAndreas Färber
401fc60a8b6SAndreas Färber res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
402fc60a8b6SAndreas Färber if (!res)
403fc60a8b6SAndreas Färber return -ENXIO;
404fc60a8b6SAndreas Färber
405fc60a8b6SAndreas Färber if (!devm_request_mem_region(port->dev, port->mapbase,
406fc60a8b6SAndreas Färber resource_size(res), dev_name(port->dev)))
407fc60a8b6SAndreas Färber return -EBUSY;
408fc60a8b6SAndreas Färber
409fc60a8b6SAndreas Färber if (port->flags & UPF_IOREMAP) {
4104bdc0d67SChristoph Hellwig port->membase = devm_ioremap(port->dev, port->mapbase,
411fc60a8b6SAndreas Färber resource_size(res));
412fc60a8b6SAndreas Färber if (!port->membase)
413fc60a8b6SAndreas Färber return -EBUSY;
414fc60a8b6SAndreas Färber }
415fc60a8b6SAndreas Färber
416fc60a8b6SAndreas Färber return 0;
417fc60a8b6SAndreas Färber }
418fc60a8b6SAndreas Färber
owl_uart_type(struct uart_port * port)419fc60a8b6SAndreas Färber static const char *owl_uart_type(struct uart_port *port)
420fc60a8b6SAndreas Färber {
421fc60a8b6SAndreas Färber return (port->type == PORT_OWL) ? "owl-uart" : NULL;
422fc60a8b6SAndreas Färber }
423fc60a8b6SAndreas Färber
owl_uart_verify_port(struct uart_port * port,struct serial_struct * ser)424fc60a8b6SAndreas Färber static int owl_uart_verify_port(struct uart_port *port,
425fc60a8b6SAndreas Färber struct serial_struct *ser)
426fc60a8b6SAndreas Färber {
427fc60a8b6SAndreas Färber if (port->type != PORT_OWL)
428fc60a8b6SAndreas Färber return -EINVAL;
429fc60a8b6SAndreas Färber
430fc60a8b6SAndreas Färber if (port->irq != ser->irq)
431fc60a8b6SAndreas Färber return -EINVAL;
432fc60a8b6SAndreas Färber
433fc60a8b6SAndreas Färber return 0;
434fc60a8b6SAndreas Färber }
435fc60a8b6SAndreas Färber
owl_uart_config_port(struct uart_port * port,int flags)436fc60a8b6SAndreas Färber static void owl_uart_config_port(struct uart_port *port, int flags)
437fc60a8b6SAndreas Färber {
438fc60a8b6SAndreas Färber if (flags & UART_CONFIG_TYPE) {
439fc60a8b6SAndreas Färber port->type = PORT_OWL;
440fc60a8b6SAndreas Färber owl_uart_request_port(port);
441fc60a8b6SAndreas Färber }
442fc60a8b6SAndreas Färber }
443fc60a8b6SAndreas Färber
4449335e23dSCristian Ciocaltea #ifdef CONFIG_CONSOLE_POLL
4459335e23dSCristian Ciocaltea
owl_uart_poll_get_char(struct uart_port * port)4469335e23dSCristian Ciocaltea static int owl_uart_poll_get_char(struct uart_port *port)
4479335e23dSCristian Ciocaltea {
4489335e23dSCristian Ciocaltea if (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_RFEM)
4499335e23dSCristian Ciocaltea return NO_POLL_CHAR;
4509335e23dSCristian Ciocaltea
4519335e23dSCristian Ciocaltea return owl_uart_read(port, OWL_UART_RXDAT);
4529335e23dSCristian Ciocaltea }
4539335e23dSCristian Ciocaltea
owl_uart_poll_put_char(struct uart_port * port,unsigned char ch)4549335e23dSCristian Ciocaltea static void owl_uart_poll_put_char(struct uart_port *port, unsigned char ch)
4559335e23dSCristian Ciocaltea {
4569335e23dSCristian Ciocaltea u32 reg;
4579335e23dSCristian Ciocaltea int ret;
4589335e23dSCristian Ciocaltea
4599335e23dSCristian Ciocaltea /* Wait while FIFO is full or timeout */
4609335e23dSCristian Ciocaltea ret = readl_poll_timeout_atomic(port->membase + OWL_UART_STAT, reg,
4619335e23dSCristian Ciocaltea !(reg & OWL_UART_STAT_TFFU),
4629335e23dSCristian Ciocaltea OWL_UART_POLL_USEC,
4639335e23dSCristian Ciocaltea OWL_UART_TIMEOUT_USEC);
4649335e23dSCristian Ciocaltea if (ret == -ETIMEDOUT) {
4659335e23dSCristian Ciocaltea dev_err(port->dev, "Timeout waiting while UART TX FULL\n");
4669335e23dSCristian Ciocaltea return;
4679335e23dSCristian Ciocaltea }
4689335e23dSCristian Ciocaltea
4699335e23dSCristian Ciocaltea owl_uart_write(port, ch, OWL_UART_TXDAT);
4709335e23dSCristian Ciocaltea }
4719335e23dSCristian Ciocaltea
4729335e23dSCristian Ciocaltea #endif /* CONFIG_CONSOLE_POLL */
4739335e23dSCristian Ciocaltea
474e5a74707SJulia Lawall static const struct uart_ops owl_uart_ops = {
475fc60a8b6SAndreas Färber .set_mctrl = owl_uart_set_mctrl,
476fc60a8b6SAndreas Färber .get_mctrl = owl_uart_get_mctrl,
477fc60a8b6SAndreas Färber .tx_empty = owl_uart_tx_empty,
478fc60a8b6SAndreas Färber .start_tx = owl_uart_start_tx,
479fc60a8b6SAndreas Färber .stop_rx = owl_uart_stop_rx,
480fc60a8b6SAndreas Färber .stop_tx = owl_uart_stop_tx,
481fc60a8b6SAndreas Färber .startup = owl_uart_startup,
482fc60a8b6SAndreas Färber .shutdown = owl_uart_shutdown,
483fc60a8b6SAndreas Färber .set_termios = owl_uart_set_termios,
484fc60a8b6SAndreas Färber .type = owl_uart_type,
485fc60a8b6SAndreas Färber .config_port = owl_uart_config_port,
486fc60a8b6SAndreas Färber .request_port = owl_uart_request_port,
487fc60a8b6SAndreas Färber .release_port = owl_uart_release_port,
488fc60a8b6SAndreas Färber .verify_port = owl_uart_verify_port,
4899335e23dSCristian Ciocaltea #ifdef CONFIG_CONSOLE_POLL
4909335e23dSCristian Ciocaltea .poll_get_char = owl_uart_poll_get_char,
4919335e23dSCristian Ciocaltea .poll_put_char = owl_uart_poll_put_char,
4929335e23dSCristian Ciocaltea #endif
493fc60a8b6SAndreas Färber };
494fc60a8b6SAndreas Färber
495e36361d7SAndreas Färber #ifdef CONFIG_SERIAL_OWL_CONSOLE
496e36361d7SAndreas Färber
owl_console_putchar(struct uart_port * port,unsigned char ch)4973f8bab17SJiri Slaby static void owl_console_putchar(struct uart_port *port, unsigned char ch)
498e36361d7SAndreas Färber {
499e36361d7SAndreas Färber if (!port->membase)
500e36361d7SAndreas Färber return;
501e36361d7SAndreas Färber
502e36361d7SAndreas Färber while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU)
503e36361d7SAndreas Färber cpu_relax();
504e36361d7SAndreas Färber
505e36361d7SAndreas Färber owl_uart_write(port, ch, OWL_UART_TXDAT);
506e36361d7SAndreas Färber }
507e36361d7SAndreas Färber
owl_uart_port_write(struct uart_port * port,const char * s,u_int count)508e36361d7SAndreas Färber static void owl_uart_port_write(struct uart_port *port, const char *s,
509e36361d7SAndreas Färber u_int count)
510e36361d7SAndreas Färber {
511e36361d7SAndreas Färber u32 old_ctl, val;
512e36361d7SAndreas Färber unsigned long flags;
5132af52148SSebastian Andrzej Siewior int locked = 1;
514e36361d7SAndreas Färber
5152af52148SSebastian Andrzej Siewior if (oops_in_progress)
5162af52148SSebastian Andrzej Siewior locked = uart_port_trylock_irqsave(port, &flags);
5172af52148SSebastian Andrzej Siewior else
5182af52148SSebastian Andrzej Siewior uart_port_lock_irqsave(port, &flags);
519e36361d7SAndreas Färber
520e36361d7SAndreas Färber old_ctl = owl_uart_read(port, OWL_UART_CTL);
521e36361d7SAndreas Färber val = old_ctl | OWL_UART_CTL_TRFS_TX;
522e36361d7SAndreas Färber /* disable IRQ */
523e36361d7SAndreas Färber val &= ~(OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE);
524e36361d7SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
525e36361d7SAndreas Färber
526e36361d7SAndreas Färber uart_console_write(port, s, count, owl_console_putchar);
527e36361d7SAndreas Färber
528e36361d7SAndreas Färber /* wait until all contents have been sent out */
529e36361d7SAndreas Färber while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TRFL_MASK)
530e36361d7SAndreas Färber cpu_relax();
531e36361d7SAndreas Färber
532e36361d7SAndreas Färber /* clear IRQ pending */
533e36361d7SAndreas Färber val = owl_uart_read(port, OWL_UART_STAT);
534e36361d7SAndreas Färber val |= OWL_UART_STAT_TIP | OWL_UART_STAT_RIP;
535e36361d7SAndreas Färber owl_uart_write(port, val, OWL_UART_STAT);
536e36361d7SAndreas Färber
537e36361d7SAndreas Färber owl_uart_write(port, old_ctl, OWL_UART_CTL);
538e36361d7SAndreas Färber
539e36361d7SAndreas Färber if (locked)
5402af52148SSebastian Andrzej Siewior uart_port_unlock_irqrestore(port, flags);
541e36361d7SAndreas Färber }
542e36361d7SAndreas Färber
owl_uart_console_write(struct console * co,const char * s,u_int count)543fc60a8b6SAndreas Färber static void owl_uart_console_write(struct console *co, const char *s,
544fc60a8b6SAndreas Färber u_int count)
545fc60a8b6SAndreas Färber {
546fc60a8b6SAndreas Färber struct owl_uart_port *owl_port;
547fc60a8b6SAndreas Färber
548fc60a8b6SAndreas Färber owl_port = owl_uart_ports[co->index];
549fc60a8b6SAndreas Färber if (!owl_port)
550fc60a8b6SAndreas Färber return;
551fc60a8b6SAndreas Färber
552fc60a8b6SAndreas Färber owl_uart_port_write(&owl_port->port, s, count);
553fc60a8b6SAndreas Färber }
554fc60a8b6SAndreas Färber
owl_uart_console_setup(struct console * co,char * options)555fc60a8b6SAndreas Färber static int owl_uart_console_setup(struct console *co, char *options)
556fc60a8b6SAndreas Färber {
557fc60a8b6SAndreas Färber struct owl_uart_port *owl_port;
558fc60a8b6SAndreas Färber int baud = 115200;
559fc60a8b6SAndreas Färber int bits = 8;
560fc60a8b6SAndreas Färber int parity = 'n';
561fc60a8b6SAndreas Färber int flow = 'n';
562fc60a8b6SAndreas Färber
563fc60a8b6SAndreas Färber if (co->index < 0 || co->index >= OWL_UART_PORT_NUM)
564fc60a8b6SAndreas Färber return -EINVAL;
565fc60a8b6SAndreas Färber
566fc60a8b6SAndreas Färber owl_port = owl_uart_ports[co->index];
567fc60a8b6SAndreas Färber if (!owl_port || !owl_port->port.membase)
568fc60a8b6SAndreas Färber return -ENODEV;
569fc60a8b6SAndreas Färber
570fc60a8b6SAndreas Färber if (options)
571fc60a8b6SAndreas Färber uart_parse_options(options, &baud, &parity, &bits, &flow);
572fc60a8b6SAndreas Färber
573fc60a8b6SAndreas Färber return uart_set_options(&owl_port->port, co, baud, parity, bits, flow);
574fc60a8b6SAndreas Färber }
575fc60a8b6SAndreas Färber
576fc60a8b6SAndreas Färber static struct console owl_uart_console = {
577fc60a8b6SAndreas Färber .name = OWL_UART_DEV_NAME,
578fc60a8b6SAndreas Färber .write = owl_uart_console_write,
579fc60a8b6SAndreas Färber .device = uart_console_device,
580fc60a8b6SAndreas Färber .setup = owl_uart_console_setup,
581fc60a8b6SAndreas Färber .flags = CON_PRINTBUFFER,
582fc60a8b6SAndreas Färber .index = -1,
583fc60a8b6SAndreas Färber .data = &owl_uart_driver,
584fc60a8b6SAndreas Färber };
585fc60a8b6SAndreas Färber
owl_uart_console_init(void)586fc60a8b6SAndreas Färber static int __init owl_uart_console_init(void)
587fc60a8b6SAndreas Färber {
588fc60a8b6SAndreas Färber register_console(&owl_uart_console);
589fc60a8b6SAndreas Färber
590fc60a8b6SAndreas Färber return 0;
591fc60a8b6SAndreas Färber }
592fc60a8b6SAndreas Färber console_initcall(owl_uart_console_init);
593fc60a8b6SAndreas Färber
owl_uart_early_console_write(struct console * co,const char * s,u_int count)594e36361d7SAndreas Färber static void owl_uart_early_console_write(struct console *co,
595e36361d7SAndreas Färber const char *s,
596e36361d7SAndreas Färber u_int count)
597e36361d7SAndreas Färber {
598e36361d7SAndreas Färber struct earlycon_device *dev = co->data;
599e36361d7SAndreas Färber
600e36361d7SAndreas Färber owl_uart_port_write(&dev->port, s, count);
601e36361d7SAndreas Färber }
602e36361d7SAndreas Färber
603e36361d7SAndreas Färber static int __init
owl_uart_early_console_setup(struct earlycon_device * device,const char * opt)604e36361d7SAndreas Färber owl_uart_early_console_setup(struct earlycon_device *device, const char *opt)
605e36361d7SAndreas Färber {
606e36361d7SAndreas Färber if (!device->port.membase)
607e36361d7SAndreas Färber return -ENODEV;
608e36361d7SAndreas Färber
609e36361d7SAndreas Färber device->con->write = owl_uart_early_console_write;
610e36361d7SAndreas Färber
611e36361d7SAndreas Färber return 0;
612e36361d7SAndreas Färber }
613e36361d7SAndreas Färber OF_EARLYCON_DECLARE(owl, "actions,owl-uart",
614e36361d7SAndreas Färber owl_uart_early_console_setup);
615e36361d7SAndreas Färber
616fc60a8b6SAndreas Färber #define OWL_UART_CONSOLE (&owl_uart_console)
617fc60a8b6SAndreas Färber #else
618fc60a8b6SAndreas Färber #define OWL_UART_CONSOLE NULL
619fc60a8b6SAndreas Färber #endif
620fc60a8b6SAndreas Färber
621fc60a8b6SAndreas Färber static struct uart_driver owl_uart_driver = {
622fc60a8b6SAndreas Färber .owner = THIS_MODULE,
623fc60a8b6SAndreas Färber .driver_name = "owl-uart",
624fc60a8b6SAndreas Färber .dev_name = OWL_UART_DEV_NAME,
625fc60a8b6SAndreas Färber .nr = OWL_UART_PORT_NUM,
626fc60a8b6SAndreas Färber .cons = OWL_UART_CONSOLE,
627fc60a8b6SAndreas Färber };
628fc60a8b6SAndreas Färber
629fc60a8b6SAndreas Färber static const struct owl_uart_info owl_s500_info = {
630fc60a8b6SAndreas Färber .tx_fifosize = 16,
631fc60a8b6SAndreas Färber };
632fc60a8b6SAndreas Färber
633fc60a8b6SAndreas Färber static const struct owl_uart_info owl_s900_info = {
634fc60a8b6SAndreas Färber .tx_fifosize = 32,
635fc60a8b6SAndreas Färber };
636fc60a8b6SAndreas Färber
637fc60a8b6SAndreas Färber static const struct of_device_id owl_uart_dt_matches[] = {
638fc60a8b6SAndreas Färber { .compatible = "actions,s500-uart", .data = &owl_s500_info },
639fc60a8b6SAndreas Färber { .compatible = "actions,s900-uart", .data = &owl_s900_info },
640fc60a8b6SAndreas Färber { }
641fc60a8b6SAndreas Färber };
642fc60a8b6SAndreas Färber MODULE_DEVICE_TABLE(of, owl_uart_dt_matches);
643fc60a8b6SAndreas Färber
owl_uart_probe(struct platform_device * pdev)644fc60a8b6SAndreas Färber static int owl_uart_probe(struct platform_device *pdev)
645fc60a8b6SAndreas Färber {
646fc60a8b6SAndreas Färber const struct of_device_id *match;
647fc60a8b6SAndreas Färber const struct owl_uart_info *info = NULL;
648fc60a8b6SAndreas Färber struct resource *res_mem;
649fc60a8b6SAndreas Färber struct owl_uart_port *owl_port;
650fc60a8b6SAndreas Färber int ret, irq;
651fc60a8b6SAndreas Färber
652fc60a8b6SAndreas Färber if (pdev->dev.of_node) {
653fc60a8b6SAndreas Färber pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
654fc60a8b6SAndreas Färber match = of_match_node(owl_uart_dt_matches, pdev->dev.of_node);
655fc60a8b6SAndreas Färber if (match)
656fc60a8b6SAndreas Färber info = match->data;
657fc60a8b6SAndreas Färber }
658fc60a8b6SAndreas Färber
659fc60a8b6SAndreas Färber if (pdev->id < 0 || pdev->id >= OWL_UART_PORT_NUM) {
660fc60a8b6SAndreas Färber dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
661fc60a8b6SAndreas Färber return -EINVAL;
662fc60a8b6SAndreas Färber }
663fc60a8b6SAndreas Färber
664fc60a8b6SAndreas Färber res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
665fc60a8b6SAndreas Färber if (!res_mem) {
666fc60a8b6SAndreas Färber dev_err(&pdev->dev, "could not get mem\n");
667fc60a8b6SAndreas Färber return -ENODEV;
668fc60a8b6SAndreas Färber }
669fc60a8b6SAndreas Färber
670fc60a8b6SAndreas Färber irq = platform_get_irq(pdev, 0);
6711df21786SStephen Boyd if (irq < 0)
672fc60a8b6SAndreas Färber return irq;
673fc60a8b6SAndreas Färber
674fc60a8b6SAndreas Färber if (owl_uart_ports[pdev->id]) {
675fc60a8b6SAndreas Färber dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);
676fc60a8b6SAndreas Färber return -EBUSY;
677fc60a8b6SAndreas Färber }
678fc60a8b6SAndreas Färber
679fc60a8b6SAndreas Färber owl_port = devm_kzalloc(&pdev->dev, sizeof(*owl_port), GFP_KERNEL);
680fc60a8b6SAndreas Färber if (!owl_port)
681fc60a8b6SAndreas Färber return -ENOMEM;
682fc60a8b6SAndreas Färber
683fc60a8b6SAndreas Färber owl_port->clk = devm_clk_get(&pdev->dev, NULL);
684fc60a8b6SAndreas Färber if (IS_ERR(owl_port->clk)) {
685fc60a8b6SAndreas Färber dev_err(&pdev->dev, "could not get clk\n");
686fc60a8b6SAndreas Färber return PTR_ERR(owl_port->clk);
687fc60a8b6SAndreas Färber }
688fc60a8b6SAndreas Färber
689abf42d2fSAmit Singh Tomar ret = clk_prepare_enable(owl_port->clk);
690abf42d2fSAmit Singh Tomar if (ret) {
691abf42d2fSAmit Singh Tomar dev_err(&pdev->dev, "could not enable clk\n");
692abf42d2fSAmit Singh Tomar return ret;
693abf42d2fSAmit Singh Tomar }
694abf42d2fSAmit Singh Tomar
695fc60a8b6SAndreas Färber owl_port->port.dev = &pdev->dev;
696fc60a8b6SAndreas Färber owl_port->port.line = pdev->id;
697fc60a8b6SAndreas Färber owl_port->port.type = PORT_OWL;
698fc60a8b6SAndreas Färber owl_port->port.iotype = UPIO_MEM;
699fc60a8b6SAndreas Färber owl_port->port.mapbase = res_mem->start;
700fc60a8b6SAndreas Färber owl_port->port.irq = irq;
701fc60a8b6SAndreas Färber owl_port->port.uartclk = clk_get_rate(owl_port->clk);
702fc60a8b6SAndreas Färber if (owl_port->port.uartclk == 0) {
703fc60a8b6SAndreas Färber dev_err(&pdev->dev, "clock rate is zero\n");
704bcea0f54SMiaoqian Lin clk_disable_unprepare(owl_port->clk);
705fc60a8b6SAndreas Färber return -EINVAL;
706fc60a8b6SAndreas Färber }
707fc60a8b6SAndreas Färber owl_port->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY;
708fc60a8b6SAndreas Färber owl_port->port.x_char = 0;
709fc60a8b6SAndreas Färber owl_port->port.fifosize = (info) ? info->tx_fifosize : 16;
710fc60a8b6SAndreas Färber owl_port->port.ops = &owl_uart_ops;
711fc60a8b6SAndreas Färber
712fc60a8b6SAndreas Färber owl_uart_ports[pdev->id] = owl_port;
713fc60a8b6SAndreas Färber platform_set_drvdata(pdev, owl_port);
714fc60a8b6SAndreas Färber
715fc60a8b6SAndreas Färber ret = uart_add_one_port(&owl_uart_driver, &owl_port->port);
716fc60a8b6SAndreas Färber if (ret)
717fc60a8b6SAndreas Färber owl_uart_ports[pdev->id] = NULL;
718fc60a8b6SAndreas Färber
719fc60a8b6SAndreas Färber return ret;
720fc60a8b6SAndreas Färber }
721fc60a8b6SAndreas Färber
owl_uart_remove(struct platform_device * pdev)7228e94fc93SUwe Kleine-König static void owl_uart_remove(struct platform_device *pdev)
723fc60a8b6SAndreas Färber {
724fc60a8b6SAndreas Färber struct owl_uart_port *owl_port = platform_get_drvdata(pdev);
725fc60a8b6SAndreas Färber
726fc60a8b6SAndreas Färber uart_remove_one_port(&owl_uart_driver, &owl_port->port);
727fc60a8b6SAndreas Färber owl_uart_ports[pdev->id] = NULL;
728abf42d2fSAmit Singh Tomar clk_disable_unprepare(owl_port->clk);
729fc60a8b6SAndreas Färber }
730fc60a8b6SAndreas Färber
731fc60a8b6SAndreas Färber static struct platform_driver owl_uart_platform_driver = {
732fc60a8b6SAndreas Färber .probe = owl_uart_probe,
7338e94fc93SUwe Kleine-König .remove_new = owl_uart_remove,
734fc60a8b6SAndreas Färber .driver = {
735fc60a8b6SAndreas Färber .name = "owl-uart",
736fc60a8b6SAndreas Färber .of_match_table = owl_uart_dt_matches,
737fc60a8b6SAndreas Färber },
738fc60a8b6SAndreas Färber };
739fc60a8b6SAndreas Färber
owl_uart_init(void)740fc60a8b6SAndreas Färber static int __init owl_uart_init(void)
741fc60a8b6SAndreas Färber {
742fc60a8b6SAndreas Färber int ret;
743fc60a8b6SAndreas Färber
744fc60a8b6SAndreas Färber ret = uart_register_driver(&owl_uart_driver);
745fc60a8b6SAndreas Färber if (ret)
746fc60a8b6SAndreas Färber return ret;
747fc60a8b6SAndreas Färber
748fc60a8b6SAndreas Färber ret = platform_driver_register(&owl_uart_platform_driver);
749fc60a8b6SAndreas Färber if (ret)
750fc60a8b6SAndreas Färber uart_unregister_driver(&owl_uart_driver);
751fc60a8b6SAndreas Färber
752fc60a8b6SAndreas Färber return ret;
753fc60a8b6SAndreas Färber }
754fc60a8b6SAndreas Färber
owl_uart_exit(void)7556264dab6SChristophe JAILLET static void __exit owl_uart_exit(void)
756fc60a8b6SAndreas Färber {
757fc60a8b6SAndreas Färber platform_driver_unregister(&owl_uart_platform_driver);
758fc60a8b6SAndreas Färber uart_unregister_driver(&owl_uart_driver);
759fc60a8b6SAndreas Färber }
760fc60a8b6SAndreas Färber
761fc60a8b6SAndreas Färber module_init(owl_uart_init);
762fc60a8b6SAndreas Färber module_exit(owl_uart_exit);
763fc60a8b6SAndreas Färber
764*f0a17485SJeff Johnson MODULE_DESCRIPTION("Actions Semi Owl family serial console");
765fc60a8b6SAndreas Färber MODULE_LICENSE("GPL");
766