1bed35c6dSRob Herring /* 2bed35c6dSRob Herring * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> 3bed35c6dSRob Herring * 4bed35c6dSRob Herring * This program is free software; you can redistribute it and/or modify 5bed35c6dSRob Herring * it under the terms of the GNU General Public License version 2 and 6bed35c6dSRob Herring * only version 2 as published by the Free Software Foundation. 7bed35c6dSRob Herring * 8bed35c6dSRob Herring * This program is distributed in the hope that it will be useful, 9bed35c6dSRob Herring * but WITHOUT ANY WARRANTY; without even the implied warranty of 10bed35c6dSRob Herring * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11bed35c6dSRob Herring * GNU General Public License for more details. 12bed35c6dSRob Herring */ 13bed35c6dSRob Herring #include <linux/kernel.h> 14bed35c6dSRob Herring #include <linux/serdev.h> 15bed35c6dSRob Herring #include <linux/tty.h> 16bed35c6dSRob Herring #include <linux/tty_driver.h> 17b3f80c8fSSebastian Reichel #include <linux/poll.h> 18bed35c6dSRob Herring 19bed35c6dSRob Herring #define SERPORT_ACTIVE 1 20bed35c6dSRob Herring 21bed35c6dSRob Herring struct serport { 22bed35c6dSRob Herring struct tty_port *port; 23bed35c6dSRob Herring struct tty_struct *tty; 24bed35c6dSRob Herring struct tty_driver *tty_drv; 25bed35c6dSRob Herring int tty_idx; 26bed35c6dSRob Herring unsigned long flags; 27bed35c6dSRob Herring }; 28bed35c6dSRob Herring 29bed35c6dSRob Herring /* 30bed35c6dSRob Herring * Callback functions from the tty port. 31bed35c6dSRob Herring */ 32bed35c6dSRob Herring 33bed35c6dSRob Herring static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp, 34bed35c6dSRob Herring const unsigned char *fp, size_t count) 35bed35c6dSRob Herring { 36bed35c6dSRob Herring struct serdev_controller *ctrl = port->client_data; 37bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 38bed35c6dSRob Herring 39bed35c6dSRob Herring if (!test_bit(SERPORT_ACTIVE, &serport->flags)) 40bed35c6dSRob Herring return 0; 41bed35c6dSRob Herring 42bed35c6dSRob Herring return serdev_controller_receive_buf(ctrl, cp, count); 43bed35c6dSRob Herring } 44bed35c6dSRob Herring 45bed35c6dSRob Herring static void ttyport_write_wakeup(struct tty_port *port) 46bed35c6dSRob Herring { 47bed35c6dSRob Herring struct serdev_controller *ctrl = port->client_data; 48bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 49bed35c6dSRob Herring 50b3f80c8fSSebastian Reichel if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) && 51b3f80c8fSSebastian Reichel test_bit(SERPORT_ACTIVE, &serport->flags)) 52bed35c6dSRob Herring serdev_controller_write_wakeup(ctrl); 53b3f80c8fSSebastian Reichel 54b3f80c8fSSebastian Reichel wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT); 55bed35c6dSRob Herring } 56bed35c6dSRob Herring 57bed35c6dSRob Herring static const struct tty_port_client_operations client_ops = { 58bed35c6dSRob Herring .receive_buf = ttyport_receive_buf, 59bed35c6dSRob Herring .write_wakeup = ttyport_write_wakeup, 60bed35c6dSRob Herring }; 61bed35c6dSRob Herring 62bed35c6dSRob Herring /* 63bed35c6dSRob Herring * Callback functions from the serdev core. 64bed35c6dSRob Herring */ 65bed35c6dSRob Herring 66bed35c6dSRob Herring static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len) 67bed35c6dSRob Herring { 68bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 69bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 70bed35c6dSRob Herring 71bed35c6dSRob Herring if (!test_bit(SERPORT_ACTIVE, &serport->flags)) 72bed35c6dSRob Herring return 0; 73bed35c6dSRob Herring 74bed35c6dSRob Herring set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 75bed35c6dSRob Herring return tty->ops->write(serport->tty, data, len); 76bed35c6dSRob Herring } 77bed35c6dSRob Herring 78bed35c6dSRob Herring static void ttyport_write_flush(struct serdev_controller *ctrl) 79bed35c6dSRob Herring { 80bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 81bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 82bed35c6dSRob Herring 83bed35c6dSRob Herring tty_driver_flush_buffer(tty); 84bed35c6dSRob Herring } 85bed35c6dSRob Herring 86bed35c6dSRob Herring static int ttyport_write_room(struct serdev_controller *ctrl) 87bed35c6dSRob Herring { 88bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 89bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 90bed35c6dSRob Herring 91bed35c6dSRob Herring return tty_write_room(tty); 92bed35c6dSRob Herring } 93bed35c6dSRob Herring 94bed35c6dSRob Herring static int ttyport_open(struct serdev_controller *ctrl) 95bed35c6dSRob Herring { 96bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 97bed35c6dSRob Herring struct tty_struct *tty; 98bed35c6dSRob Herring struct ktermios ktermios; 99bed35c6dSRob Herring 100bed35c6dSRob Herring tty = tty_init_dev(serport->tty_drv, serport->tty_idx); 10110d258c5SDan Carpenter if (IS_ERR(tty)) 10210d258c5SDan Carpenter return PTR_ERR(tty); 103bed35c6dSRob Herring serport->tty = tty; 104bed35c6dSRob Herring 105*dee7d0f3SJohan Hovold if (!tty->ops->open) 106*dee7d0f3SJohan Hovold goto err_unlock; 107*dee7d0f3SJohan Hovold 108bed35c6dSRob Herring tty->ops->open(serport->tty, NULL); 109bed35c6dSRob Herring 110bed35c6dSRob Herring /* Bring the UART into a known 8 bits no parity hw fc state */ 111bed35c6dSRob Herring ktermios = tty->termios; 112bed35c6dSRob Herring ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | 113bed35c6dSRob Herring INLCR | IGNCR | ICRNL | IXON); 114bed35c6dSRob Herring ktermios.c_oflag &= ~OPOST; 115bed35c6dSRob Herring ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 116bed35c6dSRob Herring ktermios.c_cflag &= ~(CSIZE | PARENB); 117bed35c6dSRob Herring ktermios.c_cflag |= CS8; 118bed35c6dSRob Herring ktermios.c_cflag |= CRTSCTS; 119bed35c6dSRob Herring tty_set_termios(tty, &ktermios); 120bed35c6dSRob Herring 121bed35c6dSRob Herring set_bit(SERPORT_ACTIVE, &serport->flags); 122bed35c6dSRob Herring 123bed35c6dSRob Herring tty_unlock(serport->tty); 124bed35c6dSRob Herring return 0; 125*dee7d0f3SJohan Hovold 126*dee7d0f3SJohan Hovold err_unlock: 127*dee7d0f3SJohan Hovold tty_unlock(tty); 128*dee7d0f3SJohan Hovold tty_release_struct(tty, serport->tty_idx); 129*dee7d0f3SJohan Hovold 130*dee7d0f3SJohan Hovold return -ENODEV; 131bed35c6dSRob Herring } 132bed35c6dSRob Herring 133bed35c6dSRob Herring static void ttyport_close(struct serdev_controller *ctrl) 134bed35c6dSRob Herring { 135bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 136bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 137bed35c6dSRob Herring 138bed35c6dSRob Herring clear_bit(SERPORT_ACTIVE, &serport->flags); 139bed35c6dSRob Herring 140bed35c6dSRob Herring if (tty->ops->close) 141bed35c6dSRob Herring tty->ops->close(tty, NULL); 142bed35c6dSRob Herring 143bed35c6dSRob Herring tty_release_struct(tty, serport->tty_idx); 144bed35c6dSRob Herring } 145bed35c6dSRob Herring 146bed35c6dSRob Herring static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed) 147bed35c6dSRob Herring { 148bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 149bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 150bed35c6dSRob Herring struct ktermios ktermios = tty->termios; 151bed35c6dSRob Herring 152bed35c6dSRob Herring ktermios.c_cflag &= ~CBAUD; 153bed35c6dSRob Herring tty_termios_encode_baud_rate(&ktermios, speed, speed); 154bed35c6dSRob Herring 155bed35c6dSRob Herring /* tty_set_termios() return not checked as it is always 0 */ 156bed35c6dSRob Herring tty_set_termios(tty, &ktermios); 15756c607b5SStefan Wahren return ktermios.c_ospeed; 158bed35c6dSRob Herring } 159bed35c6dSRob Herring 160bed35c6dSRob Herring static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable) 161bed35c6dSRob Herring { 162bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 163bed35c6dSRob Herring struct tty_struct *tty = serport->tty; 164bed35c6dSRob Herring struct ktermios ktermios = tty->termios; 165bed35c6dSRob Herring 166bed35c6dSRob Herring if (enable) 167bed35c6dSRob Herring ktermios.c_cflag |= CRTSCTS; 168bed35c6dSRob Herring else 169bed35c6dSRob Herring ktermios.c_cflag &= ~CRTSCTS; 170bed35c6dSRob Herring 171bed35c6dSRob Herring tty_set_termios(tty, &ktermios); 172bed35c6dSRob Herring } 173bed35c6dSRob Herring 174b3f80c8fSSebastian Reichel static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) 175b3f80c8fSSebastian Reichel { 176b3f80c8fSSebastian Reichel struct serport *serport = serdev_controller_get_drvdata(ctrl); 177b3f80c8fSSebastian Reichel struct tty_struct *tty = serport->tty; 178b3f80c8fSSebastian Reichel 179b3f80c8fSSebastian Reichel tty_wait_until_sent(tty, timeout); 180b3f80c8fSSebastian Reichel } 181b3f80c8fSSebastian Reichel 1825659dab2SSebastian Reichel static int ttyport_get_tiocm(struct serdev_controller *ctrl) 1835659dab2SSebastian Reichel { 1845659dab2SSebastian Reichel struct serport *serport = serdev_controller_get_drvdata(ctrl); 1855659dab2SSebastian Reichel struct tty_struct *tty = serport->tty; 1865659dab2SSebastian Reichel 1875659dab2SSebastian Reichel if (!tty->ops->tiocmget) 1885659dab2SSebastian Reichel return -ENOTSUPP; 1895659dab2SSebastian Reichel 1905659dab2SSebastian Reichel return tty->driver->ops->tiocmget(tty); 1915659dab2SSebastian Reichel } 1925659dab2SSebastian Reichel 1935659dab2SSebastian Reichel static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear) 1945659dab2SSebastian Reichel { 1955659dab2SSebastian Reichel struct serport *serport = serdev_controller_get_drvdata(ctrl); 1965659dab2SSebastian Reichel struct tty_struct *tty = serport->tty; 1975659dab2SSebastian Reichel 1985659dab2SSebastian Reichel if (!tty->ops->tiocmset) 1995659dab2SSebastian Reichel return -ENOTSUPP; 2005659dab2SSebastian Reichel 2015659dab2SSebastian Reichel return tty->driver->ops->tiocmset(tty, set, clear); 2025659dab2SSebastian Reichel } 2035659dab2SSebastian Reichel 204bed35c6dSRob Herring static const struct serdev_controller_ops ctrl_ops = { 205bed35c6dSRob Herring .write_buf = ttyport_write_buf, 206bed35c6dSRob Herring .write_flush = ttyport_write_flush, 207bed35c6dSRob Herring .write_room = ttyport_write_room, 208bed35c6dSRob Herring .open = ttyport_open, 209bed35c6dSRob Herring .close = ttyport_close, 210bed35c6dSRob Herring .set_flow_control = ttyport_set_flow_control, 211bed35c6dSRob Herring .set_baudrate = ttyport_set_baudrate, 212b3f80c8fSSebastian Reichel .wait_until_sent = ttyport_wait_until_sent, 2135659dab2SSebastian Reichel .get_tiocm = ttyport_get_tiocm, 2145659dab2SSebastian Reichel .set_tiocm = ttyport_set_tiocm, 215bed35c6dSRob Herring }; 216bed35c6dSRob Herring 217bed35c6dSRob Herring struct device *serdev_tty_port_register(struct tty_port *port, 218bed35c6dSRob Herring struct device *parent, 219bed35c6dSRob Herring struct tty_driver *drv, int idx) 220bed35c6dSRob Herring { 221aee5da78SJohan Hovold const struct tty_port_client_operations *old_ops; 222bed35c6dSRob Herring struct serdev_controller *ctrl; 223bed35c6dSRob Herring struct serport *serport; 224bed35c6dSRob Herring int ret; 225bed35c6dSRob Herring 226bed35c6dSRob Herring if (!port || !drv || !parent) 227bed35c6dSRob Herring return ERR_PTR(-ENODEV); 228bed35c6dSRob Herring 229bed35c6dSRob Herring ctrl = serdev_controller_alloc(parent, sizeof(struct serport)); 230bed35c6dSRob Herring if (!ctrl) 231bed35c6dSRob Herring return ERR_PTR(-ENOMEM); 232bed35c6dSRob Herring serport = serdev_controller_get_drvdata(ctrl); 233bed35c6dSRob Herring 234bed35c6dSRob Herring serport->port = port; 235bed35c6dSRob Herring serport->tty_idx = idx; 236bed35c6dSRob Herring serport->tty_drv = drv; 237bed35c6dSRob Herring 238bed35c6dSRob Herring ctrl->ops = &ctrl_ops; 239bed35c6dSRob Herring 240aee5da78SJohan Hovold old_ops = port->client_ops; 241aee5da78SJohan Hovold port->client_ops = &client_ops; 242aee5da78SJohan Hovold port->client_data = ctrl; 243aee5da78SJohan Hovold 244bed35c6dSRob Herring ret = serdev_controller_add(ctrl); 245bed35c6dSRob Herring if (ret) 246aee5da78SJohan Hovold goto err_reset_data; 247bed35c6dSRob Herring 248bed35c6dSRob Herring dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx); 249bed35c6dSRob Herring return &ctrl->dev; 250bed35c6dSRob Herring 251aee5da78SJohan Hovold err_reset_data: 252aee5da78SJohan Hovold port->client_data = NULL; 253aee5da78SJohan Hovold port->client_ops = old_ops; 254bed35c6dSRob Herring serdev_controller_put(ctrl); 255aee5da78SJohan Hovold 256bed35c6dSRob Herring return ERR_PTR(ret); 257bed35c6dSRob Herring } 258bed35c6dSRob Herring 2598cde11b2SJohan Hovold int serdev_tty_port_unregister(struct tty_port *port) 260bed35c6dSRob Herring { 261bed35c6dSRob Herring struct serdev_controller *ctrl = port->client_data; 262bed35c6dSRob Herring struct serport *serport = serdev_controller_get_drvdata(ctrl); 263bed35c6dSRob Herring 264bed35c6dSRob Herring if (!serport) 2658cde11b2SJohan Hovold return -ENODEV; 266bed35c6dSRob Herring 267bed35c6dSRob Herring serdev_controller_remove(ctrl); 268bed35c6dSRob Herring port->client_ops = NULL; 269bed35c6dSRob Herring port->client_data = NULL; 270bed35c6dSRob Herring serdev_controller_put(ctrl); 2718cde11b2SJohan Hovold 2728cde11b2SJohan Hovold return 0; 273bed35c6dSRob Herring } 274