Re: Patch for SC16IS7XX

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

 



On 25 June 2015 at 18:20, Jakub Kiciński <moorray3@xxxxx> wrote:
> Hi,
>
> please do not top post.
>
> On Thu, 25 Jun 2015 17:05:00 +0200, Michael Allwright wrote:
>> Well almost all my issues ;)
>>
>> At the moment there is missing support for having more than one
>> SC16IS7xx device. Because of this there is a name conflict in sysfs:
>>
>> [    3.241912] sysfs: cannot create duplicate filename '/class/tty/ttySC1'
>> ...
>> [    3.312774] [<c01b157c>] (sysfs_warn_dup) from [<c01b188c>]
>> (sysfs_do_create_link_sd.isra.2+0xa8/0xb4)
>> [    3.323974] [<c01b188c>] (sysfs_do_create_link_sd.isra.2) from
>> [<c03cd5a4>] (device_add+0x200/0x520)
>> [    3.333557] [<c03cd5a4>] (device_add) from [<c039d834>]
>> (tty_register_device_attr+0x1c8/0x240)
>> [    3.333557] [<c039d834>] (tty_register_device_attr) from
>> [<c03b8e84>] (uart_add_one_port+0x1c4/0x48c)
>> [    3.352294] [<c03b8e84>] (uart_add_one_port) from [<c03c0d0c>]
>> (sc16is7xx_i2c_probe+0x334/0x520)
>> [    3.353271] [<c03c0d0c>] (sc16is7xx_i2c_probe) from [<c04cc52c>]
>> (i2c_device_probe+0xf4/0x14c)
>> ...
>>
>> I will see if I can find a solution for this. It must be possible as
>> the USB serial core has no problem adding ttyUSBX interfaces for each
>> device plugged in... Any hints as always would be appreciated.
>
> I think USB does some magic which is not necessary for a simple driver
> like sc16is7xx.  Can you try the following patch?
>
> --------->8---------------
>
> From 2ba8fbbfdbfc77008639de89e021d777324de749 Mon Sep 17 00:00:00 2001
> From: Jakub Kicinski <kubakici@xxxxx>
> Date: Thu, 25 Jun 2015 18:17:58 +0200
> Subject: [PATCH] sc16is7xx: support multiple devices
>
> Signed-off-by: Jakub Kicinski <kubakici@xxxxx>
> ---
>  drivers/tty/serial/sc16is7xx.c | 48 ++++++++++++++++++++++--------------------
>  1 file changed, 25 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
> index 5ccc698cbbfa..9db22d9f0e0e 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,12 @@ struct sc16is7xx_port {
>         struct sc16is7xx_one            p[0];
>  };
>
> +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)))
>
> @@ -661,7 +669,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 +1142,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);
>
> @@ -1195,7 +1193,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 +1204,8 @@ 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);
> +       for (i = 0; i < s->devtype->nr_uart; i++)
> +               uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
>
>  #ifdef CONFIG_GPIOLIB
>         if (devtype->nr_gpio)
> @@ -1217,9 +1215,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 +1232,14 @@ 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);
>                 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 +1392,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 +1428,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,

Sorry about the top post! I tested your patch today and I think it is
a step in the right direction, although I wasn't seeing the ttySC2,3
appearing the in /dev, only the first two devices ttySC0,1 were
working. I resolved this using a hack (see patch below) that tries to
track which lines have already been registered in the driver, perhaps
(I hope) there is a more elegant solution to this...

After this, all devices show up under /dev, however ports ttySC2,3
appear to still not be working.

At the moment, I see the following errors repeating:

[ 3002.450073] sc16is7xx_port_irq: 14687 callbacks suppressed
[ 3002.455841] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.462249] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.468597] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.474975] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.481353] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.487670] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.494049] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.500488] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.506835] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0
[ 3002.513244] sc16is7xx 1-0049: Port 2: Unexpected interrupt: 0

I think this might be an issue with the kworker threads and the
interrupts. In particular, does the string for the kworker thread here
need to be unique?

s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker, "sc16is7xx");

If this is required, perhaps we could append the address of the device
at the end to give something like: "sc16is7xx@1-0049"?

>From c56ff1610eb45dbc99b1cce4de25940b591af44f Mon Sep 17 00:00:00 2001
From: Michael Allwright <allsey87@xxxxxxxxx>
Date: Fri, 26 Jun 2015 15:40:02 +0200
Subject: [PATCH] Added bookkeeping for which line has been used

---
 drivers/tty/serial/sc16is7xx.c | 84 +++++++++++++++++++++++++++++-------------
 1 file changed, 59 insertions(+), 25 deletions(-)

diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index b81f0f2..d4fadfc 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -29,6 +29,8 @@
 #include <linux/uaccess.h>

 #define SC16IS7XX_NAME            "sc16is7xx"
+#define SC16IS7XX_MAX_DEVS        8
+#define SC16IS7XX_MAX_PORTS        2

 /* SC16IS7XX register definitions */
 #define SC16IS7XX_RHR_REG        (0x00) /* RX FIFO */
@@ -318,7 +320,6 @@ struct sc16is7xx_one {
 };

 struct sc16is7xx_port {
-    struct uart_driver        uart;
     struct sc16is7xx_devtype    *devtype;
     struct regmap            *regmap;
     struct clk            *clk;
@@ -332,6 +333,14 @@ struct sc16is7xx_port {
     struct sc16is7xx_one        p[0];
 };

+static struct uart_driver sc16is7xx_uart = {
+    .owner        = THIS_MODULE,
+    .dev_name    = "ttySC",
+    .nr        = SC16IS7XX_MAX_DEVS,
+};
+
+static struct uart_port* sc16is7xx_lines[SC16IS7XX_MAX_DEVS *
SC16IS7XX_MAX_PORTS] = {NULL};
+
 #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)))

@@ -661,7 +670,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);
 }

@@ -1104,7 +1113,7 @@ static int sc16is7xx_probe(struct device *dev,
 {
     struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 };
     unsigned long freq, *pfreq = dev_get_platdata(dev);
-    int i, ret;
+    int i, j, ret;
     struct sc16is7xx_port *s;

     if (IS_ERR(regmap))
@@ -1134,23 +1143,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);

@@ -1174,8 +1173,20 @@ static int sc16is7xx_probe(struct device *dev,
 #endif

     for (i = 0; i < devtype->nr_uart; ++i) {
+        /* Find an unused line */
+        for(j = 0; j < SC16IS7XX_MAX_DEVS * SC16IS7XX_MAX_PORTS; j++) {
+            if(sc16is7xx_lines[j] == NULL) {
+                break;
+            }
+        }
+        if(j == SC16IS7XX_MAX_DEVS * SC16IS7XX_MAX_PORTS) {
+            dev_err(dev, "Could not register port %d", i);
+            continue;
+        }
+        sc16is7xx_lines[j] = &s->p[i].port;
+
         /* Initialize port data */
-        s->p[i].port.line    = i;
+        s->p[i].port.line    = j;
         s->p[i].port.dev    = dev;
         s->p[i].port.irq    = irq;
         s->p[i].port.type    = PORT_SC16IS7XX;
@@ -1195,7 +1206,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 +1217,8 @@ 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);
+    for (i = 0; i < s->devtype->nr_uart; i++)
+        uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);

 #ifdef CONFIG_GPIOLIB
     if (devtype->nr_gpio)
@@ -1217,9 +1228,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 +1245,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);
+        sc16is7xx_lines[s->p[i].port.line] = NULL;
         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);

@@ -1323,9 +1331,35 @@ static struct i2c_driver sc16is7xx_i2c_uart_driver = {
     .remove        = sc16is7xx_i2c_remove,
     .id_table    = sc16is7xx_i2c_id_table,
 };
-module_i2c_driver(sc16is7xx_i2c_uart_driver);

 MODULE_ALIAS("i2c:sc16is7xx");
+
+static int __init sc16is7xx_init(void)
+{
+    int ret;
+
+    ret = uart_register_driver(&sc16is7xx_uart);
+    if (ret) {
+        pr_err("Registering UART driver failed\n");
+        return ret;
+    }
+
+    ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver);
+    if (ret < 0) {
+        pr_err("failed to init sc16is7xx i2c --> %d\n", ret);
+        return ret;
+    }
+    return ret;
+}
+module_init(sc16is7xx_init);
+
+static void __exit sc16is7xx_exit(void)
+{
+    i2c_del_driver(&sc16is7xx_i2c_uart_driver);
+    uart_unregister_driver(&sc16is7xx_uart);
+}
+module_exit(sc16is7xx_exit);
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jon Ringle <jringle@xxxxxxxxxxxxx>");
 MODULE_DESCRIPTION("SC16IS7XX serial driver");
-- 
1.9.1

Michael Allwright

PhD Student
Paderborn Institute for Advanced Studies in Computer Science and Engineering

University of Paderborn
Office-number 02-10
Zukunftsmeile 1
33102 Paderborn
Germany
--
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