* [PATCH v4 2/8] [media] stm32-dcmi: STM32 DCMI camera interface driver
From: Hugues Fruchet @ 2017-04-20 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492704445-22186-1-git-send-email-hugues.fruchet@st.com>
This V4L2 subdev driver enables Digital Camera Memory Interface (DCMI)
of STMicroelectronics STM32 SoC series.
Reviewed-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
drivers/media/platform/Kconfig | 12 +
drivers/media/platform/Makefile | 2 +
drivers/media/platform/stm32/Makefile | 1 +
drivers/media/platform/stm32/stm32-dcmi.c | 1419 +++++++++++++++++++++++++++++
4 files changed, 1434 insertions(+)
create mode 100644 drivers/media/platform/stm32/Makefile
create mode 100644 drivers/media/platform/stm32/stm32-dcmi.c
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ac026ee..de6e18b 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -114,6 +114,18 @@ config VIDEO_S3C_CAMIF
To compile this driver as a module, choose M here: the module
will be called s3c-camif.
+config VIDEO_STM32_DCMI
+ tristate "Digital Camera Memory Interface (DCMI) support"
+ depends on VIDEO_V4L2 && OF && HAS_DMA
+ depends on ARCH_STM32 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This module makes the STM32 Digital Camera Memory Interface (DCMI)
+ available as a v4l2 device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called stm32-dcmi.
+
source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/am437x/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 63303d6..231f3c2 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -68,6 +68,8 @@ obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/
obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/
+obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
+
ccflags-y += -I$(srctree)/drivers/media/i2c
obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/stm32/Makefile
new file mode 100644
index 0000000..9b606a7
--- /dev/null
+++ b/drivers/media/platform/stm32/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
new file mode 100644
index 0000000..0ed3bd9
--- /dev/null
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -0,0 +1,1419 @@
+/*
+ * Driver for STM32 Digital Camera Memory Interface
+ *
+ * Copyright (C) STMicroelectronics SA 2017
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ * Hugues Fruchet <hugues.fruchet@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * This driver is based on atmel_isi.c
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-of.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define DRV_NAME "stm32-dcmi"
+
+/* Registers offset for DCMI */
+#define DCMI_CR 0x00 /* Control Register */
+#define DCMI_SR 0x04 /* Status Register */
+#define DCMI_RIS 0x08 /* Raw Interrupt Status register */
+#define DCMI_IER 0x0C /* Interrupt Enable Register */
+#define DCMI_MIS 0x10 /* Masked Interrupt Status register */
+#define DCMI_ICR 0x14 /* Interrupt Clear Register */
+#define DCMI_ESCR 0x18 /* Embedded Synchronization Code Register */
+#define DCMI_ESUR 0x1C /* Embedded Synchronization Unmask Register */
+#define DCMI_CWSTRT 0x20 /* Crop Window STaRT */
+#define DCMI_CWSIZE 0x24 /* Crop Window SIZE */
+#define DCMI_DR 0x28 /* Data Register */
+#define DCMI_IDR 0x2C /* IDentifier Register */
+
+/* Bits definition for control register (DCMI_CR) */
+#define CR_CAPTURE BIT(0)
+#define CR_CM BIT(1)
+#define CR_CROP BIT(2)
+#define CR_JPEG BIT(3)
+#define CR_ESS BIT(4)
+#define CR_PCKPOL BIT(5)
+#define CR_HSPOL BIT(6)
+#define CR_VSPOL BIT(7)
+#define CR_FCRC_0 BIT(8)
+#define CR_FCRC_1 BIT(9)
+#define CR_EDM_0 BIT(10)
+#define CR_EDM_1 BIT(11)
+#define CR_ENABLE BIT(14)
+
+/* Bits definition for status register (DCMI_SR) */
+#define SR_HSYNC BIT(0)
+#define SR_VSYNC BIT(1)
+#define SR_FNE BIT(2)
+
+/*
+ * Bits definition for interrupt registers
+ * (DCMI_RIS, DCMI_IER, DCMI_MIS, DCMI_ICR)
+ */
+#define IT_FRAME BIT(0)
+#define IT_OVR BIT(1)
+#define IT_ERR BIT(2)
+#define IT_VSYNC BIT(3)
+#define IT_LINE BIT(4)
+
+enum state {
+ STOPPED = 0,
+ RUNNING,
+ STOPPING,
+};
+
+#define MAX_BUS_WIDTH 14
+#define MAX_SUPPORT_WIDTH 2048
+#define MAX_SUPPORT_HEIGHT 2048
+#define MIN_SUPPORT_WIDTH 16
+#define MIN_SUPPORT_HEIGHT 16
+#define TIMEOUT_MS 1000
+
+struct dcmi_graph_entity {
+ struct device_node *node;
+
+ struct v4l2_async_subdev asd;
+ struct v4l2_subdev *subdev;
+};
+
+struct dcmi_format {
+ u32 fourcc;
+ u32 mbus_code;
+ u8 bpp;
+};
+
+struct dcmi_buf {
+ struct vb2_v4l2_buffer vb;
+ bool prepared;
+ dma_addr_t paddr;
+ size_t size;
+ struct list_head list;
+};
+
+struct stm32_dcmi {
+ /* Protects the access of variables shared within the interrupt */
+ spinlock_t irqlock;
+ struct device *dev;
+ void __iomem *regs;
+ struct resource *res;
+ struct reset_control *rstc;
+ int sequence;
+ struct list_head buffers;
+ struct dcmi_buf *active;
+
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev;
+ struct v4l2_async_notifier notifier;
+ struct dcmi_graph_entity entity;
+ struct v4l2_format fmt;
+
+ const struct dcmi_format **user_formats;
+ unsigned int num_user_formats;
+ const struct dcmi_format *current_fmt;
+
+ /* Protect this data structure */
+ struct mutex lock;
+ struct vb2_queue queue;
+
+ struct v4l2_of_bus_parallel bus;
+ struct completion complete;
+ struct clk *mclk;
+ enum state state;
+ struct dma_chan *dma_chan;
+ dma_cookie_t dma_cookie;
+ u32 misr;
+ int errors_count;
+ int buffers_count;
+};
+
+static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct stm32_dcmi, notifier);
+}
+
+static inline u32 reg_read(void __iomem *base, u32 reg)
+{
+ return readl_relaxed(base + reg);
+}
+
+static inline void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+ writel_relaxed(val, base + reg);
+}
+
+static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
+{
+ reg_write(base, reg, reg_read(base, reg) | mask);
+}
+
+static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
+{
+ reg_write(base, reg, reg_read(base, reg) & ~mask);
+}
+
+static int dcmi_start_capture(struct stm32_dcmi *dcmi);
+
+static void dcmi_dma_callback(void *param)
+{
+ struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
+ struct dma_chan *chan = dcmi->dma_chan;
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ spin_lock(&dcmi->irqlock);
+
+ /* Check DMA status */
+ status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state);
+
+ switch (status) {
+ case DMA_IN_PROGRESS:
+ dev_dbg(dcmi->dev, "%s: Received DMA_IN_PROGRESS\n", __func__);
+ break;
+ case DMA_PAUSED:
+ dev_err(dcmi->dev, "%s: Received DMA_PAUSED\n", __func__);
+ break;
+ case DMA_ERROR:
+ dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__);
+ break;
+ case DMA_COMPLETE:
+ dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
+
+ if (dcmi->active) {
+ struct dcmi_buf *buf = dcmi->active;
+ struct vb2_v4l2_buffer *vbuf = &dcmi->active->vb;
+
+ vbuf->sequence = dcmi->sequence++;
+ vbuf->field = V4L2_FIELD_NONE;
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
+ vb2_set_plane_payload(&vbuf->vb2_buf, 0, buf->size);
+ vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
+ dev_dbg(dcmi->dev, "buffer[%d] done seq=%d\n",
+ vbuf->vb2_buf.index, vbuf->sequence);
+
+ dcmi->buffers_count++;
+ dcmi->active = NULL;
+ }
+
+ /* Restart a new DMA transfer with next buffer */
+ if (dcmi->state == RUNNING) {
+ int ret;
+
+ if (list_empty(&dcmi->buffers)) {
+ dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer",
+ __func__);
+ dcmi->errors_count++;
+ dcmi->active = NULL;
+
+ spin_unlock(&dcmi->irqlock);
+ return;
+ }
+
+ dcmi->active = list_entry(dcmi->buffers.next,
+ struct dcmi_buf, list);
+
+ list_del_init(&dcmi->active->list);
+
+ ret = dcmi_start_capture(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete",
+ __func__);
+
+ spin_unlock(&dcmi->irqlock);
+ return;
+ }
+
+ /* Enable capture */
+ reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE);
+ }
+
+ break;
+ default:
+ dev_err(dcmi->dev, "%s: Received unknown status\n", __func__);
+ break;
+ }
+
+ spin_unlock(&dcmi->irqlock);
+}
+
+static int dcmi_start_dma(struct stm32_dcmi *dcmi,
+ struct dcmi_buf *buf)
+{
+ struct dma_async_tx_descriptor *desc = NULL;
+ struct dma_slave_config config;
+ int ret;
+
+ memset(&config, 0, sizeof(config));
+
+ config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR;
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ config.dst_maxburst = 4;
+
+ /* Configure DMA channel */
+ ret = dmaengine_slave_config(dcmi->dma_chan, &config);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Prepare a DMA transaction */
+ desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr,
+ buf->size,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer size %zu\n",
+ __func__, buf->size);
+ return -EINVAL;
+ }
+
+ /* Set completion callback routine for notification */
+ desc->callback = dcmi_dma_callback;
+ desc->callback_param = dcmi;
+
+ /* Push current DMA transaction in the pending queue */
+ dcmi->dma_cookie = dmaengine_submit(desc);
+
+ dma_async_issue_pending(dcmi->dma_chan);
+
+ return 0;
+}
+
+static int dcmi_start_capture(struct stm32_dcmi *dcmi)
+{
+ int ret;
+ struct dcmi_buf *buf = dcmi->active;
+
+ if (!buf)
+ return -EINVAL;
+
+ ret = dcmi_start_dma(dcmi, buf);
+ if (ret) {
+ dcmi->errors_count++;
+ return ret;
+ }
+
+ /* Enable capture */
+ reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE);
+
+ return 0;
+}
+
+static irqreturn_t dcmi_irq_thread(int irq, void *arg)
+{
+ struct stm32_dcmi *dcmi = arg;
+ int ret;
+
+ spin_lock(&dcmi->irqlock);
+
+ /* Stop capture is required */
+ if (dcmi->state == STOPPING) {
+ reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+
+ dcmi->state = STOPPED;
+
+ complete(&dcmi->complete);
+
+ spin_unlock(&dcmi->irqlock);
+ return IRQ_HANDLED;
+ }
+
+ if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) {
+ /*
+ * An overflow or an error has been detected,
+ * stop current DMA transfert & restart it
+ */
+ dev_warn(dcmi->dev, "%s: Overflow or error detected\n",
+ __func__);
+
+ dcmi->errors_count++;
+ dmaengine_terminate_all(dcmi->dma_chan);
+
+ reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR);
+
+ dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
+
+ ret = dcmi_start_capture(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n",
+ __func__);
+
+ spin_unlock(&dcmi->irqlock);
+ return IRQ_HANDLED;
+ }
+ }
+
+ spin_unlock(&dcmi->irqlock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dcmi_irq_callback(int irq, void *arg)
+{
+ struct stm32_dcmi *dcmi = arg;
+
+ spin_lock(&dcmi->irqlock);
+
+ dcmi->misr = reg_read(dcmi->regs, DCMI_MIS);
+
+ /* Clear interrupt */
+ reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR);
+
+ spin_unlock(&dcmi->irqlock);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int dcmi_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
+ unsigned int size;
+
+ size = dcmi->fmt.fmt.pix.sizeimage;
+
+ /* Make sure the image size is large enough */
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ dcmi->active = NULL;
+
+ dev_dbg(dcmi->dev, "Setup queue, count=%d, size=%d\n",
+ *nbuffers, size);
+
+ return 0;
+}
+
+static int dcmi_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
+
+static int dcmi_buf_prepare(struct vb2_buffer *vb)
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
+ unsigned long size;
+
+ size = dcmi->fmt.fmt.pix.sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(dcmi->dev, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ if (!buf->prepared) {
+ /* Get memory addresses */
+ buf->paddr =
+ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0);
+ buf->prepared = true;
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
+
+ dev_dbg(dcmi->dev, "buffer[%d] phy=0x%pad size=%zu\n",
+ vb->index, &buf->paddr, buf->size);
+ }
+
+ return 0;
+}
+
+static void dcmi_buf_queue(struct vb2_buffer *vb)
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&dcmi->irqlock, flags);
+
+ if ((dcmi->state == RUNNING) && (!dcmi->active)) {
+ int ret;
+
+ dcmi->active = buf;
+
+ dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
+ buf->vb.vb2_buf.index);
+
+ ret = dcmi_start_capture(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n",
+ __func__);
+
+ spin_unlock_irqrestore(&dcmi->irqlock, flags);
+ return;
+ }
+ } else {
+ /* Enqueue to video buffers list */
+ list_add_tail(&buf->list, &dcmi->buffers);
+ }
+
+ spin_unlock_irqrestore(&dcmi->irqlock, flags);
+}
+
+static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
+ struct dcmi_buf *buf, *node;
+ u32 val;
+ int ret;
+
+ /* Enable stream on the sub device */
+ ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD) {
+ dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error",
+ __func__);
+ goto err_release_buffers;
+ }
+
+ if (clk_enable(dcmi->mclk)) {
+ dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock",
+ __func__);
+ goto err_subdev_streamoff;
+ }
+
+ spin_lock_irq(&dcmi->irqlock);
+
+ val = reg_read(dcmi->regs, DCMI_CR);
+
+ val &= ~(CR_PCKPOL | CR_HSPOL | CR_VSPOL |
+ CR_EDM_0 | CR_EDM_1 | CR_FCRC_0 |
+ CR_FCRC_1 | CR_JPEG | CR_ESS);
+
+ /* Set bus width */
+ switch (dcmi->bus.bus_width) {
+ case 14:
+ val &= CR_EDM_0 + CR_EDM_1;
+ break;
+ case 12:
+ val &= CR_EDM_1;
+ break;
+ case 10:
+ val &= CR_EDM_0;
+ break;
+ default:
+ /* Set bus width to 8 bits by default */
+ break;
+ }
+
+ /* Set vertical synchronization polarity */
+ if (dcmi->bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ val |= CR_VSPOL;
+
+ /* Set horizontal synchronization polarity */
+ if (dcmi->bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ val |= CR_HSPOL;
+
+ /* Set pixel clock polarity */
+ if (dcmi->bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ val |= CR_PCKPOL;
+
+ reg_write(dcmi->regs, DCMI_CR, val);
+
+ /* Enable dcmi */
+ reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
+
+ dcmi->state = RUNNING;
+
+ dcmi->sequence = 0;
+ dcmi->errors_count = 0;
+ dcmi->buffers_count = 0;
+ dcmi->active = NULL;
+
+ /*
+ * Start transfer if at least one buffer has been queued,
+ * otherwise transfer is defered at buffer queueing
+ */
+ if (list_empty(&dcmi->buffers)) {
+ dev_dbg(dcmi->dev, "Start streaming is defered to next buffer queueing\n");
+ spin_unlock_irq(&dcmi->irqlock);
+ return 0;
+ }
+
+ dcmi->active = list_entry(dcmi->buffers.next, struct dcmi_buf, list);
+ list_del_init(&dcmi->active->list);
+
+ dev_dbg(dcmi->dev, "Start streaming, starting capture\n");
+
+ ret = dcmi_start_capture(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture",
+ __func__);
+
+ spin_unlock_irq(&dcmi->irqlock);
+ goto err_subdev_streamoff;
+ }
+
+ /* Enable interruptions */
+ reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+
+ spin_unlock_irq(&dcmi->irqlock);
+
+ return 0;
+
+err_subdev_streamoff:
+ v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0);
+
+err_release_buffers:
+ spin_lock_irq(&dcmi->irqlock);
+ /*
+ * Return all buffers to vb2 in QUEUED state.
+ * This will give ownership back to userspace
+ */
+ if (dcmi->active) {
+ buf = dcmi->active;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ dcmi->active = NULL;
+ }
+ list_for_each_entry_safe(buf, node, &dcmi->buffers, list) {
+ list_del_init(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irq(&dcmi->irqlock);
+
+ return ret;
+}
+
+static void dcmi_stop_streaming(struct vb2_queue *vq)
+{
+ struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
+ struct dcmi_buf *buf, *node;
+ unsigned long time_ms = msecs_to_jiffies(TIMEOUT_MS);
+ long timeout;
+ int ret;
+
+ /* Disable stream on the sub device */
+ ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0);
+ if (ret && ret != -ENOIOCTLCMD)
+ dev_err(dcmi->dev, "stream off failed in subdev\n");
+
+ dcmi->state = STOPPING;
+
+ timeout = wait_for_completion_interruptible_timeout(&dcmi->complete,
+ time_ms);
+
+ spin_lock_irq(&dcmi->irqlock);
+
+ /* Disable interruptions */
+ reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
+
+ /* Disable DCMI */
+ reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE);
+
+ if (!timeout) {
+ dev_err(dcmi->dev, "Timeout during stop streaming\n");
+ dcmi->state = STOPPED;
+ }
+
+ /* Return all queued buffers to vb2 in ERROR state */
+ if (dcmi->active) {
+ buf = dcmi->active;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ dcmi->active = NULL;
+ }
+ list_for_each_entry_safe(buf, node, &dcmi->buffers, list) {
+ list_del_init(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irq(&dcmi->irqlock);
+
+ /* Stop all pending DMA operations */
+ dmaengine_terminate_all(dcmi->dma_chan);
+
+ clk_disable(dcmi->mclk);
+
+ dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n",
+ dcmi->errors_count, dcmi->buffers_count);
+}
+
+static struct vb2_ops dcmi_video_qops = {
+ .queue_setup = dcmi_queue_setup,
+ .buf_init = dcmi_buf_init,
+ .buf_prepare = dcmi_buf_prepare,
+ .buf_queue = dcmi_buf_queue,
+ .start_streaming = dcmi_start_streaming,
+ .stop_streaming = dcmi_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int dcmi_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ *fmt = dcmi->fmt;
+
+ return 0;
+}
+
+static const struct dcmi_format *find_format_by_fourcc(struct stm32_dcmi *dcmi,
+ unsigned int fourcc)
+{
+ unsigned int num_formats = dcmi->num_user_formats;
+ const struct dcmi_format *fmt;
+ unsigned int i;
+
+ for (i = 0; i < num_formats; i++) {
+ fmt = dcmi->user_formats[i];
+ if (fmt->fourcc == fourcc)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
+ const struct dcmi_format **current_fmt)
+{
+ const struct dcmi_format *dcmi_fmt;
+ struct v4l2_pix_format *pixfmt = &f->fmt.pix;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+ int ret;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ dcmi_fmt = find_format_by_fourcc(dcmi, pixfmt->pixelformat);
+ if (!dcmi_fmt) {
+ dcmi_fmt = dcmi->user_formats[dcmi->num_user_formats - 1];
+ pixfmt->pixelformat = dcmi_fmt->fourcc;
+ }
+
+ /* Limit to hardware capabilities */
+ if (pixfmt->width > MAX_SUPPORT_WIDTH)
+ pixfmt->width = MAX_SUPPORT_WIDTH;
+ if (pixfmt->height > MAX_SUPPORT_HEIGHT)
+ pixfmt->height = MAX_SUPPORT_HEIGHT;
+ if (pixfmt->width < MIN_SUPPORT_WIDTH)
+ pixfmt->width = MIN_SUPPORT_WIDTH;
+ if (pixfmt->height < MIN_SUPPORT_HEIGHT)
+ pixfmt->height = MIN_SUPPORT_HEIGHT;
+
+ v4l2_fill_mbus_format(&format.format, pixfmt, dcmi_fmt->mbus_code);
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt,
+ &pad_cfg, &format);
+ if (ret < 0)
+ return ret;
+
+ v4l2_fill_pix_format(pixfmt, &format.format);
+
+ pixfmt->field = V4L2_FIELD_NONE;
+ pixfmt->bytesperline = pixfmt->width * dcmi_fmt->bpp;
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+ if (current_fmt)
+ *current_fmt = dcmi_fmt;
+
+ return 0;
+}
+
+static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f)
+{
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ const struct dcmi_format *current_fmt;
+ int ret;
+
+ ret = dcmi_try_fmt(dcmi, f, ¤t_fmt);
+ if (ret)
+ return ret;
+
+ v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
+ current_fmt->mbus_code);
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad,
+ set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ dcmi->fmt = *f;
+ dcmi->current_fmt = current_fmt;
+
+ return 0;
+}
+
+static int dcmi_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ if (vb2_is_streaming(&dcmi->queue))
+ return -EBUSY;
+
+ return dcmi_set_fmt(dcmi, f);
+}
+
+static int dcmi_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ return dcmi_try_fmt(dcmi, f, NULL);
+}
+
+static int dcmi_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ if (f->index >= dcmi->num_user_formats)
+ return -EINVAL;
+
+ f->pixelformat = dcmi->user_formats[f->index]->fourcc;
+ return 0;
+}
+
+static int dcmi_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, "STM32 Digital Camera Memory Interface",
+ sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform:dcmi", sizeof(cap->bus_info));
+ return 0;
+}
+
+static int dcmi_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strlcpy(i->name, "Camera", sizeof(i->name));
+ return 0;
+}
+
+static int dcmi_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int dcmi_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int dcmi_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+ const struct dcmi_format *dcmi_fmt;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .index = fsize->index,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ dcmi_fmt = find_format_by_fourcc(dcmi, fsize->pixel_format);
+ if (!dcmi_fmt)
+ return -EINVAL;
+
+ fse.code = dcmi_fmt->mbus_code;
+
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad, enum_frame_size,
+ NULL, &fse);
+ if (ret)
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fse.max_width;
+ fsize->discrete.height = fse.max_height;
+
+ return 0;
+}
+
+static int dcmi_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+ const struct dcmi_format *dcmi_fmt;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = fival->index,
+ .width = fival->width,
+ .height = fival->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ dcmi_fmt = find_format_by_fourcc(dcmi, fival->pixel_format);
+ if (!dcmi_fmt)
+ return -EINVAL;
+
+ fie.code = dcmi_fmt->mbus_code;
+
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad,
+ enum_frame_interval, NULL, &fie);
+ if (ret)
+ return ret;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = fie.interval;
+
+ return 0;
+}
+
+static const struct of_device_id stm32_dcmi_of_match[] = {
+ { .compatible = "st,stm32-dcmi"},
+ { /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, stm32_dcmi_of_match);
+
+static int dcmi_open(struct file *file)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+ struct v4l2_subdev *sd = dcmi->entity.subdev;
+ int ret;
+
+ if (mutex_lock_interruptible(&dcmi->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto unlock;
+
+ if (!v4l2_fh_is_singular_file(file))
+ goto fh_rel;
+
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto fh_rel;
+
+ ret = dcmi_set_fmt(dcmi, &dcmi->fmt);
+ if (ret)
+ v4l2_subdev_call(sd, core, s_power, 0);
+fh_rel:
+ if (ret)
+ v4l2_fh_release(file);
+unlock:
+ mutex_unlock(&dcmi->lock);
+ return ret;
+}
+
+static int dcmi_release(struct file *file)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+ struct v4l2_subdev *sd = dcmi->entity.subdev;
+ bool fh_singular;
+ int ret;
+
+ mutex_lock(&dcmi->lock);
+
+ fh_singular = v4l2_fh_is_singular_file(file);
+
+ ret = _vb2_fop_release(file, NULL);
+
+ if (fh_singular)
+ v4l2_subdev_call(sd, core, s_power, 0);
+
+ mutex_unlock(&dcmi->lock);
+
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops dcmi_ioctl_ops = {
+ .vidioc_querycap = dcmi_querycap,
+
+ .vidioc_try_fmt_vid_cap = dcmi_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = dcmi_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = dcmi_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = dcmi_enum_fmt_vid_cap,
+
+ .vidioc_enum_input = dcmi_enum_input,
+ .vidioc_g_input = dcmi_g_input,
+ .vidioc_s_input = dcmi_s_input,
+
+ .vidioc_enum_framesizes = dcmi_enum_framesizes,
+ .vidioc_enum_frameintervals = dcmi_enum_frameintervals,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations dcmi_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = dcmi_open,
+ .release = dcmi_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+#ifndef CONFIG_MMU
+ .get_unmapped_area = vb2_fop_get_unmapped_area,
+#endif
+ .read = vb2_fop_read,
+};
+
+static int dcmi_set_default_fmt(struct stm32_dcmi *dcmi)
+{
+ struct v4l2_format f = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = CIF_WIDTH,
+ .height = CIF_HEIGHT,
+ .field = V4L2_FIELD_NONE,
+ .pixelformat = dcmi->user_formats[0]->fourcc,
+ },
+ };
+ int ret;
+
+ ret = dcmi_try_fmt(dcmi, &f, NULL);
+ if (ret)
+ return ret;
+ dcmi->current_fmt = dcmi->user_formats[0];
+ dcmi->fmt = f;
+ return 0;
+}
+
+static const struct dcmi_format dcmi_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .bpp = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .bpp = 2,
+ },
+};
+
+static int dcmi_formats_init(struct stm32_dcmi *dcmi)
+{
+ const struct dcmi_format *dcmi_fmts[ARRAY_SIZE(dcmi_formats)];
+ unsigned int num_fmts = 0, i, j;
+ struct v4l2_subdev *subdev = dcmi->entity.subdev;
+ struct v4l2_subdev_mbus_code_enum mbus_code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ while (!v4l2_subdev_call(subdev, pad, enum_mbus_code,
+ NULL, &mbus_code)) {
+ for (i = 0; i < ARRAY_SIZE(dcmi_formats); i++) {
+ if (dcmi_formats[i].mbus_code != mbus_code.code)
+ continue;
+
+ /* Code supported, have we got this fourcc yet? */
+ for (j = 0; j < num_fmts; j++)
+ if (dcmi_fmts[j]->fourcc ==
+ dcmi_formats[i].fourcc)
+ /* Already available */
+ break;
+ if (j == num_fmts)
+ /* New */
+ dcmi_fmts[num_fmts++] = dcmi_formats + i;
+ }
+ mbus_code.index++;
+ }
+
+ if (!num_fmts)
+ return -ENXIO;
+
+ dcmi->num_user_formats = num_fmts;
+ dcmi->user_formats = devm_kcalloc(dcmi->dev,
+ num_fmts, sizeof(struct dcmi_format *),
+ GFP_KERNEL);
+ if (!dcmi->user_formats) {
+ dev_err(dcmi->dev, "could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ memcpy(dcmi->user_formats, dcmi_fmts,
+ num_fmts * sizeof(struct dcmi_format *));
+ dcmi->current_fmt = dcmi->user_formats[0];
+
+ return 0;
+}
+
+static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
+ int ret;
+
+ dcmi->vdev->ctrl_handler = dcmi->entity.subdev->ctrl_handler;
+ ret = dcmi_formats_init(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "No supported mediabus format found\n");
+ return ret;
+ }
+
+ ret = dcmi_set_default_fmt(dcmi);
+ if (ret) {
+ dev_err(dcmi->dev, "Could not set default format\n");
+ return ret;
+ }
+
+ ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ dev_err(dcmi->dev, "Failed to register video device\n");
+ return ret;
+ }
+
+ dev_dbg(dcmi->dev, "Device registered as %s\n",
+ video_device_node_name(dcmi->vdev));
+ return 0;
+}
+
+static void dcmi_graph_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
+
+ dev_dbg(dcmi->dev, "Removing %s\n", video_device_node_name(dcmi->vdev));
+
+ /* Checks internaly if vdev has been init or not */
+ video_unregister_device(dcmi->vdev);
+}
+
+static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
+
+ dev_dbg(dcmi->dev, "Subdev %s bound\n", subdev->name);
+
+ dcmi->entity.subdev = subdev;
+
+ return 0;
+}
+
+static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node)
+{
+ struct device_node *ep = NULL;
+ struct device_node *remote;
+
+ while (1) {
+ ep = of_graph_get_next_endpoint(node, ep);
+ if (!ep)
+ return -EINVAL;
+
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote) {
+ of_node_put(ep);
+ return -EINVAL;
+ }
+
+ /* Remote node to connect */
+ dcmi->entity.node = remote;
+ dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_OF;
+ dcmi->entity.asd.match.of.node = remote;
+ return 0;
+ }
+}
+
+static int dcmi_graph_init(struct stm32_dcmi *dcmi)
+{
+ struct v4l2_async_subdev **subdevs = NULL;
+ int ret;
+
+ /* Parse the graph to extract a list of subdevice DT nodes. */
+ ret = dcmi_graph_parse(dcmi, dcmi->dev->of_node);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "Graph parsing failed\n");
+ return ret;
+ }
+
+ /* Register the subdevices notifier. */
+ subdevs = devm_kzalloc(dcmi->dev, sizeof(*subdevs), GFP_KERNEL);
+ if (!subdevs) {
+ of_node_put(dcmi->entity.node);
+ return -ENOMEM;
+ }
+
+ subdevs[0] = &dcmi->entity.asd;
+
+ dcmi->notifier.subdevs = subdevs;
+ dcmi->notifier.num_subdevs = 1;
+ dcmi->notifier.bound = dcmi_graph_notify_bound;
+ dcmi->notifier.unbind = dcmi_graph_notify_unbind;
+ dcmi->notifier.complete = dcmi_graph_notify_complete;
+
+ ret = v4l2_async_notifier_register(&dcmi->v4l2_dev, &dcmi->notifier);
+ if (ret < 0) {
+ dev_err(dcmi->dev, "Notifier registration failed\n");
+ of_node_put(dcmi->entity.node);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dcmi_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match = NULL;
+ struct v4l2_of_endpoint ep;
+ struct stm32_dcmi *dcmi;
+ struct vb2_queue *q;
+ struct dma_chan *chan;
+ struct clk *mclk;
+ int irq;
+ int ret = 0;
+
+ match = of_match_device(of_match_ptr(stm32_dcmi_of_match), &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Could not find a match in devicetree\n");
+ return -ENODEV;
+ }
+
+ dcmi = devm_kzalloc(&pdev->dev, sizeof(struct stm32_dcmi), GFP_KERNEL);
+ if (!dcmi)
+ return -ENOMEM;
+
+ dcmi->rstc = of_reset_control_get(np, NULL);
+ if (IS_ERR(dcmi->rstc)) {
+ dev_err(&pdev->dev, "Could not get reset control\n");
+ return -ENODEV;
+ }
+
+ /* Get bus characteristics from devicetree */
+ np = of_graph_get_next_endpoint(np, NULL);
+ if (!np) {
+ dev_err(&pdev->dev, "Could not find the endpoint\n");
+ of_node_put(np);
+ goto err_reset_control_put;
+ }
+
+ ret = v4l2_of_parse_endpoint(np, &ep);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not parse the endpoint\n");
+ of_node_put(np);
+ goto err_reset_control_put;
+ }
+
+ if (ep.bus_type == V4L2_MBUS_CSI2) {
+ dev_err(&pdev->dev, "CSI bus not supported\n");
+ of_node_put(np);
+ goto err_reset_control_put;
+ }
+ dcmi->bus.flags = ep.bus.parallel.flags;
+ dcmi->bus.bus_width = ep.bus.parallel.bus_width;
+ dcmi->bus.data_shift = ep.bus.parallel.data_shift;
+
+ of_node_put(np);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "Could not get irq\n");
+ return -ENODEV;
+ }
+
+ dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!dcmi->res) {
+ dev_err(&pdev->dev, "Could not get resource\n");
+ return -ENODEV;
+ }
+
+ dcmi->regs = devm_ioremap_resource(&pdev->dev, dcmi->res);
+ if (IS_ERR(dcmi->regs)) {
+ dev_err(&pdev->dev, "Could not map registers\n");
+ return PTR_ERR(dcmi->regs);
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, dcmi_irq_callback,
+ dcmi_irq_thread, IRQF_ONESHOT,
+ dev_name(&pdev->dev), dcmi);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
+ return -ENODEV;
+ }
+
+ mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(mclk)) {
+ dev_err(&pdev->dev, "Unable to get mclk\n");
+ return PTR_ERR(mclk);
+ }
+
+ chan = dma_request_slave_channel(&pdev->dev, "tx");
+ if (!chan) {
+ dev_info(&pdev->dev, "Unable to request DMA channel, defer probing\n");
+ return -EPROBE_DEFER;
+ }
+
+ ret = clk_prepare(mclk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to prepare mclk %p\n", mclk);
+ goto err_dma_release;
+ }
+
+ spin_lock_init(&dcmi->irqlock);
+ mutex_init(&dcmi->lock);
+ init_completion(&dcmi->complete);
+ INIT_LIST_HEAD(&dcmi->buffers);
+
+ dcmi->dev = &pdev->dev;
+ dcmi->mclk = mclk;
+ dcmi->state = STOPPED;
+ dcmi->dma_chan = chan;
+
+ q = &dcmi->queue;
+
+ /* Initialize the top-level structure */
+ ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev);
+ if (ret)
+ goto err_clk_unprepare;
+
+ dcmi->vdev = video_device_alloc();
+ if (!dcmi->vdev) {
+ ret = -ENOMEM;
+ goto err_device_unregister;
+ }
+
+ /* Video node */
+ dcmi->vdev->fops = &dcmi_fops;
+ dcmi->vdev->v4l2_dev = &dcmi->v4l2_dev;
+ dcmi->vdev->queue = &dcmi->queue;
+ strlcpy(dcmi->vdev->name, KBUILD_MODNAME, sizeof(dcmi->vdev->name));
+ dcmi->vdev->release = video_device_release;
+ dcmi->vdev->ioctl_ops = &dcmi_ioctl_ops;
+ dcmi->vdev->lock = &dcmi->lock;
+ dcmi->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ video_set_drvdata(dcmi->vdev, dcmi);
+
+ /* Buffer queue */
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
+ q->lock = &dcmi->lock;
+ q->drv_priv = dcmi;
+ q->buf_struct_size = sizeof(struct dcmi_buf);
+ q->ops = &dcmi_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+ q->dev = &pdev->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize vb2 queue\n");
+ goto err_device_release;
+ }
+
+ ret = dcmi_graph_init(dcmi);
+ if (ret < 0)
+ goto err_device_release;
+
+ /* Reset device */
+ ret = reset_control_assert(dcmi->rstc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to assert the reset line\n");
+ goto err_device_release;
+ }
+
+ usleep_range(3000, 5000);
+
+ ret = reset_control_deassert(dcmi->rstc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to deassert the reset line\n");
+ goto err_device_release;
+ }
+
+ dev_info(&pdev->dev, "Probe done\n");
+
+ platform_set_drvdata(pdev, dcmi);
+ return 0;
+
+err_reset_control_put:
+ reset_control_put(dcmi->rstc);
+err_device_release:
+ video_device_release(dcmi->vdev);
+err_device_unregister:
+ v4l2_device_unregister(&dcmi->v4l2_dev);
+err_clk_unprepare:
+ clk_unprepare(dcmi->mclk);
+err_dma_release:
+ dma_release_channel(dcmi->dma_chan);
+
+ return ret;
+}
+
+static int dcmi_remove(struct platform_device *pdev)
+{
+ struct stm32_dcmi *dcmi = platform_get_drvdata(pdev);
+
+ v4l2_async_notifier_unregister(&dcmi->notifier);
+ v4l2_device_unregister(&dcmi->v4l2_dev);
+ clk_unprepare(dcmi->mclk);
+ dma_release_channel(dcmi->dma_chan);
+ reset_control_put(dcmi->rstc);
+
+ return 0;
+}
+
+static struct platform_driver stm32_dcmi_driver = {
+ .probe = dcmi_probe,
+ .remove = dcmi_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(stm32_dcmi_of_match),
+ },
+};
+
+module_platform_driver(stm32_dcmi_driver);
+
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Hugues Fruchet <hugues.fruchet@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 Digital Camera Memory Interface driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
--
1.9.1
^ permalink raw reply related
* [PATCH v4 3/8] ARM: dts: stm32: Enable DCMI support on STM32F429 MCU
From: Hugues Fruchet @ 2017-04-20 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492704445-22186-1-git-send-email-hugues.fruchet@st.com>
Enable DCMI camera interface on STM32F429 MCU.
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index ee0da97..e1ff978 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -736,6 +736,29 @@
slew-rate = <3>;
};
};
+
+ dcmi_pins: dcmi_pins at 0 {
+ pins {
+ pinmux = <STM32F429_PA4_FUNC_DCMI_HSYNC>,
+ <STM32F429_PB7_FUNC_DCMI_VSYNC>,
+ <STM32F429_PA6_FUNC_DCMI_PIXCLK>,
+ <STM32F429_PC6_FUNC_DCMI_D0>,
+ <STM32F429_PC7_FUNC_DCMI_D1>,
+ <STM32F429_PC8_FUNC_DCMI_D2>,
+ <STM32F429_PC9_FUNC_DCMI_D3>,
+ <STM32F429_PC11_FUNC_DCMI_D4>,
+ <STM32F429_PD3_FUNC_DCMI_D5>,
+ <STM32F429_PB8_FUNC_DCMI_D6>,
+ <STM32F429_PE6_FUNC_DCMI_D7>,
+ <STM32F429_PC10_FUNC_DCMI_D8>,
+ <STM32F429_PC12_FUNC_DCMI_D9>,
+ <STM32F429_PD6_FUNC_DCMI_D10>,
+ <STM32F429_PD2_FUNC_DCMI_D11>;
+ bias-disable;
+ drive-push-pull;
+ slew-rate = <3>;
+ };
+ };
};
rcc: rcc at 40023810 {
@@ -805,6 +828,20 @@
status = "disabled";
};
+ dcmi: dcmi at 50050000 {
+ compatible = "st,stm32-dcmi";
+ reg = <0x50050000 0x400>;
+ interrupts = <78>;
+ resets = <&rcc STM32F4_AHB2_RESET(DCMI)>;
+ clocks = <&rcc 0 STM32F4_AHB2_CLOCK(DCMI)>;
+ clock-names = "mclk";
+ pinctrl-names = "default";
+ pinctrl-0 = <&dcmi_pins>;
+ dmas = <&dma2 1 1 0x414 0x3>;
+ dma-names = "tx";
+ status = "disabled";
+ };
+
rng: rng at 50060800 {
compatible = "st,stm32-rng";
reg = <0x50060800 0x400>;
--
1.9.1
^ permalink raw reply related
* [PATCH v4 4/8] ARM: dts: stm32: Enable DCMI camera interface on STM32F429-EVAL board
From: Hugues Fruchet @ 2017-04-20 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492704445-22186-1-git-send-email-hugues.fruchet@st.com>
Enable DCMI camera interface on STM32F429-EVAL board.
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
arch/arm/boot/dts/stm32429i-eval.dts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 3c99466..617f2f7 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -141,6 +141,15 @@
clock-frequency = <25000000>;
};
+&dcmi {
+ status = "okay";
+
+ port {
+ dcmi_0: endpoint {
+ };
+ };
+};
+
&i2c1 {
pinctrl-0 = <&i2c1_pins>;
pinctrl-names = "default";
--
1.9.1
^ permalink raw reply related
* [PATCH v4 5/8] ARM: dts: stm32: Enable STMPE1600 gpio expander of STM32F429-EVAL board
From: Hugues Fruchet @ 2017-04-20 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492704445-22186-1-git-send-email-hugues.fruchet@st.com>
Enable STMPE1600 gpio expander of STM32F429-EVAL board.
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
arch/arm/boot/dts/stm32429i-eval.dts | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 617f2f7..2bb8a0f 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -154,6 +154,23 @@
pinctrl-0 = <&i2c1_pins>;
pinctrl-names = "default";
status = "okay";
+
+ stmpe1600: stmpe1600 at 42 {
+ compatible = "st,stmpe1600";
+ reg = <0x42>;
+ irq-gpio = <&gpioi 8 0>;
+ irq-trigger = <3>;
+ interrupts = <8 3>;
+ interrupt-parent = <&exti>;
+ interrupt-controller;
+ wakeup-source;
+
+ stmpegpio: stmpe_gpio {
+ compatible = "st,stmpe-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
};
&mac {
--
1.9.1
^ permalink raw reply related
* [PATCH v4 6/8] ARM: dts: stm32: Enable OV2640 camera support of STM32F429-EVAL board
From: Hugues Fruchet @ 2017-04-20 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492704445-22186-1-git-send-email-hugues.fruchet@st.com>
Enable OV2640 camera support of STM32F429-EVAL board.
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
arch/arm/boot/dts/stm32429i-eval.dts | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 2bb8a0f..95c33b1 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -48,6 +48,7 @@
/dts-v1/;
#include "stm32f429.dtsi"
#include <dt-bindings/input/input.h>
+#include <dt-bindings/gpio/gpio.h>
/ {
model = "STMicroelectronics STM32429i-EVAL board";
@@ -66,6 +67,14 @@
serial0 = &usart1;
};
+ clocks {
+ clk_ext_camera: clk-ext-camera {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+ };
+
soc {
dma-ranges = <0xc0000000 0x0 0x10000000>;
};
@@ -146,6 +155,11 @@
port {
dcmi_0: endpoint {
+ remote-endpoint = <&ov2640_0>;
+ bus-width = <8>;
+ hsync-active = <0>;
+ vsync-active = <0>;
+ pclk-sample = <1>;
};
};
};
@@ -155,6 +169,22 @@
pinctrl-names = "default";
status = "okay";
+ ov2640: camera at 30 {
+ compatible = "ovti,ov2640";
+ reg = <0x30>;
+ resetb-gpios = <&stmpegpio 2 GPIO_ACTIVE_HIGH>;
+ pwdn-gpios = <&stmpegpio 0 GPIO_ACTIVE_LOW>;
+ clocks = <&clk_ext_camera>;
+ clock-names = "xvclk";
+ status = "okay";
+
+ port {
+ ov2640_0: endpoint {
+ remote-endpoint = <&dcmi_0>;
+ };
+ };
+ };
+
stmpe1600: stmpe1600 at 42 {
compatible = "st,stmpe1600";
reg = <0x42>;
--
1.9.1
^ permalink raw reply related
* [PATCH v4 7/8] ARM: configs: stm32: STMPE1600 GPIO expander
From: Hugues Fruchet @ 2017-04-20 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492704445-22186-1-git-send-email-hugues.fruchet@st.com>
Enable STMPE1600 GPIO expander.
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
arch/arm/configs/stm32_defconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index a9d8e3c..84adc88 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -49,6 +49,8 @@ CONFIG_SERIAL_STM32_CONSOLE=y
# CONFIG_HW_RANDOM is not set
# CONFIG_HWMON is not set
CONFIG_REGULATOR=y
+CONFIG_GPIO_STMPE=y
+CONFIG_MFD_STMPE=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
# CONFIG_USB_SUPPORT is not set
CONFIG_NEW_LEDS=y
--
1.9.1
^ permalink raw reply related
* [PATCH v4 8/8] ARM: configs: stm32: DCMI + OV2640 camera support
From: Hugues Fruchet @ 2017-04-20 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492704445-22186-1-git-send-email-hugues.fruchet@st.com>
Enable DCMI camera interface and OV2640 camera sensor drivers.
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
arch/arm/configs/stm32_defconfig | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 84adc88..3f2e4ce 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -53,6 +53,13 @@ CONFIG_GPIO_STMPE=y
CONFIG_MFD_STMPE=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
# CONFIG_USB_SUPPORT is not set
+CONFIG_VIDEO_V4L2=y
+CONFIG_MEDIA_SUBDRV_AUTOSELECT=n
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_VIDEO_STM32_DCMI=y
+CONFIG_VIDEO_OV2640=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
--
1.9.1
^ permalink raw reply related
* [PATCH V5 3/7] ARM: exynos: Use - instead of @ for DT OPP entries
From: Krzysztof Kozlowski @ 2017-04-20 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8fd1dcd1bfa2011411dceab82bcd1587fe913cd1.1492685450.git.viresh.kumar@linaro.org>
On Thu, Apr 20, 2017 at 04:25:07PM +0530, Viresh Kumar wrote:
> Compiling the DT file with W=1, DTC warns like follows:
>
> Warning (unit_address_vs_reg): Node /opp_table0/opp at 1000000000 has a
> unit name, but no reg property
>
> Fix this by replacing '@' with '-' as the OPP nodes will never have a
> "reg" property.
>
> Reported-by: Krzysztof Kozlowski <krzk@kernel.org>
> Reported-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> Suggested-by: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
> Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
> Acked-by: Rob Herring <robh@kernel.org>
> ---
> .../devicetree/bindings/devfreq/exynos-bus.txt | 46 +++++++--------
> arch/arm/boot/dts/exynos3250.dtsi | 46 +++++++--------
> arch/arm/boot/dts/exynos4210.dtsi | 32 +++++------
> arch/arm/boot/dts/exynos4412-prime.dtsi | 4 +-
> arch/arm/boot/dts/exynos4412.dtsi | 66 +++++++++++-----------
> arch/arm/boot/dts/exynos5420.dtsi | 40 ++++++-------
> arch/arm/boot/dts/exynos5800.dtsi | 56 +++++++++---------
> arch/arm64/boot/dts/exynos/exynos5433-bus.dtsi | 48 ++++++++--------
> arch/arm64/boot/dts/exynos/exynos5433.dtsi | 50 ++++++++--------
> 9 files changed, 194 insertions(+), 194 deletions(-)
Thanks, split ARM64 from ARM and applied (for v4.12). arm-soc keeps DTS
separated between ARM architectures. Technically this should require a
resend but we had way too many resends for this simple patch.
Best regards,
Krzysztof
^ permalink raw reply
* usb: dwc2: NMI watchdog: BUG: soft lockup - CPU#0 stuck for 146s
From: Doug Anderson @ 2017-04-20 16:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <e141d7fb-7ec8-805e-d2f2-f1892b76d00e@i2se.com>
Hi,
On Thu, Apr 20, 2017 at 12:46 AM, Stefan Wahren <stefan.wahren@i2se.com> wrote:
> Am 19.04.2017 um 23:47 schrieb Doug Anderson:
>> Hi,
>>
>> On Wed, Apr 19, 2017 at 1:25 PM, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>>> Hi,
>>>
>>>> Doug Anderson <dianders@chromium.org> hat am 18. April 2017 um 22:41 geschrieben:
>>>>
>>>>
>>>> It's hard to know for sure that all of this time is really in
>>>> urb_enqueue(). Possible we could have task switched out and been
>>>> blocked elsewhere. Using ftrace to get more fine-grained timings
>>>> would be useful. ktime_get(), ktime_sub(), and ktime_to_us() are your
>>>> friends here if you want to use trace_printk.
>>> i'm a newbie to ftrace, so i hope this would be helpful.
>>>
>>> # connect PL2303 to the onboard hub
>>> # echo 0 > options/sleep-time
>>> # echo 0 > function_profile_enabled
>>> # echo 1 > function_profile_enabled
>>> # ./usb_test
>>> # Waiting for at least 20 seconds and then disconnect PL2303
>>> # echo 0 > function_profile_enabled
>>> # cat trace_stat/function0
>>>
>>> Function Hit Time Avg s^2
>>> -------- --- ---- --- ---
>>> bcm2835_handle_irq 361347 219567633 us 607.636 us 1485199 us
>>> __handle_domain_irq 1082482 212639551 us 196.437 us 3642030 us
>>> generic_handle_irq 1082482 100592051 us 92.927 us 50511334 us
>>> irq_exit 1082482 98197771 us 90.715 us 29649040 us
>>> handle_level_irq 1082482 95812379 us 88.511 us 51910093 us
>>> do_sys_open 1806 87612983 us 48512.17 us 2198507 us
>>> SyS_open 1601 87372331 us 54573.59 us 1898996 us
>>> do_filp_open 1862 87368058 us 46921.62 us 1634982 us
>>> path_openat 1862 87314553 us 46892.88 us 3357817 us
>>> __do_softirq 3035 86266050 us 28423.73 us 6449768 us
>>> vfs_open 1515 85877012 us 56684.49 us 101673.5 us
>>> do_dentry_open 1515 85861429 us 56674.21 us 812420.7 us
>>> usb_submit_urb 136 85760172 us 630589.5 us 59532024 us
>>> usb_hcd_submit_urb 134 85756518 us 639974.0 us 726298102 us
>>> _dwc2_hcd_urb_enqueue 134 85738333 us 639838.3 us 874104371 us
>> The 134 calls to this are taking a ton of time. It would be nice to
>> know where all the time actually was in here. Are you on a
>> single-core device, or multi-core?
>
> Raspberry Pi B is a single core device which runs with 700 MHz cpu freq.
>
>> Oh, probably this so slow because we're just getting interrupted
>> constantly. You can see that during your trace 80686112 us was in
>> handle_irq_event(). Presumably all of that time could have been
>> counted towards whatever code you were in when the interrupt went off.
>> Of that 80 seconds, 60 seconds or so was usb_hcd_irq, and of that 57
>> seconds was in _dwc2_hcd_irq().
>
> Since the FTDI (full speed device) and the PL2303 (also full speed
> device) behaves differently, i will try to compare those ftraces.
The most interesting to know is what the endpoints looked like for
these two devices. Since they are full speed devices, I'd expect that
any interrupt end points should fire once per "ms" at most.
>> Presumably the big part of your problem is that dwc2_handle_hcd_intr()
>> is just too slow. From glancing at the numbers below, it seems likely
>> that nothing in particular is slow, it's just running a lot of code
>> and (in total) all of that is slow. ...but someone with more time on
>> their hands would need to really debug.
>>
>> One thing that would be interesting would be to see if you can
>> increase the bug clock for talking to the dwc2 controller.
>
> Unfortunately the bcm2835 datasheet [1] doesn't provide many information
> about the USB IP core and the Synopsys documents aren't public
> available. The Raspberry Pi Foundation decided to use a different driver
> which uses FIQ in the downstream tree [2], but this won't be an option here.
>
> Maybe i could start with comparing the register settings between dwc2
> and dwc_otg.
I was suggesting looking at clock speeds related to usb that might be
in /sys/kernel/debug/clk/clk_summary
...but at 700 MHz single core (and I think it's also an older ARM core
so 700 MHz there might be a lot slower than 700 MHz on newer cores),
the theory that you're just running too many instructions is a sane
one.
Probably someone just needs to see if there's any way to fast path
some of the common cases in the IRQ handler.
-Doug
^ permalink raw reply
* [PATCH v2 0/2] hwrng: mtk: add support for hardware random generator on MT7623 SoC
From: sean.wang at mediatek.com @ 2017-04-20 16:24 UTC (permalink / raw)
To: linux-arm-kernel
From: Sean Wang <sean.wang@mediatek.com>
This patchset introduces support for Mediatek hardware random generator (RNG)
Currently, the driver is already tested successfully with rng-tools on MT7623
SoC. And it should also be workable on other similar Mediatek SoCs.
Changes since v1:
- remove unnecessary warning message
- remove mistakenly changed line
- refine macro definition with keeping one space between name and define
- remove redundant platform_get_resource() call
Sean Wang (2):
dt-bindings: hwrng: Add Mediatek hardware random generator bindings
hwrng: mtk: Add driver for hardware random generator on MT7623 SoC
Documentation/devicetree/bindings/rng/mtk-rng.txt | 18 +++
drivers/char/hw_random/Kconfig | 14 ++
drivers/char/hw_random/Makefile | 1 +
drivers/char/hw_random/mtk-rng.c | 168 ++++++++++++++++++++++
4 files changed, 201 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rng/mtk-rng.txt
create mode 100644 drivers/char/hw_random/mtk-rng.c
--
1.9.1
^ permalink raw reply
* [PATCH v2 1/2] dt-bindings: hwrng: Add Mediatek hardware random generator bindings
From: sean.wang at mediatek.com @ 2017-04-20 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492705466-27287-1-git-send-email-sean.wang@mediatek.com>
From: Sean Wang <sean.wang@mediatek.com>
Document the devicetree bindings for Mediatek random number
generator which could be found on MT7623 SoC or other similar
Mediatek SoCs.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/rng/mtk-rng.txt | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rng/mtk-rng.txt
diff --git a/Documentation/devicetree/bindings/rng/mtk-rng.txt b/Documentation/devicetree/bindings/rng/mtk-rng.txt
new file mode 100644
index 0000000..a6d62a2
--- /dev/null
+++ b/Documentation/devicetree/bindings/rng/mtk-rng.txt
@@ -0,0 +1,18 @@
+Device-Tree bindings for Mediatek random number generator
+found in Mediatek SoC family
+
+Required properties:
+- compatible : Should be "mediatek,mt7623-rng"
+- clocks : list of clock specifiers, corresponding to
+ entries in clock-names property;
+- clock-names : Should contain "rng" entries;
+- reg : Specifies base physical address and size of the registers
+
+Example:
+
+rng: rng at 1020f000 {
+ compatible = "mediatek,mt7623-rng";
+ reg = <0 0x1020f000 0 0x1000>;
+ clocks = <&infracfg CLK_INFRA_TRNG>;
+ clock-names = "rng";
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v2 2/2] hwrng: mtk: Add driver for hardware random generator on MT7623 SoC
From: sean.wang at mediatek.com @ 2017-04-20 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492705466-27287-1-git-send-email-sean.wang@mediatek.com>
From: Sean Wang <sean.wang@mediatek.com>
This patch adds support for hardware random generator on MT7623 SoC
and should also work on other similar Mediatek SoCs. Currently,
the driver is already tested successfully with rng-tools.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/char/hw_random/Kconfig | 14 ++++
drivers/char/hw_random/Makefile | 1 +
drivers/char/hw_random/mtk-rng.c | 168 +++++++++++++++++++++++++++++++++++++++
3 files changed, 183 insertions(+)
create mode 100644 drivers/char/hw_random/mtk-rng.c
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 0cafe08..df5e7c2 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -423,6 +423,20 @@ config HW_RANDOM_CAVIUM
If unsure, say Y.
+config HW_RANDOM_MTK
+ tristate "Mediatek Random Number Generator support"
+ depends on HW_RANDOM
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ default y
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Mediatek SoCs.
+
+ To compile this driver as a module, choose M here. the
+ module will be called mtk-rng.
+
+ If unsure, say Y.
+
endif # HW_RANDOM
config UML_RANDOM
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 5f52b1e..59eacb7 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -36,3 +36,4 @@ obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o
obj-$(CONFIG_HW_RANDOM_PIC32) += pic32-rng.o
obj-$(CONFIG_HW_RANDOM_MESON) += meson-rng.o
obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o
+obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o
diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c
new file mode 100644
index 0000000..df8eb54
--- /dev/null
+++ b/drivers/char/hw_random/mtk-rng.c
@@ -0,0 +1,168 @@
+/*
+ * Driver for Mediatek Hardware Random Number Generator
+ *
+ * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define MTK_RNG_DEV KBUILD_MODNAME
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define USEC_POLL 2
+#define TIMEOUT_POLL 20
+
+#define RNG_CTRL 0x00
+#define RNG_EN BIT(0)
+#define RNG_READY BIT(31)
+
+#define RNG_DATA 0x08
+
+#define to_mtk_rng(p) container_of(p, struct mtk_rng, rng)
+
+struct mtk_rng {
+ void __iomem *base;
+ struct clk *clk;
+ struct hwrng rng;
+};
+
+static int mtk_rng_init(struct hwrng *rng)
+{
+ struct mtk_rng *priv = to_mtk_rng(rng);
+ u32 val;
+ int err;
+
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ return err;
+
+ val = readl(priv->base + RNG_CTRL);
+ val |= RNG_EN;
+ writel(val, priv->base + RNG_CTRL);
+
+ return 0;
+}
+
+static void mtk_rng_cleanup(struct hwrng *rng)
+{
+ struct mtk_rng *priv = to_mtk_rng(rng);
+ u32 val;
+
+ val = readl(priv->base + RNG_CTRL);
+ val &= ~RNG_EN;
+ writel(val, priv->base + RNG_CTRL);
+
+ clk_disable_unprepare(priv->clk);
+}
+
+static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait)
+{
+ struct mtk_rng *priv = to_mtk_rng(rng);
+ int ready;
+
+ ready = readl(priv->base + RNG_CTRL) & RNG_READY;
+ if (!ready && wait)
+ readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready,
+ ready & RNG_READY, USEC_POLL,
+ TIMEOUT_POLL);
+ return !!ready;
+}
+
+static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct mtk_rng *priv = to_mtk_rng(rng);
+ int retval = 0;
+
+ while (max >= sizeof(u32)) {
+ if (!mtk_rng_wait_ready(rng, wait))
+ break;
+
+ *(u32 *)buf = readl(priv->base + RNG_DATA);
+ retval += sizeof(u32);
+ buf += sizeof(u32);
+ max -= sizeof(u32);
+ }
+
+ return retval || !wait ? retval : -EIO;
+}
+
+static int mtk_rng_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+ struct mtk_rng *priv;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no iomem resource\n");
+ return -ENXIO;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->rng.name = pdev->name;
+ priv->rng.init = mtk_rng_init;
+ priv->rng.cleanup = mtk_rng_cleanup;
+ priv->rng.read = mtk_rng_read;
+
+ priv->clk = devm_clk_get(&pdev->dev, "rng");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(&pdev->dev, "no clock for device: %d\n", ret);
+ return ret;
+ }
+
+ priv->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = devm_hwrng_register(&pdev->dev, &priv->rng);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register rng device: %d\n",
+ ret);
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "registered RNG driver\n");
+
+ return 0;
+}
+
+static const struct of_device_id mtk_rng_match[] = {
+ { .compatible = "mediatek,mt7623-rng" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_rng_match);
+
+static struct platform_driver mtk_rng_driver = {
+ .probe = mtk_rng_probe,
+ .driver = {
+ .name = MTK_RNG_DEV,
+ .of_match_table = mtk_rng_match,
+ },
+};
+
+module_platform_driver(mtk_rng_driver);
+
+MODULE_DESCRIPTION("Mediatek Random Number Generator Driver");
+MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/3] mmc: dt-bindings: update Mediatek MMC bindings
From: Rob Herring @ 2017-04-20 16:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492510391-704-2-git-send-email-yong.mao@mediatek.com>
On Tue, Apr 18, 2017 at 06:13:09PM +0800, Yong Mao wrote:
> From: yong mao <yong.mao@mediatek.com>
>
> Add description for mediatek,clk-pad-delay
>
> Signed-off-by: Yong Mao <yong.mao@mediatek.com>
> Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> ---
> Documentation/devicetree/bindings/mmc/mtk-sd.txt | 2 ++
> 1 file changed, 2 insertions(+)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [GIT PULL] ARM: dts: vexpress: fix for v4.12
From: Sudeep Holla @ 2017-04-20 16:30 UTC (permalink / raw)
To: linux-arm-kernel
Hi ARM SoC team,
Sorry for the late pull request, but this just contain fix to remove DT
build warnings that are showing up in linux-next
Please pull!
Regards,
Sudeep
The following changes since commit c1ae3cfa0e89fa1a7ecc4c99031f5e9ae99d9201:
Linux 4.11-rc1 (2017-03-05 12:59:56 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git
tags/vexpress-dt-4.12
for you to fetch changes up to e6a7efad7921f12ea8112f16681e47fb30c7d975:
ARM: dts: vexpress: fix few unit address format warnings (2017-04-19
12:08:37 +0100)
----------------------------------------------------------------
ARMv7 VExpress DT fix for v4.12
A single fix to remove device tree warnings introduced with recently
added checks in DTC
----------------------------------------------------------------
Sudeep Holla (1):
ARM: dts: vexpress: fix few unit address format warnings
arch/arm/boot/dts/vexpress-v2m-rs1.dtsi | 24 +++++++---------
arch/arm/boot/dts/vexpress-v2m.dtsi | 24 +++++++---------
arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts | 2 +-
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 18 +++++++-------
arch/arm/boot/dts/vexpress-v2p-ca5s.dts | 2 +-
arch/arm/boot/dts/vexpress-v2p-ca9.dts | 2 +-
6 files changed, 36 insertions(+), 36 deletions(-)
^ permalink raw reply
* [GIT PULL] arm64: dts: juno: fixes for v4.12
From: Sudeep Holla @ 2017-04-20 16:30 UTC (permalink / raw)
To: linux-arm-kernel
Hi ARM SoC team,
Sorry for the late pull request, but this contains only fixes
that were found when I tested linux-next.
Please pull!
Regards,
Sudeep
The following changes since commit c1ae3cfa0e89fa1a7ecc4c99031f5e9ae99d9201:
Linux 4.11-rc1 (2017-03-05 12:59:56 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git
tags/juno-fixes-4.12
for you to fetch changes up to f9936c4abf63106e0bcc3d82098dd1cfc3dc5290:
arm64: dts: juno: add information about L1 and L2 caches (2017-04-19
12:16:51 +0100)
----------------------------------------------------------------
ARMv8 Juno DT fixes for v4.12
1. Couple of fixes to remove device tree warnings introduced with
recently added checks in DTC
2. Add information about L1 and L2 caches to Juno device trees as
CCSIDR-based cacheinfo probing is now removed
----------------------------------------------------------------
Rob Herring (1):
arm64: dts: juno: fix PCI bus dtc warnings
Sudeep Holla (2):
arm64: dts: juno: fix few unit address format warnings
arm64: dts: juno: add information about L1 and L2 caches
arch/arm64/boot/dts/arm/juno-base.dtsi | 4 +--
arch/arm64/boot/dts/arm/juno-motherboard.dtsi | 12 +++---
arch/arm64/boot/dts/arm/juno-r1.dts | 42 +++++++++++++++++
arch/arm64/boot/dts/arm/juno-r2.dts | 42 +++++++++++++++++
arch/arm64/boot/dts/arm/juno.dts | 42 +++++++++++++++++
5 files changed, 134 insertions(+), 8 deletions(-)
^ permalink raw reply
* [PATCH v3 0/8] Add support for DCMI camera interface of STMicroelectronics STM32 SoC series
From: Hugues FRUCHET @ 2017-04-20 16:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <695c96ef-afbf-da4c-d9b3-7ce16067f7c0@xs4all.nl>
Hi Hans,
v4 has been sent with "v4l2-compliance -s -f" report provided in cover
letter. Bindings acked by Rob.
http://www.mail-archive.com/linux-media at vger.kernel.org/msg111743.html
BR,
Hugues.
On 04/10/2017 10:55 AM, Hans Verkuil wrote:
> On 04/04/2017 05:44 PM, Hugues Fruchet wrote:
>> This patchset introduces a basic support for Digital Camera Memory Interface
>> (DCMI) of STMicroelectronics STM32 SoC series.
>>
>> This first basic support implements RGB565 & YUV frame grabbing.
>> Cropping and JPEG support will be added later on.
>>
>> This has been tested on STM324x9I-EVAL evaluation board embedding
>> an OV2640 camera sensor.
>>
>> This driver depends on:
>> - [PATCHv6 00/14] atmel-isi/ov7670/ov2640: convert to standalone drivers http://www.spinics.net/lists/linux-media/msg113480.html
>>
>> ===========
>> = history =
>> ===========
>> version 3:
>> - stm32-dcmi: Add "Reviewed-by: Hans Verkuil <hans.verkuil@cisco.com>"
>> - dt-bindings: Fix remarks from Rob Herring:
>> http://www.mail-archive.com/linux-media at vger.kernel.org/msg110956.html
>>
>> version 2:
>> - Fix a Kbuild warning in probe:
>> http://www.mail-archive.com/linux-media at vger.kernel.org/msg110678.html
>> - Fix a warning in dcmi_queue_setup()
>> - dt-bindings: warn on sensor signals level inversion in board example
>> - Typos fixing
>>
>> version 1:
>> - Initial submission
>>
>> ===================
>> = v4l2-compliance =
>> ===================
>> Below is the v4l2-compliance report for this current version of the DCMI camera interface.
>> v4l2-compliance has been built from v4l-utils-1.12.3.
>
> Please test with 'v4l2-compliance -s -f' as well and mail me the output of
> that test.
>
> Once you have the Acks for the DT/bindings patches just let me know and I'll
> make a pull request.
>
> Regards,
>
> Hans
>
>>
>> v4l2-compliance SHA : f5f45e17ee98a0ebad7836ade2b34ceec909d751
>>
>> Driver Info:
>> Driver name : stm32-dcmi
>> Card type : STM32 Digital Camera Memory Int
>> Bus info : platform:dcmi
>> Driver version: 4.11.0
>> Capabilities : 0x85200001
>> Video Capture
>> Read/Write
>> Streaming
>> Extended Pix Format
>> Device Capabilities
>> Device Caps : 0x05200001
>> Video Capture
>> Read/Write
>> Streaming
>> Extended Pix Format
>>
>> Compliance test for device /dev/video0 (not using libv4l2):
>>
>> Required ioctls:
>> test VIDIOC_QUERYCAP: OK
>>
>> Allow for multiple opens:
>> test second video open: OK
>> test VIDIOC_QUERYCAP: OK
>> test VIDIOC_G/S_PRIORITY: OK
>> test for unlimited opens: OK
>>
>> Debug ioctls:
>> test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
>> test VIDIOC_LOG_STATUS: OK
>>
>> Input ioctls:
>> test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
>> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>> test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
>> test VIDIOC_ENUMAUDIO: OK (Not Supported)
>> test VIDIOC_G/S/ENUMINPUT: OK
>> test VIDIOC_G/S_AUDIO: OK (Not Supported)
>> Inputs: 1 Audio Inputs: 0 Tuners: 0
>>
>> Output ioctls:
>> test VIDIOC_G/S_MODULATOR: OK (Not Supported)
>> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
>> test VIDIOC_ENUMAUDOUT: OK (Not Supported)
>> test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
>> test VIDIOC_G/S_AUDOUT: OK (Not Supported)
>> Outputs: 0 Audio Outputs: 0 Modulators: 0
>>
>> Input/Output configuration ioctls:
>> test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
>> test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
>> test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
>> test VIDIOC_G/S_EDID: OK (Not Supported)
>>
>> Test input 0:
>>
>> Control ioctls:
>> test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
>> test VIDIOC_QUERYCTRL: OK
>> test VIDIOC_G/S_CTRL: OK
>> test VIDIOC_G/S/TRY_EXT_CTRLS: OK
>> test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
>> test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
>> Standard Controls: 3 Private Controls: 0
>>
>> Format ioctls:
>> test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
>> test VIDIOC_G/S_PARM: OK (Not Supported)
>> test VIDIOC_G_FBUF: OK (Not Supported)
>> test VIDIOC_G_FMT: OK
>> test VIDIOC_TRY_FMT: OK
>> test VIDIOC_S_FMT: OK
>> test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
>> test Cropping: OK (Not Supported)
>> test Composing: OK (Not Supported)
>> test Scaling: OK
>>
>> Codec ioctls:
>> test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
>> test VIDIOC_G_ENC_INDEX: OK (Not Supported)
>> test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
>>
>> Buffer ioctls:
>> test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
>> test VIDIOC_EXPBUF: OK
>>
>> Test input 0:
>>
>> Streaming ioctls:
>> test read/write: OK
>> test MMAP: OK
>> test USERPTR: OK (Not Supported)
>> test DMABUF: Cannot test, specify --expbuf-device
>>
>>
>> Total: 46, Succeeded: 46, Failed: 0, Warnings: 0
>>
>> Hugues Fruchet (8):
>> dt-bindings: Document STM32 DCMI bindings
>> [media] stm32-dcmi: STM32 DCMI camera interface driver
>> ARM: dts: stm32: Enable DCMI support on STM32F429 MCU
>> ARM: dts: stm32: Enable DCMI camera interface on STM32F429-EVAL board
>> ARM: dts: stm32: Enable STMPE1600 gpio expander of STM32F429-EVAL
>> board
>> ARM: dts: stm32: Enable OV2640 camera support of STM32F429-EVAL board
>> ARM: configs: stm32: STMPE1600 GPIO expander
>> ARM: configs: stm32: DCMI + OV2640 camera support
>>
>> .../devicetree/bindings/media/st,stm32-dcmi.txt | 46 +
>> arch/arm/boot/dts/stm32429i-eval.dts | 56 +
>> arch/arm/boot/dts/stm32f429.dtsi | 37 +
>> arch/arm/configs/stm32_defconfig | 9 +
>> drivers/media/platform/Kconfig | 12 +
>> drivers/media/platform/Makefile | 2 +
>> drivers/media/platform/stm32/Makefile | 1 +
>> drivers/media/platform/stm32/stm32-dcmi.c | 1419 ++++++++++++++++++++
>> 8 files changed, 1582 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/media/st,stm32-dcmi.txt
>> create mode 100644 drivers/media/platform/stm32/Makefile
>> create mode 100644 drivers/media/platform/stm32/stm32-dcmi.c
>>
>
^ permalink raw reply
* kvm/arm64: use-after-free in kvm_unmap_hva_handler/unmap_stage2_pmds
From: Suzuki K Poulose @ 2017-04-20 16:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170413155045.GA8387@e107814-lin.cambridge.arm.com>
On Thu, Apr 13, 2017 at 04:50:46PM +0100, Suzuki K. Poulose wrote:
> On Thu, Apr 13, 2017 at 10:17:54AM +0100, Suzuki K Poulose wrote:
> > On 12/04/17 19:43, Marc Zyngier wrote:
> > > On 12/04/17 17:19, Andrey Konovalov wrote:
> > >
> > > Hi Andrey,
> > >
> > > > Apparently this wasn't fixed, I've got this report again on
> > > > linux-next-c4e7b35a3 (Apr 11), which includes 8b3405e34 "kvm:
> > > > arm/arm64: Fix locking for kvm_free_stage2_pgd".
> > >
> > > This looks like a different bug.
> > >
> > > >
> > > > I now have a way to reproduce it, so I can test proposed patches. I
> > > > don't have a simple C reproducer though.
> > > >
> > > > The bug happens when the following syzkaller program is executed:
> > > >
> > > > mmap(&(0x7f0000000000/0xc000)=nil, (0xc000), 0x3, 0x32, 0xffffffffffffffff, 0x0)
> > > > unshare(0x400)
> > > > perf_event_open(&(0x7f000002f000-0x78)={0x1, 0x78, 0x0, 0x0, 0x0, 0x0,
> > > > 0x0, 0x6, 0x0, 0x0, 0xd34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
> > > > 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 0x0, 0xffffffff,
> > > > 0xffffffffffffffff, 0x0)
> > > > r0 = openat$kvm(0xffffffffffffff9c,
> > > > &(0x7f000000c000-0x9)="2f6465762f6b766d00", 0x0, 0x0)
> > > > ioctl$TIOCSBRK(0xffffffffffffffff, 0x5427)
> > > > r1 = ioctl$KVM_CREATE_VM(r0, 0xae01, 0x0)
> > > > syz_kvm_setup_cpu$arm64(r1, 0xffffffffffffffff,
> > > > &(0x7f0000dc6000/0x18000)=nil, &(0x7f000000c000)=[{0x0,
> > > > &(0x7f000000c000)="5ba3c16f533efbed09f8221253c73763327fadce2371813b45dd7f7982f84a873e4ae89a6c2bd1af83a6024c36a1ff518318",
> > > > 0x32}], 0x1, 0x0, &(0x7f000000d000-0x10)=[@featur2={0x1, 0x3}], 0x1)
> > >
> > > Is that the only thing the program does? Or is there anything running in
> > > parallel?
> > >
> > > > ==================================================================
> > > > BUG: KASAN: use-after-free in arch_spin_is_locked
> > > > include/linux/compiler.h:254 [inline]
> > > > BUG: KASAN: use-after-free in unmap_stage2_range+0x990/0x9a8
> > > > arch/arm64/kvm/../../../arch/arm/kvm/mmu.c:295
> > > > Read of size 8 at addr ffff800004476730 by task syz-executor/13106
> > > >
> > > > CPU: 1 PID: 13106 Comm: syz-executor Not tainted
> > > > 4.11.0-rc6-next-20170411-xc2-11025-gc4e7b35a33d4-dirty #5
> > > > Hardware name: Hardkernel ODROID-C2 (DT)
> > > > Call trace:
> > > > [<ffff20000808fd08>] dump_backtrace+0x0/0x440 arch/arm64/kernel/traps.c:505
> > > > [<ffff2000080903c0>] show_stack+0x20/0x30 arch/arm64/kernel/traps.c:228
> > > > [<ffff2000088df030>] __dump_stack lib/dump_stack.c:16 [inline]
> > > > [<ffff2000088df030>] dump_stack+0x110/0x168 lib/dump_stack.c:52
> > > > [<ffff200008406db8>] print_address_description+0x60/0x248 mm/kasan/report.c:252
> > > > [<ffff2000084072c8>] kasan_report_error mm/kasan/report.c:351 [inline]
> > > > [<ffff2000084072c8>] kasan_report+0x218/0x300 mm/kasan/report.c:408
> > > > [<ffff200008407428>] __asan_report_load8_noabort+0x18/0x20 mm/kasan/report.c:429
> > > > [<ffff2000080db1b8>] arch_spin_is_locked include/linux/compiler.h:254 [inline]
> > >
> > > This is the assert on the spinlock, and the memory is gone.
> > >
> > > > [<ffff2000080db1b8>] unmap_stage2_range+0x990/0x9a8
> > > > arch/arm64/kvm/../../../arch/arm/kvm/mmu.c:295
> > > > [<ffff2000080db248>] kvm_free_stage2_pgd.part.16+0x30/0x98
> > > > arch/arm64/kvm/../../../arch/arm/kvm/mmu.c:842
> > > > [<ffff2000080ddfb8>] kvm_free_stage2_pgd
> > > > arch/arm64/kvm/../../../arch/arm/kvm/mmu.c:838 [inline]
> > >
> > > But we've taken than lock here. There's only a handful of instructions
> > > in between, and the memory can only go away if there is something
> > > messing with us in parallel.
> > >
> > > > [<ffff2000080ddfb8>] kvm_arch_flush_shadow_all+0x40/0x58
> > > > arch/arm64/kvm/../../../arch/arm/kvm/mmu.c:1895
> > > > [<ffff2000080c379c>] kvm_mmu_notifier_release+0x154/0x1d0
> > > > arch/arm64/kvm/../../../virt/kvm/kvm_main.c:472
> > > > [<ffff2000083f2b60>] __mmu_notifier_release+0x1c0/0x3e0 mm/mmu_notifier.c:75
> > > > [<ffff2000083a1fb4>] mmu_notifier_release
> > > > include/linux/mmu_notifier.h:235 [inline]
> > > > [<ffff2000083a1fb4>] exit_mmap+0x21c/0x288 mm/mmap.c:2941
> > > > [<ffff20000810ecd4>] __mmput kernel/fork.c:888 [inline]
> > > > [<ffff20000810ecd4>] mmput+0xdc/0x2e0 kernel/fork.c:910
> > > > [<ffff20000811fda8>] exit_mm kernel/exit.c:557 [inline]
> > > > [<ffff20000811fda8>] do_exit+0x648/0x2020 kernel/exit.c:865
> > > > [<ffff2000081218b4>] do_group_exit+0xdc/0x260 kernel/exit.c:982
> > > > [<ffff20000813adf0>] get_signal+0x358/0xf58 kernel/signal.c:2318
> > > > [<ffff20000808de98>] do_signal+0x170/0xc10 arch/arm64/kernel/signal.c:370
> > > > [<ffff20000808edb4>] do_notify_resume+0xe4/0x120 arch/arm64/kernel/signal.c:421
> > > > [<ffff200008083e68>] work_pending+0x8/0x14
> > >
> > > So we're being serviced with a signal. Do you know if this signal is
> > > generated by your syzkaller program? We could be racing between do_exit
> > > triggered by a fatal signal (this trace) and the closing of the two file
> > > descriptors (vcpu and vm).
> > >
> > > Paolo: does this look possible to you? I can't see what locking we have
> > > that could prevent this race.
> >
> > On a quick look, I see two issues:
> >
> > 1) It looks like the mmu_notifier->ops.release could be called twice for a notifier,
> > from mmu_notifier_unregister() and exit_mmap()->mmu_notifier_release(), which is
> > causing the problem as above.
> >
> > This could possibly be avoided by swapping the order of the following operations
> > in themmu_notifier_unregister():
> >
> > a) Invoke ops->release under src_read_lock()
> > b) Delete the notifier from the list.
> >
> > which can prevent mmu_notifier_release() calling the ops->release() again, before
> > we reach (b).
> >
> >
> > 2) The core KVM code does an mmgrab()/mmdrop on the current->mm to pin the mm_struct. But
> > this doesn't prevent the "real_address user space" from being destroyed. Since KVM
> > actually depends on the user pages and page tables, it should really/also(?) use
> > mmget()/mmput() (See Documentation/vm/active_mm.txt). I understand that mmget() shouldn't
> > be used for pinning unbounded amount of time. But since we do it from within the same
> > process context (like say threads), we should be safe to do so.
>
Option 2 doesn't work, as it creates a circular dependency with exit_mmap vs kvm_destory_vm
due to the mmap on VCPU (which prevents KVM from getting dropped). After a couple of trials
at resolving this issue, we have something better to resolve it, below.
----8>----
kvm: Fix mmu_notifier release race
The KVM uses mmu_notifier (wherever available) to keep track
of the changes to the mm of the guest. The guest shadow page
tables are released when the VM exits via mmu_notifier->ops.release().
There is a rare chance that the mmu_notifier->release could be
called more than once via two different paths, which could end
up in use-after-free of kvm instance.
e.g:
thread A thread B
------- --------------
get_signal-> kvm_destroy_vm()->
do_exit-> mmu_notifier_unregister->
exit_mm-> kvm_arch_flush_shadow_all()->
exit_mmap-> spin_lock(&kvm->mmu_lock)
mmu_notifier_release-> ....
kvm_arch_flush_shadow_all()-> .....
... spin_lock(&kvm->mmu_lock) .....
spin_unlock(&kvm->mmu_lock)
kvm_arch_free_kvm()
*** use after free of kvm ***
This patch attempts to solve the problem by holding a reference to the KVM
for the mmu_notifier, which is dropped only from notifier->ops.release().
This will ensure that the KVM struct is available till we reach the
kvm_mmu_notifier_release, and the kvm_destroy_vm is called only from/after
it. So, we can unregister the notifier with no_release option and hence
avoiding the race above. However, we need to make sure that the KVM is
freed only after the mmu_notifier has finished processing the notifier due to
the following possible path of execution :
mmu_notifier_release -> kvm_mmu_notifier_release -> kvm_put_kvm ->
kvm_destroy_vm -> kvm_arch_free_kvm
Reported-by: andreyknvl at google.com
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Paolo Bonzin <pbonzini@redhat.com>
Cc: Radim Kr?m?? <rkrcmar@redhat.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Christoffer Dall <christoffer.dall@linaro.org>
Cc: andreyknvl at google.com
Cc: Marc Zyngier <marc.zyngier@arm.com>
Tested-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
include/linux/kvm_host.h | 1 +
virt/kvm/kvm_main.c | 59 ++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index d025074..561e968 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -424,6 +424,7 @@ struct kvm {
struct mmu_notifier mmu_notifier;
unsigned long mmu_notifier_seq;
long mmu_notifier_count;
+ struct rcu_head mmu_notifier_rcu;
#endif
long tlbs_dirty;
struct list_head devices;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 88257b3..2c3fdd4 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -471,6 +471,7 @@ static void kvm_mmu_notifier_release(struct mmu_notifier *mn,
idx = srcu_read_lock(&kvm->srcu);
kvm_arch_flush_shadow_all(kvm);
srcu_read_unlock(&kvm->srcu, idx);
+ kvm_put_kvm(kvm);
}
static const struct mmu_notifier_ops kvm_mmu_notifier_ops = {
@@ -486,8 +487,46 @@ static const struct mmu_notifier_ops kvm_mmu_notifier_ops = {
static int kvm_init_mmu_notifier(struct kvm *kvm)
{
+ int rc;
kvm->mmu_notifier.ops = &kvm_mmu_notifier_ops;
- return mmu_notifier_register(&kvm->mmu_notifier, current->mm);
+ rc = mmu_notifier_register(&kvm->mmu_notifier, current->mm);
+ /*
+ * We hold a reference to KVM here to make sure that the KVM
+ * doesn't get free'd before ops->release() completes.
+ */
+ if (!rc)
+ kvm_get_kvm(kvm);
+ return rc;
+}
+
+static void kvm_free_vm_rcu(struct rcu_head *rcu)
+{
+ struct kvm *kvm = container_of(rcu, struct kvm, mmu_notifier_rcu);
+ kvm_arch_free_vm(kvm);
+}
+
+static void kvm_flush_shadow_mmu(struct kvm *kvm)
+{
+ /*
+ * We hold a reference to kvm instance for mmu_notifier and is
+ * only released when ops->release() is called via exit_mmap path.
+ * So, when we reach here ops->release() has been called already, which
+ * flushes the shadow page tables. Hence there is no need to call the
+ * release() again when we unregister the notifier. However, we need
+ * to delay freeing up the kvm until the release() completes, since
+ * we could reach here via :
+ * kvm_mmu_notifier_release() -> kvm_put_kvm() -> kvm_destroy_vm()
+ */
+ mmu_notifier_unregister_no_release(&kvm->mmu_notifier, kvm->mm);
+}
+
+static void kvm_free_vm(struct kvm *kvm)
+{
+ /*
+ * Wait until the mmu_notifier has finished the release().
+ * See comments above in kvm_flush_shadow_mmu.
+ */
+ mmu_notifier_call_srcu(&kvm->mmu_notifier_rcu, kvm_free_vm_rcu);
}
#else /* !(CONFIG_MMU_NOTIFIER && KVM_ARCH_WANT_MMU_NOTIFIER) */
@@ -497,6 +536,16 @@ static int kvm_init_mmu_notifier(struct kvm *kvm)
return 0;
}
+static void kvm_flush_shadow_mmu(struct kvm *kvm)
+{
+ kvm_arch_flush_shadow_all(kvm);
+}
+
+static void kvm_free_vm(struct kvm *kvm)
+{
+ kvm_arch_free_vm(kvm);
+}
+
#endif /* CONFIG_MMU_NOTIFIER && KVM_ARCH_WANT_MMU_NOTIFIER */
static struct kvm_memslots *kvm_alloc_memslots(void)
@@ -733,18 +782,14 @@ static void kvm_destroy_vm(struct kvm *kvm)
kvm->buses[i] = NULL;
}
kvm_coalesced_mmio_free(kvm);
-#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
- mmu_notifier_unregister(&kvm->mmu_notifier, kvm->mm);
-#else
- kvm_arch_flush_shadow_all(kvm);
-#endif
+ kvm_flush_shadow_mmu(kvm);
kvm_arch_destroy_vm(kvm);
kvm_destroy_devices(kvm);
for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++)
kvm_free_memslots(kvm, kvm->memslots[i]);
cleanup_srcu_struct(&kvm->irq_srcu);
cleanup_srcu_struct(&kvm->srcu);
- kvm_arch_free_vm(kvm);
+ kvm_free_vm(kvm);
preempt_notifier_dec();
hardware_disable_all();
mmdrop(mm);
--
2.7.4
^ permalink raw reply related
* [PATCH v3] soc: brcmstb: enable drivers for ARM64 and BMIPS
From: Markus Mayer @ 2017-04-20 16:55 UTC (permalink / raw)
To: linux-arm-kernel
From: Markus Mayer <mmayer@broadcom.com>
We enable the BRCMSTB SoC drivers not only for ARM, but also ARM64 and
BMIPS.
Signed-off-by: Markus Mayer <mmayer@broadcom.com>
---
Changes since v2:
- It is not necessary to use (COMPILE_TEST && OF)
Changes since v1:
- Add BMIPS
- Add COMPILE_TEST
drivers/soc/bcm/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig
index a39b0d58ddd0..49f1e2a75d61 100644
--- a/drivers/soc/bcm/Kconfig
+++ b/drivers/soc/bcm/Kconfig
@@ -11,7 +11,7 @@ config RASPBERRYPI_POWER
config SOC_BRCMSTB
bool "Broadcom STB SoC drivers"
- depends on ARM
+ depends on ARM || ARM64 || BMIPS_GENERIC || COMPILE_TEST
select SOC_BUS
help
Enables drivers for the Broadcom Set-Top Box (STB) series of chips.
--
2.12.0
^ permalink raw reply related
* [PATCH] ARM: dts: Add devicetree for the Raspberry Pi 3, for arm32 (v4)
From: Florian Fainelli @ 2017-04-20 17:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170330002605.15213-1-eric@anholt.net>
On 03/29/2017 05:26 PM, Eric Anholt wrote:
> Raspbian and Fedora have decided to support the Pi3 in 32-bit mode for
> now, so it's useful to be able to test that mode on an upstream
> kernel. It's also been useful for me to use the same board for 32-bit
> and 64-bit development.
>
> Signed-off-by: Eric Anholt <eric@anholt.net>
> ---
>
> v1: Gerd's patch that put the ../../../arm64/... link in the Makefile
> v2: Michael's patch that #included from ../../../arm64/... in a new
> bcm2837-rpi-3-b.dts.
> v3: Mine, using symlinks to make sure that we don't break the split DT
> tree.
> v4: Rely on the new include/arm64 symlink.
>
> Assuming positive review feedback, I assume it would be acceptable to
> merge the shared/dt-symlinks branch in a PR of my own for the 32-bit
> DT branch?
Either that, or I can take it directly through devicetree/next whichever
you prefer. We have not gotten feedback on whether this looks acceptable
or not though...
>
> arch/arm/boot/dts/Makefile | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index 011808490fed..27d258cb50f2 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -72,6 +72,7 @@ dtb-$(CONFIG_ARCH_BCM2835) += \
> bcm2835-rpi-b-plus.dtb \
> bcm2835-rpi-a-plus.dtb \
> bcm2836-rpi-2-b.dtb \
> + include/arm64/broadcom/bcm2837-rpi-3-b.dtb \
> bcm2835-rpi-zero.dtb
> dtb-$(CONFIG_ARCH_BCM_5301X) += \
> bcm4708-asus-rt-ac56u.dtb \
>
--
Florian
^ permalink raw reply
* [PATCH RFC 0/4] coresight: support dump ETB RAM
From: Mathieu Poirier @ 2017-04-20 17:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1491901829-18477-1-git-send-email-leo.yan@linaro.org>
On 11 April 2017 at 03:10, Leo Yan <leo.yan@linaro.org> wrote:
>
> ### Introduction ###
>
> Embedded Trace Buffer (ETB) provides on-chip storage of trace data,
> usually has buffer size from 2KB to 8KB. These data has been used for
> profiling and this has been well implemented in coresight driver.
>
> This patch is to explore ETB RAM data for postmortem debugging. Due ETB
> RAM buffer has small size, so the real trace data caused error is
> easily to be overwritten by other PEs; but we could consider ETB RAM
> data is quite useful for postmortem debugging with below scenarios:
>
> Case 1: if system is bus lockup and CPU pipeline stalls for bus
> accessing, CPUs have no more chance to fill enough data into ETB RAM
> so after analyze ETB RAM we can quickly get to know the culprit if bus
> lock is caused by improper programs, one often example is wrongly to
> access the module without enable the module's clock. For this case,
> we can rely on watchdog to trigger SoC reset and if lucky the ETB RAM
> can survive after reset. So for this case, after system reboot we can
> save ETB RAM before any new data input into it.
>
> Case 2: There also has another hardware design with local ETB buffer
> (ARM DDI 0461B) chapter 1.2.7. Local ETF, with this kind design every
> CPU may has one dedicated ETB RAM. So it's quite handy that we can use
> alive CPU to help dump the hang CPU ETB RAM. Then we can quickly get
> to know what's the last point the CPU has executed before its hang.
>
>
> ### Implementation ###
>
> Based on current Coresight ETB driver, we only needs some minor
> enhancement so can support dump ETB RAM with two methods.
>
> Patches 0001/0002 are minor fixes so can support more scenarios for ETB
> RAM dumping.
>
> Patch 0003 is to dump ETB RAM after system reboot, this is for the
> platforms which use watchdog reset and ETB RAM can survive.
>
> Patch 0004 is to dump ETB RAM when panic happens, so we can save ETB RAM
> into memory. If we connect this with Kdump, then we can easily extract
> the ETB RAM from vmcore.
>
>
> ### Usage ###
>
> To dump ETB RAM after reboot, simply use below command:
> # dd if=/dev/f6402000.etf of=cstrace.bin
>
> To dump ETB RAM for kernel panic, we need add "crash_kexec_post_notifiers"
> into kernel command line so let kernel call panic notifiers before launch
> dump kernel. After dump kernel has booted up, we need use below methods
> to ETB RAM offline analysis:
>
> On the target:
> # cp /proc/vmcore ./vmcore
> # scp ./vmcore your at hostpc
>
> On the host PC:
> # ./crash vmcore vmlinux
>
> crash> log
> [...]
> [ 112.600051] coresight-tmc f6402000.etf: Flush ETB buffer 0x2000 at 0xffff800038300080
> [ 112.614743] Starting crashdump kernel...
> [ 112.618681] Bye!
> crash> rd 0xffff800038300080 0x2000 -r /tmp/cstrace.bin
> 8192 bytes copied from 0xffff800038300080 to /tmp/cstrace.bin
>
> After we get cstrace.bin data, we can use OpenCSD snapshot method to parse
> ETB trace data. These two methods have been verified on Hikey, For Hikey
> snapshot config files you can refer [1]. For total kernel patches for
> integration Kdump and Coresight, you can refer [2].
>
> [1] http://people.linaro.org/~leo.yan/opencsd_hikey/hikey_snapshot.tgz
> [2] https://git.linaro.org/people/leo.yan/linux-debug-workshop.git/log/?h=coresight_etb_dump
>
>
> ### TODO ###
>
> Need work for ETB1.0 driver, this is based on review and comments
> for this patch set.
Hi Leo and thank you for this first stab.
The first thing to do is drop the case where trace data are salvaged
from ETB memory after a crash. This method is not reliable and the
trace data is almost guaranteed to have some sort of corruption since
the debug power domain will be reset by the architecture. On top of
things it only applies to the ETB.
Also function tmc_enable/disable_etf_sink() can be called hundreds of
times during a trace session. Inserting and removing the panic
notifier is too much overhead. The notifier should be added when a
session is started and removed when it ends.
Your patchset doesn't deal with trace configuration, and that is a
serious problem. Trace data can't be decoded without them. What we
have for perf [1] is already working well and I would like to avoid
having to parse two different header format. The header could be
inserted at the beginning of the file that is retreived after a crash
dump.
Last but not least we need to come up with an API to deal with the
kernel crash dump functionality. From there sinks could chose to
simply call the API when they are ready. All the crash dump specific
stuff happens in the coresight crash dump code while everything
related to the sinks (of any kind) happens in the driver. Look at
coresight-etm-perf.c for an idea of what I mean.
Regards,
Mathieu
[1]. http://lxr.free-electrons.com/source/tools/perf/util/cs-etm.h
>
>
> Leo Yan (4):
> coresight: tmc: check dump buffer is overflow
> coresight: tmc: set read pointer before dump RAM
> coresight: tmc: dump RAM when device is disabled
> coresight: tmc: dump RAM for panic
>
> drivers/hwtracing/coresight/coresight-tmc-etf.c | 86 ++++++++++++++++++++++++-
> drivers/hwtracing/coresight/coresight-tmc.h | 2 +
> 2 files changed, 85 insertions(+), 3 deletions(-)
>
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v5] arm64: Add support for DMA_ATTR_FORCE_CONTIGUOUS to IOMMU
From: Catalin Marinas @ 2017-04-20 17:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1488908612-22522-1-git-send-email-geert+renesas@glider.be>
Catching up with these threads, so replying to a patch I already
applied.
On Tue, Mar 07, 2017 at 06:43:32PM +0100, Geert Uytterhoeven wrote:
> --- a/arch/arm64/mm/dma-mapping.c
> +++ b/arch/arm64/mm/dma-mapping.c
> @@ -584,20 +584,7 @@ static void *__iommu_alloc_attrs(struct device *dev, size_t size,
> */
> gfp |= __GFP_ZERO;
>
> - if (gfpflags_allow_blocking(gfp)) {
> - struct page **pages;
> - pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, coherent);
> -
> - pages = iommu_dma_alloc(dev, iosize, gfp, attrs, ioprot,
> - handle, flush_page);
> - if (!pages)
> - return NULL;
> -
> - addr = dma_common_pages_remap(pages, size, VM_USERMAP, prot,
> - __builtin_return_address(0));
> - if (!addr)
> - iommu_dma_free(dev, pages, iosize, handle);
> - } else {
> + if (!gfpflags_allow_blocking(gfp)) {
> struct page *page;
> /*
> * In atomic context we can't remap anything, so we'll only
> @@ -621,6 +608,45 @@ static void *__iommu_alloc_attrs(struct device *dev, size_t size,
> __free_from_pool(addr, size);
> addr = NULL;
> }
> + } else if (attrs & DMA_ATTR_FORCE_CONTIGUOUS) {
> + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, coherent);
> + struct page *page;
> +
> + page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT,
> + get_order(size), gfp);
> + if (!page)
> + return NULL;
> +
> + *handle = iommu_dma_map_page(dev, page, 0, iosize, ioprot);
> + if (iommu_dma_mapping_error(dev, *handle)) {
> + dma_release_from_contiguous(dev, page,
> + size >> PAGE_SHIFT);
> + return NULL;
> + }
> + if (!coherent)
> + __dma_flush_area(page_to_virt(page), iosize);
> +
> + addr = dma_common_contiguous_remap(page, size, VM_USERMAP,
> + prot,
> + __builtin_return_address(0));
Do we need to call dma_common_pages_remap() if the allocation is
coherent? In the __dma_alloc() case we don't do it but simply use
page_address(page) as returned by __dma_alloc_coherent().
(note that my comment is not meant to fix the issue reported by Andrzej
Hajda but I just spotted it)
--
Catalin
^ permalink raw reply
* [PATCH] arm64: xen: Implement EFI reset_system callback
From: Julien Grall @ 2017-04-20 18:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <b9238471-6957-8720-9c98-37a5a32776d2@suse.com>
Hi,
On 18/04/17 19:51, Juergen Gross wrote:
> On 18/04/17 20:46, Stefano Stabellini wrote:
>> On Tue, 18 Apr 2017, Juergen Gross wrote:
>>> On 18/04/17 20:37, Stefano Stabellini wrote:
>>>> On Thu, 6 Apr 2017, Juergen Gross wrote:
>>>>> On 06/04/17 18:43, Daniel Kiper wrote:
>>>>>> On Thu, Apr 06, 2017 at 06:22:44PM +0200, Juergen Gross wrote:
>>>>>>> On 06/04/17 18:06, Daniel Kiper wrote:
>>>>>>>> Hi Julien,
>>>>>>>>
>>>>>>>> On Thu, Apr 06, 2017 at 04:39:13PM +0100, Julien Grall wrote:
>>>>>>>>> Hi Daniel,
>>>>>>>>>
>>>>>>>>> On 06/04/17 16:20, Daniel Kiper wrote:
>>>>>>>>>> On Thu, Apr 06, 2017 at 04:38:24PM +0200, Juergen Gross wrote:
>>>>>>>>>>> On 06/04/17 16:27, Daniel Kiper wrote:
>>>>>>>>>>>> On Thu, Apr 06, 2017 at 09:32:32AM +0100, Julien Grall wrote:
>>>>>>>>>>>>> Hi Juergen,
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 06/04/17 07:23, Juergen Gross wrote:
>>>>>>>>>>>>>> On 05/04/17 21:49, Boris Ostrovsky wrote:
>>>>>>>>>>>>>>> On 04/05/2017 02:14 PM, Julien Grall wrote:
>>>>>>>>>>>>>>>> The x86 code has theoritically a similar issue, altought EFI does not
>>>>>>>>>>>>>>>> seem to be the preferred method. I have left it unimplemented on x86 and
>>>>>>>>>>>>>>>> CCed Linux Xen x86 maintainers to know their view here.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> (+Daniel)
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> This could be a problem for x86 as well, at least theoretically.
>>>>>>>>>>>>>>> xen_machine_power_off() may call pm_power_off(), which is efi.reset_system.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> So I think we should have a similar routine there.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> +1
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I don't see any problem with such a routine added, in contrast to
>>>>>>>>>>>>>> potential "reboots" instead of power off without it.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> So I think this dummy xen_efi_reset_system() should be added to
>>>>>>>>>>>>>> drivers/xen/efi.c instead.
>>>>>>>>>>>>>
>>>>>>>>>>>>> I will resend the patch during day with xen_efi_reset_system moved
>>>>>>>>>>>>> to common code and implement the x86 counterpart (thought, I will
>>>>>>>>>>>>> not be able to test it).
>>>>>>>>>>>>
>>>>>>>>>>>> I think that this is ARM specific issue. On x86 machine_restart() calls
>>>>>>>>>>>> xen_restart(). Hence, everything works. So, I think that it should be
>>>>>>>>>>>> fixed only for ARM. Anyway, please CC me when you send a patch.
>>>>>>>>>>>
>>>>>>>>>>> What about xen_machine_power_off() (as stated in Boris' mail)?
>>>>>>>>>>
>>>>>>>>>> Guys what do you think about that:
>>>>>>>>>>
>>>>>>>>>> --- a/drivers/firmware/efi/reboot.c
>>>>>>>>>> +++ b/drivers/firmware/efi/reboot.c
>>>>>>>>>> @@ -55,7 +55,7 @@ static void efi_power_off(void)
>>>>>>>>>>
>>>>>>>>>> static int __init efi_shutdown_init(void)
>>>>>>>>>> {
>>>>>>>>>> - if (!efi_enabled(EFI_RUNTIME_SERVICES))
>>>>>>>>>> + if (!efi_enabled(EFI_RUNTIME_SERVICES) || efi_enabled(EFI_PARAVIRT))
>>>>>>>>>> return -ENODEV;
>>>>>>>>>>
>>>>>>>>>> if (efi_poweroff_required())
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Julien, for ARM64 please take a look at arch/arm64/kernel/efi.c:efi_poweroff_required(void).
>>>>>>>>>>
>>>>>>>>>> I hope that tweaks for both files should solve our problem.
>>>>>>>>>
>>>>>>>>> This sounds good for power off (I haven't tried to power off DOM0
>>>>>>>>> yet). But this will not solve the restart problem (see
>>>>>>>>> machine_restart in arch/arm64/kernel/process.c) which call directly
>>>>>>>>> efi_reboot.
>>>>>>>>
>>>>>>>> Hmmm... It seems to me that efi.reset_system override with empty function
>>>>>>>> in arch/arm/xen/efi.c is the best solution. So, I see three patches here.
>>>>>>>> One for drivers/firmware/efi/reboot.c, one for arch/arm/xen/efi.c and one
>>>>>>>> for arch/arm64/kernel/efi.c. Does it make sense?
>>>>>>>
>>>>>>> I still think the empty function should be in drivers/xen/efi.c and we
>>>>>>> should use it in arch/x86/xen/efi.c, too.
>>>>>>
>>>>>> If you wish we can go that way too. Though I thing that we should fix
>>>>>> drivers/firmware/efi/reboot.c:efi_shutdown_init() too. Just in case.
>>>>>
>>>>> Sure, go ahead. I won't object.
>>>>
>>>> For the Xen on ARM side, the original patch that started this thread
>>>> (20170405181417.15985-1-julien.grall at arm.com) is good to go, right?
>>>>
>>>
>>> As I said: the dummy xen_efi_reset_system() should be in
>>> drivers/xen/efi.c
>>
>> OK. Who is working on it?
>
> Didn't Julien say he would do it?
Yes. I looked at bit closer to the problem mention with power off.
xen_efi_reset_system cannot be a NOP because there may not be fallback
alternatives (see machine_power_off in arch/arm64/kernel/process.c)
So I think we would have to translate EFI_RESET* to Xen SHUTDOWN_* and
then call HYPERVISOR_sched_op directly.
I will send a new version soon.
Cheers,
--
Julien Grall
^ permalink raw reply
* [PATCH 0/4] arm64: improve tagged pointer handling
From: Kristina Martsenko @ 2017-04-20 18:17 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
Here are some patches to fix a few issues related to tagged pointer
handling.
Tagged pointers from userspace can end up in the kernel in a number of
ways. I most likely have not found all of them, but they include at
least the following:
- Passing tagged pointers in system call arguments. This would be a
userspace bug, as documented in tagged-pointers.txt.
- Through FAR_EL1 when we take a data abort or watchpoint exception.
Watchpoint handling is currently broken if we get a tagged pointer,
patch #2 in this series fixes it. We already do the right thing for
data aborts but patch #3 tries to improve on it a little.
- Reading a tagged pointer from a GPR when trapping and emulating
instructions, e.g. cache maintenance or uprobes. Patch #1 fixes the
cache maintenance case.
- The user stack pointer, frame pointer (x29), frame records, and link
register (x30) can contain tagged pointers. Patch #4 documents that
some kernel features do not currently work with tagged pointers in
the first three of these.
- A tagged pointer can end up in the PC on an illegal exception return
(see D4.1.4 ARMARM A.k_iss10775), and from there in ELR on exception
entry. As I understand it, this can only be caused by a bad eret at
EL1 or a bad debug state exit by an external debugger, so only by a
bug in Linux/firmware or the external debugger. So I don't think we
need to handle this.
Note that the above applies to Linux only. I have spoken to Marc Zyngier
about KVM, and so far he hasn't found any problems there.
Thanks,
Kristina
Kristina Martsenko (4):
arm64: traps: fix userspace cache maintenance emulation on a tagged
pointer
arm64: hw_breakpoint: fix watchpoint matching for tagged pointers
arm64: entry: improve data abort handling of tagged pointers
arm64: documentation: document tagged pointer stack constraints
Documentation/arm64/tagged-pointers.txt | 62 +++++++++++++++++++++++++--------
arch/arm64/include/asm/asm-uaccess.h | 9 +++++
arch/arm64/include/asm/uaccess.h | 6 ++--
arch/arm64/kernel/entry.S | 4 ++-
arch/arm64/kernel/hw_breakpoint.c | 3 ++
arch/arm64/kernel/traps.c | 4 +--
6 files changed, 67 insertions(+), 21 deletions(-)
--
2.1.4
^ permalink raw reply
* [PATCH 1/4] arm64: traps: fix userspace cache maintenance emulation on a tagged pointer
From: Kristina Martsenko @ 2017-04-20 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492712234-4950-1-git-send-email-kristina.martsenko@arm.com>
When we emulate userspace cache maintenance in the kernel, we can
currently send the task a SIGSEGV even though the maintenance was done
on a valid address. This happens if the address has a non-zero address
tag, and happens to not be mapped in.
When we get the address from a user register, we don't currently remove
the address tag before performing cache maintenance on it. If the
maintenance faults, we end up in either __do_page_fault, where find_vma
can't find the VMA if the address has a tag, or in do_translation_fault,
where the tagged address will appear to be above TASK_SIZE. In both
cases, the address is not mapped in, and the task is sent a SIGSEGV.
This patch removes the tag from the address before using it. With this
patch, the fault is handled correctly, the address gets mapped in, and
the cache maintenance succeeds.
As a second bug, if cache maintenance (correctly) fails on an invalid
tagged address, the address gets passed into arm64_notify_segfault,
where find_vma fails to find the VMA due to the tag, and the wrong
si_code may be sent as part of the siginfo_t of the segfault. With this
patch, the correct si_code is sent.
Fixes: 7dd01aef0557 ("arm64: trap userspace "dc cvau" cache operation on errata-affected core")
Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
---
Note that patch #3 would also fix the first bug (incorrect segfault),
but not the second (wrong si_code), hence this patch.
arch/arm64/kernel/traps.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index e52be6aa44ee..45c8eca951bc 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -443,7 +443,7 @@ int cpu_enable_cache_maint_trap(void *__unused)
}
#define __user_cache_maint(insn, address, res) \
- if (untagged_addr(address) >= user_addr_max()) { \
+ if (address >= user_addr_max()) { \
res = -EFAULT; \
} else { \
uaccess_ttbr0_enable(); \
@@ -469,7 +469,7 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
int crm = (esr & ESR_ELx_SYS64_ISS_CRM_MASK) >> ESR_ELx_SYS64_ISS_CRM_SHIFT;
int ret = 0;
- address = pt_regs_read_reg(regs, rt);
+ address = untagged_addr(pt_regs_read_reg(regs, rt));
switch (crm) {
case ESR_ELx_SYS64_ISS_CRM_DC_CVAU: /* DC CVAU, gets promoted */
--
2.1.4
^ permalink raw reply related
* [PATCH 2/4] arm64: hw_breakpoint: fix watchpoint matching for tagged pointers
From: Kristina Martsenko @ 2017-04-20 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1492712234-4950-1-git-send-email-kristina.martsenko@arm.com>
When we take a watchpoint exception, the address that triggered the
watchpoint is found in FAR_EL1. We compare it to the address of each
configured watchpoint to see which one was hit.
The configured watchpoint addresses are untagged, while the address in
FAR_EL1 will have an address tag if the data access was done using a
tagged address. The tag needs to be removed to compare the address to
the watchpoints.
Currently we don't remove it, and as a result can report the wrong
watchpoint as being hit (specifically, always either the highest TTBR0
watchpoint or lowest TTBR1 watchpoint). This patch removes the tag.
Fixes: d50240a5f6ce ("arm64: mm: permit use of tagged pointers at EL0")
Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
---
arch/arm64/include/asm/uaccess.h | 6 +++---
arch/arm64/kernel/hw_breakpoint.c | 3 +++
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 5308d696311b..0221029e27ff 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -106,9 +106,9 @@ static inline void set_fs(mm_segment_t fs)
})
/*
- * When dealing with data aborts or instruction traps we may end up with
- * a tagged userland pointer. Clear the tag to get a sane pointer to pass
- * on to access_ok(), for instance.
+ * When dealing with data aborts, watchpoints, or instruction traps we may end
+ * up with a tagged userland pointer. Clear the tag to get a sane pointer to
+ * pass on to access_ok(), for instance.
*/
#define untagged_addr(addr) sign_extend64(addr, 55)
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 0296e7924240..749f81779420 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -36,6 +36,7 @@
#include <asm/traps.h>
#include <asm/cputype.h>
#include <asm/system_misc.h>
+#include <asm/uaccess.h>
/* Breakpoint currently in use for each BRP. */
static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[ARM_MAX_BRP]);
@@ -721,6 +722,8 @@ static u64 get_distance_from_watchpoint(unsigned long addr, u64 val,
u64 wp_low, wp_high;
u32 lens, lene;
+ addr = untagged_addr(addr);
+
lens = __ffs(ctrl->len);
lene = __fls(ctrl->len);
--
2.1.4
^ permalink raw reply related
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