Lessons learnt from using serdev with SC16IS7XX

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In some hardware I am developing, I have used the SC16IS7XX to connect
two AVR microcontrollers back to my main processor running Linux.
These microcontrollers implement an control interface which is
modelled in Linux as a multi-function device. The set up is based on
the driver for the RAVE Supervisory Processor:
https://lwn.net/Articles/724765/

One issue during initialisation when using serdev with SC16IS7XX is
that after uart_add_one_port has been called, the child driver may be
started on a different kernel thread and may start calling the startup
or set_termios functions of the serial driver before it has finished
probing. I temporarily resolved this issue with a hack which involved
adding a "ready" boolean to the driver. However, there are two proper
solutions to this problem that I can think of: either serdev should
somehow check that the probe was completed (successfully) before
trying to load child drivers or the SC16IS7XX should have a mutex that
better enforces the order in which its functions are called
(particularly during initialization). My testing suggests that the
latter may be the better solution, since even outside of the
initialization context and when using the two ttys heavily at the same
time, it is not hard to knock the SC16IS7XX device into a state where,
at a minimum, the silicon stops sending interrupts. I think this
ordering of function calls may also be partly responsible for
"sc16is7xx 1-0048: ttySC0: Unexpected interrupt: 8" kernel messages
that are mentioned in other threads.

To support multiple ttys on a single device, the serdev code and its
DT binding need to be slightly modified. My solution is to modify the
code to parse the following:

uart56: sc16is762@48 {
   compatible = "nxp,sc16is762";
   reg = <0x48>;
   clocks = <&some_clock>;
   interrupt-parent = <&some_gpio_controller>;
   interrupts = <X IRQ_TYPE_EDGE_FALLING>;

   #address-cells = <1>;
   #size-cells = <0>;

   uart0: serial@0 {
      reg = <0>;
      uc0: microcontroller {
         compatible = "atmel,atmega328p";
         current-speed = <57600>;
      };
   };

   uart1: serial@1 {
      reg = <1>;
      uc1: microcontroller {
         compatible = "atmel,atmega328p";
         current-speed = <57600>;
      };
   };
};

The code to parse this is as follows and uses the tty_idx to indicate
which of the sc16is7xx ttys we are referring to. This works nicely
with a single chip, however, when you have more than one sc16is7xx
device, the reg value needs to be 2 and 3 for the second chip instead
0 and 1 as with the first chip. The second thing that is not so nice
about this solution is that I needed to move struct serport from
drivers/tty/serdev/serdev-ttyport.c to include/linux/serdev.h so that
I could access tty_idx from drivers/tty/serdev/core.c.

static int of_serdev_register_devices(struct serdev_controller *ctrl)
{
    struct device_node *port, *node;
    struct serdev_device *serdev = NULL;
    struct serport *serport = serdev_controller_get_drvdata(ctrl);
    int err;
    unsigned reg;
    bool found = false;

     for_each_available_child_of_node(ctrl->dev.of_node, port) {
        of_property_read_u32(port, "reg", &reg);

        if (serport->tty_idx != reg)
            continue;

        for_each_available_child_of_node(port, node) {
            if (!of_get_property(node, "compatible", NULL))
                continue;

            dev_err(&ctrl->dev, "adding child %pOF\n", node);

            serdev = serdev_device_alloc(ctrl);
            if (!serdev)
                continue;

            serdev->dev.of_node = node;

            err = serdev_device_add(serdev);
            if (err) {
                dev_err(&serdev->dev,
                    "failure adding device. status %d\n", err);
                serdev_device_put(serdev);
            } else
                found = true;
        }
    }
    if (!found)
        return -ENODEV;

    return 0;
}

Anyway, these are the results from my testing. If anyone is interested
in proposing some better solutions or collaborating on some patches, I
am happy to contribute and to do some more testing.
--
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



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux