On Thu, Apr 17, 2014 at 03:06:44PM +0900, Yoshihiro YUNOMAE wrote: > Add tunable RX interrupt trigger I/F of FIFO buffers. > Serial devices are used as not only message communication devices but control > or sending communication devices. For the latter uses, normally small data > will be exchanged, so user applications want to receive data unit as soon as > possible for real-time tendency. If we have a sensor which sends a 1 byte data > each time and must control a device based on the sensor feedback, the RX > interrupt should be triggered for each data. > > According to HW specification of serial UART devices, RX interrupt trigger > can be changed, but the trigger is hard-coded. For example, RX interrupt trigger > in 16550A can be set to 1, 4, 8, or 14 bytes for HW, but current driver sets > the trigger to only 8bytes. > > This patch makes some devices change RX interrupt trigger from userland. > > <How to use> > - Read current setting > # cat /sys/dev/char/4\:64/rx_int_trig > 8 > > - Write user setting > # echo 1 > /sys/dev/char/4\:64/rx_int_trig > # cat /sys/dev/char/4\:64/rx_int_trig > 1 > > <Support uart devices> > - 16550A and Tegra (1, 4, 8, or 14 bytes) > - 16650V2 (8, 16, 24, or 28 bytes) > - 16654 (8, 16, 56, or 60 bytes) > - 16750 (1, 16, 32, or 56 bytes) > > Changed in V2: > - Use _IOW for TIOCSFIFORTRIG definition > - Pass the interrupt trigger value itself > > Changes in V3: > - Change I/F from ioctl(2) to sysfs(rx_int_trig) > > Changes in V4: > - Introduce fifo_bug flag in uart_8250_port structure > This is enabled only when parity is enabled and UART_BUG_PARITY is enabled > for up->bugs. If this flag is enabled, user cannot set RX trigger. > - Return -EOPNOTSUPP when it does not support device at convert_fcr2val() and > at convert_val2rxtrig() > - Set the nearest lower RX trigger when users input a meaningless value at > convert_val2rxtrig() > - Check whether p->fcr is existing at serial8250_clear_and_reinit_fifos() > - Set fcr = up->fcr in the begging of serial8250_do_set_termios() > > Changes in V5: > - Support Tegra, 16650V2, 16654, and 16750 > - Store default FCR value to up->fcr when the port is first created > - Add rx_trig_byte[] in uart_config[] for each device and use rx_trig_byte[] > in convert_fcr2val() and convert_val2rxtrig() > > Changes in V5.1: > - Fix FCR_RX_TRIG_MAX_STATE definition > > Changes in V6: > - Move FCR_RX_TRIG_* definition in 8250.h to include/uapi/linux/serial_reg.h, > rename those to UART_FCR_R_TRIG_*, and use UART_FCR_TRIGGER_MASK to > UART_FCR_R_TRIG_BITS() > - Change following function names: > convert_fcr2val() => fcr_get_rxtrig_bytes() > convert_val2rxtrig() => bytes_to_fcr_rxtrig() > - Fix typo in serial8250_do_set_termios() > - Delete the verbose error message pr_info() in bytes_to_fcr_rxtrig() > - Rename *rx_int_trig/rx_trig* to *rxtrig* for several functions or variables > (but UI remains rx_int_trig) > - Change the meaningless variable name 'val' to 'bytes' following functions: > fcr_get_rxtrig_bytes(), bytes_to_fcr_rxtrig(), do_set_rxtrig(), > do_serial8250_set_rxtrig(), and serial8250_set_attr_rxtrig() > - Use up->fcr in order to get rxtrig_bytes instead of rx_trig_raw in > fcr_get_rxtrig_bytes() > - Use conf_type->rxtrig_bytes[0] instead of switch statement for support check > in register_dev_spec_attr_grp() > - Delete the checking whether a user changed FCR or not when minimum buffer > is needed in serial8250_do_set_termios() > > Signed-off-by: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@xxxxxxxxxxx> > Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> > Cc: Jiri Slaby <jslaby@xxxxxxx> > Cc: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > Cc: Jingoo Han <jg1.han@xxxxxxxxxxx> > Cc: Aaron Sierra <asierra@xxxxxxxxxxx> > Reviewed-by: Stephen Warren <swarren@xxxxxxxxxx> > --- > drivers/tty/serial/8250/8250.h | 2 > drivers/tty/serial/8250/8250_core.c | 173 ++++++++++++++++++++++++++++++++--- > drivers/tty/serial/serial_core.c | 18 ++-- > include/linux/serial_8250.h | 2 > include/linux/serial_core.h | 4 + > include/uapi/linux/serial_reg.h | 5 + > 6 files changed, 182 insertions(+), 22 deletions(-) > > diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h > index 1ebf853..1b08c91 100644 > --- a/drivers/tty/serial/8250/8250.h > +++ b/drivers/tty/serial/8250/8250.h > @@ -12,6 +12,7 @@ > */ > > #include <linux/serial_8250.h> > +#include <linux/serial_reg.h> > #include <linux/dmaengine.h> > > struct uart_8250_dma { > @@ -60,6 +61,7 @@ struct serial8250_config { > unsigned short fifo_size; > unsigned short tx_loadsz; > unsigned char fcr; > + unsigned char rxtrig_bytes[UART_FCR_R_TRIG_MAX_STATE]; > unsigned int flags; > }; > > diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c > index 81f909c..fd1b3ec 100644 > --- a/drivers/tty/serial/8250/8250_core.c > +++ b/drivers/tty/serial/8250/8250_core.c > @@ -31,7 +31,6 @@ > #include <linux/tty.h> > #include <linux/ratelimit.h> > #include <linux/tty_flip.h> > -#include <linux/serial_reg.h> > #include <linux/serial_core.h> > #include <linux/serial.h> > #include <linux/serial_8250.h> > @@ -161,6 +160,7 @@ static const struct serial8250_config uart_config[] = { > .fifo_size = 16, > .tx_loadsz = 16, > .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, > + .rxtrig_bytes = {1, 4, 8, 14}, > .flags = UART_CAP_FIFO, > }, > [PORT_CIRRUS] = { > @@ -180,6 +180,7 @@ static const struct serial8250_config uart_config[] = { > .tx_loadsz = 16, > .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | > UART_FCR_T_TRIG_00, > + .rxtrig_bytes = {8, 16, 24, 28}, > .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, > }, > [PORT_16750] = { > @@ -188,6 +189,7 @@ static const struct serial8250_config uart_config[] = { > .tx_loadsz = 64, > .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | > UART_FCR7_64BYTE, > + .rxtrig_bytes = {1, 16, 32, 56}, > .flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE, > }, > [PORT_STARTECH] = { > @@ -209,6 +211,7 @@ static const struct serial8250_config uart_config[] = { > .tx_loadsz = 32, > .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | > UART_FCR_T_TRIG_10, > + .rxtrig_bytes = {8, 16, 56, 60}, > .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, > }, > [PORT_16850] = { > @@ -266,6 +269,7 @@ static const struct serial8250_config uart_config[] = { > .tx_loadsz = 8, > .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | > UART_FCR_T_TRIG_01, > + .rxtrig_bytes = {1, 4, 8, 14}, > .flags = UART_CAP_FIFO | UART_CAP_RTOIE, > }, > [PORT_XR17D15X] = { > @@ -531,11 +535,8 @@ static void serial8250_clear_fifos(struct uart_8250_port *p) > > void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) > { > - unsigned char fcr; > - > serial8250_clear_fifos(p); > - fcr = uart_config[p->port.type].fcr; > - serial_out(p, UART_FCR, fcr); > + serial_out(p, UART_FCR, p->fcr); > } > EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); > > @@ -2275,10 +2276,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, > { > struct uart_8250_port *up = > container_of(port, struct uart_8250_port, port); > - unsigned char cval, fcr = 0; > + unsigned char cval; > unsigned long flags; > unsigned int baud, quot; > - int fifo_bug = 0; > > switch (termios->c_cflag & CSIZE) { > case CS5: > @@ -2301,7 +2301,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, > if (termios->c_cflag & PARENB) { > cval |= UART_LCR_PARITY; > if (up->bugs & UART_BUG_PARITY) > - fifo_bug = 1; > + up->fifo_bug = true; > } > if (!(termios->c_cflag & PARODD)) > cval |= UART_LCR_EPAR; > @@ -2325,10 +2325,10 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, > quot++; > > if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { > - fcr = uart_config[port->type].fcr; > - if ((baud < 2400 && !up->dma) || fifo_bug) { > - fcr &= ~UART_FCR_TRIGGER_MASK; > - fcr |= UART_FCR_TRIGGER_1; > + /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */ > + if ((baud < 2400 && !up->dma) || up->fifo_bug) { > + up->fcr &= ~UART_FCR_TRIGGER_MASK; > + up->fcr |= UART_FCR_TRIGGER_1; > } > } > > @@ -2459,15 +2459,15 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, > * is written without DLAB set, this mode will be disabled. > */ > if (port->type == PORT_16750) > - serial_port_out(port, UART_FCR, fcr); > + serial_port_out(port, UART_FCR, up->fcr); > > serial_port_out(port, UART_LCR, cval); /* reset DLAB */ > up->lcr = cval; /* Save LCR */ > if (port->type != PORT_16750) { > /* emulated UARTs (Lucent Venus 167x) need two steps */ > - if (fcr & UART_FCR_ENABLE_FIFO) > + if (up->fcr & UART_FCR_ENABLE_FIFO) > serial_port_out(port, UART_FCR, UART_FCR_ENABLE_FIFO); > - serial_port_out(port, UART_FCR, fcr); /* set fcr */ > + serial_port_out(port, UART_FCR, up->fcr); /* set fcr */ > } > serial8250_set_mctrl(port, port->mctrl); > spin_unlock_irqrestore(&port->lock, flags); > @@ -2660,6 +2660,146 @@ static int serial8250_request_port(struct uart_port *port) > return ret; > } > > +static int fcr_get_rxtrig_bytes(struct uart_8250_port *up) > +{ > + const struct serial8250_config *conf_type = &uart_config[up->port.type]; > + unsigned char bytes; > + > + bytes = conf_type->rxtrig_bytes[UART_FCR_R_TRIG_BITS(up->fcr)]; > + > + return bytes ? bytes : -EOPNOTSUPP; > +} > + > +static int bytes_to_fcr_rxtrig(struct uart_8250_port *up, unsigned char bytes) > +{ > + const struct serial8250_config *conf_type = &uart_config[up->port.type]; > + int i; > + > + if (!conf_type->rxtrig_bytes[UART_FCR_R_TRIG_BITS(UART_FCR_R_TRIG_00)]) > + return -EOPNOTSUPP; > + > + for (i = 1; i < UART_FCR_R_TRIG_MAX_STATE; i++) { > + if (bytes < conf_type->rxtrig_bytes[i]) > + /* Use the nearest lower value */ > + return (--i) << UART_FCR_R_TRIG_SHIFT; > + } > + > + return UART_FCR_R_TRIG_11; > +} > + > +static int do_get_rxtrig(struct tty_port *port) > +{ > + struct uart_state *state = container_of(port, struct uart_state, port); > + struct uart_port *uport = state->uart_port; > + struct uart_8250_port *up = > + container_of(uport, struct uart_8250_port, port); > + > + if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1) > + return -EINVAL; > + > + return fcr_get_rxtrig_bytes(up); > +} > + > +static int do_serial8250_get_rxtrig(struct tty_port *port) > +{ > + int rxtrig_bytes; > + > + mutex_lock(&port->mutex); > + rxtrig_bytes = do_get_rxtrig(port); > + mutex_unlock(&port->mutex); > + > + return rxtrig_bytes; > +} > + > +static ssize_t serial8250_get_attr_rx_int_trig(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct tty_port *port = dev_get_drvdata(dev); > + int rxtrig_bytes; > + > + rxtrig_bytes = do_serial8250_get_rxtrig(port); > + if (rxtrig_bytes < 0) > + return rxtrig_bytes; > + > + return snprintf(buf, PAGE_SIZE, "%d\n", rxtrig_bytes); > +} > + > +static int do_set_rxtrig(struct tty_port *port, unsigned char bytes) > +{ > + struct uart_state *state = container_of(port, struct uart_state, port); > + struct uart_port *uport = state->uart_port; > + struct uart_8250_port *up = > + container_of(uport, struct uart_8250_port, port); > + int rxtrig; > + > + if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1 || > + up->fifo_bug) > + return -EINVAL; > + > + rxtrig = bytes_to_fcr_rxtrig(up, bytes); > + if (rxtrig < 0) > + return rxtrig; > + > + serial8250_clear_fifos(up); > + up->fcr &= ~UART_FCR_TRIGGER_MASK; > + up->fcr |= (unsigned char)rxtrig; > + serial_out(up, UART_FCR, up->fcr); > + return 0; > +} > + > +static int do_serial8250_set_rxtrig(struct tty_port *port, unsigned char bytes) > +{ > + int ret; > + > + mutex_lock(&port->mutex); > + ret = do_set_rxtrig(port, bytes); > + mutex_unlock(&port->mutex); > + > + return ret; > +} > + > +static ssize_t serial8250_set_attr_rx_int_trig(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t count) > +{ > + struct tty_port *port = dev_get_drvdata(dev); > + unsigned char bytes; > + int ret; > + > + if (!count) > + return -EINVAL; > + > + ret = kstrtou8(buf, 10, &bytes); > + if (ret < 0) > + return ret; > + > + ret = do_serial8250_set_rxtrig(port, bytes); > + if (ret < 0) > + return ret; > + > + return count; > +} > + > +static DEVICE_ATTR(rx_int_trig, S_IRUSR | S_IWUSR | S_IRGRP, > + serial8250_get_attr_rx_int_trig, > + serial8250_set_attr_rx_int_trig); > + As you are adding a new sysfs attribute, you have to add a Documentation/ABI/ entry as well. > +static struct attribute *serial8250_dev_attrs[] = { > + &dev_attr_rx_int_trig.attr, > + NULL, > + }; > + > +static struct attribute_group serial8250_dev_attr_group = { > + .attrs = serial8250_dev_attrs, > + }; What's wrong with the macro to create a group? > + > +static void register_dev_spec_attr_grp(struct uart_8250_port *up) > +{ > + const struct serial8250_config *conf_type = &uart_config[up->port.type]; > + > + if (conf_type->rxtrig_bytes[0]) > + up->port.dev_spec_attr_group = &serial8250_dev_attr_group; > +} > + > static void serial8250_config_port(struct uart_port *port, int flags) > { > struct uart_8250_port *up = > @@ -2708,6 +2848,9 @@ static void serial8250_config_port(struct uart_port *port, int flags) > if ((port->type == PORT_XR17V35X) || > (port->type == PORT_XR17D15X)) > port->handle_irq = exar_handle_irq; > + > + register_dev_spec_attr_grp(up); > + up->fcr = uart_config[up->port.type].fcr; > } > > static int > diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c > index 2cf5649..41ac44b 100644 > --- a/drivers/tty/serial/serial_core.c > +++ b/drivers/tty/serial/serial_core.c > @@ -2548,15 +2548,16 @@ static struct attribute *tty_dev_attrs[] = { > NULL, > }; > > -static const struct attribute_group tty_dev_attr_group = { > +static struct attribute_group tty_dev_attr_group = { > .attrs = tty_dev_attrs, > }; > > -static const struct attribute_group *tty_dev_attr_groups[] = { > - &tty_dev_attr_group, > - NULL > - }; > - > +static void make_uport_attr_grps(struct uart_port *uport) > +{ > + uport->attr_grps[0] = &tty_dev_attr_group; > + if (uport->dev_spec_attr_group) > + uport->attr_grps[1] = uport->dev_spec_attr_group; > +} > > /** > * uart_add_one_port - attach a driver-defined port structure > @@ -2607,12 +2608,15 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) > > uart_configure_port(drv, state, uport); > > + make_uport_attr_grps(uport); > + > /* > * Register the port whether it's detected or not. This allows > * setserial to be used to alter this port's parameters. > */ > tty_dev = tty_port_register_device_attr(port, drv->tty_driver, > - uport->line, uport->dev, port, tty_dev_attr_groups); > + uport->line, uport->dev, port, > + (const struct attribute_group **)uport->attr_grps); If you have to cast that hard, something is wrong here, why are you doing that? thanks, greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html