diff options
Diffstat (limited to 'drivers/tty/serdev')
-rw-r--r-- | drivers/tty/serdev/core.c | 118 | ||||
-rw-r--r-- | drivers/tty/serdev/serdev-ttyport.c | 32 |
2 files changed, 113 insertions, 37 deletions
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 1bef39828ca7..f439c72b9e3c 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -19,6 +19,38 @@ static bool is_registered; static DEFINE_IDA(ctrl_ida); +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len; + + len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); + if (len != -ENODEV) + return len; + + return of_device_modalias(dev, buf, PAGE_SIZE); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *serdev_device_attrs[] = { + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(serdev_device); + +static int serdev_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + int rc; + + /* TODO: platform modalias */ + + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + return of_device_uevent_modalias(dev, env); +} + static void serdev_device_release(struct device *dev) { struct serdev_device *serdev = to_serdev_device(dev); @@ -26,9 +58,16 @@ static void serdev_device_release(struct device *dev) } static const struct device_type serdev_device_type = { + .groups = serdev_device_groups, + .uevent = serdev_device_uevent, .release = serdev_device_release, }; +static bool is_serdev_device(const struct device *dev) +{ + return dev->type == &serdev_device_type; +} + static void serdev_ctrl_release(struct device *dev) { struct serdev_controller *ctrl = to_serdev_controller(dev); @@ -42,6 +81,9 @@ static const struct device_type serdev_ctrl_type = { static int serdev_device_match(struct device *dev, struct device_driver *drv) { + if (!is_serdev_device(dev)) + return 0; + /* TODO: platform matching */ if (acpi_driver_match_device(dev, drv)) return 1; @@ -49,18 +91,6 @@ static int serdev_device_match(struct device *dev, struct device_driver *drv) return of_driver_match_device(dev, drv); } -static int serdev_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - int rc; - - /* TODO: platform modalias */ - rc = acpi_device_uevent_modalias(dev, env); - if (rc != -ENODEV) - return rc; - - return of_device_uevent_modalias(dev, env); -} - /** * serdev_device_add() - add a device previously constructed via serdev_device_alloc() * @serdev: serdev_device to be added @@ -132,6 +162,33 @@ void serdev_device_close(struct serdev_device *serdev) } EXPORT_SYMBOL_GPL(serdev_device_close); +static void devm_serdev_device_release(struct device *dev, void *dr) +{ + serdev_device_close(*(struct serdev_device **)dr); +} + +int devm_serdev_device_open(struct device *dev, struct serdev_device *serdev) +{ + struct serdev_device **dr; + int ret; + + dr = devres_alloc(devm_serdev_device_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = serdev_device_open(serdev); + if (ret) { + devres_free(dr); + return ret; + } + + *dr = serdev; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_serdev_device_open); + void serdev_device_write_wakeup(struct serdev_device *serdev) { complete(&serdev->write_comp); @@ -225,6 +282,18 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) } EXPORT_SYMBOL_GPL(serdev_device_set_flow_control); +int serdev_device_set_parity(struct serdev_device *serdev, + enum serdev_parity parity) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->set_parity) + return -ENOTSUPP; + + return ctrl->ops->set_parity(ctrl, parity); +} +EXPORT_SYMBOL_GPL(serdev_device_set_parity); + void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout) { struct serdev_controller *ctrl = serdev->ctrl; @@ -268,37 +337,16 @@ static int serdev_drv_probe(struct device *dev) static int serdev_drv_remove(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); - - sdrv->remove(to_serdev_device(dev)); + if (sdrv->remove) + sdrv->remove(to_serdev_device(dev)); return 0; } -static ssize_t modalias_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int len; - - len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); - if (len != -ENODEV) - return len; - - return of_device_modalias(dev, buf, PAGE_SIZE); -} -DEVICE_ATTR_RO(modalias); - -static struct attribute *serdev_device_attrs[] = { - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(serdev_device); - static struct bus_type serdev_bus_type = { .name = "serial", .match = serdev_device_match, .probe = serdev_drv_probe, .remove = serdev_drv_remove, - .uevent = serdev_uevent, - .dev_groups = serdev_device_groups, }; /** diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index 247788a16f0b..fa1672993b4c 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -59,7 +59,8 @@ static void ttyport_write_wakeup(struct tty_port *port) test_bit(SERPORT_ACTIVE, &serport->flags)) serdev_controller_write_wakeup(ctrl); - wake_up_interruptible_poll(&tty->write_wait, POLLOUT); + /* Wake up any tty_wait_until_sent() */ + wake_up_interruptible(&tty->write_wait); tty_kref_put(tty); } @@ -122,6 +123,8 @@ static int ttyport_open(struct serdev_controller *ctrl) if (ret) goto err_close; + tty_unlock(serport->tty); + /* Bring the UART into a known 8 bits no parity hw fc state */ ktermios = tty->termios; ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | @@ -131,11 +134,12 @@ static int ttyport_open(struct serdev_controller *ctrl) ktermios.c_cflag &= ~(CSIZE | PARENB); ktermios.c_cflag |= CS8; ktermios.c_cflag |= CRTSCTS; + /* Hangups are not supported so make sure to ignore carrier detect. */ + ktermios.c_cflag |= CLOCAL; tty_set_termios(tty, &ktermios); set_bit(SERPORT_ACTIVE, &serport->flags); - tty_unlock(serport->tty); return 0; err_close: @@ -190,6 +194,29 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable tty_set_termios(tty, &ktermios); } +static int ttyport_set_parity(struct serdev_controller *ctrl, + enum serdev_parity parity) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + struct ktermios ktermios = tty->termios; + + ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR); + if (parity != SERDEV_PARITY_NONE) { + ktermios.c_cflag |= PARENB; + if (parity == SERDEV_PARITY_ODD) + ktermios.c_cflag |= PARODD; + } + + tty_set_termios(tty, &ktermios); + + if ((tty->termios.c_cflag & (PARENB | PARODD | CMSPAR)) != + (ktermios.c_cflag & (PARENB | PARODD | CMSPAR))) + return -EINVAL; + + return 0; +} + static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) { struct serport *serport = serdev_controller_get_drvdata(ctrl); @@ -227,6 +254,7 @@ static const struct serdev_controller_ops ctrl_ops = { .open = ttyport_open, .close = ttyport_close, .set_flow_control = ttyport_set_flow_control, + .set_parity = ttyport_set_parity, .set_baudrate = ttyport_set_baudrate, .wait_until_sent = ttyport_wait_until_sent, .get_tiocm = ttyport_get_tiocm, |