From: Vinod Koul <vinod.koul@intel.com>
To: Long Cheng <long.cheng@mediatek.com>
Cc: Mark Rutland <mark.rutland@arm.com>,
devicetree@vger.kernel.org, srv_heupstream@mediatek.com,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Russell King <linux@armlinux.org.uk>,
linux-kernel@vger.kernel.org, dmaengine@vger.kernel.org,
Rob Herring <robh+dt@kernel.org>,
linux-mediatek@lists.infradead.org, linux-serial@vger.kernel.org,
Jiri Slaby <jslaby@suse.com>,
Matthias Brugger <matthias.bgg@gmail.com>,
Dan Williams <dan.j.williams@intel.com>,
linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH 2/4] dmaengine: mtk_uart_dma: add Mediatek uart DMA support
Date: Tue, 7 Mar 2017 14:22:53 +0530 [thread overview]
Message-ID: <20170307085253.GH2843@localhost> (raw)
In-Reply-To: <1487243251-964-3-git-send-email-long.cheng@mediatek.com>
On Thu, Feb 16, 2017 at 07:07:29PM +0800, Long Cheng wrote:
> In DMA engine framework, add 8250 mtk dma to support it.
Can you please describe the controller here
> +/*
> + * MTK DMAengine support
> + *
> + * Copyright (c) 2016 MediaTek Inc.
we are in 2017 now
> +#define VFF_INT_FLAG_CLR_B 0
> +/*VFF_INT_EN*/
> +#define VFF_RX_INT_EN0_B BIT(0)
> +#define VFF_RX_INT_EN1_B BIT(1)
> +#define VFF_TX_INT_EN_B BIT(0)
> +#define VFF_INT_EN_CLR_B 0
empty line after each logical bloock helps in readablity
> +/*VFF_RST*/
> +#define VFF_WARM_RST_B BIT(0)
> +/*VFF_EN*/
> +#define VFF_EN_B BIT(0)
> +/*VFF_STOP*/
> +#define VFF_STOP_B BIT(0)
> +#define VFF_STOP_CLR_B 0
> +/*VFF_FLUSH*/
> +#define VFF_FLUSH_B BIT(0)
> +#define VFF_FLUSH_CLR_B 0
please align all these
> +
> +#define VFF_TX_THRE(n) ((n)*7/8)
> +#define VFF_RX_THRE(n) ((n)*3/4)
can you describe this
> +static bool mtk_dma_filter_fn(struct dma_chan *chan, void *param);
is there a reason for this, we can move the filer fn here!
> +static int 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);
> + struct timespec a, b;
> + int txcount = c->remain_size;
> + unsigned int tx_size = c->cfg.dst_addr_width*1024;
> + unsigned int len, left;
> + unsigned int wpt;
> + ktime_t begin, end;
> +
> + if (atomic_inc_and_test(&c->entry) > 1) {
why are we using atomic variables
> + 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 {
> + while (mtk_dma_chan_read(c, VFF_LEFT_SIZE) >= c->trig) {
> + left = tx_size - mtk_dma_chan_read(c, VFF_VALID_SIZE);
> + left = (left > 16) ? (left - 16) : (0);
> + len = left < c->remain_size ? left : c->remain_size;
?? can you explain this
> +
> + begin = ktime_get();
> + a = ktime_to_timespec(begin);
more alarm bells now!
> + while (len--) {
> + /*
> + * DMA limitation.
> + * Workaround: Polling flush bit to zero,
> + * set 1s timeout
> + */
1sec is ***VERY*** large, does the device recommend that?
> + while (mtk_dma_chan_read(c, VFF_FLUSH)) {
> + end = ktime_get();
> + b = ktime_to_timespec(end);
> + if ((b.tv_sec - a.tv_sec) > 1 ||
> + ((b.tv_sec - a.tv_sec) == 1 &&
> + b.tv_nsec > a.tv_nsec)) {
> + dev_info(chan->device->dev,
> + "[UART] Polling flush timeout\n");
> + return 0;
> + }
> + }
No this is not how you implement a time out. Hint use jiffes and count them.
> +
> + wpt = mtk_dma_chan_read(c, VFF_WPT);
> +
> + if ((wpt & 0x0000ffffl) ==
magic number?
> +static void mtk_dma_start_tx(struct mtk_chan *c)
> +{
> + if (mtk_dma_chan_read(c, VFF_LEFT_SIZE) == 0) {
> + dev_info(c->vc.chan.device->dev, "%s maybe need fix? %d @L %d\n",
> + __func__, mtk_dma_chan_read(c, VFF_LEFT_SIZE),
> + __LINE__);
> + mtk_dma_chan_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
> + } else {
> + reinit_completion(&c->done);
> + atomic_inc(&c->loopcnt);
> + atomic_inc(&c->loopcnt);
why would you increment twice
> +static void mtk_dma_reset(struct mtk_chan *c)
> +{
> + struct timespec a, b;
> + ktime_t begin, end;
> +
> + 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);
> +
> + begin = ktime_get();
> + a = ktime_to_timespec(begin);
> + while (mtk_dma_chan_read(c, VFF_EN)) {
> + end = ktime_get();
> + b = ktime_to_timespec(end);
> + if ((b.tv_sec - a.tv_sec) > 1 ||
> + ((b.tv_sec - a.tv_sec) == 1 &&
> + b.tv_nsec > a.tv_nsec)) {
> + dev_info(c->vc.chan.device->dev,
> + "[UART] Polling VFF EN timeout\n");
> + return;
> + }
and here we go again
> +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)) {
> + polling_cnt++;
> + if (polling_cnt > MTK_POLLING_CNT) {
> + dev_info(c->vc.chan.device->dev, "mtk_uart_stop_dma: polling VFF_FLUSH fail VFF_DEBUG_STATUS=0x%x\n",
126 chars, surely that must be a record!
> + 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 > MTK_POLLING_CNT) {
> + dev_info(c->vc.chan.device->dev, "mtk_uart_stop_dma: polling VFF_EN fail VFF_DEBUG_STATUS=0x%x\n",
and here
> + 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);
> + mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_INT_FLAG_CLR_B);
> +
> + c->paused = true;
> +}
> +
> +/*
> + * 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.
yeah sounds good to me :)
> +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;
> +
> + ret = -EBUSY;
> +
> + if (mtkd->lch_map[c->dma_ch] == NULL) {
> + c->channel_base = mtkd->base + 0x80 * c->dma_ch;
this should be probe thingy, why are we doing this here?
> +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) & 0x0000ffffl;
magic!!
> + txstate->residue = c->rx_ptr;
> + } else if (ret == DMA_COMPLETE && c->cfg.direction == DMA_DEV_TO_MEM) {
> + txstate->residue = c->remain_size;
this seems incorrect, if it is complete then why residue?
> + } else {
> + txstate->residue = 0;
which is a no-op
> +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);
> + enum dma_slave_buswidth dev_width;
> + struct scatterlist *sgent;
> + struct mtk_desc *d;
> + dma_addr_t dev_addr;
> + unsigned int i, j, en, frame_bytes;
> +
> + en = frame_bytes = 1;
> +
> + if (dir == DMA_DEV_TO_MEM) {
> + dev_addr = c->cfg.src_addr;
> + dev_width = c->cfg.src_addr_width;
> + } else if (dir == DMA_MEM_TO_DEV) {
> + dev_addr = c->cfg.dst_addr;
> + dev_width = c->cfg.dst_addr_width;
> + } else {
> + dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
> + return NULL;
> + }
> +
> + /* Now allocate and setup the descriptor. */
> + d = kmalloc(sizeof(*d) + sglen * sizeof(d->sg[0]),
> + GFP_KERNEL | ___GFP_ZERO);
why ___GFP_ZERO? why not GFP_NOATOMIC?
> +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;
> +
> + memcpy(&c->cfg, cfg, sizeof(c->cfg));
> +
> + if (cfg->direction == DMA_DEV_TO_MEM) {
> + mtk_dma_chan_write(c, VFF_ADDR, cfg->src_addr);
> + mtk_dma_chan_write(c, VFF_LEN, cfg->src_addr_width*1024);
> + mtk_dma_chan_write(c, VFF_THRE,
> + VFF_RX_THRE(cfg->src_addr_width*1024));
> + 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_INT_FLAG_CLR_B);
> + mtk_dma_chan_write(c, VFF_EN, VFF_EN_B);
this is wrong, you dont program channel here, but upon the descriptor issue.
The values should be stored locally and used to prepare.
> +static int mtk_dma_pause(struct dma_chan *chan)
> +{
> + /* Pause/Resume only allowed with cyclic mode */
> + return -EINVAL;
> +}
then remove it
> + 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_pause;
> + mtkd->ddev.device_resume = mtk_dma_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);
1bytes width, are you sure about that?
> +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);
platform_driver_register() pls
--
~Vinod
WARNING: multiple messages have this Message-ID (diff)
From: vinod.koul@intel.com (Vinod Koul)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/4] dmaengine: mtk_uart_dma: add Mediatek uart DMA support
Date: Tue, 7 Mar 2017 14:22:53 +0530 [thread overview]
Message-ID: <20170307085253.GH2843@localhost> (raw)
In-Reply-To: <1487243251-964-3-git-send-email-long.cheng@mediatek.com>
On Thu, Feb 16, 2017 at 07:07:29PM +0800, Long Cheng wrote:
> In DMA engine framework, add 8250 mtk dma to support it.
Can you please describe the controller here
> +/*
> + * MTK DMAengine support
> + *
> + * Copyright (c) 2016 MediaTek Inc.
we are in 2017 now
> +#define VFF_INT_FLAG_CLR_B 0
> +/*VFF_INT_EN*/
> +#define VFF_RX_INT_EN0_B BIT(0)
> +#define VFF_RX_INT_EN1_B BIT(1)
> +#define VFF_TX_INT_EN_B BIT(0)
> +#define VFF_INT_EN_CLR_B 0
empty line after each logical bloock helps in readablity
> +/*VFF_RST*/
> +#define VFF_WARM_RST_B BIT(0)
> +/*VFF_EN*/
> +#define VFF_EN_B BIT(0)
> +/*VFF_STOP*/
> +#define VFF_STOP_B BIT(0)
> +#define VFF_STOP_CLR_B 0
> +/*VFF_FLUSH*/
> +#define VFF_FLUSH_B BIT(0)
> +#define VFF_FLUSH_CLR_B 0
please align all these
> +
> +#define VFF_TX_THRE(n) ((n)*7/8)
> +#define VFF_RX_THRE(n) ((n)*3/4)
can you describe this
> +static bool mtk_dma_filter_fn(struct dma_chan *chan, void *param);
is there a reason for this, we can move the filer fn here!
> +static int 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);
> + struct timespec a, b;
> + int txcount = c->remain_size;
> + unsigned int tx_size = c->cfg.dst_addr_width*1024;
> + unsigned int len, left;
> + unsigned int wpt;
> + ktime_t begin, end;
> +
> + if (atomic_inc_and_test(&c->entry) > 1) {
why are we using atomic variables
> + 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 {
> + while (mtk_dma_chan_read(c, VFF_LEFT_SIZE) >= c->trig) {
> + left = tx_size - mtk_dma_chan_read(c, VFF_VALID_SIZE);
> + left = (left > 16) ? (left - 16) : (0);
> + len = left < c->remain_size ? left : c->remain_size;
?? can you explain this
> +
> + begin = ktime_get();
> + a = ktime_to_timespec(begin);
more alarm bells now!
> + while (len--) {
> + /*
> + * DMA limitation.
> + * Workaround: Polling flush bit to zero,
> + * set 1s timeout
> + */
1sec is ***VERY*** large, does the device recommend that?
> + while (mtk_dma_chan_read(c, VFF_FLUSH)) {
> + end = ktime_get();
> + b = ktime_to_timespec(end);
> + if ((b.tv_sec - a.tv_sec) > 1 ||
> + ((b.tv_sec - a.tv_sec) == 1 &&
> + b.tv_nsec > a.tv_nsec)) {
> + dev_info(chan->device->dev,
> + "[UART] Polling flush timeout\n");
> + return 0;
> + }
> + }
No this is not how you implement a time out. Hint use jiffes and count them.
> +
> + wpt = mtk_dma_chan_read(c, VFF_WPT);
> +
> + if ((wpt & 0x0000ffffl) ==
magic number?
> +static void mtk_dma_start_tx(struct mtk_chan *c)
> +{
> + if (mtk_dma_chan_read(c, VFF_LEFT_SIZE) == 0) {
> + dev_info(c->vc.chan.device->dev, "%s maybe need fix? %d @L %d\n",
> + __func__, mtk_dma_chan_read(c, VFF_LEFT_SIZE),
> + __LINE__);
> + mtk_dma_chan_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
> + } else {
> + reinit_completion(&c->done);
> + atomic_inc(&c->loopcnt);
> + atomic_inc(&c->loopcnt);
why would you increment twice
> +static void mtk_dma_reset(struct mtk_chan *c)
> +{
> + struct timespec a, b;
> + ktime_t begin, end;
> +
> + 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);
> +
> + begin = ktime_get();
> + a = ktime_to_timespec(begin);
> + while (mtk_dma_chan_read(c, VFF_EN)) {
> + end = ktime_get();
> + b = ktime_to_timespec(end);
> + if ((b.tv_sec - a.tv_sec) > 1 ||
> + ((b.tv_sec - a.tv_sec) == 1 &&
> + b.tv_nsec > a.tv_nsec)) {
> + dev_info(c->vc.chan.device->dev,
> + "[UART] Polling VFF EN timeout\n");
> + return;
> + }
and here we go again
> +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)) {
> + polling_cnt++;
> + if (polling_cnt > MTK_POLLING_CNT) {
> + dev_info(c->vc.chan.device->dev, "mtk_uart_stop_dma: polling VFF_FLUSH fail VFF_DEBUG_STATUS=0x%x\n",
126 chars, surely that must be a record!
> + 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 > MTK_POLLING_CNT) {
> + dev_info(c->vc.chan.device->dev, "mtk_uart_stop_dma: polling VFF_EN fail VFF_DEBUG_STATUS=0x%x\n",
and here
> + 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);
> + mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_INT_FLAG_CLR_B);
> +
> + c->paused = true;
> +}
> +
> +/*
> + * 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.
yeah sounds good to me :)
> +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;
> +
> + ret = -EBUSY;
> +
> + if (mtkd->lch_map[c->dma_ch] == NULL) {
> + c->channel_base = mtkd->base + 0x80 * c->dma_ch;
this should be probe thingy, why are we doing this here?
> +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) & 0x0000ffffl;
magic!!
> + txstate->residue = c->rx_ptr;
> + } else if (ret == DMA_COMPLETE && c->cfg.direction == DMA_DEV_TO_MEM) {
> + txstate->residue = c->remain_size;
this seems incorrect, if it is complete then why residue?
> + } else {
> + txstate->residue = 0;
which is a no-op
> +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);
> + enum dma_slave_buswidth dev_width;
> + struct scatterlist *sgent;
> + struct mtk_desc *d;
> + dma_addr_t dev_addr;
> + unsigned int i, j, en, frame_bytes;
> +
> + en = frame_bytes = 1;
> +
> + if (dir == DMA_DEV_TO_MEM) {
> + dev_addr = c->cfg.src_addr;
> + dev_width = c->cfg.src_addr_width;
> + } else if (dir == DMA_MEM_TO_DEV) {
> + dev_addr = c->cfg.dst_addr;
> + dev_width = c->cfg.dst_addr_width;
> + } else {
> + dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
> + return NULL;
> + }
> +
> + /* Now allocate and setup the descriptor. */
> + d = kmalloc(sizeof(*d) + sglen * sizeof(d->sg[0]),
> + GFP_KERNEL | ___GFP_ZERO);
why ___GFP_ZERO? why not GFP_NOATOMIC?
> +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;
> +
> + memcpy(&c->cfg, cfg, sizeof(c->cfg));
> +
> + if (cfg->direction == DMA_DEV_TO_MEM) {
> + mtk_dma_chan_write(c, VFF_ADDR, cfg->src_addr);
> + mtk_dma_chan_write(c, VFF_LEN, cfg->src_addr_width*1024);
> + mtk_dma_chan_write(c, VFF_THRE,
> + VFF_RX_THRE(cfg->src_addr_width*1024));
> + 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_INT_FLAG_CLR_B);
> + mtk_dma_chan_write(c, VFF_EN, VFF_EN_B);
this is wrong, you dont program channel here, but upon the descriptor issue.
The values should be stored locally and used to prepare.
> +static int mtk_dma_pause(struct dma_chan *chan)
> +{
> + /* Pause/Resume only allowed with cyclic mode */
> + return -EINVAL;
> +}
then remove it
> + 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_pause;
> + mtkd->ddev.device_resume = mtk_dma_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);
1bytes width, are you sure about that?
> +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);
platform_driver_register() pls
--
~Vinod
WARNING: multiple messages have this Message-ID (diff)
From: Vinod Koul <vinod.koul@intel.com>
To: Long Cheng <long.cheng@mediatek.com>
Cc: Rob Herring <robh+dt@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
Matthias Brugger <matthias.bgg@gmail.com>,
Russell King <linux@armlinux.org.uk>,
Dan Williams <dan.j.williams@intel.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jiri Slaby <jslaby@suse.com>,
dmaengine@vger.kernel.org, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org,
linux-serial@vger.kernel.org, srv_heupstream@mediatek.com
Subject: Re: [PATCH 2/4] dmaengine: mtk_uart_dma: add Mediatek uart DMA support
Date: Tue, 7 Mar 2017 14:22:53 +0530 [thread overview]
Message-ID: <20170307085253.GH2843@localhost> (raw)
In-Reply-To: <1487243251-964-3-git-send-email-long.cheng@mediatek.com>
On Thu, Feb 16, 2017 at 07:07:29PM +0800, Long Cheng wrote:
> In DMA engine framework, add 8250 mtk dma to support it.
Can you please describe the controller here
> +/*
> + * MTK DMAengine support
> + *
> + * Copyright (c) 2016 MediaTek Inc.
we are in 2017 now
> +#define VFF_INT_FLAG_CLR_B 0
> +/*VFF_INT_EN*/
> +#define VFF_RX_INT_EN0_B BIT(0)
> +#define VFF_RX_INT_EN1_B BIT(1)
> +#define VFF_TX_INT_EN_B BIT(0)
> +#define VFF_INT_EN_CLR_B 0
empty line after each logical bloock helps in readablity
> +/*VFF_RST*/
> +#define VFF_WARM_RST_B BIT(0)
> +/*VFF_EN*/
> +#define VFF_EN_B BIT(0)
> +/*VFF_STOP*/
> +#define VFF_STOP_B BIT(0)
> +#define VFF_STOP_CLR_B 0
> +/*VFF_FLUSH*/
> +#define VFF_FLUSH_B BIT(0)
> +#define VFF_FLUSH_CLR_B 0
please align all these
> +
> +#define VFF_TX_THRE(n) ((n)*7/8)
> +#define VFF_RX_THRE(n) ((n)*3/4)
can you describe this
> +static bool mtk_dma_filter_fn(struct dma_chan *chan, void *param);
is there a reason for this, we can move the filer fn here!
> +static int 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);
> + struct timespec a, b;
> + int txcount = c->remain_size;
> + unsigned int tx_size = c->cfg.dst_addr_width*1024;
> + unsigned int len, left;
> + unsigned int wpt;
> + ktime_t begin, end;
> +
> + if (atomic_inc_and_test(&c->entry) > 1) {
why are we using atomic variables
> + 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 {
> + while (mtk_dma_chan_read(c, VFF_LEFT_SIZE) >= c->trig) {
> + left = tx_size - mtk_dma_chan_read(c, VFF_VALID_SIZE);
> + left = (left > 16) ? (left - 16) : (0);
> + len = left < c->remain_size ? left : c->remain_size;
?? can you explain this
> +
> + begin = ktime_get();
> + a = ktime_to_timespec(begin);
more alarm bells now!
> + while (len--) {
> + /*
> + * DMA limitation.
> + * Workaround: Polling flush bit to zero,
> + * set 1s timeout
> + */
1sec is ***VERY*** large, does the device recommend that?
> + while (mtk_dma_chan_read(c, VFF_FLUSH)) {
> + end = ktime_get();
> + b = ktime_to_timespec(end);
> + if ((b.tv_sec - a.tv_sec) > 1 ||
> + ((b.tv_sec - a.tv_sec) == 1 &&
> + b.tv_nsec > a.tv_nsec)) {
> + dev_info(chan->device->dev,
> + "[UART] Polling flush timeout\n");
> + return 0;
> + }
> + }
No this is not how you implement a time out. Hint use jiffes and count them.
> +
> + wpt = mtk_dma_chan_read(c, VFF_WPT);
> +
> + if ((wpt & 0x0000ffffl) ==
magic number?
> +static void mtk_dma_start_tx(struct mtk_chan *c)
> +{
> + if (mtk_dma_chan_read(c, VFF_LEFT_SIZE) == 0) {
> + dev_info(c->vc.chan.device->dev, "%s maybe need fix? %d @L %d\n",
> + __func__, mtk_dma_chan_read(c, VFF_LEFT_SIZE),
> + __LINE__);
> + mtk_dma_chan_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
> + } else {
> + reinit_completion(&c->done);
> + atomic_inc(&c->loopcnt);
> + atomic_inc(&c->loopcnt);
why would you increment twice
> +static void mtk_dma_reset(struct mtk_chan *c)
> +{
> + struct timespec a, b;
> + ktime_t begin, end;
> +
> + 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);
> +
> + begin = ktime_get();
> + a = ktime_to_timespec(begin);
> + while (mtk_dma_chan_read(c, VFF_EN)) {
> + end = ktime_get();
> + b = ktime_to_timespec(end);
> + if ((b.tv_sec - a.tv_sec) > 1 ||
> + ((b.tv_sec - a.tv_sec) == 1 &&
> + b.tv_nsec > a.tv_nsec)) {
> + dev_info(c->vc.chan.device->dev,
> + "[UART] Polling VFF EN timeout\n");
> + return;
> + }
and here we go again
> +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)) {
> + polling_cnt++;
> + if (polling_cnt > MTK_POLLING_CNT) {
> + dev_info(c->vc.chan.device->dev, "mtk_uart_stop_dma: polling VFF_FLUSH fail VFF_DEBUG_STATUS=0x%x\n",
126 chars, surely that must be a record!
> + 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 > MTK_POLLING_CNT) {
> + dev_info(c->vc.chan.device->dev, "mtk_uart_stop_dma: polling VFF_EN fail VFF_DEBUG_STATUS=0x%x\n",
and here
> + 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);
> + mtk_dma_chan_write(c, VFF_INT_FLAG, VFF_INT_FLAG_CLR_B);
> +
> + c->paused = true;
> +}
> +
> +/*
> + * 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.
yeah sounds good to me :)
> +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;
> +
> + ret = -EBUSY;
> +
> + if (mtkd->lch_map[c->dma_ch] == NULL) {
> + c->channel_base = mtkd->base + 0x80 * c->dma_ch;
this should be probe thingy, why are we doing this here?
> +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) & 0x0000ffffl;
magic!!
> + txstate->residue = c->rx_ptr;
> + } else if (ret == DMA_COMPLETE && c->cfg.direction == DMA_DEV_TO_MEM) {
> + txstate->residue = c->remain_size;
this seems incorrect, if it is complete then why residue?
> + } else {
> + txstate->residue = 0;
which is a no-op
> +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);
> + enum dma_slave_buswidth dev_width;
> + struct scatterlist *sgent;
> + struct mtk_desc *d;
> + dma_addr_t dev_addr;
> + unsigned int i, j, en, frame_bytes;
> +
> + en = frame_bytes = 1;
> +
> + if (dir == DMA_DEV_TO_MEM) {
> + dev_addr = c->cfg.src_addr;
> + dev_width = c->cfg.src_addr_width;
> + } else if (dir == DMA_MEM_TO_DEV) {
> + dev_addr = c->cfg.dst_addr;
> + dev_width = c->cfg.dst_addr_width;
> + } else {
> + dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
> + return NULL;
> + }
> +
> + /* Now allocate and setup the descriptor. */
> + d = kmalloc(sizeof(*d) + sglen * sizeof(d->sg[0]),
> + GFP_KERNEL | ___GFP_ZERO);
why ___GFP_ZERO? why not GFP_NOATOMIC?
> +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;
> +
> + memcpy(&c->cfg, cfg, sizeof(c->cfg));
> +
> + if (cfg->direction == DMA_DEV_TO_MEM) {
> + mtk_dma_chan_write(c, VFF_ADDR, cfg->src_addr);
> + mtk_dma_chan_write(c, VFF_LEN, cfg->src_addr_width*1024);
> + mtk_dma_chan_write(c, VFF_THRE,
> + VFF_RX_THRE(cfg->src_addr_width*1024));
> + 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_INT_FLAG_CLR_B);
> + mtk_dma_chan_write(c, VFF_EN, VFF_EN_B);
this is wrong, you dont program channel here, but upon the descriptor issue.
The values should be stored locally and used to prepare.
> +static int mtk_dma_pause(struct dma_chan *chan)
> +{
> + /* Pause/Resume only allowed with cyclic mode */
> + return -EINVAL;
> +}
then remove it
> + 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_pause;
> + mtkd->ddev.device_resume = mtk_dma_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);
1bytes width, are you sure about that?
> +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);
platform_driver_register() pls
--
~Vinod
next prev parent reply other threads:[~2017-03-07 8:52 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-02-16 11:07 [PATCH 0/4] add uart DMA function Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-02-16 11:07 ` [PATCH 1/4] dt-bindings: dma: uart: add uart dma bindings Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-02-27 15:22 ` Rob Herring
2017-02-27 15:22 ` Rob Herring
2017-02-27 15:22 ` Rob Herring
2017-02-16 11:07 ` [PATCH 3/4] serial: 8250-mtk: add uart DMA support Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-02-16 11:07 ` Long Cheng
[not found] ` <1487243251-964-4-git-send-email-long.cheng-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2017-02-16 13:23 ` Arnd Bergmann
2017-02-16 13:23 ` Arnd Bergmann
2017-02-16 13:23 ` Arnd Bergmann
[not found] ` <1487243251-964-1-git-send-email-long.cheng-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2017-02-16 11:07 ` [PATCH 2/4] dmaengine: mtk_uart_dma: add Mediatek " Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-02-16 12:44 ` kbuild test robot
2017-02-16 12:44 ` kbuild test robot
2017-02-16 12:44 ` kbuild test robot
2017-03-07 8:52 ` Vinod Koul [this message]
2017-03-07 8:52 ` Vinod Koul
2017-03-07 8:52 ` Vinod Koul
2017-02-16 11:07 ` [PATCH 4/4] arm: dts: mt2701: add uart APDMA to device tree Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-02-16 11:07 ` Long Cheng
2017-03-07 8:53 ` [PATCH 0/4] add uart DMA function Vinod Koul
2017-03-07 8:53 ` Vinod Koul
2017-03-07 8:53 ` Vinod Koul
-- strict thread matches above, loose matches on Subject: below --
2018-09-20 6:41 Long Cheng
2018-09-20 6:41 ` [PATCH 2/4] dmaengine: mtk_uart_dma: add Mediatek uart DMA support Long Cheng
2018-09-20 6:41 ` Long Cheng
2018-09-20 6:41 ` Long Cheng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170307085253.GH2843@localhost \
--to=vinod.koul@intel.com \
--cc=dan.j.williams@intel.com \
--cc=devicetree@vger.kernel.org \
--cc=dmaengine@vger.kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=jslaby@suse.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-serial@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=long.cheng@mediatek.com \
--cc=mark.rutland@arm.com \
--cc=matthias.bgg@gmail.com \
--cc=robh+dt@kernel.org \
--cc=srv_heupstream@mediatek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.