Re: Patch for SC16IS7XX

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

 



On 28 June 2015 at 23:29, Jakub Kiciński <moorray3@xxxxx> wrote:
> Michael, can you try this please?
>
> ---->8--------------
>
> From: Jakub Kicinski <kubakici@xxxxx>
> Date: Thu, 25 Jun 2015 18:17:58 +0200
> Subject: [PATCH] sc16is7xx: support multiple devices
>
> We currently register the uart driver during device probe
> which makes it hard to support more than one chip.
> Move the driver registration to module init/exit time and
> preallocate space for up to 8 lines (4-8 chips).
>
> Reported-by: Michael Allwright <michael.allwright@xxxxxx>
> Signed-off-by: Jakub Kicinski <kubakici@xxxxx>
> ---
>  drivers/tty/serial/sc16is7xx.c | 73 ++++++++++++++++++++++++++++--------------
>  1 file changed, 49 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
> index 5ccc698cbbfa..84fb3e8c3f80 100644
> --- a/drivers/tty/serial/sc16is7xx.c
> +++ b/drivers/tty/serial/sc16is7xx.c
> @@ -11,6 +11,8 @@
>   *
>   */
>
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
>  #include <linux/bitops.h>
>  #include <linux/clk.h>
>  #include <linux/delay.h>
> @@ -29,6 +31,7 @@
>  #include <linux/uaccess.h>
>
>  #define SC16IS7XX_NAME                 "sc16is7xx"
> +#define SC16IS7XX_MAX_DEVS             8
>
>  /* SC16IS7XX register definitions */
>  #define SC16IS7XX_RHR_REG              (0x00) /* RX FIFO */
> @@ -318,7 +321,6 @@ struct sc16is7xx_one {
>  };
>
>  struct sc16is7xx_port {
> -       struct uart_driver              uart;
>         struct sc16is7xx_devtype        *devtype;
>         struct regmap                   *regmap;
>         struct clk                      *clk;
> @@ -332,6 +334,14 @@ struct sc16is7xx_port {
>         struct sc16is7xx_one            p[0];
>  };
>
> +static unsigned long sc16is7xx_lines;
> +
> +static struct uart_driver sc16is7xx_uart = {
> +       .owner          = THIS_MODULE,
> +       .dev_name       = "ttySC",
> +       .nr             = SC16IS7XX_MAX_DEVS,
> +};
> +
>  #define to_sc16is7xx_port(p,e) ((container_of((p), struct sc16is7xx_port, e)))
>  #define to_sc16is7xx_one(p,e)  ((container_of((p), struct sc16is7xx_one, e)))
>
> @@ -384,6 +394,18 @@ static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
>                            mask, val);
>  }
>
> +static int sc16is7xx_alloc_line(void)
> +{
> +       int i;
> +
> +       BUILD_BUG_ON(SC16IS7XX_MAX_DEVS > BITS_PER_LONG);
> +
> +       for (i = 0; i < SC16IS7XX_MAX_DEVS; i++)
> +               if (!test_and_set_bit(i, &sc16is7xx_lines))
> +                       break;
> +
> +       return i;
> +}
>
>  static void sc16is7xx_power(struct uart_port *port, int on)
>  {
> @@ -661,7 +683,7 @@ static void sc16is7xx_ist(struct kthread_work *ws)
>         struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work);
>         int i;
>
> -       for (i = 0; i < s->uart.nr; ++i)
> +       for (i = 0; i < s->devtype->nr_uart; ++i)
>                 sc16is7xx_port_irq(s, i);
>  }
>
> @@ -1134,23 +1156,13 @@ static int sc16is7xx_probe(struct device *dev,
>         s->devtype = devtype;
>         dev_set_drvdata(dev, s);
>
> -       /* Register UART driver */
> -       s->uart.owner           = THIS_MODULE;
> -       s->uart.dev_name        = "ttySC";
> -       s->uart.nr              = devtype->nr_uart;
> -       ret = uart_register_driver(&s->uart);
> -       if (ret) {
> -               dev_err(dev, "Registering UART driver failed\n");
> -               goto out_clk;
> -       }
> -
>         init_kthread_worker(&s->kworker);
>         init_kthread_work(&s->irq_work, sc16is7xx_ist);
>         s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker,
>                                       "sc16is7xx");
>         if (IS_ERR(s->kworker_task)) {
>                 ret = PTR_ERR(s->kworker_task);
> -               goto out_uart;
> +               goto out_clk;
>         }
>         sched_setscheduler(s->kworker_task, SCHED_FIFO, &sched_param);
>
> @@ -1175,7 +1187,6 @@ static int sc16is7xx_probe(struct device *dev,
>
>         for (i = 0; i < devtype->nr_uart; ++i) {
>                 /* Initialize port data */
> -               s->p[i].port.line       = i;
>                 s->p[i].port.dev        = dev;
>                 s->p[i].port.irq        = irq;
>                 s->p[i].port.type       = PORT_SC16IS7XX;
> @@ -1185,6 +1196,12 @@ static int sc16is7xx_probe(struct device *dev,
>                 s->p[i].port.uartclk    = freq;
>                 s->p[i].port.rs485_config = sc16is7xx_config_rs485;
>                 s->p[i].port.ops        = &sc16is7xx_ops;
> +               s->p[i].port.line       = sc16is7xx_alloc_line();
> +               if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) {
> +                       ret = -ENOMEM;
> +                       goto out_ports;
> +               }
> +
>                 /* Disable all interrupts */
>                 sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_IER_REG, 0);
>                 /* Disable TX/RX */
> @@ -1195,7 +1212,7 @@ static int sc16is7xx_probe(struct device *dev,
>                 init_kthread_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
>                 init_kthread_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
>                 /* Register port */
> -               uart_add_one_port(&s->uart, &s->p[i].port);
> +               uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
>                 /* Go to suspend mode */
>                 sc16is7xx_power(&s->p[i].port, 0);
>         }
> @@ -1206,8 +1223,11 @@ static int sc16is7xx_probe(struct device *dev,
>         if (!ret)
>                 return 0;
>
> -       for (i = 0; i < s->uart.nr; i++)
> -               uart_remove_one_port(&s->uart, &s->p[i].port);
> +out_ports:
> +       for (i--; i >= 0; i--) {
> +               uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
> +               clear_bit(s->p[i].port.line, &sc16is7xx_lines);
> +       }
>
>  #ifdef CONFIG_GPIOLIB
>         if (devtype->nr_gpio)
> @@ -1217,9 +1237,6 @@ out_thread:
>  #endif
>         kthread_stop(s->kworker_task);
>
> -out_uart:
> -       uart_unregister_driver(&s->uart);
> -
>  out_clk:
>         if (!IS_ERR(s->clk))
>                 clk_disable_unprepare(s->clk);
> @@ -1237,15 +1254,15 @@ static int sc16is7xx_remove(struct device *dev)
>                 gpiochip_remove(&s->gpio);
>  #endif
>
> -       for (i = 0; i < s->uart.nr; i++) {
> -               uart_remove_one_port(&s->uart, &s->p[i].port);
> +       for (i = 0; i < s->devtype->nr_uart; i++) {
> +               uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
> +               clear_bit(s->p[i].port.line, &sc16is7xx_lines);
>                 sc16is7xx_power(&s->p[i].port, 0);
>         }
>
>         flush_kthread_worker(&s->kworker);
>         kthread_stop(s->kworker_task);
>
> -       uart_unregister_driver(&s->uart);
>         if (!IS_ERR(s->clk))
>                 clk_disable_unprepare(s->clk);
>
> @@ -1398,7 +1415,14 @@ MODULE_ALIAS("i2c:sc16is7xx");
>
>  static int __init sc16is7xx_init(void)
>  {
> -       int ret = 0;
> +       int ret;
> +
> +       ret = uart_register_driver(&sc16is7xx_uart);
> +       if (ret) {
> +               pr_err("Registering UART driver failed\n");
> +               return ret;
> +       }
> +
>  #ifdef CONFIG_SERIAL_SC16IS7XX_I2C
>         ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver);
>         if (ret < 0) {
> @@ -1427,6 +1451,7 @@ static void __exit sc16is7xx_exit(void)
>  #ifdef CONFIG_SERIAL_SC16IS7XX_SPI
>         spi_unregister_driver(&sc16is7xx_spi_uart_driver);
>  #endif
> +       uart_unregister_driver(&sc16is7xx_uart);
>  }
>  module_exit(sc16is7xx_exit);
>
> --
> 2.1.0
>
> --
> 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

Hi Jakub,

I tried your patch today and although it is a cleaner implementation
than what I had, I'm still getting the same results as I did on
Friday. When both devices are configured, the ttySC2,3 shows up in
/dev but I can not get any RX or TX using picocom. I use the device
tree to instantiate the devices and when only one device is present
there are no issues (that is after the "fix FIFO address of secondary
UART" from Bo is applied -
http://www.spinics.net/lists/linux-serial/msg17820.html).

Here is the relevant parts of my device tree that are declared under
the second I2C bus, and would stress the point, that if I comment out
one of either "uart56: sc16is762@48" or "uart78: sc16is762@49", the
other device works as expected, so this is definitely a software
problem and not a hardware / device configuration problem.

I have also attached the output from sysfs of each relevant which
perhaps contains some useful information...

uart56: sc16is762@48 {
        compatible = "nxp,sc16is762";
        reg = <0x48>;
        clocks = <&uart56_clk>;

    interrupt-parent = <&gpio2>;
    interrupts = <12 IRQ_TYPE_EDGE_FALLING>; /* gpmc_a20.gpio_44 */

    uart56_osc: uart56_osc {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <1843200>;
    };

    uart56_clk: uart56_clk {
        compatible = "gpio-gate-clock";
        clocks = <&uart56_osc>;
        #clock-cells = <0>;
        enable-gpios = <&gpio3 17 GPIO_ACTIVE_HIGH>; /*
cam_shutter.gpio_81: uart56_clk_en */
    };
};

uart78: sc16is762@49 {
        compatible = "nxp,sc16is762";
        reg = <0x49>;
        clocks = <&uart78_clk>;

    interrupt-parent = <&gpio2>;
    interrupts = <16 IRQ_TYPE_EDGE_FALLING>; /* gpmc_a24.gpio_48 */

    uart78_osc: uart78_osc {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <1843200>;
    };

    uart78_clk: uart78_clk {
        compatible = "gpio-gate-clock";
        clocks = <&uart78_osc>;
        #clock-cells = <0>;
        enable-gpios = <&gpio3 18 GPIO_ACTIVE_HIGH>; /*
cam_strobe.gpio_82: uart78_clk_en */
    };
};

Regarding not working on tty-next, I'm not actually sure what the
correct workflow for resolving these sort of issues with the community
is. Since I'm also working with linux-media who also have their own
tree, I have been pulling the changes back and forth into my the
mainline kernel for my application as patches. Perhaps there is a
better way?

All the best,

Michael
/sys/class/tty/ttySC0/dev
251:0
/sys/class/tty/ttySC0/irq
71
/sys/class/tty/ttySC0/line
0
/sys/class/tty/ttySC0/port
0x0
/sys/class/tty/ttySC0/type
108
/sys/class/tty/ttySC0/flags
0x8002000
/sys/class/tty/ttySC0/power/control
auto
/sys/class/tty/ttySC0/power/wakeup_abort_count

/sys/class/tty/ttySC0/power/wakeup_active

/sys/class/tty/ttySC0/power/wakeup_total_time_ms

/sys/class/tty/ttySC0/power/wakeup_active_count

/sys/class/tty/ttySC0/power/runtime_active_time
0
/sys/class/tty/ttySC0/power/wakeup_max_time_ms

/sys/class/tty/ttySC0/power/wakeup_count

/sys/class/tty/ttySC0/power/wakeup_last_time_ms

/sys/class/tty/ttySC0/power/wakeup
disabled
/sys/class/tty/ttySC0/power/autosuspend_delay_ms
/sys/class/tty/ttySC0/power/runtime_status
unsupported
/sys/class/tty/ttySC0/power/wakeup_expire_count

/sys/class/tty/ttySC0/power/runtime_suspended_time
0
/sys/class/tty/ttySC0/iomem_base
0x0
/sys/class/tty/ttySC0/custom_divisor
0
/sys/class/tty/ttySC0/iomem_reg_shift
0
/sys/class/tty/ttySC0/uartclk
1843200
/sys/class/tty/ttySC0/xmit_fifo_size
64
/sys/class/tty/ttySC0/close_delay
50
/sys/class/tty/ttySC0/closing_wait
3000
/sys/class/tty/ttySC0/uevent
MAJOR=251
MINOR=0
DEVNAME=ttySC0
/sys/class/tty/ttySC0/io_type
0
/sys/class/tty/ttySC1/dev
251:1
/sys/class/tty/ttySC1/irq
71
/sys/class/tty/ttySC1/line
1
/sys/class/tty/ttySC1/port
0x0
/sys/class/tty/ttySC1/type
108
/sys/class/tty/ttySC1/flags
0x8002000
/sys/class/tty/ttySC1/power/control
auto
/sys/class/tty/ttySC1/power/wakeup_abort_count

/sys/class/tty/ttySC1/power/wakeup_active

/sys/class/tty/ttySC1/power/wakeup_total_time_ms

/sys/class/tty/ttySC1/power/wakeup_active_count

/sys/class/tty/ttySC1/power/runtime_active_time
0
/sys/class/tty/ttySC1/power/wakeup_max_time_ms

/sys/class/tty/ttySC1/power/wakeup_count

/sys/class/tty/ttySC1/power/wakeup_last_time_ms

/sys/class/tty/ttySC1/power/wakeup
disabled
/sys/class/tty/ttySC1/power/autosuspend_delay_ms
/sys/class/tty/ttySC1/power/runtime_status
unsupported
/sys/class/tty/ttySC1/power/wakeup_expire_count

/sys/class/tty/ttySC1/power/runtime_suspended_time
0
/sys/class/tty/ttySC1/iomem_base
0x0
/sys/class/tty/ttySC1/custom_divisor
0
/sys/class/tty/ttySC1/iomem_reg_shift
0
/sys/class/tty/ttySC1/uartclk
1843200
/sys/class/tty/ttySC1/xmit_fifo_size
64
/sys/class/tty/ttySC1/close_delay
50
/sys/class/tty/ttySC1/closing_wait
3000
/sys/class/tty/ttySC1/uevent
MAJOR=251
MINOR=1
DEVNAME=ttySC1
/sys/class/tty/ttySC1/io_type
0
/sys/class/tty/ttySC2/dev
251:2
/sys/class/tty/ttySC2/irq
75
/sys/class/tty/ttySC2/line
2
/sys/class/tty/ttySC2/port
0x0
/sys/class/tty/ttySC2/type
108
/sys/class/tty/ttySC2/flags
0x8002000
/sys/class/tty/ttySC2/power/control
auto
/sys/class/tty/ttySC2/power/wakeup_abort_count

/sys/class/tty/ttySC2/power/wakeup_active

/sys/class/tty/ttySC2/power/wakeup_total_time_ms

/sys/class/tty/ttySC2/power/wakeup_active_count

/sys/class/tty/ttySC2/power/runtime_active_time
0
/sys/class/tty/ttySC2/power/wakeup_max_time_ms

/sys/class/tty/ttySC2/power/wakeup_count

/sys/class/tty/ttySC2/power/wakeup_last_time_ms

/sys/class/tty/ttySC2/power/wakeup
disabled
/sys/class/tty/ttySC2/power/autosuspend_delay_ms
/sys/class/tty/ttySC2/power/runtime_status
unsupported
/sys/class/tty/ttySC2/power/wakeup_expire_count

/sys/class/tty/ttySC2/power/runtime_suspended_time
0
/sys/class/tty/ttySC2/iomem_base
0x0
/sys/class/tty/ttySC2/custom_divisor
0
/sys/class/tty/ttySC2/iomem_reg_shift
0
/sys/class/tty/ttySC2/uartclk
1843200
/sys/class/tty/ttySC2/xmit_fifo_size
64
/sys/class/tty/ttySC2/close_delay
50
/sys/class/tty/ttySC2/closing_wait
3000
/sys/class/tty/ttySC2/uevent
MAJOR=251
MINOR=2
DEVNAME=ttySC2
/sys/class/tty/ttySC2/io_type
0
/sys/class/tty/ttySC3/dev
251:3
/sys/class/tty/ttySC3/irq
75
/sys/class/tty/ttySC3/line
3
/sys/class/tty/ttySC3/port
0x0
/sys/class/tty/ttySC3/type
108
/sys/class/tty/ttySC3/flags
0x8002000
/sys/class/tty/ttySC3/power/control
auto
/sys/class/tty/ttySC3/power/wakeup_abort_count

/sys/class/tty/ttySC3/power/wakeup_active

/sys/class/tty/ttySC3/power/wakeup_total_time_ms

/sys/class/tty/ttySC3/power/wakeup_active_count

/sys/class/tty/ttySC3/power/runtime_active_time
0
/sys/class/tty/ttySC3/power/wakeup_max_time_ms

/sys/class/tty/ttySC3/power/wakeup_count

/sys/class/tty/ttySC3/power/wakeup_last_time_ms

/sys/class/tty/ttySC3/power/wakeup
disabled
/sys/class/tty/ttySC3/power/autosuspend_delay_ms
/sys/class/tty/ttySC3/power/runtime_status
unsupported
/sys/class/tty/ttySC3/power/wakeup_expire_count

/sys/class/tty/ttySC3/power/runtime_suspended_time
0
/sys/class/tty/ttySC3/iomem_base
0x0
/sys/class/tty/ttySC3/custom_divisor
0
/sys/class/tty/ttySC3/iomem_reg_shift
0
/sys/class/tty/ttySC3/uartclk
1843200
/sys/class/tty/ttySC3/xmit_fifo_size
64
/sys/class/tty/ttySC3/close_delay
50
/sys/class/tty/ttySC3/closing_wait
3000
/sys/class/tty/ttySC3/uevent
MAJOR=251
MINOR=3
DEVNAME=ttySC3
/sys/class/tty/ttySC3/io_type
0

[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