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
128fc60a8b6SAndreas Färber spin_lock_irqsave(&port->lock, 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
133fc60a8b6SAndreas Färber spin_unlock_irqrestore(&port->lock, 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 {
184*2d141e68SJiri Slaby (SUSE) u8 ch;
185fc60a8b6SAndreas Färber
186*2d141e68SJiri Slaby (SUSE) uart_port_tx(port, ch,
187*2d141e68SJiri Slaby (SUSE) !(owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU),
188*2d141e68SJiri 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;
202fc60a8b6SAndreas Färber
203fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RXER)
204fc60a8b6SAndreas Färber port->icount.overrun++;
205fc60a8b6SAndreas Färber
206fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RXST) {
207fc60a8b6SAndreas Färber /* We are not able to distinguish the error type. */
208fc60a8b6SAndreas Färber port->icount.brk++;
209fc60a8b6SAndreas Färber port->icount.frame++;
210fc60a8b6SAndreas Färber
211fc60a8b6SAndreas Färber stat &= port->read_status_mask;
212fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RXST)
213fc60a8b6SAndreas Färber flag = TTY_PARITY;
214fc60a8b6SAndreas Färber } else
215fc60a8b6SAndreas Färber port->icount.rx++;
216fc60a8b6SAndreas Färber
217fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_RXDAT);
218fc60a8b6SAndreas Färber val &= 0xff;
219fc60a8b6SAndreas Färber
220fc60a8b6SAndreas Färber if ((stat & port->ignore_status_mask) == 0)
221fc60a8b6SAndreas Färber tty_insert_flip_char(&port->state->port, val, flag);
222fc60a8b6SAndreas Färber
223fc60a8b6SAndreas Färber stat = owl_uart_read(port, OWL_UART_STAT);
224fc60a8b6SAndreas Färber }
225fc60a8b6SAndreas Färber
226fc60a8b6SAndreas Färber tty_flip_buffer_push(&port->state->port);
227fc60a8b6SAndreas Färber }
228fc60a8b6SAndreas Färber
owl_uart_irq(int irq,void * dev_id)229fc60a8b6SAndreas Färber static irqreturn_t owl_uart_irq(int irq, void *dev_id)
230fc60a8b6SAndreas Färber {
231fc60a8b6SAndreas Färber struct uart_port *port = dev_id;
232fc60a8b6SAndreas Färber unsigned long flags;
233fc60a8b6SAndreas Färber u32 stat;
234fc60a8b6SAndreas Färber
235fc60a8b6SAndreas Färber spin_lock_irqsave(&port->lock, flags);
236fc60a8b6SAndreas Färber
237fc60a8b6SAndreas Färber stat = owl_uart_read(port, OWL_UART_STAT);
238fc60a8b6SAndreas Färber
239fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_RIP)
240fc60a8b6SAndreas Färber owl_uart_receive_chars(port);
241fc60a8b6SAndreas Färber
242fc60a8b6SAndreas Färber if (stat & OWL_UART_STAT_TIP)
243fc60a8b6SAndreas Färber owl_uart_send_chars(port);
244fc60a8b6SAndreas Färber
245fc60a8b6SAndreas Färber stat = owl_uart_read(port, OWL_UART_STAT);
246fc60a8b6SAndreas Färber stat |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP;
247fc60a8b6SAndreas Färber owl_uart_write(port, stat, OWL_UART_STAT);
248fc60a8b6SAndreas Färber
249fc60a8b6SAndreas Färber spin_unlock_irqrestore(&port->lock, flags);
250fc60a8b6SAndreas Färber
251fc60a8b6SAndreas Färber return IRQ_HANDLED;
252fc60a8b6SAndreas Färber }
253fc60a8b6SAndreas Färber
owl_uart_shutdown(struct uart_port * port)254fc60a8b6SAndreas Färber static void owl_uart_shutdown(struct uart_port *port)
255fc60a8b6SAndreas Färber {
256fc60a8b6SAndreas Färber u32 val;
257fc60a8b6SAndreas Färber unsigned long flags;
258fc60a8b6SAndreas Färber
259fc60a8b6SAndreas Färber spin_lock_irqsave(&port->lock, flags);
260fc60a8b6SAndreas Färber
261fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_CTL);
262fc60a8b6SAndreas Färber val &= ~(OWL_UART_CTL_TXIE | OWL_UART_CTL_RXIE
263fc60a8b6SAndreas Färber | OWL_UART_CTL_TXDE | OWL_UART_CTL_RXDE | OWL_UART_CTL_EN);
264fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
265fc60a8b6SAndreas Färber
266fc60a8b6SAndreas Färber spin_unlock_irqrestore(&port->lock, flags);
267fc60a8b6SAndreas Färber
268fc60a8b6SAndreas Färber free_irq(port->irq, port);
269fc60a8b6SAndreas Färber }
270fc60a8b6SAndreas Färber
owl_uart_startup(struct uart_port * port)271fc60a8b6SAndreas Färber static int owl_uart_startup(struct uart_port *port)
272fc60a8b6SAndreas Färber {
273fc60a8b6SAndreas Färber u32 val;
274fc60a8b6SAndreas Färber unsigned long flags;
275fc60a8b6SAndreas Färber int ret;
276fc60a8b6SAndreas Färber
277fc60a8b6SAndreas Färber ret = request_irq(port->irq, owl_uart_irq, IRQF_TRIGGER_HIGH,
278fc60a8b6SAndreas Färber "owl-uart", port);
279fc60a8b6SAndreas Färber if (ret)
280fc60a8b6SAndreas Färber return ret;
281fc60a8b6SAndreas Färber
282fc60a8b6SAndreas Färber spin_lock_irqsave(&port->lock, flags);
283fc60a8b6SAndreas Färber
284fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_STAT);
285fc60a8b6SAndreas Färber val |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP
286fc60a8b6SAndreas Färber | OWL_UART_STAT_RXER | OWL_UART_STAT_TFER | OWL_UART_STAT_RXST;
287fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_STAT);
288fc60a8b6SAndreas Färber
289fc60a8b6SAndreas Färber val = owl_uart_read(port, OWL_UART_CTL);
290fc60a8b6SAndreas Färber val |= OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE;
291fc60a8b6SAndreas Färber val |= OWL_UART_CTL_EN;
292fc60a8b6SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
293fc60a8b6SAndreas Färber
294fc60a8b6SAndreas Färber spin_unlock_irqrestore(&port->lock, flags);
295fc60a8b6SAndreas Färber
296fc60a8b6SAndreas Färber return 0;
297fc60a8b6SAndreas Färber }
298fc60a8b6SAndreas Färber
owl_uart_change_baudrate(struct owl_uart_port * owl_port,unsigned long baud)299fc60a8b6SAndreas Färber static void owl_uart_change_baudrate(struct owl_uart_port *owl_port,
300fc60a8b6SAndreas Färber unsigned long baud)
301fc60a8b6SAndreas Färber {
302fc60a8b6SAndreas Färber clk_set_rate(owl_port->clk, baud * 8);
303fc60a8b6SAndreas Färber }
304fc60a8b6SAndreas Färber
owl_uart_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)305fc60a8b6SAndreas Färber static void owl_uart_set_termios(struct uart_port *port,
306fc60a8b6SAndreas Färber struct ktermios *termios,
307bec5b814SIlpo Järvinen const struct ktermios *old)
308fc60a8b6SAndreas Färber {
309fc60a8b6SAndreas Färber struct owl_uart_port *owl_port = to_owl_uart_port(port);
310fc60a8b6SAndreas Färber unsigned int baud;
311fc60a8b6SAndreas Färber u32 ctl;
312fc60a8b6SAndreas Färber unsigned long flags;
313fc60a8b6SAndreas Färber
314fc60a8b6SAndreas Färber spin_lock_irqsave(&port->lock, flags);
315fc60a8b6SAndreas Färber
316fc60a8b6SAndreas Färber ctl = owl_uart_read(port, OWL_UART_CTL);
317fc60a8b6SAndreas Färber
318fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_DWLS_MASK;
319fc60a8b6SAndreas Färber switch (termios->c_cflag & CSIZE) {
320fc60a8b6SAndreas Färber case CS5:
321fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_DWLS_5BITS;
322fc60a8b6SAndreas Färber break;
323fc60a8b6SAndreas Färber case CS6:
324fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_DWLS_6BITS;
325fc60a8b6SAndreas Färber break;
326fc60a8b6SAndreas Färber case CS7:
327fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_DWLS_7BITS;
328fc60a8b6SAndreas Färber break;
329fc60a8b6SAndreas Färber case CS8:
330fc60a8b6SAndreas Färber default:
331fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_DWLS_8BITS;
332fc60a8b6SAndreas Färber break;
333fc60a8b6SAndreas Färber }
334fc60a8b6SAndreas Färber
335fc60a8b6SAndreas Färber if (termios->c_cflag & CSTOPB)
336fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_STPS_2BITS;
337fc60a8b6SAndreas Färber else
338fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_STPS_2BITS;
339fc60a8b6SAndreas Färber
340fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_PRS_MASK;
341fc60a8b6SAndreas Färber if (termios->c_cflag & PARENB) {
342fc60a8b6SAndreas Färber if (termios->c_cflag & CMSPAR) {
343fc60a8b6SAndreas Färber if (termios->c_cflag & PARODD)
344fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_MARK;
345fc60a8b6SAndreas Färber else
346fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_SPACE;
347fc60a8b6SAndreas Färber } else if (termios->c_cflag & PARODD)
348fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_ODD;
349fc60a8b6SAndreas Färber else
350fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_EVEN;
351fc60a8b6SAndreas Färber } else
352fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_PRS_NONE;
353fc60a8b6SAndreas Färber
354fc60a8b6SAndreas Färber if (termios->c_cflag & CRTSCTS)
355fc60a8b6SAndreas Färber ctl |= OWL_UART_CTL_AFE;
356fc60a8b6SAndreas Färber else
357fc60a8b6SAndreas Färber ctl &= ~OWL_UART_CTL_AFE;
358fc60a8b6SAndreas Färber
359fc60a8b6SAndreas Färber owl_uart_write(port, ctl, OWL_UART_CTL);
360fc60a8b6SAndreas Färber
361fc60a8b6SAndreas Färber baud = uart_get_baud_rate(port, termios, old, 9600, 3200000);
362fc60a8b6SAndreas Färber owl_uart_change_baudrate(owl_port, baud);
363fc60a8b6SAndreas Färber
364fc60a8b6SAndreas Färber /* Don't rewrite B0 */
365fc60a8b6SAndreas Färber if (tty_termios_baud_rate(termios))
366fc60a8b6SAndreas Färber tty_termios_encode_baud_rate(termios, baud, baud);
367fc60a8b6SAndreas Färber
368fc60a8b6SAndreas Färber port->read_status_mask |= OWL_UART_STAT_RXER;
369fc60a8b6SAndreas Färber if (termios->c_iflag & INPCK)
370fc60a8b6SAndreas Färber port->read_status_mask |= OWL_UART_STAT_RXST;
371fc60a8b6SAndreas Färber
372fc60a8b6SAndreas Färber uart_update_timeout(port, termios->c_cflag, baud);
373fc60a8b6SAndreas Färber
374fc60a8b6SAndreas Färber spin_unlock_irqrestore(&port->lock, flags);
375fc60a8b6SAndreas Färber }
376fc60a8b6SAndreas Färber
owl_uart_release_port(struct uart_port * port)377fc60a8b6SAndreas Färber static void owl_uart_release_port(struct uart_port *port)
378fc60a8b6SAndreas Färber {
379fc60a8b6SAndreas Färber struct platform_device *pdev = to_platform_device(port->dev);
380fc60a8b6SAndreas Färber struct resource *res;
381fc60a8b6SAndreas Färber
382fc60a8b6SAndreas Färber res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
383fc60a8b6SAndreas Färber if (!res)
384fc60a8b6SAndreas Färber return;
385fc60a8b6SAndreas Färber
386fc60a8b6SAndreas Färber if (port->flags & UPF_IOREMAP) {
387fc60a8b6SAndreas Färber devm_release_mem_region(port->dev, port->mapbase,
388fc60a8b6SAndreas Färber resource_size(res));
389fc60a8b6SAndreas Färber devm_iounmap(port->dev, port->membase);
390fc60a8b6SAndreas Färber port->membase = NULL;
391fc60a8b6SAndreas Färber }
392fc60a8b6SAndreas Färber }
393fc60a8b6SAndreas Färber
owl_uart_request_port(struct uart_port * port)394fc60a8b6SAndreas Färber static int owl_uart_request_port(struct uart_port *port)
395fc60a8b6SAndreas Färber {
396fc60a8b6SAndreas Färber struct platform_device *pdev = to_platform_device(port->dev);
397fc60a8b6SAndreas Färber struct resource *res;
398fc60a8b6SAndreas Färber
399fc60a8b6SAndreas Färber res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
400fc60a8b6SAndreas Färber if (!res)
401fc60a8b6SAndreas Färber return -ENXIO;
402fc60a8b6SAndreas Färber
403fc60a8b6SAndreas Färber if (!devm_request_mem_region(port->dev, port->mapbase,
404fc60a8b6SAndreas Färber resource_size(res), dev_name(port->dev)))
405fc60a8b6SAndreas Färber return -EBUSY;
406fc60a8b6SAndreas Färber
407fc60a8b6SAndreas Färber if (port->flags & UPF_IOREMAP) {
4084bdc0d67SChristoph Hellwig port->membase = devm_ioremap(port->dev, port->mapbase,
409fc60a8b6SAndreas Färber resource_size(res));
410fc60a8b6SAndreas Färber if (!port->membase)
411fc60a8b6SAndreas Färber return -EBUSY;
412fc60a8b6SAndreas Färber }
413fc60a8b6SAndreas Färber
414fc60a8b6SAndreas Färber return 0;
415fc60a8b6SAndreas Färber }
416fc60a8b6SAndreas Färber
owl_uart_type(struct uart_port * port)417fc60a8b6SAndreas Färber static const char *owl_uart_type(struct uart_port *port)
418fc60a8b6SAndreas Färber {
419fc60a8b6SAndreas Färber return (port->type == PORT_OWL) ? "owl-uart" : NULL;
420fc60a8b6SAndreas Färber }
421fc60a8b6SAndreas Färber
owl_uart_verify_port(struct uart_port * port,struct serial_struct * ser)422fc60a8b6SAndreas Färber static int owl_uart_verify_port(struct uart_port *port,
423fc60a8b6SAndreas Färber struct serial_struct *ser)
424fc60a8b6SAndreas Färber {
425fc60a8b6SAndreas Färber if (port->type != PORT_OWL)
426fc60a8b6SAndreas Färber return -EINVAL;
427fc60a8b6SAndreas Färber
428fc60a8b6SAndreas Färber if (port->irq != ser->irq)
429fc60a8b6SAndreas Färber return -EINVAL;
430fc60a8b6SAndreas Färber
431fc60a8b6SAndreas Färber return 0;
432fc60a8b6SAndreas Färber }
433fc60a8b6SAndreas Färber
owl_uart_config_port(struct uart_port * port,int flags)434fc60a8b6SAndreas Färber static void owl_uart_config_port(struct uart_port *port, int flags)
435fc60a8b6SAndreas Färber {
436fc60a8b6SAndreas Färber if (flags & UART_CONFIG_TYPE) {
437fc60a8b6SAndreas Färber port->type = PORT_OWL;
438fc60a8b6SAndreas Färber owl_uart_request_port(port);
439fc60a8b6SAndreas Färber }
440fc60a8b6SAndreas Färber }
441fc60a8b6SAndreas Färber
4429335e23dSCristian Ciocaltea #ifdef CONFIG_CONSOLE_POLL
4439335e23dSCristian Ciocaltea
owl_uart_poll_get_char(struct uart_port * port)4449335e23dSCristian Ciocaltea static int owl_uart_poll_get_char(struct uart_port *port)
4459335e23dSCristian Ciocaltea {
4469335e23dSCristian Ciocaltea if (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_RFEM)
4479335e23dSCristian Ciocaltea return NO_POLL_CHAR;
4489335e23dSCristian Ciocaltea
4499335e23dSCristian Ciocaltea return owl_uart_read(port, OWL_UART_RXDAT);
4509335e23dSCristian Ciocaltea }
4519335e23dSCristian Ciocaltea
owl_uart_poll_put_char(struct uart_port * port,unsigned char ch)4529335e23dSCristian Ciocaltea static void owl_uart_poll_put_char(struct uart_port *port, unsigned char ch)
4539335e23dSCristian Ciocaltea {
4549335e23dSCristian Ciocaltea u32 reg;
4559335e23dSCristian Ciocaltea int ret;
4569335e23dSCristian Ciocaltea
4579335e23dSCristian Ciocaltea /* Wait while FIFO is full or timeout */
4589335e23dSCristian Ciocaltea ret = readl_poll_timeout_atomic(port->membase + OWL_UART_STAT, reg,
4599335e23dSCristian Ciocaltea !(reg & OWL_UART_STAT_TFFU),
4609335e23dSCristian Ciocaltea OWL_UART_POLL_USEC,
4619335e23dSCristian Ciocaltea OWL_UART_TIMEOUT_USEC);
4629335e23dSCristian Ciocaltea if (ret == -ETIMEDOUT) {
4639335e23dSCristian Ciocaltea dev_err(port->dev, "Timeout waiting while UART TX FULL\n");
4649335e23dSCristian Ciocaltea return;
4659335e23dSCristian Ciocaltea }
4669335e23dSCristian Ciocaltea
4679335e23dSCristian Ciocaltea owl_uart_write(port, ch, OWL_UART_TXDAT);
4689335e23dSCristian Ciocaltea }
4699335e23dSCristian Ciocaltea
4709335e23dSCristian Ciocaltea #endif /* CONFIG_CONSOLE_POLL */
4719335e23dSCristian Ciocaltea
472e5a74707SJulia Lawall static const struct uart_ops owl_uart_ops = {
473fc60a8b6SAndreas Färber .set_mctrl = owl_uart_set_mctrl,
474fc60a8b6SAndreas Färber .get_mctrl = owl_uart_get_mctrl,
475fc60a8b6SAndreas Färber .tx_empty = owl_uart_tx_empty,
476fc60a8b6SAndreas Färber .start_tx = owl_uart_start_tx,
477fc60a8b6SAndreas Färber .stop_rx = owl_uart_stop_rx,
478fc60a8b6SAndreas Färber .stop_tx = owl_uart_stop_tx,
479fc60a8b6SAndreas Färber .startup = owl_uart_startup,
480fc60a8b6SAndreas Färber .shutdown = owl_uart_shutdown,
481fc60a8b6SAndreas Färber .set_termios = owl_uart_set_termios,
482fc60a8b6SAndreas Färber .type = owl_uart_type,
483fc60a8b6SAndreas Färber .config_port = owl_uart_config_port,
484fc60a8b6SAndreas Färber .request_port = owl_uart_request_port,
485fc60a8b6SAndreas Färber .release_port = owl_uart_release_port,
486fc60a8b6SAndreas Färber .verify_port = owl_uart_verify_port,
4879335e23dSCristian Ciocaltea #ifdef CONFIG_CONSOLE_POLL
4889335e23dSCristian Ciocaltea .poll_get_char = owl_uart_poll_get_char,
4899335e23dSCristian Ciocaltea .poll_put_char = owl_uart_poll_put_char,
4909335e23dSCristian Ciocaltea #endif
491fc60a8b6SAndreas Färber };
492fc60a8b6SAndreas Färber
493e36361d7SAndreas Färber #ifdef CONFIG_SERIAL_OWL_CONSOLE
494e36361d7SAndreas Färber
owl_console_putchar(struct uart_port * port,unsigned char ch)4953f8bab17SJiri Slaby static void owl_console_putchar(struct uart_port *port, unsigned char ch)
496e36361d7SAndreas Färber {
497e36361d7SAndreas Färber if (!port->membase)
498e36361d7SAndreas Färber return;
499e36361d7SAndreas Färber
500e36361d7SAndreas Färber while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU)
501e36361d7SAndreas Färber cpu_relax();
502e36361d7SAndreas Färber
503e36361d7SAndreas Färber owl_uart_write(port, ch, OWL_UART_TXDAT);
504e36361d7SAndreas Färber }
505e36361d7SAndreas Färber
owl_uart_port_write(struct uart_port * port,const char * s,u_int count)506e36361d7SAndreas Färber static void owl_uart_port_write(struct uart_port *port, const char *s,
507e36361d7SAndreas Färber u_int count)
508e36361d7SAndreas Färber {
509e36361d7SAndreas Färber u32 old_ctl, val;
510e36361d7SAndreas Färber unsigned long flags;
511e36361d7SAndreas Färber int locked;
512e36361d7SAndreas Färber
513e36361d7SAndreas Färber local_irq_save(flags);
514e36361d7SAndreas Färber
515e36361d7SAndreas Färber if (port->sysrq)
516e36361d7SAndreas Färber locked = 0;
517e36361d7SAndreas Färber else if (oops_in_progress)
518e36361d7SAndreas Färber locked = spin_trylock(&port->lock);
519e36361d7SAndreas Färber else {
520e36361d7SAndreas Färber spin_lock(&port->lock);
521e36361d7SAndreas Färber locked = 1;
522e36361d7SAndreas Färber }
523e36361d7SAndreas Färber
524e36361d7SAndreas Färber old_ctl = owl_uart_read(port, OWL_UART_CTL);
525e36361d7SAndreas Färber val = old_ctl | OWL_UART_CTL_TRFS_TX;
526e36361d7SAndreas Färber /* disable IRQ */
527e36361d7SAndreas Färber val &= ~(OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE);
528e36361d7SAndreas Färber owl_uart_write(port, val, OWL_UART_CTL);
529e36361d7SAndreas Färber
530e36361d7SAndreas Färber uart_console_write(port, s, count, owl_console_putchar);
531e36361d7SAndreas Färber
532e36361d7SAndreas Färber /* wait until all contents have been sent out */
533e36361d7SAndreas Färber while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TRFL_MASK)
534e36361d7SAndreas Färber cpu_relax();
535e36361d7SAndreas Färber
536e36361d7SAndreas Färber /* clear IRQ pending */
537e36361d7SAndreas Färber val = owl_uart_read(port, OWL_UART_STAT);
538e36361d7SAndreas Färber val |= OWL_UART_STAT_TIP | OWL_UART_STAT_RIP;
539e36361d7SAndreas Färber owl_uart_write(port, val, OWL_UART_STAT);
540e36361d7SAndreas Färber
541e36361d7SAndreas Färber owl_uart_write(port, old_ctl, OWL_UART_CTL);
542e36361d7SAndreas Färber
543e36361d7SAndreas Färber if (locked)
544e36361d7SAndreas Färber spin_unlock(&port->lock);
545e36361d7SAndreas Färber
546e36361d7SAndreas Färber local_irq_restore(flags);
547e36361d7SAndreas Färber }
548e36361d7SAndreas Färber
owl_uart_console_write(struct console * co,const char * s,u_int count)549fc60a8b6SAndreas Färber static void owl_uart_console_write(struct console *co, const char *s,
550fc60a8b6SAndreas Färber u_int count)
551fc60a8b6SAndreas Färber {
552fc60a8b6SAndreas Färber struct owl_uart_port *owl_port;
553fc60a8b6SAndreas Färber
554fc60a8b6SAndreas Färber owl_port = owl_uart_ports[co->index];
555fc60a8b6SAndreas Färber if (!owl_port)
556fc60a8b6SAndreas Färber return;
557fc60a8b6SAndreas Färber
558fc60a8b6SAndreas Färber owl_uart_port_write(&owl_port->port, s, count);
559fc60a8b6SAndreas Färber }
560fc60a8b6SAndreas Färber
owl_uart_console_setup(struct console * co,char * options)561fc60a8b6SAndreas Färber static int owl_uart_console_setup(struct console *co, char *options)
562fc60a8b6SAndreas Färber {
563fc60a8b6SAndreas Färber struct owl_uart_port *owl_port;
564fc60a8b6SAndreas Färber int baud = 115200;
565fc60a8b6SAndreas Färber int bits = 8;
566fc60a8b6SAndreas Färber int parity = 'n';
567fc60a8b6SAndreas Färber int flow = 'n';
568fc60a8b6SAndreas Färber
569fc60a8b6SAndreas Färber if (co->index < 0 || co->index >= OWL_UART_PORT_NUM)
570fc60a8b6SAndreas Färber return -EINVAL;
571fc60a8b6SAndreas Färber
572fc60a8b6SAndreas Färber owl_port = owl_uart_ports[co->index];
573fc60a8b6SAndreas Färber if (!owl_port || !owl_port->port.membase)
574fc60a8b6SAndreas Färber return -ENODEV;
575fc60a8b6SAndreas Färber
576fc60a8b6SAndreas Färber if (options)
577fc60a8b6SAndreas Färber uart_parse_options(options, &baud, &parity, &bits, &flow);
578fc60a8b6SAndreas Färber
579fc60a8b6SAndreas Färber return uart_set_options(&owl_port->port, co, baud, parity, bits, flow);
580fc60a8b6SAndreas Färber }
581fc60a8b6SAndreas Färber
582fc60a8b6SAndreas Färber static struct console owl_uart_console = {
583fc60a8b6SAndreas Färber .name = OWL_UART_DEV_NAME,
584fc60a8b6SAndreas Färber .write = owl_uart_console_write,
585fc60a8b6SAndreas Färber .device = uart_console_device,
586fc60a8b6SAndreas Färber .setup = owl_uart_console_setup,
587fc60a8b6SAndreas Färber .flags = CON_PRINTBUFFER,
588fc60a8b6SAndreas Färber .index = -1,
589fc60a8b6SAndreas Färber .data = &owl_uart_driver,
590fc60a8b6SAndreas Färber };
591fc60a8b6SAndreas Färber
owl_uart_console_init(void)592fc60a8b6SAndreas Färber static int __init owl_uart_console_init(void)
593fc60a8b6SAndreas Färber {
594fc60a8b6SAndreas Färber register_console(&owl_uart_console);
595fc60a8b6SAndreas Färber
596fc60a8b6SAndreas Färber return 0;
597fc60a8b6SAndreas Färber }
598fc60a8b6SAndreas Färber console_initcall(owl_uart_console_init);
599fc60a8b6SAndreas Färber
owl_uart_early_console_write(struct console * co,const char * s,u_int count)600e36361d7SAndreas Färber static void owl_uart_early_console_write(struct console *co,
601e36361d7SAndreas Färber const char *s,
602e36361d7SAndreas Färber u_int count)
603e36361d7SAndreas Färber {
604e36361d7SAndreas Färber struct earlycon_device *dev = co->data;
605e36361d7SAndreas Färber
606e36361d7SAndreas Färber owl_uart_port_write(&dev->port, s, count);
607e36361d7SAndreas Färber }
608e36361d7SAndreas Färber
609e36361d7SAndreas Färber static int __init
owl_uart_early_console_setup(struct earlycon_device * device,const char * opt)610e36361d7SAndreas Färber owl_uart_early_console_setup(struct earlycon_device *device, const char *opt)
611e36361d7SAndreas Färber {
612e36361d7SAndreas Färber if (!device->port.membase)
613e36361d7SAndreas Färber return -ENODEV;
614e36361d7SAndreas Färber
615e36361d7SAndreas Färber device->con->write = owl_uart_early_console_write;
616e36361d7SAndreas Färber
617e36361d7SAndreas Färber return 0;
618e36361d7SAndreas Färber }
619e36361d7SAndreas Färber OF_EARLYCON_DECLARE(owl, "actions,owl-uart",
620e36361d7SAndreas Färber owl_uart_early_console_setup);
621e36361d7SAndreas Färber
622fc60a8b6SAndreas Färber #define OWL_UART_CONSOLE (&owl_uart_console)
623fc60a8b6SAndreas Färber #else
624fc60a8b6SAndreas Färber #define OWL_UART_CONSOLE NULL
625fc60a8b6SAndreas Färber #endif
626fc60a8b6SAndreas Färber
627fc60a8b6SAndreas Färber static struct uart_driver owl_uart_driver = {
628fc60a8b6SAndreas Färber .owner = THIS_MODULE,
629fc60a8b6SAndreas Färber .driver_name = "owl-uart",
630fc60a8b6SAndreas Färber .dev_name = OWL_UART_DEV_NAME,
631fc60a8b6SAndreas Färber .nr = OWL_UART_PORT_NUM,
632fc60a8b6SAndreas Färber .cons = OWL_UART_CONSOLE,
633fc60a8b6SAndreas Färber };
634fc60a8b6SAndreas Färber
635fc60a8b6SAndreas Färber static const struct owl_uart_info owl_s500_info = {
636fc60a8b6SAndreas Färber .tx_fifosize = 16,
637fc60a8b6SAndreas Färber };
638fc60a8b6SAndreas Färber
639fc60a8b6SAndreas Färber static const struct owl_uart_info owl_s900_info = {
640fc60a8b6SAndreas Färber .tx_fifosize = 32,
641fc60a8b6SAndreas Färber };
642fc60a8b6SAndreas Färber
643fc60a8b6SAndreas Färber static const struct of_device_id owl_uart_dt_matches[] = {
644fc60a8b6SAndreas Färber { .compatible = "actions,s500-uart", .data = &owl_s500_info },
645fc60a8b6SAndreas Färber { .compatible = "actions,s900-uart", .data = &owl_s900_info },
646fc60a8b6SAndreas Färber { }
647fc60a8b6SAndreas Färber };
648fc60a8b6SAndreas Färber MODULE_DEVICE_TABLE(of, owl_uart_dt_matches);
649fc60a8b6SAndreas Färber
owl_uart_probe(struct platform_device * pdev)650fc60a8b6SAndreas Färber static int owl_uart_probe(struct platform_device *pdev)
651fc60a8b6SAndreas Färber {
652fc60a8b6SAndreas Färber const struct of_device_id *match;
653fc60a8b6SAndreas Färber const struct owl_uart_info *info = NULL;
654fc60a8b6SAndreas Färber struct resource *res_mem;
655fc60a8b6SAndreas Färber struct owl_uart_port *owl_port;
656fc60a8b6SAndreas Färber int ret, irq;
657fc60a8b6SAndreas Färber
658fc60a8b6SAndreas Färber if (pdev->dev.of_node) {
659fc60a8b6SAndreas Färber pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
660fc60a8b6SAndreas Färber match = of_match_node(owl_uart_dt_matches, pdev->dev.of_node);
661fc60a8b6SAndreas Färber if (match)
662fc60a8b6SAndreas Färber info = match->data;
663fc60a8b6SAndreas Färber }
664fc60a8b6SAndreas Färber
665fc60a8b6SAndreas Färber if (pdev->id < 0 || pdev->id >= OWL_UART_PORT_NUM) {
666fc60a8b6SAndreas Färber dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
667fc60a8b6SAndreas Färber return -EINVAL;
668fc60a8b6SAndreas Färber }
669fc60a8b6SAndreas Färber
670fc60a8b6SAndreas Färber res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
671fc60a8b6SAndreas Färber if (!res_mem) {
672fc60a8b6SAndreas Färber dev_err(&pdev->dev, "could not get mem\n");
673fc60a8b6SAndreas Färber return -ENODEV;
674fc60a8b6SAndreas Färber }
675fc60a8b6SAndreas Färber
676fc60a8b6SAndreas Färber irq = platform_get_irq(pdev, 0);
6771df21786SStephen Boyd if (irq < 0)
678fc60a8b6SAndreas Färber return irq;
679fc60a8b6SAndreas Färber
680fc60a8b6SAndreas Färber if (owl_uart_ports[pdev->id]) {
681fc60a8b6SAndreas Färber dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);
682fc60a8b6SAndreas Färber return -EBUSY;
683fc60a8b6SAndreas Färber }
684fc60a8b6SAndreas Färber
685fc60a8b6SAndreas Färber owl_port = devm_kzalloc(&pdev->dev, sizeof(*owl_port), GFP_KERNEL);
686fc60a8b6SAndreas Färber if (!owl_port)
687fc60a8b6SAndreas Färber return -ENOMEM;
688fc60a8b6SAndreas Färber
689fc60a8b6SAndreas Färber owl_port->clk = devm_clk_get(&pdev->dev, NULL);
690fc60a8b6SAndreas Färber if (IS_ERR(owl_port->clk)) {
691fc60a8b6SAndreas Färber dev_err(&pdev->dev, "could not get clk\n");
692fc60a8b6SAndreas Färber return PTR_ERR(owl_port->clk);
693fc60a8b6SAndreas Färber }
694fc60a8b6SAndreas Färber
695abf42d2fSAmit Singh Tomar ret = clk_prepare_enable(owl_port->clk);
696abf42d2fSAmit Singh Tomar if (ret) {
697abf42d2fSAmit Singh Tomar dev_err(&pdev->dev, "could not enable clk\n");
698abf42d2fSAmit Singh Tomar return ret;
699abf42d2fSAmit Singh Tomar }
700abf42d2fSAmit Singh Tomar
701fc60a8b6SAndreas Färber owl_port->port.dev = &pdev->dev;
702fc60a8b6SAndreas Färber owl_port->port.line = pdev->id;
703fc60a8b6SAndreas Färber owl_port->port.type = PORT_OWL;
704fc60a8b6SAndreas Färber owl_port->port.iotype = UPIO_MEM;
705fc60a8b6SAndreas Färber owl_port->port.mapbase = res_mem->start;
706fc60a8b6SAndreas Färber owl_port->port.irq = irq;
707fc60a8b6SAndreas Färber owl_port->port.uartclk = clk_get_rate(owl_port->clk);
708fc60a8b6SAndreas Färber if (owl_port->port.uartclk == 0) {
709fc60a8b6SAndreas Färber dev_err(&pdev->dev, "clock rate is zero\n");
710bcea0f54SMiaoqian Lin clk_disable_unprepare(owl_port->clk);
711fc60a8b6SAndreas Färber return -EINVAL;
712fc60a8b6SAndreas Färber }
713fc60a8b6SAndreas Färber owl_port->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY;
714fc60a8b6SAndreas Färber owl_port->port.x_char = 0;
715fc60a8b6SAndreas Färber owl_port->port.fifosize = (info) ? info->tx_fifosize : 16;
716fc60a8b6SAndreas Färber owl_port->port.ops = &owl_uart_ops;
717fc60a8b6SAndreas Färber
718fc60a8b6SAndreas Färber owl_uart_ports[pdev->id] = owl_port;
719fc60a8b6SAndreas Färber platform_set_drvdata(pdev, owl_port);
720fc60a8b6SAndreas Färber
721fc60a8b6SAndreas Färber ret = uart_add_one_port(&owl_uart_driver, &owl_port->port);
722fc60a8b6SAndreas Färber if (ret)
723fc60a8b6SAndreas Färber owl_uart_ports[pdev->id] = NULL;
724fc60a8b6SAndreas Färber
725fc60a8b6SAndreas Färber return ret;
726fc60a8b6SAndreas Färber }
727fc60a8b6SAndreas Färber
owl_uart_remove(struct platform_device * pdev)728fc60a8b6SAndreas Färber static int owl_uart_remove(struct platform_device *pdev)
729fc60a8b6SAndreas Färber {
730fc60a8b6SAndreas Färber struct owl_uart_port *owl_port = platform_get_drvdata(pdev);
731fc60a8b6SAndreas Färber
732fc60a8b6SAndreas Färber uart_remove_one_port(&owl_uart_driver, &owl_port->port);
733fc60a8b6SAndreas Färber owl_uart_ports[pdev->id] = NULL;
734abf42d2fSAmit Singh Tomar clk_disable_unprepare(owl_port->clk);
735fc60a8b6SAndreas Färber
736fc60a8b6SAndreas Färber return 0;
737fc60a8b6SAndreas Färber }
738fc60a8b6SAndreas Färber
739fc60a8b6SAndreas Färber static struct platform_driver owl_uart_platform_driver = {
740fc60a8b6SAndreas Färber .probe = owl_uart_probe,
741fc60a8b6SAndreas Färber .remove = owl_uart_remove,
742fc60a8b6SAndreas Färber .driver = {
743fc60a8b6SAndreas Färber .name = "owl-uart",
744fc60a8b6SAndreas Färber .of_match_table = owl_uart_dt_matches,
745fc60a8b6SAndreas Färber },
746fc60a8b6SAndreas Färber };
747fc60a8b6SAndreas Färber
owl_uart_init(void)748fc60a8b6SAndreas Färber static int __init owl_uart_init(void)
749fc60a8b6SAndreas Färber {
750fc60a8b6SAndreas Färber int ret;
751fc60a8b6SAndreas Färber
752fc60a8b6SAndreas Färber ret = uart_register_driver(&owl_uart_driver);
753fc60a8b6SAndreas Färber if (ret)
754fc60a8b6SAndreas Färber return ret;
755fc60a8b6SAndreas Färber
756fc60a8b6SAndreas Färber ret = platform_driver_register(&owl_uart_platform_driver);
757fc60a8b6SAndreas Färber if (ret)
758fc60a8b6SAndreas Färber uart_unregister_driver(&owl_uart_driver);
759fc60a8b6SAndreas Färber
760fc60a8b6SAndreas Färber return ret;
761fc60a8b6SAndreas Färber }
762fc60a8b6SAndreas Färber
owl_uart_exit(void)7636264dab6SChristophe JAILLET static void __exit owl_uart_exit(void)
764fc60a8b6SAndreas Färber {
765fc60a8b6SAndreas Färber platform_driver_unregister(&owl_uart_platform_driver);
766fc60a8b6SAndreas Färber uart_unregister_driver(&owl_uart_driver);
767fc60a8b6SAndreas Färber }
768fc60a8b6SAndreas Färber
769fc60a8b6SAndreas Färber module_init(owl_uart_init);
770fc60a8b6SAndreas Färber module_exit(owl_uart_exit);
771fc60a8b6SAndreas Färber
772fc60a8b6SAndreas Färber MODULE_LICENSE("GPL");
773