xref: /openbmc/linux/drivers/tty/serdev/serdev-ttyport.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2bed35c6dSRob Herring /*
3bed35c6dSRob Herring  * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org>
4bed35c6dSRob Herring  */
5bed35c6dSRob Herring #include <linux/kernel.h>
6bed35c6dSRob Herring #include <linux/serdev.h>
7bed35c6dSRob Herring #include <linux/tty.h>
8bed35c6dSRob Herring #include <linux/tty_driver.h>
9b3f80c8fSSebastian Reichel #include <linux/poll.h>
10bed35c6dSRob Herring 
11bed35c6dSRob Herring #define SERPORT_ACTIVE		1
12bed35c6dSRob Herring 
13bed35c6dSRob Herring struct serport {
14bed35c6dSRob Herring 	struct tty_port *port;
15bed35c6dSRob Herring 	struct tty_struct *tty;
16bed35c6dSRob Herring 	struct tty_driver *tty_drv;
17bed35c6dSRob Herring 	int tty_idx;
18bed35c6dSRob Herring 	unsigned long flags;
19bed35c6dSRob Herring };
20bed35c6dSRob Herring 
21bed35c6dSRob Herring /*
22bed35c6dSRob Herring  * Callback functions from the tty port.
23bed35c6dSRob Herring  */
24bed35c6dSRob Herring 
ttyport_receive_buf(struct tty_port * port,const u8 * cp,const u8 * fp,size_t count)25*0468a807SJiri Slaby (SUSE) static size_t ttyport_receive_buf(struct tty_port *port, const u8 *cp,
260b7a2b28SJiri Slaby (SUSE) 				  const u8 *fp, size_t count)
27bed35c6dSRob Herring {
28bed35c6dSRob Herring 	struct serdev_controller *ctrl = port->client_data;
29bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
30eb281683SJohan Hovold 	int ret;
31bed35c6dSRob Herring 
32bed35c6dSRob Herring 	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
33bed35c6dSRob Herring 		return 0;
34bed35c6dSRob Herring 
35eb281683SJohan Hovold 	ret = serdev_controller_receive_buf(ctrl, cp, count);
36eb281683SJohan Hovold 
37eb281683SJohan Hovold 	dev_WARN_ONCE(&ctrl->dev, ret < 0 || ret > count,
38eb281683SJohan Hovold 				"receive_buf returns %d (count = %zu)\n",
39eb281683SJohan Hovold 				ret, count);
40eb281683SJohan Hovold 	if (ret < 0)
41eb281683SJohan Hovold 		return 0;
42eb281683SJohan Hovold 	else if (ret > count)
43eb281683SJohan Hovold 		return count;
44eb281683SJohan Hovold 
45eb281683SJohan Hovold 	return ret;
46bed35c6dSRob Herring }
47bed35c6dSRob Herring 
ttyport_write_wakeup(struct tty_port * port)48bed35c6dSRob Herring static void ttyport_write_wakeup(struct tty_port *port)
49bed35c6dSRob Herring {
50bed35c6dSRob Herring 	struct serdev_controller *ctrl = port->client_data;
51bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
528bcd4e6aSJohan Hovold 	struct tty_struct *tty;
53bed35c6dSRob Herring 
548bcd4e6aSJohan Hovold 	tty = tty_port_tty_get(port);
558bcd4e6aSJohan Hovold 	if (!tty)
568bcd4e6aSJohan Hovold 		return;
578bcd4e6aSJohan Hovold 
588bcd4e6aSJohan Hovold 	if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
59b3f80c8fSSebastian Reichel 	    test_bit(SERPORT_ACTIVE, &serport->flags))
60bed35c6dSRob Herring 		serdev_controller_write_wakeup(ctrl);
61b3f80c8fSSebastian Reichel 
62afe3eb60SJohan Hovold 	/* Wake up any tty_wait_until_sent() */
63afe3eb60SJohan Hovold 	wake_up_interruptible(&tty->write_wait);
648bcd4e6aSJohan Hovold 
658bcd4e6aSJohan Hovold 	tty_kref_put(tty);
66bed35c6dSRob Herring }
67bed35c6dSRob Herring 
68bed35c6dSRob Herring static const struct tty_port_client_operations client_ops = {
69bed35c6dSRob Herring 	.receive_buf = ttyport_receive_buf,
70bed35c6dSRob Herring 	.write_wakeup = ttyport_write_wakeup,
71bed35c6dSRob Herring };
72bed35c6dSRob Herring 
73bed35c6dSRob Herring /*
74bed35c6dSRob Herring  * Callback functions from the serdev core.
75bed35c6dSRob Herring  */
76bed35c6dSRob Herring 
ttyport_write_buf(struct serdev_controller * ctrl,const unsigned char * data,size_t len)77bed35c6dSRob Herring static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len)
78bed35c6dSRob Herring {
79bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
80bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
81bed35c6dSRob Herring 
82bed35c6dSRob Herring 	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
83bed35c6dSRob Herring 		return 0;
84bed35c6dSRob Herring 
85bed35c6dSRob Herring 	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
86bed35c6dSRob Herring 	return tty->ops->write(serport->tty, data, len);
87bed35c6dSRob Herring }
88bed35c6dSRob Herring 
ttyport_write_flush(struct serdev_controller * ctrl)89bed35c6dSRob Herring static void ttyport_write_flush(struct serdev_controller *ctrl)
90bed35c6dSRob Herring {
91bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
92bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
93bed35c6dSRob Herring 
94bed35c6dSRob Herring 	tty_driver_flush_buffer(tty);
95bed35c6dSRob Herring }
96bed35c6dSRob Herring 
ttyport_write_room(struct serdev_controller * ctrl)97bed35c6dSRob Herring static int ttyport_write_room(struct serdev_controller *ctrl)
98bed35c6dSRob Herring {
99bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
100bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
101bed35c6dSRob Herring 
102bed35c6dSRob Herring 	return tty_write_room(tty);
103bed35c6dSRob Herring }
104bed35c6dSRob Herring 
ttyport_open(struct serdev_controller * ctrl)105bed35c6dSRob Herring static int ttyport_open(struct serdev_controller *ctrl)
106bed35c6dSRob Herring {
107bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
108bed35c6dSRob Herring 	struct tty_struct *tty;
109bed35c6dSRob Herring 	struct ktermios ktermios;
1107c63838eSJohan Hovold 	int ret;
111bed35c6dSRob Herring 
112bed35c6dSRob Herring 	tty = tty_init_dev(serport->tty_drv, serport->tty_idx);
11310d258c5SDan Carpenter 	if (IS_ERR(tty))
11410d258c5SDan Carpenter 		return PTR_ERR(tty);
115bed35c6dSRob Herring 	serport->tty = tty;
116bed35c6dSRob Herring 
1177c63838eSJohan Hovold 	if (!tty->ops->open || !tty->ops->close) {
1187c63838eSJohan Hovold 		ret = -ENODEV;
119dee7d0f3SJohan Hovold 		goto err_unlock;
1207c63838eSJohan Hovold 	}
121dee7d0f3SJohan Hovold 
1227c63838eSJohan Hovold 	ret = tty->ops->open(serport->tty, NULL);
1237c63838eSJohan Hovold 	if (ret)
1247c63838eSJohan Hovold 		goto err_close;
125bed35c6dSRob Herring 
12651899a63SJohan Hovold 	tty_unlock(serport->tty);
12751899a63SJohan Hovold 
128bed35c6dSRob Herring 	/* Bring the UART into a known 8 bits no parity hw fc state */
129bed35c6dSRob Herring 	ktermios = tty->termios;
130bed35c6dSRob Herring 	ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
131bed35c6dSRob Herring 			      INLCR | IGNCR | ICRNL | IXON);
132bed35c6dSRob Herring 	ktermios.c_oflag &= ~OPOST;
133bed35c6dSRob Herring 	ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
134bed35c6dSRob Herring 	ktermios.c_cflag &= ~(CSIZE | PARENB);
135bed35c6dSRob Herring 	ktermios.c_cflag |= CS8;
136bed35c6dSRob Herring 	ktermios.c_cflag |= CRTSCTS;
137cda64188SJohan Hovold 	/* Hangups are not supported so make sure to ignore carrier detect. */
138cda64188SJohan Hovold 	ktermios.c_cflag |= CLOCAL;
139bed35c6dSRob Herring 	tty_set_termios(tty, &ktermios);
140bed35c6dSRob Herring 
141bed35c6dSRob Herring 	set_bit(SERPORT_ACTIVE, &serport->flags);
142bed35c6dSRob Herring 
143bed35c6dSRob Herring 	return 0;
144dee7d0f3SJohan Hovold 
1457c63838eSJohan Hovold err_close:
1467c63838eSJohan Hovold 	tty->ops->close(tty, NULL);
147dee7d0f3SJohan Hovold err_unlock:
148dee7d0f3SJohan Hovold 	tty_unlock(tty);
149dee7d0f3SJohan Hovold 	tty_release_struct(tty, serport->tty_idx);
150dee7d0f3SJohan Hovold 
1517c63838eSJohan Hovold 	return ret;
152bed35c6dSRob Herring }
153bed35c6dSRob Herring 
ttyport_close(struct serdev_controller * ctrl)154bed35c6dSRob Herring static void ttyport_close(struct serdev_controller *ctrl)
155bed35c6dSRob Herring {
156bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
157bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
158bed35c6dSRob Herring 
159bed35c6dSRob Herring 	clear_bit(SERPORT_ACTIVE, &serport->flags);
160bed35c6dSRob Herring 
16190dbad8cSJohan Hovold 	tty_lock(tty);
162bed35c6dSRob Herring 	if (tty->ops->close)
163bed35c6dSRob Herring 		tty->ops->close(tty, NULL);
16490dbad8cSJohan Hovold 	tty_unlock(tty);
165bed35c6dSRob Herring 
166bed35c6dSRob Herring 	tty_release_struct(tty, serport->tty_idx);
167bed35c6dSRob Herring }
168bed35c6dSRob Herring 
ttyport_set_baudrate(struct serdev_controller * ctrl,unsigned int speed)169bed35c6dSRob Herring static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed)
170bed35c6dSRob Herring {
171bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
172bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
173bed35c6dSRob Herring 	struct ktermios ktermios = tty->termios;
174bed35c6dSRob Herring 
175bed35c6dSRob Herring 	ktermios.c_cflag &= ~CBAUD;
176bed35c6dSRob Herring 	tty_termios_encode_baud_rate(&ktermios, speed, speed);
177bed35c6dSRob Herring 
178bed35c6dSRob Herring 	/* tty_set_termios() return not checked as it is always 0 */
179bed35c6dSRob Herring 	tty_set_termios(tty, &ktermios);
18056c607b5SStefan Wahren 	return ktermios.c_ospeed;
181bed35c6dSRob Herring }
182bed35c6dSRob Herring 
ttyport_set_flow_control(struct serdev_controller * ctrl,bool enable)183bed35c6dSRob Herring static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable)
184bed35c6dSRob Herring {
185bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
186bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
187bed35c6dSRob Herring 	struct ktermios ktermios = tty->termios;
188bed35c6dSRob Herring 
189bed35c6dSRob Herring 	if (enable)
190bed35c6dSRob Herring 		ktermios.c_cflag |= CRTSCTS;
191bed35c6dSRob Herring 	else
192bed35c6dSRob Herring 		ktermios.c_cflag &= ~CRTSCTS;
193bed35c6dSRob Herring 
194bed35c6dSRob Herring 	tty_set_termios(tty, &ktermios);
195bed35c6dSRob Herring }
196bed35c6dSRob Herring 
ttyport_set_parity(struct serdev_controller * ctrl,enum serdev_parity parity)1973a19cfccSUlrich Hecht static int ttyport_set_parity(struct serdev_controller *ctrl,
1983a19cfccSUlrich Hecht 			      enum serdev_parity parity)
1993a19cfccSUlrich Hecht {
2003a19cfccSUlrich Hecht 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
2013a19cfccSUlrich Hecht 	struct tty_struct *tty = serport->tty;
2023a19cfccSUlrich Hecht 	struct ktermios ktermios = tty->termios;
2033a19cfccSUlrich Hecht 
2043a19cfccSUlrich Hecht 	ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
2053a19cfccSUlrich Hecht 	if (parity != SERDEV_PARITY_NONE) {
2063a19cfccSUlrich Hecht 		ktermios.c_cflag |= PARENB;
2073a19cfccSUlrich Hecht 		if (parity == SERDEV_PARITY_ODD)
2083a19cfccSUlrich Hecht 			ktermios.c_cflag |= PARODD;
2093a19cfccSUlrich Hecht 	}
2103a19cfccSUlrich Hecht 
2113a19cfccSUlrich Hecht 	tty_set_termios(tty, &ktermios);
2123a19cfccSUlrich Hecht 
2133a19cfccSUlrich Hecht 	if ((tty->termios.c_cflag & (PARENB | PARODD | CMSPAR)) !=
2143a19cfccSUlrich Hecht 	    (ktermios.c_cflag & (PARENB | PARODD | CMSPAR)))
2153a19cfccSUlrich Hecht 		return -EINVAL;
2163a19cfccSUlrich Hecht 
2173a19cfccSUlrich Hecht 	return 0;
2183a19cfccSUlrich Hecht }
2193a19cfccSUlrich Hecht 
ttyport_wait_until_sent(struct serdev_controller * ctrl,long timeout)220b3f80c8fSSebastian Reichel static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout)
221b3f80c8fSSebastian Reichel {
222b3f80c8fSSebastian Reichel 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
223b3f80c8fSSebastian Reichel 	struct tty_struct *tty = serport->tty;
224b3f80c8fSSebastian Reichel 
225b3f80c8fSSebastian Reichel 	tty_wait_until_sent(tty, timeout);
226b3f80c8fSSebastian Reichel }
227b3f80c8fSSebastian Reichel 
ttyport_get_tiocm(struct serdev_controller * ctrl)2285659dab2SSebastian Reichel static int ttyport_get_tiocm(struct serdev_controller *ctrl)
2295659dab2SSebastian Reichel {
2305659dab2SSebastian Reichel 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
2315659dab2SSebastian Reichel 	struct tty_struct *tty = serport->tty;
2325659dab2SSebastian Reichel 
2335659dab2SSebastian Reichel 	if (!tty->ops->tiocmget)
23429f93a68SNeeraj Sanjay Kale 		return -EOPNOTSUPP;
2355659dab2SSebastian Reichel 
2363c635e4fSJohan Hovold 	return tty->ops->tiocmget(tty);
2375659dab2SSebastian Reichel }
2385659dab2SSebastian Reichel 
ttyport_set_tiocm(struct serdev_controller * ctrl,unsigned int set,unsigned int clear)2395659dab2SSebastian Reichel static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear)
2405659dab2SSebastian Reichel {
2415659dab2SSebastian Reichel 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
2425659dab2SSebastian Reichel 	struct tty_struct *tty = serport->tty;
2435659dab2SSebastian Reichel 
2445659dab2SSebastian Reichel 	if (!tty->ops->tiocmset)
24529f93a68SNeeraj Sanjay Kale 		return -EOPNOTSUPP;
2465659dab2SSebastian Reichel 
2473c635e4fSJohan Hovold 	return tty->ops->tiocmset(tty, set, clear);
2485659dab2SSebastian Reichel }
2495659dab2SSebastian Reichel 
ttyport_break_ctl(struct serdev_controller * ctrl,unsigned int break_state)2508eaf839eSNeeraj Sanjay Kale static int ttyport_break_ctl(struct serdev_controller *ctrl, unsigned int break_state)
2518eaf839eSNeeraj Sanjay Kale {
2528eaf839eSNeeraj Sanjay Kale 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
2538eaf839eSNeeraj Sanjay Kale 	struct tty_struct *tty = serport->tty;
2548eaf839eSNeeraj Sanjay Kale 
2558eaf839eSNeeraj Sanjay Kale 	if (!tty->ops->break_ctl)
2568eaf839eSNeeraj Sanjay Kale 		return -EOPNOTSUPP;
2578eaf839eSNeeraj Sanjay Kale 
2588eaf839eSNeeraj Sanjay Kale 	return tty->ops->break_ctl(tty, break_state);
2598eaf839eSNeeraj Sanjay Kale }
2608eaf839eSNeeraj Sanjay Kale 
261bed35c6dSRob Herring static const struct serdev_controller_ops ctrl_ops = {
262bed35c6dSRob Herring 	.write_buf = ttyport_write_buf,
263bed35c6dSRob Herring 	.write_flush = ttyport_write_flush,
264bed35c6dSRob Herring 	.write_room = ttyport_write_room,
265bed35c6dSRob Herring 	.open = ttyport_open,
266bed35c6dSRob Herring 	.close = ttyport_close,
267bed35c6dSRob Herring 	.set_flow_control = ttyport_set_flow_control,
2683a19cfccSUlrich Hecht 	.set_parity = ttyport_set_parity,
269bed35c6dSRob Herring 	.set_baudrate = ttyport_set_baudrate,
270b3f80c8fSSebastian Reichel 	.wait_until_sent = ttyport_wait_until_sent,
2715659dab2SSebastian Reichel 	.get_tiocm = ttyport_get_tiocm,
2725659dab2SSebastian Reichel 	.set_tiocm = ttyport_set_tiocm,
2738eaf839eSNeeraj Sanjay Kale 	.break_ctl = ttyport_break_ctl,
274bed35c6dSRob Herring };
275bed35c6dSRob Herring 
serdev_tty_port_register(struct tty_port * port,struct device * parent,struct tty_driver * drv,int idx)276bed35c6dSRob Herring struct device *serdev_tty_port_register(struct tty_port *port,
277bed35c6dSRob Herring 					struct device *parent,
278bed35c6dSRob Herring 					struct tty_driver *drv, int idx)
279bed35c6dSRob Herring {
280bed35c6dSRob Herring 	struct serdev_controller *ctrl;
281bed35c6dSRob Herring 	struct serport *serport;
282bed35c6dSRob Herring 	int ret;
283bed35c6dSRob Herring 
284bed35c6dSRob Herring 	if (!port || !drv || !parent)
285bed35c6dSRob Herring 		return ERR_PTR(-ENODEV);
286bed35c6dSRob Herring 
287bed35c6dSRob Herring 	ctrl = serdev_controller_alloc(parent, sizeof(struct serport));
288bed35c6dSRob Herring 	if (!ctrl)
289bed35c6dSRob Herring 		return ERR_PTR(-ENOMEM);
290bed35c6dSRob Herring 	serport = serdev_controller_get_drvdata(ctrl);
291bed35c6dSRob Herring 
292bed35c6dSRob Herring 	serport->port = port;
293bed35c6dSRob Herring 	serport->tty_idx = idx;
294bed35c6dSRob Herring 	serport->tty_drv = drv;
295bed35c6dSRob Herring 
296bed35c6dSRob Herring 	ctrl->ops = &ctrl_ops;
297bed35c6dSRob Herring 
298aee5da78SJohan Hovold 	port->client_ops = &client_ops;
299aee5da78SJohan Hovold 	port->client_data = ctrl;
300aee5da78SJohan Hovold 
301bed35c6dSRob Herring 	ret = serdev_controller_add(ctrl);
302bed35c6dSRob Herring 	if (ret)
303aee5da78SJohan Hovold 		goto err_reset_data;
304bed35c6dSRob Herring 
305bed35c6dSRob Herring 	dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
306bed35c6dSRob Herring 	return &ctrl->dev;
307bed35c6dSRob Herring 
308aee5da78SJohan Hovold err_reset_data:
309aee5da78SJohan Hovold 	port->client_data = NULL;
3100c5aae59SJohan Hovold 	port->client_ops = &tty_port_default_client_ops;
311bed35c6dSRob Herring 	serdev_controller_put(ctrl);
312aee5da78SJohan Hovold 
313bed35c6dSRob Herring 	return ERR_PTR(ret);
314bed35c6dSRob Herring }
315bed35c6dSRob Herring 
serdev_tty_port_unregister(struct tty_port * port)3168cde11b2SJohan Hovold int serdev_tty_port_unregister(struct tty_port *port)
317bed35c6dSRob Herring {
318bed35c6dSRob Herring 	struct serdev_controller *ctrl = port->client_data;
319bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
320bed35c6dSRob Herring 
321bed35c6dSRob Herring 	if (!serport)
3228cde11b2SJohan Hovold 		return -ENODEV;
323bed35c6dSRob Herring 
324bed35c6dSRob Herring 	serdev_controller_remove(ctrl);
325bed35c6dSRob Herring 	port->client_data = NULL;
3260c5aae59SJohan Hovold 	port->client_ops = &tty_port_default_client_ops;
327bed35c6dSRob Herring 	serdev_controller_put(ctrl);
3288cde11b2SJohan Hovold 
3298cde11b2SJohan Hovold 	return 0;
330bed35c6dSRob Herring }
331