1*e3b3d0f5SGreg 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 * This program is free software; you can redistribute it and/or modify 6bed35c6dSRob Herring * it under the terms of the GNU General Public License version 2 and 7bed35c6dSRob Herring * only version 2 as published by the Free Software Foundation. 8bed35c6dSRob Herring * 9bed35c6dSRob Herring * This program is distributed in the hope that it will be useful, 10bed35c6dSRob Herring * but WITHOUT ANY WARRANTY; without even the implied warranty of 11bed35c6dSRob Herring * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12bed35c6dSRob Herring * GNU General Public License for more details. 13bed35c6dSRob Herring */ 14bed35c6dSRob Herring #include <linux/kernel.h> 15bed35c6dSRob Herring #include <linux/serdev.h> 16bed35c6dSRob Herring #include <linux/tty.h> 17bed35c6dSRob Herring #include <linux/tty_driver.h> 18b3f80c8fSSebastian Reichel #include <linux/poll.h> 19bed35c6dSRob Herring 20bed35c6dSRob Herring #define SERPORT_ACTIVE 1 21bed35c6dSRob Herring 22bed35c6dSRob Herring struct serport { 23bed35c6dSRob Herring struct tty_port *port; 24bed35c6dSRob Herring struct tty_struct *tty; 25bed35c6dSRob Herring struct tty_driver *tty_drv; 26bed35c6dSRob Herring int tty_idx; 27bed35c6dSRob Herring unsigned long flags; 28bed35c6dSRob Herring }; 29bed35c6dSRob Herring 30bed35c6dSRob Herring /* 31bed35c6dSRob Herring * Callback functions from the tty port. 32bed35c6dSRob Herring */ 33bed35c6dSRob Herring 34bed35c6dSRob Herring static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp, 35bed35c6dSRob Herring const unsigned char *fp, size_t count) 36bed35c6dSRob Herring { 37bed35c6dSRob Herring struct serdev_controller *ctrl = port->client_data; 38bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 39bed35c6dSRob Herring 40bed35c6dSRob Herring if (!test_bit(SERPORT_ACTIVE, &serport->flags)) 41bed35c6dSRob Herring return 0; 42bed35c6dSRob Herring 43bed35c6dSRob Herring return serdev_controller_receive_buf(ctrl, cp, count); 44bed35c6dSRob Herring } 45bed35c6dSRob Herring 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); 50bed35c6dSRob Herring 51b3f80c8fSSebastian Reichel if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) && 52b3f80c8fSSebastian Reichel test_bit(SERPORT_ACTIVE, &serport->flags)) 53bed35c6dSRob Herring serdev_controller_write_wakeup(ctrl); 54b3f80c8fSSebastian Reichel 55b3f80c8fSSebastian Reichel wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT); 56bed35c6dSRob Herring } 57bed35c6dSRob Herring 58bed35c6dSRob Herring static const struct tty_port_client_operations client_ops = { 59bed35c6dSRob Herring .receive_buf = ttyport_receive_buf, 60bed35c6dSRob Herring .write_wakeup = ttyport_write_wakeup, 61bed35c6dSRob Herring }; 62bed35c6dSRob Herring 63bed35c6dSRob Herring /* 64bed35c6dSRob Herring * Callback functions from the serdev core. 65bed35c6dSRob Herring */ 66bed35c6dSRob Herring 67bed35c6dSRob Herring static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len) 68bed35c6dSRob Herring { 69bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 70bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 71bed35c6dSRob Herring 72bed35c6dSRob Herring if (!test_bit(SERPORT_ACTIVE, &serport->flags)) 73bed35c6dSRob Herring return 0; 74bed35c6dSRob Herring 75bed35c6dSRob Herring set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 76bed35c6dSRob Herring return tty->ops->write(serport->tty, data, len); 77bed35c6dSRob Herring } 78bed35c6dSRob Herring 79bed35c6dSRob Herring static void ttyport_write_flush(struct serdev_controller *ctrl) 80bed35c6dSRob Herring { 81bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 82bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 83bed35c6dSRob Herring 84bed35c6dSRob Herring tty_driver_flush_buffer(tty); 85bed35c6dSRob Herring } 86bed35c6dSRob Herring 87bed35c6dSRob Herring static int ttyport_write_room(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 return tty_write_room(tty); 93bed35c6dSRob Herring } 94bed35c6dSRob Herring 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 116bed35c6dSRob Herring /* Bring the UART into a known 8 bits no parity hw fc state */ 117bed35c6dSRob Herring ktermios = tty->termios; 118bed35c6dSRob Herring ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | 119bed35c6dSRob Herring INLCR | IGNCR | ICRNL | IXON); 120bed35c6dSRob Herring ktermios.c_oflag &= ~OPOST; 121bed35c6dSRob Herring ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 122bed35c6dSRob Herring ktermios.c_cflag &= ~(CSIZE | PARENB); 123bed35c6dSRob Herring ktermios.c_cflag |= CS8; 124bed35c6dSRob Herring ktermios.c_cflag |= CRTSCTS; 125bed35c6dSRob Herring tty_set_termios(tty, &ktermios); 126bed35c6dSRob Herring 127bed35c6dSRob Herring set_bit(SERPORT_ACTIVE, &serport->flags); 128bed35c6dSRob Herring 129bed35c6dSRob Herring tty_unlock(serport->tty); 130bed35c6dSRob Herring return 0; 131dee7d0f3SJohan Hovold 1327c63838eSJohan Hovold err_close: 1337c63838eSJohan Hovold tty->ops->close(tty, NULL); 134dee7d0f3SJohan Hovold err_unlock: 135dee7d0f3SJohan Hovold tty_unlock(tty); 136dee7d0f3SJohan Hovold tty_release_struct(tty, serport->tty_idx); 137dee7d0f3SJohan Hovold 1387c63838eSJohan Hovold return ret; 139bed35c6dSRob Herring } 140bed35c6dSRob Herring 141bed35c6dSRob Herring static void ttyport_close(struct serdev_controller *ctrl) 142bed35c6dSRob Herring { 143bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 144bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 145bed35c6dSRob Herring 146bed35c6dSRob Herring clear_bit(SERPORT_ACTIVE, &serport->flags); 147bed35c6dSRob Herring 148bed35c6dSRob Herring if (tty->ops->close) 149bed35c6dSRob Herring tty->ops->close(tty, NULL); 150bed35c6dSRob Herring 151bed35c6dSRob Herring tty_release_struct(tty, serport->tty_idx); 152bed35c6dSRob Herring } 153bed35c6dSRob Herring 154bed35c6dSRob Herring static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed) 155bed35c6dSRob Herring { 156bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 157bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 158bed35c6dSRob Herring struct ktermios ktermios = tty->termios; 159bed35c6dSRob Herring 160bed35c6dSRob Herring ktermios.c_cflag &= ~CBAUD; 161bed35c6dSRob Herring tty_termios_encode_baud_rate(&ktermios, speed, speed); 162bed35c6dSRob Herring 163bed35c6dSRob Herring /* tty_set_termios() return not checked as it is always 0 */ 164bed35c6dSRob Herring tty_set_termios(tty, &ktermios); 16556c607b5SStefan Wahren return ktermios.c_ospeed; 166bed35c6dSRob Herring } 167bed35c6dSRob Herring 168bed35c6dSRob Herring static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable) 169bed35c6dSRob Herring { 170bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 171bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 172bed35c6dSRob Herring struct ktermios ktermios = tty->termios; 173bed35c6dSRob Herring 174bed35c6dSRob Herring if (enable) 175bed35c6dSRob Herring ktermios.c_cflag |= CRTSCTS; 176bed35c6dSRob Herring else 177bed35c6dSRob Herring ktermios.c_cflag &= ~CRTSCTS; 178bed35c6dSRob Herring 179bed35c6dSRob Herring tty_set_termios(tty, &ktermios); 180bed35c6dSRob Herring } 181bed35c6dSRob Herring 182b3f80c8fSSebastian Reichel static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) 183b3f80c8fSSebastian Reichel { 184b3f80c8fSSebastian Reichel struct serport *serport = serdev_controller_get_drvdata(ctrl); 185b3f80c8fSSebastian Reichel struct tty_struct *tty = serport->tty; 186b3f80c8fSSebastian Reichel 187b3f80c8fSSebastian Reichel tty_wait_until_sent(tty, timeout); 188b3f80c8fSSebastian Reichel } 189b3f80c8fSSebastian Reichel 1905659dab2SSebastian Reichel static int ttyport_get_tiocm(struct serdev_controller *ctrl) 1915659dab2SSebastian Reichel { 1925659dab2SSebastian Reichel struct serport *serport = serdev_controller_get_drvdata(ctrl); 1935659dab2SSebastian Reichel struct tty_struct *tty = serport->tty; 1945659dab2SSebastian Reichel 1955659dab2SSebastian Reichel if (!tty->ops->tiocmget) 1965659dab2SSebastian Reichel return -ENOTSUPP; 1975659dab2SSebastian Reichel 1985659dab2SSebastian Reichel return tty->driver->ops->tiocmget(tty); 1995659dab2SSebastian Reichel } 2005659dab2SSebastian Reichel 2015659dab2SSebastian Reichel static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear) 2025659dab2SSebastian Reichel { 2035659dab2SSebastian Reichel struct serport *serport = serdev_controller_get_drvdata(ctrl); 2045659dab2SSebastian Reichel struct tty_struct *tty = serport->tty; 2055659dab2SSebastian Reichel 2065659dab2SSebastian Reichel if (!tty->ops->tiocmset) 2075659dab2SSebastian Reichel return -ENOTSUPP; 2085659dab2SSebastian Reichel 2095659dab2SSebastian Reichel return tty->driver->ops->tiocmset(tty, set, clear); 2105659dab2SSebastian Reichel } 2115659dab2SSebastian Reichel 212bed35c6dSRob Herring static const struct serdev_controller_ops ctrl_ops = { 213bed35c6dSRob Herring .write_buf = ttyport_write_buf, 214bed35c6dSRob Herring .write_flush = ttyport_write_flush, 215bed35c6dSRob Herring .write_room = ttyport_write_room, 216bed35c6dSRob Herring .open = ttyport_open, 217bed35c6dSRob Herring .close = ttyport_close, 218bed35c6dSRob Herring .set_flow_control = ttyport_set_flow_control, 219bed35c6dSRob Herring .set_baudrate = ttyport_set_baudrate, 220b3f80c8fSSebastian Reichel .wait_until_sent = ttyport_wait_until_sent, 2215659dab2SSebastian Reichel .get_tiocm = ttyport_get_tiocm, 2225659dab2SSebastian Reichel .set_tiocm = ttyport_set_tiocm, 223bed35c6dSRob Herring }; 224bed35c6dSRob Herring 225bed35c6dSRob Herring struct device *serdev_tty_port_register(struct tty_port *port, 226bed35c6dSRob Herring struct device *parent, 227bed35c6dSRob Herring struct tty_driver *drv, int idx) 228bed35c6dSRob Herring { 229aee5da78SJohan Hovold const struct tty_port_client_operations *old_ops; 230bed35c6dSRob Herring struct serdev_controller *ctrl; 231bed35c6dSRob Herring struct serport *serport; 232bed35c6dSRob Herring int ret; 233bed35c6dSRob Herring 234bed35c6dSRob Herring if (!port || !drv || !parent) 235bed35c6dSRob Herring return ERR_PTR(-ENODEV); 236bed35c6dSRob Herring 237bed35c6dSRob Herring ctrl = serdev_controller_alloc(parent, sizeof(struct serport)); 238bed35c6dSRob Herring if (!ctrl) 239bed35c6dSRob Herring return ERR_PTR(-ENOMEM); 240bed35c6dSRob Herring serport = serdev_controller_get_drvdata(ctrl); 241bed35c6dSRob Herring 242bed35c6dSRob Herring serport->port = port; 243bed35c6dSRob Herring serport->tty_idx = idx; 244bed35c6dSRob Herring serport->tty_drv = drv; 245bed35c6dSRob Herring 246bed35c6dSRob Herring ctrl->ops = &ctrl_ops; 247bed35c6dSRob Herring 248aee5da78SJohan Hovold old_ops = port->client_ops; 249aee5da78SJohan Hovold port->client_ops = &client_ops; 250aee5da78SJohan Hovold port->client_data = ctrl; 251aee5da78SJohan Hovold 252bed35c6dSRob Herring ret = serdev_controller_add(ctrl); 253bed35c6dSRob Herring if (ret) 254aee5da78SJohan Hovold goto err_reset_data; 255bed35c6dSRob Herring 256bed35c6dSRob Herring dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx); 257bed35c6dSRob Herring return &ctrl->dev; 258bed35c6dSRob Herring 259aee5da78SJohan Hovold err_reset_data: 260aee5da78SJohan Hovold port->client_data = NULL; 261aee5da78SJohan Hovold port->client_ops = old_ops; 262bed35c6dSRob Herring serdev_controller_put(ctrl); 263aee5da78SJohan Hovold 264bed35c6dSRob Herring return ERR_PTR(ret); 265bed35c6dSRob Herring } 266bed35c6dSRob Herring 2678cde11b2SJohan Hovold int serdev_tty_port_unregister(struct tty_port *port) 268bed35c6dSRob Herring { 269bed35c6dSRob Herring struct serdev_controller *ctrl = port->client_data; 270bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 271bed35c6dSRob Herring 272bed35c6dSRob Herring if (!serport) 2738cde11b2SJohan Hovold return -ENODEV; 274bed35c6dSRob Herring 275bed35c6dSRob Herring serdev_controller_remove(ctrl); 276bed35c6dSRob Herring port->client_ops = NULL; 277bed35c6dSRob Herring port->client_data = NULL; 278bed35c6dSRob Herring serdev_controller_put(ctrl); 2798cde11b2SJohan Hovold 2808cde11b2SJohan Hovold return 0; 281bed35c6dSRob Herring } 282