xref: /linux/drivers/tty/serdev/serdev-ttyport.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
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 
25bed35c6dSRob Herring static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp,
26bed35c6dSRob Herring 				const unsigned char *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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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)
2345659dab2SSebastian Reichel 		return -ENOTSUPP;
2355659dab2SSebastian Reichel 
2363c635e4fSJohan Hovold 	return tty->ops->tiocmget(tty);
2375659dab2SSebastian Reichel }
2385659dab2SSebastian Reichel 
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)
2455659dab2SSebastian Reichel 		return -ENOTSUPP;
2465659dab2SSebastian Reichel 
2473c635e4fSJohan Hovold 	return tty->ops->tiocmset(tty, set, clear);
2485659dab2SSebastian Reichel }
2495659dab2SSebastian Reichel 
250bed35c6dSRob Herring static const struct serdev_controller_ops ctrl_ops = {
251bed35c6dSRob Herring 	.write_buf = ttyport_write_buf,
252bed35c6dSRob Herring 	.write_flush = ttyport_write_flush,
253bed35c6dSRob Herring 	.write_room = ttyport_write_room,
254bed35c6dSRob Herring 	.open = ttyport_open,
255bed35c6dSRob Herring 	.close = ttyport_close,
256bed35c6dSRob Herring 	.set_flow_control = ttyport_set_flow_control,
2573a19cfccSUlrich Hecht 	.set_parity = ttyport_set_parity,
258bed35c6dSRob Herring 	.set_baudrate = ttyport_set_baudrate,
259b3f80c8fSSebastian Reichel 	.wait_until_sent = ttyport_wait_until_sent,
2605659dab2SSebastian Reichel 	.get_tiocm = ttyport_get_tiocm,
2615659dab2SSebastian Reichel 	.set_tiocm = ttyport_set_tiocm,
262bed35c6dSRob Herring };
263bed35c6dSRob Herring 
264bed35c6dSRob Herring struct device *serdev_tty_port_register(struct tty_port *port,
265bed35c6dSRob Herring 					struct device *parent,
266bed35c6dSRob Herring 					struct tty_driver *drv, int idx)
267bed35c6dSRob Herring {
268bed35c6dSRob Herring 	struct serdev_controller *ctrl;
269bed35c6dSRob Herring 	struct serport *serport;
270bed35c6dSRob Herring 	int ret;
271bed35c6dSRob Herring 
272bed35c6dSRob Herring 	if (!port || !drv || !parent)
273bed35c6dSRob Herring 		return ERR_PTR(-ENODEV);
274bed35c6dSRob Herring 
275bed35c6dSRob Herring 	ctrl = serdev_controller_alloc(parent, sizeof(struct serport));
276bed35c6dSRob Herring 	if (!ctrl)
277bed35c6dSRob Herring 		return ERR_PTR(-ENOMEM);
278bed35c6dSRob Herring 	serport = serdev_controller_get_drvdata(ctrl);
279bed35c6dSRob Herring 
280bed35c6dSRob Herring 	serport->port = port;
281bed35c6dSRob Herring 	serport->tty_idx = idx;
282bed35c6dSRob Herring 	serport->tty_drv = drv;
283bed35c6dSRob Herring 
284bed35c6dSRob Herring 	ctrl->ops = &ctrl_ops;
285bed35c6dSRob Herring 
286aee5da78SJohan Hovold 	port->client_ops = &client_ops;
287aee5da78SJohan Hovold 	port->client_data = ctrl;
288aee5da78SJohan Hovold 
289bed35c6dSRob Herring 	ret = serdev_controller_add(ctrl);
290bed35c6dSRob Herring 	if (ret)
291aee5da78SJohan Hovold 		goto err_reset_data;
292bed35c6dSRob Herring 
293bed35c6dSRob Herring 	dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
294bed35c6dSRob Herring 	return &ctrl->dev;
295bed35c6dSRob Herring 
296aee5da78SJohan Hovold err_reset_data:
297aee5da78SJohan Hovold 	port->client_data = NULL;
298*0c5aae59SJohan Hovold 	port->client_ops = &tty_port_default_client_ops;
299bed35c6dSRob Herring 	serdev_controller_put(ctrl);
300aee5da78SJohan Hovold 
301bed35c6dSRob Herring 	return ERR_PTR(ret);
302bed35c6dSRob Herring }
303bed35c6dSRob Herring 
3048cde11b2SJohan Hovold int serdev_tty_port_unregister(struct tty_port *port)
305bed35c6dSRob Herring {
306bed35c6dSRob Herring 	struct serdev_controller *ctrl = port->client_data;
307bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
308bed35c6dSRob Herring 
309bed35c6dSRob Herring 	if (!serport)
3108cde11b2SJohan Hovold 		return -ENODEV;
311bed35c6dSRob Herring 
312bed35c6dSRob Herring 	serdev_controller_remove(ctrl);
313bed35c6dSRob Herring 	port->client_data = NULL;
314*0c5aae59SJohan Hovold 	port->client_ops = &tty_port_default_client_ops;
315bed35c6dSRob Herring 	serdev_controller_put(ctrl);
3168cde11b2SJohan Hovold 
3178cde11b2SJohan Hovold 	return 0;
318bed35c6dSRob Herring }
319