Re: [PATCH] tty: serial: fsl_lpuart: fix del_timer_sync() vs timer routine deadlock

2016-12-03 Thread Bhuvanchandra DV

On 12/03/2016 02:58 AM, Nikita Yushchenko wrote:


Problem found via lockdep:

- lpuart_set_termios() calls del_timer_sync(>lpuart_timer) while
   holding sport->port.lock

- sport->lpuart_timer routine is lpuart_timer_func() that calls
   lpuart_copy_rx_to_tty() that acquires same lock.

To fix, move Rx DMA stopping out of lock, as it already is in other places
in the same file.

While at it, also make Rx DMA start/stop code to look the same is in
other places in the same file.

Yeah I saw that too, never really came around to look closer into it.

Thanks for looking into it.

You removed the check whether there was an old configuration, I think
the idea of that was that we only resize DMA if it was configured
differently before...

Per my code reading, checking for sport->lpuart_dma_rx_use should be
enough, this flag will be set only if DMA was previously enabled,


The check is to make sure the reconfiguration of DMA is done only when
the baudrate is altered.

--
Bhuvan




+   if (sport->lpuart_dma_rx_use) {
+   if (!lpuart_start_rx_dma(sport)) {
sport->lpuart_dma_rx_use = true;

No need to set to true here, it is guaranteed to be true at this point.

I've seen this...  However elsewhere in this file (namely in
lpuart_resume(), in very similar situation, code is exactly the same,
i.e. it sets sport->lpuart_dma_rx_use in both clauses. I thought it
could be for a reason (i.e. for readability).

Nikita




Re: [PATCH] tty: serial: fsl_lpuart: fix del_timer_sync() vs timer routine deadlock

2016-12-03 Thread Bhuvanchandra DV

On 12/03/2016 02:58 AM, Nikita Yushchenko wrote:


Problem found via lockdep:

- lpuart_set_termios() calls del_timer_sync(>lpuart_timer) while
   holding sport->port.lock

- sport->lpuart_timer routine is lpuart_timer_func() that calls
   lpuart_copy_rx_to_tty() that acquires same lock.

To fix, move Rx DMA stopping out of lock, as it already is in other places
in the same file.

While at it, also make Rx DMA start/stop code to look the same is in
other places in the same file.

Yeah I saw that too, never really came around to look closer into it.

Thanks for looking into it.

You removed the check whether there was an old configuration, I think
the idea of that was that we only resize DMA if it was configured
differently before...

Per my code reading, checking for sport->lpuart_dma_rx_use should be
enough, this flag will be set only if DMA was previously enabled,


The check is to make sure the reconfiguration of DMA is done only when
the baudrate is altered.

--
Bhuvan




+   if (sport->lpuart_dma_rx_use) {
+   if (!lpuart_start_rx_dma(sport)) {
sport->lpuart_dma_rx_use = true;

No need to set to true here, it is guaranteed to be true at this point.

I've seen this...  However elsewhere in this file (namely in
lpuart_resume(), in very similar situation, code is exactly the same,
i.e. it sets sport->lpuart_dma_rx_use in both clauses. I thought it
could be for a reason (i.e. for readability).

Nikita




Re: [PATCH] tty: serial: fsl_lpuart: fix del_timer_sync() vs timer routine deadlock

2016-12-03 Thread Bhuvanchandra DV

On 12/03/2016 02:25 PM, Nikita Yushchenko wrote:



03.12.2016 10:06, Bhuvanchandra DV пишет:

On 12/03/2016 02:58 AM, Nikita Yushchenko wrote:


Problem found via lockdep:

- lpuart_set_termios() calls del_timer_sync(>lpuart_timer) while
holding sport->port.lock

- sport->lpuart_timer routine is lpuart_timer_func() that calls
lpuart_copy_rx_to_tty() that acquires same lock.

To fix, move Rx DMA stopping out of lock, as it already is in other
places
in the same file.

While at it, also make Rx DMA start/stop code to look the same is in
other places in the same file.

Yeah I saw that too, never really came around to look closer into it.

Thanks for looking into it.

You removed the check whether there was an old configuration, I think
the idea of that was that we only resize DMA if it was configured
differently before...

Per my code reading, checking for sport->lpuart_dma_rx_use should be
enough, this flag will be set only if DMA was previously enabled,

The check is to make sure the reconfiguration of DMA is done only when
the baudrate is altered.

Then, ok to use

if (old && sport->lpuart_dma_rx_use) {...}

in both places?


Looks ok to me.

--
Bhuvan





Re: [PATCH] tty: serial: fsl_lpuart: fix del_timer_sync() vs timer routine deadlock

2016-12-03 Thread Bhuvanchandra DV

On 12/03/2016 02:25 PM, Nikita Yushchenko wrote:



03.12.2016 10:06, Bhuvanchandra DV пишет:

On 12/03/2016 02:58 AM, Nikita Yushchenko wrote:


Problem found via lockdep:

- lpuart_set_termios() calls del_timer_sync(>lpuart_timer) while
holding sport->port.lock

- sport->lpuart_timer routine is lpuart_timer_func() that calls
lpuart_copy_rx_to_tty() that acquires same lock.

To fix, move Rx DMA stopping out of lock, as it already is in other
places
in the same file.

While at it, also make Rx DMA start/stop code to look the same is in
other places in the same file.

Yeah I saw that too, never really came around to look closer into it.

Thanks for looking into it.

You removed the check whether there was an old configuration, I think
the idea of that was that we only resize DMA if it was configured
differently before...

Per my code reading, checking for sport->lpuart_dma_rx_use should be
enough, this flag will be set only if DMA was previously enabled,

The check is to make sure the reconfiguration of DMA is done only when
the baudrate is altered.

Then, ok to use

if (old && sport->lpuart_dma_rx_use) {...}

in both places?


Looks ok to me.

--
Bhuvan





Re: [PATCH] tty: serial: fsl_lpuart: Fix Tx DMA edge case

2016-10-10 Thread Bhuvanchandra DV

On 10/07/16 03:43, Aaron Brice wrote:


In the case where head == 0 on the circular buffer, there should be one
DMA buffer, not two.  The second zero-length buffer would break the
lpuart driver, transfer would never complete.


Tested-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>



Signed-off-by: Aaron Brice <aaron.br...@datasoft.com>
---
  drivers/tty/serial/fsl_lpuart.c | 3 +--
  1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index de9d510..76103f2 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -328,7 +328,7 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
  
  	sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
  
-	if (xmit->tail < xmit->head) {

+   if (xmit->tail < xmit->head || xmit->head == 0) {
sport->dma_tx_nents = 1;
sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
} else {
@@ -359,7 +359,6 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
sport->dma_tx_in_progress = true;
sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
dma_async_issue_pending(sport->dma_tx_chan);
-
  }
  
  static void lpuart_dma_tx_complete(void *arg)




Re: [PATCH] tty: serial: fsl_lpuart: Fix Tx DMA edge case

2016-10-10 Thread Bhuvanchandra DV

On 10/07/16 03:43, Aaron Brice wrote:


In the case where head == 0 on the circular buffer, there should be one
DMA buffer, not two.  The second zero-length buffer would break the
lpuart driver, transfer would never complete.


Tested-by: Bhuvanchandra DV 



Signed-off-by: Aaron Brice 
---
  drivers/tty/serial/fsl_lpuart.c | 3 +--
  1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index de9d510..76103f2 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -328,7 +328,7 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
  
  	sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
  
-	if (xmit->tail < xmit->head) {

+   if (xmit->tail < xmit->head || xmit->head == 0) {
sport->dma_tx_nents = 1;
sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
} else {
@@ -359,7 +359,6 @@ static void lpuart_dma_tx(struct lpuart_port *sport)
sport->dma_tx_in_progress = true;
sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
dma_async_issue_pending(sport->dma_tx_chan);
-
  }
  
  static void lpuart_dma_tx_complete(void *arg)




[PATCH v3 4/6] arm: dts: imx7: Update #pwm-cells for PWM polarity control

2016-10-07 Thread Bhuvanchandra DV
Update #pwm-cells to 3 in order to support PWM signal polarity control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
Acked-by: Rob Herring <r...@kernel.org>
---
 arch/arm/boot/dts/imx7s.dtsi | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 0d7d5ac..8d1d471 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -601,7 +601,7 @@
clocks = < IMX7D_PWM1_ROOT_CLK>,
 < IMX7D_PWM1_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -612,7 +612,7 @@
clocks = < IMX7D_PWM2_ROOT_CLK>,
 < IMX7D_PWM2_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -623,7 +623,7 @@
clocks = < IMX7D_PWM3_ROOT_CLK>,
 < IMX7D_PWM3_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -634,7 +634,7 @@
clocks = < IMX7D_PWM4_ROOT_CLK>,
 < IMX7D_PWM4_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
-- 
2.10.0



[PATCH v3 4/6] arm: dts: imx7: Update #pwm-cells for PWM polarity control

2016-10-07 Thread Bhuvanchandra DV
Update #pwm-cells to 3 in order to support PWM signal polarity control.

Signed-off-by: Bhuvanchandra DV 
Acked-by: Rob Herring 
---
 arch/arm/boot/dts/imx7s.dtsi | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 0d7d5ac..8d1d471 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -601,7 +601,7 @@
clocks = < IMX7D_PWM1_ROOT_CLK>,
 < IMX7D_PWM1_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -612,7 +612,7 @@
clocks = < IMX7D_PWM2_ROOT_CLK>,
 < IMX7D_PWM2_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -623,7 +623,7 @@
clocks = < IMX7D_PWM3_ROOT_CLK>,
 < IMX7D_PWM3_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -634,7 +634,7 @@
clocks = < IMX7D_PWM4_ROOT_CLK>,
 < IMX7D_PWM4_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
-- 
2.10.0



[PATCH v3 0/6] Support PWM polarity control

2016-10-07 Thread Bhuvanchandra DV
Changes since v3:

- Set pwm->args.polarity, before using pwm_set_polarity() function as suggested
  by Lukasz.
- Squash Lukasz patch( https://lkml.org/lkml/2016/10/6/32 )

Tested on Toradex Colibri iMX6S module.

Changes since v2:

- Picked the stalled patchset[1] from Lothar Wassmann which adds the basic
  support for polarity control on imx-pwm driver and adds backward compatibility
  support for devices which does not have polarity control feature.

Changes since Lothars v6:

- Squash Lukasz patch[2].

[1] http://thread.gmane.org/gmane.linux.pwm/1621
[2] https://www.spinics.net/lists/arm-kernel/msg530818.html

Bhuvanchandra DV (3):
  arm: dts: imx7: Update #pwm-cells for PWM polarity control
  arm: dts: imx7-colibri: Use pwm polarity control
  arm: dts: imx7-colibri: Use enable-gpios for BL_ON

Lothar Wassmann (3):
  pwm: print error messages with pr_err() instead of pr_debug()
  pwm: core: make the PWM_POLARITY flag in DTB optional
  pwm: imx: support output polarity inversion

 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
 arch/arm/boot/dts/imx7-colibri.dtsi   | 12 +-
 arch/arm/boot/dts/imx7s.dtsi  |  8 ++--
 drivers/pwm/core.c| 26 ++--
 drivers/pwm/pwm-imx.c | 51 +--
 5 files changed, 79 insertions(+), 24 deletions(-)

-- 
2.10.0



[PATCH v3 2/6] pwm: core: make the PWM_POLARITY flag in DTB optional

2016-10-07 Thread Bhuvanchandra DV
From: Lothar Wassmann <l...@karo-electronics.de>

Change the pwm chip driver registration, so that a chip driver that
supports polarity inversion can still be used with DTBs that don't
provide the 'PWM_POLARITY' flag.

This is done to provide polarity inversion support for the pwm-imx
driver without having to modify all existing DTS files.

Signed-off-by: Lothar Wassmann <l...@karo-electronics.de>
Signed-off-by: Lukasz Majewski <l.majew...@samsung.com>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
Suggested-by: Sascha Hauer 
---
 drivers/pwm/core.c | 22 --
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 195373e..85cdda6 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -137,9 +137,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 {
struct pwm_device *pwm;
 
+   /* check, whether the driver supports a third cell for flags */
if (pc->of_pwm_n_cells < 3)
return ERR_PTR(-EINVAL);
 
+   /* flags in the third cell are optional */
+   if (args->args_count < 2)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
 
@@ -148,11 +153,10 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
return pwm;
 
pwm->args.period = args->args[1];
+   pwm->args.polarity = PWM_POLARITY_NORMAL;
 
-   if (args->args[2] & PWM_POLARITY_INVERTED)
+   if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
pwm->args.polarity = PWM_POLARITY_INVERSED;
-   else
-   pwm->args.polarity = PWM_POLARITY_NORMAL;
 
return pwm;
 }
@@ -163,9 +167,14 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 {
struct pwm_device *pwm;
 
+   /* sanity check driver support */
if (pc->of_pwm_n_cells < 2)
return ERR_PTR(-EINVAL);
 
+   /* all cells are required */
+   if (args->args_count != pc->of_pwm_n_cells)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
 
@@ -672,13 +681,6 @@ struct pwm_device *of_pwm_get(struct device_node *np, 
const char *con_id)
goto put;
}
 
-   if (args.args_count != pc->of_pwm_n_cells) {
-   pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
-args.np->full_name);
-   pwm = ERR_PTR(-EINVAL);
-   goto put;
-   }
-
pwm = pc->of_xlate(pc, );
if (IS_ERR(pwm))
goto put;
-- 
2.10.0



[PATCH v3 0/6] Support PWM polarity control

2016-10-07 Thread Bhuvanchandra DV
Changes since v3:

- Set pwm->args.polarity, before using pwm_set_polarity() function as suggested
  by Lukasz.
- Squash Lukasz patch( https://lkml.org/lkml/2016/10/6/32 )

Tested on Toradex Colibri iMX6S module.

Changes since v2:

- Picked the stalled patchset[1] from Lothar Wassmann which adds the basic
  support for polarity control on imx-pwm driver and adds backward compatibility
  support for devices which does not have polarity control feature.

Changes since Lothars v6:

- Squash Lukasz patch[2].

[1] http://thread.gmane.org/gmane.linux.pwm/1621
[2] https://www.spinics.net/lists/arm-kernel/msg530818.html

Bhuvanchandra DV (3):
  arm: dts: imx7: Update #pwm-cells for PWM polarity control
  arm: dts: imx7-colibri: Use pwm polarity control
  arm: dts: imx7-colibri: Use enable-gpios for BL_ON

Lothar Wassmann (3):
  pwm: print error messages with pr_err() instead of pr_debug()
  pwm: core: make the PWM_POLARITY flag in DTB optional
  pwm: imx: support output polarity inversion

 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
 arch/arm/boot/dts/imx7-colibri.dtsi   | 12 +-
 arch/arm/boot/dts/imx7s.dtsi  |  8 ++--
 drivers/pwm/core.c| 26 ++--
 drivers/pwm/pwm-imx.c | 51 +--
 5 files changed, 79 insertions(+), 24 deletions(-)

-- 
2.10.0



[PATCH v3 2/6] pwm: core: make the PWM_POLARITY flag in DTB optional

2016-10-07 Thread Bhuvanchandra DV
From: Lothar Wassmann 

Change the pwm chip driver registration, so that a chip driver that
supports polarity inversion can still be used with DTBs that don't
provide the 'PWM_POLARITY' flag.

This is done to provide polarity inversion support for the pwm-imx
driver without having to modify all existing DTS files.

Signed-off-by: Lothar Wassmann 
Signed-off-by: Lukasz Majewski 
Signed-off-by: Bhuvanchandra DV 
Suggested-by: Sascha Hauer 
---
 drivers/pwm/core.c | 22 --
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 195373e..85cdda6 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -137,9 +137,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 {
struct pwm_device *pwm;
 
+   /* check, whether the driver supports a third cell for flags */
if (pc->of_pwm_n_cells < 3)
return ERR_PTR(-EINVAL);
 
+   /* flags in the third cell are optional */
+   if (args->args_count < 2)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
 
@@ -148,11 +153,10 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
return pwm;
 
pwm->args.period = args->args[1];
+   pwm->args.polarity = PWM_POLARITY_NORMAL;
 
-   if (args->args[2] & PWM_POLARITY_INVERTED)
+   if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
pwm->args.polarity = PWM_POLARITY_INVERSED;
-   else
-   pwm->args.polarity = PWM_POLARITY_NORMAL;
 
return pwm;
 }
@@ -163,9 +167,14 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 {
struct pwm_device *pwm;
 
+   /* sanity check driver support */
if (pc->of_pwm_n_cells < 2)
return ERR_PTR(-EINVAL);
 
+   /* all cells are required */
+   if (args->args_count != pc->of_pwm_n_cells)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
 
@@ -672,13 +681,6 @@ struct pwm_device *of_pwm_get(struct device_node *np, 
const char *con_id)
goto put;
}
 
-   if (args.args_count != pc->of_pwm_n_cells) {
-   pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
-args.np->full_name);
-   pwm = ERR_PTR(-EINVAL);
-   goto put;
-   }
-
pwm = pc->of_xlate(pc, );
if (IS_ERR(pwm))
goto put;
-- 
2.10.0



[PATCH v3 1/6] pwm: print error messages with pr_err() instead of pr_debug()

2016-10-07 Thread Bhuvanchandra DV
From: Lothar Wassmann <l...@karo-electronics.de>

Make the messages that are printed in case of fatal errors actually
visible to the user without having to recompile the driver with
debugging enabled.

Signed-off-by: Lothar Waßmann <l...@karo-electronics.de>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/pwm/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 0dbd29e..195373e 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -661,13 +661,13 @@ struct pwm_device *of_pwm_get(struct device_node *np, 
const char *con_id)
err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
 );
if (err) {
-   pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+   pr_err("%s(): can't parse \"pwms\" property\n", __func__);
return ERR_PTR(err);
}
 
pc = of_node_to_pwmchip(args.np);
if (IS_ERR(pc)) {
-   pr_debug("%s(): PWM chip not found\n", __func__);
+   pr_err("%s(): PWM chip not found\n", __func__);
pwm = ERR_CAST(pc);
goto put;
}
-- 
2.10.0



[PATCH v3 1/6] pwm: print error messages with pr_err() instead of pr_debug()

2016-10-07 Thread Bhuvanchandra DV
From: Lothar Wassmann 

Make the messages that are printed in case of fatal errors actually
visible to the user without having to recompile the driver with
debugging enabled.

Signed-off-by: Lothar Waßmann 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/pwm/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 0dbd29e..195373e 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -661,13 +661,13 @@ struct pwm_device *of_pwm_get(struct device_node *np, 
const char *con_id)
err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
 );
if (err) {
-   pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+   pr_err("%s(): can't parse \"pwms\" property\n", __func__);
return ERR_PTR(err);
}
 
pc = of_node_to_pwmchip(args.np);
if (IS_ERR(pc)) {
-   pr_debug("%s(): PWM chip not found\n", __func__);
+   pr_err("%s(): PWM chip not found\n", __func__);
pwm = ERR_CAST(pc);
goto put;
}
-- 
2.10.0



Re: [PATCH v2 2/6] pwm: core: make the PWM_POLARITY flag in DTB optional

2016-10-07 Thread Bhuvanchandra DV

Hi Lukasz,

On 10/06/16 12:06, Lukasz Majewski wrote:


Hi Bhuvanchandra,


From: Lothar Wassmann <l...@karo-electronics.de>

Change the pwm chip driver registration, so that a chip driver that
supports polarity inversion can still be used with DTBs that don't
provide the 'PWM_POLARITY' flag.

This is done to provide polarity inversion support for the pwm-imx
driver without having to modify all existing DTS files.

Signed-off-by: Lothar Wassmann <l...@karo-electronics.de>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
Suggested-by: Thierry Reding <thierry.red...@gmail.com>
---
  drivers/pwm/core.c | 27 ---
  1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 195373e..aae8db3 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -137,9 +137,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc,
const struct of_phandle_args *args) {
struct pwm_device *pwm;
  
+	/* check, whether the driver supports a third cell for flags

*/ if (pc->of_pwm_n_cells < 3)
return ERR_PTR(-EINVAL);
  
+	/* flags in the third cell are optional */

+   if (args->args_count < 2)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
  
@@ -149,10 +154,12 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc,

const struct of_phandle_args *args)
pwm->args.period = args->args[1];
  
-	if (args->args[2] & PWM_POLARITY_INVERTED)

-   pwm->args.polarity = PWM_POLARITY_INVERSED;
-   else
-   pwm->args.polarity = PWM_POLARITY_NORMAL;
+   if (args->args_count > 2) {
+   if (args->args[2] & PWM_POLARITY_INVERTED)
+   pwm_set_polarity(pwm, PWM_POLARITY_INVERSED);


here we should set pwm->args.polarity, since
the pwm_set_polarity() calls pwm_apply_state()
which requires duty_cycle and period to be set.

In this particular moment it is not yet set and
polarity is not properly configured.


Agreed. Will do a clean v3 patchset along with the patch you sent
(pwm: core: Use pwm->args.polarity to setup PWM_POLARITY_INVERSED).

--
Bhuvan



Patch fixing this will be sent as a reply to this e-mail. Please just
squash it and test on your platform.

Best regards,
Łukasz Majewski


+   else
+   pwm_set_polarity(pwm, PWM_POLARITY_NORMAL);
+   }
  
  	return pwm;

  }
@@ -163,9 +170,14 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const
struct of_phandle_args *args) {
struct pwm_device *pwm;
  
+	/* sanity check driver support */

if (pc->of_pwm_n_cells < 2)
return ERR_PTR(-EINVAL);
  
+	/* all cells are required */

+   if (args->args_count != pc->of_pwm_n_cells)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
  
@@ -672,13 +684,6 @@ struct pwm_device *of_pwm_get(struct device_node

*np, const char *con_id) goto put;
}
  
-	if (args.args_count != pc->of_pwm_n_cells) {

-   pr_debug("%s: wrong #pwm-cells for %s\n",
np->full_name,
-args.np->full_name);
-   pwm = ERR_PTR(-EINVAL);
-   goto put;
-   }
-
pwm = pc->of_xlate(pc, );
if (IS_ERR(pwm))
goto put;




Re: [PATCH v2 2/6] pwm: core: make the PWM_POLARITY flag in DTB optional

2016-10-07 Thread Bhuvanchandra DV

Hi Lukasz,

On 10/06/16 12:06, Lukasz Majewski wrote:


Hi Bhuvanchandra,


From: Lothar Wassmann 

Change the pwm chip driver registration, so that a chip driver that
supports polarity inversion can still be used with DTBs that don't
provide the 'PWM_POLARITY' flag.

This is done to provide polarity inversion support for the pwm-imx
driver without having to modify all existing DTS files.

Signed-off-by: Lothar Wassmann 
Signed-off-by: Bhuvanchandra DV 
Suggested-by: Thierry Reding 
---
  drivers/pwm/core.c | 27 ---
  1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 195373e..aae8db3 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -137,9 +137,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc,
const struct of_phandle_args *args) {
struct pwm_device *pwm;
  
+	/* check, whether the driver supports a third cell for flags

*/ if (pc->of_pwm_n_cells < 3)
return ERR_PTR(-EINVAL);
  
+	/* flags in the third cell are optional */

+   if (args->args_count < 2)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
  
@@ -149,10 +154,12 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc,

const struct of_phandle_args *args)
pwm->args.period = args->args[1];
  
-	if (args->args[2] & PWM_POLARITY_INVERTED)

-   pwm->args.polarity = PWM_POLARITY_INVERSED;
-   else
-   pwm->args.polarity = PWM_POLARITY_NORMAL;
+   if (args->args_count > 2) {
+   if (args->args[2] & PWM_POLARITY_INVERTED)
+   pwm_set_polarity(pwm, PWM_POLARITY_INVERSED);


here we should set pwm->args.polarity, since
the pwm_set_polarity() calls pwm_apply_state()
which requires duty_cycle and period to be set.

In this particular moment it is not yet set and
polarity is not properly configured.


Agreed. Will do a clean v3 patchset along with the patch you sent
(pwm: core: Use pwm->args.polarity to setup PWM_POLARITY_INVERSED).

--
Bhuvan



Patch fixing this will be sent as a reply to this e-mail. Please just
squash it and test on your platform.

Best regards,
Łukasz Majewski


+   else
+   pwm_set_polarity(pwm, PWM_POLARITY_NORMAL);
+   }
  
  	return pwm;

  }
@@ -163,9 +170,14 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const
struct of_phandle_args *args) {
struct pwm_device *pwm;
  
+	/* sanity check driver support */

if (pc->of_pwm_n_cells < 2)
return ERR_PTR(-EINVAL);
  
+	/* all cells are required */

+   if (args->args_count != pc->of_pwm_n_cells)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
  
@@ -672,13 +684,6 @@ struct pwm_device *of_pwm_get(struct device_node

*np, const char *con_id) goto put;
}
  
-	if (args.args_count != pc->of_pwm_n_cells) {

-   pr_debug("%s: wrong #pwm-cells for %s\n",
np->full_name,
-args.np->full_name);
-   pwm = ERR_PTR(-EINVAL);
-   goto put;
-   }
-
pwm = pc->of_xlate(pc, );
if (IS_ERR(pwm))
goto put;




[PATCH v3 3/6] pwm: imx: support output polarity inversion

2016-10-07 Thread Bhuvanchandra DV
From: Lothar Wassmann <l...@karo-electronics.de>

The i.MX pwm unit on i.MX27 and newer SoCs provides a configurable output
polarity. This patch adds support to utilize this feature where available.

Signed-off-by: Lothar Waßmann <l...@karo-electronics.de>
Signed-off-by: Lukasz Majewski <l.majew...@samsung.com>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
Acked-by: Shawn Guo <shawn@linaro.org>
Reviewed-by: Sascha Hauer <s.ha...@pengutronix.de>
---
 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
 drivers/pwm/pwm-imx.c | 51 +--
 2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt 
b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
index e00c2e9..c61bdf8 100644
--- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
@@ -6,8 +6,8 @@ Required properties:
   - "fsl,imx1-pwm" for PWM compatible with the one integrated on i.MX1
   - "fsl,imx27-pwm" for PWM compatible with the one integrated on i.MX27
 - reg: physical base address and length of the controller's registers
-- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
-  the cells format.
+- #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See pwm.txt
+  in this directory for a description of the cells format.
 - clocks : Clock specifiers for both ipg and per clocks.
 - clock-names : Clock names should include both "ipg" and "per"
 See the clock consumer binding,
@@ -17,7 +17,7 @@ See the clock consumer binding,
 Example:
 
 pwm1: pwm@53fb4000 {
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb4000 0x4000>;
clocks = < IMX5_CLK_PWM1_IPG_GATE>,
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index d600fd5..c37d223 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -38,6 +38,7 @@
 #define MX3_PWMCR_DOZEEN   (1 << 24)
 #define MX3_PWMCR_WAITEN   (1 << 23)
 #define MX3_PWMCR_DBGEN(1 << 22)
+#define MX3_PWMCR_POUTC(1 << 18)
 #define MX3_PWMCR_CLKSRC_IPG_HIGH  (2 << 16)
 #define MX3_PWMCR_CLKSRC_IPG   (1 << 16)
 #define MX3_PWMCR_SWR  (1 << 3)
@@ -180,6 +181,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
if (enable)
cr |= MX3_PWMCR_EN;
 
+   if (pwm->args.polarity == PWM_POLARITY_INVERSED)
+   cr |= MX3_PWMCR_POUTC;
+
writel(cr, imx->mmio_base + MX3_PWMCR);
 
return 0;
@@ -240,27 +244,62 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct 
pwm_device *pwm)
clk_disable_unprepare(imx->clk_per);
 }
 
-static struct pwm_ops imx_pwm_ops = {
+static int imx_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+   enum pwm_polarity polarity)
+{
+   struct imx_chip *imx = to_imx_chip(chip);
+   u32 val;
+
+   if (polarity == pwm->args.polarity)
+   return 0;
+
+   val = readl(imx->mmio_base + MX3_PWMCR);
+
+   if (polarity == PWM_POLARITY_INVERSED)
+   val |= MX3_PWMCR_POUTC;
+   else
+   val &= ~MX3_PWMCR_POUTC;
+
+   writel(val, imx->mmio_base + MX3_PWMCR);
+
+   dev_dbg(imx->chip.dev, "%s: polarity set to %s\n", __func__,
+   polarity == PWM_POLARITY_INVERSED ? "inverted" : "normal");
+
+   return 0;
+}
+
+static struct pwm_ops imx_pwm_ops_v1 = {
.enable = imx_pwm_enable,
.disable = imx_pwm_disable,
.config = imx_pwm_config,
.owner = THIS_MODULE,
 };
 
+static struct pwm_ops imx_pwm_ops_v2 = {
+   .enable = imx_pwm_enable,
+   .disable = imx_pwm_disable,
+   .set_polarity = imx_pwm_set_polarity,
+   .config = imx_pwm_config,
+   .owner = THIS_MODULE,
+};
+
 struct imx_pwm_data {
int (*config)(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns);
void (*set_enable)(struct pwm_chip *chip, bool enable);
+   struct pwm_ops *pwm_ops;
 };
 
 static struct imx_pwm_data imx_pwm_data_v1 = {
.config = imx_pwm_config_v1,
.set_enable = imx_pwm_set_enable_v1,
+   .pwm_ops = _pwm_ops_v1,
 };
 
 static struct imx_pwm_data imx_pwm_data_v2 = {
.config = imx_pwm_config_v2,
.set_enable = imx_pwm_set_enable_v2,
+   .pwm_ops = _pwm_ops_v2,
 };
 
 static const struct of_device_id imx_pwm_dt_ids[] = {
@@ -282,6 +321,8 @@ static int imx_pwm_probe(struct platform_device *pdev)
if (!of_id)
return -ENODEV;
 
+   data = of_id->data;
+
imx = dev

[PATCH v3 3/6] pwm: imx: support output polarity inversion

2016-10-07 Thread Bhuvanchandra DV
From: Lothar Wassmann 

The i.MX pwm unit on i.MX27 and newer SoCs provides a configurable output
polarity. This patch adds support to utilize this feature where available.

Signed-off-by: Lothar Waßmann 
Signed-off-by: Lukasz Majewski 
Signed-off-by: Bhuvanchandra DV 
Acked-by: Shawn Guo 
Reviewed-by: Sascha Hauer 
---
 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
 drivers/pwm/pwm-imx.c | 51 +--
 2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt 
b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
index e00c2e9..c61bdf8 100644
--- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
@@ -6,8 +6,8 @@ Required properties:
   - "fsl,imx1-pwm" for PWM compatible with the one integrated on i.MX1
   - "fsl,imx27-pwm" for PWM compatible with the one integrated on i.MX27
 - reg: physical base address and length of the controller's registers
-- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
-  the cells format.
+- #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See pwm.txt
+  in this directory for a description of the cells format.
 - clocks : Clock specifiers for both ipg and per clocks.
 - clock-names : Clock names should include both "ipg" and "per"
 See the clock consumer binding,
@@ -17,7 +17,7 @@ See the clock consumer binding,
 Example:
 
 pwm1: pwm@53fb4000 {
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb4000 0x4000>;
clocks = < IMX5_CLK_PWM1_IPG_GATE>,
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index d600fd5..c37d223 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -38,6 +38,7 @@
 #define MX3_PWMCR_DOZEEN   (1 << 24)
 #define MX3_PWMCR_WAITEN   (1 << 23)
 #define MX3_PWMCR_DBGEN(1 << 22)
+#define MX3_PWMCR_POUTC(1 << 18)
 #define MX3_PWMCR_CLKSRC_IPG_HIGH  (2 << 16)
 #define MX3_PWMCR_CLKSRC_IPG   (1 << 16)
 #define MX3_PWMCR_SWR  (1 << 3)
@@ -180,6 +181,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
if (enable)
cr |= MX3_PWMCR_EN;
 
+   if (pwm->args.polarity == PWM_POLARITY_INVERSED)
+   cr |= MX3_PWMCR_POUTC;
+
writel(cr, imx->mmio_base + MX3_PWMCR);
 
return 0;
@@ -240,27 +244,62 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct 
pwm_device *pwm)
clk_disable_unprepare(imx->clk_per);
 }
 
-static struct pwm_ops imx_pwm_ops = {
+static int imx_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+   enum pwm_polarity polarity)
+{
+   struct imx_chip *imx = to_imx_chip(chip);
+   u32 val;
+
+   if (polarity == pwm->args.polarity)
+   return 0;
+
+   val = readl(imx->mmio_base + MX3_PWMCR);
+
+   if (polarity == PWM_POLARITY_INVERSED)
+   val |= MX3_PWMCR_POUTC;
+   else
+   val &= ~MX3_PWMCR_POUTC;
+
+   writel(val, imx->mmio_base + MX3_PWMCR);
+
+   dev_dbg(imx->chip.dev, "%s: polarity set to %s\n", __func__,
+   polarity == PWM_POLARITY_INVERSED ? "inverted" : "normal");
+
+   return 0;
+}
+
+static struct pwm_ops imx_pwm_ops_v1 = {
.enable = imx_pwm_enable,
.disable = imx_pwm_disable,
.config = imx_pwm_config,
.owner = THIS_MODULE,
 };
 
+static struct pwm_ops imx_pwm_ops_v2 = {
+   .enable = imx_pwm_enable,
+   .disable = imx_pwm_disable,
+   .set_polarity = imx_pwm_set_polarity,
+   .config = imx_pwm_config,
+   .owner = THIS_MODULE,
+};
+
 struct imx_pwm_data {
int (*config)(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns);
void (*set_enable)(struct pwm_chip *chip, bool enable);
+   struct pwm_ops *pwm_ops;
 };
 
 static struct imx_pwm_data imx_pwm_data_v1 = {
.config = imx_pwm_config_v1,
.set_enable = imx_pwm_set_enable_v1,
+   .pwm_ops = _pwm_ops_v1,
 };
 
 static struct imx_pwm_data imx_pwm_data_v2 = {
.config = imx_pwm_config_v2,
.set_enable = imx_pwm_set_enable_v2,
+   .pwm_ops = _pwm_ops_v2,
 };
 
 static const struct of_device_id imx_pwm_dt_ids[] = {
@@ -282,6 +321,8 @@ static int imx_pwm_probe(struct platform_device *pdev)
if (!of_id)
return -ENODEV;
 
+   data = of_id->data;
+
imx = devm_kzalloc(>dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL)
return -ENOMEM;
@@ -300,18 +341,22 @@ static int imx_pwm_probe(struct platform_device *pdev)
  

[PATCH v3 5/6] arm: dts: imx7-colibri: Use pwm polarity control

2016-10-07 Thread Bhuvanchandra DV
Configure PWM polarity control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index a9cc657..2af5e3e 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,7 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
-   pwms = < 0 500>;
+   pwms = < 0 500 0>;
};
 
reg_module_3v3: regulator-module-3v3 {
-- 
2.10.0



[PATCH v3 5/6] arm: dts: imx7-colibri: Use pwm polarity control

2016-10-07 Thread Bhuvanchandra DV
Configure PWM polarity control.

Signed-off-by: Bhuvanchandra DV 
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index a9cc657..2af5e3e 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,7 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
-   pwms = < 0 500>;
+   pwms = < 0 500 0>;
};
 
reg_module_3v3: regulator-module-3v3 {
-- 
2.10.0



[PATCH v3 6/6] arm: dts: imx7-colibri: Use enable-gpios for BL_ON

2016-10-07 Thread Bhuvanchandra DV
Use pwm-backlight driver 'enable-gpios' property for backlight on/off control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index 2af5e3e..ce5edb5 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,10 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
+   pinctrl-names = "default";
+   pinctrl-0 = <_gpio_bl_on>;
pwms = < 0 500 0>;
+   enable-gpios = < 1 GPIO_ACTIVE_HIGH>;
};
 
reg_module_3v3: regulator-module-3v3 {
@@ -356,7 +359,6 @@
fsl,pins = <
MX7D_PAD_ECSPI2_SS0__GPIO4_IO23 0x14 /* SODIMM 
65 */
MX7D_PAD_SD1_CD_B__GPIO5_IO00x14 /* SODIMM 
69 */
-   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14 /* SODIMM 
71 */
MX7D_PAD_I2C4_SDA__GPIO4_IO15   0x14 /* SODIMM 
75 */
MX7D_PAD_ECSPI1_MISO__GPIO4_IO180x14 /* SODIMM 
79 */
MX7D_PAD_I2C3_SCL__GPIO4_IO12   0x14 /* SODIMM 
81 */
@@ -388,6 +390,12 @@
>;
};
 
+   pinctrl_gpio_bl_on: gpio-bl-on {
+   fsl,pins = <
+   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14
+   >;
+   };
+
pinctrl_i2c1_int: i2c1-int-grp { /* PMIC / TOUCH */
fsl,pins = <
MX7D_PAD_GPIO1_IO13__GPIO1_IO13 0x79
-- 
2.10.0



[PATCH v3 6/6] arm: dts: imx7-colibri: Use enable-gpios for BL_ON

2016-10-07 Thread Bhuvanchandra DV
Use pwm-backlight driver 'enable-gpios' property for backlight on/off control.

Signed-off-by: Bhuvanchandra DV 
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index 2af5e3e..ce5edb5 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,10 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
+   pinctrl-names = "default";
+   pinctrl-0 = <_gpio_bl_on>;
pwms = < 0 500 0>;
+   enable-gpios = < 1 GPIO_ACTIVE_HIGH>;
};
 
reg_module_3v3: regulator-module-3v3 {
@@ -356,7 +359,6 @@
fsl,pins = <
MX7D_PAD_ECSPI2_SS0__GPIO4_IO23 0x14 /* SODIMM 
65 */
MX7D_PAD_SD1_CD_B__GPIO5_IO00x14 /* SODIMM 
69 */
-   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14 /* SODIMM 
71 */
MX7D_PAD_I2C4_SDA__GPIO4_IO15   0x14 /* SODIMM 
75 */
MX7D_PAD_ECSPI1_MISO__GPIO4_IO180x14 /* SODIMM 
79 */
MX7D_PAD_I2C3_SCL__GPIO4_IO12   0x14 /* SODIMM 
81 */
@@ -388,6 +390,12 @@
>;
};
 
+   pinctrl_gpio_bl_on: gpio-bl-on {
+   fsl,pins = <
+   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14
+   >;
+   };
+
pinctrl_i2c1_int: i2c1-int-grp { /* PMIC / TOUCH */
fsl,pins = <
MX7D_PAD_GPIO1_IO13__GPIO1_IO13 0x79
-- 
2.10.0



[PATCH v2 6/6] arm: dts: imx7-colibri: Use enable-gpios for BL_ON

2016-10-01 Thread Bhuvanchandra DV
Use pwm-backlight driver 'enable-gpios' property for backlight on/off control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index 2af5e3e..ce5edb5 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,10 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
+   pinctrl-names = "default";
+   pinctrl-0 = <_gpio_bl_on>;
pwms = < 0 500 0>;
+   enable-gpios = < 1 GPIO_ACTIVE_HIGH>;
};
 
reg_module_3v3: regulator-module-3v3 {
@@ -356,7 +359,6 @@
fsl,pins = <
MX7D_PAD_ECSPI2_SS0__GPIO4_IO23 0x14 /* SODIMM 
65 */
MX7D_PAD_SD1_CD_B__GPIO5_IO00x14 /* SODIMM 
69 */
-   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14 /* SODIMM 
71 */
MX7D_PAD_I2C4_SDA__GPIO4_IO15   0x14 /* SODIMM 
75 */
MX7D_PAD_ECSPI1_MISO__GPIO4_IO180x14 /* SODIMM 
79 */
MX7D_PAD_I2C3_SCL__GPIO4_IO12   0x14 /* SODIMM 
81 */
@@ -388,6 +390,12 @@
>;
};
 
+   pinctrl_gpio_bl_on: gpio-bl-on {
+   fsl,pins = <
+   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14
+   >;
+   };
+
pinctrl_i2c1_int: i2c1-int-grp { /* PMIC / TOUCH */
fsl,pins = <
MX7D_PAD_GPIO1_IO13__GPIO1_IO13 0x79
-- 
2.9.2



[PATCH v2 6/6] arm: dts: imx7-colibri: Use enable-gpios for BL_ON

2016-10-01 Thread Bhuvanchandra DV
Use pwm-backlight driver 'enable-gpios' property for backlight on/off control.

Signed-off-by: Bhuvanchandra DV 
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index 2af5e3e..ce5edb5 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,10 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
+   pinctrl-names = "default";
+   pinctrl-0 = <_gpio_bl_on>;
pwms = < 0 500 0>;
+   enable-gpios = < 1 GPIO_ACTIVE_HIGH>;
};
 
reg_module_3v3: regulator-module-3v3 {
@@ -356,7 +359,6 @@
fsl,pins = <
MX7D_PAD_ECSPI2_SS0__GPIO4_IO23 0x14 /* SODIMM 
65 */
MX7D_PAD_SD1_CD_B__GPIO5_IO00x14 /* SODIMM 
69 */
-   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14 /* SODIMM 
71 */
MX7D_PAD_I2C4_SDA__GPIO4_IO15   0x14 /* SODIMM 
75 */
MX7D_PAD_ECSPI1_MISO__GPIO4_IO180x14 /* SODIMM 
79 */
MX7D_PAD_I2C3_SCL__GPIO4_IO12   0x14 /* SODIMM 
81 */
@@ -388,6 +390,12 @@
>;
};
 
+   pinctrl_gpio_bl_on: gpio-bl-on {
+   fsl,pins = <
+   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14
+   >;
+   };
+
pinctrl_i2c1_int: i2c1-int-grp { /* PMIC / TOUCH */
fsl,pins = <
MX7D_PAD_GPIO1_IO13__GPIO1_IO13 0x79
-- 
2.9.2



[PATCH v2 5/6] arm: dts: imx7-colibri: Use pwm polarity control

2016-10-01 Thread Bhuvanchandra DV
Configure PWM polarity control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index a9cc657..2af5e3e 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,7 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
-   pwms = < 0 500>;
+   pwms = < 0 500 0>;
};
 
reg_module_3v3: regulator-module-3v3 {
-- 
2.9.2



[PATCH v2 0/6] Support PWM polarity control

2016-10-01 Thread Bhuvanchandra DV
Changes since v2:

- Picked the stalled patchset[1] from Lothar Wassmann which adds the basic
  support for polarity control on imx-pwm driver and adds backward compatibility
  support for devices which does not have polarity control feature.

Changes since Lothars v6:

- Squash Lukasz patch[2].

[1] http://thread.gmane.org/gmane.linux.pwm/1621
[2] https://www.spinics.net/lists/arm-kernel/msg530818.html

Bhuvanchandra DV (3):
  arm: dts: imx7: Update #pwm-cells for PWM polarity control
  arm: dts: imx7-colibri: Use pwm polarity control
  arm: dts: imx7-colibri: Use enable-gpios for BL_ON

Lothar Wassmann (3):
  pwm: print error messages with pr_err() instead of pr_debug()
  pwm: core: make the PWM_POLARITY flag in DTB optional
  pwm: imx: support output polarity inversion

 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
 arch/arm/boot/dts/imx7-colibri.dtsi   | 12 +-
 arch/arm/boot/dts/imx7s.dtsi  |  8 ++--
 drivers/pwm/core.c| 31 --
 drivers/pwm/pwm-imx.c | 51 +--
 5 files changed, 83 insertions(+), 25 deletions(-)

-- 
2.9.2



[PATCH v2 5/6] arm: dts: imx7-colibri: Use pwm polarity control

2016-10-01 Thread Bhuvanchandra DV
Configure PWM polarity control.

Signed-off-by: Bhuvanchandra DV 
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index a9cc657..2af5e3e 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,7 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
-   pwms = < 0 500>;
+   pwms = < 0 500 0>;
};
 
reg_module_3v3: regulator-module-3v3 {
-- 
2.9.2



[PATCH v2 0/6] Support PWM polarity control

2016-10-01 Thread Bhuvanchandra DV
Changes since v2:

- Picked the stalled patchset[1] from Lothar Wassmann which adds the basic
  support for polarity control on imx-pwm driver and adds backward compatibility
  support for devices which does not have polarity control feature.

Changes since Lothars v6:

- Squash Lukasz patch[2].

[1] http://thread.gmane.org/gmane.linux.pwm/1621
[2] https://www.spinics.net/lists/arm-kernel/msg530818.html

Bhuvanchandra DV (3):
  arm: dts: imx7: Update #pwm-cells for PWM polarity control
  arm: dts: imx7-colibri: Use pwm polarity control
  arm: dts: imx7-colibri: Use enable-gpios for BL_ON

Lothar Wassmann (3):
  pwm: print error messages with pr_err() instead of pr_debug()
  pwm: core: make the PWM_POLARITY flag in DTB optional
  pwm: imx: support output polarity inversion

 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
 arch/arm/boot/dts/imx7-colibri.dtsi   | 12 +-
 arch/arm/boot/dts/imx7s.dtsi  |  8 ++--
 drivers/pwm/core.c| 31 --
 drivers/pwm/pwm-imx.c | 51 +--
 5 files changed, 83 insertions(+), 25 deletions(-)

-- 
2.9.2



[PATCH v2 2/6] pwm: core: make the PWM_POLARITY flag in DTB optional

2016-10-01 Thread Bhuvanchandra DV
From: Lothar Wassmann <l...@karo-electronics.de>

Change the pwm chip driver registration, so that a chip driver that supports
polarity inversion can still be used with DTBs that don't provide the
'PWM_POLARITY' flag.

This is done to provide polarity inversion support for the pwm-imx driver
without having to modify all existing DTS files.

Signed-off-by: Lothar Wassmann <l...@karo-electronics.de>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
Suggested-by: Thierry Reding <thierry.red...@gmail.com>
---
 drivers/pwm/core.c | 27 ---
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 195373e..aae8db3 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -137,9 +137,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 {
struct pwm_device *pwm;
 
+   /* check, whether the driver supports a third cell for flags */
if (pc->of_pwm_n_cells < 3)
return ERR_PTR(-EINVAL);
 
+   /* flags in the third cell are optional */
+   if (args->args_count < 2)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
 
@@ -149,10 +154,12 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 
pwm->args.period = args->args[1];
 
-   if (args->args[2] & PWM_POLARITY_INVERTED)
-   pwm->args.polarity = PWM_POLARITY_INVERSED;
-   else
-   pwm->args.polarity = PWM_POLARITY_NORMAL;
+   if (args->args_count > 2) {
+   if (args->args[2] & PWM_POLARITY_INVERTED)
+   pwm_set_polarity(pwm, PWM_POLARITY_INVERSED);
+   else
+   pwm_set_polarity(pwm, PWM_POLARITY_NORMAL);
+   }
 
return pwm;
 }
@@ -163,9 +170,14 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 {
struct pwm_device *pwm;
 
+   /* sanity check driver support */
if (pc->of_pwm_n_cells < 2)
return ERR_PTR(-EINVAL);
 
+   /* all cells are required */
+   if (args->args_count != pc->of_pwm_n_cells)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
 
@@ -672,13 +684,6 @@ struct pwm_device *of_pwm_get(struct device_node *np, 
const char *con_id)
goto put;
}
 
-   if (args.args_count != pc->of_pwm_n_cells) {
-   pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
-args.np->full_name);
-   pwm = ERR_PTR(-EINVAL);
-   goto put;
-   }
-
pwm = pc->of_xlate(pc, );
if (IS_ERR(pwm))
goto put;
-- 
2.9.2



[PATCH v2 1/6] pwm: print error messages with pr_err() instead of pr_debug()

2016-10-01 Thread Bhuvanchandra DV
From: Lothar Wassmann <l...@karo-electronics.de>

Make the messages that are printed in case of fatal errors actually visible to
the user without having to recompile the driver with debugging enabled.

Signed-off-by: Lothar Waßmann <l...@karo-electronics.de>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/pwm/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 0dbd29e..195373e 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -661,13 +661,13 @@ struct pwm_device *of_pwm_get(struct device_node *np, 
const char *con_id)
err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
 );
if (err) {
-   pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+   pr_err("%s(): can't parse \"pwms\" property\n", __func__);
return ERR_PTR(err);
}
 
pc = of_node_to_pwmchip(args.np);
if (IS_ERR(pc)) {
-   pr_debug("%s(): PWM chip not found\n", __func__);
+   pr_err("%s(): PWM chip not found\n", __func__);
pwm = ERR_CAST(pc);
goto put;
}
-- 
2.9.2



[PATCH v2 1/6] pwm: print error messages with pr_err() instead of pr_debug()

2016-10-01 Thread Bhuvanchandra DV
From: Lothar Wassmann 

Make the messages that are printed in case of fatal errors actually visible to
the user without having to recompile the driver with debugging enabled.

Signed-off-by: Lothar Waßmann 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/pwm/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 0dbd29e..195373e 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -661,13 +661,13 @@ struct pwm_device *of_pwm_get(struct device_node *np, 
const char *con_id)
err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
 );
if (err) {
-   pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+   pr_err("%s(): can't parse \"pwms\" property\n", __func__);
return ERR_PTR(err);
}
 
pc = of_node_to_pwmchip(args.np);
if (IS_ERR(pc)) {
-   pr_debug("%s(): PWM chip not found\n", __func__);
+   pr_err("%s(): PWM chip not found\n", __func__);
pwm = ERR_CAST(pc);
goto put;
}
-- 
2.9.2



[PATCH v2 2/6] pwm: core: make the PWM_POLARITY flag in DTB optional

2016-10-01 Thread Bhuvanchandra DV
From: Lothar Wassmann 

Change the pwm chip driver registration, so that a chip driver that supports
polarity inversion can still be used with DTBs that don't provide the
'PWM_POLARITY' flag.

This is done to provide polarity inversion support for the pwm-imx driver
without having to modify all existing DTS files.

Signed-off-by: Lothar Wassmann 
Signed-off-by: Bhuvanchandra DV 
Suggested-by: Thierry Reding 
---
 drivers/pwm/core.c | 27 ---
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 195373e..aae8db3 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -137,9 +137,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 {
struct pwm_device *pwm;
 
+   /* check, whether the driver supports a third cell for flags */
if (pc->of_pwm_n_cells < 3)
return ERR_PTR(-EINVAL);
 
+   /* flags in the third cell are optional */
+   if (args->args_count < 2)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
 
@@ -149,10 +154,12 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 
pwm->args.period = args->args[1];
 
-   if (args->args[2] & PWM_POLARITY_INVERTED)
-   pwm->args.polarity = PWM_POLARITY_INVERSED;
-   else
-   pwm->args.polarity = PWM_POLARITY_NORMAL;
+   if (args->args_count > 2) {
+   if (args->args[2] & PWM_POLARITY_INVERTED)
+   pwm_set_polarity(pwm, PWM_POLARITY_INVERSED);
+   else
+   pwm_set_polarity(pwm, PWM_POLARITY_NORMAL);
+   }
 
return pwm;
 }
@@ -163,9 +170,14 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct 
of_phandle_args *args)
 {
struct pwm_device *pwm;
 
+   /* sanity check driver support */
if (pc->of_pwm_n_cells < 2)
return ERR_PTR(-EINVAL);
 
+   /* all cells are required */
+   if (args->args_count != pc->of_pwm_n_cells)
+   return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
 
@@ -672,13 +684,6 @@ struct pwm_device *of_pwm_get(struct device_node *np, 
const char *con_id)
goto put;
}
 
-   if (args.args_count != pc->of_pwm_n_cells) {
-   pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
-args.np->full_name);
-   pwm = ERR_PTR(-EINVAL);
-   goto put;
-   }
-
pwm = pc->of_xlate(pc, );
if (IS_ERR(pwm))
goto put;
-- 
2.9.2



[PATCH v2 3/6] pwm: imx: support output polarity inversion

2016-10-01 Thread Bhuvanchandra DV
From: Lothar Wassmann <l...@karo-electronics.de>

The i.MX pwm unit on i.MX27 and newer SoCs provides a configurable output
polarity. This patch adds support to utilize this feature where available.

Signed-off-by: Lothar Waßmann <l...@karo-electronics.de>
Signed-off-by: Lukasz Majewski <l.majew...@samsung.com>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
Acked-by: Shawn Guo <shawn@linaro.org>
Reviewed-by: Sascha Hauer <s.ha...@pengutronix.de>
---
 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
 drivers/pwm/pwm-imx.c | 51 +--
 2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt 
b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
index e00c2e9..c61bdf8 100644
--- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
@@ -6,8 +6,8 @@ Required properties:
   - "fsl,imx1-pwm" for PWM compatible with the one integrated on i.MX1
   - "fsl,imx27-pwm" for PWM compatible with the one integrated on i.MX27
 - reg: physical base address and length of the controller's registers
-- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
-  the cells format.
+- #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See pwm.txt
+  in this directory for a description of the cells format.
 - clocks : Clock specifiers for both ipg and per clocks.
 - clock-names : Clock names should include both "ipg" and "per"
 See the clock consumer binding,
@@ -17,7 +17,7 @@ See the clock consumer binding,
 Example:
 
 pwm1: pwm@53fb4000 {
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb4000 0x4000>;
clocks = < IMX5_CLK_PWM1_IPG_GATE>,
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index d600fd5..c37d223 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -38,6 +38,7 @@
 #define MX3_PWMCR_DOZEEN   (1 << 24)
 #define MX3_PWMCR_WAITEN   (1 << 23)
 #define MX3_PWMCR_DBGEN(1 << 22)
+#define MX3_PWMCR_POUTC(1 << 18)
 #define MX3_PWMCR_CLKSRC_IPG_HIGH  (2 << 16)
 #define MX3_PWMCR_CLKSRC_IPG   (1 << 16)
 #define MX3_PWMCR_SWR  (1 << 3)
@@ -180,6 +181,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
if (enable)
cr |= MX3_PWMCR_EN;
 
+   if (pwm->args.polarity == PWM_POLARITY_INVERSED)
+   cr |= MX3_PWMCR_POUTC;
+
writel(cr, imx->mmio_base + MX3_PWMCR);
 
return 0;
@@ -240,27 +244,62 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct 
pwm_device *pwm)
clk_disable_unprepare(imx->clk_per);
 }
 
-static struct pwm_ops imx_pwm_ops = {
+static int imx_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+   enum pwm_polarity polarity)
+{
+   struct imx_chip *imx = to_imx_chip(chip);
+   u32 val;
+
+   if (polarity == pwm->args.polarity)
+   return 0;
+
+   val = readl(imx->mmio_base + MX3_PWMCR);
+
+   if (polarity == PWM_POLARITY_INVERSED)
+   val |= MX3_PWMCR_POUTC;
+   else
+   val &= ~MX3_PWMCR_POUTC;
+
+   writel(val, imx->mmio_base + MX3_PWMCR);
+
+   dev_dbg(imx->chip.dev, "%s: polarity set to %s\n", __func__,
+   polarity == PWM_POLARITY_INVERSED ? "inverted" : "normal");
+
+   return 0;
+}
+
+static struct pwm_ops imx_pwm_ops_v1 = {
.enable = imx_pwm_enable,
.disable = imx_pwm_disable,
.config = imx_pwm_config,
.owner = THIS_MODULE,
 };
 
+static struct pwm_ops imx_pwm_ops_v2 = {
+   .enable = imx_pwm_enable,
+   .disable = imx_pwm_disable,
+   .set_polarity = imx_pwm_set_polarity,
+   .config = imx_pwm_config,
+   .owner = THIS_MODULE,
+};
+
 struct imx_pwm_data {
int (*config)(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns);
void (*set_enable)(struct pwm_chip *chip, bool enable);
+   struct pwm_ops *pwm_ops;
 };
 
 static struct imx_pwm_data imx_pwm_data_v1 = {
.config = imx_pwm_config_v1,
.set_enable = imx_pwm_set_enable_v1,
+   .pwm_ops = _pwm_ops_v1,
 };
 
 static struct imx_pwm_data imx_pwm_data_v2 = {
.config = imx_pwm_config_v2,
.set_enable = imx_pwm_set_enable_v2,
+   .pwm_ops = _pwm_ops_v2,
 };
 
 static const struct of_device_id imx_pwm_dt_ids[] = {
@@ -282,6 +321,8 @@ static int imx_pwm_probe(struct platform_device *pdev)
if (!of_id)
return -ENODEV;
 
+   data = of_id->data;
+
imx = dev

[PATCH v2 4/6] arm: dts: imx7: Update #pwm-cells for PWM polarity control

2016-10-01 Thread Bhuvanchandra DV
Update #pwm-cells to 3 in order to support PWM signal polarity control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
Acked-by: Rob Herring <r...@kernel.org>
---
 arch/arm/boot/dts/imx7s.dtsi | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 0d7d5ac..8d1d471 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -601,7 +601,7 @@
clocks = < IMX7D_PWM1_ROOT_CLK>,
 < IMX7D_PWM1_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -612,7 +612,7 @@
clocks = < IMX7D_PWM2_ROOT_CLK>,
 < IMX7D_PWM2_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -623,7 +623,7 @@
clocks = < IMX7D_PWM3_ROOT_CLK>,
 < IMX7D_PWM3_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -634,7 +634,7 @@
clocks = < IMX7D_PWM4_ROOT_CLK>,
 < IMX7D_PWM4_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
-- 
2.9.2



[PATCH v2 3/6] pwm: imx: support output polarity inversion

2016-10-01 Thread Bhuvanchandra DV
From: Lothar Wassmann 

The i.MX pwm unit on i.MX27 and newer SoCs provides a configurable output
polarity. This patch adds support to utilize this feature where available.

Signed-off-by: Lothar Waßmann 
Signed-off-by: Lukasz Majewski 
Signed-off-by: Bhuvanchandra DV 
Acked-by: Shawn Guo 
Reviewed-by: Sascha Hauer 
---
 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
 drivers/pwm/pwm-imx.c | 51 +--
 2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt 
b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
index e00c2e9..c61bdf8 100644
--- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
@@ -6,8 +6,8 @@ Required properties:
   - "fsl,imx1-pwm" for PWM compatible with the one integrated on i.MX1
   - "fsl,imx27-pwm" for PWM compatible with the one integrated on i.MX27
 - reg: physical base address and length of the controller's registers
-- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
-  the cells format.
+- #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See pwm.txt
+  in this directory for a description of the cells format.
 - clocks : Clock specifiers for both ipg and per clocks.
 - clock-names : Clock names should include both "ipg" and "per"
 See the clock consumer binding,
@@ -17,7 +17,7 @@ See the clock consumer binding,
 Example:
 
 pwm1: pwm@53fb4000 {
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb4000 0x4000>;
clocks = < IMX5_CLK_PWM1_IPG_GATE>,
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index d600fd5..c37d223 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -38,6 +38,7 @@
 #define MX3_PWMCR_DOZEEN   (1 << 24)
 #define MX3_PWMCR_WAITEN   (1 << 23)
 #define MX3_PWMCR_DBGEN(1 << 22)
+#define MX3_PWMCR_POUTC(1 << 18)
 #define MX3_PWMCR_CLKSRC_IPG_HIGH  (2 << 16)
 #define MX3_PWMCR_CLKSRC_IPG   (1 << 16)
 #define MX3_PWMCR_SWR  (1 << 3)
@@ -180,6 +181,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
if (enable)
cr |= MX3_PWMCR_EN;
 
+   if (pwm->args.polarity == PWM_POLARITY_INVERSED)
+   cr |= MX3_PWMCR_POUTC;
+
writel(cr, imx->mmio_base + MX3_PWMCR);
 
return 0;
@@ -240,27 +244,62 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct 
pwm_device *pwm)
clk_disable_unprepare(imx->clk_per);
 }
 
-static struct pwm_ops imx_pwm_ops = {
+static int imx_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+   enum pwm_polarity polarity)
+{
+   struct imx_chip *imx = to_imx_chip(chip);
+   u32 val;
+
+   if (polarity == pwm->args.polarity)
+   return 0;
+
+   val = readl(imx->mmio_base + MX3_PWMCR);
+
+   if (polarity == PWM_POLARITY_INVERSED)
+   val |= MX3_PWMCR_POUTC;
+   else
+   val &= ~MX3_PWMCR_POUTC;
+
+   writel(val, imx->mmio_base + MX3_PWMCR);
+
+   dev_dbg(imx->chip.dev, "%s: polarity set to %s\n", __func__,
+   polarity == PWM_POLARITY_INVERSED ? "inverted" : "normal");
+
+   return 0;
+}
+
+static struct pwm_ops imx_pwm_ops_v1 = {
.enable = imx_pwm_enable,
.disable = imx_pwm_disable,
.config = imx_pwm_config,
.owner = THIS_MODULE,
 };
 
+static struct pwm_ops imx_pwm_ops_v2 = {
+   .enable = imx_pwm_enable,
+   .disable = imx_pwm_disable,
+   .set_polarity = imx_pwm_set_polarity,
+   .config = imx_pwm_config,
+   .owner = THIS_MODULE,
+};
+
 struct imx_pwm_data {
int (*config)(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns);
void (*set_enable)(struct pwm_chip *chip, bool enable);
+   struct pwm_ops *pwm_ops;
 };
 
 static struct imx_pwm_data imx_pwm_data_v1 = {
.config = imx_pwm_config_v1,
.set_enable = imx_pwm_set_enable_v1,
+   .pwm_ops = _pwm_ops_v1,
 };
 
 static struct imx_pwm_data imx_pwm_data_v2 = {
.config = imx_pwm_config_v2,
.set_enable = imx_pwm_set_enable_v2,
+   .pwm_ops = _pwm_ops_v2,
 };
 
 static const struct of_device_id imx_pwm_dt_ids[] = {
@@ -282,6 +321,8 @@ static int imx_pwm_probe(struct platform_device *pdev)
if (!of_id)
return -ENODEV;
 
+   data = of_id->data;
+
imx = devm_kzalloc(>dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL)
return -ENOMEM;
@@ -300,18 +341,22 @@ static int imx_pwm_probe(struct platform_device *pdev)
  

[PATCH v2 4/6] arm: dts: imx7: Update #pwm-cells for PWM polarity control

2016-10-01 Thread Bhuvanchandra DV
Update #pwm-cells to 3 in order to support PWM signal polarity control.

Signed-off-by: Bhuvanchandra DV 
Acked-by: Rob Herring 
---
 arch/arm/boot/dts/imx7s.dtsi | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 0d7d5ac..8d1d471 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -601,7 +601,7 @@
clocks = < IMX7D_PWM1_ROOT_CLK>,
 < IMX7D_PWM1_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -612,7 +612,7 @@
clocks = < IMX7D_PWM2_ROOT_CLK>,
 < IMX7D_PWM2_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -623,7 +623,7 @@
clocks = < IMX7D_PWM3_ROOT_CLK>,
 < IMX7D_PWM3_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -634,7 +634,7 @@
clocks = < IMX7D_PWM4_ROOT_CLK>,
 < IMX7D_PWM4_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
-- 
2.9.2



Re: [PATCH 1/3] arm: dts: imx7: Update #pwm-cells for PWM polarity control

2016-09-27 Thread Bhuvanchandra DV

On 09/23/16 23:22, Rob Herring wrote:


On Mon, Sep 19, 2016 at 07:53:45PM +0530, Bhuvanchandra DV wrote:

Update #pwm-cells to 3 in order to support PWM signal polarity control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
  Documentation/devicetree/bindings/pwm/imx-pwm.txt | 6 +++---
  arch/arm/boot/dts/imx7s.dtsi  | 8 
  2 files changed, 7 insertions(+), 7 deletions(-)

The driver will still work with old DTs, right? If so,


This patchset actually depends on the v6 patchset[1] from Lothar.
Some how that patchset got stalled in ML. I will re-spin the v6
patchset with the suggestions/changes from Lukasz in this patch[2]
after testing.

[1]http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/294027.html
[2]https://www.spinics.net/lists/arm-kernel/msg530818.html



Acked-by: Rob Herring <r...@kernel.org>




Re: [PATCH 1/3] arm: dts: imx7: Update #pwm-cells for PWM polarity control

2016-09-27 Thread Bhuvanchandra DV

On 09/23/16 23:22, Rob Herring wrote:


On Mon, Sep 19, 2016 at 07:53:45PM +0530, Bhuvanchandra DV wrote:

Update #pwm-cells to 3 in order to support PWM signal polarity control.

Signed-off-by: Bhuvanchandra DV 
---
  Documentation/devicetree/bindings/pwm/imx-pwm.txt | 6 +++---
  arch/arm/boot/dts/imx7s.dtsi  | 8 
  2 files changed, 7 insertions(+), 7 deletions(-)

The driver will still work with old DTs, right? If so,


This patchset actually depends on the v6 patchset[1] from Lothar.
Some how that patchset got stalled in ML. I will re-spin the v6
patchset with the suggestions/changes from Lukasz in this patch[2]
after testing.

[1]http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/294027.html
[2]https://www.spinics.net/lists/arm-kernel/msg530818.html



Acked-by: Rob Herring 




[PATCH 0/3] iMX7 PWM polarity control

2016-09-19 Thread Bhuvanchandra DV
This patchset depends on this patchset[1] and this patch[2] which adds support
for polarity control in imx-pwm driver.

- Use pwm polarity control on iMX7 based modules.
- Enable polarity control on Toradex Colibri iMX7D/S module.
- Add BL_ON GPIO control for Toradex Colibri iMX7D/S module.

[1] 
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/294027.html
[2] https://www.spinics.net/lists/arm-kernel/msg530818.html

Bhuvanchandra DV (3):
  arm: dts: imx7: Update #pwm-cells for PWM polarity control
  arm: dts: imx7-colibri: Use pwm polarity control
  arm: dts: imx7-colibri: Use enable-gpios for BL_ON

 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +++---
 arch/arm/boot/dts/imx7-colibri.dtsi   | 12 ++--
 arch/arm/boot/dts/imx7s.dtsi  |  8 
 3 files changed, 17 insertions(+), 9 deletions(-)

-- 
2.9.2



[PATCH 0/3] iMX7 PWM polarity control

2016-09-19 Thread Bhuvanchandra DV
This patchset depends on this patchset[1] and this patch[2] which adds support
for polarity control in imx-pwm driver.

- Use pwm polarity control on iMX7 based modules.
- Enable polarity control on Toradex Colibri iMX7D/S module.
- Add BL_ON GPIO control for Toradex Colibri iMX7D/S module.

[1] 
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-October/294027.html
[2] https://www.spinics.net/lists/arm-kernel/msg530818.html

Bhuvanchandra DV (3):
  arm: dts: imx7: Update #pwm-cells for PWM polarity control
  arm: dts: imx7-colibri: Use pwm polarity control
  arm: dts: imx7-colibri: Use enable-gpios for BL_ON

 Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +++---
 arch/arm/boot/dts/imx7-colibri.dtsi   | 12 ++--
 arch/arm/boot/dts/imx7s.dtsi  |  8 
 3 files changed, 17 insertions(+), 9 deletions(-)

-- 
2.9.2



[PATCH 2/3] arm: dts: imx7-colibri: Use pwm polarity control

2016-09-19 Thread Bhuvanchandra DV
Configure PWM polarity control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index a9cc657..2af5e3e 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,7 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
-   pwms = < 0 500>;
+   pwms = < 0 500 0>;
};
 
reg_module_3v3: regulator-module-3v3 {
-- 
2.9.2



[PATCH 2/3] arm: dts: imx7-colibri: Use pwm polarity control

2016-09-19 Thread Bhuvanchandra DV
Configure PWM polarity control.

Signed-off-by: Bhuvanchandra DV 
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index a9cc657..2af5e3e 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,7 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
-   pwms = < 0 500>;
+   pwms = < 0 500 0>;
};
 
reg_module_3v3: regulator-module-3v3 {
-- 
2.9.2



[PATCH 1/3] arm: dts: imx7: Update #pwm-cells for PWM polarity control

2016-09-19 Thread Bhuvanchandra DV
Update #pwm-cells to 3 in order to support PWM signal polarity control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 Documentation/devicetree/bindings/pwm/imx-pwm.txt | 6 +++---
 arch/arm/boot/dts/imx7s.dtsi  | 8 
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt 
b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
index e00c2e9..c61bdf8 100644
--- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
@@ -6,8 +6,8 @@ Required properties:
   - "fsl,imx1-pwm" for PWM compatible with the one integrated on i.MX1
   - "fsl,imx27-pwm" for PWM compatible with the one integrated on i.MX27
 - reg: physical base address and length of the controller's registers
-- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
-  the cells format.
+- #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See pwm.txt
+  in this directory for a description of the cells format.
 - clocks : Clock specifiers for both ipg and per clocks.
 - clock-names : Clock names should include both "ipg" and "per"
 See the clock consumer binding,
@@ -17,7 +17,7 @@ See the clock consumer binding,
 Example:
 
 pwm1: pwm@53fb4000 {
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb4000 0x4000>;
clocks = < IMX5_CLK_PWM1_IPG_GATE>,
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 0d7d5ac..8d1d471 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -601,7 +601,7 @@
clocks = < IMX7D_PWM1_ROOT_CLK>,
 < IMX7D_PWM1_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -612,7 +612,7 @@
clocks = < IMX7D_PWM2_ROOT_CLK>,
 < IMX7D_PWM2_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -623,7 +623,7 @@
clocks = < IMX7D_PWM3_ROOT_CLK>,
 < IMX7D_PWM3_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -634,7 +634,7 @@
clocks = < IMX7D_PWM4_ROOT_CLK>,
 < IMX7D_PWM4_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
-- 
2.9.2



[PATCH 1/3] arm: dts: imx7: Update #pwm-cells for PWM polarity control

2016-09-19 Thread Bhuvanchandra DV
Update #pwm-cells to 3 in order to support PWM signal polarity control.

Signed-off-by: Bhuvanchandra DV 
---
 Documentation/devicetree/bindings/pwm/imx-pwm.txt | 6 +++---
 arch/arm/boot/dts/imx7s.dtsi  | 8 
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt 
b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
index e00c2e9..c61bdf8 100644
--- a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/imx-pwm.txt
@@ -6,8 +6,8 @@ Required properties:
   - "fsl,imx1-pwm" for PWM compatible with the one integrated on i.MX1
   - "fsl,imx27-pwm" for PWM compatible with the one integrated on i.MX27
 - reg: physical base address and length of the controller's registers
-- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
-  the cells format.
+- #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See pwm.txt
+  in this directory for a description of the cells format.
 - clocks : Clock specifiers for both ipg and per clocks.
 - clock-names : Clock names should include both "ipg" and "per"
 See the clock consumer binding,
@@ -17,7 +17,7 @@ See the clock consumer binding,
 Example:
 
 pwm1: pwm@53fb4000 {
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
reg = <0x53fb4000 0x4000>;
clocks = < IMX5_CLK_PWM1_IPG_GATE>,
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 0d7d5ac..8d1d471 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -601,7 +601,7 @@
clocks = < IMX7D_PWM1_ROOT_CLK>,
 < IMX7D_PWM1_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -612,7 +612,7 @@
clocks = < IMX7D_PWM2_ROOT_CLK>,
 < IMX7D_PWM2_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -623,7 +623,7 @@
clocks = < IMX7D_PWM3_ROOT_CLK>,
 < IMX7D_PWM3_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
@@ -634,7 +634,7 @@
clocks = < IMX7D_PWM4_ROOT_CLK>,
 < IMX7D_PWM4_ROOT_CLK>;
clock-names = "ipg", "per";
-   #pwm-cells = <2>;
+   #pwm-cells = <3>;
status = "disabled";
};
 
-- 
2.9.2



[PATCH 3/3] arm: dts: imx7-colibri: Use enable-gpios for BL_ON

2016-09-19 Thread Bhuvanchandra DV
Use pwm-backlight driver 'enable-gpios' property for backlight on/off control.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index 2af5e3e..ce5edb5 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,10 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
+   pinctrl-names = "default";
+   pinctrl-0 = <_gpio_bl_on>;
pwms = < 0 500 0>;
+   enable-gpios = < 1 GPIO_ACTIVE_HIGH>;
};
 
reg_module_3v3: regulator-module-3v3 {
@@ -356,7 +359,6 @@
fsl,pins = <
MX7D_PAD_ECSPI2_SS0__GPIO4_IO23 0x14 /* SODIMM 
65 */
MX7D_PAD_SD1_CD_B__GPIO5_IO00x14 /* SODIMM 
69 */
-   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14 /* SODIMM 
71 */
MX7D_PAD_I2C4_SDA__GPIO4_IO15   0x14 /* SODIMM 
75 */
MX7D_PAD_ECSPI1_MISO__GPIO4_IO180x14 /* SODIMM 
79 */
MX7D_PAD_I2C3_SCL__GPIO4_IO12   0x14 /* SODIMM 
81 */
@@ -388,6 +390,12 @@
>;
};
 
+   pinctrl_gpio_bl_on: gpio-bl-on {
+   fsl,pins = <
+   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14
+   >;
+   };
+
pinctrl_i2c1_int: i2c1-int-grp { /* PMIC / TOUCH */
fsl,pins = <
MX7D_PAD_GPIO1_IO13__GPIO1_IO13 0x79
-- 
2.9.2



[PATCH 3/3] arm: dts: imx7-colibri: Use enable-gpios for BL_ON

2016-09-19 Thread Bhuvanchandra DV
Use pwm-backlight driver 'enable-gpios' property for backlight on/off control.

Signed-off-by: Bhuvanchandra DV 
---
 arch/arm/boot/dts/imx7-colibri.dtsi | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi 
b/arch/arm/boot/dts/imx7-colibri.dtsi
index 2af5e3e..ce5edb5 100644
--- a/arch/arm/boot/dts/imx7-colibri.dtsi
+++ b/arch/arm/boot/dts/imx7-colibri.dtsi
@@ -43,7 +43,10 @@
 / {
bl: backlight {
compatible = "pwm-backlight";
+   pinctrl-names = "default";
+   pinctrl-0 = <_gpio_bl_on>;
pwms = < 0 500 0>;
+   enable-gpios = < 1 GPIO_ACTIVE_HIGH>;
};
 
reg_module_3v3: regulator-module-3v3 {
@@ -356,7 +359,6 @@
fsl,pins = <
MX7D_PAD_ECSPI2_SS0__GPIO4_IO23 0x14 /* SODIMM 
65 */
MX7D_PAD_SD1_CD_B__GPIO5_IO00x14 /* SODIMM 
69 */
-   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14 /* SODIMM 
71 */
MX7D_PAD_I2C4_SDA__GPIO4_IO15   0x14 /* SODIMM 
75 */
MX7D_PAD_ECSPI1_MISO__GPIO4_IO180x14 /* SODIMM 
79 */
MX7D_PAD_I2C3_SCL__GPIO4_IO12   0x14 /* SODIMM 
81 */
@@ -388,6 +390,12 @@
>;
};
 
+   pinctrl_gpio_bl_on: gpio-bl-on {
+   fsl,pins = <
+   MX7D_PAD_SD1_WP__GPIO5_IO1  0x14
+   >;
+   };
+
pinctrl_i2c1_int: i2c1-int-grp { /* PMIC / TOUCH */
fsl,pins = <
MX7D_PAD_GPIO1_IO13__GPIO1_IO13 0x79
-- 
2.9.2



Re: [PATCH RESEND v2 0/8] Improve Rx/Tx DMA implementation

2016-08-03 Thread Bhuvanchandra DV

Hi Andrew,

On 08/02/16 20:58, Andrew Lunn wrote:


On Tue, Aug 02, 2016 at 01:23:32PM +0530, Bhuvanchandra DV wrote:

Hello,

Any comments on this patchset?

I tested v1 on my Vybrid board and it fixed the corruption issues i
was seeing with the console on a serial port. I will try to test this
version.


Thanks! for testing it. As such there is nothing much changed w.r.t
code, we only split the suspend/resume patch in v2.

--
Bhuvan



 Andrew


On 07/19/16 13:13, Bhuvanchandra DV wrote:


Drop the clock change patch from v2 patchset[1] since it is applied.

Changes since v1:
Split suspend/resume patch.

[1] https://lkml.org/lkml/2016/6/28/124

Bhuvanchandra DV (5):
   tty: serial: fsl_lpuart: Fix broken 8m/s1 support
   tty: serial: fsl_lpuart: Use cyclic DMA for Rx
   tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx
   tty: serial: fsl_lpuart: Update suspend/resume for DMA mode
   tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (3):
   tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
   tty: serial: fsl_lpuart: support suspend/resume
   tty: serial: fsl_lpuart: fix clearing of receive flag

  drivers/tty/serial/fsl_lpuart.c | 810 +++-
  1 file changed, 476 insertions(+), 334 deletions(-)



___
linux-arm-kernel mailing list
linux-arm-ker...@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel




Re: [PATCH RESEND v2 0/8] Improve Rx/Tx DMA implementation

2016-08-03 Thread Bhuvanchandra DV

Hi Andrew,

On 08/02/16 20:58, Andrew Lunn wrote:


On Tue, Aug 02, 2016 at 01:23:32PM +0530, Bhuvanchandra DV wrote:

Hello,

Any comments on this patchset?

I tested v1 on my Vybrid board and it fixed the corruption issues i
was seeing with the console on a serial port. I will try to test this
version.


Thanks! for testing it. As such there is nothing much changed w.r.t
code, we only split the suspend/resume patch in v2.

--
Bhuvan



 Andrew


On 07/19/16 13:13, Bhuvanchandra DV wrote:


Drop the clock change patch from v2 patchset[1] since it is applied.

Changes since v1:
Split suspend/resume patch.

[1] https://lkml.org/lkml/2016/6/28/124

Bhuvanchandra DV (5):
   tty: serial: fsl_lpuart: Fix broken 8m/s1 support
   tty: serial: fsl_lpuart: Use cyclic DMA for Rx
   tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx
   tty: serial: fsl_lpuart: Update suspend/resume for DMA mode
   tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (3):
   tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
   tty: serial: fsl_lpuart: support suspend/resume
   tty: serial: fsl_lpuart: fix clearing of receive flag

  drivers/tty/serial/fsl_lpuart.c | 810 +++-
  1 file changed, 476 insertions(+), 334 deletions(-)



___
linux-arm-kernel mailing list
linux-arm-ker...@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel




Re: [PATCH RESEND v2 0/8] Improve Rx/Tx DMA implementation

2016-08-02 Thread Bhuvanchandra DV

Hello,

Any comments on this patchset?

On 07/19/16 13:13, Bhuvanchandra DV wrote:


Drop the clock change patch from v2 patchset[1] since it is applied.

Changes since v1:
Split suspend/resume patch.

[1] https://lkml.org/lkml/2016/6/28/124

Bhuvanchandra DV (5):
   tty: serial: fsl_lpuart: Fix broken 8m/s1 support
   tty: serial: fsl_lpuart: Use cyclic DMA for Rx
   tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx
   tty: serial: fsl_lpuart: Update suspend/resume for DMA mode
   tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (3):
   tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
   tty: serial: fsl_lpuart: support suspend/resume
   tty: serial: fsl_lpuart: fix clearing of receive flag

  drivers/tty/serial/fsl_lpuart.c | 810 +++-
  1 file changed, 476 insertions(+), 334 deletions(-)





Re: [PATCH RESEND v2 0/8] Improve Rx/Tx DMA implementation

2016-08-02 Thread Bhuvanchandra DV

Hello,

Any comments on this patchset?

On 07/19/16 13:13, Bhuvanchandra DV wrote:


Drop the clock change patch from v2 patchset[1] since it is applied.

Changes since v1:
Split suspend/resume patch.

[1] https://lkml.org/lkml/2016/6/28/124

Bhuvanchandra DV (5):
   tty: serial: fsl_lpuart: Fix broken 8m/s1 support
   tty: serial: fsl_lpuart: Use cyclic DMA for Rx
   tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx
   tty: serial: fsl_lpuart: Update suspend/resume for DMA mode
   tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (3):
   tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
   tty: serial: fsl_lpuart: support suspend/resume
   tty: serial: fsl_lpuart: fix clearing of receive flag

  drivers/tty/serial/fsl_lpuart.c | 810 +++-
  1 file changed, 476 insertions(+), 334 deletions(-)





[PATCH RESEND v2 6/8] tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx

2016-07-19 Thread Bhuvanchandra DV
Drop PIO to DMA switching and use scatter/gather DMA for Tx path to improve
performance.

Some part of the code is borrowed from imx serial driver.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 257 ++--
 1 file changed, 113 insertions(+), 144 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 51d2b5a..27687d5 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -244,18 +244,18 @@ struct lpuart_port {
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
-   dma_addr_t  dma_tx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
-   unsigned char   *dma_tx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
-   int dma_tx_in_progress;
+   booldma_tx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
-   struct scatterlist  rx_sgl;
+   struct scatterlist  rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
+   unsigned intdma_tx_nents;
+   wait_queue_head_t   dma_wait;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -316,103 +316,118 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_pio_tx(struct lpuart_port *sport)
+static void lpuart_dma_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
-   unsigned long flags;
-
-   spin_lock_irqsave(>port.lock, flags);
+   struct scatterlist *sgl = sport->tx_sgl;
+   struct device *dev = sport->port.dev;
+   int ret;
 
-   while (!uart_circ_empty(xmit) &&
-   readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
-   writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
-   xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-   sport->port.icount.tx++;
-   }
+   if (sport->dma_tx_in_progress)
+   return;
 
-   if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-   uart_write_wakeup(>port);
+   sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
 
-   if (uart_circ_empty(xmit))
-   writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
-   sport->port.membase + UARTCR5);
+   if (xmit->tail < xmit->head) {
+   sport->dma_tx_nents = 1;
+   sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
+   } else {
+   sport->dma_tx_nents = 2;
+   sg_init_table(sgl, 2);
+   sg_set_buf(sgl, xmit->buf + xmit->tail,
+   UART_XMIT_SIZE - xmit->tail);
+   sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+   }
 
-   spin_unlock_irqrestore(>port.lock, flags);
-}
+   ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   if (!ret) {
+   dev_err(dev, "DMA mapping error for TX.\n");
+   return;
+   }
 
-static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
-{
-   struct circ_buf *xmit = >port.state->xmit;
-   dma_addr_t tx_bus_addr;
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
-   UART_XMIT_SIZE, DMA_TO_DEVICE);
-   sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1);
-   tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
-   sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
-   tx_bus_addr, sport->dma_tx_bytes,
+   sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
+   sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
-
if (!sport->dma_tx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for tx\n");
-   return -EIO;
+   dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   dev_err(dev, "Cannot prepare TX slave DMA!\n");
+   return;
}
 
sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
sport->dma_tx_desc->callback_param = sport;
-   sport->dma_tx_in_progress = 1;
+   sport->dma_tx_in_progress = true;

[PATCH RESEND v2 6/8] tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx

2016-07-19 Thread Bhuvanchandra DV
Drop PIO to DMA switching and use scatter/gather DMA for Tx path to improve
performance.

Some part of the code is borrowed from imx serial driver.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 257 ++--
 1 file changed, 113 insertions(+), 144 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 51d2b5a..27687d5 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -244,18 +244,18 @@ struct lpuart_port {
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
-   dma_addr_t  dma_tx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
-   unsigned char   *dma_tx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
-   int dma_tx_in_progress;
+   booldma_tx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
-   struct scatterlist  rx_sgl;
+   struct scatterlist  rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
+   unsigned intdma_tx_nents;
+   wait_queue_head_t   dma_wait;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -316,103 +316,118 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_pio_tx(struct lpuart_port *sport)
+static void lpuart_dma_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
-   unsigned long flags;
-
-   spin_lock_irqsave(>port.lock, flags);
+   struct scatterlist *sgl = sport->tx_sgl;
+   struct device *dev = sport->port.dev;
+   int ret;
 
-   while (!uart_circ_empty(xmit) &&
-   readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
-   writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
-   xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-   sport->port.icount.tx++;
-   }
+   if (sport->dma_tx_in_progress)
+   return;
 
-   if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-   uart_write_wakeup(>port);
+   sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
 
-   if (uart_circ_empty(xmit))
-   writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
-   sport->port.membase + UARTCR5);
+   if (xmit->tail < xmit->head) {
+   sport->dma_tx_nents = 1;
+   sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
+   } else {
+   sport->dma_tx_nents = 2;
+   sg_init_table(sgl, 2);
+   sg_set_buf(sgl, xmit->buf + xmit->tail,
+   UART_XMIT_SIZE - xmit->tail);
+   sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+   }
 
-   spin_unlock_irqrestore(>port.lock, flags);
-}
+   ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   if (!ret) {
+   dev_err(dev, "DMA mapping error for TX.\n");
+   return;
+   }
 
-static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
-{
-   struct circ_buf *xmit = >port.state->xmit;
-   dma_addr_t tx_bus_addr;
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
-   UART_XMIT_SIZE, DMA_TO_DEVICE);
-   sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1);
-   tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
-   sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
-   tx_bus_addr, sport->dma_tx_bytes,
+   sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
+   sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
-
if (!sport->dma_tx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for tx\n");
-   return -EIO;
+   dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   dev_err(dev, "Cannot prepare TX slave DMA!\n");
+   return;
}
 
sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
sport->dma_tx_desc->callback_param = sport;
-   sport->dma_tx_in_progress = 1;
+   sport->dma_tx_in_progress = true;
sport->dma_tx_cooki

[PATCH RESEND v2 4/8] tty: serial: fsl_lpuart: Fix broken 8m/s1 support

2016-07-19 Thread Bhuvanchandra DV
By default the driver always configure the mode as 8s1 even when 8m1 mode is
selected. Fix this by adding support to control the space/mark bit.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 9 +++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 97c1fda..615f191 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1220,13 +1220,14 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
unsigned long flags;
-   unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+   unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem;
unsigned int  baud;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned int sbr, brfa;
 
cr1 = old_cr1 = readb(sport->port.membase + UARTCR1);
old_cr2 = readb(sport->port.membase + UARTCR2);
+   cr3 = readb(sport->port.membase + UARTCR3);
cr4 = readb(sport->port.membase + UARTCR4);
bdh = readb(sport->port.membase + UARTBDH);
modem = readb(sport->port.membase + UARTMODEM);
@@ -1274,7 +1275,10 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
if ((termios->c_cflag & PARENB)) {
if (termios->c_cflag & CMSPAR) {
cr1 &= ~UARTCR1_PE;
-   cr1 |= UARTCR1_M;
+   if (termios->c_cflag & PARODD)
+   cr3 |= UARTCR3_T8;
+   else
+   cr3 &= ~UARTCR3_T8;
} else {
cr1 |= UARTCR1_PE;
if ((termios->c_cflag & CSIZE) == CS8)
@@ -1342,6 +1346,7 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
writeb(cr4 | brfa, sport->port.membase + UARTCR4);
writeb(bdh, sport->port.membase + UARTBDH);
writeb(sbr & 0xFF, sport->port.membase + UARTBDL);
+   writeb(cr3, sport->port.membase + UARTCR3);
writeb(cr1, sport->port.membase + UARTCR1);
writeb(modem, sport->port.membase + UARTMODEM);
 
-- 
2.9.0



[PATCH RESEND v2 4/8] tty: serial: fsl_lpuart: Fix broken 8m/s1 support

2016-07-19 Thread Bhuvanchandra DV
By default the driver always configure the mode as 8s1 even when 8m1 mode is
selected. Fix this by adding support to control the space/mark bit.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 9 +++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 97c1fda..615f191 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1220,13 +1220,14 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
unsigned long flags;
-   unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+   unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem;
unsigned int  baud;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned int sbr, brfa;
 
cr1 = old_cr1 = readb(sport->port.membase + UARTCR1);
old_cr2 = readb(sport->port.membase + UARTCR2);
+   cr3 = readb(sport->port.membase + UARTCR3);
cr4 = readb(sport->port.membase + UARTCR4);
bdh = readb(sport->port.membase + UARTBDH);
modem = readb(sport->port.membase + UARTMODEM);
@@ -1274,7 +1275,10 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
if ((termios->c_cflag & PARENB)) {
if (termios->c_cflag & CMSPAR) {
cr1 &= ~UARTCR1_PE;
-   cr1 |= UARTCR1_M;
+   if (termios->c_cflag & PARODD)
+   cr3 |= UARTCR3_T8;
+   else
+   cr3 &= ~UARTCR3_T8;
} else {
cr1 |= UARTCR1_PE;
if ((termios->c_cflag & CSIZE) == CS8)
@@ -1342,6 +1346,7 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
writeb(cr4 | brfa, sport->port.membase + UARTCR4);
writeb(bdh, sport->port.membase + UARTBDH);
writeb(sbr & 0xFF, sport->port.membase + UARTBDL);
+   writeb(cr3, sport->port.membase + UARTCR3);
writeb(cr1, sport->port.membase + UARTCR1);
writeb(modem, sport->port.membase + UARTMODEM);
 
-- 
2.9.0



[PATCH RESEND v2 3/8] tty: serial: fsl_lpuart: fix clearing of receive flag

2016-07-19 Thread Bhuvanchandra DV
From: Stefan Agner <ste...@agner.ch>

Commit 8e4934c6d6c6 ("tty: serial: fsl_lpuart: clear receive flag on FIFO
flush") implemented clearing of the receive flag by reading the status register
only. It turned out that even though we flush the FIFO afterwards, a explicit
read of the data register is still required.

This leads to a FIFO underrun. To avoid this, follow the advice in the overrun
"Operation section": Unconditionally clear RXUF after using RXFLUSH.

Signed-off-by: Stefan Agner <ste...@agner.ch>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 75a2098..97c1fda 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -935,13 +935,16 @@ static void lpuart_setup_watermark(struct lpuart_port 
*sport)
writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
sport->port.membase + UARTPFIFO);
 
-   /* explicitly clear RDRF */
-   readb(sport->port.membase + UARTSR1);
-
/* flush Tx and Rx FIFO */
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
 
+   /* explicitly clear RDRF */
+   if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) {
+   readb(sport->port.membase + UARTDR);
+   writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
+   }
+
writeb(0, sport->port.membase + UARTTWFIFO);
writeb(1, sport->port.membase + UARTRWFIFO);
 
-- 
2.9.0



[PATCH RESEND v2 3/8] tty: serial: fsl_lpuart: fix clearing of receive flag

2016-07-19 Thread Bhuvanchandra DV
From: Stefan Agner 

Commit 8e4934c6d6c6 ("tty: serial: fsl_lpuart: clear receive flag on FIFO
flush") implemented clearing of the receive flag by reading the status register
only. It turned out that even though we flush the FIFO afterwards, a explicit
read of the data register is still required.

This leads to a FIFO underrun. To avoid this, follow the advice in the overrun
"Operation section": Unconditionally clear RXUF after using RXFLUSH.

Signed-off-by: Stefan Agner 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 75a2098..97c1fda 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -935,13 +935,16 @@ static void lpuart_setup_watermark(struct lpuart_port 
*sport)
writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
sport->port.membase + UARTPFIFO);
 
-   /* explicitly clear RDRF */
-   readb(sport->port.membase + UARTSR1);
-
/* flush Tx and Rx FIFO */
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
 
+   /* explicitly clear RDRF */
+   if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) {
+   readb(sport->port.membase + UARTDR);
+   writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
+   }
+
writeb(0, sport->port.membase + UARTTWFIFO);
writeb(1, sport->port.membase + UARTRWFIFO);
 
-- 
2.9.0



[PATCH RESEND v2 1/8] tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty

2016-07-19 Thread Bhuvanchandra DV
From: Stefan Agner <ste...@agner.ch>

Currently the tx_empty callback only considers the Transmit Complete Flag (TC).
The reference manual is not quite clear if the TC flag covers the TX FIFO too.
Debug prints on real hardware have shown that from time to time the TC flag is
asserted (indicating Transmitter idle) while there are still data in the
TX FIFO. Hence, in this case the serial core will call the shutdown callback
even though there are data remaining in the TX FIFO buffers.

Avoid early shutdowns by considering the TX FIFO empty flag too. Also avoid
theoretical race conditions between DMA and the driver by checking whether the
TX DMA is in progress too.

Signed-off-by: Stefan Agner <ste...@agner.ch>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 14 --
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3d79003..fabfa7e 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -810,8 +810,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
 /* return TIOCSER_TEMT when transmitter is not busy */
 static unsigned int lpuart_tx_empty(struct uart_port *port)
 {
-   return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
-   TIOCSER_TEMT : 0;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+   unsigned char sr1 = readb(port->membase + UARTSR1);
+   unsigned char sfifo = readb(port->membase + UARTSFIFO);
+
+   if (sport->dma_tx_in_progress)
+   return 0;
+
+   if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT)
+   return TIOCSER_TEMT;
+
+   return 0;
 }
 
 static unsigned int lpuart32_tx_empty(struct uart_port *port)
-- 
2.9.0



[PATCH RESEND v2 7/8] tty: serial: fsl_lpuart: Update suspend/resume for DMA mode

2016-07-19 Thread Bhuvanchandra DV
When DMA mode is enabled one need to make sure the DMA channels are idle before
entering suspend mode especially when UART ports which are set as wakeup source
and console port with no_console_suspend is set. This patch takes care of
gracefully releasing DMA channels for the above two cases and start the DMA at
resume.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 44 +
 1 file changed, 44 insertions(+)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 27687d5..134090a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1943,6 +1943,30 @@ static int lpuart_suspend(struct device *dev)
}
 
uart_suspend_port(_reg, >port);
+
+   if (sport->lpuart_dma_rx_use) {
+   /*
+* EDMA driver during suspend will forcefully release any
+* non-idle DMA channels. If port wakeup is enabled or if port
+* is console port or 'no_console_suspend' is set the Rx DMA
+* cannot resume as as expected, hence gracefully release the
+* Rx DMA path before suspend and start Rx DMA path on resume.
+*/
+   if (sport->port.irq_wake) {
+   del_timer_sync(>lpuart_timer);
+   lpuart_dma_rx_free(>port);
+   }
+
+   /* Disable Rx DMA to use UART port as wakeup source */
+   writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS,
+   sport->port.membase + UARTCR5);
+   }
+
+   if (sport->lpuart_dma_tx_use) {
+   sport->dma_tx_in_progress = false;
+   dmaengine_terminate_all(sport->dma_tx_chan);
+   }
+
if (sport->port.suspended && !sport->port.irq_wake)
clk_disable_unprepare(sport->clk);
 
@@ -1970,6 +1994,26 @@ static int lpuart_resume(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
 
+   if (sport->lpuart_dma_rx_use) {
+   if (sport->port.irq_wake) {
+   if (!lpuart_start_rx_dma(sport)) {
+   sport->lpuart_dma_rx_use = true;
+   rx_dma_timer_init(sport);
+   } else {
+   sport->lpuart_dma_rx_use = false;
+   }
+   }
+   }
+
+   if (sport->dma_tx_chan && !lpuart_dma_tx_request(>port)) {
+   init_waitqueue_head(>dma_wait);
+   sport->lpuart_dma_tx_use = true;
+   writeb(readb(sport->port.membase + UARTCR5) |
+   UARTCR5_TDMAS, sport->port.membase + UARTCR5);
+   } else {
+   sport->lpuart_dma_tx_use = false;
+   }
+
uart_resume_port(_reg, >port);
 
return 0;
-- 
2.9.0



[PATCH RESEND v2 1/8] tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty

2016-07-19 Thread Bhuvanchandra DV
From: Stefan Agner 

Currently the tx_empty callback only considers the Transmit Complete Flag (TC).
The reference manual is not quite clear if the TC flag covers the TX FIFO too.
Debug prints on real hardware have shown that from time to time the TC flag is
asserted (indicating Transmitter idle) while there are still data in the
TX FIFO. Hence, in this case the serial core will call the shutdown callback
even though there are data remaining in the TX FIFO buffers.

Avoid early shutdowns by considering the TX FIFO empty flag too. Also avoid
theoretical race conditions between DMA and the driver by checking whether the
TX DMA is in progress too.

Signed-off-by: Stefan Agner 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 14 --
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3d79003..fabfa7e 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -810,8 +810,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
 /* return TIOCSER_TEMT when transmitter is not busy */
 static unsigned int lpuart_tx_empty(struct uart_port *port)
 {
-   return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
-   TIOCSER_TEMT : 0;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+   unsigned char sr1 = readb(port->membase + UARTSR1);
+   unsigned char sfifo = readb(port->membase + UARTSFIFO);
+
+   if (sport->dma_tx_in_progress)
+   return 0;
+
+   if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT)
+   return TIOCSER_TEMT;
+
+   return 0;
 }
 
 static unsigned int lpuart32_tx_empty(struct uart_port *port)
-- 
2.9.0



[PATCH RESEND v2 7/8] tty: serial: fsl_lpuart: Update suspend/resume for DMA mode

2016-07-19 Thread Bhuvanchandra DV
When DMA mode is enabled one need to make sure the DMA channels are idle before
entering suspend mode especially when UART ports which are set as wakeup source
and console port with no_console_suspend is set. This patch takes care of
gracefully releasing DMA channels for the above two cases and start the DMA at
resume.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 44 +
 1 file changed, 44 insertions(+)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 27687d5..134090a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1943,6 +1943,30 @@ static int lpuart_suspend(struct device *dev)
}
 
uart_suspend_port(_reg, >port);
+
+   if (sport->lpuart_dma_rx_use) {
+   /*
+* EDMA driver during suspend will forcefully release any
+* non-idle DMA channels. If port wakeup is enabled or if port
+* is console port or 'no_console_suspend' is set the Rx DMA
+* cannot resume as as expected, hence gracefully release the
+* Rx DMA path before suspend and start Rx DMA path on resume.
+*/
+   if (sport->port.irq_wake) {
+   del_timer_sync(>lpuart_timer);
+   lpuart_dma_rx_free(>port);
+   }
+
+   /* Disable Rx DMA to use UART port as wakeup source */
+   writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS,
+   sport->port.membase + UARTCR5);
+   }
+
+   if (sport->lpuart_dma_tx_use) {
+   sport->dma_tx_in_progress = false;
+   dmaengine_terminate_all(sport->dma_tx_chan);
+   }
+
if (sport->port.suspended && !sport->port.irq_wake)
clk_disable_unprepare(sport->clk);
 
@@ -1970,6 +1994,26 @@ static int lpuart_resume(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
 
+   if (sport->lpuart_dma_rx_use) {
+   if (sport->port.irq_wake) {
+   if (!lpuart_start_rx_dma(sport)) {
+   sport->lpuart_dma_rx_use = true;
+   rx_dma_timer_init(sport);
+   } else {
+   sport->lpuart_dma_rx_use = false;
+   }
+   }
+   }
+
+   if (sport->dma_tx_chan && !lpuart_dma_tx_request(>port)) {
+   init_waitqueue_head(>dma_wait);
+   sport->lpuart_dma_tx_use = true;
+   writeb(readb(sport->port.membase + UARTCR5) |
+   UARTCR5_TDMAS, sport->port.membase + UARTCR5);
+   } else {
+   sport->lpuart_dma_tx_use = false;
+   }
+
uart_resume_port(_reg, >port);
 
return 0;
-- 
2.9.0



[PATCH RESEND v2 5/8] tty: serial: fsl_lpuart: Use cyclic DMA for Rx

2016-07-19 Thread Bhuvanchandra DV
The initial approach of DMA implementatin for RX is inefficient due to switching
from PIO to DMA, this leads to overruns especially on instances with the smaller
FIFO. To address these issues this patch uses a cyclic DMA for receiver path.

Some part of the code is borrowed from atmel serial driver.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 483 +---
 1 file changed, 258 insertions(+), 225 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 615f191..51d2b5a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -224,7 +224,8 @@
 #define UARTWATER_TXWATER_OFF  0
 #define UARTWATER_RXWATER_OFF  16
 
-#define FSL_UART_RX_DMA_BUFFER_SIZE64
+/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
+#define DMA_RX_TIMEOUT (10)
 
 #define DRIVER_NAME"fsl-lpuart"
 #define DEV_NAME   "ttyLP"
@@ -244,17 +245,17 @@ struct lpuart_port {
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
dma_addr_t  dma_tx_buf_bus;
-   dma_addr_t  dma_rx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
unsigned char   *dma_tx_buf_virt;
-   unsigned char   *dma_rx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
int dma_tx_in_progress;
-   int dma_rx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
+   struct scatterlist  rx_sgl;
+   struct circ_buf rx_ring;
+   int rx_dma_rng_buf_len;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -270,7 +271,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
 /* Forward declare this for the dma callbacks*/
 static void lpuart_dma_tx_complete(void *arg);
-static void lpuart_dma_rx_complete(void *arg);
 
 static u32 lpuart32_read(void __iomem *addr)
 {
@@ -316,32 +316,6 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
-   struct tty_port *tty, int count)
-{
-   int copied;
-
-   sport->port.icount.rx += count;
-
-   if (!tty) {
-   dev_err(sport->port.dev, "No tty port\n");
-   return;
-   }
-
-   dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-   copied = tty_insert_flip_string(tty,
-   ((unsigned char *)(sport->dma_rx_buf_virt)), count);
-
-   if (copied != count) {
-   WARN_ON(1);
-   dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
-   }
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-}
-
 static void lpuart_pio_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
@@ -433,28 +407,6 @@ static void lpuart_dma_tx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static int lpuart_dma_rx(struct lpuart_port *sport)
-{
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-   sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
-   sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
-   DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
-
-   if (!sport->dma_rx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for rx\n");
-   return -EIO;
-   }
-
-   sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
-   sport->dma_rx_desc->callback_param = sport;
-   sport->dma_rx_in_progress = 1;
-   sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
-   dma_async_issue_pending(sport->dma_rx_chan);
-
-   return 0;
-}
-
 static void lpuart_flush_buffer(struct uart_port *port)
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
@@ -464,73 +416,6 @@ static void lpuart_flush_buffer(struct uart_port *port)
}
 }
 
-static void lpuart_dma_rx_complete(void *arg)
-{
-   struct lpuart_port *sport = arg;
-   struct tty_port *port = >port.state->port;
-   unsigned long flags;
-
-   async_tx_ack(sport->dma_rx_desc);
-   mod_timer(>lpuart_timer, jiffies + sport->dma_rx_timeout);
-
-

[PATCH RESEND v2 5/8] tty: serial: fsl_lpuart: Use cyclic DMA for Rx

2016-07-19 Thread Bhuvanchandra DV
The initial approach of DMA implementatin for RX is inefficient due to switching
from PIO to DMA, this leads to overruns especially on instances with the smaller
FIFO. To address these issues this patch uses a cyclic DMA for receiver path.

Some part of the code is borrowed from atmel serial driver.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 483 +---
 1 file changed, 258 insertions(+), 225 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 615f191..51d2b5a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -224,7 +224,8 @@
 #define UARTWATER_TXWATER_OFF  0
 #define UARTWATER_RXWATER_OFF  16
 
-#define FSL_UART_RX_DMA_BUFFER_SIZE64
+/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
+#define DMA_RX_TIMEOUT (10)
 
 #define DRIVER_NAME"fsl-lpuart"
 #define DEV_NAME   "ttyLP"
@@ -244,17 +245,17 @@ struct lpuart_port {
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
dma_addr_t  dma_tx_buf_bus;
-   dma_addr_t  dma_rx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
unsigned char   *dma_tx_buf_virt;
-   unsigned char   *dma_rx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
int dma_tx_in_progress;
-   int dma_rx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
+   struct scatterlist  rx_sgl;
+   struct circ_buf rx_ring;
+   int rx_dma_rng_buf_len;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -270,7 +271,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
 /* Forward declare this for the dma callbacks*/
 static void lpuart_dma_tx_complete(void *arg);
-static void lpuart_dma_rx_complete(void *arg);
 
 static u32 lpuart32_read(void __iomem *addr)
 {
@@ -316,32 +316,6 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
-   struct tty_port *tty, int count)
-{
-   int copied;
-
-   sport->port.icount.rx += count;
-
-   if (!tty) {
-   dev_err(sport->port.dev, "No tty port\n");
-   return;
-   }
-
-   dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-   copied = tty_insert_flip_string(tty,
-   ((unsigned char *)(sport->dma_rx_buf_virt)), count);
-
-   if (copied != count) {
-   WARN_ON(1);
-   dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
-   }
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-}
-
 static void lpuart_pio_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
@@ -433,28 +407,6 @@ static void lpuart_dma_tx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static int lpuart_dma_rx(struct lpuart_port *sport)
-{
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-   sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
-   sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
-   DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
-
-   if (!sport->dma_rx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for rx\n");
-   return -EIO;
-   }
-
-   sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
-   sport->dma_rx_desc->callback_param = sport;
-   sport->dma_rx_in_progress = 1;
-   sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
-   dma_async_issue_pending(sport->dma_rx_chan);
-
-   return 0;
-}
-
 static void lpuart_flush_buffer(struct uart_port *port)
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
@@ -464,73 +416,6 @@ static void lpuart_flush_buffer(struct uart_port *port)
}
 }
 
-static void lpuart_dma_rx_complete(void *arg)
-{
-   struct lpuart_port *sport = arg;
-   struct tty_port *port = >port.state->port;
-   unsigned long flags;
-
-   async_tx_ack(sport->dma_rx_desc);
-   mod_timer(>lpuart_timer, jiffies + sport->dma_rx_timeout);
-
-   spin_lock_irqsave(>port.lock, fl

[PATCH RESEND v2 8/8] tty: serial: fsl_lpuart: Add support for RS-485

2016-07-19 Thread Bhuvanchandra DV
Enable Vybrid's build-in support for RS-485 auto RTS for controlling line
direction of RS-485 transceiver driver.

Enable RS485 feature by either using ioctrl 'TIOCSRS485' or enable it in the
device tree by setting 'linux,rs485-enabled-at-boot-time' property.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 78 +
 1 file changed, 72 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 134090a..4d1fca4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -917,6 +917,52 @@ static void lpuart_dma_rx_free(struct uart_port *port)
sport->dma_rx_cookie = -EINVAL;
 }
 
+static int lpuart_config_rs485(struct uart_port *port,
+   struct serial_rs485 *rs485)
+{
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+
+   u8 modem = readb(sport->port.membase + UARTMODEM) &
+   ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
+   writeb(modem, sport->port.membase + UARTMODEM);
+
+   if (rs485->flags & SER_RS485_ENABLED) {
+   /* Enable auto RS-485 RTS mode */
+   modem |= UARTMODEM_TXRTSE;
+
+   /*
+* RTS needs to be logic HIGH either during transer _or_ after
+* transfer, other variants are not supported by the hardware.
+*/
+
+   if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
+   SER_RS485_RTS_AFTER_SEND)))
+   rs485->flags |= SER_RS485_RTS_ON_SEND;
+
+   if (rs485->flags & SER_RS485_RTS_ON_SEND &&
+   rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+
+   /*
+* The hardware defaults to RTS logic HIGH while transfer.
+* Switch polarity in case RTS shall be logic HIGH
+* after transfer.
+* Note: UART is assumed to be active high.
+*/
+   if (rs485->flags & SER_RS485_RTS_ON_SEND)
+   modem &= ~UARTMODEM_TXRTSPOL;
+   else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   modem |= UARTMODEM_TXRTSPOL;
+   }
+
+   /* Store the new configuration */
+   sport->port.rs485 = *rs485;
+
+   writeb(modem, sport->port.membase + UARTMODEM);
+   return 0;
+}
+
 static unsigned int lpuart_get_mctrl(struct uart_port *port)
 {
unsigned int temp = 0;
@@ -950,17 +996,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port 
*port)
 static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
unsigned char temp;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
 
-   temp = readb(port->membase + UARTMODEM) &
+   /* Make sure RXRTSE bit is not set when RS485 is enabled */
+   if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) {
+   temp = readb(sport->port.membase + UARTMODEM) &
~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
 
-   if (mctrl & TIOCM_RTS)
-   temp |= UARTMODEM_RXRTSE;
+   if (mctrl & TIOCM_RTS)
+   temp |= UARTMODEM_RXRTSE;
 
-   if (mctrl & TIOCM_CTS)
-   temp |= UARTMODEM_TXCTSE;
+   if (mctrl & TIOCM_CTS)
+   temp |= UARTMODEM_TXCTSE;
 
-   writeb(temp, port->membase + UARTMODEM);
+   writeb(temp, port->membase + UARTMODEM);
+   }
 }
 
 static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1256,6 +1307,13 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
cr1 |= UARTCR1_M;
}
 
+   /*
+* When auto RS-485 RTS mode is enabled,
+* hardware flow control need to be disabled.
+*/
+   if (sport->port.rs485.flags & SER_RS485_ENABLED)
+   termios->c_cflag &= ~CRTSCTS;
+
if (termios->c_cflag & CRTSCTS) {
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
} else {
@@ -1864,6 +1922,8 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.ops = _pops;
sport->port.flags = UPF_BOOT_AUTOCONF;
 
+   sport->port.rs485_config = lpuart_config_rs485;
+
sport->clk = devm_clk_get(>dev, "ipg");
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
@@ -1904,6 +1964,12 @@ static int lpuart_probe(struct platform_device *pdev)
dev_info(sport->port.dev, "DMA rx channel request

[PATCH RESEND v2 2/8] tty: serial: fsl_lpuart: support suspend/resume

2016-07-19 Thread Bhuvanchandra DV
From: Stefan Agner <ste...@agner.ch>

Add suspend/resume support.

Signed-off-by: Stefan Agner <ste...@agner.ch>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 16 ++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index fabfa7e..75a2098 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -483,9 +483,8 @@ static void lpuart_dma_rx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static void lpuart_timer_func(unsigned long data)
+static void lpuart_dma_rx_terminate(struct lpuart_port *sport)
 {
-   struct lpuart_port *sport = (struct lpuart_port *)data;
struct tty_port *port = >port.state->port;
struct dma_tx_state state;
unsigned long flags;
@@ -510,6 +509,11 @@ static void lpuart_timer_func(unsigned long data)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
+static void lpuart_timer_func(unsigned long data)
+{
+   lpuart_dma_rx_terminate((struct lpuart_port *)data);
+}
+
 static inline void lpuart_prepare_rx(struct lpuart_port *sport)
 {
unsigned long flags;
@@ -1925,7 +1929,12 @@ static int lpuart_suspend(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
 
+   if (sport->dma_rx_in_progress)
+   lpuart_dma_rx_terminate(sport);
+
uart_suspend_port(_reg, >port);
+   if (sport->port.suspended && !sport->port.irq_wake)
+   clk_disable_unprepare(sport->clk);
 
return 0;
 }
@@ -1935,6 +1944,9 @@ static int lpuart_resume(struct device *dev)
struct lpuart_port *sport = dev_get_drvdata(dev);
unsigned long temp;
 
+   if (sport->port.suspended && !sport->port.irq_wake)
+   clk_prepare_enable(sport->clk);
+
if (sport->lpuart32) {
lpuart32_setup_watermark(sport);
temp = lpuart32_read(sport->port.membase + UARTCTRL);
-- 
2.9.0



[PATCH RESEND v2 0/8] Improve Rx/Tx DMA implementation

2016-07-19 Thread Bhuvanchandra DV
Drop the clock change patch from v2 patchset[1] since it is applied.

Changes since v1:
Split suspend/resume patch.

[1] https://lkml.org/lkml/2016/6/28/124

Bhuvanchandra DV (5):
  tty: serial: fsl_lpuart: Fix broken 8m/s1 support
  tty: serial: fsl_lpuart: Use cyclic DMA for Rx
  tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx
  tty: serial: fsl_lpuart: Update suspend/resume for DMA mode
  tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (3):
  tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
  tty: serial: fsl_lpuart: support suspend/resume
  tty: serial: fsl_lpuart: fix clearing of receive flag

 drivers/tty/serial/fsl_lpuart.c | 810 +++-
 1 file changed, 476 insertions(+), 334 deletions(-)

-- 
2.9.0



[PATCH RESEND v2 0/8] Improve Rx/Tx DMA implementation

2016-07-19 Thread Bhuvanchandra DV
Drop the clock change patch from v2 patchset[1] since it is applied.

Changes since v1:
Split suspend/resume patch.

[1] https://lkml.org/lkml/2016/6/28/124

Bhuvanchandra DV (5):
  tty: serial: fsl_lpuart: Fix broken 8m/s1 support
  tty: serial: fsl_lpuart: Use cyclic DMA for Rx
  tty: serial: fsl_lpuart: Use scatter/gather DMA for Tx
  tty: serial: fsl_lpuart: Update suspend/resume for DMA mode
  tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (3):
  tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
  tty: serial: fsl_lpuart: support suspend/resume
  tty: serial: fsl_lpuart: fix clearing of receive flag

 drivers/tty/serial/fsl_lpuart.c | 810 +++-
 1 file changed, 476 insertions(+), 334 deletions(-)

-- 
2.9.0



[PATCH RESEND v2 8/8] tty: serial: fsl_lpuart: Add support for RS-485

2016-07-19 Thread Bhuvanchandra DV
Enable Vybrid's build-in support for RS-485 auto RTS for controlling line
direction of RS-485 transceiver driver.

Enable RS485 feature by either using ioctrl 'TIOCSRS485' or enable it in the
device tree by setting 'linux,rs485-enabled-at-boot-time' property.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 78 +
 1 file changed, 72 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 134090a..4d1fca4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -917,6 +917,52 @@ static void lpuart_dma_rx_free(struct uart_port *port)
sport->dma_rx_cookie = -EINVAL;
 }
 
+static int lpuart_config_rs485(struct uart_port *port,
+   struct serial_rs485 *rs485)
+{
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+
+   u8 modem = readb(sport->port.membase + UARTMODEM) &
+   ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
+   writeb(modem, sport->port.membase + UARTMODEM);
+
+   if (rs485->flags & SER_RS485_ENABLED) {
+   /* Enable auto RS-485 RTS mode */
+   modem |= UARTMODEM_TXRTSE;
+
+   /*
+* RTS needs to be logic HIGH either during transer _or_ after
+* transfer, other variants are not supported by the hardware.
+*/
+
+   if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
+   SER_RS485_RTS_AFTER_SEND)))
+   rs485->flags |= SER_RS485_RTS_ON_SEND;
+
+   if (rs485->flags & SER_RS485_RTS_ON_SEND &&
+   rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+
+   /*
+* The hardware defaults to RTS logic HIGH while transfer.
+* Switch polarity in case RTS shall be logic HIGH
+* after transfer.
+* Note: UART is assumed to be active high.
+*/
+   if (rs485->flags & SER_RS485_RTS_ON_SEND)
+   modem &= ~UARTMODEM_TXRTSPOL;
+   else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   modem |= UARTMODEM_TXRTSPOL;
+   }
+
+   /* Store the new configuration */
+   sport->port.rs485 = *rs485;
+
+   writeb(modem, sport->port.membase + UARTMODEM);
+   return 0;
+}
+
 static unsigned int lpuart_get_mctrl(struct uart_port *port)
 {
unsigned int temp = 0;
@@ -950,17 +996,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port 
*port)
 static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
unsigned char temp;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
 
-   temp = readb(port->membase + UARTMODEM) &
+   /* Make sure RXRTSE bit is not set when RS485 is enabled */
+   if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) {
+   temp = readb(sport->port.membase + UARTMODEM) &
~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
 
-   if (mctrl & TIOCM_RTS)
-   temp |= UARTMODEM_RXRTSE;
+   if (mctrl & TIOCM_RTS)
+   temp |= UARTMODEM_RXRTSE;
 
-   if (mctrl & TIOCM_CTS)
-   temp |= UARTMODEM_TXCTSE;
+   if (mctrl & TIOCM_CTS)
+   temp |= UARTMODEM_TXCTSE;
 
-   writeb(temp, port->membase + UARTMODEM);
+   writeb(temp, port->membase + UARTMODEM);
+   }
 }
 
 static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1256,6 +1307,13 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
cr1 |= UARTCR1_M;
}
 
+   /*
+* When auto RS-485 RTS mode is enabled,
+* hardware flow control need to be disabled.
+*/
+   if (sport->port.rs485.flags & SER_RS485_ENABLED)
+   termios->c_cflag &= ~CRTSCTS;
+
if (termios->c_cflag & CRTSCTS) {
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
} else {
@@ -1864,6 +1922,8 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.ops = _pops;
sport->port.flags = UPF_BOOT_AUTOCONF;
 
+   sport->port.rs485_config = lpuart_config_rs485;
+
sport->clk = devm_clk_get(>dev, "ipg");
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
@@ -1904,6 +1964,12 @@ static int lpuart_probe(struct platform_device *pdev)
dev_info(sport->port.dev, "DMA rx channel request failed, "
&q

[PATCH RESEND v2 2/8] tty: serial: fsl_lpuart: support suspend/resume

2016-07-19 Thread Bhuvanchandra DV
From: Stefan Agner 

Add suspend/resume support.

Signed-off-by: Stefan Agner 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 16 ++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index fabfa7e..75a2098 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -483,9 +483,8 @@ static void lpuart_dma_rx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static void lpuart_timer_func(unsigned long data)
+static void lpuart_dma_rx_terminate(struct lpuart_port *sport)
 {
-   struct lpuart_port *sport = (struct lpuart_port *)data;
struct tty_port *port = >port.state->port;
struct dma_tx_state state;
unsigned long flags;
@@ -510,6 +509,11 @@ static void lpuart_timer_func(unsigned long data)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
+static void lpuart_timer_func(unsigned long data)
+{
+   lpuart_dma_rx_terminate((struct lpuart_port *)data);
+}
+
 static inline void lpuart_prepare_rx(struct lpuart_port *sport)
 {
unsigned long flags;
@@ -1925,7 +1929,12 @@ static int lpuart_suspend(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
 
+   if (sport->dma_rx_in_progress)
+   lpuart_dma_rx_terminate(sport);
+
uart_suspend_port(_reg, >port);
+   if (sport->port.suspended && !sport->port.irq_wake)
+   clk_disable_unprepare(sport->clk);
 
return 0;
 }
@@ -1935,6 +1944,9 @@ static int lpuart_resume(struct device *dev)
struct lpuart_port *sport = dev_get_drvdata(dev);
unsigned long temp;
 
+   if (sport->port.suspended && !sport->port.irq_wake)
+   clk_prepare_enable(sport->clk);
+
if (sport->lpuart32) {
lpuart32_setup_watermark(sport);
temp = lpuart32_read(sport->port.membase + UARTCTRL);
-- 
2.9.0



Re: [PATCH v2 0/9] Improve Rx/Tx DMA implementation

2016-07-04 Thread Bhuvanchandra DV

Ping!

On 06/28/16 11:02, Bhuvanchandra DV wrote:


Changes since v1:
Split suspend/resume patch.

Bhuvanchandra DV (5):
   tty: serial: fsl_lpuart: Fix broken 8m/s1 support
   tty: serial: fsl-lpuart: Use cyclic DMA for Rx
   tty: serial: fsl-lpuart: Use scatter/gather DMA for Tx
   tty: serial: fsl-lpuart: Update suspend/resume for DMA mode
   tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (4):
   tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
   clk: imx: vf610: Disable automatic clock gating for lpuart in LPSTOP mode
   tty: serial: fsl_lpuart: support suspend/resume
   tty: serial: fsl_lpuart: fix clearing of receive flag

  drivers/clk/imx/clk-vf610.c |  12 +-
  drivers/tty/serial/fsl_lpuart.c | 810 +++-
  2 files changed, 482 insertions(+), 340 deletions(-)





Re: [PATCH v2 0/9] Improve Rx/Tx DMA implementation

2016-07-04 Thread Bhuvanchandra DV

Ping!

On 06/28/16 11:02, Bhuvanchandra DV wrote:


Changes since v1:
Split suspend/resume patch.

Bhuvanchandra DV (5):
   tty: serial: fsl_lpuart: Fix broken 8m/s1 support
   tty: serial: fsl-lpuart: Use cyclic DMA for Rx
   tty: serial: fsl-lpuart: Use scatter/gather DMA for Tx
   tty: serial: fsl-lpuart: Update suspend/resume for DMA mode
   tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (4):
   tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
   clk: imx: vf610: Disable automatic clock gating for lpuart in LPSTOP mode
   tty: serial: fsl_lpuart: support suspend/resume
   tty: serial: fsl_lpuart: fix clearing of receive flag

  drivers/clk/imx/clk-vf610.c |  12 +-
  drivers/tty/serial/fsl_lpuart.c | 810 +++-
  2 files changed, 482 insertions(+), 340 deletions(-)





[PATCH v2 7/9] tty: serial: fsl-lpuart: Use scatter/gather DMA for Tx

2016-06-28 Thread Bhuvanchandra DV
Drop PIO to DMA switching and use scatter/gather DMA for Tx path to improve
performance.

Some part of the code is borrowed from imx serial driver.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 257 ++--
 1 file changed, 113 insertions(+), 144 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 51d2b5a..27687d5 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -244,18 +244,18 @@ struct lpuart_port {
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
-   dma_addr_t  dma_tx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
-   unsigned char   *dma_tx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
-   int dma_tx_in_progress;
+   booldma_tx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
-   struct scatterlist  rx_sgl;
+   struct scatterlist  rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
+   unsigned intdma_tx_nents;
+   wait_queue_head_t   dma_wait;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -316,103 +316,118 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_pio_tx(struct lpuart_port *sport)
+static void lpuart_dma_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
-   unsigned long flags;
-
-   spin_lock_irqsave(>port.lock, flags);
+   struct scatterlist *sgl = sport->tx_sgl;
+   struct device *dev = sport->port.dev;
+   int ret;
 
-   while (!uart_circ_empty(xmit) &&
-   readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
-   writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
-   xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-   sport->port.icount.tx++;
-   }
+   if (sport->dma_tx_in_progress)
+   return;
 
-   if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-   uart_write_wakeup(>port);
+   sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
 
-   if (uart_circ_empty(xmit))
-   writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
-   sport->port.membase + UARTCR5);
+   if (xmit->tail < xmit->head) {
+   sport->dma_tx_nents = 1;
+   sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
+   } else {
+   sport->dma_tx_nents = 2;
+   sg_init_table(sgl, 2);
+   sg_set_buf(sgl, xmit->buf + xmit->tail,
+   UART_XMIT_SIZE - xmit->tail);
+   sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+   }
 
-   spin_unlock_irqrestore(>port.lock, flags);
-}
+   ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   if (!ret) {
+   dev_err(dev, "DMA mapping error for TX.\n");
+   return;
+   }
 
-static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
-{
-   struct circ_buf *xmit = >port.state->xmit;
-   dma_addr_t tx_bus_addr;
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
-   UART_XMIT_SIZE, DMA_TO_DEVICE);
-   sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1);
-   tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
-   sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
-   tx_bus_addr, sport->dma_tx_bytes,
+   sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
+   sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
-
if (!sport->dma_tx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for tx\n");
-   return -EIO;
+   dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   dev_err(dev, "Cannot prepare TX slave DMA!\n");
+   return;
}
 
sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
sport->dma_tx_desc->callback_param = sport;
-   sport->dma_tx_in_progress = 1;
+   sport->dma_tx_in_progress = true;

[PATCH v2 7/9] tty: serial: fsl-lpuart: Use scatter/gather DMA for Tx

2016-06-28 Thread Bhuvanchandra DV
Drop PIO to DMA switching and use scatter/gather DMA for Tx path to improve
performance.

Some part of the code is borrowed from imx serial driver.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 257 ++--
 1 file changed, 113 insertions(+), 144 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 51d2b5a..27687d5 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -244,18 +244,18 @@ struct lpuart_port {
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
-   dma_addr_t  dma_tx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
-   unsigned char   *dma_tx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
-   int dma_tx_in_progress;
+   booldma_tx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
-   struct scatterlist  rx_sgl;
+   struct scatterlist  rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
+   unsigned intdma_tx_nents;
+   wait_queue_head_t   dma_wait;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -316,103 +316,118 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_pio_tx(struct lpuart_port *sport)
+static void lpuart_dma_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
-   unsigned long flags;
-
-   spin_lock_irqsave(>port.lock, flags);
+   struct scatterlist *sgl = sport->tx_sgl;
+   struct device *dev = sport->port.dev;
+   int ret;
 
-   while (!uart_circ_empty(xmit) &&
-   readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
-   writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
-   xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-   sport->port.icount.tx++;
-   }
+   if (sport->dma_tx_in_progress)
+   return;
 
-   if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-   uart_write_wakeup(>port);
+   sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
 
-   if (uart_circ_empty(xmit))
-   writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
-   sport->port.membase + UARTCR5);
+   if (xmit->tail < xmit->head) {
+   sport->dma_tx_nents = 1;
+   sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
+   } else {
+   sport->dma_tx_nents = 2;
+   sg_init_table(sgl, 2);
+   sg_set_buf(sgl, xmit->buf + xmit->tail,
+   UART_XMIT_SIZE - xmit->tail);
+   sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+   }
 
-   spin_unlock_irqrestore(>port.lock, flags);
-}
+   ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   if (!ret) {
+   dev_err(dev, "DMA mapping error for TX.\n");
+   return;
+   }
 
-static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
-{
-   struct circ_buf *xmit = >port.state->xmit;
-   dma_addr_t tx_bus_addr;
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
-   UART_XMIT_SIZE, DMA_TO_DEVICE);
-   sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1);
-   tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
-   sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
-   tx_bus_addr, sport->dma_tx_bytes,
+   sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
+   sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
-
if (!sport->dma_tx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for tx\n");
-   return -EIO;
+   dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   dev_err(dev, "Cannot prepare TX slave DMA!\n");
+   return;
}
 
sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
sport->dma_tx_desc->callback_param = sport;
-   sport->dma_tx_in_progress = 1;
+   sport->dma_tx_in_progress = true;
sport->dma_tx_cooki

[PATCH v2 9/9] tty: serial: fsl_lpuart: Add support for RS-485

2016-06-28 Thread Bhuvanchandra DV
Enable Vybrid's build-in support for RS-485 auto RTS for controlling line
direction of RS-485 transceiver driver.

Enable RS485 feature by either using ioctrl 'TIOCSRS485' or enable it in the
device tree by setting 'linux,rs485-enabled-at-boot-time' property.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 78 +
 1 file changed, 72 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 134090a..4d1fca4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -917,6 +917,52 @@ static void lpuart_dma_rx_free(struct uart_port *port)
sport->dma_rx_cookie = -EINVAL;
 }
 
+static int lpuart_config_rs485(struct uart_port *port,
+   struct serial_rs485 *rs485)
+{
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+
+   u8 modem = readb(sport->port.membase + UARTMODEM) &
+   ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
+   writeb(modem, sport->port.membase + UARTMODEM);
+
+   if (rs485->flags & SER_RS485_ENABLED) {
+   /* Enable auto RS-485 RTS mode */
+   modem |= UARTMODEM_TXRTSE;
+
+   /*
+* RTS needs to be logic HIGH either during transer _or_ after
+* transfer, other variants are not supported by the hardware.
+*/
+
+   if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
+   SER_RS485_RTS_AFTER_SEND)))
+   rs485->flags |= SER_RS485_RTS_ON_SEND;
+
+   if (rs485->flags & SER_RS485_RTS_ON_SEND &&
+   rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+
+   /*
+* The hardware defaults to RTS logic HIGH while transfer.
+* Switch polarity in case RTS shall be logic HIGH
+* after transfer.
+* Note: UART is assumed to be active high.
+*/
+   if (rs485->flags & SER_RS485_RTS_ON_SEND)
+   modem &= ~UARTMODEM_TXRTSPOL;
+   else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   modem |= UARTMODEM_TXRTSPOL;
+   }
+
+   /* Store the new configuration */
+   sport->port.rs485 = *rs485;
+
+   writeb(modem, sport->port.membase + UARTMODEM);
+   return 0;
+}
+
 static unsigned int lpuart_get_mctrl(struct uart_port *port)
 {
unsigned int temp = 0;
@@ -950,17 +996,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port 
*port)
 static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
unsigned char temp;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
 
-   temp = readb(port->membase + UARTMODEM) &
+   /* Make sure RXRTSE bit is not set when RS485 is enabled */
+   if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) {
+   temp = readb(sport->port.membase + UARTMODEM) &
~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
 
-   if (mctrl & TIOCM_RTS)
-   temp |= UARTMODEM_RXRTSE;
+   if (mctrl & TIOCM_RTS)
+   temp |= UARTMODEM_RXRTSE;
 
-   if (mctrl & TIOCM_CTS)
-   temp |= UARTMODEM_TXCTSE;
+   if (mctrl & TIOCM_CTS)
+   temp |= UARTMODEM_TXCTSE;
 
-   writeb(temp, port->membase + UARTMODEM);
+   writeb(temp, port->membase + UARTMODEM);
+   }
 }
 
 static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1256,6 +1307,13 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
cr1 |= UARTCR1_M;
}
 
+   /*
+* When auto RS-485 RTS mode is enabled,
+* hardware flow control need to be disabled.
+*/
+   if (sport->port.rs485.flags & SER_RS485_ENABLED)
+   termios->c_cflag &= ~CRTSCTS;
+
if (termios->c_cflag & CRTSCTS) {
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
} else {
@@ -1864,6 +1922,8 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.ops = _pops;
sport->port.flags = UPF_BOOT_AUTOCONF;
 
+   sport->port.rs485_config = lpuart_config_rs485;
+
sport->clk = devm_clk_get(>dev, "ipg");
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
@@ -1904,6 +1964,12 @@ static int lpuart_probe(struct platform_device *pdev)
dev_info(sport->port.dev, "DMA rx channel request

[PATCH v2 9/9] tty: serial: fsl_lpuart: Add support for RS-485

2016-06-28 Thread Bhuvanchandra DV
Enable Vybrid's build-in support for RS-485 auto RTS for controlling line
direction of RS-485 transceiver driver.

Enable RS485 feature by either using ioctrl 'TIOCSRS485' or enable it in the
device tree by setting 'linux,rs485-enabled-at-boot-time' property.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 78 +
 1 file changed, 72 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 134090a..4d1fca4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -917,6 +917,52 @@ static void lpuart_dma_rx_free(struct uart_port *port)
sport->dma_rx_cookie = -EINVAL;
 }
 
+static int lpuart_config_rs485(struct uart_port *port,
+   struct serial_rs485 *rs485)
+{
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+
+   u8 modem = readb(sport->port.membase + UARTMODEM) &
+   ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
+   writeb(modem, sport->port.membase + UARTMODEM);
+
+   if (rs485->flags & SER_RS485_ENABLED) {
+   /* Enable auto RS-485 RTS mode */
+   modem |= UARTMODEM_TXRTSE;
+
+   /*
+* RTS needs to be logic HIGH either during transer _or_ after
+* transfer, other variants are not supported by the hardware.
+*/
+
+   if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
+   SER_RS485_RTS_AFTER_SEND)))
+   rs485->flags |= SER_RS485_RTS_ON_SEND;
+
+   if (rs485->flags & SER_RS485_RTS_ON_SEND &&
+   rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+
+   /*
+* The hardware defaults to RTS logic HIGH while transfer.
+* Switch polarity in case RTS shall be logic HIGH
+* after transfer.
+* Note: UART is assumed to be active high.
+*/
+   if (rs485->flags & SER_RS485_RTS_ON_SEND)
+   modem &= ~UARTMODEM_TXRTSPOL;
+   else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   modem |= UARTMODEM_TXRTSPOL;
+   }
+
+   /* Store the new configuration */
+   sport->port.rs485 = *rs485;
+
+   writeb(modem, sport->port.membase + UARTMODEM);
+   return 0;
+}
+
 static unsigned int lpuart_get_mctrl(struct uart_port *port)
 {
unsigned int temp = 0;
@@ -950,17 +996,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port 
*port)
 static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
unsigned char temp;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
 
-   temp = readb(port->membase + UARTMODEM) &
+   /* Make sure RXRTSE bit is not set when RS485 is enabled */
+   if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) {
+   temp = readb(sport->port.membase + UARTMODEM) &
~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
 
-   if (mctrl & TIOCM_RTS)
-   temp |= UARTMODEM_RXRTSE;
+   if (mctrl & TIOCM_RTS)
+   temp |= UARTMODEM_RXRTSE;
 
-   if (mctrl & TIOCM_CTS)
-   temp |= UARTMODEM_TXCTSE;
+   if (mctrl & TIOCM_CTS)
+   temp |= UARTMODEM_TXCTSE;
 
-   writeb(temp, port->membase + UARTMODEM);
+   writeb(temp, port->membase + UARTMODEM);
+   }
 }
 
 static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1256,6 +1307,13 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
cr1 |= UARTCR1_M;
}
 
+   /*
+* When auto RS-485 RTS mode is enabled,
+* hardware flow control need to be disabled.
+*/
+   if (sport->port.rs485.flags & SER_RS485_ENABLED)
+   termios->c_cflag &= ~CRTSCTS;
+
if (termios->c_cflag & CRTSCTS) {
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
} else {
@@ -1864,6 +1922,8 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.ops = _pops;
sport->port.flags = UPF_BOOT_AUTOCONF;
 
+   sport->port.rs485_config = lpuart_config_rs485;
+
sport->clk = devm_clk_get(>dev, "ipg");
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
@@ -1904,6 +1964,12 @@ static int lpuart_probe(struct platform_device *pdev)
dev_info(sport->port.dev, "DMA rx channel request failed, "
&q

[PATCH v2 3/9] tty: serial: fsl_lpuart: support suspend/resume

2016-06-28 Thread Bhuvanchandra DV
From: Stefan Agner <ste...@agner.ch>

Add suspend/resume support.

Signed-off-by: Stefan Agner <ste...@agner.ch>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 16 ++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index fabfa7e..75a2098 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -483,9 +483,8 @@ static void lpuart_dma_rx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static void lpuart_timer_func(unsigned long data)
+static void lpuart_dma_rx_terminate(struct lpuart_port *sport)
 {
-   struct lpuart_port *sport = (struct lpuart_port *)data;
struct tty_port *port = >port.state->port;
struct dma_tx_state state;
unsigned long flags;
@@ -510,6 +509,11 @@ static void lpuart_timer_func(unsigned long data)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
+static void lpuart_timer_func(unsigned long data)
+{
+   lpuart_dma_rx_terminate((struct lpuart_port *)data);
+}
+
 static inline void lpuart_prepare_rx(struct lpuart_port *sport)
 {
unsigned long flags;
@@ -1925,7 +1929,12 @@ static int lpuart_suspend(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
 
+   if (sport->dma_rx_in_progress)
+   lpuart_dma_rx_terminate(sport);
+
uart_suspend_port(_reg, >port);
+   if (sport->port.suspended && !sport->port.irq_wake)
+   clk_disable_unprepare(sport->clk);
 
return 0;
 }
@@ -1935,6 +1944,9 @@ static int lpuart_resume(struct device *dev)
struct lpuart_port *sport = dev_get_drvdata(dev);
unsigned long temp;
 
+   if (sport->port.suspended && !sport->port.irq_wake)
+   clk_prepare_enable(sport->clk);
+
if (sport->lpuart32) {
lpuart32_setup_watermark(sport);
temp = lpuart32_read(sport->port.membase + UARTCTRL);
-- 
2.9.0



[PATCH v2 3/9] tty: serial: fsl_lpuart: support suspend/resume

2016-06-28 Thread Bhuvanchandra DV
From: Stefan Agner 

Add suspend/resume support.

Signed-off-by: Stefan Agner 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 16 ++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index fabfa7e..75a2098 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -483,9 +483,8 @@ static void lpuart_dma_rx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static void lpuart_timer_func(unsigned long data)
+static void lpuart_dma_rx_terminate(struct lpuart_port *sport)
 {
-   struct lpuart_port *sport = (struct lpuart_port *)data;
struct tty_port *port = >port.state->port;
struct dma_tx_state state;
unsigned long flags;
@@ -510,6 +509,11 @@ static void lpuart_timer_func(unsigned long data)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
+static void lpuart_timer_func(unsigned long data)
+{
+   lpuart_dma_rx_terminate((struct lpuart_port *)data);
+}
+
 static inline void lpuart_prepare_rx(struct lpuart_port *sport)
 {
unsigned long flags;
@@ -1925,7 +1929,12 @@ static int lpuart_suspend(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
 
+   if (sport->dma_rx_in_progress)
+   lpuart_dma_rx_terminate(sport);
+
uart_suspend_port(_reg, >port);
+   if (sport->port.suspended && !sport->port.irq_wake)
+   clk_disable_unprepare(sport->clk);
 
return 0;
 }
@@ -1935,6 +1944,9 @@ static int lpuart_resume(struct device *dev)
struct lpuart_port *sport = dev_get_drvdata(dev);
unsigned long temp;
 
+   if (sport->port.suspended && !sport->port.irq_wake)
+   clk_prepare_enable(sport->clk);
+
if (sport->lpuart32) {
lpuart32_setup_watermark(sport);
temp = lpuart32_read(sport->port.membase + UARTCTRL);
-- 
2.9.0



[PATCH v2 8/9] tty: serial: fsl-lpuart: Update suspend/resume for DMA mode

2016-06-28 Thread Bhuvanchandra DV
When DMA mode is enabled one need to make sure the DMA channels are idle before
entering suspend mode especially when UART ports which are set as wakeup source
and console port with no_console_suspend is set. This patch takes care of
gracefully releasing DMA channels for the above two cases and start the DMA at
resume.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 44 +
 1 file changed, 44 insertions(+)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 27687d5..134090a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1943,6 +1943,30 @@ static int lpuart_suspend(struct device *dev)
}
 
uart_suspend_port(_reg, >port);
+
+   if (sport->lpuart_dma_rx_use) {
+   /*
+* EDMA driver during suspend will forcefully release any
+* non-idle DMA channels. If port wakeup is enabled or if port
+* is console port or 'no_console_suspend' is set the Rx DMA
+* cannot resume as as expected, hence gracefully release the
+* Rx DMA path before suspend and start Rx DMA path on resume.
+*/
+   if (sport->port.irq_wake) {
+   del_timer_sync(>lpuart_timer);
+   lpuart_dma_rx_free(>port);
+   }
+
+   /* Disable Rx DMA to use UART port as wakeup source */
+   writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS,
+   sport->port.membase + UARTCR5);
+   }
+
+   if (sport->lpuart_dma_tx_use) {
+   sport->dma_tx_in_progress = false;
+   dmaengine_terminate_all(sport->dma_tx_chan);
+   }
+
if (sport->port.suspended && !sport->port.irq_wake)
clk_disable_unprepare(sport->clk);
 
@@ -1970,6 +1994,26 @@ static int lpuart_resume(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
 
+   if (sport->lpuart_dma_rx_use) {
+   if (sport->port.irq_wake) {
+   if (!lpuart_start_rx_dma(sport)) {
+   sport->lpuart_dma_rx_use = true;
+   rx_dma_timer_init(sport);
+   } else {
+   sport->lpuart_dma_rx_use = false;
+   }
+   }
+   }
+
+   if (sport->dma_tx_chan && !lpuart_dma_tx_request(>port)) {
+   init_waitqueue_head(>dma_wait);
+   sport->lpuart_dma_tx_use = true;
+   writeb(readb(sport->port.membase + UARTCR5) |
+   UARTCR5_TDMAS, sport->port.membase + UARTCR5);
+   } else {
+   sport->lpuart_dma_tx_use = false;
+   }
+
uart_resume_port(_reg, >port);
 
return 0;
-- 
2.9.0



[PATCH v2 8/9] tty: serial: fsl-lpuart: Update suspend/resume for DMA mode

2016-06-28 Thread Bhuvanchandra DV
When DMA mode is enabled one need to make sure the DMA channels are idle before
entering suspend mode especially when UART ports which are set as wakeup source
and console port with no_console_suspend is set. This patch takes care of
gracefully releasing DMA channels for the above two cases and start the DMA at
resume.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 44 +
 1 file changed, 44 insertions(+)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 27687d5..134090a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1943,6 +1943,30 @@ static int lpuart_suspend(struct device *dev)
}
 
uart_suspend_port(_reg, >port);
+
+   if (sport->lpuart_dma_rx_use) {
+   /*
+* EDMA driver during suspend will forcefully release any
+* non-idle DMA channels. If port wakeup is enabled or if port
+* is console port or 'no_console_suspend' is set the Rx DMA
+* cannot resume as as expected, hence gracefully release the
+* Rx DMA path before suspend and start Rx DMA path on resume.
+*/
+   if (sport->port.irq_wake) {
+   del_timer_sync(>lpuart_timer);
+   lpuart_dma_rx_free(>port);
+   }
+
+   /* Disable Rx DMA to use UART port as wakeup source */
+   writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS,
+   sport->port.membase + UARTCR5);
+   }
+
+   if (sport->lpuart_dma_tx_use) {
+   sport->dma_tx_in_progress = false;
+   dmaengine_terminate_all(sport->dma_tx_chan);
+   }
+
if (sport->port.suspended && !sport->port.irq_wake)
clk_disable_unprepare(sport->clk);
 
@@ -1970,6 +1994,26 @@ static int lpuart_resume(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
 
+   if (sport->lpuart_dma_rx_use) {
+   if (sport->port.irq_wake) {
+   if (!lpuart_start_rx_dma(sport)) {
+   sport->lpuart_dma_rx_use = true;
+   rx_dma_timer_init(sport);
+   } else {
+   sport->lpuart_dma_rx_use = false;
+   }
+   }
+   }
+
+   if (sport->dma_tx_chan && !lpuart_dma_tx_request(>port)) {
+   init_waitqueue_head(>dma_wait);
+   sport->lpuart_dma_tx_use = true;
+   writeb(readb(sport->port.membase + UARTCR5) |
+   UARTCR5_TDMAS, sport->port.membase + UARTCR5);
+   } else {
+   sport->lpuart_dma_tx_use = false;
+   }
+
uart_resume_port(_reg, >port);
 
return 0;
-- 
2.9.0



[PATCH v2 0/9] Improve Rx/Tx DMA implementation

2016-06-28 Thread Bhuvanchandra DV
Changes since v1:
Split suspend/resume patch.

Bhuvanchandra DV (5):
  tty: serial: fsl_lpuart: Fix broken 8m/s1 support
  tty: serial: fsl-lpuart: Use cyclic DMA for Rx
  tty: serial: fsl-lpuart: Use scatter/gather DMA for Tx
  tty: serial: fsl-lpuart: Update suspend/resume for DMA mode
  tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (4):
  tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
  clk: imx: vf610: Disable automatic clock gating for lpuart in LPSTOP mode
  tty: serial: fsl_lpuart: support suspend/resume
  tty: serial: fsl_lpuart: fix clearing of receive flag

 drivers/clk/imx/clk-vf610.c |  12 +-
 drivers/tty/serial/fsl_lpuart.c | 810 +++-
 2 files changed, 482 insertions(+), 340 deletions(-)

-- 
2.9.0



[PATCH v2 0/9] Improve Rx/Tx DMA implementation

2016-06-28 Thread Bhuvanchandra DV
Changes since v1:
Split suspend/resume patch.

Bhuvanchandra DV (5):
  tty: serial: fsl_lpuart: Fix broken 8m/s1 support
  tty: serial: fsl-lpuart: Use cyclic DMA for Rx
  tty: serial: fsl-lpuart: Use scatter/gather DMA for Tx
  tty: serial: fsl-lpuart: Update suspend/resume for DMA mode
  tty: serial: fsl_lpuart: Add support for RS-485

Stefan Agner (4):
  tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty
  clk: imx: vf610: Disable automatic clock gating for lpuart in LPSTOP mode
  tty: serial: fsl_lpuart: support suspend/resume
  tty: serial: fsl_lpuart: fix clearing of receive flag

 drivers/clk/imx/clk-vf610.c |  12 +-
 drivers/tty/serial/fsl_lpuart.c | 810 +++-
 2 files changed, 482 insertions(+), 340 deletions(-)

-- 
2.9.0



[PATCH v2 5/9] tty: serial: fsl_lpuart: Fix broken 8m/s1 support

2016-06-28 Thread Bhuvanchandra DV
By default the driver always configure the mode as 8s1 even when 8m1 mode is
selected. Fix this by adding support to control the space/mark bit.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 9 +++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 97c1fda..615f191 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1220,13 +1220,14 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
unsigned long flags;
-   unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+   unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem;
unsigned int  baud;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned int sbr, brfa;
 
cr1 = old_cr1 = readb(sport->port.membase + UARTCR1);
old_cr2 = readb(sport->port.membase + UARTCR2);
+   cr3 = readb(sport->port.membase + UARTCR3);
cr4 = readb(sport->port.membase + UARTCR4);
bdh = readb(sport->port.membase + UARTBDH);
modem = readb(sport->port.membase + UARTMODEM);
@@ -1274,7 +1275,10 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
if ((termios->c_cflag & PARENB)) {
if (termios->c_cflag & CMSPAR) {
cr1 &= ~UARTCR1_PE;
-   cr1 |= UARTCR1_M;
+   if (termios->c_cflag & PARODD)
+   cr3 |= UARTCR3_T8;
+   else
+   cr3 &= ~UARTCR3_T8;
} else {
cr1 |= UARTCR1_PE;
if ((termios->c_cflag & CSIZE) == CS8)
@@ -1342,6 +1346,7 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
writeb(cr4 | brfa, sport->port.membase + UARTCR4);
writeb(bdh, sport->port.membase + UARTBDH);
writeb(sbr & 0xFF, sport->port.membase + UARTBDL);
+   writeb(cr3, sport->port.membase + UARTCR3);
writeb(cr1, sport->port.membase + UARTCR1);
writeb(modem, sport->port.membase + UARTMODEM);
 
-- 
2.9.0



[PATCH v2 5/9] tty: serial: fsl_lpuart: Fix broken 8m/s1 support

2016-06-28 Thread Bhuvanchandra DV
By default the driver always configure the mode as 8s1 even when 8m1 mode is
selected. Fix this by adding support to control the space/mark bit.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 9 +++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 97c1fda..615f191 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1220,13 +1220,14 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
unsigned long flags;
-   unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+   unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem;
unsigned int  baud;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned int sbr, brfa;
 
cr1 = old_cr1 = readb(sport->port.membase + UARTCR1);
old_cr2 = readb(sport->port.membase + UARTCR2);
+   cr3 = readb(sport->port.membase + UARTCR3);
cr4 = readb(sport->port.membase + UARTCR4);
bdh = readb(sport->port.membase + UARTBDH);
modem = readb(sport->port.membase + UARTMODEM);
@@ -1274,7 +1275,10 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
if ((termios->c_cflag & PARENB)) {
if (termios->c_cflag & CMSPAR) {
cr1 &= ~UARTCR1_PE;
-   cr1 |= UARTCR1_M;
+   if (termios->c_cflag & PARODD)
+   cr3 |= UARTCR3_T8;
+   else
+   cr3 &= ~UARTCR3_T8;
} else {
cr1 |= UARTCR1_PE;
if ((termios->c_cflag & CSIZE) == CS8)
@@ -1342,6 +1346,7 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
writeb(cr4 | brfa, sport->port.membase + UARTCR4);
writeb(bdh, sport->port.membase + UARTBDH);
writeb(sbr & 0xFF, sport->port.membase + UARTBDL);
+   writeb(cr3, sport->port.membase + UARTCR3);
writeb(cr1, sport->port.membase + UARTCR1);
writeb(modem, sport->port.membase + UARTMODEM);
 
-- 
2.9.0



[PATCH v2 2/9] clk: imx: vf610: Disable automatic clock gating for lpuart in LPSTOP mode

2016-06-28 Thread Bhuvanchandra DV
From: Stefan Agner <ste...@agner.ch>

In order to allow wake support in STOP sleep mode, clocks are needed. Use
imx_clk_gate2_cgr to disable automatic clock gating in low power mode STOP.
This allows to enable wake by UART using:
echo enabled > /sys/class/tty/ttyLP0/power/wakeup

However, if wake is not enabled, the driver should disable the clocks explicitly
to save power.

Signed-off-by: Stefan Agner <ste...@agner.ch>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/clk/imx/clk-vf610.c | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 3a1f244..0476353 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -315,12 +315,12 @@ static void __init vf610_clocks_init(struct device_node 
*ccm_node)
 
clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, 
CCM_CCGRx_CGn(7));
 
-   clk[VF610_CLK_UART0] = imx_clk_gate2("uart0", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(7));
-   clk[VF610_CLK_UART1] = imx_clk_gate2("uart1", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(8));
-   clk[VF610_CLK_UART2] = imx_clk_gate2("uart2", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(9));
-   clk[VF610_CLK_UART3] = imx_clk_gate2("uart3", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(10));
-   clk[VF610_CLK_UART4] = imx_clk_gate2("uart4", "ipg_bus", CCM_CCGR6, 
CCM_CCGRx_CGn(9));
-   clk[VF610_CLK_UART5] = imx_clk_gate2("uart5", "ipg_bus", CCM_CCGR6, 
CCM_CCGRx_CGn(10));
+   clk[VF610_CLK_UART0] = imx_clk_gate2_cgr("uart0", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(7), 0x2);
+   clk[VF610_CLK_UART1] = imx_clk_gate2_cgr("uart1", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(8), 0x2);
+   clk[VF610_CLK_UART2] = imx_clk_gate2_cgr("uart2", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(9), 0x2);
+   clk[VF610_CLK_UART3] = imx_clk_gate2_cgr("uart3", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(10), 0x2);
+   clk[VF610_CLK_UART4] = imx_clk_gate2_cgr("uart4", "ipg_bus", CCM_CCGR6, 
CCM_CCGRx_CGn(9), 0x2);
+   clk[VF610_CLK_UART5] = imx_clk_gate2_cgr("uart5", "ipg_bus", CCM_CCGR6, 
CCM_CCGRx_CGn(10), 0x2);
 
clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, 
CCM_CCGRx_CGn(6));
clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, 
CCM_CCGRx_CGn(7));
-- 
2.9.0



[PATCH v2 2/9] clk: imx: vf610: Disable automatic clock gating for lpuart in LPSTOP mode

2016-06-28 Thread Bhuvanchandra DV
From: Stefan Agner 

In order to allow wake support in STOP sleep mode, clocks are needed. Use
imx_clk_gate2_cgr to disable automatic clock gating in low power mode STOP.
This allows to enable wake by UART using:
echo enabled > /sys/class/tty/ttyLP0/power/wakeup

However, if wake is not enabled, the driver should disable the clocks explicitly
to save power.

Signed-off-by: Stefan Agner 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/clk/imx/clk-vf610.c | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 3a1f244..0476353 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -315,12 +315,12 @@ static void __init vf610_clocks_init(struct device_node 
*ccm_node)
 
clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, 
CCM_CCGRx_CGn(7));
 
-   clk[VF610_CLK_UART0] = imx_clk_gate2("uart0", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(7));
-   clk[VF610_CLK_UART1] = imx_clk_gate2("uart1", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(8));
-   clk[VF610_CLK_UART2] = imx_clk_gate2("uart2", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(9));
-   clk[VF610_CLK_UART3] = imx_clk_gate2("uart3", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(10));
-   clk[VF610_CLK_UART4] = imx_clk_gate2("uart4", "ipg_bus", CCM_CCGR6, 
CCM_CCGRx_CGn(9));
-   clk[VF610_CLK_UART5] = imx_clk_gate2("uart5", "ipg_bus", CCM_CCGR6, 
CCM_CCGRx_CGn(10));
+   clk[VF610_CLK_UART0] = imx_clk_gate2_cgr("uart0", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(7), 0x2);
+   clk[VF610_CLK_UART1] = imx_clk_gate2_cgr("uart1", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(8), 0x2);
+   clk[VF610_CLK_UART2] = imx_clk_gate2_cgr("uart2", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(9), 0x2);
+   clk[VF610_CLK_UART3] = imx_clk_gate2_cgr("uart3", "ipg_bus", CCM_CCGR0, 
CCM_CCGRx_CGn(10), 0x2);
+   clk[VF610_CLK_UART4] = imx_clk_gate2_cgr("uart4", "ipg_bus", CCM_CCGR6, 
CCM_CCGRx_CGn(9), 0x2);
+   clk[VF610_CLK_UART5] = imx_clk_gate2_cgr("uart5", "ipg_bus", CCM_CCGR6, 
CCM_CCGRx_CGn(10), 0x2);
 
clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, 
CCM_CCGRx_CGn(6));
clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, 
CCM_CCGRx_CGn(7));
-- 
2.9.0



[PATCH v2 6/9] tty: serial: fsl-lpuart: Use cyclic DMA for Rx

2016-06-28 Thread Bhuvanchandra DV
The initial approach of DMA implementatin for RX is inefficient due to switching
from PIO to DMA, this leads to overruns especially on instances with the smaller
FIFO. To address these issues this patch uses a cyclic DMA for receiver path.

Some part of the code is borrowed from atmel serial driver.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 483 +---
 1 file changed, 258 insertions(+), 225 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 615f191..51d2b5a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -224,7 +224,8 @@
 #define UARTWATER_TXWATER_OFF  0
 #define UARTWATER_RXWATER_OFF  16
 
-#define FSL_UART_RX_DMA_BUFFER_SIZE64
+/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
+#define DMA_RX_TIMEOUT (10)
 
 #define DRIVER_NAME"fsl-lpuart"
 #define DEV_NAME   "ttyLP"
@@ -244,17 +245,17 @@ struct lpuart_port {
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
dma_addr_t  dma_tx_buf_bus;
-   dma_addr_t  dma_rx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
unsigned char   *dma_tx_buf_virt;
-   unsigned char   *dma_rx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
int dma_tx_in_progress;
-   int dma_rx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
+   struct scatterlist  rx_sgl;
+   struct circ_buf rx_ring;
+   int rx_dma_rng_buf_len;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -270,7 +271,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
 /* Forward declare this for the dma callbacks*/
 static void lpuart_dma_tx_complete(void *arg);
-static void lpuart_dma_rx_complete(void *arg);
 
 static u32 lpuart32_read(void __iomem *addr)
 {
@@ -316,32 +316,6 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
-   struct tty_port *tty, int count)
-{
-   int copied;
-
-   sport->port.icount.rx += count;
-
-   if (!tty) {
-   dev_err(sport->port.dev, "No tty port\n");
-   return;
-   }
-
-   dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-   copied = tty_insert_flip_string(tty,
-   ((unsigned char *)(sport->dma_rx_buf_virt)), count);
-
-   if (copied != count) {
-   WARN_ON(1);
-   dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
-   }
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-}
-
 static void lpuart_pio_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
@@ -433,28 +407,6 @@ static void lpuart_dma_tx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static int lpuart_dma_rx(struct lpuart_port *sport)
-{
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-   sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
-   sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
-   DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
-
-   if (!sport->dma_rx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for rx\n");
-   return -EIO;
-   }
-
-   sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
-   sport->dma_rx_desc->callback_param = sport;
-   sport->dma_rx_in_progress = 1;
-   sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
-   dma_async_issue_pending(sport->dma_rx_chan);
-
-   return 0;
-}
-
 static void lpuart_flush_buffer(struct uart_port *port)
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
@@ -464,73 +416,6 @@ static void lpuart_flush_buffer(struct uart_port *port)
}
 }
 
-static void lpuart_dma_rx_complete(void *arg)
-{
-   struct lpuart_port *sport = arg;
-   struct tty_port *port = >port.state->port;
-   unsigned long flags;
-
-   async_tx_ack(sport->dma_rx_desc);
-   mod_timer(>lpuart_timer, jiffies + sport->dma_rx_timeout);
-
-

[PATCH v2 6/9] tty: serial: fsl-lpuart: Use cyclic DMA for Rx

2016-06-28 Thread Bhuvanchandra DV
The initial approach of DMA implementatin for RX is inefficient due to switching
from PIO to DMA, this leads to overruns especially on instances with the smaller
FIFO. To address these issues this patch uses a cyclic DMA for receiver path.

Some part of the code is borrowed from atmel serial driver.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 483 +---
 1 file changed, 258 insertions(+), 225 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 615f191..51d2b5a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -224,7 +224,8 @@
 #define UARTWATER_TXWATER_OFF  0
 #define UARTWATER_RXWATER_OFF  16
 
-#define FSL_UART_RX_DMA_BUFFER_SIZE64
+/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
+#define DMA_RX_TIMEOUT (10)
 
 #define DRIVER_NAME"fsl-lpuart"
 #define DEV_NAME   "ttyLP"
@@ -244,17 +245,17 @@ struct lpuart_port {
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
dma_addr_t  dma_tx_buf_bus;
-   dma_addr_t  dma_rx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
unsigned char   *dma_tx_buf_virt;
-   unsigned char   *dma_rx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
int dma_tx_in_progress;
-   int dma_rx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
+   struct scatterlist  rx_sgl;
+   struct circ_buf rx_ring;
+   int rx_dma_rng_buf_len;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -270,7 +271,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
 /* Forward declare this for the dma callbacks*/
 static void lpuart_dma_tx_complete(void *arg);
-static void lpuart_dma_rx_complete(void *arg);
 
 static u32 lpuart32_read(void __iomem *addr)
 {
@@ -316,32 +316,6 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
-   struct tty_port *tty, int count)
-{
-   int copied;
-
-   sport->port.icount.rx += count;
-
-   if (!tty) {
-   dev_err(sport->port.dev, "No tty port\n");
-   return;
-   }
-
-   dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-   copied = tty_insert_flip_string(tty,
-   ((unsigned char *)(sport->dma_rx_buf_virt)), count);
-
-   if (copied != count) {
-   WARN_ON(1);
-   dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
-   }
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-}
-
 static void lpuart_pio_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
@@ -433,28 +407,6 @@ static void lpuart_dma_tx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static int lpuart_dma_rx(struct lpuart_port *sport)
-{
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-   sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
-   sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
-   DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
-
-   if (!sport->dma_rx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for rx\n");
-   return -EIO;
-   }
-
-   sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
-   sport->dma_rx_desc->callback_param = sport;
-   sport->dma_rx_in_progress = 1;
-   sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
-   dma_async_issue_pending(sport->dma_rx_chan);
-
-   return 0;
-}
-
 static void lpuart_flush_buffer(struct uart_port *port)
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
@@ -464,73 +416,6 @@ static void lpuart_flush_buffer(struct uart_port *port)
}
 }
 
-static void lpuart_dma_rx_complete(void *arg)
-{
-   struct lpuart_port *sport = arg;
-   struct tty_port *port = >port.state->port;
-   unsigned long flags;
-
-   async_tx_ack(sport->dma_rx_desc);
-   mod_timer(>lpuart_timer, jiffies + sport->dma_rx_timeout);
-
-   spin_lock_irqsave(>port.lock, fl

[PATCH v2 4/9] tty: serial: fsl_lpuart: fix clearing of receive flag

2016-06-27 Thread Bhuvanchandra DV
From: Stefan Agner <ste...@agner.ch>

Commit 8e4934c6d6c6 ("tty: serial: fsl_lpuart: clear receive flag on FIFO
flush") implemented clearing of the receive flag by reading the status register
only. It turned out that even though we flush the FIFO afterwards, a explicit
read of the data register is still required.

This leads to a FIFO underrun. To avoid this, follow the advice in the overrun
"Operation section": Unconditionally clear RXUF after using RXFLUSH.

Signed-off-by: Stefan Agner <ste...@agner.ch>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 75a2098..97c1fda 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -935,13 +935,16 @@ static void lpuart_setup_watermark(struct lpuart_port 
*sport)
writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
sport->port.membase + UARTPFIFO);
 
-   /* explicitly clear RDRF */
-   readb(sport->port.membase + UARTSR1);
-
/* flush Tx and Rx FIFO */
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
 
+   /* explicitly clear RDRF */
+   if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) {
+   readb(sport->port.membase + UARTDR);
+   writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
+   }
+
writeb(0, sport->port.membase + UARTTWFIFO);
writeb(1, sport->port.membase + UARTRWFIFO);
 
-- 
2.9.0



[PATCH v2 4/9] tty: serial: fsl_lpuart: fix clearing of receive flag

2016-06-27 Thread Bhuvanchandra DV
From: Stefan Agner 

Commit 8e4934c6d6c6 ("tty: serial: fsl_lpuart: clear receive flag on FIFO
flush") implemented clearing of the receive flag by reading the status register
only. It turned out that even though we flush the FIFO afterwards, a explicit
read of the data register is still required.

This leads to a FIFO underrun. To avoid this, follow the advice in the overrun
"Operation section": Unconditionally clear RXUF after using RXFLUSH.

Signed-off-by: Stefan Agner 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 75a2098..97c1fda 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -935,13 +935,16 @@ static void lpuart_setup_watermark(struct lpuart_port 
*sport)
writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
sport->port.membase + UARTPFIFO);
 
-   /* explicitly clear RDRF */
-   readb(sport->port.membase + UARTSR1);
-
/* flush Tx and Rx FIFO */
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
 
+   /* explicitly clear RDRF */
+   if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) {
+   readb(sport->port.membase + UARTDR);
+   writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
+   }
+
writeb(0, sport->port.membase + UARTTWFIFO);
writeb(1, sport->port.membase + UARTRWFIFO);
 
-- 
2.9.0



[PATCH v2 1/9] tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty

2016-06-27 Thread Bhuvanchandra DV
From: Stefan Agner <ste...@agner.ch>

Currently the tx_empty callback only considers the Transmit Complete Flag (TC).
The reference manual is not quite clear if the TC flag covers the TX FIFO too.
Debug prints on real hardware have shown that from time to time the TC flag is
asserted (indicating Transmitter idle) while there are still data in the
TX FIFO. Hence, in this case the serial core will call the shutdown callback
even though there are data remaining in the TX FIFO buffers.

Avoid early shutdowns by considering the TX FIFO empty flag too. Also avoid
theoretical race conditions between DMA and the driver by checking whether the
TX DMA is in progress too.

Signed-off-by: Stefan Agner <ste...@agner.ch>
Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 14 --
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3d79003..fabfa7e 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -810,8 +810,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
 /* return TIOCSER_TEMT when transmitter is not busy */
 static unsigned int lpuart_tx_empty(struct uart_port *port)
 {
-   return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
-   TIOCSER_TEMT : 0;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+   unsigned char sr1 = readb(port->membase + UARTSR1);
+   unsigned char sfifo = readb(port->membase + UARTSFIFO);
+
+   if (sport->dma_tx_in_progress)
+   return 0;
+
+   if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT)
+   return TIOCSER_TEMT;
+
+   return 0;
 }
 
 static unsigned int lpuart32_tx_empty(struct uart_port *port)
-- 
2.9.0



[PATCH v2 1/9] tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty

2016-06-27 Thread Bhuvanchandra DV
From: Stefan Agner 

Currently the tx_empty callback only considers the Transmit Complete Flag (TC).
The reference manual is not quite clear if the TC flag covers the TX FIFO too.
Debug prints on real hardware have shown that from time to time the TC flag is
asserted (indicating Transmitter idle) while there are still data in the
TX FIFO. Hence, in this case the serial core will call the shutdown callback
even though there are data remaining in the TX FIFO buffers.

Avoid early shutdowns by considering the TX FIFO empty flag too. Also avoid
theoretical race conditions between DMA and the driver by checking whether the
TX DMA is in progress too.

Signed-off-by: Stefan Agner 
Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 14 --
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3d79003..fabfa7e 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -810,8 +810,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
 /* return TIOCSER_TEMT when transmitter is not busy */
 static unsigned int lpuart_tx_empty(struct uart_port *port)
 {
-   return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
-   TIOCSER_TEMT : 0;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+   unsigned char sr1 = readb(port->membase + UARTSR1);
+   unsigned char sfifo = readb(port->membase + UARTSFIFO);
+
+   if (sport->dma_tx_in_progress)
+   return 0;
+
+   if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT)
+   return TIOCSER_TEMT;
+
+   return 0;
 }
 
 static unsigned int lpuart32_tx_empty(struct uart_port *port)
-- 
2.9.0



Re: [PATCH 1/8] tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty

2016-06-27 Thread Bhuvanchandra DV

On 06/26/16 02:56, Greg KH wrote:


On Thu, Jun 09, 2016 at 08:40:32PM +0530, Bhuvanchandra DV wrote:

From: Stefan Agner <ste...@agner.ch>

Currently the tx_empty callback only considers the Transmit Complete
Flag (TC). The reference manual is not quite clear if the TC flag
covers the TX FIFO too. Debug prints on real hardware have shown that
from time to time the TC flag is asserted (indicating Transmitter
idle) while there are still data in the TX FIFO. Hence, in this case
the serial core will call the shutdown callback even though there are
data remaining in the TX FIFO buffers.

Avoid early shutdowns by considering the TX FIFO empty flag too. Also
avoid theoretical race conditions between DMA and the driver by
checking whether the TX DMA is in progress too.

Signed-off-by: Stefan Agner <ste...@agner.ch>
---
  drivers/tty/serial/fsl_lpuart.c | 14 --
  1 file changed, 12 insertions(+), 2 deletions(-)

Why are you not signing off on patches that are flowing through you?
Please fix this up and resend the series, after breaking up the clock
change as asked.


Okay



thanks,

greg k-h




Re: [PATCH 1/8] tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty

2016-06-27 Thread Bhuvanchandra DV

On 06/26/16 02:56, Greg KH wrote:


On Thu, Jun 09, 2016 at 08:40:32PM +0530, Bhuvanchandra DV wrote:

From: Stefan Agner 

Currently the tx_empty callback only considers the Transmit Complete
Flag (TC). The reference manual is not quite clear if the TC flag
covers the TX FIFO too. Debug prints on real hardware have shown that
from time to time the TC flag is asserted (indicating Transmitter
idle) while there are still data in the TX FIFO. Hence, in this case
the serial core will call the shutdown callback even though there are
data remaining in the TX FIFO buffers.

Avoid early shutdowns by considering the TX FIFO empty flag too. Also
avoid theoretical race conditions between DMA and the driver by
checking whether the TX DMA is in progress too.

Signed-off-by: Stefan Agner 
---
  drivers/tty/serial/fsl_lpuart.c | 14 --
  1 file changed, 12 insertions(+), 2 deletions(-)

Why are you not signing off on patches that are flowing through you?
Please fix this up and resend the series, after breaking up the clock
change as asked.


Okay



thanks,

greg k-h




[PATCH 6/8] tty: serial: fsl-lpuart: Use scatter/gather DMA for Tx Drop PIO to DMA switching and use scatter/gather DMA for Tx path to improve performance.

2016-06-09 Thread Bhuvanchandra DV
Some part of the code is borrowed from imx serial driver.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 257 ++--
 1 file changed, 113 insertions(+), 144 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 51d2b5a..27687d5 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -244,18 +244,18 @@ struct lpuart_port {
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
-   dma_addr_t  dma_tx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
-   unsigned char   *dma_tx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
-   int dma_tx_in_progress;
+   booldma_tx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
-   struct scatterlist  rx_sgl;
+   struct scatterlist  rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
+   unsigned intdma_tx_nents;
+   wait_queue_head_t   dma_wait;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -316,103 +316,118 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_pio_tx(struct lpuart_port *sport)
+static void lpuart_dma_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
-   unsigned long flags;
-
-   spin_lock_irqsave(>port.lock, flags);
+   struct scatterlist *sgl = sport->tx_sgl;
+   struct device *dev = sport->port.dev;
+   int ret;
 
-   while (!uart_circ_empty(xmit) &&
-   readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
-   writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
-   xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-   sport->port.icount.tx++;
-   }
+   if (sport->dma_tx_in_progress)
+   return;
 
-   if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-   uart_write_wakeup(>port);
+   sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
 
-   if (uart_circ_empty(xmit))
-   writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
-   sport->port.membase + UARTCR5);
+   if (xmit->tail < xmit->head) {
+   sport->dma_tx_nents = 1;
+   sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
+   } else {
+   sport->dma_tx_nents = 2;
+   sg_init_table(sgl, 2);
+   sg_set_buf(sgl, xmit->buf + xmit->tail,
+   UART_XMIT_SIZE - xmit->tail);
+   sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+   }
 
-   spin_unlock_irqrestore(>port.lock, flags);
-}
+   ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   if (!ret) {
+   dev_err(dev, "DMA mapping error for TX.\n");
+   return;
+   }
 
-static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
-{
-   struct circ_buf *xmit = >port.state->xmit;
-   dma_addr_t tx_bus_addr;
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
-   UART_XMIT_SIZE, DMA_TO_DEVICE);
-   sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1);
-   tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
-   sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
-   tx_bus_addr, sport->dma_tx_bytes,
+   sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
+   sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
-
if (!sport->dma_tx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for tx\n");
-   return -EIO;
+   dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   dev_err(dev, "Cannot prepare TX slave DMA!\n");
+   return;
}
 
sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
sport->dma_tx_desc->callback_param = sport;
-   sport->dma_tx_in_progress = 1;
+   sport->dma_tx_in_progress = true;
sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);

[PATCH 6/8] tty: serial: fsl-lpuart: Use scatter/gather DMA for Tx Drop PIO to DMA switching and use scatter/gather DMA for Tx path to improve performance.

2016-06-09 Thread Bhuvanchandra DV
Some part of the code is borrowed from imx serial driver.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 257 ++--
 1 file changed, 113 insertions(+), 144 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 51d2b5a..27687d5 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -244,18 +244,18 @@ struct lpuart_port {
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
-   dma_addr_t  dma_tx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
-   unsigned char   *dma_tx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
-   int dma_tx_in_progress;
+   booldma_tx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
-   struct scatterlist  rx_sgl;
+   struct scatterlist  rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
+   unsigned intdma_tx_nents;
+   wait_queue_head_t   dma_wait;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -316,103 +316,118 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_pio_tx(struct lpuart_port *sport)
+static void lpuart_dma_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
-   unsigned long flags;
-
-   spin_lock_irqsave(>port.lock, flags);
+   struct scatterlist *sgl = sport->tx_sgl;
+   struct device *dev = sport->port.dev;
+   int ret;
 
-   while (!uart_circ_empty(xmit) &&
-   readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
-   writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
-   xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-   sport->port.icount.tx++;
-   }
+   if (sport->dma_tx_in_progress)
+   return;
 
-   if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-   uart_write_wakeup(>port);
+   sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
 
-   if (uart_circ_empty(xmit))
-   writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
-   sport->port.membase + UARTCR5);
+   if (xmit->tail < xmit->head) {
+   sport->dma_tx_nents = 1;
+   sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
+   } else {
+   sport->dma_tx_nents = 2;
+   sg_init_table(sgl, 2);
+   sg_set_buf(sgl, xmit->buf + xmit->tail,
+   UART_XMIT_SIZE - xmit->tail);
+   sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+   }
 
-   spin_unlock_irqrestore(>port.lock, flags);
-}
+   ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   if (!ret) {
+   dev_err(dev, "DMA mapping error for TX.\n");
+   return;
+   }
 
-static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
-{
-   struct circ_buf *xmit = >port.state->xmit;
-   dma_addr_t tx_bus_addr;
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
-   UART_XMIT_SIZE, DMA_TO_DEVICE);
-   sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1);
-   tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
-   sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
-   tx_bus_addr, sport->dma_tx_bytes,
+   sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
+   sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
-
if (!sport->dma_tx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for tx\n");
-   return -EIO;
+   dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+   dev_err(dev, "Cannot prepare TX slave DMA!\n");
+   return;
}
 
sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
sport->dma_tx_desc->callback_param = sport;
-   sport->dma_tx_in_progress = 1;
+   sport->dma_tx_in_progress = true;
sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
dma_as

[PATCH 1/8] tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty

2016-06-09 Thread Bhuvanchandra DV
From: Stefan Agner 

Currently the tx_empty callback only considers the Transmit Complete
Flag (TC). The reference manual is not quite clear if the TC flag
covers the TX FIFO too. Debug prints on real hardware have shown that
from time to time the TC flag is asserted (indicating Transmitter
idle) while there are still data in the TX FIFO. Hence, in this case
the serial core will call the shutdown callback even though there are
data remaining in the TX FIFO buffers.

Avoid early shutdowns by considering the TX FIFO empty flag too. Also
avoid theoretical race conditions between DMA and the driver by
checking whether the TX DMA is in progress too.

Signed-off-by: Stefan Agner 
---
 drivers/tty/serial/fsl_lpuart.c | 14 --
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3d79003..fabfa7e 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -810,8 +810,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
 /* return TIOCSER_TEMT when transmitter is not busy */
 static unsigned int lpuart_tx_empty(struct uart_port *port)
 {
-   return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
-   TIOCSER_TEMT : 0;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+   unsigned char sr1 = readb(port->membase + UARTSR1);
+   unsigned char sfifo = readb(port->membase + UARTSFIFO);
+
+   if (sport->dma_tx_in_progress)
+   return 0;
+
+   if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT)
+   return TIOCSER_TEMT;
+
+   return 0;
 }
 
 static unsigned int lpuart32_tx_empty(struct uart_port *port)
-- 
2.8.3



[PATCH 8/8] tty: serial: fsl_lpuart: Add support for RS-485

2016-06-09 Thread Bhuvanchandra DV
Enable Vybrid's build-in support for RS-485 auto RTS for controlling
line direction of RS-485 transceiver driver.

Enable RS485 feature by either using ioctrl 'TIOCSRS485' or enable it in
the device tree by setting 'linux,rs485-enabled-at-boot-time' property.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 78 +
 1 file changed, 72 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 134090a..4d1fca4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -917,6 +917,52 @@ static void lpuart_dma_rx_free(struct uart_port *port)
sport->dma_rx_cookie = -EINVAL;
 }
 
+static int lpuart_config_rs485(struct uart_port *port,
+   struct serial_rs485 *rs485)
+{
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+
+   u8 modem = readb(sport->port.membase + UARTMODEM) &
+   ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
+   writeb(modem, sport->port.membase + UARTMODEM);
+
+   if (rs485->flags & SER_RS485_ENABLED) {
+   /* Enable auto RS-485 RTS mode */
+   modem |= UARTMODEM_TXRTSE;
+
+   /*
+* RTS needs to be logic HIGH either during transer _or_ after
+* transfer, other variants are not supported by the hardware.
+*/
+
+   if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
+   SER_RS485_RTS_AFTER_SEND)))
+   rs485->flags |= SER_RS485_RTS_ON_SEND;
+
+   if (rs485->flags & SER_RS485_RTS_ON_SEND &&
+   rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+
+   /*
+* The hardware defaults to RTS logic HIGH while transfer.
+* Switch polarity in case RTS shall be logic HIGH
+* after transfer.
+* Note: UART is assumed to be active high.
+*/
+   if (rs485->flags & SER_RS485_RTS_ON_SEND)
+   modem &= ~UARTMODEM_TXRTSPOL;
+   else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   modem |= UARTMODEM_TXRTSPOL;
+   }
+
+   /* Store the new configuration */
+   sport->port.rs485 = *rs485;
+
+   writeb(modem, sport->port.membase + UARTMODEM);
+   return 0;
+}
+
 static unsigned int lpuart_get_mctrl(struct uart_port *port)
 {
unsigned int temp = 0;
@@ -950,17 +996,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port 
*port)
 static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
unsigned char temp;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
 
-   temp = readb(port->membase + UARTMODEM) &
+   /* Make sure RXRTSE bit is not set when RS485 is enabled */
+   if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) {
+   temp = readb(sport->port.membase + UARTMODEM) &
~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
 
-   if (mctrl & TIOCM_RTS)
-   temp |= UARTMODEM_RXRTSE;
+   if (mctrl & TIOCM_RTS)
+   temp |= UARTMODEM_RXRTSE;
 
-   if (mctrl & TIOCM_CTS)
-   temp |= UARTMODEM_TXCTSE;
+   if (mctrl & TIOCM_CTS)
+   temp |= UARTMODEM_TXCTSE;
 
-   writeb(temp, port->membase + UARTMODEM);
+   writeb(temp, port->membase + UARTMODEM);
+   }
 }
 
 static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1256,6 +1307,13 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
cr1 |= UARTCR1_M;
}
 
+   /*
+* When auto RS-485 RTS mode is enabled,
+* hardware flow control need to be disabled.
+*/
+   if (sport->port.rs485.flags & SER_RS485_ENABLED)
+   termios->c_cflag &= ~CRTSCTS;
+
if (termios->c_cflag & CRTSCTS) {
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
} else {
@@ -1864,6 +1922,8 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.ops = _pops;
sport->port.flags = UPF_BOOT_AUTOCONF;
 
+   sport->port.rs485_config = lpuart_config_rs485;
+
sport->clk = devm_clk_get(>dev, "ipg");
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
@@ -1904,6 +1964,12 @@ static int lpuart_probe(struct platform_device *pdev)
dev_info(sport->port.dev, "DMA rx channel request

[PATCH 1/8] tty: serial: fsl_lpuart: consider TX FIFO too in tx_empty

2016-06-09 Thread Bhuvanchandra DV
From: Stefan Agner 

Currently the tx_empty callback only considers the Transmit Complete
Flag (TC). The reference manual is not quite clear if the TC flag
covers the TX FIFO too. Debug prints on real hardware have shown that
from time to time the TC flag is asserted (indicating Transmitter
idle) while there are still data in the TX FIFO. Hence, in this case
the serial core will call the shutdown callback even though there are
data remaining in the TX FIFO buffers.

Avoid early shutdowns by considering the TX FIFO empty flag too. Also
avoid theoretical race conditions between DMA and the driver by
checking whether the TX DMA is in progress too.

Signed-off-by: Stefan Agner 
---
 drivers/tty/serial/fsl_lpuart.c | 14 --
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3d79003..fabfa7e 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -810,8 +810,18 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
 /* return TIOCSER_TEMT when transmitter is not busy */
 static unsigned int lpuart_tx_empty(struct uart_port *port)
 {
-   return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
-   TIOCSER_TEMT : 0;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+   unsigned char sr1 = readb(port->membase + UARTSR1);
+   unsigned char sfifo = readb(port->membase + UARTSFIFO);
+
+   if (sport->dma_tx_in_progress)
+   return 0;
+
+   if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT)
+   return TIOCSER_TEMT;
+
+   return 0;
 }
 
 static unsigned int lpuart32_tx_empty(struct uart_port *port)
-- 
2.8.3



[PATCH 8/8] tty: serial: fsl_lpuart: Add support for RS-485

2016-06-09 Thread Bhuvanchandra DV
Enable Vybrid's build-in support for RS-485 auto RTS for controlling
line direction of RS-485 transceiver driver.

Enable RS485 feature by either using ioctrl 'TIOCSRS485' or enable it in
the device tree by setting 'linux,rs485-enabled-at-boot-time' property.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 78 +
 1 file changed, 72 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 134090a..4d1fca4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -917,6 +917,52 @@ static void lpuart_dma_rx_free(struct uart_port *port)
sport->dma_rx_cookie = -EINVAL;
 }
 
+static int lpuart_config_rs485(struct uart_port *port,
+   struct serial_rs485 *rs485)
+{
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
+
+   u8 modem = readb(sport->port.membase + UARTMODEM) &
+   ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
+   writeb(modem, sport->port.membase + UARTMODEM);
+
+   if (rs485->flags & SER_RS485_ENABLED) {
+   /* Enable auto RS-485 RTS mode */
+   modem |= UARTMODEM_TXRTSE;
+
+   /*
+* RTS needs to be logic HIGH either during transer _or_ after
+* transfer, other variants are not supported by the hardware.
+*/
+
+   if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
+   SER_RS485_RTS_AFTER_SEND)))
+   rs485->flags |= SER_RS485_RTS_ON_SEND;
+
+   if (rs485->flags & SER_RS485_RTS_ON_SEND &&
+   rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+
+   /*
+* The hardware defaults to RTS logic HIGH while transfer.
+* Switch polarity in case RTS shall be logic HIGH
+* after transfer.
+* Note: UART is assumed to be active high.
+*/
+   if (rs485->flags & SER_RS485_RTS_ON_SEND)
+   modem &= ~UARTMODEM_TXRTSPOL;
+   else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+   modem |= UARTMODEM_TXRTSPOL;
+   }
+
+   /* Store the new configuration */
+   sport->port.rs485 = *rs485;
+
+   writeb(modem, sport->port.membase + UARTMODEM);
+   return 0;
+}
+
 static unsigned int lpuart_get_mctrl(struct uart_port *port)
 {
unsigned int temp = 0;
@@ -950,17 +996,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port 
*port)
 static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
unsigned char temp;
+   struct lpuart_port *sport = container_of(port,
+   struct lpuart_port, port);
 
-   temp = readb(port->membase + UARTMODEM) &
+   /* Make sure RXRTSE bit is not set when RS485 is enabled */
+   if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) {
+   temp = readb(sport->port.membase + UARTMODEM) &
~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
 
-   if (mctrl & TIOCM_RTS)
-   temp |= UARTMODEM_RXRTSE;
+   if (mctrl & TIOCM_RTS)
+   temp |= UARTMODEM_RXRTSE;
 
-   if (mctrl & TIOCM_CTS)
-   temp |= UARTMODEM_TXCTSE;
+   if (mctrl & TIOCM_CTS)
+   temp |= UARTMODEM_TXCTSE;
 
-   writeb(temp, port->membase + UARTMODEM);
+   writeb(temp, port->membase + UARTMODEM);
+   }
 }
 
 static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1256,6 +1307,13 @@ lpuart_set_termios(struct uart_port *port, struct 
ktermios *termios,
cr1 |= UARTCR1_M;
}
 
+   /*
+* When auto RS-485 RTS mode is enabled,
+* hardware flow control need to be disabled.
+*/
+   if (sport->port.rs485.flags & SER_RS485_ENABLED)
+   termios->c_cflag &= ~CRTSCTS;
+
if (termios->c_cflag & CRTSCTS) {
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
} else {
@@ -1864,6 +1922,8 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.ops = _pops;
sport->port.flags = UPF_BOOT_AUTOCONF;
 
+   sport->port.rs485_config = lpuart_config_rs485;
+
sport->clk = devm_clk_get(>dev, "ipg");
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
@@ -1904,6 +1964,12 @@ static int lpuart_probe(struct platform_device *pdev)
dev_info(sport->port.dev, "DMA rx channel request failed, "
&q

[PATCH 5/8] tty: serial: fsl-lpuart: Use cyclic DMA for Rx

2016-06-09 Thread Bhuvanchandra DV
The initial approach of DMA implementatin for RX is inefficient due to switching
from PIO to DMA, this leads to overruns especially on instances with the smaller
FIFO. To address these issues this patch uses a cyclic DMA for receiver path.

Some part of the code is borrowed from atmel serial driver.

Signed-off-by: Bhuvanchandra DV <bhuvanchandra...@toradex.com>
---
 drivers/tty/serial/fsl_lpuart.c | 483 +---
 1 file changed, 258 insertions(+), 225 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 615f191..51d2b5a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -224,7 +224,8 @@
 #define UARTWATER_TXWATER_OFF  0
 #define UARTWATER_RXWATER_OFF  16
 
-#define FSL_UART_RX_DMA_BUFFER_SIZE64
+/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
+#define DMA_RX_TIMEOUT (10)
 
 #define DRIVER_NAME"fsl-lpuart"
 #define DEV_NAME   "ttyLP"
@@ -244,17 +245,17 @@ struct lpuart_port {
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
dma_addr_t  dma_tx_buf_bus;
-   dma_addr_t  dma_rx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
unsigned char   *dma_tx_buf_virt;
-   unsigned char   *dma_rx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
int dma_tx_in_progress;
-   int dma_rx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
+   struct scatterlist  rx_sgl;
+   struct circ_buf rx_ring;
+   int rx_dma_rng_buf_len;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -270,7 +271,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
 /* Forward declare this for the dma callbacks*/
 static void lpuart_dma_tx_complete(void *arg);
-static void lpuart_dma_rx_complete(void *arg);
 
 static u32 lpuart32_read(void __iomem *addr)
 {
@@ -316,32 +316,6 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
-   struct tty_port *tty, int count)
-{
-   int copied;
-
-   sport->port.icount.rx += count;
-
-   if (!tty) {
-   dev_err(sport->port.dev, "No tty port\n");
-   return;
-   }
-
-   dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-   copied = tty_insert_flip_string(tty,
-   ((unsigned char *)(sport->dma_rx_buf_virt)), count);
-
-   if (copied != count) {
-   WARN_ON(1);
-   dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
-   }
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-}
-
 static void lpuart_pio_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
@@ -433,28 +407,6 @@ static void lpuart_dma_tx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static int lpuart_dma_rx(struct lpuart_port *sport)
-{
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-   sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
-   sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
-   DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
-
-   if (!sport->dma_rx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for rx\n");
-   return -EIO;
-   }
-
-   sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
-   sport->dma_rx_desc->callback_param = sport;
-   sport->dma_rx_in_progress = 1;
-   sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
-   dma_async_issue_pending(sport->dma_rx_chan);
-
-   return 0;
-}
-
 static void lpuart_flush_buffer(struct uart_port *port)
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
@@ -464,73 +416,6 @@ static void lpuart_flush_buffer(struct uart_port *port)
}
 }
 
-static void lpuart_dma_rx_complete(void *arg)
-{
-   struct lpuart_port *sport = arg;
-   struct tty_port *port = >port.state->port;
-   unsigned long flags;
-
-   async_tx_ack(sport->dma_rx_desc);
-   mod_timer(>lpuart_timer, jiffies + sport->dma_rx_timeout);
-
-

[PATCH 5/8] tty: serial: fsl-lpuart: Use cyclic DMA for Rx

2016-06-09 Thread Bhuvanchandra DV
The initial approach of DMA implementatin for RX is inefficient due to switching
from PIO to DMA, this leads to overruns especially on instances with the smaller
FIFO. To address these issues this patch uses a cyclic DMA for receiver path.

Some part of the code is borrowed from atmel serial driver.

Signed-off-by: Bhuvanchandra DV 
---
 drivers/tty/serial/fsl_lpuart.c | 483 +---
 1 file changed, 258 insertions(+), 225 deletions(-)

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 615f191..51d2b5a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -224,7 +224,8 @@
 #define UARTWATER_TXWATER_OFF  0
 #define UARTWATER_RXWATER_OFF  16
 
-#define FSL_UART_RX_DMA_BUFFER_SIZE64
+/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
+#define DMA_RX_TIMEOUT (10)
 
 #define DRIVER_NAME"fsl-lpuart"
 #define DEV_NAME   "ttyLP"
@@ -244,17 +245,17 @@ struct lpuart_port {
struct dma_async_tx_descriptor  *dma_tx_desc;
struct dma_async_tx_descriptor  *dma_rx_desc;
dma_addr_t  dma_tx_buf_bus;
-   dma_addr_t  dma_rx_buf_bus;
dma_cookie_tdma_tx_cookie;
dma_cookie_tdma_rx_cookie;
unsigned char   *dma_tx_buf_virt;
-   unsigned char   *dma_rx_buf_virt;
unsigned intdma_tx_bytes;
unsigned intdma_rx_bytes;
int dma_tx_in_progress;
-   int dma_rx_in_progress;
unsigned intdma_rx_timeout;
struct timer_list   lpuart_timer;
+   struct scatterlist  rx_sgl;
+   struct circ_buf rx_ring;
+   int rx_dma_rng_buf_len;
 };
 
 static const struct of_device_id lpuart_dt_ids[] = {
@@ -270,7 +271,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
 /* Forward declare this for the dma callbacks*/
 static void lpuart_dma_tx_complete(void *arg);
-static void lpuart_dma_rx_complete(void *arg);
 
 static u32 lpuart32_read(void __iomem *addr)
 {
@@ -316,32 +316,6 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
 }
 
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
-   struct tty_port *tty, int count)
-{
-   int copied;
-
-   sport->port.icount.rx += count;
-
-   if (!tty) {
-   dev_err(sport->port.dev, "No tty port\n");
-   return;
-   }
-
-   dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-   copied = tty_insert_flip_string(tty,
-   ((unsigned char *)(sport->dma_rx_buf_virt)), count);
-
-   if (copied != count) {
-   WARN_ON(1);
-   dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
-   }
-
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-}
-
 static void lpuart_pio_tx(struct lpuart_port *sport)
 {
struct circ_buf *xmit = >port.state->xmit;
@@ -433,28 +407,6 @@ static void lpuart_dma_tx_complete(void *arg)
spin_unlock_irqrestore(>port.lock, flags);
 }
 
-static int lpuart_dma_rx(struct lpuart_port *sport)
-{
-   dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
-   FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-   sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
-   sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
-   DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
-
-   if (!sport->dma_rx_desc) {
-   dev_err(sport->port.dev, "Not able to get desc for rx\n");
-   return -EIO;
-   }
-
-   sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
-   sport->dma_rx_desc->callback_param = sport;
-   sport->dma_rx_in_progress = 1;
-   sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
-   dma_async_issue_pending(sport->dma_rx_chan);
-
-   return 0;
-}
-
 static void lpuart_flush_buffer(struct uart_port *port)
 {
struct lpuart_port *sport = container_of(port, struct lpuart_port, 
port);
@@ -464,73 +416,6 @@ static void lpuart_flush_buffer(struct uart_port *port)
}
 }
 
-static void lpuart_dma_rx_complete(void *arg)
-{
-   struct lpuart_port *sport = arg;
-   struct tty_port *port = >port.state->port;
-   unsigned long flags;
-
-   async_tx_ack(sport->dma_rx_desc);
-   mod_timer(>lpuart_timer, jiffies + sport->dma_rx_timeout);
-
-   spin_lock_irqsave(>port.lock, fl

  1   2   3   >