diff options
author | Magnus Damm <damm+renesas@opensource.se> | 2021-12-12 22:21:28 +0900 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-12-30 13:29:56 +0100 |
commit | d8e9a406a931f687945703a4bac45042eb81ce92 (patch) | |
tree | e93562187f9adb69b1d7002ae1e1c303ec525482 /drivers/tty | |
parent | ad234e2bac274a43c9fa540bde8cd9f0c627b71f (diff) |
serdev: BREAK/FRAME/PARITY/OVERRUN notification prototype V2
Allow serdev device drivers get notified by hardware errors such as BREAK,
FRAME, PARITY and OVERRUN.
With this patch, in the event of an error detected in the UART device driver
the serdev_device_driver will get the newly introduced ->error() callback
invoked if serdev_device_set_error_mask() has previously been used to enable
the type of error. The errors are taken straight from the TTY layer and fed
into the serdev_device_driver after filtering out only enabled errors.
Without this patch the hardware errors never reach the serdev_device_driver.
Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
Link: https://lore.kernel.org/r/163931528842.27756.3665040315954968747.sendpatchset@octo
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/serdev/core.c | 11 | ||||
-rw-r--r-- | drivers/tty/serdev/serdev-ttyport.c | 49 |
2 files changed, 60 insertions, 0 deletions
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index f1324fe99378..5c25ae20e508 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -349,6 +349,17 @@ unsigned int serdev_device_set_baudrate(struct serdev_device *serdev, unsigned i } EXPORT_SYMBOL_GPL(serdev_device_set_baudrate); +void serdev_device_set_error_mask(struct serdev_device *serdev, unsigned long mask) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->set_error_mask) + return; + + ctrl->ops->set_error_mask(ctrl, mask); +} +EXPORT_SYMBOL_GPL(serdev_device_set_error_mask); + void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) { struct serdev_controller *ctrl = serdev->ctrl; diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index d367803e2044..239a1d5c66df 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> */ +#include <linux/bits.h> #include <linux/kernel.h> #include <linux/serdev.h> #include <linux/tty.h> @@ -9,6 +10,10 @@ #include <linux/poll.h> #define SERPORT_ACTIVE 1 +#define SERPORT_NOTIFY_BREAK 2 +#define SERPORT_NOTIFY_FRAME 3 +#define SERPORT_NOTIFY_PARITY 4 +#define SERPORT_NOTIFY_OVERRUN 5 struct serport { struct tty_port *port; @@ -27,11 +32,39 @@ static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp, { struct serdev_controller *ctrl = port->client_data; struct serport *serport = serdev_controller_get_drvdata(ctrl); + unsigned long errors = 0; + unsigned int i; int ret; if (!test_bit(SERPORT_ACTIVE, &serport->flags)) return 0; + for (i = 0; fp && i < count; i++) { + switch (fp[i]) { + case TTY_BREAK: + if (test_bit(SERPORT_NOTIFY_BREAK, &serport->flags)) + __set_bit(SERDEV_ERROR_BREAK, &errors); + break; + + case TTY_FRAME: + if (test_bit(SERPORT_NOTIFY_FRAME, &serport->flags)) + __set_bit(SERDEV_ERROR_FRAME, &errors); + break; + + case TTY_PARITY: + if (test_bit(SERPORT_NOTIFY_PARITY, &serport->flags)) + __set_bit(SERDEV_ERROR_PARITY, &errors); + break; + + case TTY_OVERRUN: + if (test_bit(SERPORT_NOTIFY_OVERRUN, &serport->flags)) + __set_bit(SERDEV_ERROR_OVERRUN, &errors); + break; + } + } + if (errors) + serdev_controller_error(ctrl, errors); + ret = serdev_controller_receive_buf(ctrl, cp, count); dev_WARN_ONCE(&ctrl->dev, ret < 0 || ret > count, @@ -180,6 +213,21 @@ static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigne return ktermios.c_ospeed; } +static void ttyport_set_error_mask(struct serdev_controller *ctrl, + unsigned long m) +{ + struct serport *sp = serdev_controller_get_drvdata(ctrl); + + assign_bit(SERPORT_NOTIFY_BREAK, &sp->flags, + m & BIT(SERDEV_ERROR_BREAK)); + assign_bit(SERPORT_NOTIFY_FRAME, &sp->flags, + m & BIT(SERDEV_ERROR_FRAME)); + assign_bit(SERPORT_NOTIFY_PARITY, &sp->flags, + m & BIT(SERDEV_ERROR_PARITY)); + assign_bit(SERPORT_NOTIFY_OVERRUN, &sp->flags, + m & BIT(SERDEV_ERROR_OVERRUN)); +} + static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable) { struct serport *serport = serdev_controller_get_drvdata(ctrl); @@ -253,6 +301,7 @@ static const struct serdev_controller_ops ctrl_ops = { .write_room = ttyport_write_room, .open = ttyport_open, .close = ttyport_close, + .set_error_mask = ttyport_set_error_mask, .set_flow_control = ttyport_set_flow_control, .set_parity = ttyport_set_parity, .set_baudrate = ttyport_set_baudrate, |