* [PATCH v11 06/14] dmaengine: st_fdma: Add STMicroelectronics FDMA engine driver support
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
This patch adds support for the Flexible Direct Memory Access (FDMA) core
driver. The FDMA is a slim core CPU with a dedicated firmware.
It is a general purpose DMA controller capable of supporting 16
independent DMA channels. Data moves maybe from memory to memory
or between memory and paced latency critical real time targets and it
is found on al STi based chipsets.
Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
drivers/dma/Kconfig | 13 +
drivers/dma/Makefile | 1 +
drivers/dma/st_fdma.c | 899 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 913 insertions(+)
create mode 100644 drivers/dma/st_fdma.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index af63a6b..661f217 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -435,6 +435,19 @@ config STE_DMA40
help
Support for ST-Ericsson DMA40 controller
+config ST_FDMA
+ tristate "ST FDMA dmaengine support"
+ depends on ARCH_STI
+ select ST_SLIM_REMOTEPROC
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Enable support for ST FDMA controller.
+ It supports 16 independent DMA channels, accepts up to 32 DMA requests
+
+ Say Y here if you have such a chipset.
+ If unsure, say N.
+
config STM32_DMA
bool "STMicroelectronics STM32 DMA support"
depends on ARCH_STM32 || COMPILE_TEST
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index e4dc9ca..a4fa336 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-crossbar.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
+obj-$(CONFIG_ST_FDMA) += st_fdma.o
obj-y += qcom/
obj-y += xilinx/
diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c
new file mode 100644
index 0000000..515e1d4
--- /dev/null
+++ b/drivers/dma/st_fdma.c
@@ -0,0 +1,899 @@
+/*
+ * DMA driver for STMicroelectronics STi FDMA controller
+ *
+ * Copyright (C) 2014 STMicroelectronics
+ *
+ * Author: Ludovic Barre <Ludovic.barre@st.com>
+ * Peter Griffin <peter.griffin@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/remoteproc.h>
+
+#include "st_fdma.h"
+
+static inline struct st_fdma_chan *to_st_fdma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct st_fdma_chan, vchan.chan);
+}
+
+static struct st_fdma_desc *to_st_fdma_desc(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct st_fdma_desc, vdesc);
+}
+
+static int st_fdma_dreq_get(struct st_fdma_chan *fchan)
+{
+ struct st_fdma_dev *fdev = fchan->fdev;
+ u32 req_line_cfg = fchan->cfg.req_line;
+ u32 dreq_line;
+ int try = 0;
+
+ /*
+ * dreq_mask is shared for n channels of fdma, so all accesses must be
+ * atomic. if the dreq_mask is changed between ffz and set_bit,
+ * we retry
+ */
+ do {
+ if (fdev->dreq_mask == ~0L) {
+ dev_err(fdev->dev, "No req lines available\n");
+ return -EINVAL;
+ }
+
+ if (try || req_line_cfg >= ST_FDMA_NR_DREQS) {
+ dev_err(fdev->dev, "Invalid or used req line\n");
+ return -EINVAL;
+ } else {
+ dreq_line = req_line_cfg;
+ }
+
+ try++;
+ } while (test_and_set_bit(dreq_line, &fdev->dreq_mask));
+
+ dev_dbg(fdev->dev, "get dreq_line:%d mask:%#lx\n",
+ dreq_line, fdev->dreq_mask);
+
+ return dreq_line;
+}
+
+static void st_fdma_dreq_put(struct st_fdma_chan *fchan)
+{
+ struct st_fdma_dev *fdev = fchan->fdev;
+
+ dev_dbg(fdev->dev, "put dreq_line:%#x\n", fchan->dreq_line);
+ clear_bit(fchan->dreq_line, &fdev->dreq_mask);
+}
+
+static void st_fdma_xfer_desc(struct st_fdma_chan *fchan)
+{
+ struct virt_dma_desc *vdesc;
+ unsigned long nbytes, ch_cmd, cmd;
+
+ vdesc = vchan_next_desc(&fchan->vchan);
+ if (!vdesc)
+ return;
+
+ fchan->fdesc = to_st_fdma_desc(vdesc);
+ nbytes = fchan->fdesc->node[0].desc->nbytes;
+ cmd = FDMA_CMD_START(fchan->vchan.chan.chan_id);
+ ch_cmd = fchan->fdesc->node[0].pdesc | FDMA_CH_CMD_STA_START;
+
+ /* start the channel for the descriptor */
+ fnode_write(fchan, nbytes, FDMA_CNTN_OFST);
+ fchan_write(fchan, ch_cmd, FDMA_CH_CMD_OFST);
+ writel(cmd,
+ fchan->fdev->slim_rproc->peri + FDMA_CMD_SET_OFST);
+
+ dev_dbg(fchan->fdev->dev, "start chan:%d\n", fchan->vchan.chan.chan_id);
+}
+
+static void st_fdma_ch_sta_update(struct st_fdma_chan *fchan,
+ unsigned long int_sta)
+{
+ unsigned long ch_sta, ch_err;
+ int ch_id = fchan->vchan.chan.chan_id;
+ struct st_fdma_dev *fdev = fchan->fdev;
+
+ ch_sta = fchan_read(fchan, FDMA_CH_CMD_OFST);
+ ch_err = ch_sta & FDMA_CH_CMD_ERR_MASK;
+ ch_sta &= FDMA_CH_CMD_STA_MASK;
+
+ if (int_sta & FDMA_INT_STA_ERR) {
+ dev_warn(fdev->dev, "chan:%d, error:%ld\n", ch_id, ch_err);
+ fchan->status = DMA_ERROR;
+ return;
+ }
+
+ switch (ch_sta) {
+ case FDMA_CH_CMD_STA_PAUSED:
+ fchan->status = DMA_PAUSED;
+ break;
+
+ case FDMA_CH_CMD_STA_RUNNING:
+ fchan->status = DMA_IN_PROGRESS;
+ break;
+ }
+}
+
+static irqreturn_t st_fdma_irq_handler(int irq, void *dev_id)
+{
+ struct st_fdma_dev *fdev = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ struct st_fdma_chan *fchan = &fdev->chans[0];
+ unsigned long int_sta, clr;
+
+ int_sta = fdma_read(fdev, FDMA_INT_STA_OFST);
+ clr = int_sta;
+
+ for (; int_sta != 0 ; int_sta >>= 2, fchan++) {
+ if (!(int_sta & (FDMA_INT_STA_CH | FDMA_INT_STA_ERR)))
+ continue;
+
+ spin_lock(&fchan->vchan.lock);
+ st_fdma_ch_sta_update(fchan, int_sta);
+
+ if (fchan->fdesc) {
+ if (!fchan->fdesc->iscyclic) {
+ list_del(&fchan->fdesc->vdesc.node);
+ vchan_cookie_complete(&fchan->fdesc->vdesc);
+ fchan->fdesc = NULL;
+ fchan->status = DMA_COMPLETE;
+ } else {
+ vchan_cyclic_callback(&fchan->fdesc->vdesc);
+ }
+
+ /* Start the next descriptor (if available) */
+ if (!fchan->fdesc)
+ st_fdma_xfer_desc(fchan);
+ }
+
+ spin_unlock(&fchan->vchan.lock);
+ ret = IRQ_HANDLED;
+ }
+
+ fdma_write(fdev, clr, FDMA_INT_CLR_OFST);
+
+ return ret;
+}
+
+static struct dma_chan *st_fdma_of_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct st_fdma_dev *fdev = ofdma->of_dma_data;
+ struct dma_chan *chan;
+ struct st_fdma_chan *fchan;
+ int ret;
+
+ if (dma_spec->args_count < 1)
+ return ERR_PTR(-EINVAL);
+
+ if (fdev->dma_device.dev->of_node != dma_spec->np)
+ return ERR_PTR(-EINVAL);
+
+ ret = rproc_boot(fdev->slim_rproc->rproc);
+ if (ret == -ENOENT)
+ return ERR_PTR(-EPROBE_DEFER);
+ else if (ret)
+ return ERR_PTR(ret);
+
+ chan = dma_get_any_slave_channel(&fdev->dma_device);
+ if (!chan)
+ goto err_chan;
+
+ fchan = to_st_fdma_chan(chan);
+
+ fchan->cfg.of_node = dma_spec->np;
+ fchan->cfg.req_line = dma_spec->args[0];
+ fchan->cfg.req_ctrl = 0;
+ fchan->cfg.type = ST_FDMA_TYPE_FREE_RUN;
+
+ if (dma_spec->args_count > 1)
+ fchan->cfg.req_ctrl = dma_spec->args[1]
+ & FDMA_REQ_CTRL_CFG_MASK;
+
+ if (dma_spec->args_count > 2)
+ fchan->cfg.type = dma_spec->args[2];
+
+ if (fchan->cfg.type == ST_FDMA_TYPE_FREE_RUN) {
+ fchan->dreq_line = 0;
+ } else {
+ fchan->dreq_line = st_fdma_dreq_get(fchan);
+ if (IS_ERR_VALUE(fchan->dreq_line)) {
+ chan = ERR_PTR(fchan->dreq_line);
+ goto err_chan;
+ }
+ }
+
+ dev_dbg(fdev->dev, "xlate req_line:%d type:%d req_ctrl:%#lx\n",
+ fchan->cfg.req_line, fchan->cfg.type, fchan->cfg.req_ctrl);
+
+ return chan;
+
+err_chan:
+ rproc_shutdown(fdev->slim_rproc->rproc);
+ return chan;
+
+}
+
+static void st_fdma_free_desc(struct virt_dma_desc *vdesc)
+{
+ struct st_fdma_desc *fdesc;
+ int i;
+
+ fdesc = to_st_fdma_desc(vdesc);
+ for (i = 0; i < fdesc->n_nodes; i++)
+ dma_pool_free(fdesc->fchan->node_pool, fdesc->node[i].desc,
+ fdesc->node[i].pdesc);
+ kfree(fdesc);
+}
+
+static struct st_fdma_desc *st_fdma_alloc_desc(struct st_fdma_chan *fchan,
+ int sg_len)
+{
+ struct st_fdma_desc *fdesc;
+ int i;
+
+ fdesc = kzalloc(sizeof(*fdesc) +
+ sizeof(struct st_fdma_sw_node) * sg_len, GFP_NOWAIT);
+ if (!fdesc)
+ return NULL;
+
+ fdesc->fchan = fchan;
+ fdesc->n_nodes = sg_len;
+ for (i = 0; i < sg_len; i++) {
+ fdesc->node[i].desc = dma_pool_alloc(fchan->node_pool,
+ GFP_NOWAIT, &fdesc->node[i].pdesc);
+ if (!fdesc->node[i].desc)
+ goto err;
+ }
+ return fdesc;
+
+err:
+ while (--i >= 0)
+ dma_pool_free(fchan->node_pool, fdesc->node[i].desc,
+ fdesc->node[i].pdesc);
+ kfree(fdesc);
+ return NULL;
+}
+
+static int st_fdma_alloc_chan_res(struct dma_chan *chan)
+{
+ struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+
+ /* Create the dma pool for descriptor allocation */
+ fchan->node_pool = dma_pool_create(dev_name(&chan->dev->device),
+ fchan->fdev->dev,
+ sizeof(struct st_fdma_hw_node),
+ __alignof__(struct st_fdma_hw_node),
+ 0);
+
+ if (!fchan->node_pool) {
+ dev_err(fchan->fdev->dev, "unable to allocate desc pool\n");
+ return -ENOMEM;
+ }
+
+ dev_dbg(fchan->fdev->dev, "alloc ch_id:%d type:%d\n",
+ fchan->vchan.chan.chan_id, fchan->cfg.type);
+
+ return 0;
+}
+
+static void st_fdma_free_chan_res(struct dma_chan *chan)
+{
+ struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+ struct rproc *rproc = fchan->fdev->slim_rproc->rproc;
+ unsigned long flags;
+
+ LIST_HEAD(head);
+
+ dev_dbg(fchan->fdev->dev, "%s: freeing chan:%d\n",
+ __func__, fchan->vchan.chan.chan_id);
+
+ if (fchan->cfg.type != ST_FDMA_TYPE_FREE_RUN)
+ st_fdma_dreq_put(fchan);
+
+ spin_lock_irqsave(&fchan->vchan.lock, flags);
+ fchan->fdesc = NULL;
+ spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+
+ dma_pool_destroy(fchan->node_pool);
+ fchan->node_pool = NULL;
+ memset(&fchan->cfg, 0, sizeof(struct st_fdma_cfg));
+
+ rproc_shutdown(rproc);
+}
+
+static struct dma_async_tx_descriptor *st_fdma_prep_dma_memcpy(
+ struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct st_fdma_chan *fchan;
+ struct st_fdma_desc *fdesc;
+ struct st_fdma_hw_node *hw_node;
+
+ if (!len)
+ return NULL;
+
+ fchan = to_st_fdma_chan(chan);
+
+ /* We only require a single descriptor */
+ fdesc = st_fdma_alloc_desc(fchan, 1);
+ if (!fdesc) {
+ dev_err(fchan->fdev->dev, "no memory for desc\n");
+ return NULL;
+ }
+
+ hw_node = fdesc->node[0].desc;
+ hw_node->next = 0;
+ hw_node->control = FDMA_NODE_CTRL_REQ_MAP_FREE_RUN;
+ hw_node->control |= FDMA_NODE_CTRL_SRC_INCR;
+ hw_node->control |= FDMA_NODE_CTRL_DST_INCR;
+ hw_node->control |= FDMA_NODE_CTRL_INT_EON;
+ hw_node->nbytes = len;
+ hw_node->saddr = src;
+ hw_node->daddr = dst;
+ hw_node->generic.length = len;
+ hw_node->generic.sstride = 0;
+ hw_node->generic.dstride = 0;
+
+ return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags);
+}
+
+static int config_reqctrl(struct st_fdma_chan *fchan,
+ enum dma_transfer_direction direction)
+{
+ u32 maxburst = 0, addr = 0;
+ enum dma_slave_buswidth width;
+ int ch_id = fchan->vchan.chan.chan_id;
+ struct st_fdma_dev *fdev = fchan->fdev;
+
+ switch (direction) {
+
+ case DMA_DEV_TO_MEM:
+ fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_WNR;
+ maxburst = fchan->scfg.src_maxburst;
+ width = fchan->scfg.src_addr_width;
+ addr = fchan->scfg.src_addr;
+ break;
+
+ case DMA_MEM_TO_DEV:
+ fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_WNR;
+ maxburst = fchan->scfg.dst_maxburst;
+ width = fchan->scfg.dst_addr_width;
+ addr = fchan->scfg.dst_addr;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_OPCODE_MASK;
+
+ switch (width) {
+
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST1;
+ break;
+
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST2;
+ break;
+
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST4;
+ break;
+
+ case DMA_SLAVE_BUSWIDTH_8_BYTES:
+ fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST8;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_NUM_OPS_MASK;
+ fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_NUM_OPS(maxburst-1);
+ dreq_write(fchan, fchan->cfg.req_ctrl, FDMA_REQ_CTRL_OFST);
+
+ fchan->cfg.dev_addr = addr;
+ fchan->cfg.dir = direction;
+
+ dev_dbg(fdev->dev, "chan:%d config_reqctrl:%#x req_ctrl:%#lx\n",
+ ch_id, addr, fchan->cfg.req_ctrl);
+
+ return 0;
+}
+
+static void fill_hw_node(struct st_fdma_hw_node *hw_node,
+ struct st_fdma_chan *fchan,
+ enum dma_transfer_direction direction)
+{
+ if (direction == DMA_MEM_TO_DEV) {
+ hw_node->control |= FDMA_NODE_CTRL_SRC_INCR;
+ hw_node->control |= FDMA_NODE_CTRL_DST_STATIC;
+ hw_node->daddr = fchan->cfg.dev_addr;
+ } else {
+ hw_node->control |= FDMA_NODE_CTRL_SRC_STATIC;
+ hw_node->control |= FDMA_NODE_CTRL_DST_INCR;
+ hw_node->saddr = fchan->cfg.dev_addr;
+ }
+
+ hw_node->generic.sstride = 0;
+ hw_node->generic.dstride = 0;
+}
+
+static inline struct st_fdma_chan *st_fdma_prep_common(struct dma_chan *chan,
+ size_t len, enum dma_transfer_direction direction)
+{
+ struct st_fdma_chan *fchan;
+
+ if (!chan || !len)
+ return NULL;
+
+ fchan = to_st_fdma_chan(chan);
+
+ if (!is_slave_direction(direction)) {
+ dev_err(fchan->fdev->dev, "bad direction?\n");
+ return NULL;
+ }
+
+ return fchan;
+}
+
+static struct dma_async_tx_descriptor *st_fdma_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t buf_addr, size_t len,
+ size_t period_len, enum dma_transfer_direction direction,
+ unsigned long flags)
+{
+ struct st_fdma_chan *fchan;
+ struct st_fdma_desc *fdesc;
+ int sg_len, i;
+
+ fchan = st_fdma_prep_common(chan, len, direction);
+ if (!fchan)
+ return NULL;
+
+ if (!period_len)
+ return NULL;
+
+ if (config_reqctrl(fchan, direction)) {
+ dev_err(fchan->fdev->dev, "bad width or direction\n");
+ return NULL;
+ }
+
+ /* the buffer length must be a multiple of period_len */
+ if (len % period_len != 0) {
+ dev_err(fchan->fdev->dev, "len is not multiple of period\n");
+ return NULL;
+ }
+
+ sg_len = len / period_len;
+ fdesc = st_fdma_alloc_desc(fchan, sg_len);
+ if (!fdesc) {
+ dev_err(fchan->fdev->dev, "no memory for desc\n");
+ return NULL;
+ }
+
+ fdesc->iscyclic = true;
+
+ for (i = 0; i < sg_len; i++) {
+ struct st_fdma_hw_node *hw_node = fdesc->node[i].desc;
+
+ hw_node->next = fdesc->node[(i + 1) % sg_len].pdesc;
+
+ hw_node->control =
+ FDMA_NODE_CTRL_REQ_MAP_DREQ(fchan->dreq_line);
+ hw_node->control |= FDMA_NODE_CTRL_INT_EON;
+
+ fill_hw_node(hw_node, fchan, direction);
+
+ if (direction == DMA_MEM_TO_DEV)
+ hw_node->saddr = buf_addr + (i * period_len);
+ else
+ hw_node->daddr = buf_addr + (i * period_len);
+
+ hw_node->nbytes = period_len;
+ hw_node->generic.length = period_len;
+ }
+
+ return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *st_fdma_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct st_fdma_chan *fchan;
+ struct st_fdma_desc *fdesc;
+ struct st_fdma_hw_node *hw_node;
+ struct scatterlist *sg;
+ int i;
+
+ fchan = st_fdma_prep_common(chan, sg_len, direction);
+ if (!fchan)
+ return NULL;
+
+ if (!sgl)
+ return NULL;
+
+ fdesc = st_fdma_alloc_desc(fchan, sg_len);
+ if (!fdesc) {
+ dev_err(fchan->fdev->dev, "no memory for desc\n");
+ return NULL;
+ }
+
+ fdesc->iscyclic = false;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ hw_node = fdesc->node[i].desc;
+
+ hw_node->next = fdesc->node[(i + 1) % sg_len].pdesc;
+ hw_node->control = FDMA_NODE_CTRL_REQ_MAP_DREQ(fchan->dreq_line);
+
+ fill_hw_node(hw_node, fchan, direction);
+
+ if (direction == DMA_MEM_TO_DEV)
+ hw_node->saddr = sg_dma_address(sg);
+ else
+ hw_node->daddr = sg_dma_address(sg);
+
+ hw_node->nbytes = sg_dma_len(sg);
+ hw_node->generic.length = sg_dma_len(sg);
+ }
+
+ /* interrupt at end of last node */
+ hw_node->control |= FDMA_NODE_CTRL_INT_EON;
+
+ return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags);
+}
+
+static size_t st_fdma_desc_residue(struct st_fdma_chan *fchan,
+ struct virt_dma_desc *vdesc,
+ bool in_progress)
+{
+ struct st_fdma_desc *fdesc = fchan->fdesc;
+ size_t residue = 0;
+ dma_addr_t cur_addr = 0;
+ int i;
+
+ if (in_progress) {
+ cur_addr = fchan_read(fchan, FDMA_CH_CMD_OFST);
+ cur_addr &= FDMA_CH_CMD_DATA_MASK;
+ }
+
+ for (i = fchan->fdesc->n_nodes - 1 ; i >= 0; i--) {
+ if (cur_addr == fdesc->node[i].pdesc) {
+ residue += fnode_read(fchan, FDMA_CNTN_OFST);
+ break;
+ }
+ residue += fdesc->node[i].desc->nbytes;
+ }
+
+ return residue;
+}
+
+static enum dma_status st_fdma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+ struct virt_dma_desc *vd;
+ enum dma_status ret;
+ unsigned long flags;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_COMPLETE || !txstate)
+ return ret;
+
+ spin_lock_irqsave(&fchan->vchan.lock, flags);
+ vd = vchan_find_desc(&fchan->vchan, cookie);
+ if (fchan->fdesc && cookie == fchan->fdesc->vdesc.tx.cookie)
+ txstate->residue = st_fdma_desc_residue(fchan, vd, true);
+ else if (vd)
+ txstate->residue = st_fdma_desc_residue(fchan, vd, false);
+ else
+ txstate->residue = 0;
+
+ spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+
+ return ret;
+}
+
+static void st_fdma_issue_pending(struct dma_chan *chan)
+{
+ struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fchan->vchan.lock, flags);
+
+ if (vchan_issue_pending(&fchan->vchan) && !fchan->fdesc)
+ st_fdma_xfer_desc(fchan);
+
+ spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+}
+
+static int st_fdma_pause(struct dma_chan *chan)
+{
+ unsigned long flags;
+ LIST_HEAD(head);
+ struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+ int ch_id = fchan->vchan.chan.chan_id;
+ unsigned long cmd = FDMA_CMD_PAUSE(ch_id);
+
+ dev_dbg(fchan->fdev->dev, "pause chan:%d\n", ch_id);
+
+ spin_lock_irqsave(&fchan->vchan.lock, flags);
+ if (fchan->fdesc)
+ fdma_write(fchan->fdev, cmd, FDMA_CMD_SET_OFST);
+ spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+
+ return 0;
+}
+
+static int st_fdma_resume(struct dma_chan *chan)
+{
+ unsigned long flags;
+ unsigned long val;
+ struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+ int ch_id = fchan->vchan.chan.chan_id;
+
+ dev_dbg(fchan->fdev->dev, "resume chan:%d\n", ch_id);
+
+ spin_lock_irqsave(&fchan->vchan.lock, flags);
+ if (fchan->fdesc) {
+ val = fchan_read(fchan, FDMA_CH_CMD_OFST);
+ val &= FDMA_CH_CMD_DATA_MASK;
+ fchan_write(fchan, val, FDMA_CH_CMD_OFST);
+ }
+ spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+
+ return 0;
+}
+
+static int st_fdma_terminate_all(struct dma_chan *chan)
+{
+ unsigned long flags;
+ LIST_HEAD(head);
+ struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+ int ch_id = fchan->vchan.chan.chan_id;
+ unsigned long cmd = FDMA_CMD_PAUSE(ch_id);
+
+ dev_dbg(fchan->fdev->dev, "terminate chan:%d\n", ch_id);
+
+ spin_lock_irqsave(&fchan->vchan.lock, flags);
+ fdma_write(fchan->fdev, cmd, FDMA_CMD_SET_OFST);
+ fchan->fdesc = NULL;
+ vchan_get_all_descriptors(&fchan->vchan, &head);
+ spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+ vchan_dma_desc_free_list(&fchan->vchan, &head);
+
+ return 0;
+}
+
+static int st_fdma_slave_config(struct dma_chan *chan,
+ struct dma_slave_config *slave_cfg)
+{
+ struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+
+ memcpy(&fchan->scfg, slave_cfg, sizeof(fchan->scfg));
+ return 0;
+}
+
+static const struct st_fdma_driverdata fdma_mpe31_stih407_11 = {
+ .name = "STiH407",
+ .id = 0,
+};
+
+static const struct st_fdma_driverdata fdma_mpe31_stih407_12 = {
+ .name = "STiH407",
+ .id = 1,
+};
+
+static const struct st_fdma_driverdata fdma_mpe31_stih407_13 = {
+ .name = "STiH407",
+ .id = 2,
+};
+
+static const struct of_device_id st_fdma_match[] = {
+ { .compatible = "st,stih407-fdma-mpe31-11"
+ , .data = &fdma_mpe31_stih407_11 },
+ { .compatible = "st,stih407-fdma-mpe31-12"
+ , .data = &fdma_mpe31_stih407_12 },
+ { .compatible = "st,stih407-fdma-mpe31-13"
+ , .data = &fdma_mpe31_stih407_13 },
+ {},
+};
+MODULE_DEVICE_TABLE(of, st_fdma_match);
+
+static int st_fdma_parse_dt(struct platform_device *pdev,
+ const struct st_fdma_driverdata *drvdata,
+ struct st_fdma_dev *fdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np)
+ goto err;
+
+ ret = of_property_read_u32(np, "dma-channels", &fdev->nr_channels);
+ if (ret)
+ goto err;
+
+ snprintf(fdev->fw_name, FW_NAME_SIZE, "fdma_%s_%d.elf",
+ drvdata->name, drvdata->id);
+
+err:
+ return ret;
+}
+#define FDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+
+static void st_fdma_free(struct st_fdma_dev *fdev)
+{
+ struct st_fdma_chan *fchan;
+ int i;
+
+ for (i = 0; i < fdev->nr_channels; i++) {
+ fchan = &fdev->chans[i];
+ list_del(&fchan->vchan.chan.device_node);
+ tasklet_kill(&fchan->vchan.task);
+ }
+}
+
+static int st_fdma_probe(struct platform_device *pdev)
+{
+ struct st_fdma_dev *fdev;
+ const struct of_device_id *match;
+ struct device_node *np = pdev->dev.of_node;
+ const struct st_fdma_driverdata *drvdata;
+ int ret, i;
+
+ match = of_match_device((st_fdma_match), &pdev->dev);
+ if (!match || !match->data) {
+ dev_err(&pdev->dev, "No device match found\n");
+ return -ENODEV;
+ }
+
+ drvdata = match->data;
+
+ fdev = devm_kzalloc(&pdev->dev, sizeof(*fdev), GFP_KERNEL);
+ if (!fdev)
+ return -ENOMEM;
+
+ ret = st_fdma_parse_dt(pdev, drvdata, fdev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to find platform data\n");
+ goto err;
+ }
+
+ fdev->chans = devm_kcalloc(&pdev->dev, fdev->nr_channels,
+ sizeof(struct st_fdma_chan), GFP_KERNEL);
+ if (!fdev->chans)
+ return -ENOMEM;
+
+ fdev->dev = &pdev->dev;
+ fdev->drvdata = drvdata;
+ platform_set_drvdata(pdev, fdev);
+
+ fdev->irq = platform_get_irq(pdev, 0);
+ if (fdev->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq resource\n");
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(&pdev->dev, fdev->irq, st_fdma_irq_handler, 0,
+ dev_name(&pdev->dev), fdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq (%d)\n", ret);
+ goto err;
+ }
+
+ fdev->slim_rproc = st_slim_rproc_alloc(pdev, fdev->fw_name);
+ if (!fdev->slim_rproc) {
+ ret = PTR_ERR(fdev->slim_rproc);
+ dev_err(&pdev->dev, "slim_rproc_alloc failed (%d)\n", ret);
+ goto err;
+ }
+
+ /* Initialise list of FDMA channels */
+ INIT_LIST_HEAD(&fdev->dma_device.channels);
+ for (i = 0; i < fdev->nr_channels; i++) {
+ struct st_fdma_chan *fchan = &fdev->chans[i];
+
+ fchan->fdev = fdev;
+ fchan->vchan.desc_free = st_fdma_free_desc;
+ vchan_init(&fchan->vchan, &fdev->dma_device);
+ }
+
+ /* Initialise the FDMA dreq (reserve 0 & 31 for FDMA use) */
+ fdev->dreq_mask = BIT(0) | BIT(31);
+
+ dma_cap_set(DMA_SLAVE, fdev->dma_device.cap_mask);
+ dma_cap_set(DMA_CYCLIC, fdev->dma_device.cap_mask);
+ dma_cap_set(DMA_MEMCPY, fdev->dma_device.cap_mask);
+
+ fdev->dma_device.dev = &pdev->dev;
+ fdev->dma_device.device_alloc_chan_resources = st_fdma_alloc_chan_res;
+ fdev->dma_device.device_free_chan_resources = st_fdma_free_chan_res;
+ fdev->dma_device.device_prep_dma_cyclic = st_fdma_prep_dma_cyclic;
+ fdev->dma_device.device_prep_slave_sg = st_fdma_prep_slave_sg;
+ fdev->dma_device.device_prep_dma_memcpy = st_fdma_prep_dma_memcpy;
+ fdev->dma_device.device_tx_status = st_fdma_tx_status;
+ fdev->dma_device.device_issue_pending = st_fdma_issue_pending;
+ fdev->dma_device.device_terminate_all = st_fdma_terminate_all;
+ fdev->dma_device.device_config = st_fdma_slave_config;
+ fdev->dma_device.device_pause = st_fdma_pause;
+ fdev->dma_device.device_resume = st_fdma_resume;
+
+ fdev->dma_device.src_addr_widths = FDMA_DMA_BUSWIDTHS;
+ fdev->dma_device.dst_addr_widths = FDMA_DMA_BUSWIDTHS;
+ fdev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ fdev->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+ ret = dma_async_device_register(&fdev->dma_device);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register DMA device (%d)\n", ret);
+ goto err_rproc;
+ }
+
+ ret = of_dma_controller_register(np, st_fdma_of_xlate, fdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register controller (%d)\n", ret);
+ goto err_dma_dev;
+ }
+
+ dev_info(&pdev->dev, "ST FDMA engine driver, irq:%d\n", fdev->irq);
+
+ return 0;
+
+err_dma_dev:
+ dma_async_device_unregister(&fdev->dma_device);
+err_rproc:
+ st_fdma_free(fdev);
+ st_slim_rproc_put(fdev->slim_rproc);
+err:
+ return ret;
+}
+
+static int st_fdma_remove(struct platform_device *pdev)
+{
+ struct st_fdma_dev *fdev = platform_get_drvdata(pdev);
+
+ devm_free_irq(&pdev->dev, fdev->irq, fdev);
+ st_slim_rproc_put(fdev->slim_rproc);
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&fdev->dma_device);
+
+ return 0;
+}
+
+static struct platform_driver st_fdma_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = st_fdma_match,
+ },
+ .probe = st_fdma_probe,
+ .remove = st_fdma_remove,
+};
+module_platform_driver(st_fdma_platform_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMicroelectronics FDMA engine driver");
+MODULE_AUTHOR("Ludovic.barre <Ludovic.barre@st.com>");
+MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
+MODULE_ALIAS("platform: " DRIVER_NAME);
--
2.7.4
^ permalink raw reply related
* [PATCH v11 07/14] MAINTAINERS: Add FDMA driver files to STi section.
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
This patch adds the FDMA driver files to the STi
section of the maintainers file.
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 78b7f8b..e93762d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1774,6 +1774,7 @@ F: drivers/char/hw_random/st-rng.c
F: drivers/clocksource/arm_global_timer.c
F: drivers/clocksource/clksrc_st_lpc.c
F: drivers/cpufreq/sti-cpufreq.c
+F: drivers/dma/st_fdma*
F: drivers/i2c/busses/i2c-st.c
F: drivers/media/rc/st_rc.c
F: drivers/media/platform/sti/c8sectpfe/
--
2.7.4
^ permalink raw reply related
* [PATCH v11 08/14] ARM: multi_v7_defconfig: Enable remoteproc core
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
Now that remoteproc core is selectable it needs to be enabled
in the multi_v7 build.
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
arch/arm/configs/multi_v7_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 437d074..538c326 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -823,6 +823,7 @@ CONFIG_HWSPINLOCK_QCOM=y
CONFIG_ROCKCHIP_IOMMU=y
CONFIG_TEGRA_IOMMU_GART=y
CONFIG_TEGRA_IOMMU_SMMU=y
+CONFIG_REMOTEPROC=m
CONFIG_PM_DEVFREQ=y
CONFIG_ARM_TEGRA_DEVFREQ=m
CONFIG_MEMORY=y
--
2.7.4
^ permalink raw reply related
* [PATCH v11 09/14] ARM: multi_v7_defconfig: Enable st_remoteproc driver.
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
The st231 remote coprocessors are found on all STi chipsets.
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
arch/arm/configs/multi_v7_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 538c326..0a06af9 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -824,6 +824,7 @@ CONFIG_ROCKCHIP_IOMMU=y
CONFIG_TEGRA_IOMMU_GART=y
CONFIG_TEGRA_IOMMU_SMMU=y
CONFIG_REMOTEPROC=m
+CONFIG_ST_REMOTEPROC=m
CONFIG_PM_DEVFREQ=y
CONFIG_ARM_TEGRA_DEVFREQ=m
CONFIG_MEMORY=y
--
2.7.4
^ permalink raw reply related
* [PATCH v11 10/14] ARM: multi_v7_defconfig: Enable STi FDMA driver
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
This DMA controller is found on all STi chipsets.
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
arch/arm/configs/multi_v7_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 0a06af9..ce9ab5a 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -790,6 +790,7 @@ CONFIG_DMA_OMAP=y
CONFIG_QCOM_BAM_DMA=y
CONFIG_XILINX_DMA=y
CONFIG_DMA_SUN6I=y
+CONFIG_ST_FDMA=m
CONFIG_STAGING=y
CONFIG_SENSORS_ISL29018=y
CONFIG_SENSORS_ISL29028=y
--
2.7.4
^ permalink raw reply related
* [PATCH v11 11/14] ARM: multi_v7_defconfig: Enable STi and simple-card drivers.
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
This patch enables the STi ALSA drivers found on STi platforms
as well as the simple-card driver which is a dependency to have
working sound.
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
arch/arm/configs/multi_v7_defconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index ce9ab5a..a977e57 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -649,6 +649,9 @@ CONFIG_SND_SOC_AK4642=m
CONFIG_SND_SOC_SGTL5000=m
CONFIG_SND_SOC_SPDIF=m
CONFIG_SND_SOC_WM8978=m
+CONFIG_SND_SOC_STI=m
+CONFIG_SND_SOC_STI_SAS=m
+CONFIG_SND_SIMPLE_CARD=m
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_MVEBU=y
--
2.7.4
^ permalink raw reply related
* [PATCH v11 12/14] dmaengine: st_fdma: fix uninitialized variable access
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
From: Arnd Bergmann <arnd@arndb.de>
The newly added st_fdma driver introduces a build warning for
allmodconfig when we add '-Wmaybe-uninitialized':
drivers/dma/st_fdma.c: In function 'st_fdma_probe':
drivers/dma/st_fdma.c:777:5: error: 'ret' may be used uninitialized in this function [-Werror=maybe-uninitialized]
The warning is correct, though this can't happen in practice
as the check is redundant (we don't get to this function if
the pointer is NULL). Even if the function were called with a
NULL of_node, the check is not needed because of_property_read_u32
can deal with a NULL argument by returning an error.
Removing the unnecessary code simplifies the function and avoids
the condition that we get the warning for.
Fixes: 6b4cd727eaf1 ("dmaengine: st_fdma: Add STMicroelectronics FDMA engine driver support")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Peter Griffin <peter.griffin@linaro.org>
---
drivers/dma/st_fdma.c | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c
index 515e1d4..232d354 100644
--- a/drivers/dma/st_fdma.c
+++ b/drivers/dma/st_fdma.c
@@ -720,21 +720,11 @@ static int st_fdma_parse_dt(struct platform_device *pdev,
const struct st_fdma_driverdata *drvdata,
struct st_fdma_dev *fdev)
{
- struct device_node *np = pdev->dev.of_node;
- int ret;
-
- if (!np)
- goto err;
-
- ret = of_property_read_u32(np, "dma-channels", &fdev->nr_channels);
- if (ret)
- goto err;
-
snprintf(fdev->fw_name, FW_NAME_SIZE, "fdma_%s_%d.elf",
drvdata->name, drvdata->id);
-err:
- return ret;
+ return of_property_read_u32(pdev->dev.of_node, "dma-channels",
+ &fdev->nr_channels);
}
#define FDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
--
2.7.4
^ permalink raw reply related
* [PATCH v11 13/14] dmaengine: st_fdma: Fix the error return code in st_fdma_probe()
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
From: Wei Yongjun <weiyongjun1@huawei.com>
In case of error, the function st_slim_rproc_alloc() returns ERR_PTR()
and never returns NULL. The NULL test in the return value check should
be replaced with IS_ERR().
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Acked-by: Peter Griffin <peter.griffin@linaro.org>
---
drivers/dma/st_fdma.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c
index 232d354..bfb79bd 100644
--- a/drivers/dma/st_fdma.c
+++ b/drivers/dma/st_fdma.c
@@ -792,7 +792,7 @@ static int st_fdma_probe(struct platform_device *pdev)
}
fdev->slim_rproc = st_slim_rproc_alloc(pdev, fdev->fw_name);
- if (!fdev->slim_rproc) {
+ if (IS_ERR(fdev->slim_rproc)) {
ret = PTR_ERR(fdev->slim_rproc);
dev_err(&pdev->dev, "slim_rproc_alloc failed (%d)\n", ret);
goto err;
--
2.7.4
^ permalink raw reply related
* [PATCH v11 14/14] dmaengine: st_fdma: Update st_fdma to 'depends on REMOTEPROC'.
From: Peter Griffin @ 2016-11-07 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478542665-17089-1-git-send-email-peter.griffin@linaro.org>
During randconfig builds you can get the following warning
"warning: (ST_FDMA) selects ST_SLIM_REMOTEPROC which has unmet direct
dependencies (REMOTEPROC)"
randconfig builds should always build without any warnings so
update fdma to depend on REMOTEPROC so this can not happen.
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/dma/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 661f217..6b96710 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -438,6 +438,7 @@ config STE_DMA40
config ST_FDMA
tristate "ST FDMA dmaengine support"
depends on ARCH_STI
+ depends on REMOTEPROC
select ST_SLIM_REMOTEPROC
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
--
2.7.4
^ permalink raw reply related
* [PATCH v3 2/3] ARM64: dts: sun6i: add dma node for a64.
From: Hao Zhang @ 2016-11-07 18:22 UTC (permalink / raw)
To: linux-arm-kernel
This adds the dma node for sun50i a64.
Signed-off-by: Hao Zhang <hao5781286@gmail.com>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index e3c3d7d8..855ae2c 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -227,6 +227,15 @@
};
};
+ dma: dma-controller at 01c02000 {
+ compatible = "allwinner,sun50i-a64-dma";
+ reg = <0x01c02000 0x1000>;
+ interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_DMA>;
+ resets = <&ccu RST_BUS_DMA>;
+ #dma-cells = <1>;
+ };
+
uart0: serial at 1c28000 {
compatible = "snps,dw-apb-uart";
reg = <0x01c28000 0x400>;
--
2.7.4
^ permalink raw reply related
* [PATCH v7 2/2] ARM: EXYNOS: refactoring of mach-exynos to enable chipid driver
From: Krzysztof Kozlowski @ 2016-11-07 18:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478347427-28409-3-git-send-email-pankaj.dubey@samsung.com>
On Sat, Nov 05, 2016 at 05:33:47PM +0530, Pankaj Dubey wrote:
> This patch enables chipid driver for ARCH_EXYNOS and refactors
> machine code for using chipid driver for identification of
> SoC ID and SoC rev.
>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> ---
> arch/arm/mach-exynos/Kconfig | 1 +
> arch/arm/mach-exynos/common.h | 92 ----------------------------
> arch/arm/mach-exynos/exynos.c | 31 ----------
> arch/arm/mach-exynos/firmware.c | 10 +--
> arch/arm/mach-exynos/include/mach/map.h | 21 -------
> arch/arm/mach-exynos/platsmp.c | 22 ++++---
> arch/arm/mach-exynos/pm.c | 41 ++++++++-----
> arch/arm/plat-samsung/cpu.c | 14 -----
> arch/arm/plat-samsung/include/plat/cpu.h | 2 -
> arch/arm/plat-samsung/include/plat/map-s5p.h | 2 -
> 10 files changed, 47 insertions(+), 189 deletions(-)
> delete mode 100644 arch/arm/mach-exynos/include/mach/map.h
>
> diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
> index b085855..a76c679 100644
> --- a/arch/arm/mach-exynos/Kconfig
> +++ b/arch/arm/mach-exynos/Kconfig
> @@ -16,6 +16,7 @@ menuconfig ARCH_EXYNOS
> select ARM_AMBA
> select ARM_GIC
> select COMMON_CLK_SAMSUNG
> + select EXYNOS_CHIPID
> select EXYNOS_THERMAL
> select EXYNOS_PMU
> select EXYNOS_SROM
> diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
> index d19064b..9d76cf8 100644
> --- a/arch/arm/mach-exynos/common.h
> +++ b/arch/arm/mach-exynos/common.h
> @@ -14,97 +14,6 @@
>
> #include <linux/platform_data/cpuidle-exynos.h>
>
> -#define EXYNOS3250_SOC_ID 0xE3472000
> -#define EXYNOS3_SOC_MASK 0xFFFFF000
> -
> -#define EXYNOS4210_CPU_ID 0x43210000
> -#define EXYNOS4212_CPU_ID 0x43220000
> -#define EXYNOS4412_CPU_ID 0xE4412200
> -#define EXYNOS4_CPU_MASK 0xFFFE0000
> -
> -#define EXYNOS5250_SOC_ID 0x43520000
> -#define EXYNOS5410_SOC_ID 0xE5410000
> -#define EXYNOS5420_SOC_ID 0xE5420000
> -#define EXYNOS5440_SOC_ID 0xE5440000
> -#define EXYNOS5800_SOC_ID 0xE5422000
> -#define EXYNOS5_SOC_MASK 0xFFFFF000
> -
> -extern unsigned long samsung_cpu_id;
> -
> -#define IS_SAMSUNG_CPU(name, id, mask) \
> -static inline int is_samsung_##name(void) \
> -{ \
> - return ((samsung_cpu_id & mask) == (id & mask)); \
> -}
> -
> -IS_SAMSUNG_CPU(exynos3250, EXYNOS3250_SOC_ID, EXYNOS3_SOC_MASK)
> -IS_SAMSUNG_CPU(exynos4210, EXYNOS4210_CPU_ID, EXYNOS4_CPU_MASK)
> -IS_SAMSUNG_CPU(exynos4212, EXYNOS4212_CPU_ID, EXYNOS4_CPU_MASK)
> -IS_SAMSUNG_CPU(exynos4412, EXYNOS4412_CPU_ID, EXYNOS4_CPU_MASK)
> -IS_SAMSUNG_CPU(exynos5250, EXYNOS5250_SOC_ID, EXYNOS5_SOC_MASK)
> -IS_SAMSUNG_CPU(exynos5410, EXYNOS5410_SOC_ID, EXYNOS5_SOC_MASK)
> -IS_SAMSUNG_CPU(exynos5420, EXYNOS5420_SOC_ID, EXYNOS5_SOC_MASK)
> -IS_SAMSUNG_CPU(exynos5440, EXYNOS5440_SOC_ID, EXYNOS5_SOC_MASK)
> -IS_SAMSUNG_CPU(exynos5800, EXYNOS5800_SOC_ID, EXYNOS5_SOC_MASK)
> -
> -#if defined(CONFIG_SOC_EXYNOS3250)
> -# define soc_is_exynos3250() is_samsung_exynos3250()
> -#else
> -# define soc_is_exynos3250() 0
> -#endif
> -
> -#if defined(CONFIG_CPU_EXYNOS4210)
> -# define soc_is_exynos4210() is_samsung_exynos4210()
> -#else
> -# define soc_is_exynos4210() 0
> -#endif
> -
> -#if defined(CONFIG_SOC_EXYNOS4212)
> -# define soc_is_exynos4212() is_samsung_exynos4212()
> -#else
> -# define soc_is_exynos4212() 0
> -#endif
> -
> -#if defined(CONFIG_SOC_EXYNOS4412)
> -# define soc_is_exynos4412() is_samsung_exynos4412()
> -#else
> -# define soc_is_exynos4412() 0
> -#endif
> -
> -#define EXYNOS4210_REV_0 (0x0)
> -#define EXYNOS4210_REV_1_0 (0x10)
> -#define EXYNOS4210_REV_1_1 (0x11)
> -
> -#if defined(CONFIG_SOC_EXYNOS5250)
> -# define soc_is_exynos5250() is_samsung_exynos5250()
> -#else
> -# define soc_is_exynos5250() 0
> -#endif
> -
> -#if defined(CONFIG_SOC_EXYNOS5410)
> -# define soc_is_exynos5410() is_samsung_exynos5410()
> -#else
> -# define soc_is_exynos5410() 0
> -#endif
> -
> -#if defined(CONFIG_SOC_EXYNOS5420)
> -# define soc_is_exynos5420() is_samsung_exynos5420()
> -#else
> -# define soc_is_exynos5420() 0
> -#endif
> -
> -#if defined(CONFIG_SOC_EXYNOS5440)
> -# define soc_is_exynos5440() is_samsung_exynos5440()
> -#else
> -# define soc_is_exynos5440() 0
> -#endif
> -
> -#if defined(CONFIG_SOC_EXYNOS5800)
> -# define soc_is_exynos5800() is_samsung_exynos5800()
> -#else
> -# define soc_is_exynos5800() 0
> -#endif
> -
> extern u32 cp15_save_diag;
> extern u32 cp15_save_power;
>
> @@ -161,7 +70,6 @@ extern struct cpuidle_exynos_data cpuidle_coupled_exynos_data;
>
> extern void exynos_set_delayed_reset_assertion(bool enable);
>
> -extern unsigned int samsung_rev(void);
> extern void exynos_core_restart(u32 core_id);
> extern int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr);
> extern int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr);
> diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
> index fa08ef9..942131e 100644
> --- a/arch/arm/mach-exynos/exynos.c
> +++ b/arch/arm/mach-exynos/exynos.c
> @@ -23,9 +23,6 @@
> #include <asm/mach/arch.h>
> #include <asm/mach/map.h>
>
> -#include <mach/map.h>
> -#include <plat/cpu.h>
> -
> #include "common.h"
>
> static struct platform_device exynos_cpuidle = {
> @@ -67,37 +64,9 @@ static void __init exynos_init_late(void)
> exynos_pm_init();
> }
>
> -static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
> - int depth, void *data)
> -{
> - struct map_desc iodesc;
> - const __be32 *reg;
> - int len;
> -
> - if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid") &&
> - !of_flat_dt_is_compatible(node, "samsung,exynos5440-clock"))
> - return 0;
> -
> - reg = of_get_flat_dt_prop(node, "reg", &len);
> - if (reg == NULL || len != (sizeof(unsigned long) * 2))
> - return 0;
> -
> - iodesc.pfn = __phys_to_pfn(be32_to_cpu(reg[0]));
> - iodesc.length = be32_to_cpu(reg[1]) - 1;
> - iodesc.virtual = (unsigned long)S5P_VA_CHIPID;
> - iodesc.type = MT_DEVICE;
> - iotable_init(&iodesc, 1);
> - return 1;
> -}
> -
> static void __init exynos_init_io(void)
> {
> debug_ll_io_init();
> -
> - of_scan_flat_dt(exynos_fdt_map_chipid, NULL);
> -
> - /* detect cpu id and rev. */
> - s5p_init_cpu(S5P_VA_CHIPID);
> }
>
> /*
> diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c
> index fd6da54..a9f8504e 100644
> --- a/arch/arm/mach-exynos/firmware.c
> +++ b/arch/arm/mach-exynos/firmware.c
> @@ -44,7 +44,7 @@ static int exynos_do_idle(unsigned long mode)
> writel_relaxed(virt_to_phys(exynos_cpu_resume_ns),
> sysram_ns_base_addr + 0x24);
> writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
> - if (soc_is_exynos3250()) {
> + if (of_machine_is_compatible("samsung,exynos3250")) {
> flush_cache_all();
> exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
> SMC_POWERSTATE_IDLE, 0);
> @@ -65,7 +65,7 @@ static int exynos_cpu_boot(int cpu)
> * Exynos3250 doesn't need to send smc command for secondary CPU boot
> * because Exynos3250 removes WFE in secure mode.
> */
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250"))
> return 0;
>
> /*
> @@ -73,7 +73,7 @@ static int exynos_cpu_boot(int cpu)
> * But, Exynos4212 has only one secondary CPU so second parameter
> * isn't used for informing secure firmware about CPU id.
> */
> - if (soc_is_exynos4212())
> + if (of_machine_is_compatible("samsung,exynos4212"))
> cpu = 0;
>
> exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
> @@ -94,7 +94,7 @@ static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
> * additional offset for every CPU, with Exynos4412 being the only
> * exception.
> */
> - if (soc_is_exynos4412())
> + if (of_machine_is_compatible("samsung,exynos4412"))
> boot_reg += 4 * cpu;
>
> writel_relaxed(boot_addr, boot_reg);
> @@ -110,7 +110,7 @@ static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
>
> boot_reg = sysram_ns_base_addr + 0x1c;
>
> - if (soc_is_exynos4412())
> + if (of_machine_is_compatible("samsung,exynos4412"))
> boot_reg += 4 * cpu;
>
> *boot_addr = readl_relaxed(boot_reg);
> diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
> deleted file mode 100644
> index 0eef407..0000000
> --- a/arch/arm/mach-exynos/include/mach/map.h
> +++ /dev/null
> @@ -1,21 +0,0 @@
> -/*
> - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> - * http://www.samsung.com/
> - *
> - * EXYNOS - Memory map definitions
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License version 2 as
> - * published by the Free Software Foundation.
> -*/
> -
> -#ifndef __ASM_ARCH_MAP_H
> -#define __ASM_ARCH_MAP_H __FILE__
> -
> -#include <plat/map-base.h>
> -
> -#include <plat/map-s5p.h>
> -
> -#define EXYNOS_PA_CHIPID 0x10000000
> -
> -#endif /* __ASM_ARCH_MAP_H */
> diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
> index 553d0d9..884e885 100644
> --- a/arch/arm/mach-exynos/platsmp.c
> +++ b/arch/arm/mach-exynos/platsmp.c
> @@ -19,6 +19,7 @@
> #include <linux/smp.h>
> #include <linux/io.h>
> #include <linux/of_address.h>
> +#include <linux/sys_soc.h>
> #include <linux/soc/samsung/exynos-regs-pmu.h>
>
> #include <asm/cacheflush.h>
> @@ -27,8 +28,6 @@
> #include <asm/smp_scu.h>
> #include <asm/firmware.h>
>
> -#include <mach/map.h>
> -
> #include "common.h"
>
> extern void exynos4_secondary_startup(void);
> @@ -93,7 +92,8 @@ void exynos_cpu_power_down(int cpu)
> {
> u32 core_conf;
>
> - if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
> + if (cpu == 0 && (of_machine_is_compatible("samsung,exynos5420") ||
> + of_machine_is_compatible("samsung,exynos5800"))) {
NACK
Please see:
ca489c58ef0b ("ARM: EXYNOS: Don't use LDREX and STREX after disabling
cache coherency")
Such cases has to be fixed in different way...
> /*
> * Bypass power down for CPU0 during suspend. Check for
> * the SYS_PWR_REG value to decide if we are suspending
> @@ -120,7 +120,7 @@ void exynos_cpu_power_up(int cpu)
> {
> u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
>
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250"))
> core_conf |= S5P_CORE_AUTOWAKEUP_EN;
>
> pmu_raw_writel(core_conf,
> @@ -168,9 +168,14 @@ int exynos_cluster_power_state(int cluster)
> S5P_CORE_LOCAL_PWR_EN);
> }
>
> +static struct soc_device_attribute exynos4210_rev11[] = {
> + { .soc_id = "EXYNOS4210", .revision = "11", },
> + { },
> +};
> +
> static void __iomem *cpu_boot_reg_base(void)
> {
> - if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
> + if (soc_device_match(exynos4210_rev11))
> return pmu_base_addr + S5P_INFORM5;
> return sysram_base_addr;
> }
> @@ -182,9 +187,10 @@ static inline void __iomem *cpu_boot_reg(int cpu)
> boot_reg = cpu_boot_reg_base();
> if (!boot_reg)
> return IOMEM_ERR_PTR(-ENODEV);
> - if (soc_is_exynos4412())
> + if (of_machine_is_compatible("samsung,exynos4412"))
> boot_reg += 4*cpu;
> - else if (soc_is_exynos5420() || soc_is_exynos5800())
> + else if (of_machine_is_compatible("samsung,exynos5420") ||
> + of_machine_is_compatible("samsung,exynos5800"))
> boot_reg += 4;
> return boot_reg;
> }
> @@ -356,7 +362,7 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
>
> call_firmware_op(cpu_boot, core_id);
>
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250"))
> dsb_sev();
> else
> arch_send_wakeup_ipi_mask(cpumask_of(cpu));
> diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
> index 60e6827..430b3e2 100644
> --- a/arch/arm/mach-exynos/pm.c
> +++ b/arch/arm/mach-exynos/pm.c
> @@ -19,6 +19,7 @@
> #include <linux/io.h>
> #include <linux/of.h>
> #include <linux/of_address.h>
> +#include <linux/sys_soc.h>
> #include <linux/soc/samsung/exynos-regs-pmu.h>
> #include <linux/soc/samsung/exynos-pmu.h>
>
> @@ -29,20 +30,30 @@
>
> #include "common.h"
>
> +static struct soc_device_attribute exynos4210_rev11[] = {
> + { .soc_id = "EXYNOS4210", .revision = "11", },
> + { },
> +};
This (and all others) look like static const.
> +
> +static struct soc_device_attribute exynos4210_rev10[] = {
> + { .soc_id = "EXYNOS4210", .revision = "10", },
> + { },
> +};
> +
> static inline void __iomem *exynos_boot_vector_addr(void)
> {
> - if (samsung_rev() == EXYNOS4210_REV_1_1)
> + if (soc_device_match(exynos4210_rev11))
> return pmu_base_addr + S5P_INFORM7;
> - else if (samsung_rev() == EXYNOS4210_REV_1_0)
> + else if (soc_device_match(exynos4210_rev10))
> return sysram_base_addr + 0x24;
> return pmu_base_addr + S5P_INFORM0;
> }
>
> static inline void __iomem *exynos_boot_vector_flag(void)
> {
> - if (samsung_rev() == EXYNOS4210_REV_1_1)
> + if (soc_device_match(exynos4210_rev11))
> return pmu_base_addr + S5P_INFORM6;
> - else if (samsung_rev() == EXYNOS4210_REV_1_0)
> + else if (soc_device_match(exynos4210_rev10))
> return sysram_base_addr + 0x20;
> return pmu_base_addr + S5P_INFORM1;
> }
> @@ -122,11 +133,13 @@ int exynos_pm_central_resume(void)
> }
>
> /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
> -static void exynos_set_wakeupmask(long mask)
> +static void exynos_set_wakeupmask(void)
> {
> - pmu_raw_writel(mask, S5P_WAKEUP_MASK);
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250")) {
> + pmu_raw_writel(0x40003ffe, S5P_WAKEUP_MASK);
> pmu_raw_writel(0x0, S5P_WAKEUP_MASK2);
> + } else
> + pmu_raw_writel(0x0000ff3e, S5P_WAKEUP_MASK);
> }
>
> static void exynos_cpu_set_boot_vector(long flags)
> @@ -140,7 +153,7 @@ static int exynos_aftr_finisher(unsigned long flags)
> {
> int ret;
>
> - exynos_set_wakeupmask(soc_is_exynos3250() ? 0x40003ffe : 0x0000ff3e);
> + exynos_set_wakeupmask();
> /* Set value of power down register for aftr mode */
> exynos_sys_powerdown_conf(SYS_AFTR);
>
> @@ -163,7 +176,7 @@ void exynos_enter_aftr(void)
>
> cpu_pm_enter();
>
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250"))
> exynos_set_boot_flag(cpuid, C2_STATE);
>
> exynos_pm_central_suspend();
> @@ -192,7 +205,7 @@ void exynos_enter_aftr(void)
>
> exynos_pm_central_resume();
>
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250"))
> exynos_clear_boot_flag(cpuid, C2_STATE);
>
> cpu_pm_exit();
> @@ -263,7 +276,7 @@ abort:
> while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
> cpu_relax();
>
> - if (soc_is_exynos3250()) {
> + if (of_machine_is_compatible("samsung,exynos3250")) {
> while (!pmu_raw_readl(S5P_PMU_SPARE2) &&
> !atomic_read(&cpu1_wakeup))
> cpu_relax();
> @@ -285,7 +298,7 @@ abort:
>
> call_firmware_op(cpu_boot, 1);
>
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250"))
> dsb_sev();
> else
> arch_send_wakeup_ipi_mask(cpumask_of(1));
> @@ -297,7 +310,7 @@ fail:
>
> static int exynos_wfi_finisher(unsigned long flags)
> {
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250"))
> flush_cache_all();
> cpu_do_idle();
>
> @@ -319,7 +332,7 @@ static int exynos_cpu1_powerdown(void)
> */
> exynos_cpu_power_down(1);
>
> - if (soc_is_exynos3250())
> + if (of_machine_is_compatible("samsung,exynos3250"))
> pmu_raw_writel(0, S5P_PMU_SPARE2);
>
> ret = cpu_suspend(0, exynos_wfi_finisher);
> diff --git a/arch/arm/plat-samsung/cpu.c b/arch/arm/plat-samsung/cpu.c
> index a107b3a..e58f0f6 100644
> --- a/arch/arm/plat-samsung/cpu.c
> +++ b/arch/arm/plat-samsung/cpu.c
> @@ -21,12 +21,6 @@
> unsigned long samsung_cpu_id;
> static unsigned int samsung_cpu_rev;
>
> -unsigned int samsung_rev(void)
> -{
> - return samsung_cpu_rev;
> -}
> -EXPORT_SYMBOL(samsung_rev);
> -
> void __init s3c64xx_init_cpu(void)
> {
> samsung_cpu_id = readl_relaxed(S3C_VA_SYS + 0x118);
> @@ -43,11 +37,3 @@ void __init s3c64xx_init_cpu(void)
>
> pr_info("Samsung CPU ID: 0x%08lx\n", samsung_cpu_id);
> }
> -
> -void __init s5p_init_cpu(const void __iomem *cpuid_addr)
> -{
> - samsung_cpu_id = readl_relaxed(cpuid_addr);
> - samsung_cpu_rev = samsung_cpu_id & 0xFF;
> -
> - pr_info("Samsung CPU ID: 0x%08lx\n", samsung_cpu_id);
Actually I kind of liked the CPU ID. :) You won't find this in
cpuinfo... How about leaving this for debug purposes?
Best regards,
Krzysztof
> -}
> diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-samsung/include/plat/cpu.h
> index b7b702a..913c176 100644
> --- a/arch/arm/plat-samsung/include/plat/cpu.h
> +++ b/arch/arm/plat-samsung/include/plat/cpu.h
> @@ -115,8 +115,6 @@ extern void s3c24xx_init_io(struct map_desc *mach_desc, int size);
> extern void s3c64xx_init_cpu(void);
> extern void s5p_init_cpu(const void __iomem *cpuid_addr);
>
> -extern unsigned int samsung_rev(void);
> -
> extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no);
>
> extern void s3c24xx_init_clocks(int xtal);
> diff --git a/arch/arm/plat-samsung/include/plat/map-s5p.h b/arch/arm/plat-samsung/include/plat/map-s5p.h
> index 512ed1f..d6853f1 100644
> --- a/arch/arm/plat-samsung/include/plat/map-s5p.h
> +++ b/arch/arm/plat-samsung/include/plat/map-s5p.h
> @@ -13,8 +13,6 @@
> #ifndef __ASM_PLAT_MAP_S5P_H
> #define __ASM_PLAT_MAP_S5P_H __FILE__
>
> -#define S5P_VA_CHIPID S3C_ADDR(0x02000000)
> -
> #define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000))
> #define VA_VIC0 VA_VIC(0)
> #define VA_VIC1 VA_VIC(1)
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v2] iommu/arm-smmu: Fix out-of-bounds dereference
From: Robin Murphy @ 2016-11-07 18:25 UTC (permalink / raw)
To: linux-arm-kernel
When we iterate a master's config entries, what we generally care
about is the entry's stream map index, rather than the entry index
itself, so it's nice to have the iterator automatically assign the
former from the latter. Unfortunately, booting with KASAN reveals
the oversight that using a simple comma operator results in the
entry index being dereferenced before being checked for validity,
so we always access one element past the end of the fwspec array.
Flip things around so that the check always happens before the index
may be dereferenced.
Fixes: adfec2e709d2 ("iommu/arm-smmu: Convert to iommu_fwspec")
Reported-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
drivers/iommu/arm-smmu.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index f86683eec446..786d33900382 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -324,8 +324,10 @@ struct arm_smmu_master_cfg {
#define INVALID_SMENDX -1
#define __fwspec_cfg(fw) ((struct arm_smmu_master_cfg *)fw->iommu_priv)
#define fwspec_smmu(fw) (__fwspec_cfg(fw)->smmu)
+#define fwspec_smendx(fw, i) \
+ (i >= fw->num_ids ? INVALID_SMENDX : __fwspec_cfg(fw)->smendx[i])
#define for_each_cfg_sme(fw, i, idx) \
- for (i = 0; idx = __fwspec_cfg(fw)->smendx[i], i < fw->num_ids; ++i)
+ for (i = 0; idx = fwspec_smendx(fw, i), i < fw->num_ids; ++i)
struct arm_smmu_device {
struct device *dev;
--
2.10.2.dirty
^ permalink raw reply related
* [PATCH] arm/vdso: introduce vdso_mremap hook
From: Russell King - ARM Linux @ 2016-11-07 18:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161101172214.2938-1-dsafonov@virtuozzo.com>
On Tue, Nov 01, 2016 at 08:22:14PM +0300, Dmitry Safonov wrote:
> diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c
> index 53cf86cf2d1a..d1001f87c2f6 100644
> --- a/arch/arm/kernel/vdso.c
> +++ b/arch/arm/kernel/vdso.c
> @@ -54,8 +54,11 @@ static const struct vm_special_mapping vdso_data_mapping = {
> .pages = &vdso_data_page,
> };
>
> +static int vdso_mremap(const struct vm_special_mapping *sm,
> + struct vm_area_struct *new_vma);
I'd much rather avoid this forward declaration. Is there any reason the
function body can't be here?
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* [PATCH v3 3/3] ARM: dmaengine: sun6i: share the dma driver with sun50i
From: Hao Zhang @ 2016-11-07 18:28 UTC (permalink / raw)
To: linux-arm-kernel
According to the datasheet, the dma of A64 support 8/16/32/64 bits
so, we can add the condition of device compatible in convert_buswidth
function and other place to determine the device whether is for A64,
and then accept the 8 bytes bus width to it.
Signed-off-by: Hao Zhang <hao5781286@gmail.com>
---
drivers/dma/sun6i-dma.c | 43 +++++++++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 8346199..8a95a1a 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -247,13 +247,17 @@ static inline s8 convert_burst(u32 maxburst)
}
}
-static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width)
+static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width,
+ struct sun6i_dma_dev *sdev)
{
- if ((addr_width < DMA_SLAVE_BUSWIDTH_1_BYTE) ||
- (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES))
+ if (((addr_width >= DMA_SLAVE_BUSWIDTH_1_BYTE) &&
+ (addr_width <= DMA_SLAVE_BUSWIDTH_4_BYTES)) ||
+ ((addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) &&
+ (of_device_is_compatible(sdev->slave.dev->of_node,
+ "allwinner,sun50i-a64-dma"))))
+ return addr_width >> 1;
+ else
return -EINVAL;
-
- return addr_width >> 1;
}
static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
@@ -508,19 +512,19 @@ static int set_config(struct sun6i_dma_dev *sdev,
src_width = convert_buswidth(sconfig->src_addr_width !=
DMA_SLAVE_BUSWIDTH_UNDEFINED ?
sconfig->src_addr_width :
- DMA_SLAVE_BUSWIDTH_4_BYTES);
+ DMA_SLAVE_BUSWIDTH_4_BYTES, sdev);
dst_burst = convert_burst(sconfig->dst_maxburst);
- dst_width = convert_buswidth(sconfig->dst_addr_width);
+ dst_width = convert_buswidth(sconfig->dst_addr_width, sdev);
break;
case DMA_DEV_TO_MEM:
src_burst = convert_burst(sconfig->src_maxburst);
- src_width = convert_buswidth(sconfig->src_addr_width);
+ src_width = convert_buswidth(sconfig->src_addr_width, sdev);
dst_burst = convert_burst(sconfig->dst_maxburst ?
sconfig->dst_maxburst : 8);
dst_width = convert_buswidth(sconfig->dst_addr_width !=
DMA_SLAVE_BUSWIDTH_UNDEFINED ?
sconfig->dst_addr_width :
- DMA_SLAVE_BUSWIDTH_4_BYTES);
+ DMA_SLAVE_BUSWIDTH_4_BYTES, sdev);
break;
default:
return -EINVAL;
@@ -577,7 +581,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
v_lli->para = NORMAL_WAIT;
burst = convert_burst(8);
- width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES, sdev);
v_lli->cfg |= DMA_CHAN_CFG_SRC_DRQ(DRQ_SDRAM) |
DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) |
DMA_CHAN_CFG_DST_LINEAR_MODE |
@@ -1028,11 +1032,23 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
.nr_max_vchans = 34,
};
+/*
+ * The A64 has 8 physical channels, a maximum DRQ port id of 27,
+ * and a total of 38 usable source and destination endpoints.
+ */
+
+static struct sun6i_dma_config sun50i_a64_dma_cfg = {
+ .nr_max_channels = 8,
+ .nr_max_requests = 27,
+ .nr_max_vchans = 38,
+};
+
static const struct of_device_id sun6i_dma_match[] = {
{ .compatible = "allwinner,sun6i-a31-dma", .data = &sun6i_a31_dma_cfg },
{ .compatible = "allwinner,sun8i-a23-dma", .data = &sun8i_a23_dma_cfg },
{ .compatible = "allwinner,sun8i-a83t-dma", .data = &sun8i_a83t_dma_cfg },
{ .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg },
+ { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun6i_dma_match);
@@ -1112,6 +1128,13 @@ static int sun6i_dma_probe(struct platform_device *pdev)
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
sdc->slave.directions = BIT(DMA_DEV_TO_MEM) |
BIT(DMA_MEM_TO_DEV);
+
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "allwinner,sun50i-a64-dma")) {
+ sdc->slave.src_addr_widths |= BIT(DMA_SLAVE_BUSWIDTH_8_BYTES);
+ sdc->slave.dst_addr_widths |= BIT(DMA_SLAVE_BUSWIDTH_8_BYTES);
+ }
+
sdc->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
sdc->slave.dev = &pdev->dev;
--
2.7.4
^ permalink raw reply related
* [PATCH v3 4/6] ARM: dts: sun6i: Add audio codec device node
From: Maxime Ripard @ 2016-11-07 18:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161107100703.5586-5-wens@csie.org>
On Mon, Nov 07, 2016 at 06:07:01PM +0800, Chen-Yu Tsai wrote:
> The A31 SoC includes the Allwinner audio codec, capable of 24-bit
> playback up to 192 kHz and 24-bit capture up to 48 kHz.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
> arch/arm/boot/dts/sun6i-a31.dtsi | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
> index 2e8bf93dcfb2..f68e6102b01b 100644
> --- a/arch/arm/boot/dts/sun6i-a31.dtsi
> +++ b/arch/arm/boot/dts/sun6i-a31.dtsi
> @@ -784,6 +784,19 @@
> reset-names = "ahb";
> };
>
> + codec: codec at 01c22c00 {
> + #sound-dai-cells = <0>;
> + compatible = "allwinner,sun6i-a31-codec";
> + reg = <0x01c22c00 0x98>;
The memory mapped region is 0x400. I fixed this and applied.
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161107/81a10d8c/attachment.sig>
^ permalink raw reply
* [PATCH v3 5/6] ARM: dts: sun6i: hummingbird: Enable internal audio codec
From: Maxime Ripard @ 2016-11-07 18:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161107100703.5586-6-wens@csie.org>
On Mon, Nov 07, 2016 at 06:07:02PM +0800, Chen-Yu Tsai wrote:
> The Hummingbird A31 has headset and line in audio jacks and an onboard
> mic routed to the pins for the SoC's internal codec. The line out pins
> are routed to an onboard speaker amp, whose output is available on a
> pin header.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Applied, thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161107/42bbd2c7/attachment.sig>
^ permalink raw reply
* [PATCH v3 6/6] ARM: dts: sun6i: sina31s: Enable internal audio codec
From: Maxime Ripard @ 2016-11-07 18:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161107100703.5586-7-wens@csie.org>
On Mon, Nov 07, 2016 at 06:07:03PM +0800, Chen-Yu Tsai wrote:
> The SinA31s routes the SoC's LINEOUT pins to a line out jack, and MIC1
> to a microphone jack, with MBIAS providing phantom power.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Applied, thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161107/7de58d87/attachment.sig>
^ permalink raw reply
* [PATCH v2 0/7] soc: renesas: Identify SoC and register with the SoC bus
From: Krzysztof Kozlowski @ 2016-11-07 18:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAMuHMdV4HG0aOr4Qp_OZXU=3jLeOJ2QaMKp09a3v4489ABbRcA@mail.gmail.com>
On Mon, Nov 07, 2016 at 10:35:31AM +0100, Geert Uytterhoeven wrote:
> On Mon, Oct 31, 2016 at 12:30 PM, Geert Uytterhoeven
> <geert+renesas@glider.be> wrote:
> > Some Renesas SoCs may exist in different revisions, providing slightly
> > different functionalities (e.g. R-Car H3 ES1.x and ES2.0), and behavior
> > (errate and quirks). This needs to be catered for by drivers and/or
> > platform code. The recently proposed soc_device_match() API seems like
> > a good fit to handle this.
> >
> > This patch series implements the core infrastructure to provide SoC and
> > revision information through the SoC bus for Renesas ARM SoCs. It
> > consists of 7 patches:
> > - Patches 1-4 provide soc_device_match(), with some related fixes,
> > - Patches 5-7 implement identification of Renesas SoCs and
> > registration with the SoC bus,
> >
> > Changes compared to v1:
> > - Add Acked-by,
> > - New patches:
> > - "[4/7] base: soc: Provide a dummy implementation of
> > soc_device_match()",
> > - "[5/7] ARM: shmobile: Document DT bindings for CCCR and PRR",
> > - "[6/7] arm64: dts: r8a7795: Add device node for PRR"
> > (more similar patches available, I'm not yet spamming you all
> > with them),
> > - Drop SoC families and family names; use fixed "Renesas" instead,
> > - Drop EMEV2, which doesn't have a chip ID register, and doesn't share
> > devices with other SoCs,
> > - Drop RZ/A1H and R-CAR M1A, which don't have chip ID registers (for
> > M1A: not accessible from the ARM core?),
> > - On arm, move "select SOC_BUS" from ARCH_RENESAS to Kconfig symbols
> > for SoCs that provide a chip ID register,
> > - Build renesas-soc only if SOC_BUS is enabled,
> > - Use "renesas,prr" and "renesas,cccr" device nodes in DT if
> > available, else fall back to hardcoded addresses for compatibility
> > with existing DTBs,
> > - Remove verification of product IDs; just print the ID instead,
> > - Don't register the SoC bus if the chip ID register is missing,
> > - Change R-Mobile APE6 fallback to use PRR instead of CCCR (it has
> > both).
> >
> > Merge strategy:
> > - In theory, patches 1-4 should go through Greg's driver core tree.
> > But it's a hard dependency for all users.
> > If people agree, I can provide an immutable branch in my
> > renesas-drivers repository, to be merged by all interested parties.
> > So far I'm aware of Freescale/NXP, and Renesas.
>
> And Samsung.
Yes, I would need it as well.
> Shall I create the immutable branch now?
...or the applying person could provide one.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH] arm/vdso: introduce vdso_mremap hook
From: Dmitry Safonov @ 2016-11-07 18:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161107182734.GL1041@n2100.armlinux.org.uk>
On 11/07/2016 09:27 PM, Russell King - ARM Linux wrote:
> On Tue, Nov 01, 2016 at 08:22:14PM +0300, Dmitry Safonov wrote:
>> diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c
>> index 53cf86cf2d1a..d1001f87c2f6 100644
>> --- a/arch/arm/kernel/vdso.c
>> +++ b/arch/arm/kernel/vdso.c
>> @@ -54,8 +54,11 @@ static const struct vm_special_mapping vdso_data_mapping = {
>> .pages = &vdso_data_page,
>> };
>>
>> +static int vdso_mremap(const struct vm_special_mapping *sm,
>> + struct vm_area_struct *new_vma);
>
> I'd much rather avoid this forward declaration. Is there any reason the
> function body can't be here?
>
Well, I didn't want it to be in the middle of static file variables -
those looks nice at this moment just on top of the file.
No other than that.
--
Dmitry
^ permalink raw reply
* [PATCH 3/3] ipmi/bt-bmc: add a sysfs file to configure a maximum response time
From: Corey Minyard @ 2016-11-07 18:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478073426-3714-4-git-send-email-clg@kaod.org>
Sorry, I was at Plumbers and extra busy with other stuff. Just getting
around to reviewing this.
On 11/02/2016 02:57 AM, C?dric Le Goater wrote:
> We could also use an ioctl for that purpose. sysfs seems a better
> approach.
>
> Signed-off-by: C?dric Le Goater <clg@kaod.org>
> ---
> drivers/char/ipmi/bt-bmc.c | 31 +++++++++++++++++++++++++++++++
> 1 file changed, 31 insertions(+)
>
> diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
> index e751e4a754b7..d7146f0e900e 100644
> --- a/drivers/char/ipmi/bt-bmc.c
> +++ b/drivers/char/ipmi/bt-bmc.c
> @@ -84,6 +84,33 @@ struct bt_bmc {
>
> static atomic_t open_count = ATOMIC_INIT(0);
>
> +static ssize_t bt_bmc_show_response_time(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct bt_bmc *bt_bmc = dev_get_drvdata(dev);
> +
> + return snprintf(buf, PAGE_SIZE - 1, "%d\n", bt_bmc->response_time);
> +}
> +
> +static ssize_t bt_bmc_set_response_time(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct bt_bmc *bt_bmc = dev_get_drvdata(dev);
> + unsigned long val;
> + int err;
> +
> + err = kstrtoul(buf, 0, &val);
> + if (err)
> + return err;
> + bt_bmc->response_time = val;
> + return count;
> +}
> +
> +static DEVICE_ATTR(response_time, 0644,
> + bt_bmc_show_response_time, bt_bmc_set_response_time);
> +
> static u8 bt_inb(struct bt_bmc *bt_bmc, int reg)
> {
> return ioread8(bt_bmc->base + reg);
> @@ -572,6 +599,10 @@ static int bt_bmc_probe(struct platform_device *pdev)
> bt_bmc_config_irq(bt_bmc, pdev);
>
> bt_bmc->response_time = BT_BMC_RESPONSE_TIME;
> + rc = device_create_file(&pdev->dev, &dev_attr_response_time);
> + if (rc)
> + dev_warn(&pdev->dev, "can't create response_time file\n");
> +
You added an extra space here.
>
> if (bt_bmc->irq) {
> dev_info(dev, "Using IRQ %d\n", bt_bmc->irq);
^ permalink raw reply
* [PATCH 2/3] ipmi/bt-bmc: maintain a request expiry list
From: Corey Minyard @ 2016-11-07 19:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478073426-3714-3-git-send-email-clg@kaod.org>
On 11/02/2016 02:57 AM, C?dric Le Goater wrote:
> Regarding the response expiration handling, the IPMI spec says :
>
> The BMC must not return a given response once the corresponding
> Request-to-Response interval has passed. The BMC can ensure this
> by maintaining its own internal list of outstanding requests through
> the interface. The BMC could age and expire the entries in the list
> by expiring the entries at an interval that is somewhat shorter than
> the specified Request-to-Response interval....
>
> To handle such case, we maintain list of received requests using the
> seq number of the BT message to identify them. The list is updated
> each time a request is received and a response is sent. The expiration
> of the reponses is handled at each updates but also with a timer.
This looks correct, but it seems awfully complicated.
Why can't you get the current time before the wait_event_interruptible()
and then compare the time before you do the write? That would seem to
accomplish the same thing without any lists or extra locks.
Also, if you are going to have multiple writers on this interface, I don't
think the interface will work correctly. I think you need to claim the
mutex first. Otherwise you might get into a situation where two writers
will wake up at the same time, the first writes and releases the mutex,
then the second will find that the interface is busy and fail.
If I am correct, the mutex will need to become interruptible and come
first, I think. (And the timing would have to start before the mutex,
of course.) This applies to both the read and write interface.
Another thing is that this is request-to-release time. If a request takes
a long time to process (say, a write to a flash device) the timeout would
need to be decreased by the processing time. It's probably ok to not
do that for the moment, but you may want to add a note. Fixing that
would require adding a timeout for each message.
-corey
> Signed-off-by: C?dric Le Goater <clg@kaod.org>
> ---
> drivers/char/ipmi/bt-bmc.c | 132 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 132 insertions(+)
>
> diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
> index fc9e8891eae3..e751e4a754b7 100644
> --- a/drivers/char/ipmi/bt-bmc.c
> +++ b/drivers/char/ipmi/bt-bmc.c
> @@ -17,6 +17,7 @@
> #include <linux/platform_device.h>
> #include <linux/poll.h>
> #include <linux/sched.h>
> +#include <linux/slab.h>
> #include <linux/timer.h>
>
> /*
> @@ -57,6 +58,15 @@
>
> #define BT_BMC_BUFFER_SIZE 256
>
> +#define BT_BMC_RESPONSE_TIME 5 /* seconds */
> +
> +struct ipmi_request {
> + struct list_head list;
> +
> + u8 seq;
> + unsigned long expires;
> +};
> +
> struct bt_bmc {
> struct device dev;
> struct miscdevice miscdev;
> @@ -65,6 +75,11 @@ struct bt_bmc {
> wait_queue_head_t queue;
> struct timer_list poll_timer;
> struct mutex mutex;
> +
> + unsigned int response_time;
> + struct timer_list requests_timer;
> + spinlock_t requests_lock;
> + struct list_head requests;
> };
>
> static atomic_t open_count = ATOMIC_INIT(0);
> @@ -163,6 +178,94 @@ static int bt_bmc_open(struct inode *inode, struct file *file)
> }
>
> /*
> + * lock should be held
> + */
> +static void drop_expired_requests(struct bt_bmc *bt_bmc)
> +{
> + unsigned long flags = 0;
> + struct ipmi_request *req, *next;
> + unsigned long next_expires = 0;
> + int start_timer = 0;
> +
> + spin_lock_irqsave(&bt_bmc->requests_lock, flags);
> + list_for_each_entry_safe(req, next, &bt_bmc->requests, list) {
> + if (time_after_eq(jiffies, req->expires)) {
> + pr_warn("BT: request seq:%d has expired. dropping\n",
> + req->seq);
> + list_del(&req->list);
> + kfree(req);
> + continue;
> + }
> + if (!start_timer) {
> + start_timer = 1;
> + next_expires = req->expires;
> + } else {
> + next_expires = min(next_expires, req->expires);
> + }
> + }
> + spin_unlock_irqrestore(&bt_bmc->requests_lock, flags);
> +
> + /* and possibly restart */
> + if (start_timer)
> + mod_timer(&bt_bmc->requests_timer, next_expires);
> +}
> +
> +static void requests_timer(unsigned long data)
> +{
> + struct bt_bmc *bt_bmc = (void *)data;
> +
> + drop_expired_requests(bt_bmc);
> +}
> +
> +static int bt_bmc_add_request(struct bt_bmc *bt_bmc, u8 seq)
> +{
> + struct ipmi_request *req;
> +
> + req = kmalloc(sizeof(*req), GFP_KERNEL);
> + if (!req)
> + return -ENOMEM;
> +
> + req->seq = seq;
> + req->expires = jiffies +
> + msecs_to_jiffies(bt_bmc->response_time * 1000);
> +
> + spin_lock(&bt_bmc->requests_lock);
> + list_add(&req->list, &bt_bmc->requests);
> + spin_unlock(&bt_bmc->requests_lock);
> +
> + drop_expired_requests(bt_bmc);
> + return 0;
> +}
> +
> +static int bt_bmc_remove_request(struct bt_bmc *bt_bmc, u8 seq)
> +{
> + struct ipmi_request *req, *next;
> + int ret = -EBADRQC; /* Invalid request code */
> +
> + spin_lock(&bt_bmc->requests_lock);
> + list_for_each_entry_safe(req, next, &bt_bmc->requests, list) {
> + /*
> + * The sequence number should be unique, so remove the
> + * first matching request found. If there are others,
> + * they should expire
> + */
> + if (req->seq == seq) {
> + list_del(&req->list);
> + kfree(req);
> + ret = 0;
> + break;
> + }
> + }
> + spin_unlock(&bt_bmc->requests_lock);
> +
> + if (ret)
> + pr_warn("BT: request seq:%d is invalid\n", seq);
> +
> + drop_expired_requests(bt_bmc);
> + return ret;
> +}
> +
> +/*
> * The BT (Block Transfer) interface means that entire messages are
> * buffered by the host before a notification is sent to the BMC that
> * there is data to be read. The first byte is the length and the
> @@ -231,6 +334,13 @@ static ssize_t bt_bmc_read(struct file *file, char __user *buf,
> len_byte = 0;
> }
>
> + if (ret > 0) {
> + int ret2 = bt_bmc_add_request(bt_bmc, kbuffer[2]);
> +
> + if (ret2)
> + ret = ret2;
> + }
> +
> clr_b_busy(bt_bmc);
>
> out_unlock:
> @@ -283,12 +393,20 @@ static ssize_t bt_bmc_write(struct file *file, const char __user *buf,
> clr_wr_ptr(bt_bmc);
>
> while (count) {
> + int ret2;
> +
> nwritten = min_t(ssize_t, count, sizeof(kbuffer));
> if (copy_from_user(&kbuffer, buf, nwritten)) {
> ret = -EFAULT;
> break;
> }
>
> + ret2 = bt_bmc_remove_request(bt_bmc, kbuffer[2]);
> + if (ret2) {
> + ret = ret2;
> + break;
> + }
> +
> bt_writen(bt_bmc, kbuffer, nwritten);
>
> count -= nwritten;
> @@ -438,6 +556,8 @@ static int bt_bmc_probe(struct platform_device *pdev)
>
> mutex_init(&bt_bmc->mutex);
> init_waitqueue_head(&bt_bmc->queue);
> + INIT_LIST_HEAD(&bt_bmc->requests);
> + spin_lock_init(&bt_bmc->requests_lock);
>
> bt_bmc->miscdev.minor = MISC_DYNAMIC_MINOR,
> bt_bmc->miscdev.name = DEVICE_NAME,
> @@ -451,6 +571,8 @@ static int bt_bmc_probe(struct platform_device *pdev)
>
> bt_bmc_config_irq(bt_bmc, pdev);
>
> + bt_bmc->response_time = BT_BMC_RESPONSE_TIME;
> +
> if (bt_bmc->irq) {
> dev_info(dev, "Using IRQ %d\n", bt_bmc->irq);
> } else {
> @@ -468,6 +590,9 @@ static int bt_bmc_probe(struct platform_device *pdev)
> BT_CR0_ENABLE_IBT,
> bt_bmc->base + BT_CR0);
>
> + setup_timer(&bt_bmc->requests_timer, requests_timer,
> + (unsigned long)bt_bmc);
> +
> clr_b_busy(bt_bmc);
>
> return 0;
> @@ -476,10 +601,17 @@ static int bt_bmc_probe(struct platform_device *pdev)
> static int bt_bmc_remove(struct platform_device *pdev)
> {
> struct bt_bmc *bt_bmc = dev_get_drvdata(&pdev->dev);
> + struct ipmi_request *req, *next;
>
> misc_deregister(&bt_bmc->miscdev);
> if (!bt_bmc->irq)
> del_timer_sync(&bt_bmc->poll_timer);
> +
> + del_timer_sync(&bt_bmc->requests_timer);
> + list_for_each_entry_safe(req, next, &bt_bmc->requests, list) {
> + list_del(&req->list);
> + kfree(req);
> + }
> return 0;
> }
>
^ permalink raw reply
* [PATCH V3 0/8] IOMMU probe deferral support
From: Will Deacon @ 2016-11-07 19:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <006f01d236ae$61751c40$245f54c0$@codeaurora.org>
On Fri, Nov 04, 2016 at 08:46:06PM +0530, Sricharan wrote:
> >>>Yikes, on second look, that definitely shouldn't be happening.
> >>>Everything below is probably the resulting fallout.
> >>
> >>[ 40.206703] vfio-pci 0000:08:00.0: Failed to setup iommu ops
> >>
> >>I think the above print which says "failed to setup iommu_ops"
> >>because the call ops->add_device failed in of_pci_iommu_configure
> >>is the reason for the failure, in my case i simply do not get this even with
> >>your scripts. ops->add_device succeeds in the rebind as well. So still
> >>checking what could be happening in your case.
> >
> >I was looking at your code base from [1].The ops->add_device
> >callback from of_pci_iommu_configure on the rebind is the
> >one which is causing the failure. But not able to spot out
> >from code which point is causing the failure. It would be very helpful
> >if i can know which is the return value from the add_device callback
> >or point inside add_device callback which fails in your setup.
> >
> >
> >[1] git://linux-arm.org/linux-rm iommu/misc
So this also applies to mainline.
> With little more try, i saw an issue where i had an failure
> similar to what you reported. The issue happens when multiple
> devices fall in to same group due to matching sids. I ended up
> doing a fix like below and it would be nice to verify if it is the same
> that we are seeing in your setup and if the fix makes a difference ?
>
> From: Sricharan R <sricharan@codeaurora.org>
> Date: Fri, 4 Nov 2016 20:28:49 +0530
> Subject: [PATCH] iommu/arm-smmu: Fix group's reference counting
>
> iommu_group_get_for_dev which gets called in the add_device
> callback, increases the reference count of the iommu_group,
> so we do an iommu_group_put after that. iommu_group_get_for_dev
> inturn calls device_group callback and in the case of arm-smmu
> we call generic_device_group/pci_device_group which takes
> care of increasing the group's reference. But when we return
> an already existing group(when multiple devices have same group)
> the reference is not incremented, resulting in issues when the
> remove_device callback for the devices is invoked.
> Fixing the same here.
>
> Signed-off-by: Sricharan R <sricharan@codeaurora.org>
> ---
> drivers/iommu/arm-smmu.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index 71ce4b6..a1d0b3c 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -1516,8 +1516,10 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
> group = smmu->s2crs[idx].group;
> }
>
> - if (group)
> + if (group) {
> + iommu_group_get_by_id(iommu_group_id(group));
> return group;
> + }
This is foul :(
I think we should either add a function for incrementing the refcount on a
group, or we should get a handle on the existing aliasing device and get
the group from that. As written, this patch is far too subtle.
Joerg -- do you have any preference?
Will
^ permalink raw reply
* [PATCH V3 0/8] IOMMU probe deferral support
From: Robin Murphy @ 2016-11-07 19:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <006f01d236ae$61751c40$245f54c0$@codeaurora.org>
Hi Sricharan,
On 04/11/16 15:16, Sricharan wrote:
> Hi Robin,
>
>>>> Yikes, on second look, that definitely shouldn't be happening.
>>>> Everything below is probably the resulting fallout.
>>>
>>> [ 40.206703] vfio-pci 0000:08:00.0: Failed to setup iommu ops
>>>
>>> I think the above print which says "failed to setup iommu_ops"
>>> because the call ops->add_device failed in of_pci_iommu_configure
>>> is the reason for the failure, in my case i simply do not get this even with
>>> your scripts. ops->add_device succeeds in the rebind as well. So still
>>> checking what could be happening in your case.
>>
>> I was looking at your code base from [1].The ops->add_device
>> callback from of_pci_iommu_configure on the rebind is the
>> one which is causing the failure. But not able to spot out
>>from code which point is causing the failure. It would be very helpful
>> if i can know which is the return value from the add_device callback
>> or point inside add_device callback which fails in your setup.
>>
>>
>> [1] git://linux-arm.org/linux-rm iommu/misc
>
> With little more try, i saw an issue where i had an failure
> similar to what you reported. The issue happens when multiple
> devices fall in to same group due to matching sids. I ended up
> doing a fix like below and it would be nice to verify if it is the same
> that we are seeing in your setup and if the fix makes a difference ?
>
> From: Sricharan R <sricharan@codeaurora.org>
> Date: Fri, 4 Nov 2016 20:28:49 +0530
> Subject: [PATCH] iommu/arm-smmu: Fix group's reference counting
>
> iommu_group_get_for_dev which gets called in the add_device
> callback, increases the reference count of the iommu_group,
> so we do an iommu_group_put after that. iommu_group_get_for_dev
> inturn calls device_group callback and in the case of arm-smmu
> we call generic_device_group/pci_device_group which takes
> care of increasing the group's reference. But when we return
> an already existing group(when multiple devices have same group)
> the reference is not incremented, resulting in issues when the
> remove_device callback for the devices is invoked.
> Fixing the same here.
Bah, yes, this does look like my fault - after flip-flopping between
about 3 different ways to keep refcounts for the S2CR entries, none of
which would quite work, I ripped it all out but apparently still got
things wrong, oh well. Thanks for figuring it out.
On the probe-deferral angle, whilst it's useful to have uncovered this
bug, I don't think we should actually be calling remove_device() from
DMA teardown. I think it's preferable from a user perspective if group
numbering remains stable, rather than changing depending on the order in
which they unbind/rebind VFIO drivers. I'm really keen to try and get
this in shape for 4.10, so I've taken the liberty of hacking up my own
branch (iommu/defer) based on v3 - would you mind taking a look at the
two "iommu/of:" commits to see what you think? (Ignore the PCI changes
to your later patches - that was an experiment which didn't really work out)
> Signed-off-by: Sricharan R <sricharan@codeaurora.org>
> ---
> drivers/iommu/arm-smmu.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index 71ce4b6..a1d0b3c 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -1516,8 +1516,10 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
> group = smmu->s2crs[idx].group;
> }
>
> - if (group)
> + if (group) {
> + iommu_group_get_by_id(iommu_group_id(group));
> return group;
This might as well just be inline, i.e.:
return iommu_group_get_by_id(iommu_group_id(group));
It's a shame we have to go all round the houses when we have the group
right there, but this is probably the most expedient fix. I guess we can
extend the API with some sort of iommu_group_get(group) overload in
future if we really want to.
Robin.
> + }
>
> if (dev_is_pci(dev))
> group = pci_device_group(dev);
>
^ permalink raw reply
* [kernel-hardening] Re: [PATCHv4 0/4] WX checking for arm64
From: Mark Rutland @ 2016-11-07 19:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161107153802.GJ19796@leverpostej>
On Mon, Nov 07, 2016 at 03:38:02PM +0000, Mark Rutland wrote:
> On Sun, Oct 30, 2016 at 03:03:07PM +0000, Catalin Marinas wrote:
> > On Thu, Oct 27, 2016 at 09:27:30AM -0700, Laura Abbott wrote:
> > > Laura Abbott (4):
> > > arm64: dump: Make ptdump debugfs a separate option
> > > arm64: dump: Make the page table dumping seq_file optional
> > > arm64: dump: Remove max_addr
> > > arm64: dump: Add checking for writable and exectuable pages
> >
> > Queued for 4.10. Thanks.
>
> Catalin mentioned to me that he saw some KASAN splats when testing; it
> looks like need a fixup something like the below.
As an aside, it looks like any ptdump usage when KASAN is enabled takes
several minutes, which at boot time looks like a hang.
AFAICT, this is because KASAN allocates *huge* VA ranges (4TB+) worth of
zeroed shadow memory at pte granularity (reusing the same pmd, pud,
tables), and the ptdump code dutifully walks this with, with the added
KASAN instrumentation overhead.
I'll try to dig into that tomorrow; I suspect/hope it's not necessary to
keep all of that mapped.
Thanks,
Mark.
^ permalink raw reply
* [PATCH v3 1/3] Documentation: DT: add dma compatible for sun50i A64 SOC.
From: Maxime Ripard @ 2016-11-07 19:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161107181457.GA3619@arx12>
On Tue, Nov 08, 2016 at 02:14:57AM +0800, Hao Zhang wrote:
> This adds documentation of the sun50i a64 dma binding compatible.
>
> Signed-off-by: Hao Zhang <hao5781286@gmail.com>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Thanks,
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161107/ab8769ab/attachment.sig>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox