* [PATCH v7 3/5] media: sunxi: Add A10 CSI driver
From: Maxime Ripard @ 2019-08-20 11:24 UTC (permalink / raw)
To: Hans Verkuil, Sakari Ailus, Mauro Carvalho Chehab
Cc: Mark Rutland, devicetree, Maxime Ripard, linux-kernel,
Chen-Yu Tsai, Rob Herring, Laurent Pinchart, Thomas Petazzoni,
Frank Rowand, linux-arm-kernel, linux-media
In-Reply-To: <cover.b695c63cf668192aff5574a3005d483c601e77f6.1566300265.git-series.maxime.ripard@bootlin.com>
From: Maxime Ripard <maxime.ripard@bootlin.com>
The older CSI drivers have camera capture interface different from the one
in the newer ones.
This IP is pretty simple. Some variants (one controller out of two
instances on some SoCs) have an ISP embedded, but there's no code that make
use of it, so we ignored that part for now.
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
MAINTAINERS | 8 +-
drivers/media/platform/sunxi/Kconfig | 1 +-
drivers/media/platform/sunxi/Makefile | 1 +-
drivers/media/platform/sunxi/sun4i-csi/Kconfig | 11 +-
drivers/media/platform/sunxi/sun4i-csi/Makefile | 5 +-
drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 305 +++++++++-
drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h | 159 +++++-
drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 444 +++++++++++++-
drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 383 +++++++++++-
9 files changed, 1317 insertions(+)
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/Kconfig
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/Makefile
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 30bf852e6d6b..b15543b06a16 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1420,6 +1420,14 @@ F: drivers/pinctrl/sunxi/
F: drivers/soc/sunxi/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux.git
+Allwinner A10 CSI driver
+M: Maxime Ripard <maxime.ripard@bootlin.com>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/platform/sunxi/sun4i-csi/
+F: Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml
+
ARM/Amlogic Meson SoC CLOCK FRAMEWORK
M: Neil Armstrong <narmstrong@baylibre.com>
M: Jerome Brunet <jbrunet@baylibre.com>
diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig
index 1b6e89cb78b2..71808e93ac2e 100644
--- a/drivers/media/platform/sunxi/Kconfig
+++ b/drivers/media/platform/sunxi/Kconfig
@@ -1 +1,2 @@
+source "drivers/media/platform/sunxi/sun4i-csi/Kconfig"
source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile
index 8d06f98500ee..a05127529006 100644
--- a/drivers/media/platform/sunxi/Makefile
+++ b/drivers/media/platform/sunxi/Makefile
@@ -1 +1,2 @@
+obj-y += sun4i-csi/
obj-y += sun6i-csi/
diff --git a/drivers/media/platform/sunxi/sun4i-csi/Kconfig b/drivers/media/platform/sunxi/sun4i-csi/Kconfig
new file mode 100644
index 000000000000..e86e29b6a603
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun4i-csi/Kconfig
@@ -0,0 +1,11 @@
+config VIDEO_SUN4I_CSI
+ tristate "Allwinner A10 CMOS Sensor Interface Support"
+ depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ depends on ARCH_SUNXI || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ help
+ This is a V4L2 driver for the Allwinner A10 CSI
+
+ To compile this driver as a module, choose M here: the module
+ will be called sun4i_csi.
diff --git a/drivers/media/platform/sunxi/sun4i-csi/Makefile b/drivers/media/platform/sunxi/sun4i-csi/Makefile
new file mode 100644
index 000000000000..7c790a57f5ee
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun4i-csi/Makefile
@@ -0,0 +1,5 @@
+sun4i-csi-y += sun4i_csi.o
+sun4i-csi-y += sun4i_dma.o
+sun4i-csi-y += sun4i_v4l2.o
+
+obj-$(CONFIG_VIDEO_SUN4I_CSI) += sun4i-csi.o
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
new file mode 100644
index 000000000000..6f7980e28a98
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 NextThing Co
+ * Copyright (C) 2016-2019 Bootlin
+ *
+ * Author: Maxime Ripard <maxime.ripard@bootlin.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mediabus.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "sun4i_csi.h"
+
+static const struct media_entity_operations sun4i_csi_video_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int sun4i_csi_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct sun4i_csi *csi = container_of(notifier, struct sun4i_csi,
+ notifier);
+
+ csi->src_subdev = subdev;
+ csi->src_pad = media_entity_get_fwnode_pad(&subdev->entity,
+ subdev->fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (csi->src_pad < 0) {
+ dev_err(csi->dev, "Couldn't find output pad for subdev %s\n",
+ subdev->name);
+ return csi->src_pad;
+ }
+
+ dev_dbg(csi->dev, "Bound %s pad: %d\n", subdev->name, csi->src_pad);
+ return 0;
+}
+
+static int sun4i_csi_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct sun4i_csi *csi = container_of(notifier, struct sun4i_csi,
+ notifier);
+ struct v4l2_subdev *subdev = &csi->subdev;
+ struct video_device *vdev = &csi->vdev;
+ int ret;
+
+ ret = v4l2_device_register_subdev(&csi->v4l, subdev);
+ if (ret < 0)
+ return ret;
+
+ ret = sun4i_csi_v4l2_register(csi);
+ if (ret < 0)
+ return ret;
+
+ ret = media_device_register(&csi->mdev);
+ if (ret)
+ return ret;
+
+ /* Create link from subdev to main device */
+ ret = media_create_pad_link(&subdev->entity, CSI_SUBDEV_SOURCE,
+ &vdev->entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ goto err_clean_media;
+
+ ret = media_create_pad_link(&csi->src_subdev->entity, csi->src_pad,
+ &subdev->entity, CSI_SUBDEV_SINK,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ goto err_clean_media;
+
+ ret = v4l2_device_register_subdev_nodes(&csi->v4l);
+ if (ret < 0)
+ goto err_clean_media;
+
+ return 0;
+
+err_clean_media:
+ media_device_unregister(&csi->mdev);
+
+ return ret;
+}
+
+static const struct v4l2_async_notifier_operations sun4i_csi_notify_ops = {
+ .bound = sun4i_csi_notify_bound,
+ .complete = sun4i_csi_notify_complete,
+};
+
+static int sun4i_csi_async_parse(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ struct sun4i_csi *csi = dev_get_drvdata(dev);
+
+ if (vep->base.port || vep->base.id)
+ return -EINVAL;
+
+ if (vep->bus_type != V4L2_MBUS_PARALLEL)
+ return -EINVAL;
+
+ csi->bus = vep->bus.parallel;
+
+ return 0;
+}
+
+static int sun4i_csi_probe(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev;
+ struct video_device *vdev;
+ struct sun4i_csi *csi;
+ struct resource *res;
+ int ret;
+ int irq;
+
+ csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
+ if (!csi)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, csi);
+ csi->dev = &pdev->dev;
+ subdev = &csi->subdev;
+ vdev = &csi->vdev;
+
+ csi->mdev.dev = csi->dev;
+ strscpy(csi->mdev.model, "Allwinner Video Capture Device",
+ sizeof(csi->mdev.model));
+ csi->mdev.hw_revision = 0;
+ media_device_init(&csi->mdev);
+ v4l2_async_notifier_init(&csi->notifier);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ csi->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(csi->regs))
+ return PTR_ERR(csi->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ csi->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(csi->bus_clk)) {
+ dev_err(&pdev->dev, "Couldn't get our bus clock\n");
+ return PTR_ERR(csi->bus_clk);
+ }
+
+ csi->isp_clk = devm_clk_get(&pdev->dev, "isp");
+ if (IS_ERR(csi->isp_clk)) {
+ dev_err(&pdev->dev, "Couldn't get our ISP clock\n");
+ return PTR_ERR(csi->isp_clk);
+ }
+
+ csi->ram_clk = devm_clk_get(&pdev->dev, "ram");
+ if (IS_ERR(csi->ram_clk)) {
+ dev_err(&pdev->dev, "Couldn't get our ram clock\n");
+ return PTR_ERR(csi->ram_clk);
+ }
+
+ csi->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(csi->rst)) {
+ dev_err(&pdev->dev, "Couldn't get our reset line\n");
+ return PTR_ERR(csi->rst);
+ }
+
+ /* Initialize subdev */
+ v4l2_subdev_init(subdev, &sun4i_csi_subdev_ops);
+ subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ subdev->owner = THIS_MODULE;
+ snprintf(subdev->name, sizeof(subdev->name), "sun4i-csi-0");
+ v4l2_set_subdevdata(subdev, csi);
+
+ csi->subdev_pads[CSI_SUBDEV_SINK].flags =
+ MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+ csi->subdev_pads[CSI_SUBDEV_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&subdev->entity, CSI_SUBDEV_PADS,
+ csi->subdev_pads);
+ if (ret < 0)
+ return ret;
+
+ csi->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+ vdev->entity.ops = &sun4i_csi_video_entity_ops;
+ ret = media_entity_pads_init(&vdev->entity, 1, &csi->vdev_pad);
+ if (ret < 0)
+ return ret;
+
+ ret = sun4i_csi_dma_register(csi, irq);
+ if (ret)
+ goto err_clean_pad;
+
+ csi->v4l.mdev = &csi->mdev;
+ ret = v4l2_async_notifier_parse_fwnode_endpoints(csi->dev,
+ &csi->notifier,
+ sizeof(struct v4l2_async_subdev),
+ sun4i_csi_async_parse);
+ if (ret)
+ goto err_unregister_media;
+ csi->notifier.ops = &sun4i_csi_notify_ops;
+
+ ret = v4l2_async_notifier_register(&csi->v4l, &csi->notifier);
+ if (ret) {
+ dev_err(csi->dev,
+ "Couldn't register our v4l2-async notifier\n");
+ goto err_free_notifier;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+err_free_notifier:
+ v4l2_async_notifier_cleanup(&csi->notifier);
+
+err_unregister_media:
+ media_device_unregister(&csi->mdev);
+ sun4i_csi_dma_unregister(csi);
+
+err_clean_pad:
+ media_device_cleanup(&csi->mdev);
+
+ return ret;
+}
+
+static int sun4i_csi_remove(struct platform_device *pdev)
+{
+ struct sun4i_csi *csi = platform_get_drvdata(pdev);
+
+ v4l2_async_notifier_unregister(&csi->notifier);
+ v4l2_async_notifier_cleanup(&csi->notifier);
+ media_device_unregister(&csi->mdev);
+ sun4i_csi_dma_unregister(csi);
+ media_device_cleanup(&csi->mdev);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_csi_of_match[] = {
+ { .compatible = "allwinner,sun7i-a20-csi0" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_csi_of_match);
+
+static int __maybe_unused sun4i_csi_runtime_resume(struct device *dev)
+{
+ struct sun4i_csi *csi = dev_get_drvdata(dev);
+
+ reset_control_deassert(csi->rst);
+ clk_prepare_enable(csi->bus_clk);
+ clk_prepare_enable(csi->ram_clk);
+ clk_set_rate(csi->isp_clk, 80000000);
+ clk_prepare_enable(csi->isp_clk);
+
+ writel(1, csi->regs + CSI_EN_REG);
+
+ return 0;
+}
+
+static int __maybe_unused sun4i_csi_runtime_suspend(struct device *dev)
+{
+ struct sun4i_csi *csi = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(csi->isp_clk);
+ clk_disable_unprepare(csi->ram_clk);
+ clk_disable_unprepare(csi->bus_clk);
+
+ reset_control_assert(csi->rst);
+
+ return 0;
+}
+
+static const struct dev_pm_ops sun4i_csi_pm_ops = {
+ SET_RUNTIME_PM_OPS(sun4i_csi_runtime_suspend,
+ sun4i_csi_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver sun4i_csi_driver = {
+ .probe = sun4i_csi_probe,
+ .remove = sun4i_csi_remove,
+ .driver = {
+ .name = "sun4i-csi",
+ .of_match_table = sun4i_csi_of_match,
+ .pm = &sun4i_csi_pm_ops,
+ },
+};
+module_platform_driver(sun4i_csi_driver);
+
+MODULE_DESCRIPTION("Allwinner A10 Camera Sensor Interface driver");
+MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
new file mode 100644
index 000000000000..df23fda46c0e
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2016 NextThing Co
+ * Copyright (C) 2016-2019 Bootlin
+ *
+ * Author: Maxime Ripard <maxime.ripard@bootlin.com>
+ */
+
+#ifndef _SUN4I_CSI_H_
+#define _SUN4I_CSI_H_
+
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/videobuf2-core.h>
+
+#define CSI_EN_REG 0x00
+
+#define CSI_CFG_REG 0x04
+#define CSI_CFG_INPUT_FMT(fmt) ((fmt) << 20)
+#define CSI_CFG_OUTPUT_FMT(fmt) ((fmt) << 16)
+#define CSI_CFG_YUV_DATA_SEQ(seq) ((seq) << 8)
+#define CSI_CFG_VSYNC_POL(pol) ((pol) << 2)
+#define CSI_CFG_HSYNC_POL(pol) ((pol) << 1)
+#define CSI_CFG_PCLK_POL(pol) ((pol) << 0)
+
+#define CSI_CPT_CTRL_REG 0x08
+#define CSI_CPT_CTRL_VIDEO_START BIT(1)
+#define CSI_CPT_CTRL_IMAGE_START BIT(0)
+
+#define CSI_BUF_ADDR_REG(fifo, buf) (0x10 + (0x8 * (fifo)) + (0x4 * (buf)))
+
+#define CSI_BUF_CTRL_REG 0x28
+#define CSI_BUF_CTRL_DBN BIT(2)
+#define CSI_BUF_CTRL_DBS BIT(1)
+#define CSI_BUF_CTRL_DBE BIT(0)
+
+#define CSI_INT_EN_REG 0x30
+#define CSI_INT_FRM_DONE BIT(1)
+#define CSI_INT_CPT_DONE BIT(0)
+
+#define CSI_INT_STA_REG 0x34
+
+#define CSI_WIN_CTRL_W_REG 0x40
+#define CSI_WIN_CTRL_W_ACTIVE(w) ((w) << 16)
+
+#define CSI_WIN_CTRL_H_REG 0x44
+#define CSI_WIN_CTRL_H_ACTIVE(h) ((h) << 16)
+
+#define CSI_BUF_LEN_REG 0x48
+
+#define CSI_MAX_BUFFER 2
+#define CSI_MAX_HEIGHT 8192U
+#define CSI_MAX_WIDTH 8192U
+
+enum csi_input {
+ CSI_INPUT_RAW = 0,
+ CSI_INPUT_BT656 = 2,
+ CSI_INPUT_YUV = 3,
+};
+
+enum csi_output_raw {
+ CSI_OUTPUT_RAW_PASSTHROUGH = 0,
+};
+
+enum csi_output_yuv {
+ CSI_OUTPUT_YUV_422_PLANAR = 0,
+ CSI_OUTPUT_YUV_420_PLANAR = 1,
+ CSI_OUTPUT_YUV_422_UV = 4,
+ CSI_OUTPUT_YUV_420_UV = 5,
+ CSI_OUTPUT_YUV_422_MACRO = 8,
+ CSI_OUTPUT_YUV_420_MACRO = 9,
+};
+
+enum csi_yuv_data_seq {
+ CSI_YUV_DATA_SEQ_YUYV = 0,
+ CSI_YUV_DATA_SEQ_YVYU = 1,
+ CSI_YUV_DATA_SEQ_UYVY = 2,
+ CSI_YUV_DATA_SEQ_VYUY = 3,
+};
+
+enum csi_subdev_pads {
+ CSI_SUBDEV_SINK,
+ CSI_SUBDEV_SOURCE,
+
+ CSI_SUBDEV_PADS,
+};
+
+extern const struct v4l2_subdev_ops sun4i_csi_subdev_ops;
+
+struct sun4i_csi_format {
+ u32 mbus;
+ u32 fourcc;
+ enum csi_input input;
+ u32 output;
+ unsigned int num_planes;
+ u8 bpp[3];
+ unsigned int hsub;
+ unsigned int vsub;
+};
+
+const struct sun4i_csi_format *sun4i_csi_find_format(const u32 *fourcc,
+ const u32 *mbus);
+
+struct sun4i_csi {
+ /* Device resources */
+ struct device *dev;
+
+ void __iomem *regs;
+ struct clk *bus_clk;
+ struct clk *isp_clk;
+ struct clk *ram_clk;
+ struct reset_control *rst;
+
+ struct vb2_v4l2_buffer *current_buf[CSI_MAX_BUFFER];
+
+ struct {
+ size_t size;
+ void *vaddr;
+ dma_addr_t paddr;
+ } scratch;
+
+ struct v4l2_fwnode_bus_parallel bus;
+
+ /* Main Device */
+ struct v4l2_device v4l;
+ struct media_device mdev;
+ struct video_device vdev;
+ struct media_pad vdev_pad;
+ struct v4l2_pix_format_mplane fmt;
+
+ /* Local subdev */
+ struct v4l2_subdev subdev;
+ struct media_pad subdev_pads[CSI_SUBDEV_PADS];
+ struct v4l2_mbus_framefmt subdev_fmt;
+
+ /* V4L2 Async variables */
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *src_subdev;
+ int src_pad;
+
+ /* V4L2 variables */
+ struct mutex lock;
+
+ /* Videobuf2 */
+ struct vb2_queue queue;
+ struct list_head buf_list;
+ spinlock_t qlock;
+ unsigned int sequence;
+};
+
+int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq);
+void sun4i_csi_dma_unregister(struct sun4i_csi *csi);
+
+int sun4i_csi_v4l2_register(struct sun4i_csi *csi);
+
+#endif /* _SUN4I_CSI_H_ */
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
new file mode 100644
index 000000000000..06cea6d2ea87
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 NextThing Co
+ * Copyright (C) 2016-2019 Bootlin
+ *
+ * Author: Maxime Ripard <maxime.ripard@bootlin.com>
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "sun4i_csi.h"
+
+struct sun4i_csi_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+static inline struct sun4i_csi_buffer *
+vb2_v4l2_to_csi_buffer(const struct vb2_v4l2_buffer *p)
+{
+ return container_of(p, struct sun4i_csi_buffer, vb);
+}
+
+static inline struct sun4i_csi_buffer *
+vb2_to_csi_buffer(const struct vb2_buffer *p)
+{
+ return vb2_v4l2_to_csi_buffer(to_vb2_v4l2_buffer(p));
+}
+
+static void sun4i_csi_capture_start(struct sun4i_csi *csi)
+{
+ writel(CSI_CPT_CTRL_VIDEO_START, csi->regs + CSI_CPT_CTRL_REG);
+}
+
+static void sun4i_csi_capture_stop(struct sun4i_csi *csi)
+{
+ writel(0, csi->regs + CSI_CPT_CTRL_REG);
+}
+
+static int sun4i_csi_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct sun4i_csi *csi = vb2_get_drv_priv(vq);
+ unsigned int num_planes = csi->fmt.num_planes;
+ unsigned int i;
+
+ if (*nplanes) {
+ if (*nplanes != num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < num_planes; i++)
+ if (sizes[i] < csi->fmt.plane_fmt[i].sizeimage)
+ return -EINVAL;
+ return 0;
+ }
+
+ *nplanes = num_planes;
+ for (i = 0; i < num_planes; i++)
+ sizes[i] = csi->fmt.plane_fmt[i].sizeimage;
+
+ return 0;
+};
+
+static int sun4i_csi_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct sun4i_csi *csi = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned int i;
+
+ for (i = 0; i < csi->fmt.num_planes; i++) {
+ unsigned long size = csi->fmt.plane_fmt[i].sizeimage;
+
+ if (vb2_plane_size(vb, i) < size) {
+ dev_err(csi->dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static int sun4i_csi_setup_scratch_buffer(struct sun4i_csi *csi, unsigned int slot)
+{
+ dma_addr_t addr = csi->scratch.paddr;
+ unsigned int plane;
+
+ dev_dbg(csi->dev,
+ "No more available buffer, using the scratch buffer\n");
+
+ for (plane = 0; plane < csi->fmt.num_planes; plane++) {
+ writel(addr, csi->regs + CSI_BUF_ADDR_REG(plane, slot));
+ addr += csi->fmt.plane_fmt[plane].sizeimage;
+ }
+
+ csi->current_buf[slot] = NULL;
+ return 0;
+}
+
+static int sun4i_csi_buffer_fill_slot(struct sun4i_csi *csi, unsigned int slot)
+{
+ struct sun4i_csi_buffer *c_buf;
+ struct vb2_v4l2_buffer *v_buf;
+ unsigned int plane;
+
+ /*
+ * We should never end up in a situation where we overwrite an
+ * already filled slot.
+ */
+ if (WARN_ON(csi->current_buf[slot]))
+ return -EINVAL;
+
+ if (list_empty(&csi->buf_list))
+ return sun4i_csi_setup_scratch_buffer(csi, slot);
+
+ c_buf = list_first_entry(&csi->buf_list, struct sun4i_csi_buffer, list);
+ list_del_init(&c_buf->list);
+
+ v_buf = &c_buf->vb;
+ csi->current_buf[slot] = v_buf;
+
+ for (plane = 0; plane < csi->fmt.num_planes; plane++) {
+ dma_addr_t buf_addr;
+
+ buf_addr = vb2_dma_contig_plane_dma_addr(&v_buf->vb2_buf,
+ plane);
+ writel(buf_addr, csi->regs + CSI_BUF_ADDR_REG(plane, slot));
+ }
+
+ return 0;
+}
+
+static int sun4i_csi_buffer_fill_all(struct sun4i_csi *csi)
+{
+ unsigned int slot;
+ int ret;
+
+ for (slot = 0; slot < CSI_MAX_BUFFER; slot++) {
+ ret = sun4i_csi_buffer_fill_slot(csi, slot);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void sun4i_csi_buffer_mark_done(struct sun4i_csi *csi,
+ unsigned int slot,
+ unsigned int sequence)
+{
+ struct vb2_v4l2_buffer *v_buf;
+
+ if (!csi->current_buf[slot]) {
+ dev_dbg(csi->dev, "Scratch buffer was used, ignoring..\n");
+ return;
+ }
+
+ v_buf = csi->current_buf[slot];
+ v_buf->field = csi->fmt.field;
+ v_buf->sequence = sequence;
+ v_buf->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&v_buf->vb2_buf, VB2_BUF_STATE_DONE);
+
+ csi->current_buf[slot] = NULL;
+}
+
+static int sun4i_csi_buffer_flip(struct sun4i_csi *csi, unsigned int sequence)
+{
+ u32 reg = readl(csi->regs + CSI_BUF_CTRL_REG);
+ unsigned int curr, next;
+
+ /* Our next buffer is not the current buffer */
+ curr = !!(reg & CSI_BUF_CTRL_DBS);
+ next = !curr;
+
+ /* Report the previous buffer as done */
+ sun4i_csi_buffer_mark_done(csi, next, sequence);
+
+ /* Put a new buffer in there */
+ return sun4i_csi_buffer_fill_slot(csi, next);
+}
+
+static void sun4i_csi_buffer_queue(struct vb2_buffer *vb)
+{
+ struct sun4i_csi *csi = vb2_get_drv_priv(vb->vb2_queue);
+ struct sun4i_csi_buffer *buf = vb2_to_csi_buffer(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&csi->qlock, flags);
+ list_add_tail(&buf->list, &csi->buf_list);
+ spin_unlock_irqrestore(&csi->qlock, flags);
+}
+
+static void return_all_buffers(struct sun4i_csi *csi,
+ enum vb2_buffer_state state)
+{
+ struct sun4i_csi_buffer *buf, *node;
+ unsigned int slot;
+
+ list_for_each_entry_safe(buf, node, &csi->buf_list, list) {
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ list_del(&buf->list);
+ }
+
+ for (slot = 0; slot < CSI_MAX_BUFFER; slot++) {
+ struct vb2_v4l2_buffer *v_buf = csi->current_buf[slot];
+
+ if (!v_buf)
+ continue;
+
+ vb2_buffer_done(&v_buf->vb2_buf, state);
+ csi->current_buf[slot] = NULL;
+ }
+}
+
+static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct sun4i_csi *csi = vb2_get_drv_priv(vq);
+ struct v4l2_fwnode_bus_parallel *bus = &csi->bus;
+ const struct sun4i_csi_format *csi_fmt;
+ unsigned long hsync_pol, pclk_pol, vsync_pol;
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+
+ csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL);
+ if (!csi_fmt)
+ return -EINVAL;
+
+ dev_dbg(csi->dev, "Starting capture\n");
+
+ csi->sequence = 0;
+
+ /*
+ * We need a scratch buffer in case where we'll not have any
+ * more buffer queued so that we don't error out. One of those
+ * cases is when you end up at the last frame to capture, you
+ * don't havea any buffer queued any more, and yet it doesn't
+ * really matter since you'll never reach the next buffer.
+ *
+ * Since we support the multi-planar API, we need to have a
+ * buffer for each plane. Allocating a single one large enough
+ * to hold all the buffers is simpler, so let's go for that.
+ */
+ csi->scratch.size = 0;
+ for (i = 0; i < csi->fmt.num_planes; i++)
+ csi->scratch.size += csi->fmt.plane_fmt[i].sizeimage;
+
+ csi->scratch.vaddr = dma_alloc_coherent(csi->dev,
+ csi->scratch.size,
+ &csi->scratch.paddr,
+ GFP_KERNEL);
+ if (!csi->scratch.vaddr) {
+ dev_err(csi->dev, "Failed to allocate scratch buffer\n");
+ ret = -ENOMEM;
+ goto err_clear_dma_queue;
+ }
+
+ ret = media_pipeline_start(&csi->vdev.entity, &csi->vdev.pipe);
+ if (ret < 0)
+ goto err_free_scratch_buffer;
+
+ spin_lock_irqsave(&csi->qlock, flags);
+
+ /* Setup timings */
+ writel(CSI_WIN_CTRL_W_ACTIVE(csi->fmt.width * 2),
+ csi->regs + CSI_WIN_CTRL_W_REG);
+ writel(CSI_WIN_CTRL_H_ACTIVE(csi->fmt.height),
+ csi->regs + CSI_WIN_CTRL_H_REG);
+
+ hsync_pol = !!(bus->flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH);
+ pclk_pol = !!(bus->flags & V4L2_MBUS_DATA_ACTIVE_HIGH);
+ vsync_pol = !!(bus->flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH);
+ writel(CSI_CFG_INPUT_FMT(csi_fmt->input) |
+ CSI_CFG_OUTPUT_FMT(csi_fmt->output) |
+ CSI_CFG_VSYNC_POL(vsync_pol) |
+ CSI_CFG_HSYNC_POL(hsync_pol) |
+ CSI_CFG_PCLK_POL(pclk_pol),
+ csi->regs + CSI_CFG_REG);
+
+ /* Setup buffer length */
+ writel(csi->fmt.plane_fmt[0].bytesperline,
+ csi->regs + CSI_BUF_LEN_REG);
+
+ /* Prepare our buffers in hardware */
+ ret = sun4i_csi_buffer_fill_all(csi);
+ if (ret) {
+ spin_unlock_irqrestore(&csi->qlock, flags);
+ goto err_disable_pipeline;
+ }
+
+ /* Enable double buffering */
+ writel(CSI_BUF_CTRL_DBE, csi->regs + CSI_BUF_CTRL_REG);
+
+ /* Clear the pending interrupts */
+ writel(CSI_INT_FRM_DONE, csi->regs + 0x34);
+
+ /* Enable frame done interrupt */
+ writel(CSI_INT_FRM_DONE, csi->regs + CSI_INT_EN_REG);
+
+ sun4i_csi_capture_start(csi);
+
+ spin_unlock_irqrestore(&csi->qlock, flags);
+
+ ret = v4l2_subdev_call(csi->src_subdev, video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto err_disable_device;
+
+ return 0;
+
+err_disable_device:
+ sun4i_csi_capture_stop(csi);
+
+err_disable_pipeline:
+ media_pipeline_stop(&csi->vdev.entity);
+
+err_free_scratch_buffer:
+ dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr,
+ csi->scratch.paddr);
+
+err_clear_dma_queue:
+ spin_lock_irqsave(&csi->qlock, flags);
+ return_all_buffers(csi, VB2_BUF_STATE_QUEUED);
+ spin_unlock_irqrestore(&csi->qlock, flags);
+
+ return ret;
+}
+
+static void sun4i_csi_stop_streaming(struct vb2_queue *vq)
+{
+ struct sun4i_csi *csi = vb2_get_drv_priv(vq);
+ unsigned long flags;
+
+ dev_dbg(csi->dev, "Stopping capture\n");
+
+ v4l2_subdev_call(csi->src_subdev, video, s_stream, 0);
+ sun4i_csi_capture_stop(csi);
+
+ /* Release all active buffers */
+ spin_lock_irqsave(&csi->qlock, flags);
+ return_all_buffers(csi, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&csi->qlock, flags);
+
+ media_pipeline_stop(&csi->vdev.entity);
+
+ dma_free_coherent(csi->dev, csi->scratch.size, csi->scratch.vaddr,
+ csi->scratch.paddr);
+}
+
+static const struct vb2_ops sun4i_csi_qops = {
+ .queue_setup = sun4i_csi_queue_setup,
+ .buf_prepare = sun4i_csi_buffer_prepare,
+ .buf_queue = sun4i_csi_buffer_queue,
+ .start_streaming = sun4i_csi_start_streaming,
+ .stop_streaming = sun4i_csi_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static irqreturn_t sun4i_csi_irq(int irq, void *data)
+{
+ struct sun4i_csi *csi = data;
+ u32 reg;
+
+ reg = readl(csi->regs + CSI_INT_STA_REG);
+
+ /* Acknowledge the interrupts */
+ writel(reg, csi->regs + CSI_INT_STA_REG);
+
+ if (!(reg & CSI_INT_FRM_DONE))
+ goto out;
+
+ spin_lock(&csi->qlock);
+ if (sun4i_csi_buffer_flip(csi, csi->sequence++)) {
+ dev_warn(csi->dev, "%s: Flip failed\n", __func__);
+ sun4i_csi_capture_stop(csi);
+ }
+ spin_unlock(&csi->qlock);
+
+out:
+ return IRQ_HANDLED;
+}
+
+int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq)
+{
+ struct vb2_queue *q = &csi->queue;
+ int ret;
+ int i;
+
+ ret = v4l2_device_register(csi->dev, &csi->v4l);
+ if (ret) {
+ dev_err(csi->dev, "Couldn't register the v4l2 device\n");
+ return ret;
+ }
+
+ spin_lock_init(&csi->qlock);
+ mutex_init(&csi->lock);
+
+ INIT_LIST_HEAD(&csi->buf_list);
+ for (i = 0; i < CSI_MAX_BUFFER; i++)
+ csi->current_buf[i] = NULL;
+
+ q->min_buffers_needed = 3;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP;
+ q->lock = &csi->lock;
+ q->drv_priv = csi;
+ q->buf_struct_size = sizeof(struct sun4i_csi_buffer);
+ q->ops = &sun4i_csi_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->dev = csi->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ dev_err(csi->dev, "failed to initialize VB2 queue\n");
+ return ret;
+ }
+
+ ret = devm_request_irq(csi->dev, irq, sun4i_csi_irq, 0,
+ dev_name(csi->dev), csi);
+ if (ret) {
+ dev_err(csi->dev, "Couldn't register our interrupt\n");
+ vb2_queue_release(q);
+ return ret;
+ }
+
+ return 0;
+}
+
+void sun4i_csi_dma_unregister(struct sun4i_csi *csi)
+{
+ v4l2_device_unregister(&csi->v4l);
+}
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
new file mode 100644
index 000000000000..7e5369fab8a2
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 NextThing Co
+ * Copyright (C) 2016-2019 Bootlin
+ *
+ * Author: Maxime Ripard <maxime.ripard@bootlin.com>
+ */
+
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "sun4i_csi.h"
+
+#define CSI_DEFAULT_WIDTH 640
+#define CSI_DEFAULT_HEIGHT 480
+
+const struct sun4i_csi_format sun4i_csi_formats[] = {
+ /* YUV422 inputs */
+ {
+ .mbus = MEDIA_BUS_FMT_YUYV8_2X8,
+ .fourcc = V4L2_PIX_FMT_YUV420M,
+ .input = CSI_INPUT_YUV,
+ .output = CSI_OUTPUT_YUV_420_PLANAR,
+ .num_planes = 3,
+ .bpp = { 8, 8, 8 },
+ .hsub = 2,
+ .vsub = 2,
+ },
+};
+
+const struct sun4i_csi_format *sun4i_csi_find_format(const u32 *fourcc,
+ const u32 *mbus)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sun4i_csi_formats); i++) {
+ if (fourcc && *fourcc != sun4i_csi_formats[i].fourcc)
+ continue;
+
+ if (mbus && *mbus != sun4i_csi_formats[i].mbus)
+ continue;
+
+ return &sun4i_csi_formats[i];
+ }
+
+ return NULL;
+}
+
+static int sun4i_csi_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct sun4i_csi *csi = video_drvdata(file);
+
+ strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strscpy(cap->card, "sun4i-csi", sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(csi->dev));
+
+ return 0;
+}
+
+static int sun4i_csi_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index != 0)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ strscpy(inp->name, "Camera", sizeof(inp->name));
+
+ return 0;
+}
+
+static int sun4i_csi_g_input(struct file *file, void *fh,
+ unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int sun4i_csi_s_input(struct file *file, void *fh,
+ unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void _sun4i_csi_try_fmt(struct sun4i_csi *csi,
+ struct v4l2_pix_format_mplane *pix)
+{
+ const struct sun4i_csi_format *_fmt;
+ unsigned int height, width;
+ unsigned int i;
+
+ _fmt = sun4i_csi_find_format(&pix->pixelformat, NULL);
+ if (!_fmt)
+ _fmt = &sun4i_csi_formats[0];
+
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
+ pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
+ pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace,
+ pix->ycbcr_enc);
+
+ pix->num_planes = _fmt->num_planes;
+ pix->pixelformat = _fmt->fourcc;
+
+ memset(pix->reserved, 0, sizeof(pix->reserved));
+
+ /* Align the width and height on the subsampling */
+ width = ALIGN(pix->width, _fmt->hsub);
+ height = ALIGN(pix->height, _fmt->vsub);
+
+ /* Clamp the width and height to our capabilities */
+ pix->width = clamp(width, _fmt->hsub, CSI_MAX_WIDTH);
+ pix->height = clamp(height, _fmt->vsub, CSI_MAX_HEIGHT);
+
+ for (i = 0; i < _fmt->num_planes; i++) {
+ unsigned int hsub = i > 0 ? _fmt->hsub : 1;
+ unsigned int vsub = i > 0 ? _fmt->vsub : 1;
+ unsigned int bpl;
+
+ bpl = pix->width / hsub * _fmt->bpp[i] / 8;
+ pix->plane_fmt[i].bytesperline = bpl;
+ pix->plane_fmt[i].sizeimage = bpl * pix->height / vsub;
+ memset(pix->plane_fmt[i].reserved, 0,
+ sizeof(pix->plane_fmt[i].reserved));
+ }
+}
+
+static int sun4i_csi_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct sun4i_csi *csi = video_drvdata(file);
+
+ _sun4i_csi_try_fmt(csi, &f->fmt.pix_mp);
+ return 0;
+}
+
+static int sun4i_csi_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct sun4i_csi *csi = video_drvdata(file);
+
+ _sun4i_csi_try_fmt(csi, &f->fmt.pix_mp);
+ csi->fmt = f->fmt.pix_mp;
+
+ return 0;
+}
+
+static int sun4i_csi_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct sun4i_csi *csi = video_drvdata(file);
+
+ f->fmt.pix_mp = csi->fmt;
+
+ return 0;
+}
+
+static int sun4i_csi_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(sun4i_csi_formats))
+ return -EINVAL;
+
+ f->pixelformat = sun4i_csi_formats[f->index].fourcc;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops sun4i_csi_ioctl_ops = {
+ .vidioc_querycap = sun4i_csi_querycap,
+
+ .vidioc_enum_fmt_vid_cap = sun4i_csi_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap_mplane = sun4i_csi_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap_mplane = sun4i_csi_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap_mplane = sun4i_csi_try_fmt_vid_cap,
+
+ .vidioc_enum_input = sun4i_csi_enum_input,
+ .vidioc_g_input = sun4i_csi_g_input,
+ .vidioc_s_input = sun4i_csi_s_input,
+
+ .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,
+};
+
+static int sun4i_csi_open(struct file *file)
+{
+ struct sun4i_csi *csi = video_drvdata(file);
+ int ret;
+
+ ret = mutex_lock_interruptible(&csi->lock);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(csi->dev);
+ if (ret < 0)
+ goto err_pm_put;
+
+ ret = v4l2_pipeline_pm_use(&csi->vdev.entity, 1);
+ if (ret)
+ goto err_pm_put;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ goto err_pipeline_pm_put;
+
+ mutex_unlock(&csi->lock);
+
+ return 0;
+
+err_pipeline_pm_put:
+ v4l2_pipeline_pm_use(&csi->vdev.entity, 0);
+
+err_pm_put:
+ pm_runtime_put(csi->dev);
+ mutex_unlock(&csi->lock);
+
+ return ret;
+}
+
+static int sun4i_csi_release(struct file *file)
+{
+ struct sun4i_csi *csi = video_drvdata(file);
+
+ mutex_lock(&csi->lock);
+
+ v4l2_fh_release(file);
+ v4l2_pipeline_pm_use(&csi->vdev.entity, 0);
+ pm_runtime_put(csi->dev);
+
+ mutex_unlock(&csi->lock);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations sun4i_csi_fops = {
+ .owner = THIS_MODULE,
+ .open = sun4i_csi_open,
+ .release = sun4i_csi_release,
+ .unlocked_ioctl = video_ioctl2,
+ .read = vb2_fop_read,
+ .write = vb2_fop_write,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static const struct v4l2_mbus_framefmt sun4i_csi_pad_fmt_default = {
+ .width = CSI_DEFAULT_WIDTH,
+ .height = CSI_DEFAULT_HEIGHT,
+ .code = sun4i_csi_formats[0].mbus,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+};
+
+static int sun4i_csi_subdev_init_cfg(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(subdev, cfg, CSI_SUBDEV_SINK);
+ *fmt = sun4i_csi_pad_fmt_default;
+
+ return 0;
+}
+
+static int sun4i_csi_subdev_get_fmt(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct sun4i_csi *csi = container_of(subdev, struct sun4i_csi, subdev);
+ struct v4l2_mbus_framefmt *subdev_fmt;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ subdev_fmt = v4l2_subdev_get_try_format(subdev, cfg, fmt->pad);
+ else
+ subdev_fmt = &csi->subdev_fmt;
+
+ fmt->format = *subdev_fmt;
+
+ return 0;
+}
+
+static int sun4i_csi_subdev_set_fmt(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct sun4i_csi *csi = container_of(subdev, struct sun4i_csi, subdev);
+ struct v4l2_mbus_framefmt *subdev_fmt;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ subdev_fmt = v4l2_subdev_get_try_format(subdev, cfg, fmt->pad);
+ else
+ subdev_fmt = &csi->subdev_fmt;
+
+ /* We can only set the format on the sink pad */
+ if (fmt->pad == CSI_SUBDEV_SINK) {
+ /* It's the sink, only allow changing the frame size */
+ subdev_fmt->width = fmt->format.width;
+ subdev_fmt->height = fmt->format.height;
+ subdev_fmt->code = fmt->format.code;
+ }
+
+ fmt->format = *subdev_fmt;
+
+ return 0;
+}
+
+static int sun4i_csi_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *mbus)
+{
+ if (mbus->index >= ARRAY_SIZE(sun4i_csi_formats))
+ return -EINVAL;
+
+ mbus->code = sun4i_csi_formats[mbus->index].mbus;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops sun4i_csi_subdev_pad_ops = {
+ .link_validate = v4l2_subdev_link_validate_default,
+ .init_cfg = sun4i_csi_subdev_init_cfg,
+ .get_fmt = sun4i_csi_subdev_get_fmt,
+ .set_fmt = sun4i_csi_subdev_set_fmt,
+ .enum_mbus_code = sun4i_csi_subdev_enum_mbus_code,
+};
+
+const struct v4l2_subdev_ops sun4i_csi_subdev_ops = {
+ .pad = &sun4i_csi_subdev_pad_ops,
+};
+
+int sun4i_csi_v4l2_register(struct sun4i_csi *csi)
+{
+ struct video_device *vdev = &csi->vdev;
+ int ret;
+
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING;
+ vdev->v4l2_dev = &csi->v4l;
+ vdev->queue = &csi->queue;
+ strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
+ vdev->release = video_device_release_empty;
+ vdev->lock = &csi->lock;
+
+ /* Set a default format */
+ csi->fmt.pixelformat = sun4i_csi_formats[0].fourcc,
+ csi->fmt.width = CSI_DEFAULT_WIDTH;
+ csi->fmt.height = CSI_DEFAULT_HEIGHT;
+ _sun4i_csi_try_fmt(csi, &csi->fmt);
+ csi->subdev_fmt = sun4i_csi_pad_fmt_default;
+
+ vdev->fops = &sun4i_csi_fops;
+ vdev->ioctl_ops = &sun4i_csi_ioctl_ops;
+ video_set_drvdata(vdev, csi);
+
+ ret = video_register_device(&csi->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ return ret;
+
+ dev_info(csi->dev, "Device registered as %s\n",
+ video_device_node_name(vdev));
+
+ return 0;
+}
--
git-series 0.9.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 1/5] dt-bindings: media: Add Allwinner A10 CSI binding
From: Maxime Ripard @ 2019-08-20 11:24 UTC (permalink / raw)
To: Hans Verkuil, Sakari Ailus, Mauro Carvalho Chehab
Cc: Mark Rutland, devicetree, Rob Herring, Maxime Ripard,
linux-kernel, Chen-Yu Tsai, Rob Herring, Laurent Pinchart,
Thomas Petazzoni, Frank Rowand, linux-arm-kernel, linux-media
In-Reply-To: <cover.b695c63cf668192aff5574a3005d483c601e77f6.1566300265.git-series.maxime.ripard@bootlin.com>
From: Maxime Ripard <maxime.ripard@bootlin.com>
The Allwinner A10 CMOS Sensor Interface is a camera capture interface also
used in later (A10s, A13, A20, R8 and GR8) SoCs.
On some SoCs, like the A10, there's multiple instances of that controller,
with one instance supporting more channels and having an ISP.
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 107 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml
diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml
new file mode 100644
index 000000000000..9000bca344f9
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/allwinner,sun4i-a10-csi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A10 CMOS Sensor Interface (CSI) Device Tree Bindings
+
+maintainers:
+ - Chen-Yu Tsai <wens@csie.org>
+ - Maxime Ripard <maxime.ripard@bootlin.com>
+
+description: |-
+ The Allwinner A10 and later has a CMOS Sensor Interface to retrieve
+ frames from a parallel or BT656 sensor.
+
+properties:
+ compatible:
+ const: allwinner,sun7i-a20-csi0
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: The CSI interface clock
+ - description: The CSI module clock
+ - description: The CSI ISP clock
+ - description: The CSI DRAM clock
+
+ clock-names:
+ items:
+ - const: bus
+ - const: mod
+ - const: isp
+ - const: ram
+
+ resets:
+ maxItems: 1
+
+ port:
+ type: object
+ additionalProperties: false
+
+ properties:
+ endpoint:
+ properties:
+ bus-width:
+ const: 8
+ description: Number of data lines actively used.
+
+ data-active: true
+ hsync-active: true
+ pclk-sample: true
+ remote-endpoint: true
+ vsync-active: true
+
+ required:
+ - bus-width
+ - data-active
+ - hsync-active
+ - pclk-sample
+ - remote-endpoint
+ - vsync-active
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/sun7i-a20-ccu.h>
+ #include <dt-bindings/reset/sun4i-a10-ccu.h>
+
+ csi0: csi@1c09000 {
+ compatible = "allwinner,sun7i-a20-csi0";
+ reg = <0x01c09000 0x1000>;
+ interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_AHB_CSI0>, <&ccu CLK_CSI0>,
+ <&ccu CLK_CSI_SCLK>, <&ccu CLK_DRAM_CSI0>;
+ clock-names = "bus", "mod", "isp", "ram";
+ resets = <&ccu RST_CSI0>;
+
+ port {
+ csi_from_ov5640: endpoint {
+ remote-endpoint = <&ov5640_to_csi>;
+ bus-width = <8>;
+ hsync-active = <1>; /* Active high */
+ vsync-active = <0>; /* Active low */
+ data-active = <1>; /* Active high */
+ pclk-sample = <1>; /* Rising */
+ };
+ };
+ };
+
+...
--
git-series 0.9.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v7 0/5] media: Allwinner A10 CSI support
From: Maxime Ripard @ 2019-08-20 11:24 UTC (permalink / raw)
To: Hans Verkuil, Sakari Ailus, Mauro Carvalho Chehab
Cc: Mark Rutland, devicetree, Maxime Ripard, linux-kernel,
Chen-Yu Tsai, Rob Herring, Laurent Pinchart, Thomas Petazzoni,
Frank Rowand, linux-arm-kernel, linux-media
From: Maxime Ripard <maxime.ripard@bootlin.com>
Hi,
Here is a series introducing the support for the A10 (and SoCs of the same
generation) CMOS Sensor Interface (called CSI, not to be confused with
MIPI-CSI, which isn't support by that IP).
That interface is pretty straightforward, but the driver has a few issues
that I wanted to bring up:
* The only board I've been testing this with has an ov5640 sensor
attached, which doesn't work with the upstream driver. Copying the
Allwinner init sequence works though, and this is how it has been
tested. Testing with a second sensor would allow to see if it's an
issue on the CSI side or the sensor side.
* We don't have support for the ISP at the moment, but this can be added
eventually.
Here is the v4l2-compliance output (commit f61132e81d79 of v4l-utils)
v4l2-compliance SHA: not available, 32 bits
Compliance test for device /dev/video1:
Driver Info:
Driver name : sun4i_csi
Card type : sun4i-csi
Bus info : platform:1c09000.csi
Driver version : 5.3.0
Capabilities : 0x84201000
Video Capture Multiplanar
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04201000
Video Capture Multiplanar
Streaming
Extended Pix Format
Media Driver Info:
Driver name : sun4i-csi
Model : Allwinner Video Capture Device
Serial :
Bus info :
Media version : 5.3.0
Hardware revision: 0x00000000 (0)
Driver version : 5.3.0
Interface Info:
ID : 0x03000008
Type : V4L Video
Entity Info:
ID : 0x00000006 (6)
Name : sun4i_csi
Function : V4L2 I/O
Pad 0x01000007 : 0: Sink, Must Connect
Link 0x0200000a: from remote pad 0x1000005 of entity 'sun4i-csi-0': Data, Enabled, Immutable
Required ioctls:
test MC information (see 'Media Driver Info' above): OK
test VIDIOC_QUERYCAP: OK
Allow for multiple opens:
test second /dev/video1 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 (Not Supported)
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)
Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls (Input 0):
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 (Input 0):
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 (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
Test input 0:
Streaming ioctls:
test read/write: OK (Not Supported)
test blocking wait: OK
test MMAP: OK
test USERPTR: OK (Not Supported)
test DMABUF: OK (Not Supported)
Total: 49, Succeeded: 49, Failed: 0, Warnings: 0
media-ctl -p -d /dev/media1 output after boot:
Media controller API version 5.3.0
Media device information
------------------------
driver sun4i-csi
model Allwinner Video Capture Device
serial
bus info
hw revision 0x0
driver version 5.3.0
Device topology
- entity 1: ov5640 1-0021 (1 pad, 1 link)
type V4L2 subdev subtype Sensor flags 0
device node name /dev/v4l-subdev0
pad0: Source
[fmt:YUYV8_2X8/640x480@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601 quantization:full-range]
-> "sun4i-csi-0":0 [ENABLED,IMMUTABLE]
- entity 3: sun4i-csi-0 (2 pads, 2 links)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev1
pad0: Sink
[fmt:YUYV8_2X8/640x480 field:none colorspace:raw]
<- "ov5640 1-0021":0 [ENABLED,IMMUTABLE]
pad1: Source
[fmt:YUYV8_2X8/640x480 field:none colorspace:raw]
-> "sun4i_csi":0 [ENABLED,IMMUTABLE]
- entity 6: sun4i_csi (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video1
pad0: Sink
<- "sun4i-csi-0":1 [ENABLED,IMMUTABLE]
Let me know what you think,
Maxime
Changes from v6:
- Add init_cfg callback on the pads
- Use v4l2_subdev_link_validate instead of hand-rolled link validate
- Make sun4i_csi_qops const
- Add MODULE_DESCRIPTION, MODULE_AUTHOR and MODULE_LICENSE
- Remove the mod clock handling from the CSI driver
- Remove the A10 compatible fallback
- Rework the CSI pinctrl groups
- Add an example to the binding
Changes from v5:
- Add link_validate/get_fmt/set_fmt/enum_mbus_code to the subdevice
- Create a device file for the subdevice
- Add link_validate to the video device
- Remove the storage of both the v4l2_pix_format_mplane structure and the
sun4i_csi_format structure, since the latter can be retrieved easily
from the former, and this is actually needed in a single place.
- Fix the copyright year notice
Changes from v4:
- Created an intermediate sub-device
Changes from v3:
- Rebased on v5.1-rc
- Fixed the YAML binding according to Rob's review
Changes from v2:
- Address a few minors comments on the error path, the return type of
some functions, the type of some variables
- Disable the device if the subdev call fails in start_streaming
- Use __maybe_unused and SET_RUNTIME_PM_OPS for the runtime PM hooks
- Call media_device_cleanup in the remove function
- Add a dependency on the subdev API and the common clock framework
- Fix the MAINTAINERS entry to point to the yaml file
- Add the of graph bindings to the YAML schemas
- Rebase on next
Changes from v1:
- Make sure it's compliant with a much newer v4l2-compliance
- Conversion of the DT bindings to a JSON schema
- Drop the vendor properties and use a separate compatible instead
- Fix an issue on the last frame where we would not have any buffer
queued and would report an error by using a scratch buffer
- Fix the warnings reported by v4l2-compliance
- Rebase on top of 5.0-rc1
- Added a MAINTAINERS entry
- Switched to strscpy
- Fixed SPDX header
Maxime Ripard (5):
dt-bindings: media: Add Allwinner A10 CSI binding
media: sunxi: Refactor the Makefile and Kconfig
media: sunxi: Add A10 CSI driver
ARM: dts: sun7i: Add CSI0 controller
DO NOT MERGE: ARM: dts: bananapi: Add Camera support
Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml | 107 +++++++++++++++++-
MAINTAINERS | 8 +-
arch/arm/boot/dts/sun7i-a20-bananapi.dts | 87 ++++++++++++++-
arch/arm/boot/dts/sun7i-a20.dtsi | 25 ++++-
drivers/media/platform/Kconfig | 2 +-
drivers/media/platform/Makefile | 2 +-
drivers/media/platform/sunxi/Kconfig | 2 +-
drivers/media/platform/sunxi/Makefile | 2 +-
drivers/media/platform/sunxi/sun4i-csi/Kconfig | 11 ++-
drivers/media/platform/sunxi/sun4i-csi/Makefile | 5 +-
drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 305 +++++++++++++++++++++++++++++++++++++++++++++++++-
drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h | 159 ++++++++++++++++++++++++++-
drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 444 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
14 files changed, 1540 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml
create mode 100644 drivers/media/platform/sunxi/Kconfig
create mode 100644 drivers/media/platform/sunxi/Makefile
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/Kconfig
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/Makefile
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
create mode 100644 drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
base-commit: 85b8819be27eab140d280bbee4f01385beb11e7d
--
git-series 0.9.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH RFC] smp: Add cpu unstopped mask for smp_send_stop/stop_other_cpus
From: Thomas Gleixner @ 2019-08-20 11:24 UTC (permalink / raw)
To: Hsin-Yi Wang
Cc: Kate Stewart, Peter Zijlstra, Catalin Marinas, Mukesh Ojha,
Grzegorz Halat, H . Peter Anvin ), Guenter Roeck, Will Deacon,
Marek Szyprowski, Rob Herring, Daniel Thompson, Anders Roxell,
Yury Norov, Marc Zyngier, Russell King, Aaro Koskinen,
Ingo Molnar, Viresh Kumar, Waiman Long, Paul E . McKenney, Wei Li,
Alexey Dobriyan, Julien Thierry, Len Brown, Kees Cook,
Arnd Bergmann, Rik van Riel, Stephen Boyd, Shaokun Zhang,
Mike Rapoport, Borislav Petkov, Josh Poimboeuf, linux-arm-kernel,
Greg Kroah-Hartman, Marcelo Tosatti, linux-kernel, Armijn Hemel,
Jiri Kosina, Mathieu Desnoyers, Andrew Morton, Tim Chen,
David S . Miller
In-Reply-To: <20190820100843.3028-1-hsinyi@chromium.org>
On Tue, 20 Aug 2019, Hsin-Yi Wang wrote:
> In arm/arm64/x86, reboot IPI function uses CPU online mask to let
> primary CPU know how many secondary CPUs it has to wait for in
> smp_send_stop()/native_stop_other_cpus().
>
> However, sometimes this would trigger unnecessary warnings, since
> interrupts and tasks might fall on a CPU that has already executed
> the reboot ipi function. This is fine since CPU is already in spinloop.
> But warnings are generated since it finds that the CPU is marked as
> offiline. The warnings are supposed to catch failures in normal hotplug
> offline CPUs, and reboot isn't a regular hotplug. So instead of reusing
> online masks, we should use a new mask in reboot IPI functions to do the
> work.
>
> Take tick broadcast for example. If broadcast and smp_send_stop()
> happen together, most of the time, the CPU getting earliest broadcast
> is already in spinloop and thus won't do anything. If the first
> broadcast arrives to CPU that hasn't already executed reboot ipi, it
> would try to IPI another CPU, but the CPU is already marked as offline,
> and warning comes out:
>
> [ 22.481523] reboot: Restarting system
> [ 22.481608] WARNING: CPU: 4 PID: 0 at ...
That is really the complete wrong approach. There is no valid reason that a
regular reboot needs to use a shortcut homebrewn variant of stopping CPUs.
The proper solution is to restrict this mechansim to emergency reboots and
let the normal reboot go through the regular CPU hotplug mechanism. That
avoids all that duct tape which is just bound to break tomorrow again.
In case of an emergency reboot, we really do not care about any extra stuff
triggered. The machine is hosed already.
Thanks
tglx
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v4 04/10] mailbox: sunxi-msgbox: Add a new mailbox driver
From: Ondřej Jirman @ 2019-08-20 11:18 UTC (permalink / raw)
To: Samuel Holland
Cc: Mark Rutland, devicetree, linux-sunxi, Maxime Ripard,
Michael Turquette, Jassi Brar, linux-kernel, Stephen Boyd,
Chen-Yu Tsai, Rob Herring, Corentin Labbe, linux-clk,
linux-arm-kernel
In-Reply-To: <20190820032311.6506-5-samuel@sholland.org>
Hi Samuel,
On Mon, Aug 19, 2019 at 10:23:05PM -0500, Samuel Holland wrote:
> Allwinner sun8i, sun9i, and sun50i SoCs contain a hardware message box
> used for communication between the ARM CPUs and the ARISC management
> coprocessor. The hardware contains 8 unidirectional 4-message FIFOs.
>
> Add a driver for it, so it can be used for SCPI or other communication
> protocols.
>
> Signed-off-by: Samuel Holland <samuel@sholland.org>
> ---
> drivers/mailbox/Kconfig | 10 +
> drivers/mailbox/Makefile | 2 +
> drivers/mailbox/sunxi-msgbox.c | 323 +++++++++++++++++++++++++++++++++
> 3 files changed, 335 insertions(+)
> create mode 100644 drivers/mailbox/sunxi-msgbox.c
>
> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
> index ab4eb750bbdd..57d12936175e 100644
> --- a/drivers/mailbox/Kconfig
> +++ b/drivers/mailbox/Kconfig
> @@ -227,4 +227,14 @@ config ZYNQMP_IPI_MBOX
> message to the IPI buffer and will access the IPI control
> registers to kick the other processor or enquire status.
>
> +config SUNXI_MSGBOX
> + tristate "Allwinner sunxi Message Box"
> + depends on ARCH_SUNXI || COMPILE_TEST
> + default ARCH_SUNXI
> + help
> + Mailbox implementation for the hardware message box present in
> + Allwinner sun8i, sun9i, and sun50i SoCs. The hardware message box is
> + used for communication between the application CPUs and the power
> + management coprocessor.
> +
> endif
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> index c22fad6f696b..bec2d50b0976 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -48,3 +48,5 @@ obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o
> obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
>
> obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
> +
> +obj-$(CONFIG_SUNXI_MSGBOX) += sunxi-msgbox.o
> diff --git a/drivers/mailbox/sunxi-msgbox.c b/drivers/mailbox/sunxi-msgbox.c
> new file mode 100644
> index 000000000000..29a5101a5390
> --- /dev/null
> +++ b/drivers/mailbox/sunxi-msgbox.c
> @@ -0,0 +1,323 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2017-2019 Samuel Holland <samuel@sholland.org>
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mailbox_controller.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/spinlock.h>
> +
> +#define NUM_CHANS 8
> +
> +#define CTRL_REG(n) (0x0000 + 0x4 * ((n) / 4))
> +#define CTRL_RX(n) BIT(0 + 8 * ((n) % 4))
> +#define CTRL_TX(n) BIT(4 + 8 * ((n) % 4))
> +
> +#define REMOTE_IRQ_EN_REG 0x0040
> +#define REMOTE_IRQ_STAT_REG 0x0050
> +#define LOCAL_IRQ_EN_REG 0x0060
> +#define LOCAL_IRQ_STAT_REG 0x0070
> +
> +#define RX_IRQ(n) BIT(0 + 2 * (n))
> +#define RX_IRQ_MASK 0x5555
> +#define TX_IRQ(n) BIT(1 + 2 * (n))
> +#define TX_IRQ_MASK 0xaaaa
> +
> +#define FIFO_STAT_REG(n) (0x0100 + 0x4 * (n))
> +#define FIFO_STAT_MASK GENMASK(0, 0)
> +
> +#define MSG_STAT_REG(n) (0x0140 + 0x4 * (n))
> +#define MSG_STAT_MASK GENMASK(2, 0)
> +
> +#define MSG_DATA_REG(n) (0x0180 + 0x4 * (n))
> +
> +#define mbox_dbg(mbox, ...) dev_dbg((mbox)->controller.dev, __VA_ARGS__)
> +
> +struct sunxi_msgbox {
> + struct mbox_controller controller;
> + struct clk *clk;
> + spinlock_t lock;
> + void __iomem *regs;
> +};
> +
> +static bool sunxi_msgbox_last_tx_done(struct mbox_chan *chan);
> +static bool sunxi_msgbox_peek_data(struct mbox_chan *chan);
> +
> +static inline int channel_number(struct mbox_chan *chan)
> +{
> + return chan - chan->mbox->chans;
> +}
> +
> +static inline struct sunxi_msgbox *channel_to_msgbox(struct mbox_chan *chan)
> +{
> + return chan->con_priv;
> +}
> +
> +static irqreturn_t sunxi_msgbox_irq(int irq, void *dev_id)
> +{
> + struct sunxi_msgbox *mbox = dev_id;
> + uint32_t status;
> + int n;
> +
> + /* Only examine channels that are currently enabled. */
> + status = readl(mbox->regs + LOCAL_IRQ_EN_REG) &
> + readl(mbox->regs + LOCAL_IRQ_STAT_REG);
> +
> + if (!(status & RX_IRQ_MASK))
> + return IRQ_NONE;
> +
> + for (n = 0; n < NUM_CHANS; ++n) {
> + struct mbox_chan *chan = &mbox->controller.chans[n];
> +
> + if (!(status & RX_IRQ(n)))
> + continue;
> +
> + while (sunxi_msgbox_peek_data(chan)) {
> + uint32_t msg = readl(mbox->regs + MSG_DATA_REG(n));
> +
> + mbox_dbg(mbox, "Channel %d received 0x%08x\n", n, msg);
> + mbox_chan_received_data(chan, &msg);
> + }
> +
> + /* The IRQ can be cleared only once the FIFO is empty. */
> + writel(RX_IRQ(n), mbox->regs + LOCAL_IRQ_STAT_REG);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int sunxi_msgbox_send_data(struct mbox_chan *chan, void *data)
> +{
> + struct sunxi_msgbox *mbox = channel_to_msgbox(chan);
> + int n = channel_number(chan);
> + uint32_t msg = *(uint32_t *)data;
> +
> + /* Using a channel backwards gets the hardware into a bad state. */
> + if (WARN_ON_ONCE(!(readl(mbox->regs + CTRL_REG(n)) & CTRL_TX(n))))
> + return 0;
> +
> + /* We cannot post a new message if the FIFO is full. */
> + if (readl(mbox->regs + FIFO_STAT_REG(n)) & FIFO_STAT_MASK) {
> + mbox_dbg(mbox, "Channel %d busy sending 0x%08x\n", n, msg);
> + return -EBUSY;
> + }
> +
> + writel(msg, mbox->regs + MSG_DATA_REG(n));
> + mbox_dbg(mbox, "Channel %d sent 0x%08x\n", n, msg);
> +
> + return 0;
> +}
> +
> +static int sunxi_msgbox_startup(struct mbox_chan *chan)
> +{
> + struct sunxi_msgbox *mbox = channel_to_msgbox(chan);
> + int n = channel_number(chan);
> +
> + /* The coprocessor is responsible for setting channel directions. */
> + if (readl(mbox->regs + CTRL_REG(n)) & CTRL_RX(n)) {
> + /* Flush the receive FIFO. */
> + while (sunxi_msgbox_peek_data(chan))
> + readl(mbox->regs + MSG_DATA_REG(n));
> + writel(RX_IRQ(n), mbox->regs + LOCAL_IRQ_STAT_REG);
> +
> + /* Enable the receive IRQ. */
> + spin_lock(&mbox->lock);
> + writel(readl(mbox->regs + LOCAL_IRQ_EN_REG) | RX_IRQ(n),
> + mbox->regs + LOCAL_IRQ_EN_REG);
> + spin_unlock(&mbox->lock);
> + }
> +
> + mbox_dbg(mbox, "Channel %d startup complete\n", n);
> +
> + return 0;
> +}
> +
> +static void sunxi_msgbox_shutdown(struct mbox_chan *chan)
> +{
> + struct sunxi_msgbox *mbox = channel_to_msgbox(chan);
> + int n = channel_number(chan);
> +
> + if (readl(mbox->regs + CTRL_REG(n)) & CTRL_RX(n)) {
> + /* Disable the receive IRQ. */
> + spin_lock(&mbox->lock);
> + writel(readl(mbox->regs + LOCAL_IRQ_EN_REG) & ~RX_IRQ(n),
> + mbox->regs + LOCAL_IRQ_EN_REG);
> + spin_unlock(&mbox->lock);
> +
> + /* Attempt to flush the FIFO until the IRQ is cleared. */
> + do {
> + while (sunxi_msgbox_peek_data(chan))
> + readl(mbox->regs + MSG_DATA_REG(n));
> + writel(RX_IRQ(n), mbox->regs + LOCAL_IRQ_STAT_REG);
> + } while (readl(mbox->regs + LOCAL_IRQ_STAT_REG) & RX_IRQ(n));
> + }
> +
> + mbox_dbg(mbox, "Channel %d shutdown complete\n", n);
> +}
> +
> +static bool sunxi_msgbox_last_tx_done(struct mbox_chan *chan)
> +{
> + struct sunxi_msgbox *mbox = channel_to_msgbox(chan);
> + int n = channel_number(chan);
> +
> + /*
> + * The hardware allows snooping on the remote user's IRQ statuses.
> + * We consider a message to be acknowledged only once the receive IRQ
> + * for that channel is cleared. Since the receive IRQ for a channel
> + * cannot be cleared until the FIFO for that channel is empty, this
> + * ensures that the message has actually been read. It also gives the
> + * recipient an opportunity to perform minimal processing before
> + * acknowledging the message.
> + */
> + return !(readl(mbox->regs + REMOTE_IRQ_STAT_REG) & RX_IRQ(n));
> +}
> +
> +static bool sunxi_msgbox_peek_data(struct mbox_chan *chan)
> +{
> + struct sunxi_msgbox *mbox = channel_to_msgbox(chan);
> + int n = channel_number(chan);
> +
> + return readl(mbox->regs + MSG_STAT_REG(n)) & MSG_STAT_MASK;
> +}
> +
> +static const struct mbox_chan_ops sunxi_msgbox_chan_ops = {
> + .send_data = sunxi_msgbox_send_data,
> + .startup = sunxi_msgbox_startup,
> + .shutdown = sunxi_msgbox_shutdown,
> + .last_tx_done = sunxi_msgbox_last_tx_done,
> + .peek_data = sunxi_msgbox_peek_data,
> +};
> +
> +static int sunxi_msgbox_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct mbox_chan *chans;
> + struct reset_control *reset;
> + struct resource *res;
> + struct sunxi_msgbox *mbox;
> + int i, ret;
> +
> + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
> + if (!mbox)
> + return -ENOMEM;
> +
> + chans = devm_kcalloc(dev, NUM_CHANS, sizeof(*chans), GFP_KERNEL);
> + if (!chans)
> + return -ENOMEM;
> +
> + for (i = 0; i < NUM_CHANS; ++i)
> + chans[i].con_priv = mbox;
> +
> + mbox->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(mbox->clk)) {
> + ret = PTR_ERR(mbox->clk);
> + dev_err(dev, "Failed to get clock: %d\n", ret);
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(mbox->clk);
> + if (ret) {
> + dev_err(dev, "Failed to enable clock: %d\n", ret);
> + return ret;
> + }
> +
> + reset = devm_reset_control_get(dev, NULL);
> + if (IS_ERR(reset)) {
> + ret = PTR_ERR(reset);
> + dev_err(dev, "Failed to get reset control: %d\n", ret);
> + goto err_disable_unprepare;
> + }
> +
> + ret = reset_control_deassert(reset);
> + if (ret) {
> + dev_err(dev, "Failed to deassert reset: %d\n", ret);
> + goto err_disable_unprepare;
> + }
You need to assert the reset again from now on, in error paths. devm
will not do that for you.
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + ret = -ENODEV;
> + goto err_disable_unprepare;
> + }
> +
> + mbox->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(mbox->regs)) {
> + ret = PTR_ERR(mbox->regs);
> + dev_err(dev, "Failed to map MMIO resource: %d\n", ret);
> + goto err_disable_unprepare;
> + }
> +
> + /* Disable all IRQs for this end of the msgbox. */
> + writel(0, mbox->regs + LOCAL_IRQ_EN_REG);
> +
> + ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
> + sunxi_msgbox_irq, 0, dev_name(dev), mbox);
> + if (ret) {
> + dev_err(dev, "Failed to register IRQ handler: %d\n", ret);
> + goto err_disable_unprepare;
> + }
> +
> + mbox->controller.dev = dev;
> + mbox->controller.ops = &sunxi_msgbox_chan_ops;
> + mbox->controller.chans = chans;
> + mbox->controller.num_chans = NUM_CHANS;
> + mbox->controller.txdone_irq = false;
> + mbox->controller.txdone_poll = true;
> + mbox->controller.txpoll_period = 5;
> +
> + spin_lock_init(&mbox->lock);
> + platform_set_drvdata(pdev, mbox);
> +
> + ret = mbox_controller_register(&mbox->controller);
> + if (ret) {
> + dev_err(dev, "Failed to register controller: %d\n", ret);
> + goto err_disable_unprepare;
> + }
> +
> + return 0;
> +
> +err_disable_unprepare:
> + clk_disable_unprepare(mbox->clk);
> +
> + return ret;
> +}
> +
> +static int sunxi_msgbox_remove(struct platform_device *pdev)
> +{
> + struct sunxi_msgbox *mbox = platform_get_drvdata(pdev);
> +
> + mbox_controller_unregister(&mbox->controller);
> + clk_disable_unprepare(mbox->clk);
Also, assert the reset here.
regards,
o.
> + return 0;
> +}
> +
> +static const struct of_device_id sunxi_msgbox_of_match[] = {
> + { .compatible = "allwinner,sun6i-a31-msgbox", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, sunxi_msgbox_of_match);
> +
> +static struct platform_driver sunxi_msgbox_driver = {
> + .driver = {
> + .name = "sunxi-msgbox",
> + .of_match_table = sunxi_msgbox_of_match,
> + },
> + .probe = sunxi_msgbox_probe,
> + .remove = sunxi_msgbox_remove,
> +};
> +module_platform_driver(sunxi_msgbox_driver);
> +
> +MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
> +MODULE_DESCRIPTION("Allwinner sunxi Message Box");
> +MODULE_LICENSE("GPL v2");
> --
> 2.21.0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v8 2/3] fdt: add support for rng-seed
From: Ard Biesheuvel @ 2019-08-20 11:14 UTC (permalink / raw)
To: Hsin-Yi Wang
Cc: Mark Rutland, Devicetree List, Theodore Y. Ts'o, Yu Zhao,
Kees Cook, Catalin Marinas, Stephen Boyd, Will Deacon, lkml,
Mike Rapoport, Jun Yao, Miles Chen, Rob Herring, James Morse,
Andrew Murray, Andrew Morton, Laura Abbott, Frank Rowand,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
Robin Murphy
In-Reply-To: <CAJMQK-ghQ8weMerXW7t0DFZTAg_c5M80Yp5DTAtyY2LA7YpS1A@mail.gmail.com>
On Tue, 20 Aug 2019 at 10:43, Hsin-Yi Wang <hsinyi@chromium.org> wrote:
>
> Hi Ted,
>
> Thanks for raising this question.
>
> For UEFI based system, they have a config table that carries rng seed
> and can be passed to device randomness. However, they also use
> add_device_randomness (not sure if it's the same reason that they
> can't guarantee _all_ bootloader can be trusted)
The config table is actually a Linux invention: it is populated by the
EFI stub code (which is part of the kernel) based on the output of a
call into the EFI_RNG_PROTOCOL, which is defined in the UEFI spec, but
optional and not widely available.
I have opted for add_device_randomness() since there is no way to
establish the quality level of the output of EFI_RNG_PROTOCOL, and so
it is currently only used to prevent the bootup state of the entropy
pool to be too predictable, and the output does not contribute to the
entropy estimate kept by the RNG core.
> This patch is to let DT based system also have similar features, which
> can make initial random number stronger. (We only care initial
> situation here, since more entropy would be added to kernel as time
> goes on )
>
> Conservatively, we can use add_device_randomness() as well, which
> would pass buffer to crng_slow_load() instead of crng_fast_load().
> But I think we should trust bootloader here. Whoever wants to use this
> feature should make sure their bootloader can pass valid (random
> enough) seeds. If they are not sure, they can just don't add the
> property to DT.
It is the firmware that adds the property to the DT, not the user.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] efi/arm: fix allocation failure when reserving the kernel base
From: Ard Biesheuvel @ 2019-08-20 11:00 UTC (permalink / raw)
To: Mike Rapoport
Cc: Juergen Gross, Joey Lee, linux-efi@vger.kernel.org,
guillaume.gardet@arm.com, linux@armlinux.org.uk,
linux-kernel@vger.kernel.org, Chester Lin, geert@linux-m68k.org,
ren_guo@c-sky.com, Gary Lin, akpm@linux-foundation.org,
mingo@kernel.org, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20190820074930.GC5989@rapoport-lnx>
On Tue, 20 Aug 2019 at 10:49, Mike Rapoport <rppt@linux.ibm.com> wrote:
>
> On Mon, Aug 19, 2019 at 05:56:51PM +0300, Ard Biesheuvel wrote:
> > On Mon, 19 Aug 2019 at 11:01, Chester Lin <clin@suse.com> wrote:
> > >
> > > Hi Mike and Ard,
> > >
> > > On Thu, Aug 15, 2019 at 04:37:39PM +0300, Mike Rapoport wrote:
> > > > On Thu, Aug 15, 2019 at 02:32:50PM +0300, Ard Biesheuvel wrote:
> > > > > (adding Mike)
> > > > >
>
> ...
>
> > > > > > In this case the kernel failed to reserve cma, which should hit the issue of
> > > > > > memblock_limit=0x1000 as I had mentioned in my patch description. The first
> > > > > > block [0-0xfff] was scanned in adjust_lowmem_bounds(), but it did not align
> > > > > > with PMD_SIZE so the cma reservation failed because the memblock.current_limit
> > > > > > was extremely low. That's why I expand the first reservation from 1 PAGESIZE to
> > > > > > 1 PMD_SIZE in my patch in order to avoid this issue. Please kindly let me know
> > > > > > if any suggestion, thank you.
> > > >
> > > >
> > > > > This looks like it is a separate issue. The memblock/cma code should
> > > > > not choke on a reserved page of memory at 0x0.
> > > > >
> > > > > Perhaps Russell or Mike (cc'ed) have an idea how to address this?
> > > >
> > > > Presuming that the last memblock dump comes from the end of
> > > > arm_memblock_init() with the this memory map
> > > >
> > > > memory[0x0] [0x0000000000000000-0x0000000000000fff], 0x0000000000001000 bytes flags: 0x4
> > > > memory[0x1] [0x0000000000001000-0x0000000007ef5fff], 0x0000000007ef5000 bytes flags: 0x0
> > > > memory[0x2] [0x0000000007ef6000-0x0000000007f09fff], 0x0000000000014000 bytes flags: 0x4
> > > > memory[0x3] [0x0000000007f0a000-0x000000003cb3efff], 0x0000000034c35000 bytes flags: 0x0
> > > >
> > > > adjust_lowmem_bounds() will set the memblock_limit (and respectively global
> > > > memblock.current_limit) to 0x1000 and any further memblock_alloc*() will
> > > > happily fail.
> > > >
> > > > I believe that the assumption for memblock_limit calculations was that the
> > > > first bank has several megs at least.
> > > >
> > > > I wonder if this hack would help:
> > > >
> > > > diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
> > > > index d9a0038..948e5b9 100644
> > > > --- a/arch/arm/mm/mmu.c
> > > > +++ b/arch/arm/mm/mmu.c
> > > > @@ -1206,7 +1206,7 @@ void __init adjust_lowmem_bounds(void)
> > > > * allocated when mapping the start of bank 0, which
> > > > * occurs before any free memory is mapped.
> > > > */
> > > > - if (!memblock_limit) {
> > > > + if (memblock_limit < PMD_SIZE) {
> > > > if (!IS_ALIGNED(block_start, PMD_SIZE))
> > > > memblock_limit = block_start;
> > > > else if (!IS_ALIGNED(block_end, PMD_SIZE))
> > > >
> > >
> > > I applied this patch as well and it works well on rpi-2 model B.
> > >
> >
> > Thanks, Chester, that is good to know.
> >
> > However, afaict, this only affects systems where physical memory
> > starts at address 0x0, so I think we need a better fix.
>
> This hack can be easily extended to handle systems with arbitrary start
> address, but it's still a hack...
>
> > I know Mike has been looking into the NOMAP stuff lately, and your
> > original patch contains a hunk that makes this code (?) disregard
> > nomap memblocks. That might be a better approach.
>
> I was actually looking how to replace NOMAP with something else to make
> memblock.memory consistent with actual physical memory banks. But this work
> is stashed for now.
>
> I'm not sure that skipping NOMAP regions would be good enough.
> If I understand corrrectly, with Chester's original patch the reservation
> of PMD aligned chunk of 32M for the kernel made the first conv-mem region
> PMD aligned and then memblock_limit will be set to the end of this region.
>
> Is there a reason for marking EFI_RESERVED_TYPE as NOMAP rather than simply
> reserve them with memblock_reserve()?
>
Yes.
On ARM systems, reserved memory regions should never be mapped by
default, since the cacheable mappings we use in the linear region may
conflict with the mapping attributes used by the firmware or driver
components that are using this memory.
In this particular case, we are talking about things like spin tables
and pens for secondaries that boot up with their caches disabled, and
having a cacheable mapping on the primary CPU might cause a loss of
coherency.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 2/3] kprobes: adjust kprobe addr for KPROBES_ON_FTRACE
From: Jisheng Zhang @ 2019-08-20 10:41 UTC (permalink / raw)
To: Naveen N. Rao
Cc: Jonathan Corbet, Catalin Marinas, x86@kernel.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
Anil S Keshavamurthy, Ingo Molnar, Borislav Petkov,
Masami Hiramatsu, H. Peter Anvin, Thomas Gleixner, Will Deacon,
David S. Miller, linux-arm-kernel@lists.infradead.org
In-Reply-To: <1566295437.yqnot2qd2e.naveen@linux.ibm.com>
On Tue, 20 Aug 2019 15:45:24 +0530 "Naveen N. Rao" wrote:
>
>
> Jisheng Zhang wrote:
> > For KPROBES_ON_FTRACE case, we need to adjust the kprobe's addr
> > correspondingly.
> >
> > Signed-off-by: Jisheng Zhang <Jisheng.Zhang@synaptics.com>
> > ---
> > kernel/kprobes.c | 10 +++++++---
> > 1 file changed, 7 insertions(+), 3 deletions(-)
> >
> > diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> > index 9873fc627d61..3fd2f68644da 100644
> > --- a/kernel/kprobes.c
> > +++ b/kernel/kprobes.c
> > @@ -1484,15 +1484,19 @@ static inline int check_kprobe_rereg(struct kprobe *p)
> >
> > int __weak arch_check_ftrace_location(struct kprobe *p)
> > {
> > - unsigned long ftrace_addr;
> > + unsigned long ftrace_addr, addr = (unsigned long)p->addr;
> >
> > - ftrace_addr = ftrace_location((unsigned long)p->addr);
> > +#ifdef CONFIG_KPROBES_ON_FTRACE
> > + addr = ftrace_call_adjust(addr);
> > +#endif
>
> Looking at the commit message for patch 3/3, it looks like you want the
> probe to be placed on ftrace entry by default, and this patch seems to
> be aimed at that.
Yeah.
>
> If so, this is not the right approach. As I mentioned previously, you
> would want to over-ride kprobe_lookup_name(). This ensures that the
> address is changed only if the user provided a symbol, and not if the
> user wanted to probe at a very specific address. See commit
Great! Now I understand the reason.
> 24bd909e94776 ("powerpc/kprobes: Prefer ftrace when probing function
> entry").
Now, I got your meaning. You are right. I will update the patch in newer
version.
Thanks a lot!
>
> If this patch is for some other purpose, then it isn't clear from the
> commit log. Please provide a better explanation.
>
>
> - Naveen
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] ARM: scoop: Use the right include
From: Linus Walleij @ 2019-08-20 10:34 UTC (permalink / raw)
To: arm, soc; +Cc: Linus Walleij, Richard Purdie, linux-arm-kernel
This is a GPIO driver so it should include
<linux/gpio/driver.h> not <linux/gpio.h>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ARM SoC folks: please apply this directly for v5.4 if
OK.
---
arch/arm/common/scoop.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c
index 60130bd7b182..6edb961bd6c1 100644
--- a/arch/arm/common/scoop.c
+++ b/arch/arm/common/scoop.c
@@ -8,7 +8,7 @@
*/
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
--
2.21.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH] efi/arm: fix allocation failure when reserving the kernel base
From: Chester Lin @ 2019-08-20 10:29 UTC (permalink / raw)
To: Mike Rapoport
Cc: Juergen Gross, Joey Lee, linux-efi@vger.kernel.org,
guillaume.gardet@arm.com, Ard Biesheuvel, linux@armlinux.org.uk,
linux-kernel@vger.kernel.org, Chester Lin, geert@linux-m68k.org,
ren_guo@c-sky.com, Gary Lin, akpm@linux-foundation.org,
mingo@kernel.org, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20190820074930.GC5989@rapoport-lnx>
On Tue, Aug 20, 2019 at 10:49:30AM +0300, Mike Rapoport wrote:
> On Mon, Aug 19, 2019 at 05:56:51PM +0300, Ard Biesheuvel wrote:
> > On Mon, 19 Aug 2019 at 11:01, Chester Lin <clin@suse.com> wrote:
> > >
> > > Hi Mike and Ard,
> > >
> > > On Thu, Aug 15, 2019 at 04:37:39PM +0300, Mike Rapoport wrote:
> > > > On Thu, Aug 15, 2019 at 02:32:50PM +0300, Ard Biesheuvel wrote:
> > > > > (adding Mike)
> > > > >
>
> ...
>
> > > > > > In this case the kernel failed to reserve cma, which should hit the issue of
> > > > > > memblock_limit=0x1000 as I had mentioned in my patch description. The first
> > > > > > block [0-0xfff] was scanned in adjust_lowmem_bounds(), but it did not align
> > > > > > with PMD_SIZE so the cma reservation failed because the memblock.current_limit
> > > > > > was extremely low. That's why I expand the first reservation from 1 PAGESIZE to
> > > > > > 1 PMD_SIZE in my patch in order to avoid this issue. Please kindly let me know
> > > > > > if any suggestion, thank you.
> > > >
> > > >
> > > > > This looks like it is a separate issue. The memblock/cma code should
> > > > > not choke on a reserved page of memory at 0x0.
> > > > >
> > > > > Perhaps Russell or Mike (cc'ed) have an idea how to address this?
> > > >
> > > > Presuming that the last memblock dump comes from the end of
> > > > arm_memblock_init() with the this memory map
> > > >
> > > > memory[0x0] [0x0000000000000000-0x0000000000000fff], 0x0000000000001000 bytes flags: 0x4
> > > > memory[0x1] [0x0000000000001000-0x0000000007ef5fff], 0x0000000007ef5000 bytes flags: 0x0
> > > > memory[0x2] [0x0000000007ef6000-0x0000000007f09fff], 0x0000000000014000 bytes flags: 0x4
> > > > memory[0x3] [0x0000000007f0a000-0x000000003cb3efff], 0x0000000034c35000 bytes flags: 0x0
> > > >
> > > > adjust_lowmem_bounds() will set the memblock_limit (and respectively global
> > > > memblock.current_limit) to 0x1000 and any further memblock_alloc*() will
> > > > happily fail.
> > > >
> > > > I believe that the assumption for memblock_limit calculations was that the
> > > > first bank has several megs at least.
> > > >
> > > > I wonder if this hack would help:
> > > >
> > > > diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
> > > > index d9a0038..948e5b9 100644
> > > > --- a/arch/arm/mm/mmu.c
> > > > +++ b/arch/arm/mm/mmu.c
> > > > @@ -1206,7 +1206,7 @@ void __init adjust_lowmem_bounds(void)
> > > > * allocated when mapping the start of bank 0, which
> > > > * occurs before any free memory is mapped.
> > > > */
> > > > - if (!memblock_limit) {
> > > > + if (memblock_limit < PMD_SIZE) {
> > > > if (!IS_ALIGNED(block_start, PMD_SIZE))
> > > > memblock_limit = block_start;
> > > > else if (!IS_ALIGNED(block_end, PMD_SIZE))
> > > >
> > >
> > > I applied this patch as well and it works well on rpi-2 model B.
> > >
> >
> > Thanks, Chester, that is good to know.
> >
> > However, afaict, this only affects systems where physical memory
> > starts at address 0x0, so I think we need a better fix.
>
> This hack can be easily extended to handle systems with arbitrary start
> address, but it's still a hack...
>
> > I know Mike has been looking into the NOMAP stuff lately, and your
> > original patch contains a hunk that makes this code (?) disregard
> > nomap memblocks. That might be a better approach.
>
> I was actually looking how to replace NOMAP with something else to make
> memblock.memory consistent with actual physical memory banks. But this work
> is stashed for now.
>
> I'm not sure that skipping NOMAP regions would be good enough.
> If I understand corrrectly, with Chester's original patch the reservation
> of PMD aligned chunk of 32M for the kernel made the first conv-mem region
> PMD aligned and then memblock_limit will be set to the end of this region.
>
> Is there a reason for marking EFI_RESERVED_TYPE as NOMAP rather than simply
> reserve them with memblock_reserve()?
>
Hi Mike,
I make this change in efistub so I am not sure if memblock_reserve() can be
linked by ld or not. I tried using efi_mem_reserve() but got a linker error of
undefined reference. Is there a better place to call memblock_reserve() after
efistub?
Thanks,
Chester
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 3/4] iommu/io-pgtable-arm: Rationalise TCR handling
From: Will Deacon @ 2019-08-20 10:31 UTC (permalink / raw)
To: Robin Murphy; +Cc: robdclark, joro, jcrouse, iommu, linux-arm-kernel
In-Reply-To: <78df4f8e2510e88f3ded59eb385f79b4442ed4f2.1566238530.git.robin.murphy@arm.com>
On Mon, Aug 19, 2019 at 07:19:30PM +0100, Robin Murphy wrote:
> Although it's conceptually nice for the io_pgtable_cfg to provide a
> standard VMSA TCR value, the reality is that no VMSA-compliant IOMMU
> looks exactly like an Arm CPU, and they all have various other TCR
> controls which io-pgtable can't be expected to understand. Thus since
> there is an expectation that drivers will have to add to the given TCR
> value anyway, let's strip it down to just the essentials that are
> directly relevant to io-pgatble's inner workings - namely the address
> sizes, walk attributes, and where appropriate, format selection.
>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> ---
> drivers/iommu/arm-smmu-v3.c | 7 +------
> drivers/iommu/arm-smmu.c | 1 +
> drivers/iommu/arm-smmu.h | 2 ++
> drivers/iommu/io-pgtable-arm-v7s.c | 6 ++----
> drivers/iommu/io-pgtable-arm.c | 4 ----
> drivers/iommu/qcom_iommu.c | 2 +-
> 6 files changed, 7 insertions(+), 15 deletions(-)
Hmm, so I'm a bit nervous about this one since I think we really should
be providing a TCR with EPD1 set if we're only giving you TTBR0. Relying
on the driver to do this worries me. See my comments on the next patch.
Will
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 4/4] iommu/io-pgtable-arm: Prepare for TTBR1 usage
From: Will Deacon @ 2019-08-20 10:30 UTC (permalink / raw)
To: Robin Murphy; +Cc: robdclark, joro, jcrouse, iommu, linux-arm-kernel
In-Reply-To: <6596469d5fa1e918145fdd4e6b1a3ad67f7cde2e.1566238530.git.robin.murphy@arm.com>
On Mon, Aug 19, 2019 at 07:19:31PM +0100, Robin Murphy wrote:
> Now that callers are free to use a given table for TTBR1 if they wish
> (all they need do is shift the provided attributes when constructing
> their final TCR value), the only remaining impediment is the address
> validation on map/unmap. The fact that the LPAE address space split is
> symmetric makes this easy to accommodate - by simplifying the current
> range checks into explicit tests that address bits above IAS are all
> zero, it then follows straightforwardly to add the inverse test to
> allow the all-ones case as well.
>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> ---
> drivers/iommu/io-pgtable-arm.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> index 09cb20671fbb..f39c50356351 100644
> --- a/drivers/iommu/io-pgtable-arm.c
> +++ b/drivers/iommu/io-pgtable-arm.c
> @@ -475,13 +475,13 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
> arm_lpae_iopte *ptep = data->pgd;
> int ret, lvl = ARM_LPAE_START_LVL(data);
> arm_lpae_iopte prot;
> + long iaext = (long)iova >> data->iop.cfg.ias;
>
> /* If no access, then nothing to do */
> if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
> return 0;
>
> - if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias) ||
> - paddr >= (1ULL << data->iop.cfg.oas)))
> + if (WARN_ON((iaext && ~iaext) || paddr >> data->iop.cfg.oas))
I had to read that '&&' twice, but I see what you're doing now :)
> return -ERANGE;
This doesn't seem sufficient to prevent a mixture of TTBR1 and TTBR0
addresses from being mapped in the same TTBR. Perhaps we need a quirk for
TTBR1, which could then take care of setting EPDx appropriately?
Will
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3] gpio: pl061: Fix the issue failed to register the ACPI interrtupion
From: Linus Walleij @ 2019-08-20 10:26 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Salil Mehta, jinying, Tangkunshan, Liguozhu (Kenneth), John Garry,
Rafael J. Wysocki, Linux Kernel Mailing List, Wei Xu, Linuxarm,
open list:GPIO SUBSYSTEM, Shiju Jose, Shameerali Kolothum Thodi,
huangdaode, Jonathan Cameron, Thierry Reding, Mika Westerberg,
Zhangyi ac, linux-arm Mailing List, Len Brown
In-Reply-To: <CAHp75VcwDZdOwFsT4Gf-1a4tNGQdowK-RKRvSif2m7oTsVQNbw@mail.gmail.com>
On Tue, Aug 20, 2019 at 10:51 AM Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
> On Tue, Aug 20, 2019 at 10:12 AM Linus Walleij <linus.walleij@linaro.org> wrote:
> > On Mon, Aug 19, 2019 at 5:07 PM Andy Shevchenko
> > <andy.shevchenko@gmail.com> wrote:
> > Exactly what do you refer to when you want me to
> > "re-do the approach for IRQ handling"? Do you mean
> > this driver or are you referring to:
> >
> > commit e0d89728981393b7d694bd3419b7794b9882c92d
> > Author: Thierry Reding <treding@nvidia.com>
> > Date: Tue Nov 7 19:15:54 2017 +0100
> >
> > gpio: Implement tighter IRQ chip integration
> >
> > Currently GPIO drivers are required to add the GPIO chip and its
> > corresponding IRQ chip separately, which can result in a lot of
> > boilerplate. Use the newly introduced struct gpio_irq_chip, embedded in
> > struct gpio_chip, that drivers can fill in if they want the GPIO core
> > to automatically register the IRQ chip associated with a GPIO chip.
> >
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > Acked-by: Grygorii Strashko <grygorii.strashko@ti.com>
> > Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
>
> Yes.
OK let's fix this mess, it shouldn't be too hard, I've sent a first
few patches.
> > The problem comes from the problem/mess I am trying to
> > clean up in the first place. So if the new way of registering GPIO
> > irqchips is not working for ACPI, then we have to fix that instead
> > of reverting all attempts to use the new API IMO.
>
> Sorry for me being impatient and asking for a groundless requests.
> I'll help you with cleaning this.
Sorry if I sounded harsh :( I just have a bit of panic.
I am sure we can fix this, I only recently realized what a headache
the new API is going to be if I can't straighten it out and have to
keep the old stuff around forever in parallel.
Yours,
Linus Walleij
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 2/4] iommu/io-pgtable-arm: Rationalise TTBRn handling
From: Will Deacon @ 2019-08-20 10:19 UTC (permalink / raw)
To: Robin Murphy; +Cc: robdclark, joro, jcrouse, iommu, linux-arm-kernel
In-Reply-To: <dbb942070c2ef812e379414c236734931613d860.1566238530.git.robin.murphy@arm.com>
On Mon, Aug 19, 2019 at 07:19:29PM +0100, Robin Murphy wrote:
> TTBR1 values have so far been redundant since no users implement any
> support for split address spaces. Crucially, though, one of the main
> reasons for wanting to do so is to be able to manage each half entirely
> independently, e.g. context-switching one set of mappings without
> disturbing the other. Thus it seems unlikely that tying two tables
> together in a single io_pgtable_cfg would ever be particularly desirable
> or useful.
>
> Streamline the configs to just a single conceptual TTBR value
> representing the allocated table. This paves the way for future users to
> support split address spaces by simply allocating a table and dealing
> with the detailed TTBRn logistics themselves.
>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> ---
> drivers/iommu/arm-smmu-v3.c | 2 +-
> drivers/iommu/arm-smmu.c | 9 ++++-----
> drivers/iommu/io-pgtable-arm-v7s.c | 16 +++++++---------
> drivers/iommu/io-pgtable-arm.c | 7 +++----
> drivers/iommu/ipmmu-vmsa.c | 2 +-
> drivers/iommu/msm_iommu.c | 4 ++--
> drivers/iommu/mtk_iommu.c | 4 ++--
> drivers/iommu/qcom_iommu.c | 3 +--
> include/linux/io-pgtable.h | 4 ++--
> 9 files changed, 23 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
> index 2a8db896d698..2e50cf49c3c4 100644
> --- a/drivers/iommu/arm-smmu-v3.c
> +++ b/drivers/iommu/arm-smmu-v3.c
> @@ -1722,7 +1722,7 @@ static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
> }
>
> cfg->cd.asid = (u16)asid;
> - cfg->cd.ttbr = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
> + cfg->cd.ttbr = pgtbl_cfg->arm_lpae_s1_cfg.ttbr;
> cfg->cd.tcr = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
> cfg->cd.mair = pgtbl_cfg->arm_lpae_s1_cfg.mair;
> return 0;
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index 184ca41e9de7..19030c4b5904 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -473,13 +473,12 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
> /* TTBRs */
> if (stage1) {
> if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
> - cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr[0];
> - cb->ttbr[1] = pgtbl_cfg->arm_v7s_cfg.ttbr[1];
> + cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr;
> + cb->ttbr[1] = 0;
> } else {
> - cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
> + cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr;
> cb->ttbr[0] |= FIELD_PREP(TTBRn_ASID, cfg->asid);
> - cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
> - cb->ttbr[1] |= FIELD_PREP(TTBRn_ASID, cfg->asid);
> + cb->ttbr[1] = FIELD_PREP(TTBRn_ASID, cfg->asid);
Why do you continue to put the ASID in here?
> diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c
> index 34bb357b3cfa..de55b6d82ef1 100644
> --- a/drivers/iommu/qcom_iommu.c
> +++ b/drivers/iommu/qcom_iommu.c
> @@ -247,10 +247,9 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
>
> /* TTBRs */
> iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
> - pgtbl_cfg.arm_lpae_s1_cfg.ttbr[0] |
> + pgtbl_cfg.arm_lpae_s1_cfg.ttbr |
> FIELD_PREP(TTBRn_ASID, ctx->asid));
> iommu_writeq(ctx, ARM_SMMU_CB_TTBR1,
> - pgtbl_cfg.arm_lpae_s1_cfg.ttbr[1] |
> FIELD_PREP(TTBRn_ASID, ctx->asid));
Same here.
> diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
> index a6c8aa204733..7a0905d7a006 100644
> --- a/include/linux/io-pgtable.h
> +++ b/include/linux/io-pgtable.h
> @@ -90,7 +90,7 @@ struct io_pgtable_cfg {
> /* Low-level data specific to the table format */
> union {
> struct {
> - u64 ttbr[2];
> + u64 ttbr;
> u64 tcr;
> u64 mair;
> } arm_lpae_s1_cfg;
> @@ -101,7 +101,7 @@ struct io_pgtable_cfg {
> } arm_lpae_s2_cfg;
>
> struct {
> - u32 ttbr[2];
> + u32 ttbr;
We could probably do with a comment for these 'ttbr' field now saying that
they refer to ttbr0 (since the tcr will have EPD1 set).
Will
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 2/3] kprobes: adjust kprobe addr for KPROBES_ON_FTRACE
From: Naveen N. Rao @ 2019-08-20 10:15 UTC (permalink / raw)
To: Anil S, Keshavamurthy, Borislav Petkov, Catalin Marinas,
Jonathan Corbet, David S. Miller, H. Peter Anvin, Jisheng Zhang,
Masami Hiramatsu, Ingo Molnar, Thomas Gleixner, Will Deacon,
x86@kernel.org
Cc: linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <20190820114224.0c8963c4@xhacker.debian>
Jisheng Zhang wrote:
> For KPROBES_ON_FTRACE case, we need to adjust the kprobe's addr
> correspondingly.
>
> Signed-off-by: Jisheng Zhang <Jisheng.Zhang@synaptics.com>
> ---
> kernel/kprobes.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> index 9873fc627d61..3fd2f68644da 100644
> --- a/kernel/kprobes.c
> +++ b/kernel/kprobes.c
> @@ -1484,15 +1484,19 @@ static inline int check_kprobe_rereg(struct kprobe *p)
>
> int __weak arch_check_ftrace_location(struct kprobe *p)
> {
> - unsigned long ftrace_addr;
> + unsigned long ftrace_addr, addr = (unsigned long)p->addr;
>
> - ftrace_addr = ftrace_location((unsigned long)p->addr);
> +#ifdef CONFIG_KPROBES_ON_FTRACE
> + addr = ftrace_call_adjust(addr);
> +#endif
Looking at the commit message for patch 3/3, it looks like you want the
probe to be placed on ftrace entry by default, and this patch seems to
be aimed at that.
If so, this is not the right approach. As I mentioned previously, you
would want to over-ride kprobe_lookup_name(). This ensures that the
address is changed only if the user provided a symbol, and not if the
user wanted to probe at a very specific address. See commit
24bd909e94776 ("powerpc/kprobes: Prefer ftrace when probing function
entry").
If this patch is for some other purpose, then it isn't clear from the
commit log. Please provide a better explanation.
- Naveen
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 17/17] iommu/arm-smmu: Add context init implementation hook
From: Vivek Gautam @ 2019-08-20 10:15 UTC (permalink / raw)
To: Robin Murphy, will
Cc: robdclark, gregory.clement, bjorn.andersson, iommu, jcrouse, joro,
linux-arm-kernel
In-Reply-To: <f50c14834bb7a4f0f7c765d433c2defdb03661c9.1565892337.git.robin.murphy@arm.com>
[-- Attachment #1: Type: text/plain, Size: 8220 bytes --]
On 8/16/2019 12:07 AM, Robin Murphy wrote:
> Allocating and initialising a context for a domain is another point
> where certain implementations are known to want special behaviour.
> Currently the other half of the Cavium workaround comes into play here,
> so let's finish the job to get the whole thing right out of the way.
>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> ---
> drivers/iommu/arm-smmu-impl.c | 42 ++++++++++++++++++++++++++---
> drivers/iommu/arm-smmu.c | 51 +++++++----------------------------
> drivers/iommu/arm-smmu.h | 42 +++++++++++++++++++++++++++--
> 3 files changed, 87 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c
> index 4dc8b1c4befb..e22e9004f449 100644
> --- a/drivers/iommu/arm-smmu-impl.c
> +++ b/drivers/iommu/arm-smmu-impl.c
> @@ -48,25 +48,60 @@ const struct arm_smmu_impl calxeda_impl = {
> };
>
>
> +struct cavium_smmu {
> + struct arm_smmu_device smmu;
> + u32 id_base;
> +};
> +
> static int cavium_cfg_probe(struct arm_smmu_device *smmu)
> {
> static atomic_t context_count = ATOMIC_INIT(0);
> + struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu);
> /*
> * Cavium CN88xx erratum #27704.
> * Ensure ASID and VMID allocation is unique across all SMMUs in
> * the system.
> */
> - smmu->cavium_id_base = atomic_fetch_add(smmu->num_context_banks,
> - &context_count);
> + cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count);
> dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n");
>
> return 0;
> }
>
> +int cavium_init_context(struct arm_smmu_domain *smmu_domain)
> +{
> + struct cavium_smmu *cs = container_of(smmu_domain->smmu,
> + struct cavium_smmu, smmu);
> +
> + if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2)
> + smmu_domain->cfg.vmid += cs->id_base;
> + else
> + smmu_domain->cfg.asid += cs->id_base;
> +
> + return 0;
> +}
> +
> const struct arm_smmu_impl cavium_impl = {
> .cfg_probe = cavium_cfg_probe,
> + .init_context = cavium_init_context,
> };
>
> +struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu)
> +{
> + struct cavium_smmu *cs;
> +
> + cs = devm_kzalloc(smmu->dev, sizeof(*cs), GFP_KERNEL);
> + if (!cs)
> + return ERR_PTR(-ENOMEM);
> +
> + cs->smmu = *smmu;
> + cs->smmu.impl = &cavium_impl;
> +
> + devm_kfree(smmu->dev, smmu);
> +
> + return &cs->smmu;
> +}
> +
>
> #define ARM_MMU500_ACTLR_CPRE (1 << 1)
>
> @@ -126,8 +161,7 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
> smmu->impl = &arm_mmu500_impl;
> break;
> case CAVIUM_SMMUV2:
> - smmu->impl = &cavium_impl;
> - break;
> + return cavium_smmu_impl_init(smmu);
> default:
> break;
> }
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index fc98992d120d..b8628e2ab579 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -27,7 +27,6 @@
> #include <linux/interrupt.h>
> #include <linux/io.h>
> #include <linux/io-64-nonatomic-hi-lo.h>
> -#include <linux/io-pgtable.h>
> #include <linux/iopoll.h>
> #include <linux/init.h>
> #include <linux/moduleparam.h>
> @@ -111,44 +110,6 @@ struct arm_smmu_master_cfg {
> #define for_each_cfg_sme(fw, i, idx) \
> for (i = 0; idx = fwspec_smendx(fw, i), i < fw->num_ids; ++i)
>
> -enum arm_smmu_context_fmt {
> - ARM_SMMU_CTX_FMT_NONE,
> - ARM_SMMU_CTX_FMT_AARCH64,
> - ARM_SMMU_CTX_FMT_AARCH32_L,
> - ARM_SMMU_CTX_FMT_AARCH32_S,
> -};
> -
> -struct arm_smmu_cfg {
> - u8 cbndx;
> - u8 irptndx;
> - union {
> - u16 asid;
> - u16 vmid;
> - };
> - enum arm_smmu_cbar_type cbar;
> - enum arm_smmu_context_fmt fmt;
> -};
> -#define INVALID_IRPTNDX 0xff
> -
> -enum arm_smmu_domain_stage {
> - ARM_SMMU_DOMAIN_S1 = 0,
> - ARM_SMMU_DOMAIN_S2,
> - ARM_SMMU_DOMAIN_NESTED,
> - ARM_SMMU_DOMAIN_BYPASS,
> -};
> -
> -struct arm_smmu_domain {
> - struct arm_smmu_device *smmu;
> - struct io_pgtable_ops *pgtbl_ops;
> - const struct iommu_gather_ops *tlb_ops;
> - struct arm_smmu_cfg cfg;
> - enum arm_smmu_domain_stage stage;
> - bool non_strict;
> - struct mutex init_mutex; /* Protects smmu pointer */
> - spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */
> - struct iommu_domain domain;
> -};
> -
> static bool using_legacy_binding, using_generic_binding;
>
> static inline int arm_smmu_rpm_get(struct arm_smmu_device *smmu)
> @@ -749,9 +710,16 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
> }
>
> if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2)
> - cfg->vmid = cfg->cbndx + 1 + smmu->cavium_id_base;
> + cfg->vmid = cfg->cbndx + 1;
> else
> - cfg->asid = cfg->cbndx + smmu->cavium_id_base;
> + cfg->asid = cfg->cbndx;
> +
> + smmu_domain->smmu = smmu;
> + if (smmu->impl && smmu->impl->init_context) {
> + ret = smmu->impl->init_context(smmu_domain);
> + if (ret)
> + goto out_unlock;
> + }
>
> pgtbl_cfg = (struct io_pgtable_cfg) {
> .pgsize_bitmap = smmu->pgsize_bitmap,
> @@ -765,7 +733,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
> if (smmu_domain->non_strict)
> pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT;
>
> - smmu_domain->smmu = smmu;
> pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
> if (!pgtbl_ops) {
> ret = -ENOMEM;
> diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h
> index ddafe872a396..611ed742e56f 100644
> --- a/drivers/iommu/arm-smmu.h
> +++ b/drivers/iommu/arm-smmu.h
> @@ -14,6 +14,7 @@
> #include <linux/bits.h>
> #include <linux/clk.h>
> #include <linux/device.h>
> +#include <linux/io-pgtable.h>
> #include <linux/iommu.h>
> #include <linux/mutex.h>
> #include <linux/spinlock.h>
> @@ -270,14 +271,50 @@ struct arm_smmu_device {
> struct clk_bulk_data *clks;
> int num_clks;
>
> - u32 cavium_id_base; /* Specific to Cavium */
> -
> spinlock_t global_sync_lock;
>
> /* IOMMU core code handle */
> struct iommu_device iommu;
> };
>
> +enum arm_smmu_context_fmt {
> + ARM_SMMU_CTX_FMT_NONE,
> + ARM_SMMU_CTX_FMT_AARCH64,
> + ARM_SMMU_CTX_FMT_AARCH32_L,
> + ARM_SMMU_CTX_FMT_AARCH32_S,
> +};
> +
> +struct arm_smmu_cfg {
> + u8 cbndx;
> + u8 irptndx;
> + union {
> + u16 asid;
> + u16 vmid;
> + };
> + enum arm_smmu_cbar_type cbar;
> + enum arm_smmu_context_fmt fmt;
> +};
> +#define INVALID_IRPTNDX 0xff
> +
> +enum arm_smmu_domain_stage {
> + ARM_SMMU_DOMAIN_S1 = 0,
> + ARM_SMMU_DOMAIN_S2,
> + ARM_SMMU_DOMAIN_NESTED,
> + ARM_SMMU_DOMAIN_BYPASS,
> +};
> +
> +struct arm_smmu_domain {
> + struct arm_smmu_device *smmu;
> + struct io_pgtable_ops *pgtbl_ops;
> + const struct iommu_gather_ops *tlb_ops;
> + struct arm_smmu_cfg cfg;
> + enum arm_smmu_domain_stage stage;
> + bool non_strict;
> + struct mutex init_mutex; /* Protects smmu pointer */
> + spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */
> + struct iommu_domain domain;
> +};
> +
>
> /* Implementation details, yay! */
> struct arm_smmu_impl {
> @@ -289,6 +326,7 @@ struct arm_smmu_impl {
> u64 val);
> int (*cfg_probe)(struct arm_smmu_device *smmu);
> int (*reset)(struct arm_smmu_device *smmu);
> + int (*init_context)(struct arm_smmu_domain *smmu_domain);
Hi Robin,
Sorry for responding late to this series. I have couple of doubts here
that I wanted to discuss.
Are we standardizing these implementation specific ops? Each vendor
implementations will have something peculiar to take care. Things are
good right now with 'reset', 'cfg_probe', and 'init_context' hooks.
But, on top of vendor implementation details, there can be SoC specific
errata changes that need to be added.
Moreover, adding implementation data based on __model__ may not suffice
for long. Do you suggest adding any other data variable in the
ARM_SMMU_MATCH_DATA?
To show SoC specific needs, I have the change attached in this email to
take care of the SDM845 'wait-for-safe' sequence.
Please take a look.
Thanks & Regards
Vivek
> };
>
> static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n)
[-- Attachment #2: 0003-iommu-arm-smmu-impl-Add-SDM845-specific-implementati.patch --]
[-- Type: text/plain, Size: 3417 bytes --]
From 3830ec7e22deb49de72b6bc29bd965f7b07b9669 Mon Sep 17 00:00:00 2001
From: Vivek Gautam <vivek.gautam@codeaurora.org>
Date: Tue, 20 Aug 2019 15:28:16 +0530
Subject: [PATCH 3/4] iommu: arm-smmu-impl: Add SDM845 specific implementation
hook
Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
---
drivers/iommu/arm-smmu-impl.c | 31 +++++++++++++++++++++++++++++++
drivers/iommu/arm-smmu.c | 2 ++
drivers/iommu/arm-smmu.h | 1 +
3 files changed, 34 insertions(+)
diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c
index 3f88cd078dd5..0e6f5ab0e0ce 100644
--- a/drivers/iommu/arm-smmu-impl.c
+++ b/drivers/iommu/arm-smmu-impl.c
@@ -6,6 +6,7 @@
#include <linux/bitfield.h>
#include <linux/of.h>
+#include <linux/qcom_scm.h>
#include "arm-smmu.h"
@@ -148,6 +149,32 @@ static const struct arm_smmu_impl arm_mmu500_impl = {
};
+static int qcom_sdm845_smmu500_cfg_probe(struct arm_smmu_device *smmu)
+{
+ int ret;
+
+ /*
+ * To address performance degradation in non-real time clients,
+ * such as USB and UFS, turn off wait-for-safe on sdm845 platforms,
+ * such as MTP, whose firmwares implement corresponding secure monitor
+ * call handlers.
+ */
+ if (of_property_read_bool(smmu->dev->of_node,
+ "qcom,smmu-500-fw-impl-safe-errata")) {
+ ret = qcom_scm_qsmmu500_wait_safe_toggle(0);
+ if (ret)
+ dev_warn(smmu->dev, "Failed to turn off SAFE logic\n");
+ }
+
+ return 0;
+}
+
+const struct arm_smmu_impl qcom_sdm845_smmu500_impl = {
+ .reset = arm_mmu500_reset,
+ .cfg_probe = qcom_sdm845_smmu500_cfg_probe,
+};
+
+
struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
{
/*
@@ -170,5 +197,9 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
"calxeda,smmu-secure-config-access"))
smmu->impl = &calxeda_impl;
+ if (smmu->model == QCOM_SMMU500 &&
+ of_device_is_compatible(smmu->dev->of_node, "qcom,sdm845-smmu-500"))
+ smmu->impl = &qcom_sdm845_smmu500_impl;
+
return smmu;
}
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index dd7f78a8e146..f3e5e20ebe9c 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1825,6 +1825,7 @@ ARM_SMMU_MATCH_DATA(arm_mmu401, ARM_SMMU_V1_64K, GENERIC_SMMU);
ARM_SMMU_MATCH_DATA(arm_mmu500, ARM_SMMU_V2, ARM_MMU500);
ARM_SMMU_MATCH_DATA(cavium_smmuv2, ARM_SMMU_V2, CAVIUM_SMMUV2);
ARM_SMMU_MATCH_DATA(qcom_smmuv2, ARM_SMMU_V2, QCOM_SMMUV2);
+ARM_SMMU_MATCH_DATA(qcom_smmu500, ARM_SMMU_V2, QCOM_SMMU500);
static const struct of_device_id arm_smmu_of_match[] = {
{ .compatible = "arm,smmu-v1", .data = &smmu_generic_v1 },
@@ -1834,6 +1835,7 @@ static const struct of_device_id arm_smmu_of_match[] = {
{ .compatible = "arm,mmu-500", .data = &arm_mmu500 },
{ .compatible = "cavium,smmu-v2", .data = &cavium_smmuv2 },
{ .compatible = "qcom,smmu-v2", .data = &qcom_smmuv2 },
+ { .compatible = "qcom,sdm845-smmu-500", .data = &qcom_smmu500 },
{ },
};
diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h
index ac9eac966cf5..48211aad32ea 100644
--- a/drivers/iommu/arm-smmu.h
+++ b/drivers/iommu/arm-smmu.h
@@ -220,6 +220,7 @@ enum arm_smmu_implementation {
ARM_MMU500,
CAVIUM_SMMUV2,
QCOM_SMMUV2,
+ QCOM_SMMU500,
};
struct arm_smmu_device {
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
[-- Attachment #3: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH RFC] smp: Add cpu unstopped mask for smp_send_stop/stop_other_cpus
From: Hsin-Yi Wang @ 2019-08-20 10:08 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Kate Stewart, Peter Zijlstra, Catalin Marinas, Mukesh Ojha,
Grzegorz Halat, H . Peter Anvin ), Guenter Roeck, Will Deacon,
Marek Szyprowski, Rob Herring, Daniel Thompson, Anders Roxell,
Yury Norov, Marc Zyngier, Russell King, Aaro Koskinen,
Ingo Molnar, Viresh Kumar, Waiman Long, Paul E . McKenney, Wei Li,
Alexey Dobriyan, Julien Thierry, Len Brown, Kees Cook,
Arnd Bergmann, Rik van Riel, Stephen Boyd, Shaokun Zhang,
Mike Rapoport, Borislav Petkov, Josh Poimboeuf, Thomas Gleixner,
Greg Kroah-Hartman, Marcelo Tosatti, linux-kernel, Armijn Hemel,
Jiri Kosina, Mathieu Desnoyers, Andrew Morton, Tim Chen,
David S . Miller
In arm/arm64/x86, reboot IPI function uses CPU online mask to let
primary CPU know how many secondary CPUs it has to wait for in
smp_send_stop()/native_stop_other_cpus().
However, sometimes this would trigger unnecessary warnings, since
interrupts and tasks might fall on a CPU that has already executed
the reboot ipi function. This is fine since CPU is already in spinloop.
But warnings are generated since it finds that the CPU is marked as
offiline. The warnings are supposed to catch failures in normal hotplug
offline CPUs, and reboot isn't a regular hotplug. So instead of reusing
online masks, we should use a new mask in reboot IPI functions to do the
work.
Take tick broadcast for example. If broadcast and smp_send_stop()
happen together, most of the time, the CPU getting earliest broadcast
is already in spinloop and thus won't do anything. If the first
broadcast arrives to CPU that hasn't already executed reboot ipi, it
would try to IPI another CPU, but the CPU is already marked as offline,
and warning comes out:
[ 22.481523] reboot: Restarting system
[ 22.481608] WARNING: CPU: 4 PID: 0 at ...
.....
[ 22.481980] Call trace:
[ 22.481991] tick_handle_oneshot_broadcast+0x1f8/0x214
[ 22.482003] mtk_syst_handler+0x34/0x44
[ 22.482016] __handle_irq_event_percpu+0x16c/0x28c
[ 22.482026] handle_irq_event_percpu+0x34/0x8c
[ 22.482035] handle_irq_event+0x48/0x78
[ 22.482044] handle_fasteoi_irq+0xd0/0x1a0
[ 22.482054] __handle_domain_irq+0x84/0xc4
[ 22.482065] gic_handle_irq+0x154/0x1a4
[ 22.482073] el1_irq+0xb0/0x128
[ 22.482081] __do_softirq+0x88/0x2fc
[ 22.482091] irq_exit+0xa0/0xa4
[ 22.482101] handle_IPI+0x1ac/0x2cc
[ 22.482109] gic_handle_irq+0x124/0x1a4
[ 22.482117] el1_irq+0xb0/0x128
[ 22.482127] cpuidle_enter_state+0x298/0x328
[ 22.482135] cpuidle_enter+0x30/0x40
[ 22.482146] do_idle+0x154/0x270
[ 22.482154] cpu_startup_entry+0x24/0x28
[ 22.482164] secondary_start_kernel+0x15c/0x168
[ 22.482171] ---[ end trace 25f699b7e87857ff ]---
From kernel/time/tick-broadcast.c:
/*
* Sanity check. Catch the case where we try to broadcast to
* offline cpus.
*/
if (WARN_ON_ONCE(!cpumask_subset(tmpmask, cpu_online_mask)))
cpumask_and(tmpmask, tmpmask, cpu_online_mask);
Signed-off-by: Hsin-Yi Wang <hsinyi@chromium.org>
---
Note
- The warning comes from arm64 device
- Previous related patches
- https://lkml.org/lkml/2012/8/22/3
- https://patchwork.kernel.org/patch/10535409/
---
arch/arm/kernel/smp.c | 9 +++++----
arch/arm64/kernel/smp.c | 12 +++++++-----
arch/x86/kernel/process.c | 2 +-
arch/x86/kernel/smp.c | 6 +++---
arch/x86/kernel/smpboot.c | 2 ++
include/linux/cpumask.h | 18 ++++++++++++++++++
kernel/cpu.c | 4 ++++
7 files changed, 40 insertions(+), 13 deletions(-)
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 4b0bab2607e4..18f90cea05b2 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -245,6 +245,7 @@ int __cpu_disable(void)
* and we must not schedule until we're ready to give up the cpu.
*/
set_cpu_online(cpu, false);
+ set_cpu_unstopped(cpu, false);
/*
* OK - migrate IRQs away from this CPU
@@ -430,6 +431,7 @@ asmlinkage void secondary_start_kernel(void)
* before we continue - which happens after __cpu_up returns.
*/
set_cpu_online(cpu, true);
+ set_cpu_unstopped(cpu, true);
check_other_bugs();
@@ -593,11 +595,10 @@ static void ipi_cpu_stop(unsigned int cpu)
raw_spin_unlock(&stop_lock);
}
- set_cpu_online(cpu, false);
-
local_fiq_disable();
local_irq_disable();
+ set_cpu_unstopped(cpu, false);
while (1) {
cpu_relax();
wfe();
@@ -713,10 +714,10 @@ void smp_send_stop(void)
/* Wait up to one second for other CPUs to stop */
timeout = USEC_PER_SEC;
- while (num_online_cpus() > 1 && timeout--)
+ while (num_unstopped_cpus() > 1 && timeout--)
udelay(1);
- if (num_online_cpus() > 1)
+ if (num_unstopped_cpus() > 1)
pr_warn("SMP: failed to stop secondary CPUs\n");
}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 018a33e01b0e..ff0d9fcf97ed 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -249,6 +249,7 @@ asmlinkage notrace void secondary_start_kernel(void)
read_cpuid_id());
update_cpu_boot_status(CPU_BOOT_SUCCESS);
set_cpu_online(cpu, true);
+ set_cpu_unstopped(cpu, true);
complete(&cpu_running);
local_daif_restore(DAIF_PROCCTX);
@@ -299,6 +300,7 @@ int __cpu_disable(void)
* and we must not schedule until we're ready to give up the cpu.
*/
set_cpu_online(cpu, false);
+ set_cpu_unstopped(cpu, false);
/*
* OK - migrate IRQs away from this CPU
@@ -827,7 +829,7 @@ void arch_irq_work_raise(void)
static void local_cpu_stop(void)
{
- set_cpu_online(smp_processor_id(), false);
+ set_cpu_unstopped(smp_processor_id(), false);
local_daif_mask();
sdei_mask_local_cpu();
@@ -957,7 +959,7 @@ void smp_send_stop(void)
{
unsigned long timeout;
- if (num_online_cpus() > 1) {
+ if (num_unstopped_cpus() > 1) {
cpumask_t mask;
cpumask_copy(&mask, cpu_online_mask);
@@ -970,12 +972,12 @@ void smp_send_stop(void)
/* Wait up to one second for other CPUs to stop */
timeout = USEC_PER_SEC;
- while (num_online_cpus() > 1 && timeout--)
+ while (num_unstopped_cpus() > 1 && timeout--)
udelay(1);
- if (num_online_cpus() > 1)
+ if (num_unstopped_cpus() > 1)
pr_warning("SMP: failed to stop secondary CPUs %*pbl\n",
- cpumask_pr_args(cpu_online_mask));
+ cpumask_pr_args(cpu_unstopped_mask));
sdei_mask_local_cpu();
}
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 5e94c4354d4e..fb286f189082 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -601,7 +601,6 @@ void stop_this_cpu(void *dummy)
/*
* Remove this CPU:
*/
- set_cpu_online(smp_processor_id(), false);
disable_local_APIC();
mcheck_cpu_clear(this_cpu_ptr(&cpu_info));
@@ -616,6 +615,7 @@ void stop_this_cpu(void *dummy)
*/
if (boot_cpu_has(X86_FEATURE_SME))
native_wbinvd();
+ set_cpu_unstopped(smp_processor_id(), false);
for (;;) {
/*
* Use native_halt() so that memory contents don't change
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index b8d4e9c3c070..99daba583a9a 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -167,7 +167,7 @@ static void native_stop_other_cpus(int wait)
* code. By syncing, we give the cpus up to one second to
* finish their work before we force them off with the NMI.
*/
- if (num_online_cpus() > 1) {
+ if (num_unstopped_cpus() > 1) {
/* did someone beat us here? */
if (atomic_cmpxchg(&stopping_cpu, -1, safe_smp_processor_id()) != -1)
return;
@@ -184,12 +184,12 @@ static void native_stop_other_cpus(int wait)
* CPUs reach shutdown state.
*/
timeout = USEC_PER_SEC;
- while (num_online_cpus() > 1 && timeout--)
+ while (num_unstopped_cpus() > 1 && timeout--)
udelay(1);
}
/* if the REBOOT_VECTOR didn't work, try with the NMI */
- if (num_online_cpus() > 1) {
+ if (num_unstopped_cpus() > 1) {
/*
* If NMI IPI is enabled, try to register the stop handler
* and send the IPI. In any case try to wait for the other
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 69881b2d446c..2fa96cc9e7d2 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -247,6 +247,7 @@ static void notrace start_secondary(void *unused)
*/
lock_vector_lock();
set_cpu_online(smp_processor_id(), true);
+ set_cpu_unstopped(smp_processor_id(), true);
lapic_online();
unlock_vector_lock();
cpu_set_state_online(smp_processor_id());
@@ -1562,6 +1563,7 @@ static void remove_siblinginfo(int cpu)
static void remove_cpu_from_maps(int cpu)
{
set_cpu_online(cpu, false);
+ set_cpu_unstopped(cpu, false);
cpumask_clear_cpu(cpu, cpu_callout_mask);
cpumask_clear_cpu(cpu, cpu_callin_mask);
/* was set by cpu_init() */
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index 78a73eba64dd..3cd929d4ebc8 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -89,10 +89,12 @@ extern unsigned int nr_cpu_ids;
extern struct cpumask __cpu_possible_mask;
extern struct cpumask __cpu_online_mask;
+extern struct cpumask __cpu_unstopped_mask;
extern struct cpumask __cpu_present_mask;
extern struct cpumask __cpu_active_mask;
#define cpu_possible_mask ((const struct cpumask *)&__cpu_possible_mask)
#define cpu_online_mask ((const struct cpumask *)&__cpu_online_mask)
+#define cpu_unstopped_mask ((const struct cpumask *)&__cpu_unstopped_mask)
#define cpu_present_mask ((const struct cpumask *)&__cpu_present_mask)
#define cpu_active_mask ((const struct cpumask *)&__cpu_active_mask)
@@ -111,6 +113,12 @@ static inline unsigned int num_online_cpus(void)
{
return atomic_read(&__num_online_cpus);
}
+
+static inline unsigned int num_unstopped_cpus(void)
+{
+ return atomic_read(&__cpu_unstopped_mask);
+}
+
#define num_possible_cpus() cpumask_weight(cpu_possible_mask)
#define num_present_cpus() cpumask_weight(cpu_present_mask)
#define num_active_cpus() cpumask_weight(cpu_active_mask)
@@ -120,6 +128,7 @@ static inline unsigned int num_online_cpus(void)
#define cpu_active(cpu) cpumask_test_cpu((cpu), cpu_active_mask)
#else
#define num_online_cpus() 1U
+#define num_unstopped_cpus() 1U
#define num_possible_cpus() 1U
#define num_present_cpus() 1U
#define num_active_cpus() 1U
@@ -837,6 +846,15 @@ set_cpu_present(unsigned int cpu, bool present)
void set_cpu_online(unsigned int cpu, bool online);
+static inline void
+set_cpu_unstopped(unsigned int cpu, bool unstopped)
+{
+ if (unstopped)
+ cpumask_set_cpu(cpu, &__cpu_unstopped_mask);
+ else
+ cpumask_clear_cpu(cpu, &__cpu_unstopped_mask);
+}
+
static inline void
set_cpu_active(unsigned int cpu, bool active)
{
diff --git a/kernel/cpu.c b/kernel/cpu.c
index e1967e9eddc2..8b95c06e674f 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -2292,6 +2292,9 @@ EXPORT_SYMBOL(__cpu_possible_mask);
struct cpumask __cpu_online_mask __read_mostly;
EXPORT_SYMBOL(__cpu_online_mask);
+struct cpumask __cpu_unstopped_mask __read_mostly;
+EXPORT_SYMBOL(__cpu_unstopped_mask);
+
struct cpumask __cpu_present_mask __read_mostly;
EXPORT_SYMBOL(__cpu_present_mask);
@@ -2346,6 +2349,7 @@ void __init boot_cpu_init(void)
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
set_cpu_online(cpu, true);
+ set_cpu_unstopped(cpu, true);
set_cpu_active(cpu, true);
set_cpu_present(cpu, true);
set_cpu_possible(cpu, true);
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH] ARM: dts: rockchip: remove rk3288 fennec board support
From: Kever Yang @ 2019-08-20 10:03 UTC (permalink / raw)
To: heiko
Cc: Mark Rutland, devicetree, Kever Yang, linux-kernel,
linux-rockchip, Rob Herring, linux-arm-kernel
Since there is no one using this board, remove it.
Signed-off-by: Kever Yang <kever.yang@rock-chips.com>
---
arch/arm/boot/dts/rk3288-fennec.dts | 347 ----------------------------
1 file changed, 347 deletions(-)
delete mode 100644 arch/arm/boot/dts/rk3288-fennec.dts
diff --git a/arch/arm/boot/dts/rk3288-fennec.dts b/arch/arm/boot/dts/rk3288-fennec.dts
deleted file mode 100644
index 4847cf902a15..000000000000
--- a/arch/arm/boot/dts/rk3288-fennec.dts
+++ /dev/null
@@ -1,347 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
-
-/dts-v1/;
-
-#include "rk3288.dtsi"
-
-/ {
- model = "Rockchip RK3288 Fennec Board";
- compatible = "rockchip,rk3288-fennec", "rockchip,rk3288";
-
- memory@0 {
- reg = <0x0 0x0 0x0 0x80000000>;
- device_type = "memory";
- };
-
- ext_gmac: external-gmac-clock {
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <125000000>;
- clock-output-names = "ext_gmac";
- };
-
- vcc_sys: vsys-regulator {
- compatible = "regulator-fixed";
- regulator-name = "vcc_sys";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- regulator-always-on;
- regulator-boot-on;
- };
-};
-
-&cpu0 {
- cpu0-supply = <&vdd_cpu>;
-};
-
-&emmc {
- bus-width = <8>;
- cap-mmc-highspeed;
- non-removable;
- pinctrl-names = "default";
- pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_pwr &emmc_bus8>;
- status = "okay";
-};
-
-&gmac {
- assigned-clocks = <&cru SCLK_MAC>;
- assigned-clock-parents = <&ext_gmac>;
- clock_in_out = "input";
- pinctrl-names = "default";
- pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
- phy-supply = <&vcc_lan>;
- phy-mode = "rgmii";
- snps,reset-active-low;
- snps,reset-delays-us = <0 10000 1000000>;
- snps,reset-gpio = <&gpio4 RK_PB0 GPIO_ACTIVE_LOW>;
- tx_delay = <0x30>;
- rx_delay = <0x10>;
- status = "okay";
-};
-
-&gpu {
- mali-supply = <&vdd_gpu>;
- status = "okay";
-};
-
-&hdmi {
- status = "okay";
-};
-
-&i2c0 {
- status = "okay";
- clock-frequency = <400000>;
-
- rk808: pmic@1b {
- compatible = "rockchip,rk808";
- reg = <0x1b>;
- interrupt-parent = <&gpio0>;
- interrupts = <RK_PA4 IRQ_TYPE_LEVEL_LOW>;
- #clock-cells = <1>;
- clock-output-names = "xin32k", "rk808-clkout2";
- pinctrl-names = "default";
- pinctrl-0 = <&pmic_int &global_pwroff>;
- rockchip,system-power-controller;
- wakeup-source;
-
- vcc1-supply = <&vcc_sys>;
- vcc2-supply = <&vcc_sys>;
- vcc3-supply = <&vcc_sys>;
- vcc4-supply = <&vcc_sys>;
- vcc6-supply = <&vcc_sys>;
- vcc7-supply = <&vcc_sys>;
- vcc8-supply = <&vcc_io>;
- vcc9-supply = <&vcc_io>;
- vcc10-supply = <&vcc_io>;
- vcc11-supply = <&vcc_io>;
- vcc12-supply = <&vcc_io>;
- vddio-supply = <&vcc_io>;
-
- regulators {
- vdd_cpu: DCDC_REG1 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <750000>;
- regulator-max-microvolt = <1350000>;
- regulator-name = "vdd_arm";
- regulator-state-mem {
- regulator-off-in-suspend;
- };
- };
-
- vdd_gpu: DCDC_REG2 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <850000>;
- regulator-max-microvolt = <1250000>;
- regulator-name = "vdd_gpu";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <1000000>;
- };
- };
-
- vcc_ddr: DCDC_REG3 {
- regulator-always-on;
- regulator-boot-on;
- regulator-name = "vcc_ddr";
- regulator-state-mem {
- regulator-on-in-suspend;
- };
- };
-
- vcc_io: DCDC_REG4 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- regulator-name = "vcc_io";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <3300000>;
- };
- };
-
- vccio_pmu: LDO_REG1 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- regulator-name = "vccio_pmu";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <3300000>;
- };
- };
-
- vcca_33: LDO_REG2 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- regulator-name = "vcca_33";
- regulator-state-mem {
- regulator-off-in-suspend;
- };
- };
-
- vdd_10: LDO_REG3 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <1000000>;
- regulator-max-microvolt = <1000000>;
- regulator-name = "vdd_10";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <1000000>;
- };
- };
-
- vcc_wl: LDO_REG4 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- regulator-name = "vcc_wl";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <1800000>;
- };
- };
-
- vccio_sd: LDO_REG5 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <3300000>;
- regulator-name = "vccio_sd";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <3300000>;
- };
- };
-
- vdd10_lcd: LDO_REG6 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <1000000>;
- regulator-max-microvolt = <1000000>;
- regulator-name = "vdd10_lcd";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <1000000>;
- };
- };
-
- vcc_18: LDO_REG7 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- regulator-name = "vcc_18";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <1800000>;
- };
- };
-
- vcc18_lcd: LDO_REG8 {
- regulator-always-on;
- regulator-boot-on;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- regulator-name = "vcc18_lcd";
- regulator-state-mem {
- regulator-on-in-suspend;
- regulator-suspend-microvolt = <1800000>;
- };
- };
-
- vcc_sd: SWITCH_REG1 {
- regulator-always-on;
- regulator-boot-on;
- regulator-name = "vcc_sd";
- regulator-state-mem {
- regulator-on-in-suspend;
- };
- };
-
- vcc_lan: SWITCH_REG2 {
- regulator-always-on;
- regulator-boot-on;
- regulator-name = "vcc_lan";
- regulator-state-mem {
- regulator-on-in-suspend;
- };
- };
- };
- };
-};
-
-&pinctrl {
- pcfg_output_high: pcfg-output-high {
- output-high;
- };
-
- pcfg_output_low: pcfg-output-low {
- output-low;
- };
-
- pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
- drive-strength = <8>;
- };
-
- pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
- bias-pull-up;
- drive-strength = <8>;
- };
-
- gmac {
- phy_int: phy-int {
- rockchip,pins = <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>;
- };
-
- phy_pmeb: phy-pmeb {
- rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>;
- };
-
- phy_rst: phy-rst {
- rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_output_high>;
- };
- };
-
- pmic {
- pmic_int: pmic-int {
- rockchip,pins = <0 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>;
- };
- };
-
- usbphy {
- host_drv: host-drv {
- rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>;
- };
- };
-};
-
-&uart2 {
- status = "okay";
-};
-
-&usbphy {
- pinctrl-names = "default";
- pinctrl-0 = <&host_drv>;
- vbus_drv-gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
- status = "okay";
-};
-
-&usb_host0_ehci {
- status = "okay";
-};
-
-&usb_host1 {
- status = "okay";
-};
-
-&usb_otg {
- status = "okay";
-};
-
-&usb_hsic {
- status = "okay";
-};
-
-&vopb {
- status = "okay";
-};
-
-&vopb_mmu {
- status = "okay";
-};
-
-&vopl {
- status = "okay";
-};
-
-&vopl_mmu {
- status = "okay";
-};
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v2 1/2] dt-bindings: media: Add YAML schemas for the generic RC bindings
From: Maxime Ripard @ 2019-08-20 9:50 UTC (permalink / raw)
To: Sean Young
Cc: Mark Rutland, devicetree, Rob Herring, linux-kernel, Chen-Yu Tsai,
Rob Herring, mchehab, Frank Rowand, linux-arm-kernel, linux-media
In-Reply-To: <20190820081525.celdosrgcvwoq6e7@gofer.mess.org>
[-- Attachment #1.1: Type: text/plain, Size: 677 bytes --]
Hi Sean,
On Tue, Aug 20, 2019 at 09:15:26AM +0100, Sean Young wrote:
> On Mon, Aug 19, 2019 at 08:26:18PM +0200, Maxime Ripard wrote:
> > From: Maxime Ripard <maxime.ripard@bootlin.com>
> >
> > The RC controllers have a bunch of generic properties that are needed in a
> > device tree. Add a YAML schemas for those.
> >
> > Reviewed-by: Rob Herring <robh@kernel.org>
> > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
>
> For the series (both 1/2 and 2.2):
>
> Reviewed-by: Sean Young <sean@mess.org>
>
> How's tree should this go through?
Either yours or Rob's, I guess?
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [RESEND, PATCH v13 11/12] soc: mediatek: cmdq: add cmdq_dev_get_client_reg function
From: houlong wei @ 2019-08-20 9:47 UTC (permalink / raw)
To: Bibby Hsieh
Cc: devicetree@vger.kernel.org, Nicolas Boichat, Philipp Zabel,
srv_heupstream, Daoyuan Huang (黃道原),
Sascha Hauer, Jassi Brar, linux-kernel@vger.kernel.org,
Daniel Kurtz, houlong.wei, YT Shen (沈岳霆),
CK Hu (胡俊光), Rob Herring,
linux-mediatek@lists.infradead.org,
Ginny Chen (陳治傑), Sascha Hauer,
Matthias Brugger, Jiaguang Zhang (张加广),
linux-arm-kernel@lists.infradead.org,
Dennis-YC Hsieh (謝宇哲)
In-Reply-To: <20190820084932.22282-12-bibby.hsieh@mediatek.com>
On Tue, 2019-08-20 at 16:49 +0800, Bibby Hsieh wrote:
> GCE cannot know the register base address, this function
> can help cmdq client to get the cmdq_client_reg structure.
>
> Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
> Reviewed-by: CK Hu <ck.hu@mediatek.com>
> ---
> drivers/soc/mediatek/mtk-cmdq-helper.c | 29 ++++++++++++++++++++++++++
> include/linux/soc/mediatek/mtk-cmdq.h | 21 +++++++++++++++++++
> 2 files changed, 50 insertions(+)
>
> diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c
> index c53f8476c68d..80f75a1075b4 100644
> --- a/drivers/soc/mediatek/mtk-cmdq-helper.c
> +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c
> @@ -27,6 +27,35 @@ struct cmdq_instruction {
> u8 op;
> };
>
> +int cmdq_dev_get_client_reg(struct device *dev,
> + struct cmdq_client_reg *client_reg, int idx)
> +{
> + struct of_phandle_args spec;
> + int err;
> +
> + if (!client_reg)
> + return -ENOENT;
> +
> + err = of_parse_phandle_with_fixed_args(dev->of_node,
> + "mediatek,gce-client-reg",
> + 3, idx, &spec);
> + if (err < 0) {
> + dev_err(dev,
> + "error %d can't parse gce-client-reg property (%d)",
> + err, idx);
> +
> + return err;
> + }
> +
> + client_reg->subsys = (u8)spec.args[0];
> + client_reg->offset = (u16)spec.args[1];
> + client_reg->size = (u16)spec.args[2];
> + of_node_put(spec.np);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(cmdq_dev_get_client_reg);
> +
> static void cmdq_client_timeout(struct timer_list *t)
> {
> struct cmdq_client *client = from_timer(client, t, timer);
> diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h
> index a345870a6d10..02ddd60b212f 100644
> --- a/include/linux/soc/mediatek/mtk-cmdq.h
> +++ b/include/linux/soc/mediatek/mtk-cmdq.h
> @@ -15,6 +15,12 @@
>
> struct cmdq_pkt;
>
> +struct cmdq_client_reg {
> + u8 subsys;
> + u16 offset;
> + u16 size;
> +};
> +
> struct cmdq_client {
> spinlock_t lock;
> u32 pkt_cnt;
> @@ -24,6 +30,21 @@ struct cmdq_client {
> u32 timeout_ms; /* in unit of microsecond */
> };
>
> +/**
> + * cmdq_dev_get_client_reg() - parse cmdq client reg from the device
> + * node of CMDQ client
> + * @dev: device of CMDQ mailbox client
> + * @client_reg: CMDQ client reg pointer
> + * @idx: the index of desired reg
> + *
> + * Return: 0 for success; else the error code is returned
> + *
> + * Help CMDQ client parsing the cmdq client reg
> + * from the device node of CMDQ client.
> + */
> +int cmdq_dev_get_client_reg(struct device *dev,
> + struct cmdq_client_reg *client_reg, int idx);
> +
> /**
> * cmdq_mbox_create() - create CMDQ mailbox client and channel
> * @dev: device of CMDQ mailbox client
Reviewed-by: Houlong Wei <houlong.wei@mediatek.com>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH V5 4/5] iommu/dma-iommu: Use the dev->coherent_dma_mask
From: Christoph Hellwig @ 2019-08-20 9:43 UTC (permalink / raw)
To: Tom Murphy
Cc: Heiko Stuebner, virtualization, Matthias Brugger, Thierry Reding,
Will Deacon, Jean-Philippe Brucker, linux-samsung-soc,
Krzysztof Kozlowski, Jonathan Hunter, linux-rockchip, Kukjin Kim,
Andy Gross, linux-s390, Gerald Schaefer, linux-arm-msm,
linux-mediatek, linux-tegra, linux-arm-kernel, David Woodhouse,
linux-kernel, iommu, Robin Murphy
In-Reply-To: <20190815110944.3579-5-murphyt7@tcd.ie>
Looks good, and should probably be queued up asap as a bug fix:
Reviewed-by: Christoph Hellwig <hch@lst.de>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH V5 3/5] iommu/dma-iommu: Handle deferred devices
From: Christoph Hellwig @ 2019-08-20 9:43 UTC (permalink / raw)
To: Tom Murphy
Cc: Heiko Stuebner, virtualization, Matthias Brugger, Thierry Reding,
Will Deacon, Jean-Philippe Brucker, linux-samsung-soc,
Krzysztof Kozlowski, Jonathan Hunter, linux-rockchip, Kukjin Kim,
Andy Gross, linux-s390, Gerald Schaefer, linux-arm-msm,
linux-mediatek, linux-tegra, linux-arm-kernel, David Woodhouse,
linux-kernel, iommu, Robin Murphy
In-Reply-To: <20190815110944.3579-4-murphyt7@tcd.ie>
> +static int handle_deferred_device(struct device *dev,
> + struct iommu_domain *domain)
Nitick: we usually use double tab indents (or indents to after
the opening brace) for multi-line prototyped.
> + if (!is_kdump_kernel())
> + return 0;
> +
> + if (unlikely(ops->is_attach_deferred &&
> + ops->is_attach_deferred(domain, dev)))
> + return iommu_attach_device(domain, dev);
And for multi-line conditionals we also use two-tab indents.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH V5 2/5] iommu: Add gfp parameter to iommu_ops::map
From: Christoph Hellwig @ 2019-08-20 9:41 UTC (permalink / raw)
To: Tom Murphy
Cc: Heiko Stuebner, virtualization, Matthias Brugger, Thierry Reding,
Will Deacon, Jean-Philippe Brucker, linux-samsung-soc,
Krzysztof Kozlowski, Jonathan Hunter, linux-rockchip, Kukjin Kim,
Andy Gross, linux-s390, Gerald Schaefer, linux-arm-msm,
linux-mediatek, linux-tegra, linux-arm-kernel, David Woodhouse,
linux-kernel, iommu, Robin Murphy
In-Reply-To: <20190815110944.3579-3-murphyt7@tcd.ie>
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH V5 1/5] iommu/amd: Remove unnecessary locking from AMD iommu driver
From: Christoph Hellwig @ 2019-08-20 9:41 UTC (permalink / raw)
To: Tom Murphy
Cc: Heiko Stuebner, virtualization, Matthias Brugger, Thierry Reding,
Will Deacon, Jean-Philippe Brucker, linux-samsung-soc,
Krzysztof Kozlowski, Jonathan Hunter, linux-rockchip, Kukjin Kim,
Andy Gross, linux-s390, Gerald Schaefer, linux-arm-msm,
linux-mediatek, linux-tegra, linux-arm-kernel, David Woodhouse,
linux-kernel, iommu, Robin Murphy
In-Reply-To: <20190815110944.3579-2-murphyt7@tcd.ie>
On Thu, Aug 15, 2019 at 12:09:39PM +0100, Tom Murphy wrote:
> We can remove the mutex lock from amd_iommu_map and amd_iommu_unmap.
> iommu_map doesn’t lock while mapping and so no two calls should touch
> the same iova range. The AMD driver already handles the page table page
> allocations without locks so we can safely remove the locks.
I've been looking over the code and trying to understand how the
synchronization works. I gues we the cmpxchg64 in free_clear_pte
is the important point here? I have to admit I don't fully understand
the concurrency issues here, but neither do I understand what the
mutex you removed might have helped to start with.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [RESEND, PATCH v13 09/12] soc: mediatek: cmdq: define the instruction struct
From: houlong wei @ 2019-08-20 9:39 UTC (permalink / raw)
To: Bibby Hsieh
Cc: devicetree@vger.kernel.org, Nicolas Boichat, Philipp Zabel,
srv_heupstream, Daoyuan Huang (黃道原),
Sascha Hauer, Jassi Brar, linux-kernel@vger.kernel.org,
Daniel Kurtz, houlong.wei, YT Shen (沈岳霆),
CK Hu (胡俊光), Rob Herring,
linux-mediatek@lists.infradead.org,
Ginny Chen (陳治傑), Sascha Hauer,
Matthias Brugger, Jiaguang Zhang (张加广),
linux-arm-kernel@lists.infradead.org,
Dennis-YC Hsieh (謝宇哲)
In-Reply-To: <20190820084932.22282-10-bibby.hsieh@mediatek.com>
Reviewed-by: Houlong Wei <houlong.wei@mediatek.com>
On Tue, 2019-08-20 at 16:49 +0800, Bibby Hsieh wrote:
> Define an instruction structure for gce driver to append command.
> This structure can make the client's code more readability.
>
> Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
> Reviewed-by: CK Hu <ck.hu@mediatek.com>
> ---
> drivers/soc/mediatek/mtk-cmdq-helper.c | 106 +++++++++++++++--------
> include/linux/mailbox/mtk-cmdq-mailbox.h | 2 +
> 2 files changed, 74 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c
> index 7aa0517ff2f3..e3d5b0be8e79 100644
> --- a/drivers/soc/mediatek/mtk-cmdq-helper.c
> +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c
> @@ -9,12 +9,24 @@
> #include <linux/mailbox_controller.h>
> #include <linux/soc/mediatek/mtk-cmdq.h>
>
> -#define CMDQ_ARG_A_WRITE_MASK 0xffff
> #define CMDQ_WRITE_ENABLE_MASK BIT(0)
> #define CMDQ_EOC_IRQ_EN BIT(0)
> #define CMDQ_EOC_CMD ((u64)((CMDQ_CODE_EOC << CMDQ_OP_CODE_SHIFT)) \
> << 32 | CMDQ_EOC_IRQ_EN)
>
> +struct cmdq_instruction {
> + union {
> + u32 value;
> + u32 mask;
> + };
> + union {
> + u16 offset;
> + u16 event;
> + };
> + u8 subsys;
> + u8 op;
> +};
> +
> static void cmdq_client_timeout(struct timer_list *t)
> {
> struct cmdq_client *client = from_timer(client, t, timer);
> @@ -110,10 +122,8 @@ void cmdq_pkt_destroy(struct cmdq_pkt *pkt)
> }
> EXPORT_SYMBOL(cmdq_pkt_destroy);
>
> -static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code,
> - u32 arg_a, u32 arg_b)
> +static struct cmdq_instruction *cmdq_pkt_append_command(struct cmdq_pkt *pkt)
> {
> - u64 *cmd_ptr;
>
> if (unlikely(pkt->cmd_buf_size + CMDQ_INST_SIZE > pkt->buf_size)) {
> /*
> @@ -127,81 +137,109 @@ static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code,
> pkt->cmd_buf_size += CMDQ_INST_SIZE;
> WARN_ONCE(1, "%s: buffer size %u is too small !\n",
> __func__, (u32)pkt->buf_size);
> - return -ENOMEM;
> + return NULL;
> }
> - cmd_ptr = pkt->va_base + pkt->cmd_buf_size;
> - (*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
> +
> + *(u64 *)(pkt->va_base + pkt->cmd_buf_size) = 0;
> pkt->cmd_buf_size += CMDQ_INST_SIZE;
>
> - return 0;
> + return pkt->va_base + pkt->cmd_buf_size - CMDQ_INST_SIZE;
> }
>
> int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value)
> {
> - u32 arg_a = (offset & CMDQ_ARG_A_WRITE_MASK) |
> - (subsys << CMDQ_SUBSYS_SHIFT);
> + struct cmdq_instruction *inst;
> +
> + inst = cmdq_pkt_append_command(pkt);
> + if (!inst)
> + return -ENOMEM;
> +
> + inst->op = CMDQ_CODE_WRITE;
> + inst->value = value;
> + inst->offset = offset;
> + inst->subsys = subsys;
>
> - return cmdq_pkt_append_command(pkt, CMDQ_CODE_WRITE, arg_a, value);
> + return 0;
> }
> EXPORT_SYMBOL(cmdq_pkt_write);
>
> int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
> u16 offset, u32 value, u32 mask)
> {
> - u32 offset_mask = offset;
> - int err = 0;
> + struct cmdq_instruction *inst;
> + u16 offset_mask = offset;
>
> if (mask != 0xffffffff) {
> - err = cmdq_pkt_append_command(pkt, CMDQ_CODE_MASK, 0, ~mask);
> + inst = cmdq_pkt_append_command(pkt);
> + if (!inst)
> + return -ENOMEM;
> +
> + inst->op = CMDQ_CODE_MASK;
> + inst->mask = ~mask;
> offset_mask |= CMDQ_WRITE_ENABLE_MASK;
> }
> - err |= cmdq_pkt_write(pkt, value, subsys, offset_mask);
>
> - return err;
> + return cmdq_pkt_write(pkt, subsys, offset_mask, value);
> }
> EXPORT_SYMBOL(cmdq_pkt_write_mask);
>
> int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event)
> {
> - u32 arg_b;
> + struct cmdq_instruction *inst;
>
> if (event >= CMDQ_MAX_EVENT)
> return -EINVAL;
>
> - /*
> - * WFE arg_b
> - * bit 0-11: wait value
> - * bit 15: 1 - wait, 0 - no wait
> - * bit 16-27: update value
> - * bit 31: 1 - update, 0 - no update
> - */
> - arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
> + inst = cmdq_pkt_append_command(pkt);
> + if (!inst)
> + return -ENOMEM;
> +
> + inst->op = CMDQ_CODE_WFE;
> + inst->value = CMDQ_WFE_OPTION;
> + inst->event = event;
>
> - return cmdq_pkt_append_command(pkt, CMDQ_CODE_WFE, event, arg_b);
> + return 0;
> }
> EXPORT_SYMBOL(cmdq_pkt_wfe);
>
> int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event)
> {
> + struct cmdq_instruction *inst;
> +
> if (event >= CMDQ_MAX_EVENT)
> return -EINVAL;
>
> - return cmdq_pkt_append_command(pkt, CMDQ_CODE_WFE, event,
> - CMDQ_WFE_UPDATE);
> + inst = cmdq_pkt_append_command(pkt);
> + if (!inst)
> + return -ENOMEM;
> +
> + inst->op = CMDQ_CODE_WFE;
> + inst->value = CMDQ_WFE_UPDATE;
> + inst->event = event;
> +
> + return 0;
> }
> EXPORT_SYMBOL(cmdq_pkt_clear_event);
>
> static int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
> {
> - int err;
> + struct cmdq_instruction *inst;
> +
> + inst = cmdq_pkt_append_command(pkt);
> + if (!inst)
> + return -ENOMEM;
>
> - /* insert EOC and generate IRQ for each command iteration */
> - err = cmdq_pkt_append_command(pkt, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
> + inst->op = CMDQ_CODE_EOC;
> + inst->value = CMDQ_EOC_IRQ_EN;
>
> - /* JUMP to end */
> - err |= cmdq_pkt_append_command(pkt, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
> + inst = cmdq_pkt_append_command(pkt);
> + if (!inst)
> + return -ENOMEM;
> +
> + inst->op = CMDQ_CODE_JUMP;
> + inst->value = CMDQ_JUMP_PASS;
>
> - return err;
> + return 0;
> }
>
> static void cmdq_pkt_flush_async_cb(struct cmdq_cb_data data)
> diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h
> index 911475da7a53..c8adedefaf42 100644
> --- a/include/linux/mailbox/mtk-cmdq-mailbox.h
> +++ b/include/linux/mailbox/mtk-cmdq-mailbox.h
> @@ -19,6 +19,8 @@
> #define CMDQ_WFE_UPDATE BIT(31)
> #define CMDQ_WFE_WAIT BIT(15)
> #define CMDQ_WFE_WAIT_VALUE 0x1
> +#define CMDQ_WFE_OPTION (CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | \
> + CMDQ_WFE_WAIT_VALUE)
> /** cmdq event maximum */
> #define CMDQ_MAX_EVENT 0x3ff
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox