Linux Serial subsystem development
 help / color / mirror / Atom feed
* [PATCH] serial: imx: restore handshaking irq for imx1
From: Uwe Kleine-König @ 2018-09-20 12:11 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: YueHaibing, linux-serial, kernel, kernel-janitors, linux-kernel,
	Leonard Crestez, Andy Duan
In-Reply-To: <20180920071957.xnwni4lqb5kgncfc@pengutronix.de>

Back in 2015 when irda was dropped from the driver imx1 was broken. This
change reintroduces the support for the third interrupt of the UART.

Fixes: afe9cbb1a6ad ("serial: imx: drop support for IRDA")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 drivers/tty/serial/imx.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 4e853570ea80..554a69db1bca 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -2350,6 +2350,14 @@ static int imx_uart_probe(struct platform_device *pdev)
 				ret);
 			return ret;
 		}
+
+		ret = devm_request_irq(&pdev->dev, rtsirq, imx_uart_rtsint, 0,
+				       dev_name(&pdev->dev), sport);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to request rts irq: %d\n",
+				ret);
+			return ret;
+		}
 	} else {
 		ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,
 				       dev_name(&pdev->dev), sport);
-- 
2.18.0

^ permalink raw reply related

* Re: [PATCH -next] serial: imx: remove set but not used variable 'rtsirq'
From: YueHaibing @ 2018-09-20 12:04 UTC (permalink / raw)
  To: Andy Duan, Leonard Crestez, u.kleine-koenig@pengutronix.de,
	jslaby@suse.cz
  Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org,
	A.s. Dong, Abel Vesa, Anson Huang, Bough Chen, Cosmin Samoila,
	Daniel Baluta, Han Xu, Jacky Bai, Jun Li, Leo Zhang, Peng Fan,
	Peter Chen, Ranjani Vaidyanathan, Robert Chiras, Robin Gong,
	Shenwei Wang, Viorel Suman, Zening Wang, gregkh
In-Reply-To: <VI1PR0402MB3600A7368D538E3B6D044C5FFF130@VI1PR0402MB3600.eurprd04.prod.outlook.com>

On 2018/9/20 17:41, Andy Duan wrote:
> From: Leonard Crestez Sent: 2018年9月20日 16:51
>> On Thu, 2018-09-20 at 08:45 +0200, Jiri Slaby wrote:
>>> On 09/20/2018, 03:58 AM, YueHaibing wrote:
>>>> Fixes gcc '-Wunused-but-set-variable' warning:
>>>>
>>>> drivers/tty/serial/imx.c: In function 'imx_uart_probe':
>>>> drivers/tty/serial/imx.c:2198:20: warning:
>>>>  variable 'rtsirq' set but not used [-Wunused-but-set-variable]
>>>>
>>>> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c @@
>>>> -2220,7 +2220,6 @@ static int imx_uart_probe(struct
>> platform_device
>>>> *pdev)
>>>>
>>>>  	rxirq = platform_get_irq(pdev, 0);
>>>>  	txirq = platform_get_irq(pdev, 1);
>>>> -	rtsirq = platform_get_irq(pdev, 2);
>>>
>>> I am not sure this is correct. platform_get_irq has side effects (like
>>> enabling the IRQ). Are you sure this won't change the behaviour (this
>>> is question mostly to IMX fellows)?
>>
>> As far as I can tell there was a request_irq call for rtsirq which was
>> removed by mistake in commit afe9cbb1a6ad ("serial: imx: drop support
>> for IRDA"):
>>
>> -               /* do not use RTS IRQ on IrDA */
>> -               if (!USE_IRDA(sport)) {
>> -                       ret = devm_request_irq(&pdev->dev, rtsirq,
>> -                                              imx_rtsint, 0,
>> -
>> dev_name(&pdev->dev), sport);
>> -                       if (ret)
>> -                               return ret;
>> -               }
>>
>> This should have just removed the IRDA check and request rtsirq
>> unconditionally. Nobody noticed this by testing RTS on imx1, this is an old
>> chip and later variants have a single combined irq.
>>
>> The correct fix for the warning would be to restore that request_irq.
>>
>> --
>> Regards,
>> Leonard
> 
> Yes, your explain is very correct! Thanks for your comment.
> We should restore rtsirq request that for i.MX1.

Ok, I will post a new fix patch as Leonard and suggested.

> 
> Regards,
> Andy Duan
> 

^ permalink raw reply

* Re: [PATCH v3 2/2] serial: uartps: Change uart ID port allocation
From: Michal Simek @ 2018-09-20 11:43 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Michal Simek
  Cc: linux-kernel, monstr, Jiri Slaby, linux-serial, linux-arm-kernel
In-Reply-To: <20180920113225.GA19956@kroah.com>

On 20.9.2018 13:32, Greg Kroah-Hartman wrote:
> On Tue, Sep 18, 2018 at 04:57:59PM +0200, Michal Simek wrote:
>> For IPs which have alias algorightm all the time using that alias and
>> minor number. It means serial20 alias ends up as ttyPS20.
>>
>> If alias is not setup for probed IP instance the first unused position is
>> used but that needs to be checked if it is really empty because another
>> instance doesn't need to be probed at that time. of_alias_get_alias_list()
>> fills alias bitmap which exactly shows which ID is free.
>> If alias pointing to different not compatible IP, it is free to use.
>>
>> cdns_get_id() call is placed below structure allocation to simplify
>> error path.
>>
>> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>> ---
>>
>> Changes in v3: None
>> Changes in v2:
>> - Add handle of return value from of_alias_get_alias_list
>>
>> Needs to be applied on the top of
>> https://lkml.org/lkml/2018/9/3/404
>> https://lkml.org/lkml/2018/9/3/400 (minor that's why just in case)
>>
>> Greg: Please apply it with
>> "of: base: Introduce of_alias_get_alias_list() to check alias IDs
>> Rob is ok with that.
> 
> I don't have that patch, can you resend the whole series?

done.
M

^ permalink raw reply

* [RESEND PATCH v3 2/2] serial: uartps: Change uart ID port allocation
From: Michal Simek @ 2018-09-20 11:41 UTC (permalink / raw)
  To: linux-kernel, monstr, gregkh; +Cc: Jiri Slaby, linux-serial, linux-arm-kernel
In-Reply-To: <b1772d99d4e8b788448cde0fc5c7dccb1f63b507.1537443710.git.michal.simek@xilinx.com>

For IPs which have alias algorightm all the time using that alias and
minor number. It means serial20 alias ends up as ttyPS20.

If alias is not setup for probed IP instance the first unused position is
used but that needs to be checked if it is really empty because another
instance doesn't need to be probed at that time. of_alias_get_alias_list()
fills alias bitmap which exactly shows which ID is free.
If alias pointing to different not compatible IP, it is free to use.

cdns_get_id() call is placed below structure allocation to simplify
error path.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

Changes in v3: None
Changes in v2:
- Add handle of return value from of_alias_get_alias_list

Needs to be applied on the top of
https://lkml.org/lkml/2018/9/3/404
https://lkml.org/lkml/2018/9/3/400 (minor that's why just in case)

Greg: Please apply it with
"of: base: Introduce of_alias_get_alias_list() to check alias IDs
Rob is ok with that.

---
 drivers/tty/serial/xilinx_uartps.c | 109 ++++++++++++++++++++++++++++++++-----
 1 file changed, 96 insertions(+), 13 deletions(-)

diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 71c032744dae..f77200a0f461 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -30,7 +30,6 @@
 #define CDNS_UART_TTY_NAME	"ttyPS"
 #define CDNS_UART_NAME		"xuartps"
 #define CDNS_UART_MAJOR		0	/* use dynamic node allocation */
-#define CDNS_UART_NR_PORTS	2
 #define CDNS_UART_FIFO_SIZE	64	/* FIFO size */
 #define CDNS_UART_REGISTER_SPACE	0x1000
 
@@ -1370,6 +1369,88 @@ static int __maybe_unused cdns_runtime_resume(struct device *dev)
 };
 MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
 
+/*
+ * Maximum number of instances without alias IDs but if there is alias
+ * which target "< MAX_UART_INSTANCES" range this ID can't be used.
+ */
+#define MAX_UART_INSTANCES	32
+
+/* Stores static aliases list */
+static DECLARE_BITMAP(alias_bitmap, MAX_UART_INSTANCES);
+static int alias_bitmap_initialized;
+
+/* Stores actual bitmap of allocated IDs with alias IDs together */
+static DECLARE_BITMAP(bitmap, MAX_UART_INSTANCES);
+/* Protect bitmap operations to have unique IDs */
+static DEFINE_MUTEX(bitmap_lock);
+
+static int cdns_get_id(struct platform_device *pdev)
+{
+	int id, ret;
+
+	mutex_lock(&bitmap_lock);
+
+	/* Alias list is stable that's why get alias bitmap only once */
+	if (!alias_bitmap_initialized) {
+		ret = of_alias_get_alias_list(cdns_uart_of_match, "serial",
+					      alias_bitmap, MAX_UART_INSTANCES);
+		if (ret)
+			return ret;
+
+		alias_bitmap_initialized++;
+	}
+
+	/* Make sure that alias ID is not taken by instance without alias */
+	bitmap_or(bitmap, bitmap, alias_bitmap, MAX_UART_INSTANCES);
+
+	dev_dbg(&pdev->dev, "Alias bitmap: %*pb\n",
+		MAX_UART_INSTANCES, bitmap);
+
+	/* Look for a serialN alias */
+	id = of_alias_get_id(pdev->dev.of_node, "serial");
+	if (id < 0) {
+		dev_warn(&pdev->dev,
+			 "No serial alias passed. Using the first free id\n");
+
+		/*
+		 * Start with id 0 and check if there is no serial0 alias
+		 * which points to device which is compatible with this driver.
+		 * If alias exists then try next free position.
+		 */
+		id = 0;
+
+		for (;;) {
+			dev_info(&pdev->dev, "Checking id %d\n", id);
+			id = find_next_zero_bit(bitmap, MAX_UART_INSTANCES, id);
+
+			/* No free empty instance */
+			if (id == MAX_UART_INSTANCES) {
+				dev_err(&pdev->dev, "No free ID\n");
+				mutex_unlock(&bitmap_lock);
+				return -EINVAL;
+			}
+
+			dev_dbg(&pdev->dev, "The empty id is %d\n", id);
+			/* Check if ID is empty */
+			if (!test_and_set_bit(id, bitmap)) {
+				/* Break the loop if bit is taken */
+				dev_dbg(&pdev->dev,
+					"Selected ID %d allocation passed\n",
+					id);
+				break;
+			}
+			dev_dbg(&pdev->dev,
+				"Selected ID %d allocation failed\n", id);
+			/* if taking bit fails then try next one */
+			id++;
+		}
+	}
+
+	mutex_unlock(&bitmap_lock);
+
+	return id;
+}
+
 /**
  * cdns_uart_probe - Platform driver probe
  * @pdev: Pointer to the platform device structure
@@ -1403,21 +1484,17 @@ static int cdns_uart_probe(struct platform_device *pdev)
 	if (!cdns_uart_uart_driver)
 		return -ENOMEM;
 
-	/* Look for a serialN alias */
-	cdns_uart_data->id = of_alias_get_id(pdev->dev.of_node, "serial");
+	cdns_uart_data->id = cdns_get_id(pdev);
 	if (cdns_uart_data->id < 0)
-		cdns_uart_data->id = 0;
-
-	if (cdns_uart_data->id >= CDNS_UART_NR_PORTS) {
-		dev_err(&pdev->dev, "Cannot get uart_port structure\n");
-		return -ENODEV;
-	}
+		return cdns_uart_data->id;
 
 	/* There is a need to use unique driver name */
 	driver_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%d",
 				     CDNS_UART_NAME, cdns_uart_data->id);
-	if (!driver_name)
-		return -ENOMEM;
+	if (!driver_name) {
+		rc = -ENOMEM;
+		goto err_out_id;
+	}
 
 	cdns_uart_uart_driver->owner = THIS_MODULE;
 	cdns_uart_uart_driver->driver_name = driver_name;
@@ -1446,7 +1523,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
 	rc = uart_register_driver(cdns_uart_uart_driver);
 	if (rc < 0) {
 		dev_err(&pdev->dev, "Failed to register driver\n");
-		return rc;
+		goto err_out_id;
 	}
 
 	cdns_uart_data->cdns_uart_driver = cdns_uart_uart_driver;
@@ -1587,7 +1664,10 @@ static int cdns_uart_probe(struct platform_device *pdev)
 	clk_disable_unprepare(cdns_uart_data->pclk);
 err_out_unregister_driver:
 	uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
-
+err_out_id:
+	mutex_lock(&bitmap_lock);
+	clear_bit(cdns_uart_data->id, bitmap);
+	mutex_unlock(&bitmap_lock);
 	return rc;
 }
 
@@ -1610,6 +1690,9 @@ static int cdns_uart_remove(struct platform_device *pdev)
 #endif
 	rc = uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
 	port->mapbase = 0;
+	mutex_lock(&bitmap_lock);
+	clear_bit(cdns_uart_data->id, bitmap);
+	mutex_unlock(&bitmap_lock);
 	clk_disable_unprepare(cdns_uart_data->uartclk);
 	clk_disable_unprepare(cdns_uart_data->pclk);
 	pm_runtime_disable(&pdev->dev);
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH v3 2/2] serial: uartps: Change uart ID port allocation
From: Greg Kroah-Hartman @ 2018-09-20 11:32 UTC (permalink / raw)
  To: Michal Simek
  Cc: linux-kernel, monstr, Jiri Slaby, linux-serial, linux-arm-kernel
In-Reply-To: <dcf1c72203baf2629492dce765ce2d555316be0c.1537282676.git.michal.simek@xilinx.com>

On Tue, Sep 18, 2018 at 04:57:59PM +0200, Michal Simek wrote:
> For IPs which have alias algorightm all the time using that alias and
> minor number. It means serial20 alias ends up as ttyPS20.
> 
> If alias is not setup for probed IP instance the first unused position is
> used but that needs to be checked if it is really empty because another
> instance doesn't need to be probed at that time. of_alias_get_alias_list()
> fills alias bitmap which exactly shows which ID is free.
> If alias pointing to different not compatible IP, it is free to use.
> 
> cdns_get_id() call is placed below structure allocation to simplify
> error path.
> 
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> ---
> 
> Changes in v3: None
> Changes in v2:
> - Add handle of return value from of_alias_get_alias_list
> 
> Needs to be applied on the top of
> https://lkml.org/lkml/2018/9/3/404
> https://lkml.org/lkml/2018/9/3/400 (minor that's why just in case)
> 
> Greg: Please apply it with
> "of: base: Introduce of_alias_get_alias_list() to check alias IDs
> Rob is ok with that.

I don't have that patch, can you resend the whole series?

thanks,

greg k-h

^ permalink raw reply

* RE: [PATCH v1] tty: serial: imx: enable IDDMAEN for the last tail data
From: Andy Duan @ 2018-09-20 10:54 UTC (permalink / raw)
  To: Lucas Stach, Robin Gong, Uwe Kleine-König
  Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org,
	dl-linux-imx, kernel@pengutronix.de, jslaby@suse.com
In-Reply-To: <1537440125.3362.161.camel@pengutronix.de>

From: Lucas Stach <l.stach@pengutronix.de> Sent: 2018年9月20日 18:42
> Am Donnerstag, den 20.09.2018, 10:33 +0000 schrieb Andy Duan:
> > From: Lucas Stach <l.stach@pengutronix.de> Sent: 2018年9月20日
> 17:40
> > > Am Donnerstag, den 20.09.2018, 08:39 +0000 schrieb Robin Gong:
> > > > > -----Original Message-----
> > > > > From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > > > Sent: 2018年9月20日 15:55
> > > > > To: Robin Gong <yibin.gong@nxp.com>
> > > > > Cc: jslaby@suse.com; Andy Duan <fugang.duan@nxp.com>;
> > > > > linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; dl-
> > > > > linux-imx <linux-imx@nxp.com>; kernel@pengutronix.de
> > > > > Subject: Re: [PATCH v1] tty: serial: imx: enable IDDMAEN for the
> > > > > last tail data
> > > > >
> > > > > Hello,
> > > > >
> > > > > On Thu, Sep 20, 2018 at 11:26:00PM +0800, Robin Gong wrote:
> > > > > > enable IDDMAEN in UCR4 to let sdma script has the chance to
> > > > > > detect the idle status and transfer the last tail data with
> > > > > > the interrupt notifying uart driver.Otherwise, the last dma
> > > > > > done interrupt of the tail data in rxfifo whose size is less
> > > > > > than watermark may never be received by uart driver.
> > > > >
> > > > > Is this a theoretic issue? Or does it fix a real problem?
> > > > >
> > > > > If the former I'd say UCR1_ATDMAEN being set should prevent this
> > > > > stall.
> > > >
> > > > It's a real fix, you will see 'timeout We read 0 byte' with the
> > > > attached stress test running on i.mx7d or i.mx6 if no this patch
> > > > applied.
> > > > ' while [ 1 ]; do /unit_tests/UART/mxc_uart_stress_test.out
> > > > /dev/ttymxc5 2000000 D L 100 100 L; done &'
> > > > Please make sure dma enabled on ttymxcX for your test, and sdma
> > > > firmware setup in your rootfs /lib/firmware/imx/sdma/sdma-
> > > > imx6q.bin
> > >
> > > or
> > > > /lib/firmware/imx/sdma/sdma- imx7d.bin.
> > > > You can get sdma firmware from below:
> > > >
> > >
> > >
> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2F
> > > gi
> > > t
> > > > .kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Ffirmware%2
> Flin
> > >
> > > ux-&amp
> > > > ;data=02%7C01%7Cfugang.duan%40nxp.com%7C213076f6d111414
> 8b5
> > >
> > > ce08d61edcfb
> > > >
> > >
> > >
> 88%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636730331
> > > 814804545&amp
> > > > ;sdata=d0uZMjLc%2FrIae3Wd6PWtX2gEGlvZcUjPBVqKK9550jg%3D&
> amp
> > >
> > > ;reserved=0
> > > > firmware.git/tree/imx/sdma
> > >
> > > Please see commit 905c0decad28402aa166975023fb88c8f62f93c8
> on why
> > > using the idle detect together with with SDMA is wrong. We don't
> > > want to re- introduce the broken behavior.
> > >
> >
> > The patch just want to use idle timer to stop SDMA transfer using the
> > new ram script, whose action is not the same as rom script.
> > Sdma rom script how to stop sdma transfer if only one byte in rx FIFO
> > that trigger SDMA request ?
> 
> Right, and using the idle detect doesn't work with the ROM script as this
> doesn't read the remaining bytes in the FIFO when the idle detect
> condition is hit.
> 
> Instead with the ROM script the aging timer is used to terminate the DMA
> transfer. So if 1 byte is sitting in the FIFO below the watermark level, the
> serial core will trigger a DMA request when the aging time is hit. The ROM
> script checks the aging status and reads the remaining bytes from the FIFO,
> then terminates the transfer. This is all documented in the reference
> manual.
> 
> I fail to see why this shouldn't work with the RAM script. If it doesn't work
> with the RAM firmware, this script is violating the interface between the
> serial core and the script in an incompatible way and needs fixing.
> 
If rom script stop DMA transfer when SDMA is triggered by aging timer,  then idler timer is not needed. And the data copy action should have no problem. I will do stress test with SDMA rom script.
For ram script, there have many changes and different action with rom script. Aging timer trigger cannot stop current SDMA transfer, so use idler timer as the stop condition trigger source. 
We will do more test on rom script, if pass our all test cases, we can align ram script with rom script.
Thanks.

Andy

^ permalink raw reply

* Re: [PATCH v1] tty: serial: imx: enable IDDMAEN for the last tail data
From: Lucas Stach @ 2018-09-20 10:42 UTC (permalink / raw)
  To: Andy Duan, Robin Gong, Uwe Kleine-König
  Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org,
	dl-linux-imx, kernel@pengutronix.de, jslaby@suse.com
In-Reply-To: <VI1PR0402MB36009F89D7E629CE99A7C592FF130@VI1PR0402MB3600.eurprd04.prod.outlook.com>

Am Donnerstag, den 20.09.2018, 10:33 +0000 schrieb Andy Duan:
> From: Lucas Stach <l.stach@pengutronix.de> Sent: 2018年9月20日 17:40
> > Am Donnerstag, den 20.09.2018, 08:39 +0000 schrieb Robin Gong:
> > > > -----Original Message-----
> > > > From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > > Sent: 2018年9月20日 15:55
> > > > To: Robin Gong <yibin.gong@nxp.com>
> > > > Cc: jslaby@suse.com; Andy Duan <fugang.duan@nxp.com>;
> > > > linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; dl-
> > > > linux-imx <linux-imx@nxp.com>; kernel@pengutronix.de
> > > > Subject: Re: [PATCH v1] tty: serial: imx: enable IDDMAEN for
> > > > the
> > > > last tail data
> > > > 
> > > > Hello,
> > > > 
> > > > On Thu, Sep 20, 2018 at 11:26:00PM +0800, Robin Gong wrote:
> > > > > enable IDDMAEN in UCR4 to let sdma script has the chance to
> > > > > detect
> > > > > the idle status and transfer the last tail data with the
> > > > > interrupt
> > > > > notifying uart driver.Otherwise, the last dma done interrupt
> > > > > of
> > > > > the tail data in rxfifo whose size is less than watermark may
> > > > > never be received by uart driver.
> > > > 
> > > > Is this a theoretic issue? Or does it fix a real problem?
> > > > 
> > > > If the former I'd say UCR1_ATDMAEN being set should prevent
> > > > this
> > > > stall.
> > > 
> > > It's a real fix, you will see 'timeout We read 0 byte' with the
> > > attached stress test running on i.mx7d or i.mx6 if no this patch
> > > applied.
> > > ' while [ 1 ]; do /unit_tests/UART/mxc_uart_stress_test.out
> > > /dev/ttymxc5 2000000 D L 100 100 L; done &'
> > > Please make sure dma enabled on ttymxcX for your test, and sdma
> > > firmware setup in your rootfs /lib/firmware/imx/sdma/sdma-
> > > imx6q.bin
> > 
> > or
> > > /lib/firmware/imx/sdma/sdma- imx7d.bin.
> > > You can get sdma firmware from below:
> > > 
> > 
> > https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2F
> > gi
> > t
> > > .kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Ffirmware%2Flin
> > 
> > ux-&amp
> > > ;data=02%7C01%7Cfugang.duan%40nxp.com%7C213076f6d1114148b5
> > 
> > ce08d61edcfb
> > > 
> > 
> > 88%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636730331
> > 814804545&amp
> > > ;sdata=d0uZMjLc%2FrIae3Wd6PWtX2gEGlvZcUjPBVqKK9550jg%3D&amp
> > 
> > ;reserved=0
> > > firmware.git/tree/imx/sdma
> > 
> > Please see commit 905c0decad28402aa166975023fb88c8f62f93c8 on
> > why using the idle detect together with with SDMA is wrong. We
> > don't
> > want to re- introduce the broken behavior.
> > 
> 
> The patch just want to use idle timer to stop SDMA transfer using the
> new ram script, whose action is not the same as rom script.
> Sdma rom script how to stop sdma transfer if only one byte in rx FIFO
> that trigger SDMA request ?

Right, and using the idle detect doesn't work with the ROM script as
this doesn't read the remaining bytes in the FIFO when the idle detect
condition is hit.

Instead with the ROM script the aging timer is used to terminate the
DMA transfer. So if 1 byte is sitting in the FIFO below the watermark
level, the serial core will trigger a DMA request when the aging time
is hit. The ROM script checks the aging status and reads the remaining
bytes from the FIFO, then terminates the transfer. This is all
documented in the reference manual.

I fail to see why this shouldn't work with the RAM script. If it
doesn't work with the RAM firmware, this script is violating the
interface between the serial core and the script in an incompatible way
and needs fixing.

Regards,
Lucas

^ permalink raw reply

* RE: [PATCH v1] tty: serial: imx: enable IDDMAEN for the last tail data
From: Andy Duan @ 2018-09-20 10:33 UTC (permalink / raw)
  To: Lucas Stach, Robin Gong, Uwe Kleine-König
  Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org,
	dl-linux-imx, kernel@pengutronix.de, jslaby@suse.com
In-Reply-To: <1537436376.3362.159.camel@pengutronix.de>

From: Lucas Stach <l.stach@pengutronix.de> Sent: 2018年9月20日 17:40
> Am Donnerstag, den 20.09.2018, 08:39 +0000 schrieb Robin Gong:
> > > -----Original Message-----
> > > From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > Sent: 2018年9月20日 15:55
> > > To: Robin Gong <yibin.gong@nxp.com>
> > > Cc: jslaby@suse.com; Andy Duan <fugang.duan@nxp.com>;
> > > linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; dl-
> > > linux-imx <linux-imx@nxp.com>; kernel@pengutronix.de
> > > Subject: Re: [PATCH v1] tty: serial: imx: enable IDDMAEN for the
> > > last tail data
> > >
> > > Hello,
> > >
> > > On Thu, Sep 20, 2018 at 11:26:00PM +0800, Robin Gong wrote:
> > > > enable IDDMAEN in UCR4 to let sdma script has the chance to detect
> > > > the idle status and transfer the last tail data with the interrupt
> > > > notifying uart driver.Otherwise, the last dma done interrupt of
> > > > the tail data in rxfifo whose size is less than watermark may
> > > > never be received by uart driver.
> > >
> > > Is this a theoretic issue? Or does it fix a real problem?
> > >
> > > If the former I'd say UCR1_ATDMAEN being set should prevent this
> > > stall.
> >
> > It's a real fix, you will see 'timeout We read 0 byte' with the
> > attached stress test running on i.mx7d or i.mx6 if no this patch
> > applied.
> > ' while [ 1 ]; do /unit_tests/UART/mxc_uart_stress_test.out
> > /dev/ttymxc5 2000000 D L 100 100 L; done &'
> > Please make sure dma enabled on ttymxcX for your test, and sdma
> > firmware setup in your rootfs /lib/firmware/imx/sdma/sdma-imx6q.bin
> or
> > /lib/firmware/imx/sdma/sdma- imx7d.bin.
> > You can get sdma firmware from below:
> >
> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgi
> t
> > .kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Ffirmware%2Flin
> ux-&amp
> > ;data=02%7C01%7Cfugang.duan%40nxp.com%7C213076f6d1114148b5
> ce08d61edcfb
> >
> 88%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636730331
> 814804545&amp
> > ;sdata=d0uZMjLc%2FrIae3Wd6PWtX2gEGlvZcUjPBVqKK9550jg%3D&amp
> ;reserved=0
> > firmware.git/tree/imx/sdma
> 
> Please see commit 905c0decad28402aa166975023fb88c8f62f93c8 on
> why using the idle detect together with with SDMA is wrong. We don't
> want to re- introduce the broken behavior.
> 
The patch just want to use idle timer to stop SDMA transfer using the new ram script, whose action is not the same as rom script.
Sdma rom script how to stop sdma transfer if only one byte in rx FIFO that trigger SDMA request ?

^ permalink raw reply

* Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array
From: Marek Szyprowski @ 2018-09-20 10:11 UTC (permalink / raw)
  To: Janusz Krzysztofik, Linus Walleij
  Cc: Jonathan Corbet, Miguel Ojeda Sandonis, Peter Korsgaard,
	Peter Rosin, Ulf Hansson, Andrew Lunn, Florian Fainelli,
	David S. Miller, Dominik Brodowski, Greg Kroah-Hartman,
	Kishon Vijay Abraham I, Lars-Peter Clausen, Michael Hennerich,
	Jonathan Cameron, Hartmut Knaack, Peter Meerwald-Stadler,
	Jiri Slaby, Willy Tarreau, Geert Uytterhoeven
In-Reply-To: <20180902120144.6855-5-jmkrzyszt@gmail.com>

Hi All,

On 2018-09-02 14:01, Janusz Krzysztofik wrote:
> Certain GPIO descriptor arrays returned by gpio_get_array() may contain
> information on direct mapping of array members to pins of a single GPIO
> chip in hardware order.  In such cases, bitmaps of values can be passed
> directly from/to the chip's .get/set_multiple() callbacks without
> wasting time on iterations.
>
> Add respective code to gpiod_get/set_array_bitmap_complex() functions.
> Pins not applicable for fast path are processed as before, skipping
> over the 'fast' ones.
>
> Cc: Jonathan Corbet <corbet@lwn.net>
> Signed-off-by: Janusz Krzysztofik <jmkrzyszt@gmail.com>

I've just noticed that this patch landed in today's linux-next. Sadly it
breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
device-tree source arch/arm/boot/dts/exynos5250-snow.dts).

Booting hangs after detecting MMC cards. Reverting this patch fixes the
boot. I will try later to add some debugs and investigate it further what
really happens when booting hangs.

> ---
>   Documentation/driver-api/gpio/board.rst    | 15 ++++++
>   Documentation/driver-api/gpio/consumer.rst |  8 +++
>   drivers/gpio/gpiolib.c                     | 87 ++++++++++++++++++++++++++++--
>   3 files changed, 105 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst
> index 2c112553df84..c66821e033c2 100644
> --- a/Documentation/driver-api/gpio/board.rst
> +++ b/Documentation/driver-api/gpio/board.rst
> @@ -193,3 +193,18 @@ And the table can be added to the board code as follows::
>   
>   The line will be hogged as soon as the gpiochip is created or - in case the
>   chip was created earlier - when the hog table is registered.
> +
> +Arrays of pins
> +--------------
> +In addition to requesting pins belonging to a function one by one, a device may
> +also request an array of pins assigned to the function.  The way those pins are
> +mapped to the device determines if the array qualifies for fast bitmap
> +processing.  If yes, a bitmap is passed over get/set array functions directly
> +between a caller and a respective .get/set_multiple() callback of a GPIO chip.
> +
> +In order to qualify for fast bitmap processing, the pin mapping must meet the
> +following requirements:
> +- it must belong to the same chip as other 'fast' pins of the function,
> +- its index within the function must match its hardware number within the chip.
> +
> +Open drain and open source pins are excluded from fast bitmap output processing.
> diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
> index 0afd95a12b10..cf992e5ab976 100644
> --- a/Documentation/driver-api/gpio/consumer.rst
> +++ b/Documentation/driver-api/gpio/consumer.rst
> @@ -388,6 +388,14 @@ array_info should be set to NULL.
>   Note that for optimal performance GPIOs belonging to the same chip should be
>   contiguous within the array of descriptors.
>   
> +Still better performance may be achieved if array indexes of the descriptors
> +match hardware pin numbers of a single chip.  If an array passed to a get/set
> +array function matches the one obtained from gpiod_get_array() and array_info
> +associated with the array is also passed, the function may take a fast bitmap
> +processing path, passing the value_bitmap argument directly to the respective
> +.get/set_multiple() callback of the chip.  That allows for utilization of GPIO
> +banks as data I/O ports without much loss of performance.
> +
>   The return value of gpiod_get_array_value() and its variants is 0 on success
>   or negative on error. Note the difference to gpiod_get_value(), which returns
>   0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index cef6ee31fe05..b9d083fb13ee 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -2789,7 +2789,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
>   				  struct gpio_array *array_info,
>   				  unsigned long *value_bitmap)
>   {
> -	int i = 0;
> +	int err, i = 0;
> +
> +	/*
> +	 * Validate array_info against desc_array and its size.
> +	 * It should immediately follow desc_array if both
> +	 * have been obtained from the same gpiod_get_array() call.
> +	 */
> +	if (array_info && array_info->desc == desc_array &&
> +	    array_size <= array_info->size &&
> +	    (void *)array_info == desc_array + array_info->size) {
> +		if (!can_sleep)
> +			WARN_ON(array_info->chip->can_sleep);
> +
> +		err = gpio_chip_get_multiple(array_info->chip,
> +					     array_info->get_mask,
> +					     value_bitmap);
> +		if (err)
> +			return err;
> +
> +		if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
> +			bitmap_xor(value_bitmap, value_bitmap,
> +				   array_info->invert_mask, array_size);
> +
> +		if (bitmap_full(array_info->get_mask, array_size))
> +			return 0;
> +
> +		i = find_first_zero_bit(array_info->get_mask, array_size);
> +	} else {
> +		array_info = NULL;
> +	}
>   
>   	while (i < array_size) {
>   		struct gpio_chip *chip = desc_array[i]->gdev->chip;
> @@ -2820,7 +2849,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
>   			int hwgpio = gpio_chip_hwgpio(desc);
>   
>   			__set_bit(hwgpio, mask);
> -			i++;
> +
> +			if (array_info)
> +				find_next_zero_bit(array_info->get_mask,
> +						   array_size, i);
> +			else
> +				i++;
>   		} while ((i < array_size) &&
>   			 (desc_array[i]->gdev->chip == chip));
>   
> @@ -2831,7 +2865,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
>   			return ret;
>   		}
>   
> -		for (j = first; j < i; j++) {
> +		for (j = first; j < i; ) {
>   			const struct gpio_desc *desc = desc_array[j];
>   			int hwgpio = gpio_chip_hwgpio(desc);
>   			int value = test_bit(hwgpio, bits);
> @@ -2840,6 +2874,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
>   				value = !value;
>   			__assign_bit(j, value_bitmap, value);
>   			trace_gpio_value(desc_to_gpio(desc), 1, value);
> +
> +			if (array_info)
> +				find_next_zero_bit(array_info->get_mask, i, j);
> +			else
> +				j++;
>   		}
>   
>   		if (mask != fastpath)
> @@ -3041,6 +3080,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
>   {
>   	int i = 0;
>   
> +	/*
> +	 * Validate array_info against desc_array and its size.
> +	 * It should immediately follow desc_array if both
> +	 * have been obtained from the same gpiod_get_array() call.
> +	 */
> +	if (array_info && array_info->desc == desc_array &&
> +	    array_size <= array_info->size &&
> +	    (void *)array_info == desc_array + array_info->size) {
> +		if (!can_sleep)
> +			WARN_ON(array_info->chip->can_sleep);
> +
> +		if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
> +			bitmap_xor(value_bitmap, value_bitmap,
> +				   array_info->invert_mask, array_size);
> +
> +		gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
> +				       value_bitmap);
> +
> +		if (bitmap_full(array_info->set_mask, array_size))
> +			return 0;
> +
> +		i = find_first_zero_bit(array_info->set_mask, array_size);
> +	} else {
> +		array_info = NULL;
> +	}
> +
>   	while (i < array_size) {
>   		struct gpio_chip *chip = desc_array[i]->gdev->chip;
>   		unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
> @@ -3068,7 +3133,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
>   			int hwgpio = gpio_chip_hwgpio(desc);
>   			int value = test_bit(i, value_bitmap);
>   
> -			if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
> +			/*
> +			 * Pins applicable for fast input but not for
> +			 * fast output processing may have been already
> +			 * inverted inside the fast path, skip them.
> +			 */
> +			if (!raw && !(array_info &&
> +			    test_bit(i, array_info->invert_mask)) &&
> +			    test_bit(FLAG_ACTIVE_LOW, &desc->flags))
>   				value = !value;
>   			trace_gpio_value(desc_to_gpio(desc), 0, value);
>   			/*
> @@ -3087,7 +3159,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
>   					__clear_bit(hwgpio, bits);
>   				count++;
>   			}
> -			i++;
> +
> +			if (array_info)
> +				find_next_zero_bit(array_info->set_mask,
> +						   array_size, i);
> +			else
> +				i++;
>   		} while ((i < array_size) &&
>   			 (desc_array[i]->gdev->chip == chip));
>   		/* push collected bits to outputs */
>

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

^ permalink raw reply

* RE: [PATCH -next] serial: imx: remove set but not used variable 'rtsirq'
From: Andy Duan @ 2018-09-20  9:41 UTC (permalink / raw)
  To: yuehaibing@huawei.com, u.kleine-koenig@pengutronix.de,
	jslaby@suse.cz
  Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org,
	A.s. Dong, Abel Vesa, Anson Huang, Bough Chen, Cosmin Samoila,
	Daniel Baluta, Han Xu, Jacky Bai, Jun Li, Leo Zhang,
	Leonard Crestez, Peng Fan, Peter Chen, Ranjani Vaidyanathan,
	Robert Chiras, Robin Gong, Shenwei Wang, Viorel Suman, Zening
In-Reply-To: <96170d620618804d49ad19323d3cdee853070c72.camel@nxp.com>

From: Leonard Crestez Sent: 2018年9月20日 16:51
> On Thu, 2018-09-20 at 08:45 +0200, Jiri Slaby wrote:
> > On 09/20/2018, 03:58 AM, YueHaibing wrote:
> > > Fixes gcc '-Wunused-but-set-variable' warning:
> > >
> > > drivers/tty/serial/imx.c: In function 'imx_uart_probe':
> > > drivers/tty/serial/imx.c:2198:20: warning:
> > >  variable 'rtsirq' set but not used [-Wunused-but-set-variable]
> > >
> > > diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c @@
> > > -2220,7 +2220,6 @@ static int imx_uart_probe(struct
> platform_device
> > > *pdev)
> > >
> > >  	rxirq = platform_get_irq(pdev, 0);
> > >  	txirq = platform_get_irq(pdev, 1);
> > > -	rtsirq = platform_get_irq(pdev, 2);
> >
> > I am not sure this is correct. platform_get_irq has side effects (like
> > enabling the IRQ). Are you sure this won't change the behaviour (this
> > is question mostly to IMX fellows)?
> 
> As far as I can tell there was a request_irq call for rtsirq which was
> removed by mistake in commit afe9cbb1a6ad ("serial: imx: drop support
> for IRDA"):
> 
> -               /* do not use RTS IRQ on IrDA */
> -               if (!USE_IRDA(sport)) {
> -                       ret = devm_request_irq(&pdev->dev, rtsirq,
> -                                              imx_rtsint, 0,
> -
> dev_name(&pdev->dev), sport);
> -                       if (ret)
> -                               return ret;
> -               }
> 
> This should have just removed the IRDA check and request rtsirq
> unconditionally. Nobody noticed this by testing RTS on imx1, this is an old
> chip and later variants have a single combined irq.
> 
> The correct fix for the warning would be to restore that request_irq.
> 
> --
> Regards,
> Leonard

Yes, your explain is very correct! Thanks for your comment.
We should restore rtsirq request that for i.MX1.

Regards,
Andy Duan

^ permalink raw reply

* Re: [PATCH v1] tty: serial: imx: enable IDDMAEN for the last tail data
From: Lucas Stach @ 2018-09-20  9:39 UTC (permalink / raw)
  To: Robin Gong, Uwe Kleine-König
  Cc: Andy Duan, linux-serial@vger.kernel.org,
	linux-kernel@vger.kernel.org, dl-linux-imx, kernel@pengutronix.de,
	jslaby@suse.com
In-Reply-To: <VI1PR04MB45432291716B64380C1B90DD89130@VI1PR04MB4543.eurprd04.prod.outlook.com>

Am Donnerstag, den 20.09.2018, 08:39 +0000 schrieb Robin Gong:
> > -----Original Message-----
> > From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > Sent: 2018年9月20日 15:55
> > To: Robin Gong <yibin.gong@nxp.com>
> > Cc: jslaby@suse.com; Andy Duan <fugang.duan@nxp.com>;
> > linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; dl-
> > linux-imx
> > <linux-imx@nxp.com>; kernel@pengutronix.de
> > Subject: Re: [PATCH v1] tty: serial: imx: enable IDDMAEN for the
> > last tail data
> > 
> > Hello,
> > 
> > On Thu, Sep 20, 2018 at 11:26:00PM +0800, Robin Gong wrote:
> > > enable IDDMAEN in UCR4 to let sdma script has the chance to
> > > detect the
> > > idle status and transfer the last tail data with the interrupt
> > > notifying uart driver.Otherwise, the last dma done interrupt of
> > > the
> > > tail data in rxfifo whose size is less than watermark may never
> > > be
> > > received by uart driver.
> > 
> > Is this a theoretic issue? Or does it fix a real problem?
> > 
> > If the former I'd say UCR1_ATDMAEN being set should prevent this
> > stall.
> 
> It's a real fix, you will see 'timeout We read 0 byte' with the
> attached stress test
> running on i.mx7d or i.mx6 if no this patch applied. 
> ' while [ 1 ]; do /unit_tests/UART/mxc_uart_stress_test.out
> /dev/ttymxc5 2000000 D L 100 100 L; done &'
> Please make sure dma enabled on ttymxcX for your test, and sdma
> firmware setup in your rootfs 
> /lib/firmware/imx/sdma/sdma-imx6q.bin or /lib/firmware/imx/sdma/sdma-
> imx7d.bin.
> You can get sdma firmware from below:
> https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-
> firmware.git/tree/imx/sdma

Please see commit 905c0decad28402aa166975023fb88c8f62f93c8 on why using
the idle detect together with with SDMA is wrong. We don't want to re-
introduce the broken behavior.

Regards,
Lucas

^ permalink raw reply

* Re: [PATCH -next] serial: imx: remove set but not used variable 'rtsirq'
From: Leonard Crestez @ 2018-09-20  8:50 UTC (permalink / raw)
  To: yuehaibing@huawei.com, u.kleine-koenig@pengutronix.de,
	jslaby@suse.cz
  Cc: linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org,
	dl-linux-imx, gregkh@linuxfoundation.org, Andy Duan,
	kernel-janitors@vger.kernel.org
In-Reply-To: <3b23675a-f537-868f-2432-b7f8a0c8c466@suse.cz>

On Thu, 2018-09-20 at 08:45 +0200, Jiri Slaby wrote:
> On 09/20/2018, 03:58 AM, YueHaibing wrote:
> > Fixes gcc '-Wunused-but-set-variable' warning:
> > 
> > drivers/tty/serial/imx.c: In function 'imx_uart_probe':
> > drivers/tty/serial/imx.c:2198:20: warning:
> >  variable 'rtsirq' set but not used [-Wunused-but-set-variable]
> > 
> > diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> > @@ -2220,7 +2220,6 @@ static int imx_uart_probe(struct platform_device *pdev)
> >  
> >  	rxirq = platform_get_irq(pdev, 0);
> >  	txirq = platform_get_irq(pdev, 1);
> > -	rtsirq = platform_get_irq(pdev, 2);
> 
> I am not sure this is correct. platform_get_irq has side effects (like
> enabling the IRQ). Are you sure this won't change the behaviour (this is
> question mostly to IMX fellows)?

As far as I can tell there was a request_irq call for rtsirq which was
removed by mistake in commit afe9cbb1a6ad ("serial: imx: drop support
for IRDA"):

-               /* do not use RTS IRQ on IrDA */
-               if (!USE_IRDA(sport)) {
-                       ret = devm_request_irq(&pdev->dev, rtsirq,
-                                              imx_rtsint, 0,
-                                              dev_name(&pdev->dev), sport);
-                       if (ret)
-                               return ret;
-               }

This should have just removed the IRDA check and request rtsirq
unconditionally. Nobody noticed this by testing RTS on imx1, this is an
old chip and later variants have a single combined irq.

The correct fix for the warning would be to restore that request_irq.

--
Regards,
Leonard

^ permalink raw reply

* RE: [PATCH v1] tty: serial: imx: enable IDDMAEN for the last tail data
From: Robin Gong @ 2018-09-20  8:39 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: jslaby@suse.com, Andy Duan, linux-serial@vger.kernel.org,
	linux-kernel@vger.kernel.org, dl-linux-imx, kernel@pengutronix.de
In-Reply-To: <20180920075513.6jqfbrrjax3sp4cn@pengutronix.de>

[-- Attachment #1: Type: text/plain, Size: 3603 bytes --]



> -----Original Message-----
> From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Sent: 2018年9月20日 15:55
> To: Robin Gong <yibin.gong@nxp.com>
> Cc: jslaby@suse.com; Andy Duan <fugang.duan@nxp.com>;
> linux-serial@vger.kernel.org; linux-kernel@vger.kernel.org; dl-linux-imx
> <linux-imx@nxp.com>; kernel@pengutronix.de
> Subject: Re: [PATCH v1] tty: serial: imx: enable IDDMAEN for the last tail data
> 
> Hello,
> 
> On Thu, Sep 20, 2018 at 11:26:00PM +0800, Robin Gong wrote:
> > enable IDDMAEN in UCR4 to let sdma script has the chance to detect the
> > idle status and transfer the last tail data with the interrupt
> > notifying uart driver.Otherwise, the last dma done interrupt of the
> > tail data in rxfifo whose size is less than watermark may never be
> > received by uart driver.
> 
> Is this a theoretic issue? Or does it fix a real problem?
> 
> If the former I'd say UCR1_ATDMAEN being set should prevent this stall.
It's a real fix, you will see 'timeout We read 0 byte' with the attached stress test
running on i.mx7d or i.mx6 if no this patch applied. 
' while [ 1 ]; do /unit_tests/UART/mxc_uart_stress_test.out /dev/ttymxc5 2000000 D L 100 100 L; done &'
Please make sure dma enabled on ttymxcX for your test, and sdma firmware setup in your rootfs 
/lib/firmware/imx/sdma/sdma-imx6q.bin or /lib/firmware/imx/sdma/sdma-imx7d.bin.
You can get sdma firmware from below:
https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tree/imx/sdma
> 
> Also I'd say the SDMA scripts I know don't properly handle the IDDMA request.
> 
> Best regards
> Uwe
> 
> > Signed-off-by: Robin Gong <yibin.gong@nxp.com>
> > ---
> >  drivers/tty/serial/imx.c | 12 ++++++++++--
> >  1 file changed, 10 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index
> > 239c0fa..bbb1693 100644
> > --- a/drivers/tty/serial/imx.c
> > +++ b/drivers/tty/serial/imx.c
> > @@ -1278,7 +1278,7 @@ static int imx_uart_dma_init(struct imx_port
> > *sport)
> >
> >  static void imx_uart_enable_dma(struct imx_port *sport)  {
> > -	u32 ucr1;
> > +	u32 ucr1, ucr4;
> >
> >  	imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA);
> >
> > @@ -1287,18 +1287,26 @@ static void imx_uart_enable_dma(struct
> imx_port *sport)
> >  	ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN;
> >  	imx_uart_writel(sport, ucr1, UCR1);
> >
> > +	ucr4 = imx_uart_readl(sport, UCR4);
> > +	ucr4 |= UCR4_IDDMAEN;
> > +	imx_uart_writel(sport, ucr4, UCR4);
> > +
> >  	sport->dma_is_enabled = 1;
> >  }
> >
> >  static void imx_uart_disable_dma(struct imx_port *sport)  {
> > -	u32 ucr1;
> > +	u32 ucr1, ucr4;
> >
> >  	/* clear UCR1 */
> >  	ucr1 = imx_uart_readl(sport, UCR1);
> >  	ucr1 &= ~(UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN);
> >  	imx_uart_writel(sport, ucr1, UCR1);
> >
> > +	ucr4 = imx_uart_readl(sport, UCR4);
> > +	ucr4 &= ~UCR4_IDDMAEN;
> > +	imx_uart_writel(sport, ucr4, UCR4);
> > +
> >  	imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
> >
> >  	sport->dma_is_enabled = 0;
> > --
> > 2.7.4
> >
> >
> 
> --
> Pengutronix e.K.                           | Uwe Kleine-König
> |
> Industrial Linux Solutions                 |
> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.
> pengutronix.de%2F&amp;data=02%7C01%7Cyibin.gong%40nxp.com%7Cebbc2
> 26ea12d4f6dd93b08d61ece6add%7C686ea1d3bc2b4c6fa92cd99c5c301635%7
> C0%7C0%7C636730269265048370&amp;sdata=7NstF74mOGOFajEwFjBw7FAh
> h3JdvGAIC9nliwrsf0Y%3D&amp;reserved=0  |

[-- Attachment #2: mxc_uart_stress_test.out --]
[-- Type: application/octet-stream, Size: 33740 bytes --]

^ permalink raw reply

* Re: [PATCH v1] tty: serial: imx: enable IDDMAEN for the last tail data
From: Uwe Kleine-König @ 2018-09-20  7:55 UTC (permalink / raw)
  To: Robin Gong
  Cc: jslaby, fugang.duan, linux-serial, linux-kernel, linux-imx,
	kernel
In-Reply-To: <1537457160-10792-1-git-send-email-yibin.gong@nxp.com>

Hello,

On Thu, Sep 20, 2018 at 11:26:00PM +0800, Robin Gong wrote:
> enable IDDMAEN in UCR4 to let sdma script has the chance to detect
> the idle status and transfer the last tail data with the interrupt
> notifying uart driver.Otherwise, the last dma done interrupt of the
> tail data in rxfifo whose size is less than watermark may never be
> received by uart driver.

Is this a theoretic issue? Or does it fix a real problem?

If the former I'd say UCR1_ATDMAEN being set should prevent this stall.

Also I'd say the SDMA scripts I know don't properly handle the IDDMA
request.

Best regards
Uwe

> Signed-off-by: Robin Gong <yibin.gong@nxp.com>
> ---
>  drivers/tty/serial/imx.c | 12 ++++++++++--
>  1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index 239c0fa..bbb1693 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -1278,7 +1278,7 @@ static int imx_uart_dma_init(struct imx_port *sport)
>  
>  static void imx_uart_enable_dma(struct imx_port *sport)
>  {
> -	u32 ucr1;
> +	u32 ucr1, ucr4;
>  
>  	imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA);
>  
> @@ -1287,18 +1287,26 @@ static void imx_uart_enable_dma(struct imx_port *sport)
>  	ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN;
>  	imx_uart_writel(sport, ucr1, UCR1);
>  
> +	ucr4 = imx_uart_readl(sport, UCR4);
> +	ucr4 |= UCR4_IDDMAEN;
> +	imx_uart_writel(sport, ucr4, UCR4);
> +
>  	sport->dma_is_enabled = 1;
>  }
>  
>  static void imx_uart_disable_dma(struct imx_port *sport)
>  {
> -	u32 ucr1;
> +	u32 ucr1, ucr4;
>  
>  	/* clear UCR1 */
>  	ucr1 = imx_uart_readl(sport, UCR1);
>  	ucr1 &= ~(UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN);
>  	imx_uart_writel(sport, ucr1, UCR1);
>  
> +	ucr4 = imx_uart_readl(sport, UCR4);
> +	ucr4 &= ~UCR4_IDDMAEN;
> +	imx_uart_writel(sport, ucr4, UCR4);
> +
>  	imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
>  
>  	sport->dma_is_enabled = 0;
> -- 
> 2.7.4
> 
> 

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: [PATCH -next] serial: imx: remove set but not used variable 'rtsirq'
From: Uwe Kleine-König @ 2018-09-20  7:19 UTC (permalink / raw)
  To: YueHaibing
  Cc: Greg Kroah-Hartman, Jiri Slaby, linux-serial, kernel-janitors,
	linux-kernel
In-Reply-To: <1537408725-196364-1-git-send-email-yuehaibing@huawei.com>

On Thu, Sep 20, 2018 at 01:58:45AM +0000, YueHaibing wrote:
> Fixes gcc '-Wunused-but-set-variable' warning:
> 
> drivers/tty/serial/imx.c: In function 'imx_uart_probe':
> drivers/tty/serial/imx.c:2198:20: warning:
>  variable 'rtsirq' set but not used [-Wunused-but-set-variable]
> 
> Signed-off-by: YueHaibing <yuehaibing@huawei.com>

Wow, this variable is write-only since 

	afe9cbb1a6ad ("serial: imx: drop support for IRDA")

which is over three years old. The last hunk of this patch is wrong
however, which means that nobody uses handshaking on imx1 with a kernel
newer than 4.1-rc1. (Well, or they fixed it and didn't made the effort
to tell.)

I suggest to break rx and tx on imx1, too, and if nobody reports a
regression within the next three years, we rip out imx1 support
completely. :-)

Otherwise we need:

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 4e853570ea80..554a69db1bca 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -2350,6 +2350,14 @@ static int imx_uart_probe(struct platform_device *pdev)
 				ret);
 			return ret;
 		}
+
+		ret = devm_request_irq(&pdev->dev, rtsirq, imx_uart_rtsint, 0,
+				       dev_name(&pdev->dev), sport);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to request rts irq: %d\n",
+				ret);
+			return ret;
+		}
 	} else {
 		ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,
 				       dev_name(&pdev->dev), sport);

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply related

* [PATCH 4/4] arm: dts: mt2701: add uart APDMA to device tree
From: Long Cheng @ 2018-09-20  6:41 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Ed Blake, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen
In-Reply-To: <1537425673-18807-1-git-send-email-long.cheng@mediatek.com>

1. add uart APDMA controller device node
2. add uart 0/1/2/3 DMA function
3. uart0 is console, So disable DMA
4. enable uart2 port to test DMA function.

Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt2712e.dtsi |   50 +++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt2712e.dtsi b/arch/arm64/boot/dts/mediatek/mt2712e.dtsi
index 593ddc7..ff94437 100644
--- a/arch/arm64/boot/dts/mediatek/mt2712e.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt2712e.dtsi
@@ -299,6 +299,9 @@
 		interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW>;
 		clocks = <&baud_clk>, <&sys_clk>;
 		clock-names = "baud", "bus";
+		dmas = <&apdma 10
+			&apdma 11>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -366,6 +369,38 @@
 		status = "disabled";
 	};
 
+	apdma: dma-controller@11000400 {
+		compatible = "mediatek,mt2712-uart-dma",
+			     "mediatek,mt6577-uart-dma";
+		reg = <0 0x11000400 0 0x80>,
+		      <0 0x11000480 0 0x80>,
+		      <0 0x11000500 0 0x80>,
+		      <0 0x11000580 0 0x80>,
+		      <0 0x11000600 0 0x80>,
+		      <0 0x11000680 0 0x80>,
+		      <0 0x11000700 0 0x80>,
+		      <0 0x11000780 0 0x80>,
+		      <0 0x11000800 0 0x80>,
+		      <0 0x11000880 0 0x80>,
+		      <0 0x11000900 0 0x80>,
+		      <0 0x11000980 0 0x80>;
+		interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 105 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 106 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 107 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 108 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 109 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 110 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 111 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 112 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 113 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 114 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&pericfg CLK_PERI_AP_DMA>;
+		clock-names = "apdma";
+		#dma-cells = <1>;
+	};
+
 	uart0: serial@11002000 {
 		compatible = "mediatek,mt2712-uart",
 			     "mediatek,mt6577-uart";
@@ -373,6 +408,9 @@
 		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
 		clocks = <&baud_clk>, <&sys_clk>;
 		clock-names = "baud", "bus";
+		dmas = <&apdma 0
+			&apdma 1>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -383,6 +421,9 @@
 		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
 		clocks = <&baud_clk>, <&sys_clk>;
 		clock-names = "baud", "bus";
+		dmas = <&apdma 2
+			&apdma 3>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -393,6 +434,9 @@
 		interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_LOW>;
 		clocks = <&baud_clk>, <&sys_clk>;
 		clock-names = "baud", "bus";
+		dmas = <&apdma 4
+			&apdma 5>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -403,6 +447,9 @@
 		interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_LOW>;
 		clocks = <&baud_clk>, <&sys_clk>;
 		clock-names = "baud", "bus";
+		dmas = <&apdma 6
+			&apdma 7>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -503,6 +550,9 @@
 		interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_LOW>;
 		clocks = <&baud_clk>, <&sys_clk>;
 		clock-names = "baud", "bus";
+		dmas = <&apdma 8
+			&apdma 9>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 3/4] serial: 8250-mtk: add uart DMA support
From: Long Cheng @ 2018-09-20  6:41 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Ed Blake, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen
In-Reply-To: <1537425673-18807-1-git-send-email-long.cheng@mediatek.com>

Modify uart register to support DMA function.

Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
 drivers/tty/serial/8250/8250_mtk.c |  211 +++++++++++++++++++++++++++++++++++-
 1 file changed, 210 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index dd5e1ce..9da9db4 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -14,6 +14,10 @@
 #include <linux/pm_runtime.h>
 #include <linux/serial_8250.h>
 #include <linux/serial_reg.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
 
 #include "8250.h"
 
@@ -22,12 +26,173 @@
 #define UART_MTK_SAMPLE_POINT	0x0b	/* Sample point register */
 #define MTK_UART_RATE_FIX	0x0d	/* UART Rate Fix Register */
 
+#define MTK_UART_DMA_EN		0x13	/* DMA Enable register */
+#define MTK_UART_DMA_EN_TX	0x2
+#define MTK_UART_DMA_EN_RX	0x5
+
+#define MTK_UART_TX_SIZE	UART_XMIT_SIZE
+#define MTK_UART_RX_SIZE	0x8000
+#define MTK_UART_TX_TRIGGER	1
+#define MTK_UART_RX_TRIGGER	MTK_UART_RX_SIZE
+
+#ifdef CONFIG_SERIAL_8250_DMA
+enum dma_rx_status {
+	DMA_RX_START = 0,
+	DMA_RX_RUNNING = 1,
+	DMA_RX_SHUTDOWN = 2,
+};
+#endif
+
 struct mtk8250_data {
 	int			line;
+	unsigned int		rx_pos;
 	struct clk		*uart_clk;
 	struct clk		*bus_clk;
+	struct uart_8250_dma	*dma;
+#ifdef CONFIG_SERIAL_8250_DMA
+	enum dma_rx_status	rx_status;
+#endif
 };
 
+#ifdef CONFIG_SERIAL_8250_DMA
+static void mtk8250_rx_dma(struct uart_8250_port *up);
+
+static void mtk8250_dma_rx_complete(void *param)
+{
+	struct uart_8250_port *up = param;
+	struct uart_8250_dma *dma = up->dma;
+	struct mtk8250_data *data = up->port.private_data;
+	struct tty_port *tty_port = &up->port.state->port;
+	struct dma_tx_state state;
+	unsigned char *ptr;
+	int copied;
+
+	dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
+				dma->rx_size, DMA_FROM_DEVICE);
+
+	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+	dmaengine_terminate_all(dma->rxchan);
+
+	if (data->rx_status == DMA_RX_SHUTDOWN)
+		return;
+
+	if ((data->rx_pos + state.residue) <= dma->rx_size) {
+		ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+		copied = tty_insert_flip_string(tty_port, ptr, state.residue);
+	} else {
+		ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+		copied = tty_insert_flip_string(tty_port, ptr,
+						dma->rx_size - data->rx_pos);
+		ptr = (unsigned char *)(dma->rx_buf);
+		copied += tty_insert_flip_string(tty_port, ptr,
+				data->rx_pos + state.residue - dma->rx_size);
+	}
+	up->port.icount.rx += copied;
+
+	tty_flip_buffer_push(tty_port);
+
+	mtk8250_rx_dma(up);
+}
+
+static void mtk8250_rx_dma(struct uart_8250_port *up)
+{
+	struct uart_8250_dma *dma = up->dma;
+	struct mtk8250_data *data = up->port.private_data;
+	struct dma_async_tx_descriptor	*desc;
+	struct dma_tx_state	 state;
+
+	desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
+					   dma->rx_size, DMA_DEV_TO_MEM,
+					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		pr_err("failed to prepare rx slave single\n");
+		return;
+	}
+
+	desc->callback = mtk8250_dma_rx_complete;
+	desc->callback_param = up;
+
+	dma->rx_cookie = dmaengine_submit(desc);
+
+	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+	data->rx_pos = state.residue;
+
+	dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
+				   dma->rx_size, DMA_FROM_DEVICE);
+
+	dma_async_issue_pending(dma->rxchan);
+}
+
+static void mtk8250_dma_enable(struct uart_8250_port *up)
+{
+	struct uart_8250_dma *dma = up->dma;
+	struct mtk8250_data *data = up->port.private_data;
+	int lcr = serial_in(up, UART_LCR);
+
+	if (data->rx_status != DMA_RX_START)
+		return;
+
+	dma->rxconf.direction		= DMA_DEV_TO_MEM;
+	dma->rxconf.src_addr_width	= dma->rx_size / 1024;
+	dma->rxconf.src_addr		= dma->rx_addr;
+
+	dma->txconf.direction		= DMA_MEM_TO_DEV;
+	dma->txconf.dst_addr_width	= MTK_UART_TX_SIZE / 1024;
+	dma->txconf.dst_addr		= dma->tx_addr;
+
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+		UART_FCR_CLEAR_XMIT);
+	serial_out(up, MTK_UART_DMA_EN,
+		   MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_EFR, UART_EFR_ECB);
+	serial_out(up, UART_LCR, lcr);
+
+	if (dmaengine_slave_config(dma->rxchan, &dma->rxconf) != 0)
+		pr_err("failed to configure rx dma channel\n");
+	if (dmaengine_slave_config(dma->txchan, &dma->txconf) != 0)
+		pr_err("failed to configure tx dma channel\n");
+
+	data->rx_status = DMA_RX_RUNNING;
+	data->rx_pos = 0;
+	mtk8250_rx_dma(up);
+}
+#endif
+
+static int mtk8250_startup(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_8250_DMA
+	struct uart_8250_port *up = up_to_u8250p(port);
+	struct mtk8250_data *data = port->private_data;
+
+	/* disable DMA for console */
+	if (uart_console(port))
+		up->dma = NULL;
+
+	if (up->dma) {
+		data->rx_status = DMA_RX_START;
+		uart_circ_clear(&port->state->xmit);
+	}
+#endif
+	memset(&port->icount, 0, sizeof(port->icount));
+
+	return serial8250_do_startup(port);
+}
+
+static void mtk8250_shutdown(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_8250_DMA
+	struct uart_8250_port *up = up_to_u8250p(port);
+	struct mtk8250_data *data = port->private_data;
+
+	if (up->dma)
+		data->rx_status = DMA_RX_SHUTDOWN;
+#endif
+
+	return serial8250_do_shutdown(port);
+}
+
 static void
 mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
 			struct ktermios *old)
@@ -36,6 +201,17 @@ struct mtk8250_data {
 	unsigned long flags;
 	unsigned int baud, quot;
 
+#ifdef CONFIG_SERIAL_8250_DMA
+	if (up->dma) {
+		if (uart_console(port)) {
+			devm_kfree(up->port.dev, up->dma);
+			up->dma = NULL;
+		} else {
+			mtk8250_dma_enable(up);
+		}
+	}
+#endif
+
 	serial8250_do_set_termios(port, termios, old);
 
 	/*
@@ -143,9 +319,20 @@ static int __maybe_unused mtk8250_runtime_resume(struct device *dev)
 		pm_runtime_put_sync_suspend(port->dev);
 }
 
+#ifdef CONFIG_SERIAL_8250_DMA
+static bool mtk8250_dma_filter(struct dma_chan *chan, void *param)
+{
+	return false;
+}
+#endif
+
 static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
 			   struct mtk8250_data *data)
 {
+#ifdef CONFIG_SERIAL_8250_DMA
+	int dmacnt;
+#endif
+
 	data->uart_clk = devm_clk_get(&pdev->dev, "baud");
 	if (IS_ERR(data->uart_clk)) {
 		/*
@@ -162,7 +349,23 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
 	}
 
 	data->bus_clk = devm_clk_get(&pdev->dev, "bus");
-	return PTR_ERR_OR_ZERO(data->bus_clk);
+	if (IS_ERR(data->bus_clk))
+		return PTR_ERR(data->bus_clk);
+
+	data->dma = NULL;
+#ifdef CONFIG_SERIAL_8250_DMA
+	dmacnt = of_property_count_strings(pdev->dev.of_node, "dma-names");
+	if (dmacnt == 2) {
+		data->dma = devm_kzalloc(&pdev->dev, sizeof(*data->dma),
+					 GFP_KERNEL);
+		data->dma->fn = mtk8250_dma_filter;
+		data->dma->rx_size = MTK_UART_RX_SIZE;
+		data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER;
+		data->dma->txconf.dst_maxburst = MTK_UART_TX_TRIGGER;
+	}
+#endif
+
+	return 0;
 }
 
 static int mtk8250_probe(struct platform_device *pdev)
@@ -204,8 +407,14 @@ static int mtk8250_probe(struct platform_device *pdev)
 	uart.port.iotype = UPIO_MEM32;
 	uart.port.regshift = 2;
 	uart.port.private_data = data;
+	uart.port.shutdown = mtk8250_shutdown;
+	uart.port.startup = mtk8250_startup;
 	uart.port.set_termios = mtk8250_set_termios;
 	uart.port.uartclk = clk_get_rate(data->uart_clk);
+#ifdef CONFIG_SERIAL_8250_DMA
+	if (data->dma)
+		uart.dma = data->dma;
+#endif
 
 	/* Disable Rate Fix function */
 	writel(0x0, uart.port.membase +
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 2/4] dmaengine: mtk_uart_dma: add Mediatek uart DMA support
From: Long Cheng @ 2018-09-20  6:41 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Ed Blake, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen
In-Reply-To: <1537425673-18807-1-git-send-email-long.cheng@mediatek.com>

In DMA engine framework, add 8250 mtk dma to support it.

Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
 drivers/dma/8250_mtk_dma.c | 1049 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/dma/Kconfig        |   11 +
 drivers/dma/Makefile       |    1 +
 3 files changed, 1061 insertions(+)
 create mode 100644 drivers/dma/8250_mtk_dma.c

diff --git a/drivers/dma/8250_mtk_dma.c b/drivers/dma/8250_mtk_dma.c
new file mode 100644
index 0000000..a07844e
--- /dev/null
+++ b/drivers/dma/8250_mtk_dma.c
@@ -0,0 +1,1049 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek 8250 DMA driver.
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Long Cheng <long.cheng@mediatek.com>
+ */
+
+#define pr_fmt(fmt) "8250-mtk-dma: " fmt
+#define DRV_NAME    "8250-mtk-dma"
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_dma.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/pm_runtime.h>
+
+#include "virt-dma.h"
+
+#define MTK_SDMA_REQUESTS	127
+#define MTK_SDMA_CHANNELS	(CONFIG_SERIAL_8250_NR_UARTS * 2)
+
+#define VFF_RX_INT_FLAG_CLR_B	(BIT(0U) | BIT(1U))
+#define VFF_TX_INT_FLAG_CLR_B	0
+#define VFF_RX_INT_EN0_B	BIT(0U)	/*rx valid size >=  vff thre */
+#define VFF_RX_INT_EN1_B	BIT(1U)
+#define VFF_TX_INT_EN_B		BIT(0U)	/*tx left size >= vff thre */
+#define VFF_INT_EN_CLR_B	0
+#define VFF_WARM_RST_B		BIT(0U)
+#define VFF_EN_B		BIT(0U)
+#define VFF_STOP_B		BIT(0U)
+#define VFF_STOP_CLR_B		0
+#define VFF_FLUSH_B		BIT(0U)
+#define VFF_FLUSH_CLR_B		0
+#define VFF_4G_SUPPORT_B	BIT(0U)
+#define VFF_4G_SUPPORT_CLR_B	0
+
+/* interrupt trigger level for tx */
+#define VFF_TX_THRE(n)		((n) * 7 / 8)
+/* interrupt trigger level for rx */
+#define VFF_RX_THRE(n)		((n) * 3 / 4)
+
+#define MTK_DMA_RING_SIZE	0xffffU
+/* invert this bit when wrap ring head again*/
+#define MTK_DMA_RING_WRAP	0x10000U
+
+struct mtk_dmadev {
+	struct dma_device ddev;
+	void __iomem *mem_base[MTK_SDMA_CHANNELS];
+	spinlock_t lock; /* dma dev lock */
+	struct tasklet_struct task;
+	struct list_head pending;
+	struct clk *clk;
+	unsigned int dma_requests;
+	bool support_33bits;
+	unsigned int dma_irq[MTK_SDMA_CHANNELS];
+	struct mtk_chan *lch_map[MTK_SDMA_CHANNELS];
+};
+
+struct mtk_chan {
+	struct virt_dma_chan vc;
+	struct list_head node;
+	struct dma_slave_config	cfg;
+	void __iomem *channel_base;
+	struct mtk_dma_desc *desc;
+
+	bool paused;
+	bool requested;
+
+	unsigned int dma_sig;
+	unsigned int dma_ch;
+	unsigned int sgidx;
+	unsigned int remain_size;
+	unsigned int rx_ptr;
+
+	/*sync*/
+	struct completion done;	/* dma transfer done */
+	spinlock_t lock; /* channel lock */
+	atomic_t loopcnt;
+	atomic_t entry;		/* entry count */
+};
+
+struct mtk_dma_sg {
+	dma_addr_t addr;
+	unsigned int en;		/* number of elements (24-bit) */
+	unsigned int fn;		/* number of frames (16-bit) */
+};
+
+struct mtk_dma_desc {
+	struct virt_dma_desc vd;
+	enum dma_transfer_direction dir;
+	dma_addr_t dev_addr;
+
+	unsigned int sglen;
+	struct mtk_dma_sg sg[0];
+};
+
+enum {
+	VFF_INT_FLAG		= 0x00,
+	VFF_INT_EN		= 0x04,
+	VFF_EN			= 0x08,
+	VFF_RST			= 0x0c,
+	VFF_STOP		= 0x10,
+	VFF_FLUSH		= 0x14,
+	VFF_ADDR		= 0x1c,
+	VFF_LEN			= 0x24,
+	VFF_THRE		= 0x28,
+	VFF_WPT			= 0x2c,
+	VFF_RPT			= 0x30,
+	/*TX: the buffer size HW can read. RX: the buffer size SW can read.*/
+	VFF_VALID_SIZE		= 0x3c,
+	/*TX: the buffer size SW can write. RX: the buffer size HW can write.*/
+	VFF_LEFT_SIZE		= 0x40,
+	VFF_DEBUG_STATUS	= 0x50,
+	VFF_4G_SUPPORT		= 0x54,
+};
+
+static bool mtk_dma_filter_fn(struct dma_chan *chan, void *param);
+static struct of_dma_filter_info mtk_dma_info = {
+	.filter_fn = mtk_dma_filter_fn,
+};
+
+static inline struct mtk_dmadev *to_mtk_dma_dev(struct dma_device *d)
+{
+	return container_of(d, struct mtk_dmadev, ddev);
+}
+
+static inline struct mtk_chan *to_mtk_dma_chan(struct dma_chan *c)
+{
+	return container_of(c, struct mtk_chan, vc.chan);
+}
+
+static inline struct mtk_dma_desc *to_mtk_dma_desc
+	(struct dma_async_tx_descriptor *t)
+{
+	return container_of(t, struct mtk_dma_desc, vd.tx);
+}
+
+static void mtk_dma_chan_write(struct mtk_chan *c,
+			       unsigned int reg, unsigned int val)
+{
+	writel(val, c->channel_base + reg);
+}
+
+static unsigned int mtk_dma_chan_read(struct mtk_chan *c, unsigned int reg)
+{
+	return readl(c->channel_base + reg);
+}
+
+static void mtk_dma_desc_free(struct virt_dma_desc *vd)
+{
+	struct dma_chan *chan = vd->tx.chan;
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (c->desc && c->cfg.direction == DMA_DEV_TO_MEM)
+		atomic_dec(&c->entry);
+
+	kfree(c->desc);
+	c->desc = NULL;
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static int mtk_dma_clk_enable(struct mtk_dmadev *mtkd)
+{
+	int ret;
+
+	ret = clk_prepare_enable(mtkd->clk);
+	if (ret) {
+		dev_err(mtkd->ddev.dev, "Couldn't enable the clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mtk_dma_clk_disable(struct mtk_dmadev *mtkd)
+{
+	clk_disable_unprepare(mtkd->clk);
+}
+
+static void mtk_dma_remove_virt_list(dma_cookie_t cookie,
+				     struct virt_dma_chan *vc)
+{
+	struct virt_dma_desc *vd;
+
+	if (list_empty(&vc->desc_issued) == 0) {
+		list_for_each_entry(vd, &vc->desc_issued, node) {
+			if (cookie == vd->tx.cookie) {
+				INIT_LIST_HEAD(&vc->desc_issued);
+				break;
+			}
+		}
+	}
+}
+
+static void mtk_dma_tx_flush(struct dma_chan *chan)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+
+	if (mtk_dma_chan_read(c, VFF_FLUSH) == 0U) {
+		mtk_dma_chan_write(c, VFF_FLUSH, VFF_FLUSH_B);
+		if (atomic_dec_and_test(&c->loopcnt))
+			complete(&c->done);
+	}
+}
+
+/*
+ * check whether the dma flush operation is finished or not.
+ * return 0 for flush success.
+ * return others for flush timeout.
+ */
+static int mtk_dma_check_flush_result(struct dma_chan *chan)
+{
+	struct timespec start, end;
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+
+	start = ktime_to_timespec(ktime_get());
+
+	while ((mtk_dma_chan_read(c, VFF_FLUSH) & VFF_FLUSH_B) == VFF_FLUSH_B) {
+		end = ktime_to_timespec(ktime_get());
+		if ((end.tv_sec - start.tv_sec) > 1 ||
+		    ((end.tv_sec - start.tv_sec) == 1 &&
+		      end.tv_nsec > start.tv_nsec)) {
+			dev_err(chan->device->dev,
+				"[DMA] Polling flush timeout\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static void mtk_dma_tx_write(struct dma_chan *chan)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	unsigned int txcount = c->remain_size;
+	unsigned int len, send, left, wpt, wrap;
+
+	if (atomic_inc_return(&c->entry) > 1) {
+		if (vchan_issue_pending(&c->vc) && !c->desc) {
+			spin_lock(&mtkd->lock);
+			list_add_tail(&c->node, &mtkd->pending);
+			spin_unlock(&mtkd->lock);
+			tasklet_schedule(&mtkd->task);
+		}
+	} else {
+		len = mtk_dma_chan_read(c, VFF_LEN);
+		if (mtk_dma_check_flush_result(chan) != 0)
+			return;
+
+		while ((left = mtk_dma_chan_read(c, VFF_LEFT_SIZE)) > 0U) {
+			send = min(left, c->remain_size);
+			wpt = mtk_dma_chan_read(c, VFF_WPT);
+			wrap = wpt & MTK_DMA_RING_WRAP ? 0U : MTK_DMA_RING_WRAP;
+
+			if ((wpt & (len - 1U)) + send < len)
+				mtk_dma_chan_write(c, VFF_WPT, wpt + send);
+			else
+				mtk_dma_chan_write(c, VFF_WPT,
+						   ((wpt + send) & (len - 1U))
+						   | wrap);
+
+			c->remain_size -= send;
+			if (c->remain_size == 0U)
+				break;
+		}
+
+		if (txcount != c->remain_size) {
+			mtk_dma_chan_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
+			mtk_dma_tx_flush(chan);
+		}
+	}
+	atomic_dec(&c->entry);
+}
+
+static void mtk_dma_start_tx(struct mtk_chan *c)
+{
+	if (mtk_dma_chan_read(c, VFF_LEFT_SIZE) == 0U) {
+		pr_info("%s maybe need fix? @L %d\n", __func__, __LINE__);
+		mtk_dma_chan_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
+	} else {
+		reinit_completion(&c->done);
+
+		/* inc twice, once for tx_flush, another for tx_interrupt */
+		atomic_inc(&c->loopcnt);
+		atomic_inc(&c->loopcnt);
+		mtk_dma_tx_write(&c->vc.chan);
+	}
+	c->paused = false;
+}
+
+static void mtk_dma_get_rx_size(struct mtk_chan *c)
+{
+	unsigned int count;
+	unsigned int rdptr, wrptr, wrreg, rdreg;
+	unsigned int rx_size = mtk_dma_chan_read(c, VFF_LEN);
+
+	rdreg = mtk_dma_chan_read(c, VFF_RPT);
+	wrreg = mtk_dma_chan_read(c, VFF_WPT);
+	rdptr = rdreg & MTK_DMA_RING_SIZE;
+	wrptr = wrreg & MTK_DMA_RING_SIZE;
+	count = ((rdreg ^ wrreg) & MTK_DMA_RING_WRAP) ?
+			(wrptr + rx_size - rdptr) : (wrptr - rdptr);
+
+	c->remain_size = count;
+	c->rx_ptr = rdptr;
+
+	mtk_dma_chan_write(c, VFF_RPT, wrreg);
+}
+
+static void mtk_dma_start_rx(struct mtk_chan *c)
+{
+	struct dma_chan *chan = &c->vc.chan;
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	struct mtk_dma_desc *d = c->desc;
+
+	if (mtk_dma_chan_read(c, VFF_VALID_SIZE) != 0U &&
+	    d && d->vd.tx.cookie != 0) {
+		mtk_dma_get_rx_size(c);
+		mtk_dma_remove_virt_list(d->vd.tx.cookie, &c->vc);
+		vchan_cookie_complete(&d->vd);
+	} else {
+		if (mtk_dma_chan_read(c, VFF_VALID_SIZE) != 0U) {
+			spin_lock(&mtkd->lock);
+			if (list_empty(&mtkd->pending))
+				list_add_tail(&c->node, &mtkd->pending);
+			spin_unlock(&mtkd->lock);
+			tasklet_schedule(&mtkd->task);
+		} else {
+			if (atomic_read(&c->entry) > 0)
+				atomic_set(&c->entry, 0);
+		}
+	}
+}
+
+static void mtk_dma_reset(struct mtk_chan *c)
+{
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(c->vc.chan.device);
+
+	mtk_dma_chan_write(c, VFF_ADDR, 0);
+	mtk_dma_chan_write(c, VFF_THRE, 0);
+	mtk_dma_chan_write(c, VFF_LEN, 0);
+	mtk_dma_chan_write(c, VFF_RST, VFF_WARM_RST_B);
+
+	while
+		(mtk_dma_chan_read(c, VFF_EN));
+
+	if (c->cfg.direction == DMA_DEV_TO_MEM)
+		mtk_dma_chan_write(c, VFF_RPT, 0);
+	else if (c->cfg.direction == DMA_MEM_TO_DEV)
+		mtk_dma_chan_write(c, VFF_WPT, 0);
+	else
+		dev_info(c->vc.chan.device->dev, "Unknown direction.\n");
+
+	if (mtkd->support_33bits)
+		mtk_dma_chan_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B);
+}
+
+static void mtk_dma_stop(struct mtk_chan *c)
+{
+	int polling_cnt;
+
+	mtk_dma_chan_write(c, VFF_FLUSH, VFF_FLUSH_CLR_B);
+
+	polling_cnt = 0;
+	while ((mtk_dma_chan_read(c, VFF_FLUSH) & VFF_FLUSH_B) ==
+		VFF_FLUSH_B)	{
+		polling_cnt++;
+		if (polling_cnt > 10000) {
+			dev_err(c->vc.chan.device->dev,
+				"dma stop: polling FLUSH fail, DEBUG=0x%x\n",
+				mtk_dma_chan_read(c, VFF_DEBUG_STATUS));
+			break;
+		}
+	}
+
+	polling_cnt = 0;
+	/*set stop as 1 -> wait until en is 0 -> set stop as 0*/
+	mtk_dma_chan_write(c, VFF_STOP, VFF_STOP_B);
+	while (mtk_dma_chan_read(c, VFF_EN)) {
+		polling_cnt++;
+		if (polling_cnt > 10000) {
+			dev_err(c->vc.chan.device->dev,
+				"dma stop: polling VFF_EN fail, DEBUG=0x%x\n",
+				mtk_dma_chan_read(c, VFF_DEBUG_STATUS));
+			break;
+		}
+	}
+	mtk_dma_chan_write(c, VFF_STOP, VFF_STOP_CLR_B);
+	mtk_dma_chan_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B);
+
+	if (c->cfg.direction == DMA_DEV_TO_MEM)
+		mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
+	else
+		mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
+
+	c->paused = true;
+}
+
+/*
+ * We need to deal with 'all channels in-use'
+ */
+static void mtk_dma_rx_sched(struct mtk_chan *c)
+{
+	struct dma_chan *chan = &c->vc.chan;
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+
+	if (atomic_read(&c->entry) < 1) {
+		mtk_dma_start_rx(c);
+	} else {
+		spin_lock(&mtkd->lock);
+		if (list_empty(&mtkd->pending))
+			list_add_tail(&c->node, &mtkd->pending);
+		spin_unlock(&mtkd->lock);
+		tasklet_schedule(&mtkd->task);
+	}
+}
+
+/*
+ * This callback schedules all pending channels. We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void mtk_dma_sched(unsigned long data)
+{
+	struct mtk_dmadev *mtkd = (struct mtk_dmadev *)data;
+	struct mtk_chan *c;
+	struct virt_dma_desc *vd;
+	dma_cookie_t cookie;
+	LIST_HEAD(head);
+	unsigned long flags;
+
+	spin_lock_irq(&mtkd->lock);
+	list_splice_tail_init(&mtkd->pending, &head);
+	spin_unlock_irq(&mtkd->lock);
+
+	if (list_empty(&head) == 0) {
+		c = list_first_entry(&head, struct mtk_chan, node);
+		cookie = c->vc.chan.cookie;
+
+		spin_lock_irqsave(&c->vc.lock, flags);
+		if (c->cfg.direction == DMA_DEV_TO_MEM) {
+			list_del_init(&c->node);
+			mtk_dma_rx_sched(c);
+		} else if (c->cfg.direction == DMA_MEM_TO_DEV) {
+			vd = vchan_find_desc(&c->vc, cookie);
+
+			c->desc = to_mtk_dma_desc(&vd->tx);
+			list_del_init(&c->node);
+			mtk_dma_start_tx(c);
+		}
+		spin_unlock_irqrestore(&c->vc.lock, flags);
+	}
+}
+
+static int mtk_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	int ret = -EBUSY;
+
+	pm_runtime_get_sync(mtkd->ddev.dev);
+
+	if (!mtkd->lch_map[c->dma_ch]) {
+		c->channel_base = mtkd->mem_base[c->dma_ch];
+		mtkd->lch_map[c->dma_ch] = c;
+		ret = 1;
+	}
+	c->requested = false;
+	mtk_dma_reset(c);
+
+	return ret;
+}
+
+static void mtk_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+
+	if (c->requested) {
+		c->requested = false;
+		free_irq(mtkd->dma_irq[c->dma_ch], chan);
+	}
+
+	tasklet_kill(&mtkd->task);
+
+	c->channel_base = NULL;
+	mtkd->lch_map[c->dma_ch] = NULL;
+	vchan_free_chan_resources(&c->vc);
+
+	dev_dbg(mtkd->ddev.dev, "freeing channel for %u\n", c->dma_sig);
+	c->dma_sig = 0;
+
+	pm_runtime_put_sync(mtkd->ddev.dev);
+}
+
+static enum dma_status mtk_dma_tx_status(struct dma_chan *chan,
+					 dma_cookie_t cookie,
+					 struct dma_tx_state *txstate)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	enum dma_status ret;
+	unsigned long flags;
+
+	ret = dma_cookie_status(chan, cookie, txstate);
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (ret == DMA_IN_PROGRESS) {
+		c->rx_ptr = mtk_dma_chan_read(c, VFF_RPT) & MTK_DMA_RING_SIZE;
+		txstate->residue = c->rx_ptr;
+	} else if (ret == DMA_COMPLETE && c->cfg.direction == DMA_DEV_TO_MEM) {
+		txstate->residue = c->remain_size;
+	} else {
+		txstate->residue = 0;
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+
+	return ret;
+}
+
+static unsigned int mtk_dma_desc_size(struct mtk_dma_desc *d)
+{
+	struct mtk_dma_sg *sg;
+	unsigned int i;
+	unsigned int size;
+
+	for (size = i = 0; i < d->sglen; i++) {
+		sg = &d->sg[i];
+		size += sg->en * sg->fn;
+	}
+	return size;
+}
+
+static struct dma_async_tx_descriptor *mtk_dma_prep_slave_sg
+	(struct dma_chan *chan, struct scatterlist *sgl,
+	unsigned int sglen,	enum dma_transfer_direction dir,
+	unsigned long tx_flags, void *context)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct scatterlist *sgent;
+	struct mtk_dma_desc *d;
+	dma_addr_t dev_addr;
+	unsigned int i, j, en, frame_bytes;
+
+	en = 1;
+	frame_bytes = 1;
+
+	if (dir == DMA_DEV_TO_MEM) {
+		dev_addr = c->cfg.src_addr;
+	} else if (dir == DMA_MEM_TO_DEV) {
+		dev_addr = c->cfg.dst_addr;
+	} else {
+		dev_err(chan->device->dev, "bad direction\n");
+		return NULL;
+	}
+
+	/* Now allocate and setup the descriptor. */
+	d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+	if (!d)
+		return NULL;
+
+	d->dir = dir;
+	d->dev_addr = dev_addr;
+
+	j = 0;
+	for_each_sg(sgl, sgent, sglen, i) {
+		d->sg[j].addr = sg_dma_address(sgent);
+		d->sg[j].en = en;
+		d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
+		j++;
+	}
+
+	d->sglen = j;
+
+	if (dir == DMA_MEM_TO_DEV)
+		c->remain_size = mtk_dma_desc_size(d);
+
+	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static void mtk_dma_issue_pending(struct dma_chan *chan)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct mtk_dmadev *mtkd;
+	struct virt_dma_desc *vd;
+	dma_cookie_t cookie;
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (c->cfg.direction == DMA_DEV_TO_MEM) {
+		cookie = c->vc.chan.cookie;
+		mtkd = to_mtk_dma_dev(chan->device);
+		if (vchan_issue_pending(&c->vc) && !c->desc) {
+			vd = vchan_find_desc(&c->vc, cookie);
+			c->desc = to_mtk_dma_desc(&vd->tx);
+			if (atomic_read(&c->entry) > 0)
+				atomic_set(&c->entry, 0);
+		}
+	} else if (c->cfg.direction == DMA_MEM_TO_DEV) {
+		cookie = c->vc.chan.cookie;
+		if (vchan_issue_pending(&c->vc) && !c->desc) {
+			vd = vchan_find_desc(&c->vc, cookie);
+			c->desc = to_mtk_dma_desc(&vd->tx);
+			mtk_dma_start_tx(c);
+		}
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static irqreturn_t mtk_dma_rx_interrupt(int irq, void *dev_id)
+{
+	struct dma_chan *chan = (struct dma_chan *)dev_id;
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
+
+	if (atomic_inc_return(&c->entry) > 1) {
+		if (list_empty(&mtkd->pending))
+			list_add_tail(&c->node, &mtkd->pending);
+		tasklet_schedule(&mtkd->task);
+	} else {
+		mtk_dma_start_rx(c);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dma_tx_interrupt(int irq, void *dev_id)
+{
+	struct dma_chan *chan = (struct dma_chan *)dev_id;
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+	struct mtk_dma_desc *d = c->desc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (c->remain_size != 0U) {
+		list_add_tail(&c->node, &mtkd->pending);
+		tasklet_schedule(&mtkd->task);
+	} else {
+		mtk_dma_remove_virt_list(d->vd.tx.cookie, &c->vc);
+		vchan_cookie_complete(&d->vd);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+
+	mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
+	if (atomic_dec_and_test(&c->loopcnt))
+		complete(&c->done);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_dma_slave_config(struct dma_chan *chan,
+				struct dma_slave_config *cfg)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	struct mtk_dmadev *mtkd = to_mtk_dma_dev(c->vc.chan.device);
+	int ret;
+
+	c->cfg = *cfg;
+
+	if (cfg->direction == DMA_DEV_TO_MEM) {
+		unsigned int rx_len = cfg->src_addr_width * 1024;
+
+		mtk_dma_chan_write(c, VFF_ADDR, cfg->src_addr);
+		mtk_dma_chan_write(c, VFF_LEN, rx_len);
+		mtk_dma_chan_write(c, VFF_THRE, VFF_RX_THRE(rx_len));
+		mtk_dma_chan_write(c,
+				   VFF_INT_EN, VFF_RX_INT_EN0_B
+				   | VFF_RX_INT_EN1_B);
+		mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_RX_INT_FLAG_CLR_B);
+		mtk_dma_chan_write(c, VFF_EN, VFF_EN_B);
+
+		if (!c->requested) {
+			atomic_set(&c->entry, 0);
+			c->requested = true;
+			ret = request_irq(mtkd->dma_irq[c->dma_ch],
+					  mtk_dma_rx_interrupt,
+					  IRQF_TRIGGER_NONE,
+					  DRV_NAME, chan);
+			if (ret < 0) {
+				dev_err(chan->device->dev, "Can't request rx dma IRQ\n");
+				return -EINVAL;
+			}
+		}
+	} else if (cfg->direction == DMA_MEM_TO_DEV)	{
+		unsigned int tx_len = cfg->dst_addr_width * 1024;
+
+		mtk_dma_chan_write(c, VFF_ADDR, cfg->dst_addr);
+		mtk_dma_chan_write(c, VFF_LEN, tx_len);
+		mtk_dma_chan_write(c, VFF_THRE, VFF_TX_THRE(tx_len));
+		mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_TX_INT_FLAG_CLR_B);
+		mtk_dma_chan_write(c, VFF_EN, VFF_EN_B);
+
+		if (!c->requested) {
+			c->requested = true;
+			ret = request_irq(mtkd->dma_irq[c->dma_ch],
+					  mtk_dma_tx_interrupt,
+					  IRQF_TRIGGER_NONE,
+					  DRV_NAME, chan);
+			if (ret < 0) {
+				dev_err(chan->device->dev, "Can't request tx dma IRQ\n");
+				return -EINVAL;
+			}
+		}
+	} else {
+		dev_info(chan->device->dev, "Unknown direction!\n");
+	}
+
+	if (mtkd->support_33bits)
+		mtk_dma_chan_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B);
+
+	if (mtk_dma_chan_read(c, VFF_EN) != VFF_EN_B) {
+		dev_err(chan->device->dev,
+			"config dma dir[%d] fail\n", cfg->direction);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mtk_dma_terminate_all(struct dma_chan *chan)
+{
+	struct mtk_chan *c = to_mtk_dma_chan(chan);
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	if (atomic_read(&c->loopcnt) != 0)
+		wait_for_completion(&c->done);
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (c->desc) {
+		mtk_dma_remove_virt_list(c->desc->vd.tx.cookie, &c->vc);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
+
+		mtk_dma_desc_free(&c->desc->vd);
+
+		spin_lock_irqsave(&c->vc.lock, flags);
+		if (!c->paused)	{
+			list_del_init(&c->node);
+			mtk_dma_stop(c);
+		}
+	}
+	vchan_get_all_descriptors(&c->vc, &head);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+
+	vchan_dma_desc_free_list(&c->vc, &head);
+
+	return 0;
+}
+
+static int mtk_dma_device_pause(struct dma_chan *chan)
+{
+	/* Pause/Resume only allowed with cyclic mode */
+	return -EINVAL;
+}
+
+static int mtk_dma_device_resume(struct dma_chan *chan)
+{
+	/* Pause/Resume only allowed with cyclic mode */
+	return -EINVAL;
+}
+
+static int mtk_dma_chan_init(struct mtk_dmadev *mtkd)
+{
+	struct mtk_chan *c;
+
+	c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return -ENOMEM;
+
+	c->vc.desc_free = mtk_dma_desc_free;
+	vchan_init(&c->vc, &mtkd->ddev);
+	spin_lock_init(&c->lock);
+	INIT_LIST_HEAD(&c->node);
+
+	init_completion(&c->done);
+	atomic_set(&c->loopcnt, 0);
+	atomic_set(&c->entry, 0);
+
+	return 0;
+}
+
+static void mtk_dma_free(struct mtk_dmadev *mtkd)
+{
+	tasklet_kill(&mtkd->task);
+	while (list_empty(&mtkd->ddev.channels) == 0) {
+		struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels,
+			struct mtk_chan, vc.chan.device_node);
+
+		list_del(&c->vc.chan.device_node);
+		tasklet_kill(&c->vc.task);
+		devm_kfree(mtkd->ddev.dev, c);
+	}
+}
+
+static const struct of_device_id mtk_uart_dma_match[] = {
+	{ .compatible = "mediatek,mt6577-uart-dma", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_uart_dma_match);
+
+static int mtk_dma_probe(struct platform_device *pdev)
+{
+	struct mtk_dmadev *mtkd;
+	struct resource *res;
+	unsigned int i;
+	int rc;
+
+	mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL);
+	if (!mtkd)
+		return -ENOMEM;
+
+	for (i = 0; i < MTK_SDMA_CHANNELS; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		if (!res)
+			return -ENODEV;
+		mtkd->mem_base[i] = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(mtkd->mem_base[i]))
+			return PTR_ERR(mtkd->mem_base[i]);
+	}
+
+	/* request irq */
+	for (i = 0; i < MTK_SDMA_CHANNELS; i++) {
+		mtkd->dma_irq[i] = platform_get_irq(pdev, i);
+		if ((int)mtkd->dma_irq[i] < 0) {
+			dev_err(&pdev->dev, "failed to get IRQ[%d]\n", i);
+			return -EINVAL;
+		}
+	}
+
+	mtkd->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mtkd->clk)) {
+		dev_err(&pdev->dev, "No clock specified\n");
+		return PTR_ERR(mtkd->clk);
+	}
+
+	if (of_property_read_bool(pdev->dev.of_node, "dma-33bits")) {
+		dev_info(&pdev->dev, "Support dma 33bits\n");
+		mtkd->support_33bits = true;
+	}
+
+	if (mtkd->support_33bits)
+		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
+	else
+		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	if (rc)
+		return rc;
+
+	dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask);
+	mtkd->ddev.device_alloc_chan_resources = mtk_dma_alloc_chan_resources;
+	mtkd->ddev.device_free_chan_resources = mtk_dma_free_chan_resources;
+	mtkd->ddev.device_tx_status = mtk_dma_tx_status;
+	mtkd->ddev.device_issue_pending = mtk_dma_issue_pending;
+	mtkd->ddev.device_prep_slave_sg = mtk_dma_prep_slave_sg;
+	mtkd->ddev.device_config = mtk_dma_slave_config;
+	mtkd->ddev.device_pause = mtk_dma_device_pause;
+	mtkd->ddev.device_resume = mtk_dma_device_resume;
+	mtkd->ddev.device_terminate_all = mtk_dma_terminate_all;
+	mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+	mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+	mtkd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+	mtkd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+	mtkd->ddev.dev = &pdev->dev;
+	INIT_LIST_HEAD(&mtkd->ddev.channels);
+	INIT_LIST_HEAD(&mtkd->pending);
+
+	spin_lock_init(&mtkd->lock);
+	tasklet_init(&mtkd->task, mtk_dma_sched, (unsigned long)mtkd);
+
+	mtkd->dma_requests = MTK_SDMA_REQUESTS;
+	if (of_property_read_u32(pdev->dev.of_node,
+				 "dma-requests", &mtkd->dma_requests) != 0) {
+		dev_info(&pdev->dev,
+			 "Missing dma-requests property, using %u.\n",
+			 MTK_SDMA_REQUESTS);
+	}
+
+	for (i = 0; i < MTK_SDMA_CHANNELS; i++) {
+		rc = mtk_dma_chan_init(mtkd);
+		if (rc)
+			goto err_no_dma;
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+
+	rc = dma_async_device_register(&mtkd->ddev);
+	if (rc) {
+		dev_warn(&pdev->dev, "fail to register DMA device: %d\n", rc);
+		mtk_dma_clk_disable(mtkd);
+		goto err_no_dma;
+	}
+
+	platform_set_drvdata(pdev, mtkd);
+
+	if (pdev->dev.of_node) {
+		mtk_dma_info.dma_cap = mtkd->ddev.cap_mask;
+
+		/* Device-tree DMA controller registration */
+		rc = of_dma_controller_register(pdev->dev.of_node,
+						of_dma_simple_xlate,
+						&mtk_dma_info);
+		if (rc) {
+			dev_warn(&pdev->dev, "fail to register DMA controller\n");
+			dma_async_device_unregister(&mtkd->ddev);
+			mtk_dma_clk_disable(mtkd);
+			goto err_no_dma;
+		}
+	}
+
+	return rc;
+
+err_no_dma:
+	mtk_dma_free(mtkd);
+	return rc;
+}
+
+static int mtk_dma_remove(struct platform_device *pdev)
+{
+	struct mtk_dmadev *mtkd = platform_get_drvdata(pdev);
+
+	if (pdev->dev.of_node)
+		of_dma_controller_free(pdev->dev.of_node);
+
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_put_noidle(&pdev->dev);
+
+	dma_async_device_unregister(&mtkd->ddev);
+
+	mtk_dma_free(mtkd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dma_suspend(struct device *dev)
+{
+	struct mtk_dmadev *mtkd = dev_get_drvdata(dev);
+
+	if (!pm_runtime_suspended(dev))
+		mtk_dma_clk_disable(mtkd);
+
+	return 0;
+}
+
+static int mtk_dma_resume(struct device *dev)
+{
+	int ret;
+	struct mtk_dmadev *mtkd = dev_get_drvdata(dev);
+
+	if (!pm_runtime_suspended(dev)) {
+		ret = mtk_dma_clk_enable(mtkd);
+		if (ret) {
+			dev_info(dev, "fail to enable clk: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int mtk_dma_runtime_suspend(struct device *dev)
+{
+	struct mtk_dmadev *mtkd = dev_get_drvdata(dev);
+
+	mtk_dma_clk_disable(mtkd);
+
+	return 0;
+}
+
+static int mtk_dma_runtime_resume(struct device *dev)
+{
+	int ret;
+	struct mtk_dmadev *mtkd = dev_get_drvdata(dev);
+
+	ret = mtk_dma_clk_enable(mtkd);
+	if (ret) {
+		dev_warn(dev, "fail to enable clk: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops mtk_dma_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mtk_dma_suspend, mtk_dma_resume)
+	SET_RUNTIME_PM_OPS(mtk_dma_runtime_suspend,
+			   mtk_dma_runtime_resume, NULL)
+};
+
+static struct platform_driver mtk_dma_driver = {
+	.probe	= mtk_dma_probe,
+	.remove	= mtk_dma_remove,
+	.driver = {
+		.name		= "8250-mtk-dma",
+		.pm		= &mtk_dma_pm_ops,
+		.of_match_table = of_match_ptr(mtk_uart_dma_match),
+	},
+};
+
+static bool mtk_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	if (chan->device->dev->driver == &mtk_dma_driver.driver) {
+		struct mtk_dmadev *mtkd = to_mtk_dma_dev(chan->device);
+		struct mtk_chan *c = to_mtk_dma_chan(chan);
+		unsigned int req = *(unsigned int *)param;
+
+		if (req <= mtkd->dma_requests) {
+			c->dma_sig = req;
+			c->dma_ch = req;
+			return true;
+		}
+	}
+	return false;
+}
+
+static int mtk_dma_init(void)
+{
+	return platform_driver_register(&mtk_dma_driver);
+}
+subsys_initcall(mtk_dma_init);
+
+static void __exit mtk_dma_exit(void)
+{
+	platform_driver_unregister(&mtk_dma_driver);
+}
+module_exit(mtk_dma_exit);
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index dacf3f4..cfa1699 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -151,6 +151,17 @@ config DMA_JZ4780
 	  If you have a board based on such a SoC and wish to use DMA for
 	  devices which can use the DMA controller, say Y or M here.
 
+config DMA_MTK_UART
+	tristate "MediaTek SoCs APDMA support for UART"
+	depends on OF
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  Support for the UART DMA engine found on MediaTek MTK SoCs.
+	  when 8250 mtk uart is enabled, and if you want to using DMA,
+	  you can enable the config. the DMA engine just only be used
+	  with MediaTek Socs.
+
 config DMA_SA11X0
 	tristate "SA-11x0 DMA support"
 	depends on ARCH_SA1100 || COMPILE_TEST
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index c91702d..42690d8 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
 obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
 obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
 obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o
+obj-$(CONFIG_DMA_MTK_UART) += 8250_mtk_dma.o
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
 obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o
 obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 1/4] dt-bindings: dma: uart: add uart dma bindings
From: Long Cheng @ 2018-09-20  6:41 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Ed Blake, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen
In-Reply-To: <1537425673-18807-1-git-send-email-long.cheng@mediatek.com>

add uart dma bindings

Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
 .../devicetree/bindings/dma/8250_mtk_dma.txt       |   32 ++++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/8250_mtk_dma.txt

diff --git a/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt b/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
new file mode 100644
index 0000000..b140cf4
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
@@ -0,0 +1,32 @@
+* Mediatek UART APDMA Controller
+
+Required properties:
+- compatible should contain:
+  * "mediatek,mt2712-uart-dma" for MT2712 compatible APDMA
+  * "mediatek,mt6577-uart-dma" for MT6577 and all of the above
+
+- reg: The base address of the APDMA register bank.
+
+- interrupts: A single interrupt specifier.
+
+- clocks : Must contain an entry for each entry in clock-names.
+  See ../clocks/clock-bindings.txt for details.
+- clock-names: The APDMA clock for register accesses
+
+Examples:
+
+	apdma: dma-controller@11000380 {
+		compatible = "mediatek,mt2712-uart-dma";
+		reg = <0 0x11000380 0 0x400>;
+		interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 65 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 66 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 67 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 68 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 69 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 70 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&pericfg CLK_PERI_AP_DMA>;
+		clock-names = "apdma";
+		#dma-cells = <1>;
+	};
\ No newline at end of file
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 0/4] add uart DMA function
From: Long Cheng @ 2018-09-20  6:41 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Matthias Brugger, Dan Williams, Greg Kroah-Hartman, Jiri Slaby,
	Ed Blake, Long Cheng, dmaengine, devicetree, linux-arm-kernel,
	linux-mediatek, linux-kernel, linux-serial, srv_heupstream,
	Yingjoe Chen, YT Shen

In Mediatek SOCs, the uart can support DMA function.
Base on DMA engine formwork, we add the DMA code to support uart. And put the code under drivers/dma.

This series contains document bindings, Kconfig to control the function enable or not,
device tree including interrupt and dma device node, the code of UART DMA function.

Long Cheng (4):
  dt-bindings: dma: uart: add uart dma bindings
  dmaengine: mtk_uart_dma: add Mediatek uart DMA support
  serial: 8250-mtk: add uart DMA support
  arm: dts: mt2701: add uart APDMA to device tree

 .../devicetree/bindings/dma/8250_mtk_dma.txt       |   32 +
 arch/arm64/boot/dts/mediatek/mt2712e.dtsi          |   50 +
 drivers/dma/8250_mtk_dma.c                         | 1049 ++++++++++++++++++++
 drivers/dma/Kconfig                                |   11 +
 drivers/dma/Makefile                               |    1 +
 drivers/tty/serial/8250/8250_mtk.c                 |  211 +++-
 6 files changed, 1353 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
 create mode 100644 drivers/dma/8250_mtk_dma.c

-- 
1.7.9.5

^ permalink raw reply

* [PATCH -next] tty: serial: remove set but not used variable 'error'
From: YueHaibing @ 2018-09-20  1:59 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Benjamin Herrenschmidt,
	Paul Mackerras, Michael Ellerman
  Cc: YueHaibing, linux-serial, linuxppc-dev, kernel-janitors,
	linux-kernel

Fixes gcc '-Wunused-but-set-variable' warning:

drivers/tty/serial/pmac_zilog.c: In function 'pmz_receive_chars':
drivers/tty/serial/pmac_zilog.c:222:30: warning:
 variable 'error' set but not used [-Wunused-but-set-variable]

Signed-off-by: YueHaibing <yuehaibing@huawei.com>
---
 drivers/tty/serial/pmac_zilog.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index a4ec22d..a9d4098 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -219,7 +219,7 @@ static void pmz_interrupt_control(struct uart_pmac_port *uap, int enable)
 static bool pmz_receive_chars(struct uart_pmac_port *uap)
 {
 	struct tty_port *port;
-	unsigned char ch, r1, drop, error, flag;
+	unsigned char ch, r1, drop, flag;
 	int loops = 0;
 
 	/* Sanity check, make sure the old bug is no longer happening */
@@ -231,7 +231,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap)
 	port = &uap->port.state->port;
 
 	while (1) {
-		error = 0;
 		drop = 0;
 
 		r1 = read_zsreg(uap, R1);
@@ -273,7 +272,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap)
 		uap->port.icount.rx++;
 
 		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | BRK_ABRT)) {
-			error = 1;
 			if (r1 & BRK_ABRT) {
 				pmz_debug("pmz: got break !\n");
 				r1 &= ~(PAR_ERR | CRC_ERR);

^ permalink raw reply related

* [PATCH -next] serial: imx: remove set but not used variable 'rtsirq'
From: YueHaibing @ 2018-09-20  1:58 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: YueHaibing, linux-serial, kernel-janitors, linux-kernel

Fixes gcc '-Wunused-but-set-variable' warning:

drivers/tty/serial/imx.c: In function 'imx_uart_probe':
drivers/tty/serial/imx.c:2198:20: warning:
 variable 'rtsirq' set but not used [-Wunused-but-set-variable]

Signed-off-by: YueHaibing <yuehaibing@huawei.com>
---
 drivers/tty/serial/imx.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 4341589..1df7d23 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -2195,7 +2195,7 @@ static int imx_uart_probe(struct platform_device *pdev)
 	int ret = 0;
 	u32 ucr1;
 	struct resource *res;
-	int txirq, rxirq, rtsirq;
+	int txirq, rxirq;
 
 	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
 	if (!sport)
@@ -2220,7 +2220,6 @@ static int imx_uart_probe(struct platform_device *pdev)
 
 	rxirq = platform_get_irq(pdev, 0);
 	txirq = platform_get_irq(pdev, 1);
-	rtsirq = platform_get_irq(pdev, 2);
 
 	sport->port.dev = &pdev->dev;
 	sport->port.mapbase = res->start;

^ permalink raw reply related

* Re: [PATCH v8 0/4] gpiolib: speed up GPIO array processing
From: Linus Walleij @ 2018-09-19 18:08 UTC (permalink / raw)
  To: Janusz Krzysztofik
  Cc: Jonathan Corbet, Miguel Ojeda Sandonis, Peter Korsgaard,
	Peter Rosin, Ulf Hansson, Andrew Lunn, Florian Fainelli,
	David S. Miller, Dominik Brodowski, Greg KH, kishon,
	Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	Hartmut Knaack, Peter Meerwald, Jiri Slaby, Willy Tarreau,
	Geert Uytterhoeven, Sebastien Bourdelin
In-Reply-To: <CACRpkdbAG1N0v-BrXruf5088L0x3XwSHbFTWOFxjHQWx0LKz5g@mail.gmail.com>

On Thu, Sep 13, 2018 at 2:22 AM Linus Walleij <linus.walleij@linaro.org> wrote:
> On Wed, Sep 5, 2018 at 11:49 PM Janusz Krzysztofik <jmkrzyszt@gmail.com> wrote:
>
> > The goal is to boost performance of get/set array functions while
> > processing GPIO arrays which represent pins of a signle chip in
> > hardware order.  If resulting performance is close to PIO, GPIO API
> > can be used for data I/O without much loss of speed.
>
> I applied the v8 to an immutable branch and pushed to kernelorg
> so the build servers can churn it a bit, and if it works fine
> then we can merge this into the devel branch and also set up
> that as something other subsystems can pull in if they need it.
>
> I'm really excited to merge this!

The branch built with no problems, and now I merged this into
devel. If that also builds fine, I will let it hit linux-next so we can
stabilize it for v4.20.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH 3/3] serial: 8250_uniphier: add auto-flow-control support
From: Masahiro Yamada @ 2018-09-19  5:28 UTC (permalink / raw)
  To: Greg KH, linux-serial
  Cc: devicetree, Rob Herring, Dai Okamura, Masahiro Yamada, Jiri Slaby,
	linux-kernel, Mark Rutland, linux-arm-kernel
In-Reply-To: <1537334893-26079-1-git-send-email-yamada.masahiro@socionext.com>

From: Dai Okamura <okamura.dai@socionext.com>

Add selective auto-flow-control support for UniPhier serial driver.

Signed-off-by: Dai Okamura <okamura.dai@socionext.com>
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 Documentation/devicetree/bindings/serial/uniphier-uart.txt | 3 +++
 drivers/tty/serial/8250/8250_uniphier.c                    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/serial/uniphier-uart.txt b/Documentation/devicetree/bindings/serial/uniphier-uart.txt
index 811c479..7a1bf02 100644
--- a/Documentation/devicetree/bindings/serial/uniphier-uart.txt
+++ b/Documentation/devicetree/bindings/serial/uniphier-uart.txt
@@ -6,6 +6,9 @@ Required properties:
 - interrupts: a single interrupt specifier.
 - clocks: phandle to the input clock.
 
+Optional properties:
+-auto-flow-control: enable automatic flow control support.
+
 Example:
 	aliases {
 		serial0 = &serial0;
diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c
index 1028c02..164ba13 100644
--- a/drivers/tty/serial/8250/8250_uniphier.c
+++ b/drivers/tty/serial/8250/8250_uniphier.c
@@ -222,6 +222,9 @@ static int uniphier_uart_probe(struct platform_device *pdev)
 	up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE;
 	up.capabilities = UART_CAP_FIFO;
 
+	if (of_property_read_bool(dev->of_node, "auto-flow-control"))
+		up.capabilities |= UART_CAP_AFE;
+
 	up.port.serial_in = uniphier_serial_in;
 	up.port.serial_out = uniphier_serial_out;
 	up.dl_read = uniphier_serial_dl_read;
-- 
2.7.4

^ permalink raw reply related

* [PATCH 2/3] serial: 8250_uniphier: flatten probe function
From: Masahiro Yamada @ 2018-09-19  5:28 UTC (permalink / raw)
  To: Greg KH, linux-serial
  Cc: devicetree, Rob Herring, Dai Okamura, Masahiro Yamada, Jiri Slaby,
	linux-kernel, linux-arm-kernel
In-Reply-To: <1537334893-26079-1-git-send-email-yamada.masahiro@socionext.com>

Currently, the DT-related settings are split out to
uniphier_of_serial_setup(), but it turned out to be not nice.
The next commit will add a DT property, but it will not fit in
the helper.  Merge the helper into the probe function.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 drivers/tty/serial/8250/8250_uniphier.c | 49 ++++++++++++---------------------
 1 file changed, 17 insertions(+), 32 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c
index d292654..1028c02 100644
--- a/drivers/tty/serial/8250/8250_uniphier.c
+++ b/drivers/tty/serial/8250/8250_uniphier.c
@@ -155,36 +155,6 @@ static void uniphier_serial_dl_write(struct uart_8250_port *up, int value)
 	writel(value, up->port.membase + UNIPHIER_UART_DLR);
 }
 
-static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port,
-				    struct uniphier8250_priv *priv)
-{
-	int ret;
-	u32 prop;
-	struct device_node *np = dev->of_node;
-
-	ret = of_alias_get_id(np, "serial");
-	if (ret < 0) {
-		dev_err(dev, "failed to get alias id\n");
-		return ret;
-	}
-	port->line = ret;
-
-	/* Get clk rate through clk driver */
-	priv->clk = devm_clk_get(dev, NULL);
-	if (IS_ERR(priv->clk)) {
-		dev_err(dev, "failed to get clock\n");
-		return PTR_ERR(priv->clk);
-	}
-
-	ret = clk_prepare_enable(priv->clk);
-	if (ret < 0)
-		return ret;
-
-	port->uartclk = clk_get_rate(priv->clk);
-
-	return 0;
-}
-
 static int uniphier_uart_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -217,9 +187,24 @@ static int uniphier_uart_probe(struct platform_device *pdev)
 
 	memset(&up, 0, sizeof(up));
 
-	ret = uniphier_of_serial_setup(dev, &up.port, priv);
-	if (ret < 0)
+	ret = of_alias_get_id(dev->of_node, "serial");
+	if (ret < 0) {
+		dev_err(dev, "failed to get alias id\n");
 		return ret;
+	}
+	up.port.line = ret;
+
+	priv->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(dev, "failed to get clock\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		return ret;
+
+	up.port.uartclk = clk_get_rate(priv->clk);
 
 	spin_lock_init(&priv->atomic_write_lock);
 
-- 
2.7.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox