1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2bed35c6dSRob Herring /*
3bed35c6dSRob Herring  * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <[email protected]>
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)250468a807SJiri 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);
30*fed99212SFrancesco Dolcini 	size_t 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 
37*fed99212SFrancesco Dolcini 	dev_WARN_ONCE(&ctrl->dev, ret > count,
38*fed99212SFrancesco Dolcini 				"receive_buf returns %zu (count = %zu)\n",
39eb281683SJohan Hovold 				ret, count);
40*fed99212SFrancesco Dolcini 	if (ret > count)
41eb281683SJohan Hovold 		return count;
42eb281683SJohan Hovold 
43eb281683SJohan Hovold 	return ret;
44bed35c6dSRob Herring }
45bed35c6dSRob Herring 
ttyport_write_wakeup(struct tty_port * port)46bed35c6dSRob Herring static void ttyport_write_wakeup(struct tty_port *port)
47bed35c6dSRob Herring {
48bed35c6dSRob Herring 	struct serdev_controller *ctrl = port->client_data;
49bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
508bcd4e6aSJohan Hovold 	struct tty_struct *tty;
51bed35c6dSRob Herring 
528bcd4e6aSJohan Hovold 	tty = tty_port_tty_get(port);
538bcd4e6aSJohan Hovold 	if (!tty)
548bcd4e6aSJohan Hovold 		return;
558bcd4e6aSJohan Hovold 
568bcd4e6aSJohan Hovold 	if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
57b3f80c8fSSebastian Reichel 	    test_bit(SERPORT_ACTIVE, &serport->flags))
58bed35c6dSRob Herring 		serdev_controller_write_wakeup(ctrl);
59b3f80c8fSSebastian Reichel 
60afe3eb60SJohan Hovold 	/* Wake up any tty_wait_until_sent() */
61afe3eb60SJohan Hovold 	wake_up_interruptible(&tty->write_wait);
628bcd4e6aSJohan Hovold 
638bcd4e6aSJohan Hovold 	tty_kref_put(tty);
64bed35c6dSRob Herring }
65bed35c6dSRob Herring 
66bed35c6dSRob Herring static const struct tty_port_client_operations client_ops = {
67bed35c6dSRob Herring 	.receive_buf = ttyport_receive_buf,
68bed35c6dSRob Herring 	.write_wakeup = ttyport_write_wakeup,
69bed35c6dSRob Herring };
70bed35c6dSRob Herring 
71bed35c6dSRob Herring /*
72bed35c6dSRob Herring  * Callback functions from the serdev core.
73bed35c6dSRob Herring  */
74bed35c6dSRob Herring 
ttyport_write_buf(struct serdev_controller * ctrl,const u8 * data,size_t len)75f2470d2bSJiri Slaby (SUSE) static ssize_t ttyport_write_buf(struct serdev_controller *ctrl, const u8 *data, size_t len)
76bed35c6dSRob Herring {
77bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
78bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
79bed35c6dSRob Herring 
80bed35c6dSRob Herring 	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
81bed35c6dSRob Herring 		return 0;
82bed35c6dSRob Herring 
83bed35c6dSRob Herring 	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
84bed35c6dSRob Herring 	return tty->ops->write(serport->tty, data, len);
85bed35c6dSRob Herring }
86bed35c6dSRob Herring 
ttyport_write_flush(struct serdev_controller * ctrl)87bed35c6dSRob Herring static void ttyport_write_flush(struct serdev_controller *ctrl)
88bed35c6dSRob Herring {
89bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
90bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
91bed35c6dSRob Herring 
92bed35c6dSRob Herring 	tty_driver_flush_buffer(tty);
93bed35c6dSRob Herring }
94bed35c6dSRob Herring 
ttyport_open(struct serdev_controller * ctrl)95bed35c6dSRob Herring static int ttyport_open(struct serdev_controller *ctrl)
96bed35c6dSRob Herring {
97bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
98bed35c6dSRob Herring 	struct tty_struct *tty;
99bed35c6dSRob Herring 	struct ktermios ktermios;
1007c63838eSJohan Hovold 	int ret;
101bed35c6dSRob Herring 
102bed35c6dSRob Herring 	tty = tty_init_dev(serport->tty_drv, serport->tty_idx);
10310d258c5SDan Carpenter 	if (IS_ERR(tty))
10410d258c5SDan Carpenter 		return PTR_ERR(tty);
105bed35c6dSRob Herring 	serport->tty = tty;
106bed35c6dSRob Herring 
1077c63838eSJohan Hovold 	if (!tty->ops->open || !tty->ops->close) {
1087c63838eSJohan Hovold 		ret = -ENODEV;
109dee7d0f3SJohan Hovold 		goto err_unlock;
1107c63838eSJohan Hovold 	}
111dee7d0f3SJohan Hovold 
1127c63838eSJohan Hovold 	ret = tty->ops->open(serport->tty, NULL);
1137c63838eSJohan Hovold 	if (ret)
1147c63838eSJohan Hovold 		goto err_close;
115bed35c6dSRob Herring 
11651899a63SJohan Hovold 	tty_unlock(serport->tty);
11751899a63SJohan Hovold 
118bed35c6dSRob Herring 	/* Bring the UART into a known 8 bits no parity hw fc state */
119bed35c6dSRob Herring 	ktermios = tty->termios;
120bed35c6dSRob Herring 	ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
121bed35c6dSRob Herring 			      INLCR | IGNCR | ICRNL | IXON);
122bed35c6dSRob Herring 	ktermios.c_oflag &= ~OPOST;
123bed35c6dSRob Herring 	ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
124bed35c6dSRob Herring 	ktermios.c_cflag &= ~(CSIZE | PARENB);
125bed35c6dSRob Herring 	ktermios.c_cflag |= CS8;
126bed35c6dSRob Herring 	ktermios.c_cflag |= CRTSCTS;
127cda64188SJohan Hovold 	/* Hangups are not supported so make sure to ignore carrier detect. */
128cda64188SJohan Hovold 	ktermios.c_cflag |= CLOCAL;
129bed35c6dSRob Herring 	tty_set_termios(tty, &ktermios);
130bed35c6dSRob Herring 
131bed35c6dSRob Herring 	set_bit(SERPORT_ACTIVE, &serport->flags);
132bed35c6dSRob Herring 
133bed35c6dSRob Herring 	return 0;
134dee7d0f3SJohan Hovold 
1357c63838eSJohan Hovold err_close:
1367c63838eSJohan Hovold 	tty->ops->close(tty, NULL);
137dee7d0f3SJohan Hovold err_unlock:
138dee7d0f3SJohan Hovold 	tty_unlock(tty);
139dee7d0f3SJohan Hovold 	tty_release_struct(tty, serport->tty_idx);
140dee7d0f3SJohan Hovold 
1417c63838eSJohan Hovold 	return ret;
142bed35c6dSRob Herring }
143bed35c6dSRob Herring 
ttyport_close(struct serdev_controller * ctrl)144bed35c6dSRob Herring static void ttyport_close(struct serdev_controller *ctrl)
145bed35c6dSRob Herring {
146bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
147bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
148bed35c6dSRob Herring 
149bed35c6dSRob Herring 	clear_bit(SERPORT_ACTIVE, &serport->flags);
150bed35c6dSRob Herring 
15190dbad8cSJohan Hovold 	tty_lock(tty);
152bed35c6dSRob Herring 	if (tty->ops->close)
153bed35c6dSRob Herring 		tty->ops->close(tty, NULL);
15490dbad8cSJohan Hovold 	tty_unlock(tty);
155bed35c6dSRob Herring 
156bed35c6dSRob Herring 	tty_release_struct(tty, serport->tty_idx);
157bed35c6dSRob Herring }
158bed35c6dSRob Herring 
ttyport_set_baudrate(struct serdev_controller * ctrl,unsigned int speed)159bed35c6dSRob Herring static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed)
160bed35c6dSRob Herring {
161bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
162bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
163bed35c6dSRob Herring 	struct ktermios ktermios = tty->termios;
164bed35c6dSRob Herring 
165bed35c6dSRob Herring 	ktermios.c_cflag &= ~CBAUD;
166bed35c6dSRob Herring 	tty_termios_encode_baud_rate(&ktermios, speed, speed);
167bed35c6dSRob Herring 
168bed35c6dSRob Herring 	/* tty_set_termios() return not checked as it is always 0 */
169bed35c6dSRob Herring 	tty_set_termios(tty, &ktermios);
17056c607b5SStefan Wahren 	return ktermios.c_ospeed;
171bed35c6dSRob Herring }
172bed35c6dSRob Herring 
ttyport_set_flow_control(struct serdev_controller * ctrl,bool enable)173bed35c6dSRob Herring static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable)
174bed35c6dSRob Herring {
175bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
176bed35c6dSRob Herring 	struct tty_struct *tty = serport->tty;
177bed35c6dSRob Herring 	struct ktermios ktermios = tty->termios;
178bed35c6dSRob Herring 
179bed35c6dSRob Herring 	if (enable)
180bed35c6dSRob Herring 		ktermios.c_cflag |= CRTSCTS;
181bed35c6dSRob Herring 	else
182bed35c6dSRob Herring 		ktermios.c_cflag &= ~CRTSCTS;
183bed35c6dSRob Herring 
184bed35c6dSRob Herring 	tty_set_termios(tty, &ktermios);
185bed35c6dSRob Herring }
186bed35c6dSRob Herring 
ttyport_set_parity(struct serdev_controller * ctrl,enum serdev_parity parity)1873a19cfccSUlrich Hecht static int ttyport_set_parity(struct serdev_controller *ctrl,
1883a19cfccSUlrich Hecht 			      enum serdev_parity parity)
1893a19cfccSUlrich Hecht {
1903a19cfccSUlrich Hecht 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
1913a19cfccSUlrich Hecht 	struct tty_struct *tty = serport->tty;
1923a19cfccSUlrich Hecht 	struct ktermios ktermios = tty->termios;
1933a19cfccSUlrich Hecht 
1943a19cfccSUlrich Hecht 	ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
1953a19cfccSUlrich Hecht 	if (parity != SERDEV_PARITY_NONE) {
1963a19cfccSUlrich Hecht 		ktermios.c_cflag |= PARENB;
1973a19cfccSUlrich Hecht 		if (parity == SERDEV_PARITY_ODD)
1983a19cfccSUlrich Hecht 			ktermios.c_cflag |= PARODD;
1993a19cfccSUlrich Hecht 	}
2003a19cfccSUlrich Hecht 
2013a19cfccSUlrich Hecht 	tty_set_termios(tty, &ktermios);
2023a19cfccSUlrich Hecht 
2033a19cfccSUlrich Hecht 	if ((tty->termios.c_cflag & (PARENB | PARODD | CMSPAR)) !=
2043a19cfccSUlrich Hecht 	    (ktermios.c_cflag & (PARENB | PARODD | CMSPAR)))
2053a19cfccSUlrich Hecht 		return -EINVAL;
2063a19cfccSUlrich Hecht 
2073a19cfccSUlrich Hecht 	return 0;
2083a19cfccSUlrich Hecht }
2093a19cfccSUlrich Hecht 
ttyport_wait_until_sent(struct serdev_controller * ctrl,long timeout)210b3f80c8fSSebastian Reichel static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout)
211b3f80c8fSSebastian Reichel {
212b3f80c8fSSebastian Reichel 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
213b3f80c8fSSebastian Reichel 	struct tty_struct *tty = serport->tty;
214b3f80c8fSSebastian Reichel 
215b3f80c8fSSebastian Reichel 	tty_wait_until_sent(tty, timeout);
216b3f80c8fSSebastian Reichel }
217b3f80c8fSSebastian Reichel 
ttyport_get_tiocm(struct serdev_controller * ctrl)2185659dab2SSebastian Reichel static int ttyport_get_tiocm(struct serdev_controller *ctrl)
2195659dab2SSebastian Reichel {
2205659dab2SSebastian Reichel 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
2215659dab2SSebastian Reichel 	struct tty_struct *tty = serport->tty;
2225659dab2SSebastian Reichel 
2235659dab2SSebastian Reichel 	if (!tty->ops->tiocmget)
22429f93a68SNeeraj Sanjay Kale 		return -EOPNOTSUPP;
2255659dab2SSebastian Reichel 
2263c635e4fSJohan Hovold 	return tty->ops->tiocmget(tty);
2275659dab2SSebastian Reichel }
2285659dab2SSebastian Reichel 
ttyport_set_tiocm(struct serdev_controller * ctrl,unsigned int set,unsigned int clear)2295659dab2SSebastian Reichel static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear)
2305659dab2SSebastian Reichel {
2315659dab2SSebastian Reichel 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
2325659dab2SSebastian Reichel 	struct tty_struct *tty = serport->tty;
2335659dab2SSebastian Reichel 
2345659dab2SSebastian Reichel 	if (!tty->ops->tiocmset)
23529f93a68SNeeraj Sanjay Kale 		return -EOPNOTSUPP;
2365659dab2SSebastian Reichel 
2373c635e4fSJohan Hovold 	return tty->ops->tiocmset(tty, set, clear);
2385659dab2SSebastian Reichel }
2395659dab2SSebastian Reichel 
ttyport_break_ctl(struct serdev_controller * ctrl,unsigned int break_state)2408eaf839eSNeeraj Sanjay Kale static int ttyport_break_ctl(struct serdev_controller *ctrl, unsigned int break_state)
2418eaf839eSNeeraj Sanjay Kale {
2428eaf839eSNeeraj Sanjay Kale 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
2438eaf839eSNeeraj Sanjay Kale 	struct tty_struct *tty = serport->tty;
2448eaf839eSNeeraj Sanjay Kale 
2458eaf839eSNeeraj Sanjay Kale 	if (!tty->ops->break_ctl)
2468eaf839eSNeeraj Sanjay Kale 		return -EOPNOTSUPP;
2478eaf839eSNeeraj Sanjay Kale 
2488eaf839eSNeeraj Sanjay Kale 	return tty->ops->break_ctl(tty, break_state);
2498eaf839eSNeeraj Sanjay Kale }
2508eaf839eSNeeraj Sanjay Kale 
251bed35c6dSRob Herring static const struct serdev_controller_ops ctrl_ops = {
252bed35c6dSRob Herring 	.write_buf = ttyport_write_buf,
253bed35c6dSRob Herring 	.write_flush = ttyport_write_flush,
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,
2628eaf839eSNeeraj Sanjay Kale 	.break_ctl = ttyport_break_ctl,
263bed35c6dSRob Herring };
264bed35c6dSRob Herring 
serdev_tty_port_register(struct tty_port * port,struct device * host,struct device * parent,struct tty_driver * drv,int idx)265bed35c6dSRob Herring struct device *serdev_tty_port_register(struct tty_port *port,
266b286f4e8STony Lindgren 					struct device *host,
267bed35c6dSRob Herring 					struct device *parent,
268bed35c6dSRob Herring 					struct tty_driver *drv, int idx)
269bed35c6dSRob Herring {
270bed35c6dSRob Herring 	struct serdev_controller *ctrl;
271bed35c6dSRob Herring 	struct serport *serport;
272bed35c6dSRob Herring 	int ret;
273bed35c6dSRob Herring 
274bed35c6dSRob Herring 	if (!port || !drv || !parent)
275bed35c6dSRob Herring 		return ERR_PTR(-ENODEV);
276bed35c6dSRob Herring 
277b286f4e8STony Lindgren 	ctrl = serdev_controller_alloc(host, parent, sizeof(struct serport));
278bed35c6dSRob Herring 	if (!ctrl)
279bed35c6dSRob Herring 		return ERR_PTR(-ENOMEM);
280bed35c6dSRob Herring 	serport = serdev_controller_get_drvdata(ctrl);
281bed35c6dSRob Herring 
282bed35c6dSRob Herring 	serport->port = port;
283bed35c6dSRob Herring 	serport->tty_idx = idx;
284bed35c6dSRob Herring 	serport->tty_drv = drv;
285bed35c6dSRob Herring 
286bed35c6dSRob Herring 	ctrl->ops = &ctrl_ops;
287bed35c6dSRob Herring 
288aee5da78SJohan Hovold 	port->client_ops = &client_ops;
289aee5da78SJohan Hovold 	port->client_data = ctrl;
290aee5da78SJohan Hovold 
291bed35c6dSRob Herring 	ret = serdev_controller_add(ctrl);
292bed35c6dSRob Herring 	if (ret)
293aee5da78SJohan Hovold 		goto err_reset_data;
294bed35c6dSRob Herring 
295bed35c6dSRob Herring 	dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
296bed35c6dSRob Herring 	return &ctrl->dev;
297bed35c6dSRob Herring 
298aee5da78SJohan Hovold err_reset_data:
299aee5da78SJohan Hovold 	port->client_data = NULL;
3000c5aae59SJohan Hovold 	port->client_ops = &tty_port_default_client_ops;
301bed35c6dSRob Herring 	serdev_controller_put(ctrl);
302aee5da78SJohan Hovold 
303bed35c6dSRob Herring 	return ERR_PTR(ret);
304bed35c6dSRob Herring }
305bed35c6dSRob Herring 
serdev_tty_port_unregister(struct tty_port * port)3068cde11b2SJohan Hovold int serdev_tty_port_unregister(struct tty_port *port)
307bed35c6dSRob Herring {
308bed35c6dSRob Herring 	struct serdev_controller *ctrl = port->client_data;
309bed35c6dSRob Herring 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
310bed35c6dSRob Herring 
311bed35c6dSRob Herring 	if (!serport)
3128cde11b2SJohan Hovold 		return -ENODEV;
313bed35c6dSRob Herring 
314bed35c6dSRob Herring 	serdev_controller_remove(ctrl);
315bed35c6dSRob Herring 	port->client_data = NULL;
3160c5aae59SJohan Hovold 	port->client_ops = &tty_port_default_client_ops;
317bed35c6dSRob Herring 	serdev_controller_put(ctrl);
3188cde11b2SJohan Hovold 
3198cde11b2SJohan Hovold 	return 0;
320bed35c6dSRob Herring }
321