Re: Lessons learnt from using serdev with SC16IS7XX

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

 



On Wed, Jul 25, 2018 at 2:55 AM Michael Allwright <allsey87@xxxxxxxxx> wrote:
>
> 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/

The SC16IS7XX is a popular topic suddenly[1].

> 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 {

This structure is what I suggested to Andreas.

>          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.

This is a problem.

This needs to be solved by having the serdev ctrlr DT node be uart0/1
nodes rather than the parent node. Then the current core code should
work and you don't need reg to equal the tty index which is wrong.

> 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.

[1] https://www.spinics.net/lists/linux-serial/msg31354.html
--
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