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