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 */ +#include #include #include #include @@ -9,6 +10,10 @@ #include #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, diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 3368c261ab62..0d0b22fc7e37 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -19,12 +19,15 @@ struct serdev_device; /** * struct serdev_device_ops - Callback operations for a serdev device + * @error: Function called with errors received from device; + * may sleep. * @receive_buf: Function called with data received from device; * returns number of bytes accepted; may sleep. * @write_wakeup: Function called when ready to transmit more data; must * not sleep. */ struct serdev_device_ops { + void (*error)(struct serdev_device *, unsigned long); int (*receive_buf)(struct serdev_device *, const unsigned char *, size_t); void (*write_wakeup)(struct serdev_device *); }; @@ -76,6 +79,11 @@ enum serdev_parity { SERDEV_PARITY_ODD, }; +#define SERDEV_ERROR_BREAK 0 +#define SERDEV_ERROR_FRAME 1 +#define SERDEV_ERROR_PARITY 2 +#define SERDEV_ERROR_OVERRUN 3 + /* * serdev controller structures */ @@ -85,6 +93,7 @@ struct serdev_controller_ops { int (*write_room)(struct serdev_controller *); int (*open)(struct serdev_controller *); void (*close)(struct serdev_controller *); + void (*set_error_mask)(struct serdev_controller *, unsigned long); void (*set_flow_control)(struct serdev_controller *, bool); int (*set_parity)(struct serdev_controller *, enum serdev_parity); unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int); @@ -190,12 +199,24 @@ static inline int serdev_controller_receive_buf(struct serdev_controller *ctrl, return serdev->ops->receive_buf(serdev, data, count); } +static inline void serdev_controller_error(struct serdev_controller *ctrl, + unsigned long errors) +{ + struct serdev_device *serdev = ctrl->serdev; + + if (!serdev || !serdev->ops->error) + return; + + serdev->ops->error(serdev, errors); +} + #if IS_ENABLED(CONFIG_SERIAL_DEV_BUS) int serdev_device_open(struct serdev_device *); void serdev_device_close(struct serdev_device *); int devm_serdev_device_open(struct device *, struct serdev_device *); unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int); +void serdev_device_set_error_mask(struct serdev_device *, unsigned long); void serdev_device_set_flow_control(struct serdev_device *, bool); int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t); void serdev_device_wait_until_sent(struct serdev_device *, long); @@ -238,6 +259,7 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev { return 0; } +static inline void serdev_device_set_error_mask(struct serdev_device *sdev, unsigned long mask) {} static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {} static inline int serdev_device_write_buf(struct serdev_device *serdev, const unsigned char *buf,